cozy-sharing 33.6.0 → 33.6.1
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/CHANGELOG.md +6 -0
- package/dist/SharingProvider.spec.js +242 -113
- package/dist/state.js +4 -3
- package/dist/state.spec.js +44 -27
- package/package.json +2 -2
- package/src/SharingProvider.spec.jsx +115 -1
- package/src/state.js +4 -12
- package/src/state.spec.js +63 -29
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [33.6.1](https://github.com/cozy/cozy-libs/compare/cozy-sharing@33.6.0...cozy-sharing@33.6.1) (2026-06-22)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- Forget owner-only shared drive sharings ([795eff2](https://github.com/cozy/cozy-libs/commit/795eff246218780d6315ae49d4b55dbe732c450b))
|
|
11
|
+
|
|
6
12
|
# [33.6.0](https://github.com/cozy/cozy-libs/compare/cozy-sharing@33.5.1...cozy-sharing@33.6.0) (2026-06-17)
|
|
7
13
|
|
|
8
14
|
### Features
|
|
@@ -11,7 +11,7 @@ import React from 'react';
|
|
|
11
11
|
import { createMockClient } from 'cozy-client';
|
|
12
12
|
import { SharingProvider } from './SharingProvider';
|
|
13
13
|
import SharingContext from './context';
|
|
14
|
-
import reducer, { addSharingLink, getDocumentPermissions } from './state';
|
|
14
|
+
import reducer, { addSharingLink, getDocumentPermissions, getDocumentSharing, receiveSharings } from './state';
|
|
15
15
|
import AppLike from '../test/AppLike';
|
|
16
16
|
|
|
17
17
|
var AppWrapper = function AppWrapper(_ref) {
|
|
@@ -411,6 +411,135 @@ describe('updateDocumentPermissions', function () {
|
|
|
411
411
|
}, _callee7);
|
|
412
412
|
})));
|
|
413
413
|
});
|
|
414
|
+
describe('shared drive recipient revocation', function () {
|
|
415
|
+
var SHARED_DRIVE = {
|
|
416
|
+
_id: 'sharing_123',
|
|
417
|
+
id: 'sharing_123',
|
|
418
|
+
type: 'io.cozy.sharings',
|
|
419
|
+
attributes: {
|
|
420
|
+
drive: true,
|
|
421
|
+
owner: true,
|
|
422
|
+
description: 'Shared folder',
|
|
423
|
+
rules: [{
|
|
424
|
+
title: 'Shared folder',
|
|
425
|
+
doctype: 'io.cozy.files',
|
|
426
|
+
values: ['folder_123'],
|
|
427
|
+
add: 'none',
|
|
428
|
+
update: 'none',
|
|
429
|
+
remove: 'none'
|
|
430
|
+
}],
|
|
431
|
+
members: [{
|
|
432
|
+
status: 'owner',
|
|
433
|
+
email: 'owner@cozy.local',
|
|
434
|
+
instance: 'http://cozy.local'
|
|
435
|
+
}, {
|
|
436
|
+
status: 'pending',
|
|
437
|
+
email: 'recipient@cozy.local',
|
|
438
|
+
instance: 'http://recipient.cozy.local'
|
|
439
|
+
}]
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
var NEW_SHARING = _objectSpread(_objectSpread({}, SHARED_DRIVE), {}, {
|
|
444
|
+
_id: 'sharing_456',
|
|
445
|
+
id: 'sharing_456'
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
var document = {
|
|
449
|
+
_id: 'folder_123',
|
|
450
|
+
id: 'folder_123',
|
|
451
|
+
path: '/Shared folder'
|
|
452
|
+
};
|
|
453
|
+
var recipient = {
|
|
454
|
+
_id: 'contact_123',
|
|
455
|
+
id: 'contact_123',
|
|
456
|
+
_type: 'io.cozy.contacts',
|
|
457
|
+
email: 'recipient@cozy.local'
|
|
458
|
+
};
|
|
459
|
+
var provider;
|
|
460
|
+
var sharingCol;
|
|
461
|
+
beforeEach(function () {
|
|
462
|
+
var mockClient = createMockClient({});
|
|
463
|
+
|
|
464
|
+
mockClient.getStackClient = function () {
|
|
465
|
+
return {
|
|
466
|
+
uri: 'http://cozy.local'
|
|
467
|
+
};
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
mockClient.collection = jest.fn().mockReturnValue({});
|
|
471
|
+
provider = new SharingProvider({
|
|
472
|
+
client: mockClient
|
|
473
|
+
});
|
|
474
|
+
provider.state = _objectSpread(_objectSpread({}, provider.state), reducer(undefined, receiveSharings({
|
|
475
|
+
sharings: [SHARED_DRIVE]
|
|
476
|
+
})));
|
|
477
|
+
provider.dispatch = jest.fn(function (action) {
|
|
478
|
+
provider.state = _objectSpread(_objectSpread({}, provider.state), reducer(provider.state, action));
|
|
479
|
+
});
|
|
480
|
+
sharingCol = {
|
|
481
|
+
addRecipients: jest.fn(),
|
|
482
|
+
create: jest.fn().mockResolvedValue({
|
|
483
|
+
data: NEW_SHARING
|
|
484
|
+
}),
|
|
485
|
+
revokeRecipient: jest.fn().mockResolvedValue({})
|
|
486
|
+
};
|
|
487
|
+
provider.sharingCol = sharingCol;
|
|
488
|
+
});
|
|
489
|
+
it('forgets the shared drive when revoking its last recipient', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee8() {
|
|
490
|
+
return _regeneratorRuntime.wrap(function _callee8$(_context8) {
|
|
491
|
+
while (1) switch (_context8.prev = _context8.next) {
|
|
492
|
+
case 0:
|
|
493
|
+
_context8.next = 2;
|
|
494
|
+
return provider.revoke(document, SHARED_DRIVE.id, 1);
|
|
495
|
+
|
|
496
|
+
case 2:
|
|
497
|
+
expect(sharingCol.revokeRecipient).toHaveBeenCalledWith(SHARED_DRIVE, 1);
|
|
498
|
+
expect(getDocumentSharing(provider.state, document.id)).toBeNull();
|
|
499
|
+
|
|
500
|
+
case 4:
|
|
501
|
+
case "end":
|
|
502
|
+
return _context8.stop();
|
|
503
|
+
}
|
|
504
|
+
}, _callee8);
|
|
505
|
+
})));
|
|
506
|
+
it('creates a new sharing when re-adding after the last recipient was revoked', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee9() {
|
|
507
|
+
return _regeneratorRuntime.wrap(function _callee9$(_context9) {
|
|
508
|
+
while (1) switch (_context9.prev = _context9.next) {
|
|
509
|
+
case 0:
|
|
510
|
+
_context9.next = 2;
|
|
511
|
+
return provider.revoke(document, SHARED_DRIVE.id, 1);
|
|
512
|
+
|
|
513
|
+
case 2:
|
|
514
|
+
_context9.next = 4;
|
|
515
|
+
return provider.share({
|
|
516
|
+
document: document,
|
|
517
|
+
recipients: [recipient],
|
|
518
|
+
readOnlyRecipients: [],
|
|
519
|
+
description: 'Shared folder',
|
|
520
|
+
openSharing: true,
|
|
521
|
+
sharedDrive: true
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
case 4:
|
|
525
|
+
expect(sharingCol.addRecipients).not.toHaveBeenCalled();
|
|
526
|
+
expect(sharingCol.create).toHaveBeenCalledWith({
|
|
527
|
+
document: document,
|
|
528
|
+
recipients: [recipient],
|
|
529
|
+
readOnlyRecipients: [],
|
|
530
|
+
description: 'Shared folder',
|
|
531
|
+
previewPath: '/preview',
|
|
532
|
+
openSharing: true,
|
|
533
|
+
sharedDrive: true
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
case 6:
|
|
537
|
+
case "end":
|
|
538
|
+
return _context9.stop();
|
|
539
|
+
}
|
|
540
|
+
}, _callee9);
|
|
541
|
+
})));
|
|
542
|
+
});
|
|
414
543
|
describe('fetchSharedDriveSharingLinks', function () {
|
|
415
544
|
var PERM_DRIVE_FILE = {
|
|
416
545
|
type: 'io.cozy.permissions',
|
|
@@ -434,10 +563,10 @@ describe('fetchSharedDriveSharingLinks', function () {
|
|
|
434
563
|
id: 'file_in_drive',
|
|
435
564
|
driveId: 'drive_123'
|
|
436
565
|
};
|
|
437
|
-
it('fetches shared drive sharing links by file id', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
566
|
+
it('fetches shared drive sharing links by file id', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10() {
|
|
438
567
|
var mockFindLinksByIds, mockClient, provider, result, permissions;
|
|
439
|
-
return _regeneratorRuntime.wrap(function
|
|
440
|
-
while (1) switch (
|
|
568
|
+
return _regeneratorRuntime.wrap(function _callee10$(_context10) {
|
|
569
|
+
while (1) switch (_context10.prev = _context10.next) {
|
|
441
570
|
case 0:
|
|
442
571
|
mockFindLinksByIds = jest.fn().mockResolvedValue({
|
|
443
572
|
data: [PERM_DRIVE_FILE]
|
|
@@ -453,11 +582,11 @@ describe('fetchSharedDriveSharingLinks', function () {
|
|
|
453
582
|
provider.dispatch = jest.fn(function (action) {
|
|
454
583
|
provider.state = reducer(provider.state, action);
|
|
455
584
|
});
|
|
456
|
-
|
|
585
|
+
_context10.next = 8;
|
|
457
586
|
return provider.fetchSharedDriveSharingLinks(driveFile);
|
|
458
587
|
|
|
459
588
|
case 8:
|
|
460
|
-
result =
|
|
589
|
+
result = _context10.sent;
|
|
461
590
|
expect(mockClient.collection).toHaveBeenCalledWith('io.cozy.permissions', {
|
|
462
591
|
driveId: 'drive_123'
|
|
463
592
|
});
|
|
@@ -470,14 +599,14 @@ describe('fetchSharedDriveSharingLinks', function () {
|
|
|
470
599
|
|
|
471
600
|
case 16:
|
|
472
601
|
case "end":
|
|
473
|
-
return
|
|
602
|
+
return _context10.stop();
|
|
474
603
|
}
|
|
475
|
-
},
|
|
604
|
+
}, _callee10);
|
|
476
605
|
})));
|
|
477
|
-
it('returns an empty array and skips the API call for non-shared-drive documents', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
606
|
+
it('returns an empty array and skips the API call for non-shared-drive documents', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11() {
|
|
478
607
|
var mockClient, findLinksByIds, provider, result;
|
|
479
|
-
return _regeneratorRuntime.wrap(function
|
|
480
|
-
while (1) switch (
|
|
608
|
+
return _regeneratorRuntime.wrap(function _callee11$(_context11) {
|
|
609
|
+
while (1) switch (_context11.prev = _context11.next) {
|
|
481
610
|
case 0:
|
|
482
611
|
mockClient = createMockClient({});
|
|
483
612
|
findLinksByIds = jest.fn();
|
|
@@ -488,27 +617,27 @@ describe('fetchSharedDriveSharingLinks', function () {
|
|
|
488
617
|
client: mockClient
|
|
489
618
|
});
|
|
490
619
|
provider.dispatch = jest.fn();
|
|
491
|
-
|
|
620
|
+
_context11.next = 7;
|
|
492
621
|
return provider.fetchSharedDriveSharingLinks({
|
|
493
622
|
_id: 'regular_file'
|
|
494
623
|
});
|
|
495
624
|
|
|
496
625
|
case 7:
|
|
497
|
-
result =
|
|
626
|
+
result = _context11.sent;
|
|
498
627
|
expect(result).toEqual([]);
|
|
499
628
|
expect(findLinksByIds).not.toHaveBeenCalled();
|
|
500
629
|
expect(provider.dispatch).not.toHaveBeenCalled();
|
|
501
630
|
|
|
502
631
|
case 11:
|
|
503
632
|
case "end":
|
|
504
|
-
return
|
|
633
|
+
return _context11.stop();
|
|
505
634
|
}
|
|
506
|
-
},
|
|
635
|
+
}, _callee11);
|
|
507
636
|
})));
|
|
508
|
-
it('uses document.id as a fallback when _id is missing', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
637
|
+
it('uses document.id as a fallback when _id is missing', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12() {
|
|
509
638
|
var mockFindLinksByIds, mockClient, provider;
|
|
510
|
-
return _regeneratorRuntime.wrap(function
|
|
511
|
-
while (1) switch (
|
|
639
|
+
return _regeneratorRuntime.wrap(function _callee12$(_context12) {
|
|
640
|
+
while (1) switch (_context12.prev = _context12.next) {
|
|
512
641
|
case 0:
|
|
513
642
|
mockFindLinksByIds = jest.fn().mockResolvedValue({
|
|
514
643
|
data: [PERM_DRIVE_FILE]
|
|
@@ -524,7 +653,7 @@ describe('fetchSharedDriveSharingLinks', function () {
|
|
|
524
653
|
provider.dispatch = jest.fn(function (action) {
|
|
525
654
|
provider.state = reducer(provider.state, action);
|
|
526
655
|
});
|
|
527
|
-
|
|
656
|
+
_context12.next = 8;
|
|
528
657
|
return provider.fetchSharedDriveSharingLinks({
|
|
529
658
|
id: 'file_in_drive',
|
|
530
659
|
driveId: 'drive_123'
|
|
@@ -535,14 +664,14 @@ describe('fetchSharedDriveSharingLinks', function () {
|
|
|
535
664
|
|
|
536
665
|
case 9:
|
|
537
666
|
case "end":
|
|
538
|
-
return
|
|
667
|
+
return _context12.stop();
|
|
539
668
|
}
|
|
540
|
-
},
|
|
669
|
+
}, _callee12);
|
|
541
670
|
})));
|
|
542
|
-
it('does not dispatch when the response has no data', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
671
|
+
it('does not dispatch when the response has no data', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee13() {
|
|
543
672
|
var mockFindLinksByIds, mockClient, provider, result;
|
|
544
|
-
return _regeneratorRuntime.wrap(function
|
|
545
|
-
while (1) switch (
|
|
673
|
+
return _regeneratorRuntime.wrap(function _callee13$(_context13) {
|
|
674
|
+
while (1) switch (_context13.prev = _context13.next) {
|
|
546
675
|
case 0:
|
|
547
676
|
mockFindLinksByIds = jest.fn().mockResolvedValue({
|
|
548
677
|
data: []
|
|
@@ -555,19 +684,19 @@ describe('fetchSharedDriveSharingLinks', function () {
|
|
|
555
684
|
client: mockClient
|
|
556
685
|
});
|
|
557
686
|
provider.dispatch = jest.fn();
|
|
558
|
-
|
|
687
|
+
_context13.next = 7;
|
|
559
688
|
return provider.fetchSharedDriveSharingLinks(driveFile);
|
|
560
689
|
|
|
561
690
|
case 7:
|
|
562
|
-
result =
|
|
691
|
+
result = _context13.sent;
|
|
563
692
|
expect(result).toEqual([]);
|
|
564
693
|
expect(provider.dispatch).not.toHaveBeenCalled();
|
|
565
694
|
|
|
566
695
|
case 10:
|
|
567
696
|
case "end":
|
|
568
|
-
return
|
|
697
|
+
return _context13.stop();
|
|
569
698
|
}
|
|
570
|
-
},
|
|
699
|
+
}, _callee13);
|
|
571
700
|
})));
|
|
572
701
|
});
|
|
573
702
|
describe('shareByLink shared drive 409 recovery', function () {
|
|
@@ -593,10 +722,10 @@ describe('shareByLink shared drive 409 recovery', function () {
|
|
|
593
722
|
id: 'file_in_drive',
|
|
594
723
|
driveId: 'drive_123'
|
|
595
724
|
};
|
|
596
|
-
it('falls back to fetching existing links when create returns 409', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
725
|
+
it('falls back to fetching existing links when create returns 409', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14() {
|
|
597
726
|
var conflict, mockCreateSharingLink, mockFindLinksByIds, mockClient, provider, resp;
|
|
598
|
-
return _regeneratorRuntime.wrap(function
|
|
599
|
-
while (1) switch (
|
|
727
|
+
return _regeneratorRuntime.wrap(function _callee14$(_context14) {
|
|
728
|
+
while (1) switch (_context14.prev = _context14.next) {
|
|
600
729
|
case 0:
|
|
601
730
|
conflict = Object.assign(new Error('Conflict'), {
|
|
602
731
|
status: 409
|
|
@@ -617,27 +746,27 @@ describe('shareByLink shared drive 409 recovery', function () {
|
|
|
617
746
|
provider.dispatch = jest.fn(function (action) {
|
|
618
747
|
provider.state = reducer(provider.state, action);
|
|
619
748
|
});
|
|
620
|
-
|
|
749
|
+
_context14.next = 10;
|
|
621
750
|
return provider.shareByLink(driveFile, {
|
|
622
751
|
verbs: ['GET']
|
|
623
752
|
});
|
|
624
753
|
|
|
625
754
|
case 10:
|
|
626
|
-
resp =
|
|
755
|
+
resp = _context14.sent;
|
|
627
756
|
expect(mockCreateSharingLink).toHaveBeenCalled();
|
|
628
757
|
expect(mockFindLinksByIds).toHaveBeenCalledWith(['file_in_drive']);
|
|
629
758
|
expect(resp.data.id).toBe(PERM_DRIVE_FILE.id);
|
|
630
759
|
|
|
631
760
|
case 14:
|
|
632
761
|
case "end":
|
|
633
|
-
return
|
|
762
|
+
return _context14.stop();
|
|
634
763
|
}
|
|
635
|
-
},
|
|
764
|
+
}, _callee14);
|
|
636
765
|
})));
|
|
637
|
-
it('does not duplicate existing links when 409 recovery refetches them', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
766
|
+
it('does not duplicate existing links when 409 recovery refetches them', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee15() {
|
|
638
767
|
var conflict, mockCreateSharingLink, mockFindLinksByIds, mockClient, provider, resp, permissions;
|
|
639
|
-
return _regeneratorRuntime.wrap(function
|
|
640
|
-
while (1) switch (
|
|
768
|
+
return _regeneratorRuntime.wrap(function _callee15$(_context15) {
|
|
769
|
+
while (1) switch (_context15.prev = _context15.next) {
|
|
641
770
|
case 0:
|
|
642
771
|
conflict = Object.assign(new Error('Conflict'), {
|
|
643
772
|
status: 409
|
|
@@ -658,13 +787,13 @@ describe('shareByLink shared drive 409 recovery', function () {
|
|
|
658
787
|
provider.dispatch = jest.fn(function (action) {
|
|
659
788
|
provider.state = reducer(provider.state, action);
|
|
660
789
|
});
|
|
661
|
-
|
|
790
|
+
_context15.next = 10;
|
|
662
791
|
return provider.shareByLink(driveFile, {
|
|
663
792
|
verbs: ['GET']
|
|
664
793
|
});
|
|
665
794
|
|
|
666
795
|
case 10:
|
|
667
|
-
resp =
|
|
796
|
+
resp = _context15.sent;
|
|
668
797
|
permissions = getDocumentPermissions(provider.state, 'file_in_drive');
|
|
669
798
|
expect(resp.data.id).toBe(PERM_DRIVE_FILE.id);
|
|
670
799
|
expect(provider.dispatch).not.toHaveBeenCalled();
|
|
@@ -672,14 +801,14 @@ describe('shareByLink shared drive 409 recovery', function () {
|
|
|
672
801
|
|
|
673
802
|
case 15:
|
|
674
803
|
case "end":
|
|
675
|
-
return
|
|
804
|
+
return _context15.stop();
|
|
676
805
|
}
|
|
677
|
-
},
|
|
806
|
+
}, _callee15);
|
|
678
807
|
})));
|
|
679
|
-
it('rethrows the original error when 409 happens and no link is found', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
808
|
+
it('rethrows the original error when 409 happens and no link is found', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee16() {
|
|
680
809
|
var conflict, mockCreateSharingLink, mockFindLinksByIds, mockClient, provider;
|
|
681
|
-
return _regeneratorRuntime.wrap(function
|
|
682
|
-
while (1) switch (
|
|
810
|
+
return _regeneratorRuntime.wrap(function _callee16$(_context16) {
|
|
811
|
+
while (1) switch (_context16.prev = _context16.next) {
|
|
683
812
|
case 0:
|
|
684
813
|
conflict = Object.assign(new Error('Conflict'), {
|
|
685
814
|
status: 409
|
|
@@ -698,16 +827,16 @@ describe('shareByLink shared drive 409 recovery', function () {
|
|
|
698
827
|
});
|
|
699
828
|
provider.state = reducer();
|
|
700
829
|
provider.dispatch = jest.fn();
|
|
701
|
-
|
|
830
|
+
_context16.next = 10;
|
|
702
831
|
return expect(provider.shareByLink(driveFile, {
|
|
703
832
|
verbs: ['GET']
|
|
704
833
|
})).rejects.toBe(conflict);
|
|
705
834
|
|
|
706
835
|
case 10:
|
|
707
836
|
case "end":
|
|
708
|
-
return
|
|
837
|
+
return _context16.stop();
|
|
709
838
|
}
|
|
710
|
-
},
|
|
839
|
+
}, _callee16);
|
|
711
840
|
})));
|
|
712
841
|
});
|
|
713
842
|
describe('updateSharingMemberType', function () {
|
|
@@ -748,37 +877,37 @@ describe('updateSharingMemberType', function () {
|
|
|
748
877
|
instance.state = _objectSpread(_objectSpread({}, instance.state), reducer(instance.state, action));
|
|
749
878
|
});
|
|
750
879
|
});
|
|
751
|
-
it('should throw when sharing is not found', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
752
|
-
return _regeneratorRuntime.wrap(function
|
|
753
|
-
while (1) switch (
|
|
880
|
+
it('should throw when sharing is not found', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee17() {
|
|
881
|
+
return _regeneratorRuntime.wrap(function _callee17$(_context17) {
|
|
882
|
+
while (1) switch (_context17.prev = _context17.next) {
|
|
754
883
|
case 0:
|
|
755
|
-
|
|
884
|
+
_context17.next = 2;
|
|
756
885
|
return expect(instance.updateSharingMemberType('unknown-id', 1, 'one-way')).rejects.toThrow('Sharing not found');
|
|
757
886
|
|
|
758
887
|
case 2:
|
|
759
888
|
case "end":
|
|
760
|
-
return
|
|
889
|
+
return _context17.stop();
|
|
761
890
|
}
|
|
762
|
-
},
|
|
891
|
+
}, _callee17);
|
|
763
892
|
})));
|
|
764
|
-
it('should throw when member is not found', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
765
|
-
return _regeneratorRuntime.wrap(function
|
|
766
|
-
while (1) switch (
|
|
893
|
+
it('should throw when member is not found', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee18() {
|
|
894
|
+
return _regeneratorRuntime.wrap(function _callee18$(_context18) {
|
|
895
|
+
while (1) switch (_context18.prev = _context18.next) {
|
|
767
896
|
case 0:
|
|
768
|
-
|
|
897
|
+
_context18.next = 2;
|
|
769
898
|
return expect(instance.updateSharingMemberType('sharing-123', 99, 'one-way')).rejects.toThrow('Member not found');
|
|
770
899
|
|
|
771
900
|
case 2:
|
|
772
901
|
case "end":
|
|
773
|
-
return
|
|
902
|
+
return _context18.stop();
|
|
774
903
|
}
|
|
775
|
-
},
|
|
904
|
+
}, _callee18);
|
|
776
905
|
})));
|
|
777
|
-
it('should call setReadOnly when switching to one-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
778
|
-
return _regeneratorRuntime.wrap(function
|
|
779
|
-
while (1) switch (
|
|
906
|
+
it('should call setReadOnly when switching to one-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee19() {
|
|
907
|
+
return _regeneratorRuntime.wrap(function _callee19$(_context19) {
|
|
908
|
+
while (1) switch (_context19.prev = _context19.next) {
|
|
780
909
|
case 0:
|
|
781
|
-
|
|
910
|
+
_context19.next = 2;
|
|
782
911
|
return instance.updateSharingMemberType('sharing-123', 1, 'one-way');
|
|
783
912
|
|
|
784
913
|
case 2:
|
|
@@ -787,15 +916,15 @@ describe('updateSharingMemberType', function () {
|
|
|
787
916
|
|
|
788
917
|
case 4:
|
|
789
918
|
case "end":
|
|
790
|
-
return
|
|
919
|
+
return _context19.stop();
|
|
791
920
|
}
|
|
792
|
-
},
|
|
921
|
+
}, _callee19);
|
|
793
922
|
})));
|
|
794
|
-
it('should call setReadWrite when switching to two-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
795
|
-
return _regeneratorRuntime.wrap(function
|
|
796
|
-
while (1) switch (
|
|
923
|
+
it('should call setReadWrite when switching to two-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20() {
|
|
924
|
+
return _regeneratorRuntime.wrap(function _callee20$(_context20) {
|
|
925
|
+
while (1) switch (_context20.prev = _context20.next) {
|
|
797
926
|
case 0:
|
|
798
|
-
|
|
927
|
+
_context20.next = 2;
|
|
799
928
|
return instance.updateSharingMemberType('sharing-123', 1, 'two-way');
|
|
800
929
|
|
|
801
930
|
case 2:
|
|
@@ -804,15 +933,15 @@ describe('updateSharingMemberType', function () {
|
|
|
804
933
|
|
|
805
934
|
case 4:
|
|
806
935
|
case "end":
|
|
807
|
-
return
|
|
936
|
+
return _context20.stop();
|
|
808
937
|
}
|
|
809
|
-
},
|
|
938
|
+
}, _callee20);
|
|
810
939
|
})));
|
|
811
|
-
it('should optimistically dispatch a state update when switching to one-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
812
|
-
return _regeneratorRuntime.wrap(function
|
|
813
|
-
while (1) switch (
|
|
940
|
+
it('should optimistically dispatch a state update when switching to one-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee21() {
|
|
941
|
+
return _regeneratorRuntime.wrap(function _callee21$(_context21) {
|
|
942
|
+
while (1) switch (_context21.prev = _context21.next) {
|
|
814
943
|
case 0:
|
|
815
|
-
|
|
944
|
+
_context21.next = 2;
|
|
816
945
|
return instance.updateSharingMemberType('sharing-123', 1, 'one-way');
|
|
817
946
|
|
|
818
947
|
case 2:
|
|
@@ -829,15 +958,15 @@ describe('updateSharingMemberType', function () {
|
|
|
829
958
|
|
|
830
959
|
case 3:
|
|
831
960
|
case "end":
|
|
832
|
-
return
|
|
961
|
+
return _context21.stop();
|
|
833
962
|
}
|
|
834
|
-
},
|
|
963
|
+
}, _callee21);
|
|
835
964
|
})));
|
|
836
|
-
it('should optimistically dispatch a state update when switching to two-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
837
|
-
return _regeneratorRuntime.wrap(function
|
|
838
|
-
while (1) switch (
|
|
965
|
+
it('should optimistically dispatch a state update when switching to two-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee22() {
|
|
966
|
+
return _regeneratorRuntime.wrap(function _callee22$(_context22) {
|
|
967
|
+
while (1) switch (_context22.prev = _context22.next) {
|
|
839
968
|
case 0:
|
|
840
|
-
|
|
969
|
+
_context22.next = 2;
|
|
841
970
|
return instance.updateSharingMemberType('sharing-123', 1, 'two-way');
|
|
842
971
|
|
|
843
972
|
case 2:
|
|
@@ -854,44 +983,44 @@ describe('updateSharingMemberType', function () {
|
|
|
854
983
|
|
|
855
984
|
case 3:
|
|
856
985
|
case "end":
|
|
857
|
-
return
|
|
986
|
+
return _context22.stop();
|
|
858
987
|
}
|
|
859
|
-
},
|
|
988
|
+
}, _callee22);
|
|
860
989
|
})));
|
|
861
|
-
it('should rethrow error if setReadOnly fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
862
|
-
return _regeneratorRuntime.wrap(function
|
|
863
|
-
while (1) switch (
|
|
990
|
+
it('should rethrow error if setReadOnly fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee23() {
|
|
991
|
+
return _regeneratorRuntime.wrap(function _callee23$(_context23) {
|
|
992
|
+
while (1) switch (_context23.prev = _context23.next) {
|
|
864
993
|
case 0:
|
|
865
994
|
instance.sharingCol.setReadOnly.mockRejectedValue(new Error('Network error'));
|
|
866
|
-
|
|
995
|
+
_context23.next = 3;
|
|
867
996
|
return expect(instance.updateSharingMemberType('sharing-123', 1, 'one-way')).rejects.toThrow('Network error');
|
|
868
997
|
|
|
869
998
|
case 3:
|
|
870
999
|
case "end":
|
|
871
|
-
return
|
|
1000
|
+
return _context23.stop();
|
|
872
1001
|
}
|
|
873
|
-
},
|
|
1002
|
+
}, _callee23);
|
|
874
1003
|
})));
|
|
875
|
-
it('should rethrow error if setReadWrite fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
876
|
-
return _regeneratorRuntime.wrap(function
|
|
877
|
-
while (1) switch (
|
|
1004
|
+
it('should rethrow error if setReadWrite fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee24() {
|
|
1005
|
+
return _regeneratorRuntime.wrap(function _callee24$(_context24) {
|
|
1006
|
+
while (1) switch (_context24.prev = _context24.next) {
|
|
878
1007
|
case 0:
|
|
879
1008
|
instance.sharingCol.setReadWrite.mockRejectedValue(new Error('Network error'));
|
|
880
|
-
|
|
1009
|
+
_context24.next = 3;
|
|
881
1010
|
return expect(instance.updateSharingMemberType('sharing-123', 1, 'two-way')).rejects.toThrow('Network error');
|
|
882
1011
|
|
|
883
1012
|
case 3:
|
|
884
1013
|
case "end":
|
|
885
|
-
return
|
|
1014
|
+
return _context24.stop();
|
|
886
1015
|
}
|
|
887
|
-
},
|
|
1016
|
+
}, _callee24);
|
|
888
1017
|
})));
|
|
889
|
-
it('should rollback optimistic state update if API call fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
890
|
-
return _regeneratorRuntime.wrap(function
|
|
891
|
-
while (1) switch (
|
|
1018
|
+
it('should rollback optimistic state update if API call fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee25() {
|
|
1019
|
+
return _regeneratorRuntime.wrap(function _callee25$(_context25) {
|
|
1020
|
+
while (1) switch (_context25.prev = _context25.next) {
|
|
892
1021
|
case 0:
|
|
893
1022
|
instance.sharingCol.setReadOnly.mockRejectedValue(new Error('Network error'));
|
|
894
|
-
|
|
1023
|
+
_context25.next = 3;
|
|
895
1024
|
return expect(instance.updateSharingMemberType('sharing-123', 1, 'one-way')).rejects.toThrow('Network error');
|
|
896
1025
|
|
|
897
1026
|
case 3:
|
|
@@ -918,14 +1047,14 @@ describe('updateSharingMemberType', function () {
|
|
|
918
1047
|
|
|
919
1048
|
case 5:
|
|
920
1049
|
case "end":
|
|
921
|
-
return
|
|
1050
|
+
return _context25.stop();
|
|
922
1051
|
}
|
|
923
|
-
},
|
|
1052
|
+
}, _callee25);
|
|
924
1053
|
})));
|
|
925
|
-
it('should preserve realtime changes when rolling back an API failure', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
1054
|
+
it('should preserve realtime changes when rolling back an API failure', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee27() {
|
|
926
1055
|
var realtimeSharing;
|
|
927
|
-
return _regeneratorRuntime.wrap(function
|
|
928
|
-
while (1) switch (
|
|
1056
|
+
return _regeneratorRuntime.wrap(function _callee27$(_context27) {
|
|
1057
|
+
while (1) switch (_context27.prev = _context27.next) {
|
|
929
1058
|
case 0:
|
|
930
1059
|
realtimeSharing = _objectSpread(_objectSpread({}, mockSharing), {}, {
|
|
931
1060
|
attributes: _objectSpread(_objectSpread({}, mockSharing.attributes), {}, {
|
|
@@ -937,9 +1066,9 @@ describe('updateSharingMemberType', function () {
|
|
|
937
1066
|
})]
|
|
938
1067
|
})
|
|
939
1068
|
});
|
|
940
|
-
instance.sharingCol.setReadOnly.mockImplementation( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
941
|
-
return _regeneratorRuntime.wrap(function
|
|
942
|
-
while (1) switch (
|
|
1069
|
+
instance.sharingCol.setReadOnly.mockImplementation( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee26() {
|
|
1070
|
+
return _regeneratorRuntime.wrap(function _callee26$(_context26) {
|
|
1071
|
+
while (1) switch (_context26.prev = _context26.next) {
|
|
943
1072
|
case 0:
|
|
944
1073
|
instance.state = _objectSpread(_objectSpread({}, instance.state), {}, {
|
|
945
1074
|
sharings: instance.state.sharings.map(function (sharing) {
|
|
@@ -950,11 +1079,11 @@ describe('updateSharingMemberType', function () {
|
|
|
950
1079
|
|
|
951
1080
|
case 2:
|
|
952
1081
|
case "end":
|
|
953
|
-
return
|
|
1082
|
+
return _context26.stop();
|
|
954
1083
|
}
|
|
955
|
-
},
|
|
1084
|
+
}, _callee26);
|
|
956
1085
|
})));
|
|
957
|
-
|
|
1086
|
+
_context27.next = 4;
|
|
958
1087
|
return expect(instance.updateSharingMemberType('sharing-123', 1, 'one-way')).rejects.toThrow('Network error');
|
|
959
1088
|
|
|
960
1089
|
case 4:
|
|
@@ -971,9 +1100,9 @@ describe('updateSharingMemberType', function () {
|
|
|
971
1100
|
|
|
972
1101
|
case 5:
|
|
973
1102
|
case "end":
|
|
974
|
-
return
|
|
1103
|
+
return _context27.stop();
|
|
975
1104
|
}
|
|
976
|
-
},
|
|
1105
|
+
}, _callee27);
|
|
977
1106
|
})));
|
|
978
1107
|
}); // TODO Convert with react-testing-library
|
|
979
1108
|
// describe('hasWriteAccess', () => {
|
package/dist/state.js
CHANGED
|
@@ -237,7 +237,7 @@ var byDocId = function byDocId() {
|
|
|
237
237
|
case REVOKE_GROUP:
|
|
238
238
|
case REVOKE_RECIPIENT:
|
|
239
239
|
case UPDATE_SHARING:
|
|
240
|
-
if (
|
|
240
|
+
if (areAllRecipientsRevoked(action.sharing)) {
|
|
241
241
|
return forgetSharing(state, action.sharing);
|
|
242
242
|
}
|
|
243
243
|
|
|
@@ -337,7 +337,7 @@ var sharings = function sharings() {
|
|
|
337
337
|
case UPDATE_SHARING:
|
|
338
338
|
case REVOKE_GROUP:
|
|
339
339
|
case REVOKE_RECIPIENT:
|
|
340
|
-
if (
|
|
340
|
+
if (areAllRecipientsRevoked(action.sharing)) {
|
|
341
341
|
return state.filter(function (s) {
|
|
342
342
|
return s.id !== action.sharing.id;
|
|
343
343
|
});
|
|
@@ -374,7 +374,8 @@ var sharedPaths = function sharedPaths() {
|
|
|
374
374
|
|
|
375
375
|
case REVOKE_GROUP:
|
|
376
376
|
case REVOKE_RECIPIENT:
|
|
377
|
-
|
|
377
|
+
case UPDATE_SHARING:
|
|
378
|
+
if (areAllRecipientsRevoked(action.sharing)) {
|
|
378
379
|
return state.filter(function (p) {
|
|
379
380
|
return p !== action.path;
|
|
380
381
|
});
|
package/dist/state.spec.js
CHANGED
|
@@ -4,7 +4,7 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
|
|
|
4
4
|
|
|
5
5
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
6
6
|
|
|
7
|
-
import reducer, { receiveSharings, addSharing, addSharingLink, updateSharingLink, revokeSharingLink, getRecipients, getRecipientsFromSharing, revokeRecipient, revokeSelf, updateSharing, matchingInstanceName, getSharingLink, hasSharedParent, hasSharedChild, getSharedDocIdsBySharings, getSharingType, getPermissionDocIds, getDocumentSharingType, getSharedParentPath, getExternalSharingIds, getRecipientsWithGroups } from './state';
|
|
7
|
+
import reducer, { receiveSharings, addSharing, addSharingLink, updateSharingLink, revokeSharingLink, getRecipients, getRecipientsFromSharing, revokeGroup, revokeRecipient, revokeSelf, updateSharing, matchingInstanceName, getSharingLink, hasSharedParent, hasSharedChild, getSharedDocIdsBySharings, getSharingType, getPermissionDocIds, getDocumentSharingType, getSharedParentPath, getExternalSharingIds, getRecipientsWithGroups } from './state';
|
|
8
8
|
import { SHARING_1, SHARING_2, SHARING_3, SHARING_READ_ONLY, PERM_1, PERM_2, PERM_WITHOUT_DOC, SHARING_NO_ACTIVE, SHARING_WITH_SHORTCUT, APPS } from '../__tests__/fixtures';
|
|
9
9
|
describe('Sharing state', function () {
|
|
10
10
|
it('should have a default state', function () {
|
|
@@ -106,7 +106,7 @@ describe('Sharing state', function () {
|
|
|
106
106
|
});
|
|
107
107
|
expect(state.sharings).toEqual([SHARING_1, SHARED_DRIVE_WITHOUT_RECIPIENTS]);
|
|
108
108
|
});
|
|
109
|
-
it('should
|
|
109
|
+
it('should forget shared drives without recipients when updating', function () {
|
|
110
110
|
var SHARED_DRIVE_WITHOUT_RECIPIENTS = _objectSpread(_objectSpread({}, SHARING_1), {}, {
|
|
111
111
|
id: 'shared_drive_no_recipients',
|
|
112
112
|
attributes: _objectSpread(_objectSpread({}, SHARING_1.attributes), {}, {
|
|
@@ -139,15 +139,45 @@ describe('Sharing state', function () {
|
|
|
139
139
|
});
|
|
140
140
|
|
|
141
141
|
var state = reducer(initialState, updateSharing(updatedSharing));
|
|
142
|
-
expect(state.byDocId).toEqual({
|
|
143
|
-
|
|
144
|
-
sharings: [SHARED_DRIVE_WITHOUT_RECIPIENTS.id],
|
|
145
|
-
permissions: []
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
expect(state.sharings).toEqual([updatedSharing]);
|
|
142
|
+
expect(state.byDocId).toEqual({});
|
|
143
|
+
expect(state.sharings).toEqual([]);
|
|
149
144
|
});
|
|
150
|
-
it('should
|
|
145
|
+
it('should forget a shared drive when revoking its last group', function () {
|
|
146
|
+
var SHARED_DRIVE_WITH_GROUP = _objectSpread(_objectSpread({}, SHARING_1), {}, {
|
|
147
|
+
id: 'shared_drive_group',
|
|
148
|
+
attributes: _objectSpread(_objectSpread({}, SHARING_1.attributes), {}, {
|
|
149
|
+
drive: true,
|
|
150
|
+
members: [{
|
|
151
|
+
status: 'owner',
|
|
152
|
+
name: 'Jane Doe',
|
|
153
|
+
email: 'jane@doe.com',
|
|
154
|
+
instance: 'http://cozy.tools:8080'
|
|
155
|
+
}],
|
|
156
|
+
groups: [{
|
|
157
|
+
name: 'Last group',
|
|
158
|
+
addedBy: 0
|
|
159
|
+
}],
|
|
160
|
+
rules: [{
|
|
161
|
+
title: 'My Shared Drive',
|
|
162
|
+
doctype: 'io.cozy.files',
|
|
163
|
+
values: ['shared_drive_group_folder'],
|
|
164
|
+
add: 'sync',
|
|
165
|
+
update: 'sync',
|
|
166
|
+
remove: 'sync'
|
|
167
|
+
}]
|
|
168
|
+
})
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
var sharedDrivePath = '/shared_drive_group_folder';
|
|
172
|
+
var initialState = reducer(reducer(undefined, receiveSharings({
|
|
173
|
+
sharings: [SHARED_DRIVE_WITH_GROUP]
|
|
174
|
+
})), addSharing(SHARED_DRIVE_WITH_GROUP, sharedDrivePath));
|
|
175
|
+
var state = reducer(initialState, revokeGroup(SHARED_DRIVE_WITH_GROUP, 0, sharedDrivePath));
|
|
176
|
+
expect(state.byDocId).toEqual({});
|
|
177
|
+
expect(state.sharings).toEqual([]);
|
|
178
|
+
expect(state.sharedPaths).toEqual([]);
|
|
179
|
+
});
|
|
180
|
+
it('should forget a shared drive when revoking last recipient', function () {
|
|
151
181
|
var SHARED_DRIVE_WITH_ONE_RECIPIENT = _objectSpread(_objectSpread({}, SHARING_1), {}, {
|
|
152
182
|
id: 'shared_drive_one_recipient',
|
|
153
183
|
attributes: _objectSpread(_objectSpread({}, SHARING_1.attributes), {}, {
|
|
@@ -178,23 +208,10 @@ describe('Sharing state', function () {
|
|
|
178
208
|
var initialState = reducer(reducer(undefined, receiveSharings({
|
|
179
209
|
sharings: [SHARED_DRIVE_WITH_ONE_RECIPIENT]
|
|
180
210
|
})), addSharing(SHARED_DRIVE_WITH_ONE_RECIPIENT, sharedDrivePath));
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
status: 'revoked'
|
|
186
|
-
})]
|
|
187
|
-
})
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
var state = reducer(initialState, revokeRecipient(sharingAfterRevoke, 1, sharedDrivePath));
|
|
191
|
-
expect(state.sharedPaths).toContain(sharedDrivePath);
|
|
192
|
-
expect(state.byDocId).toEqual({
|
|
193
|
-
shared_drive_folder: {
|
|
194
|
-
sharings: [SHARED_DRIVE_WITH_ONE_RECIPIENT.id],
|
|
195
|
-
permissions: []
|
|
196
|
-
}
|
|
197
|
-
});
|
|
211
|
+
var state = reducer(initialState, revokeRecipient(SHARED_DRIVE_WITH_ONE_RECIPIENT, 1, sharedDrivePath));
|
|
212
|
+
expect(state.byDocId).toEqual({});
|
|
213
|
+
expect(state.sharings).toEqual([]);
|
|
214
|
+
expect(state.sharedPaths).toEqual([]);
|
|
198
215
|
});
|
|
199
216
|
it('should index received permissions', function () {
|
|
200
217
|
var state = reducer(undefined, receiveSharings({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-sharing",
|
|
3
|
-
"version": "33.6.
|
|
3
|
+
"version": "33.6.1",
|
|
4
4
|
"description": "Provides sharing login for React applications.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"author": "Cozy",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"sideEffects": [
|
|
84
84
|
"*.css"
|
|
85
85
|
],
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "10aa2298384087fd396bbfce29c1198d80244361"
|
|
87
87
|
}
|
|
@@ -5,7 +5,12 @@ import { createMockClient } from 'cozy-client'
|
|
|
5
5
|
|
|
6
6
|
import { SharingProvider } from './SharingProvider'
|
|
7
7
|
import SharingContext from './context'
|
|
8
|
-
import reducer, {
|
|
8
|
+
import reducer, {
|
|
9
|
+
addSharingLink,
|
|
10
|
+
getDocumentPermissions,
|
|
11
|
+
getDocumentSharing,
|
|
12
|
+
receiveSharings
|
|
13
|
+
} from './state'
|
|
9
14
|
import AppLike from '../test/AppLike'
|
|
10
15
|
|
|
11
16
|
const AppWrapper = ({ children, client, isPublic }) => {
|
|
@@ -291,6 +296,115 @@ describe('updateDocumentPermissions', () => {
|
|
|
291
296
|
})
|
|
292
297
|
})
|
|
293
298
|
|
|
299
|
+
describe('shared drive recipient revocation', () => {
|
|
300
|
+
const SHARED_DRIVE = {
|
|
301
|
+
_id: 'sharing_123',
|
|
302
|
+
id: 'sharing_123',
|
|
303
|
+
type: 'io.cozy.sharings',
|
|
304
|
+
attributes: {
|
|
305
|
+
drive: true,
|
|
306
|
+
owner: true,
|
|
307
|
+
description: 'Shared folder',
|
|
308
|
+
rules: [
|
|
309
|
+
{
|
|
310
|
+
title: 'Shared folder',
|
|
311
|
+
doctype: 'io.cozy.files',
|
|
312
|
+
values: ['folder_123'],
|
|
313
|
+
add: 'none',
|
|
314
|
+
update: 'none',
|
|
315
|
+
remove: 'none'
|
|
316
|
+
}
|
|
317
|
+
],
|
|
318
|
+
members: [
|
|
319
|
+
{
|
|
320
|
+
status: 'owner',
|
|
321
|
+
email: 'owner@cozy.local',
|
|
322
|
+
instance: 'http://cozy.local'
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
status: 'pending',
|
|
326
|
+
email: 'recipient@cozy.local',
|
|
327
|
+
instance: 'http://recipient.cozy.local'
|
|
328
|
+
}
|
|
329
|
+
]
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
const NEW_SHARING = {
|
|
333
|
+
...SHARED_DRIVE,
|
|
334
|
+
_id: 'sharing_456',
|
|
335
|
+
id: 'sharing_456'
|
|
336
|
+
}
|
|
337
|
+
const document = {
|
|
338
|
+
_id: 'folder_123',
|
|
339
|
+
id: 'folder_123',
|
|
340
|
+
path: '/Shared folder'
|
|
341
|
+
}
|
|
342
|
+
const recipient = {
|
|
343
|
+
_id: 'contact_123',
|
|
344
|
+
id: 'contact_123',
|
|
345
|
+
_type: 'io.cozy.contacts',
|
|
346
|
+
email: 'recipient@cozy.local'
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
let provider
|
|
350
|
+
let sharingCol
|
|
351
|
+
|
|
352
|
+
beforeEach(() => {
|
|
353
|
+
const mockClient = createMockClient({})
|
|
354
|
+
mockClient.getStackClient = () => ({ uri: 'http://cozy.local' })
|
|
355
|
+
mockClient.collection = jest.fn().mockReturnValue({})
|
|
356
|
+
|
|
357
|
+
provider = new SharingProvider({ client: mockClient })
|
|
358
|
+
provider.state = {
|
|
359
|
+
...provider.state,
|
|
360
|
+
...reducer(undefined, receiveSharings({ sharings: [SHARED_DRIVE] }))
|
|
361
|
+
}
|
|
362
|
+
provider.dispatch = jest.fn(action => {
|
|
363
|
+
provider.state = {
|
|
364
|
+
...provider.state,
|
|
365
|
+
...reducer(provider.state, action)
|
|
366
|
+
}
|
|
367
|
+
})
|
|
368
|
+
sharingCol = {
|
|
369
|
+
addRecipients: jest.fn(),
|
|
370
|
+
create: jest.fn().mockResolvedValue({ data: NEW_SHARING }),
|
|
371
|
+
revokeRecipient: jest.fn().mockResolvedValue({})
|
|
372
|
+
}
|
|
373
|
+
provider.sharingCol = sharingCol
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
it('forgets the shared drive when revoking its last recipient', async () => {
|
|
377
|
+
await provider.revoke(document, SHARED_DRIVE.id, 1)
|
|
378
|
+
|
|
379
|
+
expect(sharingCol.revokeRecipient).toHaveBeenCalledWith(SHARED_DRIVE, 1)
|
|
380
|
+
expect(getDocumentSharing(provider.state, document.id)).toBeNull()
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
it('creates a new sharing when re-adding after the last recipient was revoked', async () => {
|
|
384
|
+
await provider.revoke(document, SHARED_DRIVE.id, 1)
|
|
385
|
+
|
|
386
|
+
await provider.share({
|
|
387
|
+
document,
|
|
388
|
+
recipients: [recipient],
|
|
389
|
+
readOnlyRecipients: [],
|
|
390
|
+
description: 'Shared folder',
|
|
391
|
+
openSharing: true,
|
|
392
|
+
sharedDrive: true
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
expect(sharingCol.addRecipients).not.toHaveBeenCalled()
|
|
396
|
+
expect(sharingCol.create).toHaveBeenCalledWith({
|
|
397
|
+
document,
|
|
398
|
+
recipients: [recipient],
|
|
399
|
+
readOnlyRecipients: [],
|
|
400
|
+
description: 'Shared folder',
|
|
401
|
+
previewPath: '/preview',
|
|
402
|
+
openSharing: true,
|
|
403
|
+
sharedDrive: true
|
|
404
|
+
})
|
|
405
|
+
})
|
|
406
|
+
})
|
|
407
|
+
|
|
294
408
|
describe('fetchSharedDriveSharingLinks', () => {
|
|
295
409
|
const PERM_DRIVE_FILE = {
|
|
296
410
|
type: 'io.cozy.permissions',
|
package/src/state.js
CHANGED
|
@@ -184,10 +184,7 @@ const byDocId = (state = {}, action) => {
|
|
|
184
184
|
case REVOKE_GROUP:
|
|
185
185
|
case REVOKE_RECIPIENT:
|
|
186
186
|
case UPDATE_SHARING:
|
|
187
|
-
if (
|
|
188
|
-
!isSharingADrive(action.sharing) &&
|
|
189
|
-
areAllRecipientsRevoked(action.sharing)
|
|
190
|
-
) {
|
|
187
|
+
if (areAllRecipientsRevoked(action.sharing)) {
|
|
191
188
|
return forgetSharing(state, action.sharing)
|
|
192
189
|
}
|
|
193
190
|
return state
|
|
@@ -258,10 +255,7 @@ const sharings = (state = [], action) => {
|
|
|
258
255
|
case UPDATE_SHARING:
|
|
259
256
|
case REVOKE_GROUP:
|
|
260
257
|
case REVOKE_RECIPIENT:
|
|
261
|
-
if (
|
|
262
|
-
!isSharingADrive(action.sharing) &&
|
|
263
|
-
areAllRecipientsRevoked(action.sharing)
|
|
264
|
-
) {
|
|
258
|
+
if (areAllRecipientsRevoked(action.sharing)) {
|
|
265
259
|
return state.filter(s => s.id !== action.sharing.id)
|
|
266
260
|
}
|
|
267
261
|
return state.map(s => {
|
|
@@ -287,10 +281,8 @@ const sharedPaths = (state = [], action) => {
|
|
|
287
281
|
return newState
|
|
288
282
|
case REVOKE_GROUP:
|
|
289
283
|
case REVOKE_RECIPIENT:
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
areAllRecipientsRevoked(action.sharing)
|
|
293
|
-
) {
|
|
284
|
+
case UPDATE_SHARING:
|
|
285
|
+
if (areAllRecipientsRevoked(action.sharing)) {
|
|
294
286
|
return state.filter(p => p !== action.path)
|
|
295
287
|
}
|
|
296
288
|
return state
|
package/src/state.spec.js
CHANGED
|
@@ -6,6 +6,7 @@ import reducer, {
|
|
|
6
6
|
revokeSharingLink,
|
|
7
7
|
getRecipients,
|
|
8
8
|
getRecipientsFromSharing,
|
|
9
|
+
revokeGroup,
|
|
9
10
|
revokeRecipient,
|
|
10
11
|
revokeSelf,
|
|
11
12
|
updateSharing,
|
|
@@ -143,7 +144,7 @@ describe('Sharing state', () => {
|
|
|
143
144
|
expect(state.sharings).toEqual([SHARING_1, SHARED_DRIVE_WITHOUT_RECIPIENTS])
|
|
144
145
|
})
|
|
145
146
|
|
|
146
|
-
it('should
|
|
147
|
+
it('should forget shared drives without recipients when updating', () => {
|
|
147
148
|
const SHARED_DRIVE_WITHOUT_RECIPIENTS = {
|
|
148
149
|
...SHARING_1,
|
|
149
150
|
id: 'shared_drive_no_recipients',
|
|
@@ -184,16 +185,65 @@ describe('Sharing state', () => {
|
|
|
184
185
|
}
|
|
185
186
|
}
|
|
186
187
|
const state = reducer(initialState, updateSharing(updatedSharing))
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
188
|
+
|
|
189
|
+
expect(state.byDocId).toEqual({})
|
|
190
|
+
expect(state.sharings).toEqual([])
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it('should forget a shared drive when revoking its last group', () => {
|
|
194
|
+
const SHARED_DRIVE_WITH_GROUP = {
|
|
195
|
+
...SHARING_1,
|
|
196
|
+
id: 'shared_drive_group',
|
|
197
|
+
attributes: {
|
|
198
|
+
...SHARING_1.attributes,
|
|
199
|
+
drive: true,
|
|
200
|
+
members: [
|
|
201
|
+
{
|
|
202
|
+
status: 'owner',
|
|
203
|
+
name: 'Jane Doe',
|
|
204
|
+
email: 'jane@doe.com',
|
|
205
|
+
instance: 'http://cozy.tools:8080'
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
groups: [
|
|
209
|
+
{
|
|
210
|
+
name: 'Last group',
|
|
211
|
+
addedBy: 0
|
|
212
|
+
}
|
|
213
|
+
],
|
|
214
|
+
rules: [
|
|
215
|
+
{
|
|
216
|
+
title: 'My Shared Drive',
|
|
217
|
+
doctype: 'io.cozy.files',
|
|
218
|
+
values: ['shared_drive_group_folder'],
|
|
219
|
+
add: 'sync',
|
|
220
|
+
update: 'sync',
|
|
221
|
+
remove: 'sync'
|
|
222
|
+
}
|
|
223
|
+
]
|
|
191
224
|
}
|
|
192
|
-
}
|
|
193
|
-
|
|
225
|
+
}
|
|
226
|
+
const sharedDrivePath = '/shared_drive_group_folder'
|
|
227
|
+
const initialState = reducer(
|
|
228
|
+
reducer(
|
|
229
|
+
undefined,
|
|
230
|
+
receiveSharings({
|
|
231
|
+
sharings: [SHARED_DRIVE_WITH_GROUP]
|
|
232
|
+
})
|
|
233
|
+
),
|
|
234
|
+
addSharing(SHARED_DRIVE_WITH_GROUP, sharedDrivePath)
|
|
235
|
+
)
|
|
236
|
+
const state = reducer(
|
|
237
|
+
initialState,
|
|
238
|
+
revokeGroup(SHARED_DRIVE_WITH_GROUP, 0, sharedDrivePath)
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
expect(state.byDocId).toEqual({})
|
|
242
|
+
expect(state.sharings).toEqual([])
|
|
243
|
+
expect(state.sharedPaths).toEqual([])
|
|
194
244
|
})
|
|
195
245
|
|
|
196
|
-
it('should
|
|
246
|
+
it('should forget a shared drive when revoking last recipient', () => {
|
|
197
247
|
const SHARED_DRIVE_WITH_ONE_RECIPIENT = {
|
|
198
248
|
...SHARING_1,
|
|
199
249
|
id: 'shared_drive_one_recipient',
|
|
@@ -236,30 +286,14 @@ describe('Sharing state', () => {
|
|
|
236
286
|
),
|
|
237
287
|
addSharing(SHARED_DRIVE_WITH_ONE_RECIPIENT, sharedDrivePath)
|
|
238
288
|
)
|
|
239
|
-
const sharingAfterRevoke = {
|
|
240
|
-
...SHARED_DRIVE_WITH_ONE_RECIPIENT,
|
|
241
|
-
attributes: {
|
|
242
|
-
...SHARED_DRIVE_WITH_ONE_RECIPIENT.attributes,
|
|
243
|
-
members: [
|
|
244
|
-
SHARED_DRIVE_WITH_ONE_RECIPIENT.attributes.members[0],
|
|
245
|
-
{
|
|
246
|
-
...SHARED_DRIVE_WITH_ONE_RECIPIENT.attributes.members[1],
|
|
247
|
-
status: 'revoked'
|
|
248
|
-
}
|
|
249
|
-
]
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
289
|
const state = reducer(
|
|
253
290
|
initialState,
|
|
254
|
-
revokeRecipient(
|
|
291
|
+
revokeRecipient(SHARED_DRIVE_WITH_ONE_RECIPIENT, 1, sharedDrivePath)
|
|
255
292
|
)
|
|
256
|
-
|
|
257
|
-
expect(state.byDocId).toEqual({
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
permissions: []
|
|
261
|
-
}
|
|
262
|
-
})
|
|
293
|
+
|
|
294
|
+
expect(state.byDocId).toEqual({})
|
|
295
|
+
expect(state.sharings).toEqual([])
|
|
296
|
+
expect(state.sharedPaths).toEqual([])
|
|
263
297
|
})
|
|
264
298
|
|
|
265
299
|
it('should index received permissions', () => {
|