solid-logic 1.3.17-6a574cd0 → 1.3.17-6e0634d8
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/README.md +2 -2
- package/lib/acl/aclLogic.d.ts +3 -30
- package/lib/acl/aclLogic.d.ts.map +1 -1
- package/lib/acl/aclLogic.js +150 -119
- package/lib/acl/aclLogic.js.map +1 -1
- package/lib/authn/SolidAuthnLogic.d.ts.map +1 -1
- package/lib/authn/SolidAuthnLogic.js +2 -2
- package/lib/authn/SolidAuthnLogic.js.map +1 -1
- package/lib/chat/chatLogic.d.ts +3 -0
- package/lib/chat/chatLogic.d.ts.map +1 -0
- package/lib/chat/{ChatLogic.js → chatLogic.js} +82 -86
- package/lib/chat/chatLogic.js.map +1 -0
- package/lib/inbox/inboxLogic.d.ts +3 -0
- package/lib/inbox/inboxLogic.d.ts.map +1 -0
- package/lib/inbox/{InboxLogic.js → inboxLogic.js} +59 -64
- package/lib/inbox/inboxLogic.js.map +1 -0
- package/lib/index.d.ts +6 -11
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +5 -38
- package/lib/index.js.map +1 -1
- package/lib/logic/CustomError.d.ts +4 -0
- package/lib/logic/CustomError.d.ts.map +1 -1
- package/lib/logic/CustomError.js +17 -1
- package/lib/logic/CustomError.js.map +1 -1
- package/lib/logic/solidLogic.d.ts +6 -0
- package/lib/logic/solidLogic.d.ts.map +1 -0
- package/lib/logic/solidLogic.js +92 -0
- package/lib/logic/solidLogic.js.map +1 -0
- package/lib/logic/solidLogicSingleton.d.ts +1 -2
- package/lib/logic/solidLogicSingleton.d.ts.map +1 -1
- package/lib/logic/solidLogicSingleton.js +3 -3
- package/lib/logic/solidLogicSingleton.js.map +1 -1
- package/lib/profile/profileLogic.d.ts +3 -0
- package/lib/profile/profileLogic.d.ts.map +1 -0
- package/lib/profile/profileLogic.js +246 -0
- package/lib/profile/profileLogic.js.map +1 -0
- package/lib/typeIndex/typeIndexLogic.d.ts +2 -21
- package/lib/typeIndex/typeIndexLogic.d.ts.map +1 -1
- package/lib/typeIndex/typeIndexLogic.js +304 -296
- package/lib/typeIndex/typeIndexLogic.js.map +1 -1
- package/lib/types.d.ts +82 -1
- package/lib/types.d.ts.map +1 -1
- package/lib/util/containerLogic.d.ts +11 -0
- package/lib/util/containerLogic.d.ts.map +1 -0
- package/lib/{profile/ProfileLogic.js → util/containerLogic.js} +51 -44
- package/lib/util/containerLogic.js.map +1 -0
- package/lib/util/ns.d.ts +2 -0
- package/lib/util/ns.d.ts.map +1 -0
- package/lib/util/ns.js +34 -0
- package/lib/util/ns.js.map +1 -0
- package/lib/util/utilityLogic.d.ts +15 -0
- package/lib/util/utilityLogic.d.ts.map +1 -0
- package/lib/util/utilityLogic.js +274 -0
- package/lib/util/utilityLogic.js.map +1 -0
- package/lib/util/utils.d.ts +8 -0
- package/lib/util/utils.d.ts.map +1 -0
- package/lib/util/utils.js +48 -0
- package/lib/util/utils.js.map +1 -0
- package/package.json +3 -1
- package/src/acl/aclLogic.ts +136 -118
- package/src/authn/SolidAuthnLogic.ts +3 -2
- package/src/chat/chatLogic.ts +225 -0
- package/src/inbox/inboxLogic.ts +58 -0
- package/src/index.ts +11 -42
- package/src/logic/CustomError.ts +5 -1
- package/src/logic/solidLogic.ts +75 -0
- package/src/logic/solidLogicSingleton.ts +3 -3
- package/src/profile/profileLogic.ts +126 -0
- package/src/typeIndex/typeIndexLogic.ts +175 -182
- package/src/types.ts +83 -4
- package/src/util/containerLogic.ts +53 -0
- package/src/util/ns.ts +5 -0
- package/src/util/utilityLogic.ts +156 -0
- package/src/util/utils.ts +52 -0
- package/test/aclLogic.test.ts +13 -4
- package/test/chatLogic.test.ts +70 -71
- package/test/container.test.ts +57 -0
- package/test/helpers/dataSetup.ts +134 -0
- package/test/helpers/setup.ts +4 -0
- package/test/inboxLogic.test.ts +40 -38
- package/test/logic.test.ts +10 -9
- package/test/profileLogic.test.ts +246 -0
- package/test/typeIndexLogic.test.ts +487 -30
- package/test/utilityLogic.test.ts +172 -126
- package/test/utils.test.ts +32 -0
- package/lib/chat/ChatLogic.d.ts +0 -26
- package/lib/chat/ChatLogic.d.ts.map +0 -1
- package/lib/chat/ChatLogic.js.map +0 -1
- package/lib/chat/determineChatContainer.d.ts +0 -3
- package/lib/chat/determineChatContainer.d.ts.map +0 -1
- package/lib/chat/determineChatContainer.js +0 -12
- package/lib/chat/determineChatContainer.js.map +0 -1
- package/lib/discovery/discoveryLogic.d.ts +0 -37
- package/lib/discovery/discoveryLogic.d.ts.map +0 -1
- package/lib/discovery/discoveryLogic.js +0 -502
- package/lib/discovery/discoveryLogic.js.map +0 -1
- package/lib/inbox/InboxLogic.d.ts +0 -18
- package/lib/inbox/InboxLogic.d.ts.map +0 -1
- package/lib/inbox/InboxLogic.js.map +0 -1
- package/lib/logic/SolidLogic.d.ts +0 -45
- package/lib/logic/SolidLogic.d.ts.map +0 -1
- package/lib/logic/SolidLogic.js +0 -320
- package/lib/logic/SolidLogic.js.map +0 -1
- package/lib/profile/ProfileLogic.d.ts +0 -13
- package/lib/profile/ProfileLogic.d.ts.map +0 -1
- package/lib/profile/ProfileLogic.js.map +0 -1
- package/lib/util/UtilityLogic.d.ts +0 -27
- package/lib/util/UtilityLogic.d.ts.map +0 -1
- package/lib/util/UtilityLogic.js +0 -216
- package/lib/util/UtilityLogic.js.map +0 -1
- package/lib/util/uri.d.ts +0 -3
- package/lib/util/uri.d.ts.map +0 -1
- package/lib/util/uri.js +0 -9
- package/lib/util/uri.js.map +0 -1
- package/src/chat/ChatLogic.ts +0 -244
- package/src/chat/determineChatContainer.ts +0 -14
- package/src/discovery/discoveryLogic.ts +0 -269
- package/src/inbox/InboxLogic.ts +0 -66
- package/src/logic/SolidLogic.ts +0 -259
- package/src/profile/ProfileLogic.ts +0 -44
- package/src/util/UtilityLogic.ts +0 -144
- package/src/util/uri.ts +0 -5
- package/test/discoveryLogic.test.ts +0 -712
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { NamedNode, st, sym } from "rdflib";
|
|
2
|
+
import { CrossOriginForbiddenError, FetchError, NotEditableError, SameOriginForbiddenError, UnauthorizedError, WebOperationError } from "../logic/CustomError";
|
|
3
|
+
import * as debug from '../util/debug';
|
|
4
|
+
import { differentOrigin } from "./utils";
|
|
5
|
+
|
|
6
|
+
export function createUtilityLogic(store, aclLogic, containerLogic) {
|
|
7
|
+
|
|
8
|
+
async function recursiveDelete(containerNode: NamedNode) {
|
|
9
|
+
try {
|
|
10
|
+
if (containerLogic.isContainer(containerNode)) {
|
|
11
|
+
const aclDocUrl = await aclLogic.findAclDocUrl(containerNode)
|
|
12
|
+
await store.fetcher._fetch(aclDocUrl, { method: "DELETE" });
|
|
13
|
+
const containerMembers = await containerLogic.getContainerMembers(containerNode);
|
|
14
|
+
await Promise.all(
|
|
15
|
+
containerMembers.map((url) => recursiveDelete(url))
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
const nodeToStringHere = containerNode.value;
|
|
19
|
+
return store.fetcher._fetch(nodeToStringHere, { method: "DELETE" });
|
|
20
|
+
} catch (e) {
|
|
21
|
+
// debug.log(`Please manually remove ${url} from your system under test.`, e);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create a resource if it really does not exist
|
|
27
|
+
* Be absolutely sure something does not exist before creating a new empty file
|
|
28
|
+
* as otherwise existing could be deleted.
|
|
29
|
+
* @param doc {NamedNode} - The resource
|
|
30
|
+
*/
|
|
31
|
+
async function loadOrCreateIfNotExists(doc: NamedNode) {
|
|
32
|
+
let response
|
|
33
|
+
try {
|
|
34
|
+
response = await store.fetcher.load(doc)
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (err.response.status === 404) {
|
|
37
|
+
try {
|
|
38
|
+
await store.fetcher.webOperation('PUT', doc, { data: '', contentType: 'text/turtle' })
|
|
39
|
+
} catch (err) {
|
|
40
|
+
const msg = 'createIfNotExists: PUT FAILED: ' + doc + ': ' + err
|
|
41
|
+
throw new WebOperationError(msg)
|
|
42
|
+
}
|
|
43
|
+
await store.fetcher.load(doc)
|
|
44
|
+
} else {
|
|
45
|
+
if (err.response.status === 401) {
|
|
46
|
+
throw new UnauthorizedError();
|
|
47
|
+
}
|
|
48
|
+
if (err.response.status === 403) {
|
|
49
|
+
if (differentOrigin(doc)) {
|
|
50
|
+
throw new CrossOriginForbiddenError();
|
|
51
|
+
}
|
|
52
|
+
throw new SameOriginForbiddenError();
|
|
53
|
+
}
|
|
54
|
+
const msg = 'createIfNotExists doc load error NOT 404: ' + doc + ': ' + err
|
|
55
|
+
throw new FetchError(err.status, err.message + msg)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return response
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Follow link from this doc to another thing, or else make a new link
|
|
62
|
+
**
|
|
63
|
+
** @returns existing object, or creates it if non existent
|
|
64
|
+
*/
|
|
65
|
+
async function followOrCreateLink(subject: NamedNode, predicate: NamedNode,
|
|
66
|
+
object: NamedNode, doc: NamedNode
|
|
67
|
+
): Promise<NamedNode | null> {
|
|
68
|
+
await store.fetcher.load(doc)
|
|
69
|
+
const result = store.any(subject, predicate, null, doc)
|
|
70
|
+
|
|
71
|
+
if (result) return result as NamedNode
|
|
72
|
+
if (!store.updater.editable(doc)) {
|
|
73
|
+
const msg = `followOrCreateLink: cannot edit ${doc.value}`
|
|
74
|
+
debug.warn(msg)
|
|
75
|
+
throw new NotEditableError(msg)
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
await store.updater.update([], [st(subject, predicate, object, doc)])
|
|
79
|
+
} catch (err) {
|
|
80
|
+
const msg = `followOrCreateLink: Error making link in ${doc} to ${object}: ${err}`
|
|
81
|
+
debug.warn(msg)
|
|
82
|
+
throw new WebOperationError(err)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
await loadOrCreateIfNotExists(object)
|
|
87
|
+
// store.fetcher.webOperation('PUT', object, { data: '', contentType: 'text/turtle'})
|
|
88
|
+
} catch (err) {
|
|
89
|
+
debug.warn(`followOrCreateLink: Error loading or saving new linked document: ${object}: ${err}`)
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
return object
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Copied from https://github.com/solidos/web-access-control-tests/blob/v3.0.0/test/surface/delete.test.ts#L5
|
|
96
|
+
async function setSinglePeerAccess(options: {
|
|
97
|
+
ownerWebId: string,
|
|
98
|
+
peerWebId: string,
|
|
99
|
+
accessToModes?: string,
|
|
100
|
+
defaultModes?: string,
|
|
101
|
+
target: string
|
|
102
|
+
}) {
|
|
103
|
+
let str = [
|
|
104
|
+
'@prefix acl: <http://www.w3.org/ns/auth/acl#>.',
|
|
105
|
+
'',
|
|
106
|
+
`<#alice> a acl:Authorization;\n acl:agent <${options.ownerWebId}>;`,
|
|
107
|
+
` acl:accessTo <${options.target}>;`,
|
|
108
|
+
` acl:default <${options.target}>;`,
|
|
109
|
+
' acl:mode acl:Read, acl:Write, acl:Control.',
|
|
110
|
+
''
|
|
111
|
+
].join('\n')
|
|
112
|
+
if (options.accessToModes) {
|
|
113
|
+
str += [
|
|
114
|
+
'<#bobAccessTo> a acl:Authorization;',
|
|
115
|
+
` acl:agent <${options.peerWebId}>;`,
|
|
116
|
+
` acl:accessTo <${options.target}>;`,
|
|
117
|
+
` acl:mode ${options.accessToModes}.`,
|
|
118
|
+
''
|
|
119
|
+
].join('\n')
|
|
120
|
+
}
|
|
121
|
+
if (options.defaultModes) {
|
|
122
|
+
str += [
|
|
123
|
+
'<#bobDefault> a acl:Authorization;',
|
|
124
|
+
` acl:agent <${options.peerWebId}>;`,
|
|
125
|
+
` acl:default <${options.target}>;`,
|
|
126
|
+
` acl:mode ${options.defaultModes}.`,
|
|
127
|
+
''
|
|
128
|
+
].join('\n')
|
|
129
|
+
}
|
|
130
|
+
const aclDocUrl = await aclLogic.findAclDocUrl(sym(options.target))
|
|
131
|
+
return store.fetcher._fetch(aclDocUrl, {
|
|
132
|
+
method: 'PUT',
|
|
133
|
+
body: str,
|
|
134
|
+
headers: [
|
|
135
|
+
['Content-Type', 'text/turtle']
|
|
136
|
+
]
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function createEmptyRdfDoc(doc: NamedNode, comment: string) {
|
|
141
|
+
await store.fetcher.webOperation("PUT", doc.uri, {
|
|
142
|
+
data: `# ${new Date()} ${comment}
|
|
143
|
+
`,
|
|
144
|
+
contentType: "text/turtle",
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
recursiveDelete,
|
|
150
|
+
setSinglePeerAccess,
|
|
151
|
+
createEmptyRdfDoc,
|
|
152
|
+
followOrCreateLink,
|
|
153
|
+
loadOrCreateIfNotExists
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { NamedNode, sym } from "rdflib";
|
|
2
|
+
|
|
3
|
+
export function newThing(doc: NamedNode): NamedNode {
|
|
4
|
+
return sym(doc.uri + "#" + "id" + ("" + Date.now()));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function uniqueNodes (arr: NamedNode[]): NamedNode[] {
|
|
8
|
+
const uris = arr.map(x => x.uri)
|
|
9
|
+
const set = new Set(uris)
|
|
10
|
+
const uris2 = Array.from(set)
|
|
11
|
+
const arr2 = uris2.map(u => new NamedNode(u))
|
|
12
|
+
return arr2 // Array.from(new Set(arr.map(x => x.uri))).map(u => sym(u))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getArchiveUrl(baseUrl: string, date: Date) {
|
|
16
|
+
const year = date.getUTCFullYear();
|
|
17
|
+
const month = ('0' + (date.getUTCMonth()+1)).slice(-2);
|
|
18
|
+
const day = ('0' + (date.getUTCDate())).slice(-2);
|
|
19
|
+
const parts = baseUrl.split('/');
|
|
20
|
+
const filename = parts[parts.length -1 ];
|
|
21
|
+
return new URL(`./archive/${year}/${month}/${day}/${filename}`, baseUrl).toString();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function differentOrigin(doc): boolean {
|
|
25
|
+
if (!doc) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return (
|
|
29
|
+
`${window.location.origin}/` !== new URL(doc.value).origin
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function suggestPreferencesFile (me:NamedNode) {
|
|
34
|
+
const stripped = me.uri.replace('/profile/', '/').replace('/public/', '/')
|
|
35
|
+
// const stripped = me.uri.replace(\/[p|P]rofile/\g, '/').replace(\/[p|P]ublic/\g, '/')
|
|
36
|
+
const folderURI = stripped.split('/').slice(0,-1).join('/') + '/Settings/'
|
|
37
|
+
const fileURI = folderURI + 'Preferences.ttl'
|
|
38
|
+
return sym(fileURI)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function determineChatContainer(
|
|
42
|
+
invitee: NamedNode,
|
|
43
|
+
podRoot: NamedNode
|
|
44
|
+
): NamedNode {
|
|
45
|
+
// Create chat
|
|
46
|
+
// See https://gitter.im/solid/chat-app?at=5f3c800f855be416a23ae74a
|
|
47
|
+
const chatContainerStr = new URL(
|
|
48
|
+
`IndividualChats/${new URL(invitee.value).host}/`,
|
|
49
|
+
podRoot.value
|
|
50
|
+
).toString();
|
|
51
|
+
return new NamedNode(chatContainerStr);
|
|
52
|
+
}
|
package/test/aclLogic.test.ts
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
|
-
import { sym } from 'rdflib'
|
|
2
|
-
import {
|
|
1
|
+
import { Fetcher, Store, sym, UpdateManager } from 'rdflib';
|
|
2
|
+
import { createAclLogic } from '../src/acl/aclLogic';
|
|
3
3
|
|
|
4
4
|
describe('setACLUserPublic', () => {
|
|
5
|
+
let aclLogic
|
|
6
|
+
let store
|
|
7
|
+
beforeAll(() => {
|
|
8
|
+
const options = { fetch: fetch };
|
|
9
|
+
store = new Store()
|
|
10
|
+
store.fetcher = new Fetcher(store, options);
|
|
11
|
+
store.updater = new UpdateManager(store);
|
|
12
|
+
aclLogic = createAclLogic(store)
|
|
13
|
+
})
|
|
5
14
|
it('exists', () => {
|
|
6
|
-
expect(setACLUserPublic).toBeInstanceOf(Function)
|
|
15
|
+
expect(aclLogic.setACLUserPublic).toBeInstanceOf(Function)
|
|
7
16
|
})
|
|
8
17
|
it.skip('runs', async () => {
|
|
9
|
-
expect(await setACLUserPublic(
|
|
18
|
+
expect(await aclLogic.setACLUserPublic(
|
|
10
19
|
'https://test.test#',
|
|
11
20
|
sym('https://test.test#'),
|
|
12
21
|
{}
|
package/test/chatLogic.test.ts
CHANGED
|
@@ -1,34 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*
|
|
4
|
+
*/
|
|
5
|
+
import { UpdateManager, Store, Fetcher } from "rdflib";
|
|
6
|
+
import { createAclLogic } from "../src/acl/aclLogic";
|
|
7
|
+
import { createChatLogic } from '../src/chat/chatLogic';
|
|
8
|
+
import { createProfileLogic } from "../src/profile/profileLogic";
|
|
9
|
+
import { createContainerLogic } from "../src/util/containerLogic";
|
|
10
|
+
import { createUtilityLogic } from "../src/util/utilityLogic";
|
|
11
|
+
import { alice, bob } from "./helpers/dataSetup";
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const alice = rdf.sym("https://alice.example/profile/card#me");
|
|
12
|
-
const bob = rdf.sym("https://bob.example/profile/card#me");
|
|
13
|
+
window.$SolidTestEnvironment = { username: alice.uri }
|
|
13
14
|
|
|
14
15
|
describe("Chat logic", () => {
|
|
15
|
-
let
|
|
16
|
+
let chatLogic;
|
|
16
17
|
let store;
|
|
17
18
|
beforeEach(() => {
|
|
18
19
|
fetchMock.resetMocks();
|
|
19
20
|
fetchMock.mockResponse("Not Found", {
|
|
20
21
|
status: 404,
|
|
21
22
|
});
|
|
22
|
-
store =
|
|
23
|
-
store.fetcher =
|
|
23
|
+
store = new Store()
|
|
24
|
+
store.fetcher = new Fetcher(store, { fetch: fetch });
|
|
24
25
|
store.updater = new UpdateManager(store);
|
|
25
26
|
const authn = {
|
|
26
27
|
currentUser: () => {
|
|
27
28
|
return alice;
|
|
28
29
|
},
|
|
29
30
|
};
|
|
30
|
-
const
|
|
31
|
-
|
|
31
|
+
const util = createUtilityLogic(store, createAclLogic(store), createContainerLogic(store))
|
|
32
|
+
chatLogic = createChatLogic(store, createProfileLogic(store, authn, util))
|
|
32
33
|
});
|
|
33
34
|
|
|
34
35
|
describe("get chat, without creating", () => {
|
|
@@ -37,19 +38,19 @@ describe("Chat logic", () => {
|
|
|
37
38
|
beforeEach(async () => {
|
|
38
39
|
aliceHasValidProfile();
|
|
39
40
|
noChatWithBobExists();
|
|
40
|
-
result = await
|
|
41
|
+
result = await chatLogic.getChat(bob, false);
|
|
41
42
|
});
|
|
42
43
|
it("does not return a chat", async () => {
|
|
43
44
|
expect(result).toBeNull();
|
|
44
45
|
});
|
|
45
46
|
it("loaded the current user profile", () => {
|
|
46
47
|
expect(fetchMock.mock.calls[0][0]).toBe(
|
|
47
|
-
"https://alice.example/profile/card"
|
|
48
|
+
"https://alice.example.com/profile/card.ttl"
|
|
48
49
|
);
|
|
49
50
|
});
|
|
50
51
|
it("tried to load the chat document", () => {
|
|
51
52
|
expect(fetchMock.mock.calls[1][0]).toBe(
|
|
52
|
-
"https://alice.example/IndividualChats/bob.example/index.ttl"
|
|
53
|
+
"https://alice.example.com/IndividualChats/bob.example.com/index.ttl"
|
|
53
54
|
);
|
|
54
55
|
});
|
|
55
56
|
it("has no additional fetch requests", () => {
|
|
@@ -74,23 +75,23 @@ describe("Chat logic", () => {
|
|
|
74
75
|
chatContainerAclCanBeSet();
|
|
75
76
|
editablePrivateTypeIndexIsFound();
|
|
76
77
|
privateTypeIndexIsUpdated();
|
|
77
|
-
result = await
|
|
78
|
+
result = await chatLogic.getChat(bob, true);
|
|
78
79
|
});
|
|
79
|
-
it("returns the chat URI based on the invitee's WebID",
|
|
80
|
+
it("returns the chat URI based on the invitee's WebID", () => {
|
|
80
81
|
expect(result.uri).toBe(
|
|
81
|
-
"https://alice.example/IndividualChats/bob.example/index.ttl#this"
|
|
82
|
+
"https://alice.example.com/IndividualChats/bob.example.com/index.ttl#this"
|
|
82
83
|
);
|
|
83
84
|
});
|
|
84
85
|
it("created a chat document", () => {
|
|
85
86
|
const request = getRequestTo(
|
|
86
87
|
"PUT",
|
|
87
|
-
"https://alice.example/IndividualChats/bob.example/index.ttl"
|
|
88
|
+
"https://alice.example.com/IndividualChats/bob.example.com/index.ttl"
|
|
88
89
|
);
|
|
89
90
|
expect(request.body).toBe(`@prefix : <#>.
|
|
90
91
|
@prefix dc: <http://purl.org/dc/elements/1.1/>.
|
|
91
92
|
@prefix meeting: <http://www.w3.org/ns/pim/meeting#>.
|
|
92
93
|
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
|
|
93
|
-
@prefix c: </profile/card#>.
|
|
94
|
+
@prefix c: </profile/card.ttl#>.
|
|
94
95
|
|
|
95
96
|
:this
|
|
96
97
|
a meeting:LongChat;
|
|
@@ -102,41 +103,42 @@ describe("Chat logic", () => {
|
|
|
102
103
|
it("allowed Bob to participate in the chat by adding an ACL", () => {
|
|
103
104
|
const request = getRequestTo(
|
|
104
105
|
"PUT",
|
|
105
|
-
"https://alice.example/IndividualChats/bob.example/.acl"
|
|
106
|
+
"https://alice.example.com/IndividualChats/bob.example.com/.acl"
|
|
106
107
|
);
|
|
107
108
|
expect(request.body).toBe(`
|
|
108
|
-
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
|
|
109
|
-
<#owner>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
<#invitee>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
`);
|
|
109
|
+
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
|
|
110
|
+
<#owner>
|
|
111
|
+
a acl:Authorization;
|
|
112
|
+
acl:agent <https://alice.example.com/profile/card.ttl#me>;
|
|
113
|
+
acl:accessTo <.>;
|
|
114
|
+
acl:default <.>;
|
|
115
|
+
acl:mode
|
|
116
|
+
acl:Read, acl:Write, acl:Control.
|
|
117
|
+
<#invitee>
|
|
118
|
+
a acl:Authorization;
|
|
119
|
+
acl:agent <https://bob.example.com/profile/card.ttl#me>;
|
|
120
|
+
acl:accessTo <.>;
|
|
121
|
+
acl:default <.>;
|
|
122
|
+
acl:mode
|
|
123
|
+
acl:Read, acl:Append.
|
|
124
|
+
`);
|
|
124
125
|
});
|
|
125
126
|
it("sent an invitation to invitee inbox", () => {
|
|
126
|
-
const request = getRequestTo("POST", "https://bob.example/inbox");
|
|
127
|
+
const request = getRequestTo("POST", "https://bob.example.com/inbox");
|
|
127
128
|
expect(request.body).toContain(`
|
|
128
|
-
<> a <http://www.w3.org/ns/pim/meeting#LongChatInvite> ;
|
|
129
|
-
<http://www.w3.org/1999/02/22-rdf-syntax-ns#seeAlso> <https://alice.example/IndividualChats/bob.example/index.ttl#this> .
|
|
130
|
-
|
|
129
|
+
<> a <http://www.w3.org/ns/pim/meeting#LongChatInvite> ;
|
|
130
|
+
<http://www.w3.org/1999/02/22-rdf-syntax-ns#seeAlso> <https://alice.example.com/IndividualChats/bob.example.com/index.ttl#this> .
|
|
131
|
+
`);
|
|
132
|
+
});
|
|
131
133
|
it("added the new chat to private type index", () => {
|
|
132
134
|
const request = getRequestTo(
|
|
133
135
|
"PATCH",
|
|
134
|
-
"https://alice.example/settings/privateTypeIndex.ttl"
|
|
136
|
+
"https://alice.example.com/settings/privateTypeIndex.ttl"
|
|
135
137
|
);
|
|
136
138
|
expect(request.body)
|
|
137
|
-
.toBe(`INSERT DATA { <https://alice.example/settings/privateTypeIndex.ttl#id1612606272000> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/solid/terms#TypeRegistration> .
|
|
138
|
-
<https://alice.example/settings/privateTypeIndex.ttl#id1612606272000> <http://www.w3.org/ns/solid/terms#forClass> <http://www.w3.org/ns/pim/meeting#LongChat> .
|
|
139
|
-
<https://alice.example/settings/privateTypeIndex.ttl#id1612606272000> <http://www.w3.org/ns/solid/terms#instance> <https://alice.example/IndividualChats/bob.example/index.ttl#this> .
|
|
139
|
+
.toBe(`INSERT DATA { <https://alice.example.com/settings/privateTypeIndex.ttl#id1612606272000> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/solid/terms#TypeRegistration> .
|
|
140
|
+
<https://alice.example.com/settings/privateTypeIndex.ttl#id1612606272000> <http://www.w3.org/ns/solid/terms#forClass> <http://www.w3.org/ns/pim/meeting#LongChat> .
|
|
141
|
+
<https://alice.example.com/settings/privateTypeIndex.ttl#id1612606272000> <http://www.w3.org/ns/solid/terms#instance> <https://alice.example.com/IndividualChats/bob.example.com/index.ttl#this> .
|
|
140
142
|
}
|
|
141
143
|
`);
|
|
142
144
|
});
|
|
@@ -148,13 +150,13 @@ describe("Chat logic", () => {
|
|
|
148
150
|
|
|
149
151
|
describe("possible errors", () => {
|
|
150
152
|
it("profile does not link to storage", async () => {
|
|
151
|
-
fetchMock.mockOnceIf("https://alice.example/profile/card", "<><><>.", {
|
|
153
|
+
fetchMock.mockOnceIf("https://alice.example.com/profile/card.ttl", "<><><>.", {
|
|
152
154
|
headers: {
|
|
153
155
|
"Content-Type": "text/turtle",
|
|
154
156
|
},
|
|
155
157
|
});
|
|
156
158
|
const expectedError = new Error("User pod root not found!");
|
|
157
|
-
await expect(
|
|
159
|
+
await expect(chatLogic.getChat(bob, false)).rejects.toEqual(expectedError);
|
|
158
160
|
});
|
|
159
161
|
|
|
160
162
|
it("invitee inbox not found", async () => {
|
|
@@ -163,19 +165,19 @@ describe("Chat logic", () => {
|
|
|
163
165
|
chatWithBobCanBeCreated();
|
|
164
166
|
bobDoesNotHaveAnInbox();
|
|
165
167
|
const expectedError = new Error(
|
|
166
|
-
"Invitee inbox not found! https://bob.example/profile/card#me"
|
|
168
|
+
"Invitee inbox not found! https://bob.example.com/profile/card.ttl#me"
|
|
167
169
|
);
|
|
168
|
-
await expect(
|
|
170
|
+
await expect(chatLogic.getChat(bob, true)).rejects.toEqual(expectedError);
|
|
169
171
|
});
|
|
170
172
|
});
|
|
171
173
|
|
|
172
174
|
function aliceHasValidProfile() {
|
|
173
175
|
fetchMock.mockOnceIf(
|
|
174
|
-
"https://alice.example/profile/card",
|
|
176
|
+
"https://alice.example.com/profile/card.ttl",
|
|
175
177
|
`
|
|
176
|
-
<https://alice.example/profile/card#me>
|
|
177
|
-
<http://www.w3.org/ns/pim/space#storage> <https://alice.example/> ;
|
|
178
|
-
<http://www.w3.org/ns/solid/terms#privateTypeIndex> <https://alice.example/settings/privateTypeIndex.ttl> ;
|
|
178
|
+
<https://alice.example.com/profile/card.ttl#me>
|
|
179
|
+
<http://www.w3.org/ns/pim/space#storage> <https://alice.example.com/> ;
|
|
180
|
+
<http://www.w3.org/ns/solid/terms#privateTypeIndex> <https://alice.example.com/settings/privateTypeIndex.ttl> ;
|
|
179
181
|
.`,
|
|
180
182
|
{
|
|
181
183
|
headers: {
|
|
@@ -188,7 +190,7 @@ describe("Chat logic", () => {
|
|
|
188
190
|
function noChatWithBobExists() {
|
|
189
191
|
return fetchMock.mockOnceIf(
|
|
190
192
|
({ url, method }) =>
|
|
191
|
-
url === "https://alice.example/IndividualChats/bob.example/index.ttl" &&
|
|
193
|
+
url === "https://alice.example.com/IndividualChats/bob.example.com/index.ttl" &&
|
|
192
194
|
method === "GET",
|
|
193
195
|
"Not found",
|
|
194
196
|
{
|
|
@@ -200,7 +202,7 @@ describe("Chat logic", () => {
|
|
|
200
202
|
function chatWithBobCanBeCreated() {
|
|
201
203
|
return fetchMock.mockOnceIf(
|
|
202
204
|
({ url, method }) =>
|
|
203
|
-
url === "https://alice.example/IndividualChats/bob.example/index.ttl" &&
|
|
205
|
+
url === "https://alice.example.com/IndividualChats/bob.example.com/index.ttl" &&
|
|
204
206
|
method === "PUT",
|
|
205
207
|
"Created",
|
|
206
208
|
{
|
|
@@ -211,8 +213,8 @@ describe("Chat logic", () => {
|
|
|
211
213
|
|
|
212
214
|
function bobHasAnInbox() {
|
|
213
215
|
fetchMock.mockOnceIf(
|
|
214
|
-
"https://bob.example/profile/card",
|
|
215
|
-
"<https://bob.example/profile/card#me><http://www.w3.org/ns/ldp#inbox><https://bob.example/inbox>.",
|
|
216
|
+
"https://bob.example.com/profile/card.ttl",
|
|
217
|
+
"<https://bob.example.com/profile/card.ttl#me><http://www.w3.org/ns/ldp#inbox><https://bob.example.com/inbox>.",
|
|
216
218
|
{
|
|
217
219
|
headers: { "Content-Type": "text/turtle" },
|
|
218
220
|
}
|
|
@@ -220,7 +222,7 @@ describe("Chat logic", () => {
|
|
|
220
222
|
}
|
|
221
223
|
|
|
222
224
|
function bobDoesNotHaveAnInbox() {
|
|
223
|
-
fetchMock.mockOnceIf("https://bob.example/profile/card", "<><><>.", {
|
|
225
|
+
fetchMock.mockOnceIf("https://bob.example.com/profile/card.ttl", "<><><>.", {
|
|
224
226
|
headers: {
|
|
225
227
|
"Content-Type": "text/turtle",
|
|
226
228
|
},
|
|
@@ -230,13 +232,13 @@ describe("Chat logic", () => {
|
|
|
230
232
|
function invitationCanBeSent() {
|
|
231
233
|
return fetchMock.mockOnceIf(
|
|
232
234
|
({ url, method }) =>
|
|
233
|
-
url === "https://bob.example/inbox" && method === "POST",
|
|
235
|
+
url === "https://bob.example.com/inbox" && method === "POST",
|
|
234
236
|
"Created",
|
|
235
237
|
{
|
|
236
238
|
status: 201,
|
|
237
239
|
headers: {
|
|
238
240
|
location:
|
|
239
|
-
"https://bob.example/inbox/22373339-6cc0-49fc-b69e-0402edda6e4e.ttl",
|
|
241
|
+
"https://bob.example.com/inbox/22373339-6cc0-49fc-b69e-0402edda6e4e.ttl",
|
|
240
242
|
},
|
|
241
243
|
}
|
|
242
244
|
);
|
|
@@ -245,7 +247,7 @@ describe("Chat logic", () => {
|
|
|
245
247
|
function chatContainerIsFound() {
|
|
246
248
|
return fetchMock.mockOnceIf(
|
|
247
249
|
({ url, method }) =>
|
|
248
|
-
url === "https://alice.example/IndividualChats/bob.example/" &&
|
|
250
|
+
url === "https://alice.example.com/IndividualChats/bob.example.com/" &&
|
|
249
251
|
method === "GET",
|
|
250
252
|
"<><><>.",
|
|
251
253
|
{
|
|
@@ -261,7 +263,7 @@ describe("Chat logic", () => {
|
|
|
261
263
|
function chatContainerAclCanBeSet() {
|
|
262
264
|
return fetchMock.mockOnceIf(
|
|
263
265
|
({ url, method }) =>
|
|
264
|
-
url === "https://alice.example/IndividualChats/bob.example/.acl" &&
|
|
266
|
+
url === "https://alice.example.com/IndividualChats/bob.example.com/.acl" &&
|
|
265
267
|
method === "PUT",
|
|
266
268
|
"Created",
|
|
267
269
|
{
|
|
@@ -273,7 +275,7 @@ describe("Chat logic", () => {
|
|
|
273
275
|
function editablePrivateTypeIndexIsFound() {
|
|
274
276
|
return fetchMock.mockOnceIf(
|
|
275
277
|
({ url, method }) =>
|
|
276
|
-
url === "https://alice.example/settings/privateTypeIndex.ttl" &&
|
|
278
|
+
url === "https://alice.example.com/settings/privateTypeIndex.ttl" &&
|
|
277
279
|
method === "GET",
|
|
278
280
|
"<><><>.",
|
|
279
281
|
{
|
|
@@ -290,7 +292,7 @@ describe("Chat logic", () => {
|
|
|
290
292
|
function privateTypeIndexIsUpdated() {
|
|
291
293
|
return fetchMock.mockOnceIf(
|
|
292
294
|
({ url, method }) =>
|
|
293
|
-
url === "https://alice.example/settings/privateTypeIndex.ttl" &&
|
|
295
|
+
url === "https://alice.example.com/settings/privateTypeIndex.ttl" &&
|
|
294
296
|
method === "PATCH",
|
|
295
297
|
"OK",
|
|
296
298
|
{
|
|
@@ -299,9 +301,6 @@ describe("Chat logic", () => {
|
|
|
299
301
|
);
|
|
300
302
|
}
|
|
301
303
|
|
|
302
|
-
/*
|
|
303
|
-
|
|
304
|
-
*/
|
|
305
304
|
function getRequestTo(
|
|
306
305
|
method: "GET" | "PUT" | "POST" | "DELETE" | "PATCH",
|
|
307
306
|
url: string
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*
|
|
4
|
+
*/
|
|
5
|
+
import { UpdateManager, Store, Fetcher, sym } from "rdflib";
|
|
6
|
+
import { createContainerLogic } from "../src/util/containerLogic";
|
|
7
|
+
import { alice } from "./helpers/dataSetup";
|
|
8
|
+
|
|
9
|
+
window.$SolidTestEnvironment = { username: alice.uri }
|
|
10
|
+
|
|
11
|
+
describe("Container", () => {
|
|
12
|
+
let store
|
|
13
|
+
let containerLogic
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
fetchMock.resetMocks()
|
|
16
|
+
store = new Store()
|
|
17
|
+
store.fetcher = new Fetcher(store, { fetch: fetch });
|
|
18
|
+
store.updater = new UpdateManager(store);
|
|
19
|
+
containerLogic = createContainerLogic(store)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it("getContainerMembers - When container has some containment triples", async () => {
|
|
23
|
+
containerHasSomeContainmentTriples()
|
|
24
|
+
const containerMembers = await containerLogic.getContainerMembers(sym('https://container.com/'));
|
|
25
|
+
const result = containerMembers.map(oneResult => oneResult.value)
|
|
26
|
+
expect(result.sort()).toEqual([
|
|
27
|
+
'https://container.com/foo.txt',
|
|
28
|
+
'https://container.com/bar/'
|
|
29
|
+
].sort());
|
|
30
|
+
});
|
|
31
|
+
it("getContainerMembers- When container is empty - Resolves to an empty array", async () => {
|
|
32
|
+
containerIsEmpty();
|
|
33
|
+
const result = await containerLogic.getContainerMembers(sym('https://container.com/'));
|
|
34
|
+
expect(result).toEqual([]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
function containerIsEmpty() {
|
|
38
|
+
fetchMock.mockOnceIf(
|
|
39
|
+
"https://com/",
|
|
40
|
+
" ", // FIXME: https://github.com/jefflau/jest-fetch-mock/issues/189
|
|
41
|
+
{
|
|
42
|
+
headers: { "Content-Type": "text/turtle" },
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function containerHasSomeContainmentTriples() {
|
|
48
|
+
fetchMock.mockOnceIf(
|
|
49
|
+
"https://container.com/",
|
|
50
|
+
"<.> <http://www.w3.org/ns/ldp#contains> <./foo.txt>, <./bar/> .",
|
|
51
|
+
{
|
|
52
|
+
headers: { "Content-Type": "text/turtle" },
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
})
|