-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Experimental note body serialization #1
Changes from 5 commits
e922b85
6b59327
d803628
2b7242c
bc7ddff
b7921a0
0f2af1b
4e96b87
c018106
f2821c3
cd24cd0
07d592f
6ec317b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
|
||
import { | ||
SolidDataset, Thing, | ||
getThing, buildThing, createThing, getUrl, | ||
} from '@inrupt/solid-client'; | ||
import { first, rest, nil } from '@ontologies/rdf' | ||
|
||
type JSOToThingConverter = (obj: any, path: number[]) => Thing[] | ||
|
||
export function arrayToThings(slateArray: object[], jsoToThing: JSOToThingConverter, path: number[] = [0]): Thing[] { | ||
const [el, ...restOfEls] = slateArray | ||
const last = path[path.length - 1] | ||
const [listElementThing, ...listElementSubThings] = jsoToThing(el, path) | ||
const [nextListThing, ...nextListSubThings] = (restOfEls.length > 0) ? arrayToThings(restOfEls, jsoToThing, [...path.slice(0, path.length - 1), last + 1]) : [] | ||
const listThing = buildThing(createThing({ name: `li-${path.join("-")}` })) | ||
.addUrl(first.value, listElementThing) | ||
.addUrl(rest.value, nextListThing || nil.value) | ||
.build() | ||
|
||
return [listThing, listElementThing, ...listElementSubThings, nextListThing, ...nextListSubThings] | ||
} | ||
|
||
export type ThingToJSOConvertor = (thing: Thing, dataset: SolidDataset) => any | ||
|
||
export function thingsToArray(thing: Thing, dataset: SolidDataset, thingToSlateObject: ThingToJSOConvertor): object[] { | ||
const restValue = getUrl(thing, rest.value) | ||
const restThing = (restValue && (restValue !== nil.value)) && getThing(dataset, restValue) | ||
const firstUrl = getUrl(thing, first.value) | ||
const firstThing = firstUrl && getThing(dataset, firstUrl) | ||
const firstElement = firstThing && thingToSlateObject(firstThing, dataset) | ||
return firstElement ? [ | ||
firstElement, | ||
...(restThing ? thingsToArray(restThing, dataset, thingToSlateObject) : []) | ||
] : [] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,9 @@ | ||
export * from "./hooks" | ||
export * from "./collections" | ||
export * from "./garden" | ||
export * from "./hooks" | ||
export * from "./items" | ||
export * from "./spaces" | ||
export * from "./settings" | ||
export * from "./spaces" | ||
export * from "./types" | ||
export * from "./utils" | ||
export * from "./vocab" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { | ||
SolidDataset, Thing, | ||
getThing, createThing, | ||
addUrl, | ||
setInteger, setDecimal, setStringNoLocale, setBoolean, | ||
getStringNoLocale, getInteger, getBoolean, getUrl, | ||
} from '@inrupt/solid-client'; | ||
import { createNS } from "@ontologies/core" | ||
|
||
import {arrayToThings, thingsToArray} from "./collections" | ||
|
||
const noteNSUrl = "https://mysilio.garden/ontologies/note#" | ||
const noteNS = createNS(noteNSUrl) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I use See There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't matter much which one we use, I don't think, because they both build NamedNodes. I think I even came across the @ontologies one, might be by the same guy? |
||
|
||
function addKeyValToThing(thing: Thing, key: string, value: any, path: number[]): Thing[] { | ||
if (Array.isArray(value)) { | ||
const [arrayThing, ...restOfThings] = arrayToThings(value, createThingFromSlateJSOElement, [...path, 0]) | ||
if (arrayThing) { | ||
return [ | ||
addUrl(thing, key, arrayThing), | ||
arrayThing, | ||
...restOfThings | ||
] | ||
} else { | ||
// TODO: is this the right thing to do? arrayThing should never be falsy so maybe throw an error? | ||
return [] | ||
} | ||
} else { | ||
|
||
switch (typeof value) { | ||
case 'string': | ||
return [setStringNoLocale(thing, key, value)] | ||
case 'boolean': | ||
return [setBoolean(thing, key, value)] | ||
case 'number': | ||
if (Number.isInteger(value)) { | ||
return [setInteger(thing, key, value)] | ||
} else { | ||
return [setDecimal(thing, key, value)] | ||
} | ||
default: | ||
return [thing] | ||
} | ||
} | ||
|
||
} | ||
|
||
export function createThingFromSlateJSOElement(o: any, path: number[]): Thing[] { | ||
const thing = createThing({ name: (o.id ? `${o.id}` : `el-${path.join("-")}`) }) | ||
const otherThings = Object.keys(o).reduce((m: Thing[], k: string) => { | ||
const [mThing, ...mRest] = m; | ||
const [thing, ...restOfThings] = addKeyValToThing(mThing, noteNS(k).value, o[k], path) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like all the places you use this in the downstream function are Inrupt fns. Those all actually take NamedNodes as well as URLStrings for the predicates, so you don't have to do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
return [thing, ...mRest, ...restOfThings] | ||
}, [thing]) | ||
return [thing, ...otherThings] | ||
} | ||
|
||
function childrenArrayFromDataset(dataset: SolidDataset, childrenUrl: string) { | ||
const thing = getThing(dataset, childrenUrl); | ||
|
||
if (thing) { | ||
return thingsToArray(thing, dataset, noteThingToSlateObject) | ||
} else { | ||
return [] | ||
} | ||
} | ||
|
||
const childrenPred = noteNS('children') | ||
export function noteThingToSlateObject(thing: Thing, dataset: SolidDataset) { | ||
const obj: any = {} | ||
for (const pred in thing.predicates) { | ||
const [, key] = pred.split(noteNSUrl) | ||
if (key) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just noticed the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh yep! this is expecting that there might be other predicates on the thing and trying to only deal with things in our NS. I'm really not sure about this particular part of the approach - at the very least I need to double check that my namespaces make sense before shipping this, will take another look at this tomorrow! |
||
if (key === 'children') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not instead test to see if To me that reads a little cleaner than splitting on the predicate namespace. |
||
const children = getUrl(thing, childrenPred.value) | ||
if (children) obj.children = childrenArrayFromDataset(dataset, children) | ||
} else { | ||
const literals = thing.predicates[pred].literals | ||
// currently just uses the first literal it can find | ||
if (literals) { | ||
if (literals['http://www.w3.org/2001/XMLSchema#string']) { | ||
const str = getStringNoLocale(thing, pred) | ||
if (str || (str === "")) obj[key] = str | ||
} else if (literals['http://www.w3.org/2001/XMLSchema#boolean']){ | ||
const bool = getBoolean(thing, pred) | ||
if (bool === true || bool === false) obj[key] = bool | ||
} else if (literals['http://www.w3.org/2001/XMLSchema#integer']){ | ||
const n = getInteger(thing, pred) | ||
if (n) obj[key] = n | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
return obj | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,6 @@ | ||
import { sum } from '../src'; | ||
|
||
describe('blah', () => { | ||
it('works', () => { | ||
expect(sum(1, 1)).toEqual(2); | ||
expect(1 + 1).toEqual(2); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { | ||
Thing, | ||
getThing, setThing, createSolidDataset, saveSolidDatasetAt | ||
} from '@inrupt/solid-client'; | ||
|
||
import { arrayToThings, thingsToArray } from '../src/collections' | ||
import { createThingFromSlateJSOElement, noteThingToSlateObject } from '../src/note' | ||
|
||
const noteBody = [{ "children": [{ "text": "Hi everybody!" }], "type": "h1" }, { "type": "p", "id": 1651696062634, "children": [{ "text": "" }] }, { "children": [{ "text": "" }], "type": "h1" }, { "children": [{ "text": "thanks", "bold": true }, { "text": " for coming to the " }, { "text": "digital gardening party", "italic": true }, { "text": "." }], "type": "h2" }, { "children": [{ "text": "" }], "type": "h2" }, { "children": [{ "text": "I'm " }, { "type": "concept", "children": [{ "text": "[[Travis]]" }], "name": "Travis" }, { "text": "." }], "type": "h2" }, { "children": [{ "text": "" }], "type": "h2" }, { "children": [{ "text": "This is not a cat:" }], "type": "h2" }, { "children": [{ "text": "" }], "type": "h2" }, { "children": [{ "text": "" }], "type": "h2" }, { "type": "img", "children": [{ "text": "" }], "url": "https://travis.mysilio.me/public/itme/online/images/1cd18da0-6c9e-11eb-b09e-dd1bdda70e9f.jpg", "originalUrl": "https://travis.mysilio.me/public/itme/online/images/1cd18da0-6c9e-11eb-b09e-dd1bdda70e9f.original.jpg", "alt": "", "mime": "image/jpeg" }] | ||
|
||
describe('a JSON note body', () => { | ||
it('can be serialized and deserialized to and from RDF', async () => { | ||
const things: Thing[] = arrayToThings(noteBody, createThingFromSlateJSOElement) | ||
const listUrl = things[0].url | ||
|
||
let dataset = things.filter(x => !!x).reduce(setThing, createSolidDataset()) | ||
|
||
// need to save this to resolve the various local references in Thing objects | ||
dataset = await saveSolidDatasetAt("https://example.com/note", dataset, { | ||
fetch: jest.fn(() => | ||
Promise.resolve({ | ||
ok: true, | ||
url: "https://example.com/note", | ||
headers: new Headers(), | ||
}), | ||
) as jest.Mock | ||
}) | ||
|
||
const listThing = getThing(dataset, listUrl) | ||
const newNoteBody = listThing && thingsToArray(listThing, dataset, noteThingToSlateObject) | ||
expect(newNoteBody).toEqual(noteBody); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why dev deps for this one but not the others?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oo I think this is a good catch, thx!