cozy-client 60.32.0 → 60.33.0
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/CozyClient.js +1 -1
- package/dist/__mocks__/cozy-stack-client.js +21 -1
- package/dist/const.js +4 -2
- package/dist/links/StackLink.js +169 -77
- package/dist/links/StackLink.spec.js +90 -0
- package/dist/queries/dsl.js +34 -0
- package/dist/queries/dsl.spec.js +32 -0
- package/dist/storage.js +3 -1
- package/dist/storage.spec.js +9 -0
- package/dist/store/queries.js +17 -0
- package/dist/store/queries.spec.js +30 -0
- package/package.json +3 -3
- package/types/__mocks__/cozy-stack-client.d.ts +16 -0
- package/types/const.d.ts +1 -0
- package/types/queries/dsl.d.ts +11 -0
package/dist/CozyClient.js
CHANGED
|
@@ -3126,7 +3126,7 @@ var CozyClient = /*#__PURE__*/function () {
|
|
|
3126
3126
|
CozyClient.hooks = CozyClient.hooks || {};
|
|
3127
3127
|
CozyClient.fetchPolicies = _policies.default; //COZY_CLIENT_VERSION_PACKAGE in replaced by babel. See babel config
|
|
3128
3128
|
|
|
3129
|
-
CozyClient.version = "60.
|
|
3129
|
+
CozyClient.version = "60.33.0";
|
|
3130
3130
|
|
|
3131
3131
|
_microee.default.mixin(CozyClient);
|
|
3132
3132
|
|
|
@@ -24,9 +24,28 @@ var _jest$requireActual = jest.requireActual('cozy-stack-client'),
|
|
|
24
24
|
OriginalOAuthClient = _jest$requireActual.OAuthClient,
|
|
25
25
|
normalizeDoc = _jest$requireActual.normalizeDoc,
|
|
26
26
|
FetchError = _jest$requireActual.FetchError;
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} fileId - The target file or folder id
|
|
29
|
+
* @param {object} [options] - Query options
|
|
30
|
+
* @param {string|null} [options.driveId] - The shared drive id
|
|
31
|
+
* @returns {Promise<{data: object[], meta: {file_id: string}}>} The mocked response
|
|
32
|
+
*/
|
|
33
|
+
|
|
27
34
|
|
|
28
35
|
exports.FetchError = FetchError;
|
|
29
36
|
exports.normalizeDoc = normalizeDoc;
|
|
37
|
+
|
|
38
|
+
function fetchEffectiveRecipients(fileId) {
|
|
39
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
40
|
+
void options;
|
|
41
|
+
return Promise.resolve({
|
|
42
|
+
data: [],
|
|
43
|
+
meta: {
|
|
44
|
+
file_id: fileId
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
30
49
|
var collectionMock = {
|
|
31
50
|
all: jest.fn(function () {
|
|
32
51
|
return Promise.resolve();
|
|
@@ -55,7 +74,8 @@ var collectionMock = {
|
|
|
55
74
|
}),
|
|
56
75
|
findReferencedBy: jest.fn(function () {
|
|
57
76
|
return Promise.resolve();
|
|
58
|
-
})
|
|
77
|
+
}),
|
|
78
|
+
fetchEffectiveRecipients: jest.fn(fetchEffectiveRecipients)
|
|
59
79
|
};
|
|
60
80
|
|
|
61
81
|
var MockedStackClient = /*#__PURE__*/function (_StackClient) {
|
package/dist/const.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.DOCTYPE_FILES_ENCRYPTION = exports.DOCTYPE_PERMISSIONS = exports.DOCTYPE_FILES = exports.REGISTRATION_ABORT = void 0;
|
|
6
|
+
exports.DOCTYPE_SHARING_RECIPIENTS = exports.DOCTYPE_FILES_ENCRYPTION = exports.DOCTYPE_PERMISSIONS = exports.DOCTYPE_FILES = exports.REGISTRATION_ABORT = void 0;
|
|
7
7
|
var REGISTRATION_ABORT = 'REGISTRATION_ABORT';
|
|
8
8
|
exports.REGISTRATION_ABORT = REGISTRATION_ABORT;
|
|
9
9
|
var DOCTYPE_FILES = 'io.cozy.files';
|
|
@@ -11,4 +11,6 @@ exports.DOCTYPE_FILES = DOCTYPE_FILES;
|
|
|
11
11
|
var DOCTYPE_PERMISSIONS = 'io.cozy.permissions';
|
|
12
12
|
exports.DOCTYPE_PERMISSIONS = DOCTYPE_PERMISSIONS;
|
|
13
13
|
var DOCTYPE_FILES_ENCRYPTION = 'io.cozy.files.encryption';
|
|
14
|
-
exports.DOCTYPE_FILES_ENCRYPTION = DOCTYPE_FILES_ENCRYPTION;
|
|
14
|
+
exports.DOCTYPE_FILES_ENCRYPTION = DOCTYPE_FILES_ENCRYPTION;
|
|
15
|
+
var DOCTYPE_SHARING_RECIPIENTS = 'io.cozy.sharings.recipients';
|
|
16
|
+
exports.DOCTYPE_SHARING_RECIPIENTS = DOCTYPE_SHARING_RECIPIENTS;
|
package/dist/links/StackLink.js
CHANGED
|
@@ -208,53 +208,69 @@ var StackLink = /*#__PURE__*/function (_CozyLink) {
|
|
|
208
208
|
|
|
209
209
|
case 8:
|
|
210
210
|
if (!_context2.t0) {
|
|
211
|
-
_context2.next =
|
|
211
|
+
_context2.next = 12;
|
|
212
212
|
break;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
if (!(operation.doctype === _const.DOCTYPE_SHARING_RECIPIENTS)) {
|
|
216
|
+
_context2.next = 11;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
throw new Error('Effective recipients cannot be fetched offline');
|
|
221
|
+
|
|
222
|
+
case 11:
|
|
215
223
|
return _context2.abrupt("return", forward(operation, options));
|
|
216
224
|
|
|
217
|
-
case
|
|
218
|
-
_context2.prev =
|
|
225
|
+
case 12:
|
|
226
|
+
_context2.prev = 12;
|
|
219
227
|
|
|
220
228
|
if (!operation.mutationType) {
|
|
221
|
-
_context2.next =
|
|
229
|
+
_context2.next = 17;
|
|
222
230
|
break;
|
|
223
231
|
}
|
|
224
232
|
|
|
225
|
-
_context2.next =
|
|
233
|
+
_context2.next = 16;
|
|
226
234
|
return this.executeMutation(operation, options, result, forward);
|
|
227
235
|
|
|
228
|
-
case
|
|
236
|
+
case 16:
|
|
229
237
|
return _context2.abrupt("return", _context2.sent);
|
|
230
238
|
|
|
231
|
-
case
|
|
232
|
-
_context2.next =
|
|
239
|
+
case 17:
|
|
240
|
+
_context2.next = 19;
|
|
233
241
|
return this.executeQuery(operation);
|
|
234
242
|
|
|
235
|
-
case
|
|
243
|
+
case 19:
|
|
236
244
|
return _context2.abrupt("return", _context2.sent);
|
|
237
245
|
|
|
238
|
-
case
|
|
239
|
-
_context2.prev =
|
|
240
|
-
_context2.t1 = _context2["catch"](
|
|
246
|
+
case 22:
|
|
247
|
+
_context2.prev = 22;
|
|
248
|
+
_context2.t1 = _context2["catch"](12);
|
|
241
249
|
|
|
242
250
|
if (!(forceLink !== 'stack' && (0, _utils.isReactNativeOfflineError)(_context2.t1))) {
|
|
243
|
-
_context2.next =
|
|
251
|
+
_context2.next = 28;
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (!(operation.doctype === _const.DOCTYPE_SHARING_RECIPIENTS)) {
|
|
256
|
+
_context2.next = 27;
|
|
244
257
|
break;
|
|
245
258
|
}
|
|
246
259
|
|
|
260
|
+
throw _context2.t1;
|
|
261
|
+
|
|
262
|
+
case 27:
|
|
247
263
|
return _context2.abrupt("return", forward(operation, options));
|
|
248
264
|
|
|
249
|
-
case
|
|
265
|
+
case 28:
|
|
250
266
|
throw _context2.t1;
|
|
251
267
|
|
|
252
|
-
case
|
|
268
|
+
case 29:
|
|
253
269
|
case "end":
|
|
254
270
|
return _context2.stop();
|
|
255
271
|
}
|
|
256
272
|
}
|
|
257
|
-
}, _callee2, this, [[
|
|
273
|
+
}, _callee2, this, [[12, 22]]);
|
|
258
274
|
}));
|
|
259
275
|
|
|
260
276
|
function request(_x, _x2, _x3, _x4) {
|
|
@@ -295,128 +311,204 @@ var StackLink = /*#__PURE__*/function (_CozyLink) {
|
|
|
295
311
|
|
|
296
312
|
}, {
|
|
297
313
|
key: "executeQuery",
|
|
298
|
-
value: function
|
|
299
|
-
var
|
|
300
|
-
|
|
301
|
-
id = query.id,
|
|
302
|
-
ids = query.ids,
|
|
303
|
-
referenced = query.referenced,
|
|
304
|
-
sharingId = query.sharingId,
|
|
305
|
-
rawOptions = (0, _objectWithoutProperties2.default)(query, ["doctype", "selector", "id", "ids", "referenced", "sharingId"]);
|
|
306
|
-
|
|
307
|
-
var options = _objectSpread(_objectSpread({}, rawOptions), {}, {
|
|
308
|
-
driveId: undefined
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
if (!doctype) {
|
|
312
|
-
_logger.default.warn('Bad query', query);
|
|
313
|
-
|
|
314
|
-
throw new Error('No doctype found in a query definition');
|
|
315
|
-
}
|
|
314
|
+
value: function () {
|
|
315
|
+
var _executeQuery = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(query) {
|
|
316
|
+
var doctype, selector, id, ids, referenced, sharingId, rawOptions, options, _selector$drive_id, fileId, driveId, response, collection;
|
|
316
317
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
318
|
+
return _regenerator.default.wrap(function _callee4$(_context4) {
|
|
319
|
+
while (1) {
|
|
320
|
+
switch (_context4.prev = _context4.next) {
|
|
321
|
+
case 0:
|
|
322
|
+
doctype = query.doctype, selector = query.selector, id = query.id, ids = query.ids, referenced = query.referenced, sharingId = query.sharingId, rawOptions = (0, _objectWithoutProperties2.default)(query, ["doctype", "selector", "id", "ids", "referenced", "sharingId"]);
|
|
323
|
+
options = _objectSpread(_objectSpread({}, rawOptions), {}, {
|
|
324
|
+
driveId: undefined
|
|
325
|
+
});
|
|
322
326
|
|
|
323
|
-
|
|
327
|
+
if (doctype) {
|
|
328
|
+
_context4.next = 5;
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
324
331
|
|
|
325
|
-
|
|
326
|
-
return collection.get(id, query);
|
|
327
|
-
}
|
|
332
|
+
_logger.default.warn('Bad query', query);
|
|
328
333
|
|
|
329
|
-
|
|
330
|
-
return collection.getAll(ids);
|
|
331
|
-
}
|
|
334
|
+
throw new Error('No doctype found in a query definition');
|
|
332
335
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
+
case 5:
|
|
337
|
+
if (doctype === _const.DOCTYPE_FILES && sharingId) {
|
|
338
|
+
options = _objectSpread(_objectSpread({}, options), {}, {
|
|
339
|
+
driveId: sharingId
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (!(doctype === _const.DOCTYPE_SHARING_RECIPIENTS)) {
|
|
344
|
+
_context4.next = 15;
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
fileId = selector === null || selector === void 0 ? void 0 : selector.file_id;
|
|
349
|
+
driveId = (_selector$drive_id = selector === null || selector === void 0 ? void 0 : selector.drive_id) !== null && _selector$drive_id !== void 0 ? _selector$drive_id : null;
|
|
350
|
+
|
|
351
|
+
if (fileId) {
|
|
352
|
+
_context4.next = 11;
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
throw new Error("".concat(_const.DOCTYPE_SHARING_RECIPIENTS, " must be queried with effectiveRecipients"));
|
|
336
357
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
358
|
+
case 11:
|
|
359
|
+
_context4.next = 13;
|
|
360
|
+
return this.stackClient.collection('io.cozy.sharings').fetchEffectiveRecipients(fileId, {
|
|
361
|
+
driveId: driveId
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
case 13:
|
|
365
|
+
response = _context4.sent;
|
|
366
|
+
return _context4.abrupt("return", _objectSpread(_objectSpread({}, response), {}, {
|
|
367
|
+
data: response.data.map(function (recipient) {
|
|
368
|
+
var recipientId = recipient.id,
|
|
369
|
+
attributes = (0, _objectWithoutProperties2.default)(recipient, ["id"]); // Recipient attributes vary by target, so IDs include the query scope.
|
|
370
|
+
|
|
371
|
+
var scope = driveId ? "drive/".concat(encodeURIComponent(driveId)) : 'personal';
|
|
372
|
+
var id = "".concat(scope, "/file/").concat(encodeURIComponent(fileId), "/recipient/").concat(encodeURIComponent(recipientId));
|
|
373
|
+
return _objectSpread({
|
|
374
|
+
id: id,
|
|
375
|
+
_id: id,
|
|
376
|
+
_type: _const.DOCTYPE_SHARING_RECIPIENTS,
|
|
377
|
+
recipient_id: recipientId,
|
|
378
|
+
file_id: fileId,
|
|
379
|
+
drive_id: driveId
|
|
380
|
+
}, attributes);
|
|
381
|
+
})
|
|
382
|
+
}));
|
|
383
|
+
|
|
384
|
+
case 15:
|
|
385
|
+
collection = this.stackClient.collection(doctype, options);
|
|
386
|
+
|
|
387
|
+
if (!id) {
|
|
388
|
+
_context4.next = 18;
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return _context4.abrupt("return", collection.get(id, query));
|
|
393
|
+
|
|
394
|
+
case 18:
|
|
395
|
+
if (!ids) {
|
|
396
|
+
_context4.next = 20;
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return _context4.abrupt("return", collection.getAll(ids));
|
|
401
|
+
|
|
402
|
+
case 20:
|
|
403
|
+
if (!referenced) {
|
|
404
|
+
_context4.next = 22;
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return _context4.abrupt("return", collection.findReferencedBy(referenced, options));
|
|
409
|
+
|
|
410
|
+
case 22:
|
|
411
|
+
if (!hasFindOptions(query)) {
|
|
412
|
+
_context4.next = 26;
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return _context4.abrupt("return", collection.find(selector, options));
|
|
417
|
+
|
|
418
|
+
case 26:
|
|
419
|
+
return _context4.abrupt("return", collection.all(options));
|
|
420
|
+
|
|
421
|
+
case 27:
|
|
422
|
+
case "end":
|
|
423
|
+
return _context4.stop();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}, _callee4, this);
|
|
427
|
+
}));
|
|
428
|
+
|
|
429
|
+
function executeQuery(_x7) {
|
|
430
|
+
return _executeQuery.apply(this, arguments);
|
|
341
431
|
}
|
|
342
|
-
|
|
432
|
+
|
|
433
|
+
return executeQuery;
|
|
434
|
+
}()
|
|
343
435
|
}, {
|
|
344
436
|
key: "executeMutation",
|
|
345
437
|
value: function () {
|
|
346
|
-
var _executeMutation = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function
|
|
438
|
+
var _executeMutation = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(mutation, options, result, forward) {
|
|
347
439
|
var mutationType, doc, docs, props, updateAllResp;
|
|
348
|
-
return _regenerator.default.wrap(function
|
|
440
|
+
return _regenerator.default.wrap(function _callee5$(_context5) {
|
|
349
441
|
while (1) {
|
|
350
|
-
switch (
|
|
442
|
+
switch (_context5.prev = _context5.next) {
|
|
351
443
|
case 0:
|
|
352
444
|
mutationType = mutation.mutationType, doc = mutation.document, docs = mutation.documents, props = (0, _objectWithoutProperties2.default)(mutation, ["mutationType", "document", "documents"]);
|
|
353
|
-
|
|
354
|
-
|
|
445
|
+
_context5.t0 = mutationType;
|
|
446
|
+
_context5.next = _context5.t0 === _dsl.MutationTypes.CREATE_DOCUMENT ? 4 : _context5.t0 === _dsl.MutationTypes.UPDATE_DOCUMENTS ? 5 : _context5.t0 === _dsl.MutationTypes.UPDATE_DOCUMENT ? 9 : _context5.t0 === _dsl.MutationTypes.DELETE_DOCUMENT ? 10 : _context5.t0 === _dsl.MutationTypes.DELETE_DOCUMENTS ? 11 : _context5.t0 === _dsl.MutationTypes.ADD_REFERENCES_TO ? 12 : _context5.t0 === _dsl.MutationTypes.REMOVE_REFERENCES_TO ? 13 : _context5.t0 === _dsl.MutationTypes.ADD_REFERENCED_BY ? 14 : _context5.t0 === _dsl.MutationTypes.REMOVE_REFERENCED_BY ? 19 : _context5.t0 === _dsl.MutationTypes.UPLOAD_FILE ? 24 : 25;
|
|
355
447
|
break;
|
|
356
448
|
|
|
357
449
|
case 4:
|
|
358
|
-
return
|
|
450
|
+
return _context5.abrupt("return", this.stackClient.collection(doc._type).create(doc));
|
|
359
451
|
|
|
360
452
|
case 5:
|
|
361
|
-
|
|
453
|
+
_context5.next = 7;
|
|
362
454
|
return this.stackClient.collection(docs[0]._type).updateAll(docs);
|
|
363
455
|
|
|
364
456
|
case 7:
|
|
365
|
-
updateAllResp =
|
|
366
|
-
return
|
|
457
|
+
updateAllResp = _context5.sent;
|
|
458
|
+
return _context5.abrupt("return", transformBulkDocsResponse(updateAllResp, docs));
|
|
367
459
|
|
|
368
460
|
case 9:
|
|
369
|
-
return
|
|
461
|
+
return _context5.abrupt("return", this.stackClient.collection(doc._type).update(doc));
|
|
370
462
|
|
|
371
463
|
case 10:
|
|
372
|
-
return
|
|
464
|
+
return _context5.abrupt("return", this.stackClient.collection(doc._type).destroy(doc));
|
|
373
465
|
|
|
374
466
|
case 11:
|
|
375
|
-
return
|
|
467
|
+
return _context5.abrupt("return", this.stackClient.collection(doc._type).destroyAll(docs));
|
|
376
468
|
|
|
377
469
|
case 12:
|
|
378
|
-
return
|
|
470
|
+
return _context5.abrupt("return", this.stackClient.collection(props.referencedDocuments[0]._type).addReferencesTo(doc, props.referencedDocuments));
|
|
379
471
|
|
|
380
472
|
case 13:
|
|
381
|
-
return
|
|
473
|
+
return _context5.abrupt("return", this.stackClient.collection(props.referencedDocuments[0]._type).removeReferencesTo(doc, props.referencedDocuments));
|
|
382
474
|
|
|
383
475
|
case 14:
|
|
384
476
|
if (!(doc._type === _const.DOCTYPE_FILES)) {
|
|
385
|
-
|
|
477
|
+
_context5.next = 18;
|
|
386
478
|
break;
|
|
387
479
|
}
|
|
388
480
|
|
|
389
|
-
return
|
|
481
|
+
return _context5.abrupt("return", this.stackClient.collection(_const.DOCTYPE_FILES).addReferencedBy(doc, props.referencedDocuments));
|
|
390
482
|
|
|
391
483
|
case 18:
|
|
392
484
|
throw new Error('The document type should be io.cozy.files');
|
|
393
485
|
|
|
394
486
|
case 19:
|
|
395
487
|
if (!(doc._type === _const.DOCTYPE_FILES)) {
|
|
396
|
-
|
|
488
|
+
_context5.next = 23;
|
|
397
489
|
break;
|
|
398
490
|
}
|
|
399
491
|
|
|
400
|
-
return
|
|
492
|
+
return _context5.abrupt("return", this.stackClient.collection(_const.DOCTYPE_FILES).removeReferencedBy(doc, props.referencedDocuments));
|
|
401
493
|
|
|
402
494
|
case 23:
|
|
403
495
|
throw new Error('The document type should be io.cozy.files');
|
|
404
496
|
|
|
405
497
|
case 24:
|
|
406
|
-
return
|
|
498
|
+
return _context5.abrupt("return", this.stackClient.collection(_const.DOCTYPE_FILES).upload(props.file, props.dirPath));
|
|
407
499
|
|
|
408
500
|
case 25:
|
|
409
|
-
return
|
|
501
|
+
return _context5.abrupt("return", forward(mutation, options, result));
|
|
410
502
|
|
|
411
503
|
case 26:
|
|
412
504
|
case "end":
|
|
413
|
-
return
|
|
505
|
+
return _context5.stop();
|
|
414
506
|
}
|
|
415
507
|
}
|
|
416
|
-
},
|
|
508
|
+
}, _callee5, this);
|
|
417
509
|
}));
|
|
418
510
|
|
|
419
|
-
function executeMutation(
|
|
511
|
+
function executeMutation(_x8, _x9, _x10, _x11) {
|
|
420
512
|
return _executeMutation.apply(this, arguments);
|
|
421
513
|
}
|
|
422
514
|
|
|
@@ -134,6 +134,96 @@ describe('StackLink', () => {
|
|
|
134
134
|
expect.anything()
|
|
135
135
|
)
|
|
136
136
|
})
|
|
137
|
+
|
|
138
|
+
it('should fetch and normalize effective recipients for a classic sharing', async () => {
|
|
139
|
+
const query = Q('io.cozy.sharings.recipients').effectiveRecipients(
|
|
140
|
+
'file/1'
|
|
141
|
+
)
|
|
142
|
+
stackClient.collection().fetchEffectiveRecipients.mockResolvedValue({
|
|
143
|
+
data: [
|
|
144
|
+
{
|
|
145
|
+
id: 'https://alice.example',
|
|
146
|
+
name: 'Alice',
|
|
147
|
+
read_only: false
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
meta: { file_id: 'file/1' }
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
const response = await link.request(query)
|
|
154
|
+
|
|
155
|
+
expect(stackClient.collection).toHaveBeenCalledWith('io.cozy.sharings')
|
|
156
|
+
expect(
|
|
157
|
+
stackClient.collection().fetchEffectiveRecipients
|
|
158
|
+
).toHaveBeenCalledWith('file/1', { driveId: null })
|
|
159
|
+
expect(response).toEqual({
|
|
160
|
+
data: [
|
|
161
|
+
{
|
|
162
|
+
id: 'personal/file/file%2F1/recipient/https%3A%2F%2Falice.example',
|
|
163
|
+
_id: 'personal/file/file%2F1/recipient/https%3A%2F%2Falice.example',
|
|
164
|
+
_type: 'io.cozy.sharings.recipients',
|
|
165
|
+
recipient_id: 'https://alice.example',
|
|
166
|
+
file_id: 'file/1',
|
|
167
|
+
drive_id: null,
|
|
168
|
+
name: 'Alice',
|
|
169
|
+
read_only: false
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
meta: { file_id: 'file/1' }
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('should fetch effective recipients in a shared drive scope', async () => {
|
|
177
|
+
const query = Q('io.cozy.sharings.recipients').effectiveRecipients(
|
|
178
|
+
'file-1',
|
|
179
|
+
{ driveId: 'drive/1' }
|
|
180
|
+
)
|
|
181
|
+
stackClient.collection().fetchEffectiveRecipients.mockResolvedValue({
|
|
182
|
+
data: [{ id: 'alice@example.test', name: 'Alice' }]
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
const response = await link.request(query)
|
|
186
|
+
|
|
187
|
+
expect(
|
|
188
|
+
stackClient.collection().fetchEffectiveRecipients
|
|
189
|
+
).toHaveBeenCalledWith('file-1', { driveId: 'drive/1' })
|
|
190
|
+
expect(response.data[0]).toMatchObject({
|
|
191
|
+
id: 'drive/drive%2F1/file/file-1/recipient/alice%40example.test',
|
|
192
|
+
recipient_id: 'alice@example.test',
|
|
193
|
+
file_id: 'file-1',
|
|
194
|
+
drive_id: 'drive/1'
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('should scope virtual document ids by target', async () => {
|
|
199
|
+
stackClient.collection().fetchEffectiveRecipients.mockResolvedValue({
|
|
200
|
+
data: [{ id: 'sharing-1:0', name: 'Alice' }]
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
const fileResponse = await link.request(
|
|
204
|
+
Q('io.cozy.sharings.recipients').effectiveRecipients('file-1')
|
|
205
|
+
)
|
|
206
|
+
const childResponse = await link.request(
|
|
207
|
+
Q('io.cozy.sharings.recipients').effectiveRecipients('child-1')
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
expect(fileResponse.data[0].recipient_id).toBe('sharing-1:0')
|
|
211
|
+
expect(childResponse.data[0].recipient_id).toBe('sharing-1:0')
|
|
212
|
+
expect(fileResponse.data[0].id).not.toBe(childResponse.data[0].id)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('should not forward effective recipients queries when offline', async () => {
|
|
216
|
+
const query = Q('io.cozy.sharings.recipients').effectiveRecipients(
|
|
217
|
+
'file-1'
|
|
218
|
+
)
|
|
219
|
+
const forward = jest.fn()
|
|
220
|
+
link.isOnline = jest.fn().mockResolvedValue(false)
|
|
221
|
+
|
|
222
|
+
await expect(link.request(query, {}, null, forward)).rejects.toThrow(
|
|
223
|
+
'Effective recipients cannot be fetched offline'
|
|
224
|
+
)
|
|
225
|
+
expect(forward).not.toHaveBeenCalled()
|
|
226
|
+
})
|
|
137
227
|
})
|
|
138
228
|
|
|
139
229
|
describe('reset', () => {
|
package/dist/queries/dsl.js
CHANGED
|
@@ -15,6 +15,8 @@ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/creat
|
|
|
15
15
|
|
|
16
16
|
var _findKey = _interopRequireDefault(require("lodash/findKey"));
|
|
17
17
|
|
|
18
|
+
var _const = require("../const");
|
|
19
|
+
|
|
18
20
|
var _logger = _interopRequireDefault(require("../logger"));
|
|
19
21
|
|
|
20
22
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
|
|
@@ -460,6 +462,38 @@ var QueryDefinition = /*#__PURE__*/function () {
|
|
|
460
462
|
sharingId: id
|
|
461
463
|
}));
|
|
462
464
|
}
|
|
465
|
+
/**
|
|
466
|
+
* Query all recipients who can access a file or folder, either through a
|
|
467
|
+
* direct sharing on the target or through a sharing inherited from an ancestor.
|
|
468
|
+
*
|
|
469
|
+
* @param {string} fileId - The target file or folder id
|
|
470
|
+
* @param {{driveId?: string|null}} [options] - Query options; driveId scopes a shared drive and defaults to null
|
|
471
|
+
* @returns {QueryDefinition} The QueryDefinition object.
|
|
472
|
+
*/
|
|
473
|
+
|
|
474
|
+
}, {
|
|
475
|
+
key: "effectiveRecipients",
|
|
476
|
+
value: function effectiveRecipients(fileId) {
|
|
477
|
+
var _options$driveId;
|
|
478
|
+
|
|
479
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
480
|
+
|
|
481
|
+
if (this.doctype !== _const.DOCTYPE_SHARING_RECIPIENTS) {
|
|
482
|
+
throw new Error("effectiveRecipients must be called on ".concat(_const.DOCTYPE_SHARING_RECIPIENTS));
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (!fileId) {
|
|
486
|
+
throw new Error('effectiveRecipients called with undefined fileId');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
var driveId = (_options$driveId = options.driveId) !== null && _options$driveId !== void 0 ? _options$driveId : null;
|
|
490
|
+
return new QueryDefinition(_objectSpread(_objectSpread({}, this.toDefinition()), {}, {
|
|
491
|
+
selector: {
|
|
492
|
+
file_id: fileId,
|
|
493
|
+
drive_id: driveId
|
|
494
|
+
}
|
|
495
|
+
}));
|
|
496
|
+
}
|
|
463
497
|
}, {
|
|
464
498
|
key: "toDefinition",
|
|
465
499
|
value: function toDefinition() {
|
package/dist/queries/dsl.spec.js
CHANGED
|
@@ -44,6 +44,38 @@ describe('QueryDefinition', () => {
|
|
|
44
44
|
})
|
|
45
45
|
})
|
|
46
46
|
|
|
47
|
+
it('should build an effective recipients query for a classic sharing', () => {
|
|
48
|
+
const query = Q('io.cozy.sharings.recipients').effectiveRecipients('file-1')
|
|
49
|
+
|
|
50
|
+
expect(query).toMatchObject({
|
|
51
|
+
doctype: 'io.cozy.sharings.recipients',
|
|
52
|
+
selector: { file_id: 'file-1', drive_id: null }
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should build an effective recipients query for a shared drive', () => {
|
|
57
|
+
const query = Q('io.cozy.sharings.recipients').effectiveRecipients(
|
|
58
|
+
'file-1',
|
|
59
|
+
{ driveId: 'drive-1' }
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
expect(query).toMatchObject({
|
|
63
|
+
selector: { file_id: 'file-1', drive_id: 'drive-1' }
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('should reject effective recipients queries on another doctype', () => {
|
|
68
|
+
expect(() => Q('io.cozy.sharings').effectiveRecipients('file-1')).toThrow(
|
|
69
|
+
'effectiveRecipients must be called on io.cozy.sharings.recipients'
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('should reject effective recipients queries without a file id', () => {
|
|
74
|
+
expect(() =>
|
|
75
|
+
Q('io.cozy.sharings.recipients').effectiveRecipients(null)
|
|
76
|
+
).toThrow('effectiveRecipients called with undefined fileId')
|
|
77
|
+
})
|
|
78
|
+
|
|
47
79
|
it('paginates only one way', () => {
|
|
48
80
|
const q = Q('io.cozy.files')
|
|
49
81
|
const withSkip = q.offset(2)
|
package/dist/storage.js
CHANGED
|
@@ -13,6 +13,8 @@ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/
|
|
|
13
13
|
|
|
14
14
|
var _CozyClient = _interopRequireDefault(require("./CozyClient"));
|
|
15
15
|
|
|
16
|
+
var _const = require("./const");
|
|
17
|
+
|
|
16
18
|
var _dsl = require("./queries/dsl");
|
|
17
19
|
|
|
18
20
|
function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
@@ -46,7 +48,7 @@ var shouldDocumentBePersisted = function shouldDocumentBePersisted(document) {
|
|
|
46
48
|
|
|
47
49
|
var shouldEnforce = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
48
50
|
|
|
49
|
-
if (!document || document.cozyLocalOnly) {
|
|
51
|
+
if (!document || document.cozyLocalOnly || document._type === _const.DOCTYPE_SHARING_RECIPIENTS) {
|
|
50
52
|
return false;
|
|
51
53
|
}
|
|
52
54
|
|
package/dist/storage.spec.js
CHANGED
|
@@ -11,6 +11,15 @@ describe('shouldDocumentBePersisted', () => {
|
|
|
11
11
|
expect(shouldDocumentBePersisted(doc)).toBe(false)
|
|
12
12
|
})
|
|
13
13
|
|
|
14
|
+
it('should not persist effective recipients', () => {
|
|
15
|
+
const doc = {
|
|
16
|
+
...BASE_DOC,
|
|
17
|
+
_type: 'io.cozy.sharings.recipients'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
expect(shouldDocumentBePersisted(doc)).toBe(false)
|
|
21
|
+
})
|
|
22
|
+
|
|
14
23
|
it('should not persist a doc with rev', () => {
|
|
15
24
|
const doc1 = { ...BASE_DOC, _rev: '123' }
|
|
16
25
|
expect(shouldDocumentBePersisted(doc1)).toBe(false)
|
package/dist/store/queries.js
CHANGED
|
@@ -49,6 +49,8 @@ var _helpers = require("./helpers");
|
|
|
49
49
|
|
|
50
50
|
var _dsl = require("../queries/dsl");
|
|
51
51
|
|
|
52
|
+
var _const = require("../const");
|
|
53
|
+
|
|
52
54
|
var _logger = _interopRequireDefault(require("../logger"));
|
|
53
55
|
|
|
54
56
|
var _stateHelpers = require("./stateHelpers");
|
|
@@ -264,6 +266,21 @@ var query = function query(performanceApi) {
|
|
|
264
266
|
|
|
265
267
|
var count = response.meta && response.meta.count ? response.meta.count : response.data.length;
|
|
266
268
|
|
|
269
|
+
if (state.definition.doctype === _const.DOCTYPE_SHARING_RECIPIENTS) {
|
|
270
|
+
performanceApi.measure({
|
|
271
|
+
markName: markName,
|
|
272
|
+
measureName: "".concat(markName, " with full snapshot"),
|
|
273
|
+
category: 'CozyClientStore'
|
|
274
|
+
});
|
|
275
|
+
return _objectSpread(_objectSpread(_objectSpread({}, state), common), {}, {
|
|
276
|
+
bookmark: null,
|
|
277
|
+
hasMore: false,
|
|
278
|
+
count: count,
|
|
279
|
+
fetchedPagesCount: 1,
|
|
280
|
+
data: response.data.map(_helpers.properId)
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
267
284
|
if (action.backgroundFetching) {
|
|
268
285
|
performanceApi.measure({
|
|
269
286
|
markName: markName,
|
|
@@ -142,6 +142,36 @@ describe('queries reducer', () => {
|
|
|
142
142
|
|
|
143
143
|
expect(state.a.data).toEqual([TODO_1._id])
|
|
144
144
|
})
|
|
145
|
+
|
|
146
|
+
it('should replace the previous result for a full snapshot query', () => {
|
|
147
|
+
const recipient1 = {
|
|
148
|
+
_id: 'file-1/recipient-1',
|
|
149
|
+
_type: 'io.cozy.sharings.recipients'
|
|
150
|
+
}
|
|
151
|
+
const recipient2 = {
|
|
152
|
+
_id: 'file-1/recipient-2',
|
|
153
|
+
_type: 'io.cozy.sharings.recipients'
|
|
154
|
+
}
|
|
155
|
+
const queryDefinition = Q(
|
|
156
|
+
'io.cozy.sharings.recipients'
|
|
157
|
+
).effectiveRecipients('file-1')
|
|
158
|
+
applyAction(initQuery('recipients', queryDefinition))
|
|
159
|
+
|
|
160
|
+
applyAction(
|
|
161
|
+
receiveQueryResult('recipients', {
|
|
162
|
+
data: [recipient1, recipient2]
|
|
163
|
+
})
|
|
164
|
+
)
|
|
165
|
+
applyAction(
|
|
166
|
+
receiveQueryResult('recipients', {
|
|
167
|
+
data: [recipient1]
|
|
168
|
+
})
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
expect(state.recipients.data).toEqual([recipient1._id])
|
|
172
|
+
expect(state.recipients.count).toBe(1)
|
|
173
|
+
expect(state.recipients.hasMore).toBe(false)
|
|
174
|
+
})
|
|
145
175
|
})
|
|
146
176
|
|
|
147
177
|
describe('RECEIVE_QUERY_ERROR', () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-client",
|
|
3
|
-
"version": "60.
|
|
3
|
+
"version": "60.33.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.node.js",
|
|
6
6
|
"browser": "dist/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"@types/jest": "^26.0.20",
|
|
20
20
|
"@types/lodash": "^4.14.170",
|
|
21
21
|
"btoa": "^1.2.1",
|
|
22
|
-
"cozy-stack-client": "^60.
|
|
22
|
+
"cozy-stack-client": "^60.33.0",
|
|
23
23
|
"date-fns": "^2.30.0",
|
|
24
24
|
"fast-deep-equal": "^3.1.3",
|
|
25
25
|
"json-stable-stringify": "^1.0.1",
|
|
@@ -74,5 +74,5 @@
|
|
|
74
74
|
"react-native-ios11-devicecheck": "^0.0.3"
|
|
75
75
|
},
|
|
76
76
|
"sideEffects": false,
|
|
77
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "a93abce8ac4c9f8538818dc48593dde32e774720"
|
|
78
78
|
}
|
|
@@ -13,6 +13,14 @@ export class OAuthClient extends OAuthClient_base {
|
|
|
13
13
|
updateAll: jest.Mock<Promise<void>, []>;
|
|
14
14
|
destroy: jest.Mock<Promise<void>, []>;
|
|
15
15
|
findReferencedBy: jest.Mock<Promise<void>, []>;
|
|
16
|
+
fetchEffectiveRecipients: jest.Mock<Promise<{
|
|
17
|
+
data: object[];
|
|
18
|
+
meta: {
|
|
19
|
+
file_id: string;
|
|
20
|
+
};
|
|
21
|
+
}>, [fileId: string, options?: {
|
|
22
|
+
driveId?: string | null;
|
|
23
|
+
}]>;
|
|
16
24
|
}, []>;
|
|
17
25
|
}
|
|
18
26
|
export default MockedStackClient;
|
|
@@ -31,6 +39,14 @@ declare class MockedStackClient extends MockedStackClient_base {
|
|
|
31
39
|
updateAll: jest.Mock<Promise<void>, []>;
|
|
32
40
|
destroy: jest.Mock<Promise<void>, []>;
|
|
33
41
|
findReferencedBy: jest.Mock<Promise<void>, []>;
|
|
42
|
+
fetchEffectiveRecipients: jest.Mock<Promise<{
|
|
43
|
+
data: object[];
|
|
44
|
+
meta: {
|
|
45
|
+
file_id: string;
|
|
46
|
+
};
|
|
47
|
+
}>, [fileId: string, options?: {
|
|
48
|
+
driveId?: string | null;
|
|
49
|
+
}]>;
|
|
34
50
|
}, []>;
|
|
35
51
|
}
|
|
36
52
|
export const normalizeDoc: any;
|
package/types/const.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export const REGISTRATION_ABORT: "REGISTRATION_ABORT";
|
|
|
2
2
|
export const DOCTYPE_FILES: "io.cozy.files";
|
|
3
3
|
export const DOCTYPE_PERMISSIONS: "io.cozy.permissions";
|
|
4
4
|
export const DOCTYPE_FILES_ENCRYPTION: "io.cozy.files.encryption";
|
|
5
|
+
export const DOCTYPE_SHARING_RECIPIENTS: "io.cozy.sharings.recipients";
|
package/types/queries/dsl.d.ts
CHANGED
|
@@ -301,6 +301,17 @@ export class QueryDefinition {
|
|
|
301
301
|
* @returns {QueryDefinition} The QueryDefinition object.
|
|
302
302
|
*/
|
|
303
303
|
sharingById(id: string): QueryDefinition;
|
|
304
|
+
/**
|
|
305
|
+
* Query all recipients who can access a file or folder, either through a
|
|
306
|
+
* direct sharing on the target or through a sharing inherited from an ancestor.
|
|
307
|
+
*
|
|
308
|
+
* @param {string} fileId - The target file or folder id
|
|
309
|
+
* @param {{driveId?: string|null}} [options] - Query options; driveId scopes a shared drive and defaults to null
|
|
310
|
+
* @returns {QueryDefinition} The QueryDefinition object.
|
|
311
|
+
*/
|
|
312
|
+
effectiveRecipients(fileId: string, options?: {
|
|
313
|
+
driveId?: string | null;
|
|
314
|
+
}): QueryDefinition;
|
|
304
315
|
toDefinition(): {
|
|
305
316
|
doctype: string;
|
|
306
317
|
id: string;
|