@graffiti-garden/api 0.1.8 → 0.1.9
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/index.cjs.js +1 -1
- package/dist/index.js +1 -1
- package/dist/src/2-types.d.ts +7 -3
- package/dist/src/2-types.d.ts.map +1 -1
- package/dist/src/3-errors.d.ts +0 -3
- package/dist/src/3-errors.d.ts.map +1 -1
- package/dist/tests/index.js +1 -1
- package/dist/tests/src/crud.d.ts.map +1 -1
- package/dist/tests/src/discover.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/2-types.ts +14 -5
- package/src/3-errors.ts +0 -8
- package/tests/src/crud.ts +414 -402
- package/tests/src/discover.ts +531 -511
package/tests/src/discover.ts
CHANGED
|
@@ -11,588 +11,608 @@ export const graffitiDiscoverTests = (
|
|
|
11
11
|
useSession1: () => GraffitiSession,
|
|
12
12
|
useSession2: () => GraffitiSession,
|
|
13
13
|
) => {
|
|
14
|
-
describe(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const session2 = useSession2();
|
|
54
|
-
|
|
55
|
-
const object = randomPutObject();
|
|
56
|
-
object.allowed = [randomString(), randomString()];
|
|
57
|
-
const putted = await graffiti.put(object, session1);
|
|
58
|
-
|
|
59
|
-
const iteratorSession1 = graffiti.discover(object.channels, {}, session1);
|
|
60
|
-
const value = await nextStreamValue(iteratorSession1);
|
|
61
|
-
expect(value.value).toEqual(object.value);
|
|
62
|
-
expect(value.channels).toEqual(object.channels);
|
|
63
|
-
expect(value.allowed).toEqual(object.allowed);
|
|
64
|
-
expect(value.actor).toEqual(session1.actor);
|
|
65
|
-
expect(value.tombstone).toBe(false);
|
|
66
|
-
expect(value.lastModified).toEqual(putted.lastModified);
|
|
67
|
-
|
|
68
|
-
const iteratorSession2 = graffiti.discover(object.channels, {}, session2);
|
|
69
|
-
expect(await iteratorSession2.next()).toHaveProperty("done", true);
|
|
70
|
-
|
|
71
|
-
const iteratorNoSession = graffiti.discover(object.channels, {});
|
|
72
|
-
expect(await iteratorNoSession.next()).toHaveProperty("done", true);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("discover allowed", async () => {
|
|
76
|
-
const graffiti = useGraffiti();
|
|
77
|
-
const session1 = useSession1();
|
|
78
|
-
const session2 = useSession2();
|
|
79
|
-
|
|
80
|
-
const object = randomPutObject();
|
|
81
|
-
object.allowed = [randomString(), session2.actor, randomString()];
|
|
82
|
-
const putted = await graffiti.put(object, session1);
|
|
83
|
-
|
|
84
|
-
const iteratorSession2 = graffiti.discover(object.channels, {}, session2);
|
|
85
|
-
const value = await nextStreamValue(iteratorSession2);
|
|
86
|
-
expect(value.value).toEqual(object.value);
|
|
87
|
-
expect(value.allowed).toEqual([session2.actor]);
|
|
88
|
-
expect(value.channels).toEqual(object.channels);
|
|
89
|
-
expect(value.actor).toEqual(session1.actor);
|
|
90
|
-
expect(value.tombstone).toBe(false);
|
|
91
|
-
expect(value.lastModified).toEqual(putted.lastModified);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
for (const prop of ["name", "actor", "lastModified"] as const) {
|
|
95
|
-
it(`discover for ${prop}`, async () => {
|
|
14
|
+
describe(
|
|
15
|
+
"discover",
|
|
16
|
+
() => {
|
|
17
|
+
it("discover nothing", async () => {
|
|
18
|
+
const graffiti = useGraffiti();
|
|
19
|
+
const iterator = graffiti.discover([], {});
|
|
20
|
+
expect(await iterator.next()).toHaveProperty("done", true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("discover single", async () => {
|
|
24
|
+
const graffiti = useGraffiti();
|
|
25
|
+
const session = useSession1();
|
|
26
|
+
const object = randomPutObject();
|
|
27
|
+
|
|
28
|
+
const putted = await graffiti.put(object, session);
|
|
29
|
+
|
|
30
|
+
const queryChannels = [randomString(), object.channels[0]];
|
|
31
|
+
const iterator = graffiti.discover(queryChannels, {});
|
|
32
|
+
const value = await nextStreamValue(iterator);
|
|
33
|
+
expect(value.value).toEqual(object.value);
|
|
34
|
+
expect(value.channels).toEqual([object.channels[0]]);
|
|
35
|
+
expect(value.allowed).toBeUndefined();
|
|
36
|
+
expect(value.actor).toEqual(session.actor);
|
|
37
|
+
expect(value.tombstone).toBe(false);
|
|
38
|
+
expect(value.lastModified).toEqual(putted.lastModified);
|
|
39
|
+
const result2 = await iterator.next();
|
|
40
|
+
expect(result2.done).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("discover wrong channel", async () => {
|
|
44
|
+
const graffiti = useGraffiti();
|
|
45
|
+
const session = useSession1();
|
|
46
|
+
const object = randomPutObject();
|
|
47
|
+
await graffiti.put(object, session);
|
|
48
|
+
const iterator = graffiti.discover([randomString()], {});
|
|
49
|
+
await expect(iterator.next()).resolves.toHaveProperty("done", true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("discover not allowed", async () => {
|
|
96
53
|
const graffiti = useGraffiti();
|
|
97
54
|
const session1 = useSession1();
|
|
98
55
|
const session2 = useSession2();
|
|
99
56
|
|
|
100
|
-
const
|
|
101
|
-
|
|
57
|
+
const object = randomPutObject();
|
|
58
|
+
object.allowed = [randomString(), randomString()];
|
|
59
|
+
const putted = await graffiti.put(object, session1);
|
|
102
60
|
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
61
|
+
const iteratorSession1 = graffiti.discover(
|
|
62
|
+
object.channels,
|
|
63
|
+
{},
|
|
64
|
+
session1,
|
|
65
|
+
);
|
|
66
|
+
const value = await nextStreamValue(iteratorSession1);
|
|
67
|
+
expect(value.value).toEqual(object.value);
|
|
68
|
+
expect(value.channels).toEqual(object.channels);
|
|
69
|
+
expect(value.allowed).toEqual(object.allowed);
|
|
70
|
+
expect(value.actor).toEqual(session1.actor);
|
|
71
|
+
expect(value.tombstone).toBe(false);
|
|
72
|
+
expect(value.lastModified).toEqual(putted.lastModified);
|
|
73
|
+
|
|
74
|
+
const iteratorSession2 = graffiti.discover(
|
|
75
|
+
object.channels,
|
|
76
|
+
{},
|
|
77
|
+
session2,
|
|
78
|
+
);
|
|
79
|
+
expect(await iteratorSession2.next()).toHaveProperty("done", true);
|
|
80
|
+
|
|
81
|
+
const iteratorNoSession = graffiti.discover(object.channels, {});
|
|
82
|
+
expect(await iteratorNoSession.next()).toHaveProperty("done", true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("discover allowed", async () => {
|
|
86
|
+
const graffiti = useGraffiti();
|
|
87
|
+
const session1 = useSession1();
|
|
88
|
+
const session2 = useSession2();
|
|
89
|
+
|
|
90
|
+
const object = randomPutObject();
|
|
91
|
+
object.allowed = [randomString(), session2.actor, randomString()];
|
|
92
|
+
const putted = await graffiti.put(object, session1);
|
|
93
|
+
|
|
94
|
+
const iteratorSession2 = graffiti.discover(
|
|
95
|
+
object.channels,
|
|
96
|
+
{},
|
|
97
|
+
session2,
|
|
98
|
+
);
|
|
99
|
+
const value = await nextStreamValue(iteratorSession2);
|
|
100
|
+
expect(value.value).toEqual(object.value);
|
|
101
|
+
expect(value.allowed).toEqual([session2.actor]);
|
|
102
|
+
expect(value.channels).toEqual(object.channels);
|
|
103
|
+
expect(value.actor).toEqual(session1.actor);
|
|
104
|
+
expect(value.tombstone).toBe(false);
|
|
105
|
+
expect(value.lastModified).toEqual(putted.lastModified);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
for (const prop of ["name", "actor", "lastModified"] as const) {
|
|
109
|
+
it(`discover for ${prop}`, async () => {
|
|
110
|
+
const graffiti = useGraffiti();
|
|
111
|
+
const session1 = useSession1();
|
|
112
|
+
const session2 = useSession2();
|
|
113
|
+
|
|
114
|
+
const object1 = randomPutObject();
|
|
115
|
+
const putted1 = await graffiti.put(object1, session1);
|
|
116
|
+
|
|
117
|
+
const object2 = randomPutObject();
|
|
118
|
+
object2.channels = object1.channels;
|
|
119
|
+
// Make sure the lastModified is different for the query
|
|
120
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
121
|
+
const putted2 = await graffiti.put(object2, session2);
|
|
122
|
+
|
|
123
|
+
const iterator = graffiti.discover(object1.channels, {
|
|
124
|
+
properties: {
|
|
125
|
+
[prop]: {
|
|
126
|
+
enum: [putted1[prop]],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const value = await nextStreamValue(iterator);
|
|
132
|
+
expect(value.name).toEqual(putted1.name);
|
|
133
|
+
expect(value.name).not.toEqual(putted2.name);
|
|
134
|
+
expect(value.value).toEqual(object1.value);
|
|
135
|
+
await expect(iterator.next()).resolves.toHaveProperty("done", true);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
it("discover with lastModified range", async () => {
|
|
140
|
+
const graffiti = useGraffiti();
|
|
141
|
+
const session = useSession1();
|
|
142
|
+
|
|
143
|
+
const object = randomPutObject();
|
|
144
|
+
const putted1 = await graffiti.put(object, session);
|
|
145
|
+
// Make sure the lastModified is different
|
|
106
146
|
await new Promise((r) => setTimeout(r, 20));
|
|
107
|
-
const putted2 = await graffiti.put(
|
|
147
|
+
const putted2 = await graffiti.put(object, session);
|
|
108
148
|
|
|
109
|
-
|
|
149
|
+
expect(putted1.name).not.toEqual(putted2.name);
|
|
150
|
+
expect(putted1.lastModified).toBeLessThan(putted2.lastModified);
|
|
151
|
+
|
|
152
|
+
const gtIterator = graffiti.discover([object.channels[0]], {
|
|
110
153
|
properties: {
|
|
111
|
-
|
|
112
|
-
|
|
154
|
+
lastModified: {
|
|
155
|
+
minimum: putted2.lastModified,
|
|
156
|
+
exclusiveMinimum: true,
|
|
113
157
|
},
|
|
114
158
|
},
|
|
115
159
|
});
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
it("discover with lastModified range", async () => {
|
|
126
|
-
const graffiti = useGraffiti();
|
|
127
|
-
const session = useSession1();
|
|
128
|
-
|
|
129
|
-
const object = randomPutObject();
|
|
130
|
-
const putted1 = await graffiti.put(object, session);
|
|
131
|
-
// Make sure the lastModified is different
|
|
132
|
-
await new Promise((r) => setTimeout(r, 20));
|
|
133
|
-
const putted2 = await graffiti.put(object, session);
|
|
134
|
-
|
|
135
|
-
expect(putted1.name).not.toEqual(putted2.name);
|
|
136
|
-
expect(putted1.lastModified).toBeLessThan(putted2.lastModified);
|
|
137
|
-
|
|
138
|
-
const gtIterator = graffiti.discover([object.channels[0]], {
|
|
139
|
-
properties: {
|
|
140
|
-
lastModified: {
|
|
141
|
-
minimum: putted2.lastModified,
|
|
142
|
-
exclusiveMinimum: true,
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
|
-
expect(await gtIterator.next()).toHaveProperty("done", true);
|
|
147
|
-
const gtIteratorEpsilon = graffiti.discover([object.channels[0]], {
|
|
148
|
-
properties: {
|
|
149
|
-
lastModified: {
|
|
150
|
-
minimum: putted2.lastModified - 0.1,
|
|
151
|
-
exclusiveMinimum: true,
|
|
160
|
+
expect(await gtIterator.next()).toHaveProperty("done", true);
|
|
161
|
+
const gtIteratorEpsilon = graffiti.discover([object.channels[0]], {
|
|
162
|
+
properties: {
|
|
163
|
+
lastModified: {
|
|
164
|
+
minimum: putted2.lastModified - 0.1,
|
|
165
|
+
exclusiveMinimum: true,
|
|
166
|
+
},
|
|
152
167
|
},
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
168
|
+
});
|
|
169
|
+
const value1 = await nextStreamValue(gtIteratorEpsilon);
|
|
170
|
+
expect(value1.name).toEqual(putted2.name);
|
|
171
|
+
expect(await gtIteratorEpsilon.next()).toHaveProperty("done", true);
|
|
172
|
+
const gteIterator = graffiti.discover(object.channels, {
|
|
173
|
+
properties: {
|
|
174
|
+
value: {},
|
|
175
|
+
lastModified: {
|
|
176
|
+
minimum: putted2.lastModified,
|
|
177
|
+
},
|
|
163
178
|
},
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
179
|
+
});
|
|
180
|
+
const value = await nextStreamValue(gteIterator);
|
|
181
|
+
expect(value.name).toEqual(putted2.name);
|
|
182
|
+
expect(await gteIterator.next()).toHaveProperty("done", true);
|
|
183
|
+
const gteIteratorEpsilon = graffiti.discover(object.channels, {
|
|
184
|
+
properties: {
|
|
185
|
+
lastModified: {
|
|
186
|
+
minimum: putted2.lastModified + 0.1,
|
|
187
|
+
},
|
|
173
188
|
},
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
expect(await gteIteratorEpsilon.next()).toHaveProperty("done", true);
|
|
189
|
+
});
|
|
190
|
+
expect(await gteIteratorEpsilon.next()).toHaveProperty("done", true);
|
|
177
191
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
192
|
+
const ltIterator = graffiti.discover(object.channels, {
|
|
193
|
+
properties: {
|
|
194
|
+
lastModified: {
|
|
195
|
+
maximum: putted1.lastModified,
|
|
196
|
+
exclusiveMaximum: true,
|
|
197
|
+
},
|
|
183
198
|
},
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
expect(await ltIterator.next()).toHaveProperty("done", true);
|
|
199
|
+
});
|
|
200
|
+
expect(await ltIterator.next()).toHaveProperty("done", true);
|
|
187
201
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
202
|
+
const ltIteratorEpsilon = graffiti.discover(object.channels, {
|
|
203
|
+
properties: {
|
|
204
|
+
lastModified: {
|
|
205
|
+
maximum: putted1.lastModified + 0.1,
|
|
206
|
+
exclusiveMaximum: true,
|
|
207
|
+
},
|
|
193
208
|
},
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
209
|
+
});
|
|
210
|
+
const value3 = await nextStreamValue(ltIteratorEpsilon);
|
|
211
|
+
expect(value3.name).toEqual(putted1.name);
|
|
212
|
+
expect(await ltIteratorEpsilon.next()).toHaveProperty("done", true);
|
|
213
|
+
|
|
214
|
+
const lteIterator = graffiti.discover(object.channels, {
|
|
215
|
+
properties: {
|
|
216
|
+
lastModified: {
|
|
217
|
+
maximum: putted1.lastModified,
|
|
218
|
+
},
|
|
204
219
|
},
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
220
|
+
});
|
|
221
|
+
const value2 = await nextStreamValue(lteIterator);
|
|
222
|
+
expect(value2.name).toEqual(putted1.name);
|
|
223
|
+
expect(await lteIterator.next()).toHaveProperty("done", true);
|
|
224
|
+
|
|
225
|
+
const lteIteratorEpsilon = graffiti.discover(object.channels, {
|
|
226
|
+
properties: {
|
|
227
|
+
lastModified: {
|
|
228
|
+
maximum: putted1.lastModified - 0.1,
|
|
229
|
+
},
|
|
215
230
|
},
|
|
216
|
-
}
|
|
231
|
+
});
|
|
232
|
+
expect(await lteIteratorEpsilon.next()).toHaveProperty("done", true);
|
|
217
233
|
});
|
|
218
|
-
expect(await lteIteratorEpsilon.next()).toHaveProperty("done", true);
|
|
219
|
-
});
|
|
220
234
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
235
|
+
it("discover schema allowed, as and not as owner", async () => {
|
|
236
|
+
const graffiti = useGraffiti();
|
|
237
|
+
const session1 = useSession1();
|
|
238
|
+
const session2 = useSession2();
|
|
225
239
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
240
|
+
const object = randomPutObject();
|
|
241
|
+
object.allowed = [randomString(), session2.actor, randomString()];
|
|
242
|
+
await graffiti.put(object, session1);
|
|
229
243
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
244
|
+
const iteratorSession1 = graffiti.discover(
|
|
245
|
+
object.channels,
|
|
246
|
+
{
|
|
247
|
+
properties: {
|
|
248
|
+
allowed: {
|
|
249
|
+
minItems: 3,
|
|
250
|
+
// Make sure session2.actor is in the allow list
|
|
251
|
+
not: {
|
|
252
|
+
items: {
|
|
253
|
+
not: {
|
|
254
|
+
enum: [session2.actor],
|
|
255
|
+
},
|
|
241
256
|
},
|
|
242
257
|
},
|
|
243
258
|
},
|
|
244
259
|
},
|
|
245
260
|
},
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
261
|
+
session1,
|
|
262
|
+
);
|
|
263
|
+
const value = await nextStreamValue(iteratorSession1);
|
|
264
|
+
expect(value.value).toEqual(object.value);
|
|
265
|
+
await expect(iteratorSession1.next()).resolves.toHaveProperty(
|
|
266
|
+
"done",
|
|
267
|
+
true,
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const iteratorSession2BigAllow = graffiti.discover(
|
|
271
|
+
object.channels,
|
|
272
|
+
{
|
|
273
|
+
properties: {
|
|
274
|
+
allowed: {
|
|
275
|
+
minItems: 3,
|
|
276
|
+
},
|
|
262
277
|
},
|
|
263
278
|
},
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
279
|
+
session2,
|
|
280
|
+
);
|
|
281
|
+
await expect(iteratorSession2BigAllow.next()).resolves.toHaveProperty(
|
|
282
|
+
"done",
|
|
283
|
+
true,
|
|
284
|
+
);
|
|
285
|
+
const iteratorSession2PeekOther = graffiti.discover(
|
|
286
|
+
object.channels,
|
|
287
|
+
{
|
|
288
|
+
properties: {
|
|
289
|
+
allowed: {
|
|
290
|
+
not: {
|
|
291
|
+
items: {
|
|
292
|
+
not: {
|
|
293
|
+
enum: [object.channels[0]],
|
|
294
|
+
},
|
|
280
295
|
},
|
|
281
296
|
},
|
|
282
297
|
},
|
|
283
298
|
},
|
|
284
299
|
},
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
300
|
+
session2,
|
|
301
|
+
);
|
|
302
|
+
await expect(iteratorSession2PeekOther.next()).resolves.toHaveProperty(
|
|
303
|
+
"done",
|
|
304
|
+
true,
|
|
305
|
+
);
|
|
306
|
+
const iteratorSession2SmallAllowPeekSelf = graffiti.discover(
|
|
307
|
+
object.channels,
|
|
308
|
+
{
|
|
309
|
+
properties: {
|
|
310
|
+
allowed: {
|
|
311
|
+
maxItems: 1,
|
|
312
|
+
not: {
|
|
313
|
+
items: {
|
|
314
|
+
not: {
|
|
315
|
+
enum: [session2.actor],
|
|
316
|
+
},
|
|
302
317
|
},
|
|
303
318
|
},
|
|
304
319
|
},
|
|
305
320
|
},
|
|
306
321
|
},
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
322
|
+
session2,
|
|
323
|
+
);
|
|
324
|
+
const value2 = await nextStreamValue(
|
|
325
|
+
iteratorSession2SmallAllowPeekSelf,
|
|
326
|
+
);
|
|
327
|
+
expect(value2.value).toEqual(object.value);
|
|
328
|
+
await expect(
|
|
329
|
+
iteratorSession2SmallAllowPeekSelf.next(),
|
|
330
|
+
).resolves.toHaveProperty("done", true);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it("discover schema channels, as and not as owner", async () => {
|
|
334
|
+
const graffiti = useGraffiti();
|
|
335
|
+
const session1 = useSession1();
|
|
336
|
+
const session2 = useSession2();
|
|
337
|
+
|
|
338
|
+
const object = randomPutObject();
|
|
339
|
+
object.channels = [randomString(), randomString(), randomString()];
|
|
340
|
+
await graffiti.put(object, session1);
|
|
341
|
+
|
|
342
|
+
const iteratorSession1 = graffiti.discover(
|
|
343
|
+
[object.channels[0], object.channels[2]],
|
|
344
|
+
{
|
|
345
|
+
properties: {
|
|
346
|
+
channels: {
|
|
347
|
+
minItems: 3,
|
|
348
|
+
// Make sure session2.actor is in the allow list
|
|
349
|
+
not: {
|
|
350
|
+
items: {
|
|
351
|
+
not: {
|
|
352
|
+
enum: [object.channels[1]],
|
|
353
|
+
},
|
|
337
354
|
},
|
|
338
355
|
},
|
|
339
356
|
},
|
|
340
357
|
},
|
|
341
358
|
},
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
359
|
+
session1,
|
|
360
|
+
);
|
|
361
|
+
const value = await nextStreamValue(iteratorSession1);
|
|
362
|
+
expect(value.value).toEqual(object.value);
|
|
363
|
+
await expect(iteratorSession1.next()).resolves.toHaveProperty(
|
|
364
|
+
"done",
|
|
365
|
+
true,
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
const iteratorSession2BigAllow = graffiti.discover(
|
|
369
|
+
[object.channels[0], object.channels[2]],
|
|
370
|
+
{
|
|
371
|
+
properties: {
|
|
372
|
+
channels: {
|
|
373
|
+
minItems: 3,
|
|
374
|
+
},
|
|
358
375
|
},
|
|
359
376
|
},
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
377
|
+
session2,
|
|
378
|
+
);
|
|
379
|
+
await expect(iteratorSession2BigAllow.next()).resolves.toHaveProperty(
|
|
380
|
+
"done",
|
|
381
|
+
true,
|
|
382
|
+
);
|
|
383
|
+
const iteratorSession2PeekOther = graffiti.discover(
|
|
384
|
+
[object.channels[0], object.channels[2]],
|
|
385
|
+
{
|
|
386
|
+
properties: {
|
|
387
|
+
channels: {
|
|
388
|
+
not: {
|
|
389
|
+
items: {
|
|
390
|
+
not: {
|
|
391
|
+
enum: [object.channels[1]],
|
|
392
|
+
},
|
|
376
393
|
},
|
|
377
394
|
},
|
|
378
395
|
},
|
|
379
396
|
},
|
|
380
397
|
},
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
+
session2,
|
|
399
|
+
);
|
|
400
|
+
await expect(iteratorSession2PeekOther.next()).resolves.toHaveProperty(
|
|
401
|
+
"done",
|
|
402
|
+
true,
|
|
403
|
+
);
|
|
404
|
+
const iteratorSession2SmallAllowPeekSelf = graffiti.discover(
|
|
405
|
+
[object.channels[0], object.channels[2]],
|
|
406
|
+
{
|
|
407
|
+
properties: {
|
|
408
|
+
allowed: {
|
|
409
|
+
maxItems: 2,
|
|
410
|
+
not: {
|
|
411
|
+
items: {
|
|
412
|
+
not: {
|
|
413
|
+
enum: [object.channels[2]],
|
|
414
|
+
},
|
|
398
415
|
},
|
|
399
416
|
},
|
|
400
417
|
},
|
|
401
418
|
},
|
|
402
419
|
},
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
} satisfies JSONSchema4;
|
|
424
|
-
|
|
425
|
-
await graffiti.put(publicO, session1);
|
|
426
|
-
const iterator = graffiti.discover(
|
|
427
|
-
publicO.channels,
|
|
428
|
-
publicSchema,
|
|
429
|
-
session1,
|
|
430
|
-
);
|
|
431
|
-
const value = await nextStreamValue(iterator);
|
|
432
|
-
expect(value.value).toEqual(publicO.value);
|
|
433
|
-
expect(value.allowed).toBeUndefined();
|
|
434
|
-
await expect(iterator.next()).resolves.toHaveProperty("done", true);
|
|
435
|
-
|
|
436
|
-
const restricted = randomPutObject();
|
|
437
|
-
restricted.allowed = [];
|
|
438
|
-
await graffiti.put(restricted, session1);
|
|
439
|
-
const iterator2 = graffiti.discover(
|
|
440
|
-
restricted.channels,
|
|
441
|
-
publicSchema,
|
|
442
|
-
session1,
|
|
443
|
-
);
|
|
444
|
-
await expect(iterator2.next()).resolves.toHaveProperty("done", true);
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
it("discover query for values", async () => {
|
|
448
|
-
const graffiti = useGraffiti();
|
|
449
|
-
const session = useSession1();
|
|
450
|
-
|
|
451
|
-
const object1 = randomPutObject();
|
|
452
|
-
object1.value = { test: randomString() };
|
|
453
|
-
await graffiti.put(object1, session);
|
|
454
|
-
|
|
455
|
-
const object2 = randomPutObject();
|
|
456
|
-
object2.channels = object1.channels;
|
|
457
|
-
object2.value = { test: randomString(), something: randomString() };
|
|
458
|
-
await graffiti.put(object2, session);
|
|
459
|
-
|
|
460
|
-
const object3 = randomPutObject();
|
|
461
|
-
object3.channels = object1.channels;
|
|
462
|
-
object3.value = { other: randomString(), something: randomString() };
|
|
463
|
-
await graffiti.put(object3, session);
|
|
464
|
-
|
|
465
|
-
const counts = new Map<string, number>();
|
|
466
|
-
for (const property of ["test", "something", "other"] as const) {
|
|
467
|
-
let count = 0;
|
|
468
|
-
for await (const result of graffiti.discover(object1.channels, {
|
|
469
|
-
properties: {
|
|
470
|
-
value: {
|
|
471
|
-
required: [property],
|
|
472
|
-
},
|
|
420
|
+
session2,
|
|
421
|
+
);
|
|
422
|
+
const value2 = await nextStreamValue(
|
|
423
|
+
iteratorSession2SmallAllowPeekSelf,
|
|
424
|
+
);
|
|
425
|
+
expect(value2.value).toEqual(object.value);
|
|
426
|
+
await expect(
|
|
427
|
+
iteratorSession2SmallAllowPeekSelf.next(),
|
|
428
|
+
).resolves.toHaveProperty("done", true);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it("discover query for empty allowed", async () => {
|
|
432
|
+
const graffiti = useGraffiti();
|
|
433
|
+
const session1 = useSession1();
|
|
434
|
+
|
|
435
|
+
const publicO = randomPutObject();
|
|
436
|
+
|
|
437
|
+
const publicSchema = {
|
|
438
|
+
not: {
|
|
439
|
+
required: ["allowed"],
|
|
473
440
|
},
|
|
474
|
-
}
|
|
475
|
-
assert(!result.error, "result has error");
|
|
476
|
-
if (property in result.value.value) {
|
|
477
|
-
count++;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
counts.set(property, count);
|
|
481
|
-
}
|
|
441
|
+
} satisfies JSONSchema4;
|
|
482
442
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
it("discover for replaced channels", async () => {
|
|
507
|
-
// Do this a bunch to check for concurrency issues
|
|
508
|
-
for (let i = 0; i < 10; i++) {
|
|
443
|
+
await graffiti.put(publicO, session1);
|
|
444
|
+
const iterator = graffiti.discover(
|
|
445
|
+
publicO.channels,
|
|
446
|
+
publicSchema,
|
|
447
|
+
session1,
|
|
448
|
+
);
|
|
449
|
+
const value = await nextStreamValue(iterator);
|
|
450
|
+
expect(value.value).toEqual(publicO.value);
|
|
451
|
+
expect(value.allowed).toBeUndefined();
|
|
452
|
+
await expect(iterator.next()).resolves.toHaveProperty("done", true);
|
|
453
|
+
|
|
454
|
+
const restricted = randomPutObject();
|
|
455
|
+
restricted.allowed = [];
|
|
456
|
+
await graffiti.put(restricted, session1);
|
|
457
|
+
const iterator2 = graffiti.discover(
|
|
458
|
+
restricted.channels,
|
|
459
|
+
publicSchema,
|
|
460
|
+
session1,
|
|
461
|
+
);
|
|
462
|
+
await expect(iterator2.next()).resolves.toHaveProperty("done", true);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it("discover query for values", async () => {
|
|
509
466
|
const graffiti = useGraffiti();
|
|
510
467
|
const session = useSession1();
|
|
511
468
|
|
|
512
469
|
const object1 = randomPutObject();
|
|
513
|
-
|
|
470
|
+
object1.value = { test: randomString() };
|
|
471
|
+
await graffiti.put(object1, session);
|
|
472
|
+
|
|
514
473
|
const object2 = randomPutObject();
|
|
515
|
-
|
|
474
|
+
object2.channels = object1.channels;
|
|
475
|
+
object2.value = { test: randomString(), something: randomString() };
|
|
476
|
+
await graffiti.put(object2, session);
|
|
477
|
+
|
|
478
|
+
const object3 = randomPutObject();
|
|
479
|
+
object3.channels = object1.channels;
|
|
480
|
+
object3.value = { other: randomString(), something: randomString() };
|
|
481
|
+
await graffiti.put(object3, session);
|
|
482
|
+
|
|
483
|
+
const counts = new Map<string, number>();
|
|
484
|
+
for (const property of ["test", "something", "other"] as const) {
|
|
485
|
+
let count = 0;
|
|
486
|
+
for await (const result of graffiti.discover(object1.channels, {
|
|
487
|
+
properties: {
|
|
488
|
+
value: {
|
|
489
|
+
required: [property],
|
|
490
|
+
},
|
|
491
|
+
},
|
|
492
|
+
})) {
|
|
493
|
+
assert(!result.error, "result has error");
|
|
494
|
+
if (property in result.value.value) {
|
|
495
|
+
count++;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
counts.set(property, count);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
expect(counts.get("test")).toBe(2);
|
|
502
|
+
expect(counts.get("something")).toBe(2);
|
|
503
|
+
expect(counts.get("other")).toBe(1);
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
it("discover for deleted content", async () => {
|
|
507
|
+
const graffiti = useGraffiti();
|
|
508
|
+
const session = useSession1();
|
|
509
|
+
|
|
510
|
+
const object = randomPutObject();
|
|
511
|
+
const putted = await graffiti.put(object, session);
|
|
512
|
+
const deleted = await graffiti.delete(putted, session);
|
|
513
|
+
|
|
514
|
+
const iterator = graffiti.discover(object.channels, {});
|
|
515
|
+
const value = await nextStreamValue(iterator);
|
|
516
|
+
expect(value.tombstone).toBe(true);
|
|
517
|
+
expect(value.value).toEqual(object.value);
|
|
518
|
+
expect(value.channels).toEqual(object.channels);
|
|
519
|
+
expect(value.actor).toEqual(session.actor);
|
|
520
|
+
expect(value.lastModified).toEqual(deleted.lastModified);
|
|
521
|
+
await expect(iterator.next()).resolves.toHaveProperty("done", true);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
it("discover for replaced channels", async () => {
|
|
525
|
+
// Do this a bunch to check for concurrency issues
|
|
526
|
+
for (let i = 0; i < 10; i++) {
|
|
527
|
+
const graffiti = useGraffiti();
|
|
528
|
+
const session = useSession1();
|
|
529
|
+
|
|
530
|
+
const object1 = randomPutObject();
|
|
531
|
+
const putted = await graffiti.put(object1, session);
|
|
532
|
+
const object2 = randomPutObject();
|
|
533
|
+
const replaced = await graffiti.put(
|
|
534
|
+
{
|
|
535
|
+
...putted,
|
|
536
|
+
...object2,
|
|
537
|
+
},
|
|
538
|
+
session,
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
const iterator1 = graffiti.discover(object1.channels, {});
|
|
542
|
+
const value1 = await nextStreamValue(iterator1);
|
|
543
|
+
await expect(iterator1.next()).resolves.toHaveProperty("done", true);
|
|
544
|
+
|
|
545
|
+
const iterator2 = graffiti.discover(object2.channels, {});
|
|
546
|
+
const value2 = await nextStreamValue(iterator2);
|
|
547
|
+
await expect(iterator2.next()).resolves.toHaveProperty("done", true);
|
|
548
|
+
|
|
549
|
+
// If they have the same timestamp, except
|
|
550
|
+
// only one to have a tombstone
|
|
551
|
+
if (putted.lastModified === replaced.lastModified) {
|
|
552
|
+
expect(value1.tombstone || value2.tombstone).toBe(true);
|
|
553
|
+
expect(value1.tombstone && value2.tombstone).toBe(false);
|
|
554
|
+
} else {
|
|
555
|
+
expect(value1.tombstone).toBe(true);
|
|
556
|
+
expect(value1.value).toEqual(object1.value);
|
|
557
|
+
expect(value1.channels).toEqual(object1.channels);
|
|
558
|
+
expect(value1.lastModified).toEqual(replaced.lastModified);
|
|
559
|
+
|
|
560
|
+
expect(value2.tombstone).toBe(false);
|
|
561
|
+
expect(value2.value).toEqual(object2.value);
|
|
562
|
+
expect(value2.channels).toEqual(object2.channels);
|
|
563
|
+
expect(value2.lastModified).toEqual(replaced.lastModified);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it("discover for patched allowed", async () => {
|
|
569
|
+
const graffiti = useGraffiti();
|
|
570
|
+
const session = useSession1();
|
|
571
|
+
const object = randomPutObject();
|
|
572
|
+
const putted = await graffiti.put(object, session);
|
|
573
|
+
await graffiti.patch(
|
|
516
574
|
{
|
|
517
|
-
|
|
518
|
-
...object2,
|
|
575
|
+
allowed: [{ op: "add", path: "", value: [] }],
|
|
519
576
|
},
|
|
577
|
+
putted,
|
|
520
578
|
session,
|
|
521
579
|
);
|
|
580
|
+
const iterator = graffiti.discover(object.channels, {});
|
|
581
|
+
const value = await nextStreamValue(iterator);
|
|
582
|
+
expect(value.tombstone).toBe(true);
|
|
583
|
+
expect(value.value).toEqual(object.value);
|
|
584
|
+
expect(value.channels).toEqual(object.channels);
|
|
585
|
+
expect(value.allowed).toBeUndefined();
|
|
586
|
+
await expect(iterator.next()).resolves.toHaveProperty("done", true);
|
|
587
|
+
});
|
|
522
588
|
|
|
523
|
-
|
|
524
|
-
const
|
|
525
|
-
|
|
589
|
+
it("put concurrently and discover one", async () => {
|
|
590
|
+
const graffiti = useGraffiti();
|
|
591
|
+
const session = useSession1();
|
|
526
592
|
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
await expect(iterator2.next()).resolves.toHaveProperty("done", true);
|
|
593
|
+
const object = randomPutObject();
|
|
594
|
+
object.name = randomString();
|
|
530
595
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
it("discover for patched allowed", async () => {
|
|
551
|
-
const graffiti = useGraffiti();
|
|
552
|
-
const session = useSession1();
|
|
553
|
-
const object = randomPutObject();
|
|
554
|
-
const putted = await graffiti.put(object, session);
|
|
555
|
-
await graffiti.patch(
|
|
556
|
-
{
|
|
557
|
-
allowed: [{ op: "add", path: "", value: [] }],
|
|
558
|
-
},
|
|
559
|
-
putted,
|
|
560
|
-
session,
|
|
561
|
-
);
|
|
562
|
-
const iterator = graffiti.discover(object.channels, {});
|
|
563
|
-
const value = await nextStreamValue(iterator);
|
|
564
|
-
expect(value.tombstone).toBe(true);
|
|
565
|
-
expect(value.value).toEqual(object.value);
|
|
566
|
-
expect(value.channels).toEqual(object.channels);
|
|
567
|
-
expect(value.allowed).toBeUndefined();
|
|
568
|
-
await expect(iterator.next()).resolves.toHaveProperty("done", true);
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
it("put concurrently and discover one", async () => {
|
|
572
|
-
const graffiti = useGraffiti();
|
|
573
|
-
const session = useSession1();
|
|
574
|
-
|
|
575
|
-
const object = randomPutObject();
|
|
576
|
-
object.name = randomString();
|
|
577
|
-
|
|
578
|
-
const putPromises = Array(100)
|
|
579
|
-
.fill(0)
|
|
580
|
-
.map(() => graffiti.put(object, session));
|
|
581
|
-
await Promise.all(putPromises);
|
|
582
|
-
|
|
583
|
-
const iterator = graffiti.discover(object.channels, {});
|
|
584
|
-
let tombstoneCount = 0;
|
|
585
|
-
let valueCount = 0;
|
|
586
|
-
for await (const result of iterator) {
|
|
587
|
-
assert(!result.error, "result has error");
|
|
588
|
-
if (result.value.tombstone) {
|
|
589
|
-
tombstoneCount++;
|
|
590
|
-
} else {
|
|
591
|
-
valueCount++;
|
|
596
|
+
const putPromises = Array(100)
|
|
597
|
+
.fill(0)
|
|
598
|
+
.map(() => graffiti.put(object, session));
|
|
599
|
+
await Promise.all(putPromises);
|
|
600
|
+
|
|
601
|
+
const iterator = graffiti.discover(object.channels, {});
|
|
602
|
+
let tombstoneCount = 0;
|
|
603
|
+
let valueCount = 0;
|
|
604
|
+
for await (const result of iterator) {
|
|
605
|
+
assert(!result.error, "result has error");
|
|
606
|
+
if (result.value.tombstone) {
|
|
607
|
+
tombstoneCount++;
|
|
608
|
+
} else {
|
|
609
|
+
valueCount++;
|
|
610
|
+
}
|
|
592
611
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
|
|
612
|
+
expect(tombstoneCount).toBe(99);
|
|
613
|
+
expect(valueCount).toBe(1);
|
|
614
|
+
});
|
|
615
|
+
},
|
|
616
|
+
{ timeout: 20000 },
|
|
617
|
+
);
|
|
598
618
|
};
|