solid-logic 1.3.17-9d25ceb7 → 1.3.17-a849582e

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.
Files changed (114) hide show
  1. package/lib/acl/aclLogic.d.ts +12 -30
  2. package/lib/acl/aclLogic.d.ts.map +1 -1
  3. package/lib/acl/aclLogic.js +151 -116
  4. package/lib/acl/aclLogic.js.map +1 -1
  5. package/lib/authn/SolidAuthnLogic.d.ts.map +1 -1
  6. package/lib/authn/SolidAuthnLogic.js +2 -2
  7. package/lib/authn/SolidAuthnLogic.js.map +1 -1
  8. package/lib/chat/chatLogic.d.ts +16 -0
  9. package/lib/chat/chatLogic.d.ts.map +1 -0
  10. package/lib/chat/{ChatLogic.js → chatLogic.js} +82 -87
  11. package/lib/chat/chatLogic.js.map +1 -0
  12. package/lib/discovery/discoveryLogic.d.ts +8 -8
  13. package/lib/discovery/discoveryLogic.d.ts.map +1 -1
  14. package/lib/discovery/discoveryLogic.js +65 -72
  15. package/lib/discovery/discoveryLogic.js.map +1 -1
  16. package/lib/inbox/inboxLogic.d.ts +7 -0
  17. package/lib/inbox/inboxLogic.d.ts.map +1 -0
  18. package/lib/inbox/{InboxLogic.js → inboxLogic.js} +58 -64
  19. package/lib/inbox/inboxLogic.js.map +1 -0
  20. package/lib/index.d.ts +10 -13
  21. package/lib/index.d.ts.map +1 -1
  22. package/lib/index.js +76 -32
  23. package/lib/index.js.map +1 -1
  24. package/lib/logic/CustomError.d.ts +4 -0
  25. package/lib/logic/CustomError.d.ts.map +1 -1
  26. package/lib/logic/CustomError.js +17 -1
  27. package/lib/logic/CustomError.js.map +1 -1
  28. package/lib/logic/solidLogicSingleton.d.ts +35 -3
  29. package/lib/logic/solidLogicSingleton.d.ts.map +1 -1
  30. package/lib/logic/solidLogicSingleton.js +87 -8
  31. package/lib/logic/solidLogicSingleton.js.map +1 -1
  32. package/lib/profile/profileLogic.d.ts +13 -0
  33. package/lib/profile/profileLogic.d.ts.map +1 -0
  34. package/lib/profile/profileLogic.js +268 -0
  35. package/lib/profile/profileLogic.js.map +1 -0
  36. package/lib/typeIndex/typeIndexLogic.d.ts +31 -21
  37. package/lib/typeIndex/typeIndexLogic.d.ts.map +1 -1
  38. package/lib/typeIndex/typeIndexLogic.js +650 -295
  39. package/lib/typeIndex/typeIndexLogic.js.map +1 -1
  40. package/lib/types.d.ts +17 -0
  41. package/lib/types.d.ts.map +1 -1
  42. package/lib/util/containerLogic.d.ts +11 -0
  43. package/lib/util/containerLogic.d.ts.map +1 -0
  44. package/lib/{profile/ProfileLogic.js → util/containerLogic.js} +53 -44
  45. package/lib/util/containerLogic.js.map +1 -0
  46. package/lib/util/utilityLogic.d.ts +15 -0
  47. package/lib/util/utilityLogic.d.ts.map +1 -0
  48. package/lib/util/utilityLogic.js +272 -0
  49. package/lib/util/utilityLogic.js.map +1 -0
  50. package/lib/util/utils.d.ts +8 -0
  51. package/lib/util/utils.d.ts.map +1 -0
  52. package/lib/util/utils.js +48 -0
  53. package/lib/util/utils.js.map +1 -0
  54. package/package.json +3 -1
  55. package/src/acl/aclLogic.ts +135 -119
  56. package/src/authn/SolidAuthnLogic.ts +3 -2
  57. package/src/chat/chatLogic.ts +225 -0
  58. package/src/discovery/discoveryLogic.ts +66 -87
  59. package/src/inbox/inboxLogic.ts +57 -0
  60. package/src/index.ts +74 -21
  61. package/src/logic/CustomError.ts +5 -1
  62. package/src/logic/solidLogicSingleton.ts +160 -7
  63. package/src/profile/profileLogic.ts +134 -0
  64. package/src/typeIndex/typeIndexLogic.ts +417 -153
  65. package/src/types.ts +7 -3
  66. package/src/util/containerLogic.ts +54 -0
  67. package/src/util/ns.js +5 -0
  68. package/src/util/utilityLogic.ts +155 -0
  69. package/src/util/utils.ts +52 -0
  70. package/test/aclLogic.test.ts +13 -4
  71. package/test/chatLogic.test.ts +70 -71
  72. package/test/container.test.ts +56 -0
  73. package/test/discoveryLogic.test.ts +14 -14
  74. package/test/helpers/dataSetup.ts +134 -0
  75. package/test/helpers/setup.ts +4 -0
  76. package/test/inboxLogic.test.ts +39 -38
  77. package/test/logic.test.ts +11 -9
  78. package/test/profileLogic.test.ts +246 -0
  79. package/test/typeIndexLogic.test.ts +49 -22
  80. package/test/typeIndexLogicPart2.test.ts +485 -0
  81. package/test/utilityLogic.test.ts +172 -126
  82. package/test/utils.test.ts +32 -0
  83. package/lib/chat/ChatLogic.d.ts +0 -26
  84. package/lib/chat/ChatLogic.d.ts.map +0 -1
  85. package/lib/chat/ChatLogic.js.map +0 -1
  86. package/lib/chat/determineChatContainer.d.ts +0 -3
  87. package/lib/chat/determineChatContainer.d.ts.map +0 -1
  88. package/lib/chat/determineChatContainer.js +0 -12
  89. package/lib/chat/determineChatContainer.js.map +0 -1
  90. package/lib/inbox/InboxLogic.d.ts +0 -18
  91. package/lib/inbox/InboxLogic.d.ts.map +0 -1
  92. package/lib/inbox/InboxLogic.js.map +0 -1
  93. package/lib/logic/SolidLogic.d.ts +0 -48
  94. package/lib/logic/SolidLogic.d.ts.map +0 -1
  95. package/lib/logic/SolidLogic.js +0 -321
  96. package/lib/logic/SolidLogic.js.map +0 -1
  97. package/lib/profile/ProfileLogic.d.ts +0 -13
  98. package/lib/profile/ProfileLogic.d.ts.map +0 -1
  99. package/lib/profile/ProfileLogic.js.map +0 -1
  100. package/lib/util/UtilityLogic.d.ts +0 -33
  101. package/lib/util/UtilityLogic.d.ts.map +0 -1
  102. package/lib/util/UtilityLogic.js +0 -240
  103. package/lib/util/UtilityLogic.js.map +0 -1
  104. package/lib/util/uri.d.ts +0 -3
  105. package/lib/util/uri.d.ts.map +0 -1
  106. package/lib/util/uri.js +0 -9
  107. package/lib/util/uri.js.map +0 -1
  108. package/src/chat/ChatLogic.ts +0 -244
  109. package/src/chat/determineChatContainer.ts +0 -14
  110. package/src/inbox/InboxLogic.ts +0 -66
  111. package/src/logic/SolidLogic.ts +0 -262
  112. package/src/profile/ProfileLogic.ts +0 -44
  113. package/src/util/UtilityLogic.ts +0 -161
  114. package/src/util/uri.ts +0 -5
@@ -0,0 +1,54 @@
1
+ import { NamedNode, Statement, sym } from "rdflib";
2
+
3
+ /**
4
+ * Container-related class
5
+ */
6
+ export function createContainerLogic(store) {
7
+
8
+ function getContainerElements(containerNode: NamedNode): NamedNode[] {
9
+ return store
10
+ .statementsMatching(
11
+ containerNode,
12
+ sym("http://www.w3.org/ns/ldp#contains"),
13
+ undefined,
14
+ containerNode.doc()
15
+ )
16
+ .map((st: Statement) => st.object as NamedNode);
17
+ }
18
+
19
+ function isContainer(url: string) {
20
+ return url.charAt(url.length - 1) === "/";
21
+ }
22
+
23
+ async function createContainer(url: string) {
24
+ if (!isContainer(url)) {
25
+ throw new Error(`Not a container URL ${url}`);
26
+ }
27
+ // Copied from https://github.com/solidos/solid-crud-tests/blob/v3.1.0/test/surface/create-container.test.ts#L56-L64
28
+ const result = await store.fetcher._fetch(url, {
29
+ method: "PUT",
30
+ headers: {
31
+ "Content-Type": "text/turtle",
32
+ "If-None-Match": "*",
33
+ Link: '<http://www.w3.org/ns/ldp#BasicContainer>; rel="type"', // See https://github.com/solidos/node-solid-server/issues/1465
34
+ },
35
+ body: " ", // work around https://github.com/michielbdejong/community-server/issues/4#issuecomment-776222863
36
+ });
37
+ if (result.status.toString()[0] !== '2') {
38
+ throw new Error(`Not OK: got ${result.status} response while creating container at ${url}`);
39
+ }
40
+ }
41
+
42
+ async function getContainerMembers(containerUrl: string): Promise<string[]> {
43
+ const containerNode = store.sym(containerUrl);
44
+ await store.fetcher.load(containerNode);
45
+ const nodes = getContainerElements(containerNode);
46
+ return nodes.map(node => node.value);
47
+ }
48
+ return {
49
+ isContainer,
50
+ createContainer,
51
+ getContainerElements,
52
+ getContainerMembers
53
+ }
54
+ }
package/src/util/ns.js ADDED
@@ -0,0 +1,5 @@
1
+ // Namespaces we commonly use and have common prefixes for around Solid
2
+ import solidNamespace from 'solid-namespace' // Delegate to this which takes RDFlib as param.
3
+ import * as $rdf from 'rdflib'
4
+
5
+ export const ns = solidNamespace($rdf)
@@ -0,0 +1,155 @@
1
+ import { NamedNode, st } 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(url: string) {
9
+ try {
10
+ if (containerLogic.isContainer(url)) {
11
+ const aclDocUrl = await aclLogic.findAclDocUrl(url);
12
+ await store.fetcher._fetch(aclDocUrl, { method: "DELETE" });
13
+ const containerMembers = await containerLogic.getContainerMembers(url);
14
+ await Promise.all(
15
+ containerMembers.map((url) => recursiveDelete(url))
16
+ );
17
+ }
18
+ return store.fetcher._fetch(url, { method: "DELETE" });
19
+ } catch (e) {
20
+ // debug.log(`Please manually remove ${url} from your system under test.`, e);
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Create a resource if it really does not exist
26
+ * Be absolutely sure something does not exist before creating a new empty file
27
+ * as otherwise existing could be deleted.
28
+ * @param doc {NamedNode} - The resource
29
+ */
30
+ async function loadOrCreateIfNotExists(doc: NamedNode) {
31
+ let response
32
+ try {
33
+ response = await store.fetcher.load(doc)
34
+ } catch (err) {
35
+ if (err.response.status === 404) {
36
+ try {
37
+ await store.fetcher.webOperation('PUT', doc, { data: '', contentType: 'text/turtle' })
38
+ } catch (err) {
39
+ const msg = 'createIfNotExists: PUT FAILED: ' + doc + ': ' + err
40
+ throw new WebOperationError(msg)
41
+ }
42
+ await store.fetcher.load(doc)
43
+ } else {
44
+ if (err.response.status === 401) {
45
+ throw new UnauthorizedError();
46
+ }
47
+ if (err.response.status === 403) {
48
+ if (differentOrigin(doc)) {
49
+ throw new CrossOriginForbiddenError();
50
+ }
51
+ throw new SameOriginForbiddenError();
52
+ }
53
+ const msg = 'createIfNotExists doc load error NOT 404: ' + doc + ': ' + err
54
+ throw new FetchError(err.status, err.message + msg)
55
+ }
56
+ }
57
+ return response
58
+ }
59
+
60
+ /* Follow link from this doc to another thing, or else make a new link
61
+ **
62
+ ** @returns existing object, or creates it if non existent
63
+ */
64
+ async function followOrCreateLink(subject: NamedNode, predicate: NamedNode,
65
+ object: NamedNode, doc: NamedNode
66
+ ): Promise<NamedNode | null> {
67
+ await store.fetcher.load(doc)
68
+ const result = store.any(subject, predicate, null, doc)
69
+
70
+ if (result) return result as NamedNode
71
+ if (!store.updater.editable(doc)) {
72
+ const msg = `followOrCreateLink: cannot edit ${doc.value}`
73
+ debug.warn(msg)
74
+ throw new NotEditableError(msg)
75
+ }
76
+ try {
77
+ await store.updater.update([], [st(subject, predicate, object, doc)])
78
+ } catch (err) {
79
+ const msg = `followOrCreateLink: Error making link in ${doc} to ${object}: ${err}`
80
+ debug.warn(msg)
81
+ throw new WebOperationError(err)
82
+ }
83
+
84
+ try {
85
+ await loadOrCreateIfNotExists(object)
86
+ // store.fetcher.webOperation('PUT', object, { data: '', contentType: 'text/turtle'})
87
+ } catch (err) {
88
+ debug.warn(`followOrCreateLink: Error loading or saving new linked document: ${object}: ${err}`)
89
+ throw err;
90
+ }
91
+ return object
92
+ }
93
+
94
+ // Copied from https://github.com/solidos/web-access-control-tests/blob/v3.0.0/test/surface/delete.test.ts#L5
95
+ async function setSinglePeerAccess(options: {
96
+ ownerWebId: string,
97
+ peerWebId: string,
98
+ accessToModes?: string,
99
+ defaultModes?: string,
100
+ target: string
101
+ }) {
102
+ let str = [
103
+ '@prefix acl: <http://www.w3.org/ns/auth/acl#>.',
104
+ '',
105
+ `<#alice> a acl:Authorization;\n acl:agent <${options.ownerWebId}>;`,
106
+ ` acl:accessTo <${options.target}>;`,
107
+ ` acl:default <${options.target}>;`,
108
+ ' acl:mode acl:Read, acl:Write, acl:Control.',
109
+ ''
110
+ ].join('\n')
111
+ if (options.accessToModes) {
112
+ str += [
113
+ '<#bobAccessTo> a acl:Authorization;',
114
+ ` acl:agent <${options.peerWebId}>;`,
115
+ ` acl:accessTo <${options.target}>;`,
116
+ ` acl:mode ${options.accessToModes}.`,
117
+ ''
118
+ ].join('\n')
119
+ }
120
+ if (options.defaultModes) {
121
+ str += [
122
+ '<#bobDefault> a acl:Authorization;',
123
+ ` acl:agent <${options.peerWebId}>;`,
124
+ ` acl:default <${options.target}>;`,
125
+ ` acl:mode ${options.defaultModes}.`,
126
+ ''
127
+ ].join('\n')
128
+ }
129
+ const aclDocUrl = await aclLogic.findAclDocUrl(options.target);
130
+ return store.fetcher._fetch(aclDocUrl, {
131
+ method: 'PUT',
132
+ body: str,
133
+ headers: [
134
+ ['Content-Type', 'text/turtle']
135
+ ]
136
+ });
137
+ }
138
+
139
+ async function createEmptyRdfDoc(doc: NamedNode, comment: string) {
140
+ await store.fetcher.webOperation("PUT", doc.uri, {
141
+ data: `# ${new Date()} ${comment}
142
+ `,
143
+ contentType: "text/turtle",
144
+ });
145
+ }
146
+
147
+ return {
148
+ recursiveDelete,
149
+ setSinglePeerAccess,
150
+ createEmptyRdfDoc,
151
+ followOrCreateLink,
152
+ loadOrCreateIfNotExists
153
+ }
154
+ }
155
+
@@ -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
+ }
@@ -1,12 +1,21 @@
1
- import { sym } from 'rdflib'
2
- import { setACLUserPublic } from '../src/acl/aclLogic'
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
  {}
@@ -1,34 +1,35 @@
1
- /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
- import { ChatLogic } from "../src/chat/ChatLogic";
3
- import solidNamespace from "solid-namespace";
4
- import * as rdf from "rdflib";
5
- import { ProfileLogic } from "../src/profile/ProfileLogic";
6
- import { UpdateManager } from "rdflib";
7
- import { SolidNamespace } from "../src/types";
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
- const ns: SolidNamespace = solidNamespace(rdf);
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 chat;
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 = rdf.graph();
23
- store.fetcher = rdf.fetcher(store, { fetch: fetchMock, timeout: 5 });
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 profile = new ProfileLogic(store, ns, authn);
31
- chat = new ChatLogic(store, ns, profile);
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 chat.getChat(bob, false);
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 chat.getChat(bob, true);
78
+ result = await chatLogic.getChat(bob, true);
78
79
  });
79
- it("returns the chat URI based on the invitee's WebID", async () => {
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
- a acl:Authorization;
111
- acl:agent <https://alice.example/profile/card#me>;
112
- acl:accessTo <.>;
113
- acl:default <.>;
114
- acl:mode
115
- acl:Read, acl:Write, acl:Control.
116
- <#invitee>
117
- a acl:Authorization;
118
- acl:agent <https://bob.example/profile/card#me>;
119
- acl:accessTo <.>;
120
- acl:default <.>;
121
- acl:mode
122
- acl:Read, acl:Append.
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(chat.getChat(bob, false)).rejects.toEqual(expectedError);
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(chat.getChat(bob, true)).rejects.toEqual(expectedError);
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,56 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ *
4
+ */
5
+ import { UpdateManager, Store, Fetcher } 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 result = await containerLogic.getContainerMembers('https://com/');
25
+ expect(result.sort()).toEqual([
26
+ 'https://com/foo.txt',
27
+ 'https://com/bar/'
28
+ ].sort());
29
+ });
30
+ it("getContainerMembers- When container is empty - Resolves to an empty array", async () => {
31
+ containerIsEmpty();
32
+ const result = await containerLogic.getContainerMembers('https://container.com/');
33
+ expect(result).toEqual([]);
34
+ });
35
+
36
+ function containerIsEmpty() {
37
+ fetchMock.mockOnceIf(
38
+ "https://com/",
39
+ " ", // FIXME: https://github.com/jefflau/jest-fetch-mock/issues/189
40
+ {
41
+ headers: { "Content-Type": "text/turtle" },
42
+ }
43
+ );
44
+ }
45
+
46
+ function containerHasSomeContainmentTriples() {
47
+ fetchMock.mockOnceIf(
48
+ "https://com/",
49
+ "<.> <http://www.w3.org/ns/ldp#contains> <./foo.txt>, <./bar/> .",
50
+ {
51
+ headers: { "Content-Type": "text/turtle" },
52
+ }
53
+ );
54
+
55
+ }
56
+ })