@noeldemartin/solid-utils 0.5.0 → 0.6.0-next.508449b33de64b0bcade86b642c9793381434231
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chai.d.ts +31 -0
- package/dist/chai.js +23 -0
- package/dist/chai.js.map +1 -0
- package/dist/helpers-DGXMj9cx.js +107 -0
- package/dist/helpers-DGXMj9cx.js.map +1 -0
- package/dist/io-wCcrq4b9.js +401 -0
- package/dist/io-wCcrq4b9.js.map +1 -0
- package/dist/noeldemartin-solid-utils.d.ts +61 -65
- package/dist/noeldemartin-solid-utils.js +224 -0
- package/dist/noeldemartin-solid-utils.js.map +1 -0
- package/dist/testing.d.ts +40 -0
- package/dist/testing.js +7 -0
- package/dist/testing.js.map +1 -0
- package/dist/vitest.d.ts +50 -0
- package/dist/vitest.js +55 -0
- package/dist/vitest.js.map +1 -0
- package/package.json +67 -63
- package/src/chai/assertions.ts +35 -0
- package/src/chai/index.ts +19 -0
- package/src/errors/UnauthorizedError.ts +1 -3
- package/src/errors/UnsuccessfulNetworkRequestError.ts +2 -2
- package/src/helpers/auth.test.ts +221 -0
- package/src/helpers/auth.ts +28 -27
- package/src/helpers/identifiers.test.ts +76 -0
- package/src/helpers/identifiers.ts +14 -17
- package/src/helpers/index.ts +0 -1
- package/src/helpers/interop.ts +23 -16
- package/src/helpers/io.test.ts +228 -0
- package/src/helpers/io.ts +57 -77
- package/src/helpers/jsonld.ts +6 -6
- package/src/helpers/vocabs.ts +3 -6
- package/src/helpers/wac.test.ts +64 -0
- package/src/helpers/wac.ts +10 -6
- package/src/index.ts +4 -0
- package/src/models/SolidDocument.test.ts +77 -0
- package/src/models/SolidDocument.ts +14 -18
- package/src/models/SolidStore.ts +22 -12
- package/src/models/SolidThing.ts +5 -7
- package/src/models/index.ts +2 -0
- package/src/{helpers/testing.ts → testing/helpers.ts} +24 -27
- package/src/testing/hepers.test.ts +329 -0
- package/src/testing/index.ts +1 -2
- package/src/types/index.ts +2 -0
- package/src/types/n3.d.ts +0 -2
- package/src/vitest/index.ts +20 -0
- package/src/vitest/matchers.ts +68 -0
- package/.github/workflows/ci.yml +0 -16
- package/.nvmrc +0 -1
- package/CHANGELOG.md +0 -70
- package/dist/noeldemartin-solid-utils.cjs.js +0 -2
- package/dist/noeldemartin-solid-utils.cjs.js.map +0 -1
- package/dist/noeldemartin-solid-utils.esm.js +0 -2
- package/dist/noeldemartin-solid-utils.esm.js.map +0 -1
- package/dist/noeldemartin-solid-utils.umd.js +0 -90
- package/dist/noeldemartin-solid-utils.umd.js.map +0 -1
- package/noeldemartin.config.js +0 -9
- package/src/main.ts +0 -5
- package/src/plugins/chai/assertions.ts +0 -40
- package/src/plugins/chai/index.ts +0 -5
- package/src/plugins/cypress/types.d.ts +0 -15
- package/src/plugins/index.ts +0 -2
- package/src/plugins/jest/index.ts +0 -5
- package/src/plugins/jest/matchers.ts +0 -65
- package/src/plugins/jest/types.d.ts +0 -14
- package/src/testing/ResponseStub.ts +0 -46
- package/src/testing/mocking.ts +0 -33
package/src/helpers/auth.ts
CHANGED
|
@@ -19,7 +19,10 @@ export interface SolidUserProfile {
|
|
|
19
19
|
privateTypeIndexUrl?: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
async function fetchExtendedUserProfile(
|
|
22
|
+
async function fetchExtendedUserProfile(
|
|
23
|
+
webIdDocument: SolidDocument,
|
|
24
|
+
options?: FetchSolidDocumentOptions,
|
|
25
|
+
): Promise<{
|
|
23
26
|
store: SolidStore;
|
|
24
27
|
cloaked: boolean;
|
|
25
28
|
writableProfileUrl: string | null;
|
|
@@ -29,12 +32,12 @@ async function fetchExtendedUserProfile(webIdDocument: SolidDocument, options?:
|
|
|
29
32
|
const addReferencedDocumentUrls = (document: SolidDocument) => {
|
|
30
33
|
document
|
|
31
34
|
.statements(undefined, 'foaf:isPrimaryTopicOf')
|
|
32
|
-
.map(quad => quad.object.value)
|
|
33
|
-
.forEach(profileDocumentUrl => documents[profileDocumentUrl] = documents[profileDocumentUrl] ?? null);
|
|
35
|
+
.map((quad) => quad.object.value)
|
|
36
|
+
.forEach((profileDocumentUrl) => (documents[profileDocumentUrl] = documents[profileDocumentUrl] ?? null));
|
|
34
37
|
document
|
|
35
38
|
.statements(undefined, 'foaf:primaryTopic')
|
|
36
|
-
.map(quad => quad.subject.value)
|
|
37
|
-
.forEach(profileDocumentUrl => documents[profileDocumentUrl] = documents[profileDocumentUrl] ?? null);
|
|
39
|
+
.map((quad) => quad.subject.value)
|
|
40
|
+
.forEach((profileDocumentUrl) => (documents[profileDocumentUrl] = documents[profileDocumentUrl] ?? null));
|
|
38
41
|
};
|
|
39
42
|
const loadProfileDocuments = async (): Promise<void> => {
|
|
40
43
|
for (const [url, document] of Object.entries(documents)) {
|
|
@@ -43,12 +46,12 @@ async function fetchExtendedUserProfile(webIdDocument: SolidDocument, options?:
|
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
try {
|
|
46
|
-
const
|
|
49
|
+
const _document = await fetchSolidDocument(url, options);
|
|
47
50
|
|
|
48
|
-
documents[url] =
|
|
49
|
-
store.addQuads(
|
|
51
|
+
documents[url] = _document;
|
|
52
|
+
store.addQuads(_document.getQuads());
|
|
50
53
|
|
|
51
|
-
addReferencedDocumentUrls(
|
|
54
|
+
addReferencedDocumentUrls(_document);
|
|
52
55
|
} catch (error) {
|
|
53
56
|
if (error instanceof UnauthorizedError) {
|
|
54
57
|
documents[url] = false;
|
|
@@ -65,18 +68,16 @@ async function fetchExtendedUserProfile(webIdDocument: SolidDocument, options?:
|
|
|
65
68
|
|
|
66
69
|
do {
|
|
67
70
|
await loadProfileDocuments();
|
|
68
|
-
} while (Object.values(documents).some(document => document === null));
|
|
71
|
+
} while (Object.values(documents).some((document) => document === null));
|
|
69
72
|
|
|
70
73
|
return {
|
|
71
74
|
store,
|
|
72
|
-
cloaked: Object.values(documents).some(document => document === false),
|
|
73
|
-
writableProfileUrl:
|
|
74
|
-
webIdDocument.
|
|
75
|
-
|
|
76
|
-
:
|
|
77
|
-
|
|
78
|
-
.find((document): document is SolidDocument => !!document && document.isUserWritable())
|
|
79
|
-
?.url ?? null,
|
|
75
|
+
cloaked: Object.values(documents).some((document) => document === false),
|
|
76
|
+
writableProfileUrl: webIdDocument.isUserWritable()
|
|
77
|
+
? webIdDocument.url
|
|
78
|
+
: (Object.values(documents).find(
|
|
79
|
+
(document): document is SolidDocument => !!document && document.isUserWritable(),
|
|
80
|
+
)?.url ?? null),
|
|
80
81
|
};
|
|
81
82
|
}
|
|
82
83
|
|
|
@@ -97,7 +98,7 @@ async function fetchUserProfile(webId: string, options: FetchUserProfileOptions
|
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
const { store, writableProfileUrl, cloaked } = await fetchExtendedUserProfile(document, options);
|
|
100
|
-
const storageUrls = store.statements(webId, 'pim:storage').map(storage => storage.object.value);
|
|
101
|
+
const storageUrls = store.statements(webId, 'pim:storage').map((storage) => storage.object.value);
|
|
101
102
|
const publicTypeIndex = store.statement(webId, 'solid:publicTypeIndex');
|
|
102
103
|
const privateTypeIndex = store.statement(webId, 'solid:privateTypeIndex');
|
|
103
104
|
|
|
@@ -118,7 +119,7 @@ async function fetchUserProfile(webId: string, options: FetchUserProfileOptions
|
|
|
118
119
|
throw new Error(`Could not find any storage for ${webId}.`);
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
await options.onLoaded?.(
|
|
122
|
+
await options.onLoaded?.(store);
|
|
122
123
|
|
|
123
124
|
return {
|
|
124
125
|
webId,
|
|
@@ -126,9 +127,7 @@ async function fetchUserProfile(webId: string, options: FetchUserProfileOptions
|
|
|
126
127
|
writableProfileUrl,
|
|
127
128
|
storageUrls: arrayUnique(storageUrls) as [string, ...string[]],
|
|
128
129
|
...objectWithoutEmpty({
|
|
129
|
-
name:
|
|
130
|
-
store.statement(webId, 'vcard:fn')?.object.value ??
|
|
131
|
-
store.statement(webId, 'foaf:name')?.object.value,
|
|
130
|
+
name: store.statement(webId, 'vcard:fn')?.object.value ?? store.statement(webId, 'foaf:name')?.object.value,
|
|
132
131
|
avatarUrl:
|
|
133
132
|
store.statement(webId, 'vcard:hasPhoto')?.object.value ??
|
|
134
133
|
store.statement(webId, 'foaf:img')?.object.value,
|
|
@@ -156,9 +155,11 @@ export async function fetchLoginUserProfile(
|
|
|
156
155
|
return fetchUserProfile(loginUrl, options);
|
|
157
156
|
}
|
|
158
157
|
|
|
159
|
-
const fetchProfile = silenced(url => fetchUserProfile(url, options));
|
|
158
|
+
const fetchProfile = silenced((url) => fetchUserProfile(url, options));
|
|
160
159
|
|
|
161
|
-
return
|
|
162
|
-
|
|
163
|
-
|
|
160
|
+
return (
|
|
161
|
+
(await fetchProfile(loginUrl)) ??
|
|
162
|
+
(await fetchProfile(loginUrl.replace(/\/$/, '').concat('/profile/card#me'))) ??
|
|
163
|
+
(await fetchProfile(urlRoot(loginUrl).concat('/profile/card#me')))
|
|
164
|
+
);
|
|
164
165
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { mintJsonLDIdentifiers } from '@noeldemartin/solid-utils/helpers';
|
|
4
|
+
import { parseResourceSubject } from '@noeldemartin/solid-utils/helpers/identifiers';
|
|
5
|
+
import type { JsonLD } from '@noeldemartin/solid-utils/helpers';
|
|
6
|
+
|
|
7
|
+
describe('Identifiers helpers', () => {
|
|
8
|
+
|
|
9
|
+
it('mints JsonLD identifiers', () => {
|
|
10
|
+
// Arrange
|
|
11
|
+
const jsonld = {
|
|
12
|
+
'@context': { '@vocab': 'https://schema.org/' },
|
|
13
|
+
'@type': 'Recipe',
|
|
14
|
+
'name': 'Ramen',
|
|
15
|
+
'ingredients': ['Broth', 'Noodles'],
|
|
16
|
+
'instructions': [
|
|
17
|
+
{
|
|
18
|
+
'@type': 'HowToStep',
|
|
19
|
+
'text': 'Boil Noodles',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
'@type': 'HowToStep',
|
|
23
|
+
'text': 'Dip them into the broth',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
'http://purl.org/dc/terms/created': {
|
|
27
|
+
'@type': 'http://www.w3.org/2001/XMLSchema#dateTime',
|
|
28
|
+
'@value': '1997-07-21T23:42:00.000Z',
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Act
|
|
33
|
+
const jsonldWithIds = mintJsonLDIdentifiers(jsonld);
|
|
34
|
+
|
|
35
|
+
// Assert
|
|
36
|
+
expect(jsonldWithIds['@id']).not.toBeUndefined();
|
|
37
|
+
|
|
38
|
+
const createdAt = jsonldWithIds['http://purl.org/dc/terms/created'] as Record<string, unknown>;
|
|
39
|
+
expect(createdAt['@id']).toBeUndefined();
|
|
40
|
+
|
|
41
|
+
const instructions = jsonldWithIds['instructions'] as [JsonLD, JsonLD];
|
|
42
|
+
expect(instructions[0]['@id']).not.toBeUndefined();
|
|
43
|
+
expect(instructions[1]['@id']).not.toBeUndefined();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('parses subjects', () => {
|
|
47
|
+
expect(parseResourceSubject('https://my-pod.com/profile/card#me')).toEqual({
|
|
48
|
+
containerUrl: 'https://my-pod.com/profile/',
|
|
49
|
+
documentName: 'card',
|
|
50
|
+
resourceHash: 'me',
|
|
51
|
+
});
|
|
52
|
+
expect(parseResourceSubject('https://my-pod.com/about')).toEqual({
|
|
53
|
+
containerUrl: 'https://my-pod.com/',
|
|
54
|
+
documentName: 'about',
|
|
55
|
+
});
|
|
56
|
+
expect(parseResourceSubject('/profile/card#me')).toEqual({
|
|
57
|
+
containerUrl: '/profile/',
|
|
58
|
+
documentName: 'card',
|
|
59
|
+
resourceHash: 'me',
|
|
60
|
+
});
|
|
61
|
+
expect(parseResourceSubject('/about#sections')).toEqual({
|
|
62
|
+
containerUrl: '/',
|
|
63
|
+
documentName: 'about',
|
|
64
|
+
resourceHash: 'sections',
|
|
65
|
+
});
|
|
66
|
+
expect(parseResourceSubject('about#sections')).toEqual({
|
|
67
|
+
documentName: 'about',
|
|
68
|
+
resourceHash: 'sections',
|
|
69
|
+
});
|
|
70
|
+
expect(parseResourceSubject('about')).toEqual({
|
|
71
|
+
documentName: 'about',
|
|
72
|
+
});
|
|
73
|
+
expect(parseResourceSubject('')).toEqual({});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { arr, isArray, isObject, objectDeepClone, objectWithoutEmpty, tap, urlParse, uuid } from '@noeldemartin/utils';
|
|
2
2
|
import type { UrlParts } from '@noeldemartin/utils';
|
|
3
|
-
import type { JsonLD, JsonLDResource } from '
|
|
3
|
+
import type { JsonLD, JsonLDResource } from '@noeldemartin/solid-utils/helpers';
|
|
4
4
|
|
|
5
5
|
export interface SubjectParts {
|
|
6
6
|
containerUrl?: string;
|
|
@@ -9,11 +9,9 @@ export interface SubjectParts {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
function getContainerPath(parts: UrlParts): string | null {
|
|
12
|
-
if (!parts.path || !parts.path.startsWith('/'))
|
|
13
|
-
return null;
|
|
12
|
+
if (!parts.path || !parts.path.startsWith('/')) return null;
|
|
14
13
|
|
|
15
|
-
if (parts.path.match(/^\/[^/]*$/))
|
|
16
|
-
return '/';
|
|
14
|
+
if (parts.path.match(/^\/[^/]*$/)) return '/';
|
|
17
15
|
|
|
18
16
|
return `/${arr(parts.path.split('/')).filter().slice(0, -1).join('/')}/`.replace('//', '/');
|
|
19
17
|
}
|
|
@@ -27,30 +25,29 @@ function getContainerUrl(parts: UrlParts): string | null {
|
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
function __mintJsonLDIdentifiers(jsonld: JsonLD): void {
|
|
30
|
-
if (!('@type' in jsonld) || '@value' in jsonld)
|
|
31
|
-
return;
|
|
28
|
+
if (!('@type' in jsonld) || '@value' in jsonld) return;
|
|
32
29
|
|
|
33
30
|
jsonld['@id'] = jsonld['@id'] ?? uuid();
|
|
34
31
|
|
|
35
32
|
for (const propertyValue of Object.values(jsonld)) {
|
|
36
|
-
if (isObject(propertyValue))
|
|
37
|
-
__mintJsonLDIdentifiers(propertyValue);
|
|
33
|
+
if (isObject(propertyValue)) __mintJsonLDIdentifiers(propertyValue);
|
|
38
34
|
|
|
39
|
-
if (isArray(propertyValue))
|
|
40
|
-
propertyValue.forEach(value => isObject(value) && __mintJsonLDIdentifiers(value));
|
|
35
|
+
if (isArray(propertyValue)) propertyValue.forEach((value) => isObject(value) && __mintJsonLDIdentifiers(value));
|
|
41
36
|
}
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
export function mintJsonLDIdentifiers(jsonld: JsonLD): JsonLDResource {
|
|
45
|
-
return tap(objectDeepClone(jsonld) as JsonLDResource, clone => __mintJsonLDIdentifiers(clone));
|
|
40
|
+
return tap(objectDeepClone(jsonld) as JsonLDResource, (clone) => __mintJsonLDIdentifiers(clone));
|
|
46
41
|
}
|
|
47
42
|
|
|
48
43
|
export function parseResourceSubject(subject: string): SubjectParts {
|
|
49
44
|
const parts = urlParse(subject);
|
|
50
45
|
|
|
51
|
-
return !parts
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
46
|
+
return !parts
|
|
47
|
+
? {}
|
|
48
|
+
: objectWithoutEmpty({
|
|
49
|
+
containerUrl: getContainerUrl(parts),
|
|
50
|
+
documentName: parts.path ? parts.path.split('/').pop() : null,
|
|
51
|
+
resourceHash: parts.fragment,
|
|
52
|
+
});
|
|
56
53
|
}
|
package/src/helpers/index.ts
CHANGED
package/src/helpers/interop.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { uuid } from '@noeldemartin/utils';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
createSolidDocument,
|
|
5
|
+
fetchSolidDocument,
|
|
6
|
+
solidDocumentExists,
|
|
7
|
+
updateSolidDocument,
|
|
8
|
+
} from '@noeldemartin/solid-utils/helpers/io';
|
|
9
|
+
import type { Fetch } from '@noeldemartin/solid-utils/helpers/io';
|
|
10
|
+
import type { SolidUserProfile } from '@noeldemartin/solid-utils/helpers/auth';
|
|
6
11
|
|
|
7
12
|
type TypeIndexType = 'public' | 'private';
|
|
8
13
|
|
|
@@ -12,7 +17,7 @@ async function mintTypeIndexUrl(user: SolidUserProfile, type: TypeIndexType, fet
|
|
|
12
17
|
const storageUrl = user.storageUrls[0];
|
|
13
18
|
const typeIndexUrl = `${storageUrl}settings/${type}TypeIndex`;
|
|
14
19
|
|
|
15
|
-
return await solidDocumentExists(typeIndexUrl, { fetch })
|
|
20
|
+
return (await solidDocumentExists(typeIndexUrl, { fetch }))
|
|
16
21
|
? `${storageUrl}settings/${type}TypeIndex-${uuid()}`
|
|
17
22
|
: typeIndexUrl;
|
|
18
23
|
}
|
|
@@ -25,9 +30,10 @@ async function createTypeIndex(user: SolidUserProfile, type: TypeIndexType, fetc
|
|
|
25
30
|
fetch = fetch ?? window.fetch.bind(fetch);
|
|
26
31
|
|
|
27
32
|
const typeIndexUrl = await mintTypeIndexUrl(user, type, fetch);
|
|
28
|
-
const typeIndexBody =
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
const typeIndexBody =
|
|
34
|
+
type === 'public'
|
|
35
|
+
? '<> a <http://www.w3.org/ns/solid/terms#TypeIndex> .'
|
|
36
|
+
: `
|
|
31
37
|
<> a
|
|
32
38
|
<http://www.w3.org/ns/solid/terms#TypeIndex>,
|
|
33
39
|
<http://www.w3.org/ns/solid/terms#UnlistedDocument> .
|
|
@@ -60,15 +66,16 @@ async function findRegistrations(
|
|
|
60
66
|
const typeIndex = await fetchSolidDocument(typeIndexUrl, { fetch });
|
|
61
67
|
const types = Array.isArray(type) ? type : [type];
|
|
62
68
|
|
|
63
|
-
return types
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
return types
|
|
70
|
+
.map((_type) =>
|
|
71
|
+
typeIndex
|
|
72
|
+
.statements(undefined, 'rdf:type', 'solid:TypeRegistration')
|
|
73
|
+
.filter((statement) => typeIndex.contains(statement.subject.value, 'solid:forClass', _type))
|
|
74
|
+
.map((statement) => typeIndex.statements(statement.subject.value, predicate))
|
|
75
|
+
.flat()
|
|
76
|
+
.map((statement) => statement.object.value)
|
|
77
|
+
.filter((url) => !!url))
|
|
78
|
+
.flat();
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
/**
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { jsonldToQuads, normalizeSparql, quadsToJsonLD, sparqlToQuadsSync, turtleToQuadsSync } from './io';
|
|
4
|
+
import type { Quad } from '@rdfjs/types';
|
|
5
|
+
|
|
6
|
+
describe('IO', () => {
|
|
7
|
+
|
|
8
|
+
it('parses jsonld', async () => {
|
|
9
|
+
// Arrange
|
|
10
|
+
const jsonld = {
|
|
11
|
+
'@context': { '@vocab': 'https://schema.org/' },
|
|
12
|
+
'@type': 'Movie',
|
|
13
|
+
'name': 'Spirited Away',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Act
|
|
17
|
+
const quads = (await jsonldToQuads(jsonld)) as [Quad, Quad];
|
|
18
|
+
|
|
19
|
+
// Assert
|
|
20
|
+
expect(quads).toHaveLength(2);
|
|
21
|
+
|
|
22
|
+
expect(quads[0].subject.termType).toEqual('BlankNode');
|
|
23
|
+
expect(quads[0].predicate.termType).toEqual('NamedNode');
|
|
24
|
+
expect(quads[0].predicate.value).toEqual('http://www.w3.org/1999/02/22-rdf-syntax-ns#type');
|
|
25
|
+
expect(quads[0].object.termType).toEqual('NamedNode');
|
|
26
|
+
expect(quads[0].object.value).toEqual('https://schema.org/Movie');
|
|
27
|
+
|
|
28
|
+
expect(quads[1].subject.termType).toEqual('BlankNode');
|
|
29
|
+
expect(quads[1].predicate.termType).toEqual('NamedNode');
|
|
30
|
+
expect(quads[1].predicate.value).toEqual('https://schema.org/name');
|
|
31
|
+
expect(quads[1].object.termType).toEqual('Literal');
|
|
32
|
+
expect(quads[1].object.value).toEqual('Spirited Away');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('parses jsonld with anonymous subjects', async () => {
|
|
36
|
+
// Arrange
|
|
37
|
+
const jsonld = {
|
|
38
|
+
'@context': { '@vocab': 'https://schema.org/' },
|
|
39
|
+
'@id': '#it',
|
|
40
|
+
'@type': 'Movie',
|
|
41
|
+
'name': 'Spirited Away',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Act
|
|
45
|
+
const quads = (await jsonldToQuads(jsonld)) as [Quad, Quad];
|
|
46
|
+
|
|
47
|
+
// Assert
|
|
48
|
+
expect(quads).toHaveLength(2);
|
|
49
|
+
|
|
50
|
+
expect(quads[0].subject.termType).toEqual('NamedNode');
|
|
51
|
+
expect(quads[0].subject.value).toEqual('#it');
|
|
52
|
+
expect(quads[0].predicate.termType).toEqual('NamedNode');
|
|
53
|
+
expect(quads[0].predicate.value).toEqual('http://www.w3.org/1999/02/22-rdf-syntax-ns#type');
|
|
54
|
+
expect(quads[0].object.termType).toEqual('NamedNode');
|
|
55
|
+
expect(quads[0].object.value).toEqual('https://schema.org/Movie');
|
|
56
|
+
|
|
57
|
+
expect(quads[1].subject.termType).toEqual('NamedNode');
|
|
58
|
+
expect(quads[1].subject.value).toEqual('#it');
|
|
59
|
+
expect(quads[1].predicate.termType).toEqual('NamedNode');
|
|
60
|
+
expect(quads[1].predicate.value).toEqual('https://schema.org/name');
|
|
61
|
+
expect(quads[1].object.termType).toEqual('Literal');
|
|
62
|
+
expect(quads[1].object.value).toEqual('Spirited Away');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('parses jsonld graphs', async () => {
|
|
66
|
+
// Arrange
|
|
67
|
+
const jsonld = {
|
|
68
|
+
'@graph': [
|
|
69
|
+
{
|
|
70
|
+
'@context': { '@vocab': 'https://schema.org/' },
|
|
71
|
+
'@id': 'solid://movies/spirited-away',
|
|
72
|
+
'@type': 'Movie',
|
|
73
|
+
'name': 'Spirited Away',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
'@context': { '@vocab': 'https://schema.org/' },
|
|
77
|
+
'@id': 'solid://movies/spirited-away',
|
|
78
|
+
'@type': 'Movie',
|
|
79
|
+
'name': 'Spirited Away',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Act
|
|
85
|
+
const quads = await jsonldToQuads(jsonld);
|
|
86
|
+
|
|
87
|
+
// Assert
|
|
88
|
+
expect(quads).toHaveLength(4);
|
|
89
|
+
|
|
90
|
+
[0, 2].forEach((index) => {
|
|
91
|
+
expect(quads[index]?.subject.termType).toEqual('NamedNode');
|
|
92
|
+
expect(quads[index]?.subject.value).toEqual('solid://movies/spirited-away');
|
|
93
|
+
expect(quads[index]?.predicate.termType).toEqual('NamedNode');
|
|
94
|
+
expect(quads[index]?.predicate.value).toEqual('http://www.w3.org/1999/02/22-rdf-syntax-ns#type');
|
|
95
|
+
expect(quads[index]?.object.termType).toEqual('NamedNode');
|
|
96
|
+
expect(quads[index]?.object.value).toEqual('https://schema.org/Movie');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
[1, 3].forEach((index) => {
|
|
100
|
+
expect(quads[index]?.subject.termType).toEqual('NamedNode');
|
|
101
|
+
expect(quads[index]?.subject.value).toEqual('solid://movies/spirited-away');
|
|
102
|
+
expect(quads[index]?.predicate.termType).toEqual('NamedNode');
|
|
103
|
+
expect(quads[index]?.predicate.value).toEqual('https://schema.org/name');
|
|
104
|
+
expect(quads[index]?.object.termType).toEqual('Literal');
|
|
105
|
+
expect(quads[index]?.object.value).toEqual('Spirited Away');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('normalizes sparql', () => {
|
|
110
|
+
// Arrange
|
|
111
|
+
const insertTurtle = `
|
|
112
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
|
113
|
+
|
|
114
|
+
<#me>
|
|
115
|
+
foaf:name "Amy" ;
|
|
116
|
+
foaf:lastName "Doe" .
|
|
117
|
+
`;
|
|
118
|
+
const deleteTurtle = `
|
|
119
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
|
120
|
+
|
|
121
|
+
<#me> foaf:name "John Doe" .
|
|
122
|
+
`;
|
|
123
|
+
const sparql = `
|
|
124
|
+
INSERT DATA { ${insertTurtle} } ;
|
|
125
|
+
DELETE DATA { ${deleteTurtle} }
|
|
126
|
+
`;
|
|
127
|
+
|
|
128
|
+
// Act
|
|
129
|
+
const normalized = normalizeSparql(sparql);
|
|
130
|
+
|
|
131
|
+
// Assert
|
|
132
|
+
expect(normalized).toEqual(
|
|
133
|
+
[
|
|
134
|
+
'INSERT DATA {',
|
|
135
|
+
' <#me> <http://xmlns.com/foaf/0.1/lastName> "Doe" .',
|
|
136
|
+
' <#me> <http://xmlns.com/foaf/0.1/name> "Amy" .',
|
|
137
|
+
'} ;',
|
|
138
|
+
'DELETE DATA {',
|
|
139
|
+
' <#me> <http://xmlns.com/foaf/0.1/name> "John Doe" .',
|
|
140
|
+
'}',
|
|
141
|
+
].join('\n'),
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('parses sparql', () => {
|
|
146
|
+
// Arrange
|
|
147
|
+
const insertTurtle = `
|
|
148
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
|
149
|
+
|
|
150
|
+
<#me> foaf:name "Amy Doe" .
|
|
151
|
+
`;
|
|
152
|
+
const deleteTurtle = `
|
|
153
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
|
154
|
+
|
|
155
|
+
<#me> foaf:name "John Doe" .
|
|
156
|
+
`;
|
|
157
|
+
const sparql = `
|
|
158
|
+
INSERT DATA { ${insertTurtle} } ;
|
|
159
|
+
DELETE DATA { ${deleteTurtle} }
|
|
160
|
+
`;
|
|
161
|
+
|
|
162
|
+
// Act
|
|
163
|
+
const quads = sparqlToQuadsSync(sparql);
|
|
164
|
+
|
|
165
|
+
// Assert
|
|
166
|
+
expect(Object.keys(quads)).toHaveLength(2);
|
|
167
|
+
|
|
168
|
+
expect(quads.insert).toEqual(turtleToQuadsSync(insertTurtle));
|
|
169
|
+
expect(quads.delete).toEqual(turtleToQuadsSync(deleteTurtle));
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('parses turtle synchronously', () => {
|
|
173
|
+
// Arrange
|
|
174
|
+
const turtle = `
|
|
175
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
|
176
|
+
|
|
177
|
+
<#me>
|
|
178
|
+
a foaf:Person ;
|
|
179
|
+
foaf:name "John Doe" .
|
|
180
|
+
`;
|
|
181
|
+
|
|
182
|
+
// Act
|
|
183
|
+
const quads = turtleToQuadsSync(turtle) as [Quad, Quad];
|
|
184
|
+
|
|
185
|
+
// Assert
|
|
186
|
+
expect(quads).toHaveLength(2);
|
|
187
|
+
|
|
188
|
+
expect(quads[0].subject.termType).toEqual('NamedNode');
|
|
189
|
+
expect(quads[0].subject.value).toEqual('#me');
|
|
190
|
+
expect(quads[0].predicate.termType).toEqual('NamedNode');
|
|
191
|
+
expect(quads[0].predicate.value).toEqual('http://www.w3.org/1999/02/22-rdf-syntax-ns#type');
|
|
192
|
+
expect(quads[0].object.termType).toEqual('NamedNode');
|
|
193
|
+
expect(quads[0].object.value).toEqual('http://xmlns.com/foaf/0.1/Person');
|
|
194
|
+
|
|
195
|
+
expect(quads[1].subject.termType).toEqual('NamedNode');
|
|
196
|
+
expect(quads[1].subject.value).toEqual('#me');
|
|
197
|
+
expect(quads[1].predicate.termType).toEqual('NamedNode');
|
|
198
|
+
expect(quads[1].predicate.value).toEqual('http://xmlns.com/foaf/0.1/name');
|
|
199
|
+
expect(quads[1].object.termType).toEqual('Literal');
|
|
200
|
+
expect(quads[1].object.value).toEqual('John Doe');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('converts quads to jsonld', async () => {
|
|
204
|
+
// Arrange
|
|
205
|
+
const quads = turtleToQuadsSync(`
|
|
206
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
|
207
|
+
|
|
208
|
+
<#me>
|
|
209
|
+
a foaf:Person ;
|
|
210
|
+
foaf:name "John Doe" .
|
|
211
|
+
`);
|
|
212
|
+
|
|
213
|
+
// Act
|
|
214
|
+
const jsonld = await quadsToJsonLD(quads);
|
|
215
|
+
|
|
216
|
+
// Assert
|
|
217
|
+
expect(jsonld).toEqual({
|
|
218
|
+
'@graph': [
|
|
219
|
+
{
|
|
220
|
+
'@id': '#me',
|
|
221
|
+
'@type': ['http://xmlns.com/foaf/0.1/Person'],
|
|
222
|
+
'http://xmlns.com/foaf/0.1/name': [{ '@value': 'John Doe' }],
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
});
|