cozy-client 45.10.0 → 45.11.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.
@@ -2703,7 +2703,7 @@ var CozyClient = /*#__PURE__*/function () {
2703
2703
  CozyClient.hooks = CozyClient.hooks || {};
2704
2704
  CozyClient.fetchPolicies = _policies.default; //COZY_CLIENT_VERSION_PACKAGE in replaced by babel. See babel config
2705
2705
 
2706
- CozyClient.version = "45.10.0";
2706
+ CozyClient.version = "45.11.0";
2707
2707
 
2708
2708
  _microee.default.mixin(CozyClient);
2709
2709
 
@@ -17,6 +17,8 @@ var _CozyClient = _interopRequireDefault(require("../CozyClient"));
17
17
 
18
18
  var _const = require("../const");
19
19
 
20
+ var _dsl = require("../queries/dsl");
21
+
20
22
  var APP_DOCTYPE = 'io.cozy.apps';
21
23
  var administrative = 'administrative';
22
24
  var photos = 'photos';
@@ -168,26 +170,24 @@ exports.createFolderWithReference = createFolderWithReference;
168
170
 
169
171
  var getReferencedFolder = /*#__PURE__*/function () {
170
172
  var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(client, document) {
171
- var _yield$client$collect, included, foldersOutsideTrash;
173
+ var _yield$client$query, folders;
172
174
 
173
175
  return _regenerator.default.wrap(function _callee3$(_context3) {
174
176
  while (1) {
175
177
  switch (_context3.prev = _context3.next) {
176
178
  case 0:
177
179
  _context3.next = 2;
178
- return client.collection(_const.DOCTYPE_FILES).findReferencedBy(document);
180
+ return client.query((0, _dsl.Q)(_const.DOCTYPE_FILES).partialIndex({
181
+ type: 'directory',
182
+ trashed: false
183
+ }).referencedBy(document));
179
184
 
180
185
  case 2:
181
- _yield$client$collect = _context3.sent;
182
- included = _yield$client$collect.included;
183
- foldersOutsideTrash = included.filter(function (folder) {
184
- return !/^\/\.cozy_trash/.test(folder.path);
185
- }); // there can be multiple folders with the same reference in some edge cases
186
- // when this happens we return the most recent one
187
-
188
- return _context3.abrupt("return", foldersOutsideTrash.length > 0 ? (0, _sortBy.default)(foldersOutsideTrash, 'created_at').pop() : null);
186
+ _yield$client$query = _context3.sent;
187
+ folders = _yield$client$query.included;
188
+ return _context3.abrupt("return", folders.length > 0 ? (0, _sortBy.default)(folders, 'created_at').pop() : null);
189
189
 
190
- case 6:
190
+ case 5:
191
191
  case "end":
192
192
  return _context3.stop();
193
193
  }
@@ -3,6 +3,7 @@ import { MAGIC_FOLDERS, ensureMagicFolder, getReferencedFolder } from './folder'
3
3
  describe('Folder model', () => {
4
4
  const mockClient = {
5
5
  collection: () => mockClient,
6
+ query: jest.fn(),
6
7
  findReferencedBy: jest.fn(),
7
8
  ensureDirectoryExists: jest.fn(),
8
9
  addReferencesTo: jest.fn(),
@@ -38,7 +39,7 @@ describe('Folder model', () => {
38
39
  }
39
40
  ]
40
41
 
41
- mockClient.findReferencedBy.mockResolvedValue({
42
+ mockClient.query.mockResolvedValueOnce({
42
43
  included: existingMagicFolders
43
44
  })
44
45
 
@@ -66,10 +67,9 @@ describe('Folder model', () => {
66
67
  })
67
68
 
68
69
  it('should create magic folder', async () => {
69
- mockClient.findReferencedBy.mockResolvedValue({
70
+ mockClient.query.mockResolvedValueOnce({
70
71
  included: []
71
72
  })
72
-
73
73
  mockClient.ensureDirectoryExists.mockResolvedValue('dir-id')
74
74
  mockClient.get.mockResolvedValue({ data: {} })
75
75
 
@@ -90,24 +90,6 @@ describe('Folder model', () => {
90
90
  })
91
91
 
92
92
  describe('getReferencedFolder', () => {
93
- it('should filter trashed folders', async () => {
94
- const referencedFolder = {
95
- path: '/Reference/Folder'
96
- }
97
-
98
- const trashFolder = {
99
- path: '/.cozy_trash/Old/Reference/Folder'
100
- }
101
-
102
- mockClient.findReferencedBy.mockResolvedValue({
103
- included: [referencedFolder, trashFolder]
104
- })
105
-
106
- const result = await getReferencedFolder(mockClient, 'ref')
107
-
108
- expect(result).toBe(referencedFolder)
109
- })
110
-
111
93
  it('should return the most recently created folder', async () => {
112
94
  const oldFolder = {
113
95
  path: '/Old/folder',
@@ -118,7 +100,7 @@ describe('Folder model', () => {
118
100
  created_at: '2020-03-02T14:57:07.661588+01:00'
119
101
  }
120
102
 
121
- mockClient.findReferencedBy.mockResolvedValue({
103
+ mockClient.query.mockResolvedValueOnce({
122
104
  included: [oldFolder, recentFolder]
123
105
  })
124
106
 
@@ -5,7 +5,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.buildFolderPermission = exports.buildFolderPath = exports.statDirectoryByPath = exports.createDirectoryByPath = exports.ensureKonnectorFolder = void 0;
8
+ exports.findKonnectorAccountFolderByReference = exports.buildFolderPermission = exports.buildFolderPath = exports.statDirectoryByPath = exports.createDirectoryByPath = exports.ensureKonnectorFolder = void 0;
9
9
 
10
10
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
11
11
 
@@ -21,6 +21,8 @@ var _account = require("./account");
21
21
 
22
22
  var _locales = require("./document/locales");
23
23
 
24
+ var _dsl = require("../queries/dsl");
25
+
24
26
  //@ts-check
25
27
  var FILES_DOCTYPE = 'io.cozy.files';
26
28
  var PERMISSIONS_DOCTYPE = 'io.cozy.permissions'; // Default name for base directory
@@ -39,7 +41,7 @@ var DEFAULT_LOCALIZED_BASE_DIR = 'Administrative';
39
41
 
40
42
  var ensureKonnectorFolder = /*#__PURE__*/function () {
41
43
  var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(client, _ref) {
42
- var konnector, account, lang, permissions, files, t, _yield$Promise$all, _yield$Promise$all2, adminFolder, photosFolder, path, folder;
44
+ var konnector, account, lang, permissions, fileCollection, t, _yield$Promise$all, _yield$Promise$all2, adminFolder, photosFolder, sourceAccountIdentifier, alreadyExistingAccountFolder, alreadyExistingKonnectorMainFolder, path, folder, _yield$fileCollection, konnectorFolder;
43
45
 
44
46
  return _regenerator.default.wrap(function _callee$(_context) {
45
47
  while (1) {
@@ -47,7 +49,7 @@ var ensureKonnectorFolder = /*#__PURE__*/function () {
47
49
  case 0:
48
50
  konnector = _ref.konnector, account = _ref.account, lang = _ref.lang;
49
51
  permissions = client.collection(PERMISSIONS_DOCTYPE);
50
- files = client.collection(FILES_DOCTYPE);
52
+ fileCollection = client.collection(FILES_DOCTYPE);
51
53
  t = (0, _locales.getLocalizer)(lang);
52
54
  _context.next = 6;
53
55
  return Promise.all([(0, _folder.ensureMagicFolder)(client, _folder.MAGIC_FOLDERS.ADMINISTRATIVE, t('MagicFolders.administrative')), (0, _folder.ensureMagicFolder)(client, _folder.MAGIC_FOLDERS.PHOTOS, t('MagicFolders.photos'))]);
@@ -57,40 +59,100 @@ var ensureKonnectorFolder = /*#__PURE__*/function () {
57
59
  _yield$Promise$all2 = (0, _slicedToArray2.default)(_yield$Promise$all, 2);
58
60
  adminFolder = _yield$Promise$all2[0];
59
61
  photosFolder = _yield$Promise$all2[1];
62
+ sourceAccountIdentifier = (0, _account.getAccountName)(account);
63
+ _context.next = 13;
64
+ return findKonnectorAccountFolderByReference({
65
+ client: client,
66
+ slug: konnector.slug,
67
+ sourceAccountIdentifier: sourceAccountIdentifier
68
+ });
69
+
70
+ case 13:
71
+ alreadyExistingAccountFolder = _context.sent;
72
+
73
+ if (!alreadyExistingAccountFolder) {
74
+ _context.next = 16;
75
+ break;
76
+ }
77
+
78
+ return _context.abrupt("return", alreadyExistingAccountFolder);
79
+
80
+ case 16:
81
+ _context.next = 18;
82
+ return findKonnectorMainFolderByReference({
83
+ client: client,
84
+ slug: konnector.slug
85
+ });
86
+
87
+ case 18:
88
+ alreadyExistingKonnectorMainFolder = _context.sent;
89
+
90
+ if (!alreadyExistingKonnectorMainFolder) {
91
+ _context.next = 23;
92
+ break;
93
+ }
94
+
95
+ _context.next = 22;
96
+ return saveKonnectorAccountFolder({
97
+ client: client,
98
+ mainFolder: alreadyExistingKonnectorMainFolder,
99
+ konnector: konnector,
100
+ sourceAccountIdentifier: sourceAccountIdentifier
101
+ });
102
+
103
+ case 22:
104
+ return _context.abrupt("return", _context.sent);
105
+
106
+ case 23:
107
+ // if the previous shortcuts did not work, create the folders like we did before but with proper references
60
108
  path = buildFolderPath(konnector, account, {
61
109
  administrative: adminFolder.path,
62
110
  photos: photosFolder.path
63
111
  });
64
- _context.next = 13;
112
+ _context.next = 26;
65
113
  return statDirectoryByPath(client, path);
66
114
 
67
- case 13:
115
+ case 26:
68
116
  _context.t0 = _context.sent;
69
117
 
70
118
  if (_context.t0) {
71
- _context.next = 18;
119
+ _context.next = 31;
72
120
  break;
73
121
  }
74
122
 
75
- _context.next = 17;
123
+ _context.next = 30;
76
124
  return createDirectoryByPath(client, path);
77
125
 
78
- case 17:
126
+ case 30:
79
127
  _context.t0 = _context.sent;
80
128
 
81
- case 18:
129
+ case 31:
82
130
  folder = _context.t0;
83
- _context.next = 21;
84
- return permissions.add(konnector, buildFolderPermission(folder));
85
-
86
- case 21:
87
- _context.next = 23;
88
- return files.addReferencesTo(konnector, [folder]);
89
-
90
- case 23:
131
+ _context.next = 34;
132
+ return fileCollection.statById(folder.dir_id);
133
+
134
+ case 34:
135
+ _yield$fileCollection = _context.sent;
136
+ konnectorFolder = _yield$fileCollection.data;
137
+ _context.next = 38;
138
+ return Promise.all([permissions.add(konnector, buildFolderPermission(folder)), ensureKonnectorReference({
139
+ client: client,
140
+ folder: folder,
141
+ konnector: konnector
142
+ }), ensureSourceAccountIdentifierReference({
143
+ client: client,
144
+ folder: folder,
145
+ sourceAccountIdentifier: sourceAccountIdentifier
146
+ }), ensureKonnectorReference({
147
+ client: client,
148
+ folder: konnectorFolder,
149
+ konnector: konnector
150
+ })]);
151
+
152
+ case 38:
91
153
  return _context.abrupt("return", folder);
92
154
 
93
- case 24:
155
+ case 39:
94
156
  case "end":
95
157
  return _context.stop();
96
158
  }
@@ -357,7 +419,7 @@ var buildFolderPermission = function buildFolderPermission(folder) {
357
419
  return {
358
420
  // Legacy name
359
421
  saveFolder: {
360
- type: 'io.cozy.files',
422
+ type: FILES_DOCTYPE,
361
423
  values: [folder._id],
362
424
  verbs: ['GET', 'PATCH', 'POST']
363
425
  }
@@ -377,4 +439,289 @@ exports.buildFolderPermission = buildFolderPermission;
377
439
  var trim = function trim(str) {
378
440
  var c = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '\\s';
379
441
  return str.replace(new RegExp("^([".concat(c, "]*)(.*?)([").concat(c, "]*)$")), '$2');
380
- };
442
+ };
443
+ /**
444
+ * Test if a given folder has a given konnector reference
445
+ *
446
+ * @param {import('../types').IOCozyFolder} folder - folder to test
447
+ * @param {string} slug - konnector slug
448
+ * @returns {boolean}
449
+ */
450
+
451
+
452
+ var hasKonnectorReference = function hasKonnectorReference(folder, slug) {
453
+ var _folder$referenced_by;
454
+
455
+ return Boolean((_folder$referenced_by = folder.referenced_by) === null || _folder$referenced_by === void 0 ? void 0 : _folder$referenced_by.find(function (ref) {
456
+ return ref.type === 'io.cozy.konnectors' && ref.id === 'io.cozy.konnectors/' + slug;
457
+ }));
458
+ };
459
+ /**
460
+ * Test if a given folder has a given source account identifier reference
461
+ *
462
+ * @param {import('../types').IOCozyFolder} folder - folder to test
463
+ * @param {string} [sourceAccountIdentifier] - source account identifier
464
+ * @returns {boolean}
465
+ */
466
+
467
+
468
+ var hasSourceAccountIdentifierReference = function hasSourceAccountIdentifierReference(folder, sourceAccountIdentifier) {
469
+ var _folder$referenced_by2;
470
+
471
+ return Boolean((_folder$referenced_by2 = folder.referenced_by) === null || _folder$referenced_by2 === void 0 ? void 0 : _folder$referenced_by2.find(function (ref) {
472
+ return ref.type === 'io.cozy.accounts.sourceAccountIdentifier' && (sourceAccountIdentifier ? ref.id === sourceAccountIdentifier : true);
473
+ }));
474
+ };
475
+ /**
476
+ * Ensure that a given folder has the given konnector reference
477
+ *
478
+ * @param {object} options - options object
479
+ * @param {CozyClient} options.client - CozyClient instance
480
+ * @param {import('../types').IOCozyFolder} options.folder - folder to test
481
+ * @param {import('../types').IOCozyKonnector} options.konnector - konnector to which we want the reference
482
+ * @returns {Promise<void>}
483
+ */
484
+
485
+
486
+ var ensureKonnectorReference = /*#__PURE__*/function () {
487
+ var _ref6 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(_ref5) {
488
+ var client, folder, konnector, fileCollection;
489
+ return _regenerator.default.wrap(function _callee4$(_context4) {
490
+ while (1) {
491
+ switch (_context4.prev = _context4.next) {
492
+ case 0:
493
+ client = _ref5.client, folder = _ref5.folder, konnector = _ref5.konnector;
494
+ fileCollection = client.collection(FILES_DOCTYPE);
495
+
496
+ if (hasKonnectorReference(folder, konnector.slug)) {
497
+ _context4.next = 5;
498
+ break;
499
+ }
500
+
501
+ _context4.next = 5;
502
+ return fileCollection.addReferencesTo(konnector, [folder]);
503
+
504
+ case 5:
505
+ case "end":
506
+ return _context4.stop();
507
+ }
508
+ }
509
+ }, _callee4);
510
+ }));
511
+
512
+ return function ensureKonnectorReference(_x7) {
513
+ return _ref6.apply(this, arguments);
514
+ };
515
+ }();
516
+ /**
517
+ * Ensure that a given folder has the given source account identifier reference
518
+ *
519
+ * @param {object} options - options object
520
+ * @param {CozyClient} options.client - CozyClient instance
521
+ * @param {import('../types').IOCozyFolder} options.folder - folder to test
522
+ * @param {string} options.sourceAccountIdentifier - source account identifier to which we want the reference
523
+ * @returns {Promise<void>}
524
+ */
525
+
526
+
527
+ var ensureSourceAccountIdentifierReference = /*#__PURE__*/function () {
528
+ var _ref8 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(_ref7) {
529
+ var client, folder, sourceAccountIdentifier, fileCollection;
530
+ return _regenerator.default.wrap(function _callee5$(_context5) {
531
+ while (1) {
532
+ switch (_context5.prev = _context5.next) {
533
+ case 0:
534
+ client = _ref7.client, folder = _ref7.folder, sourceAccountIdentifier = _ref7.sourceAccountIdentifier;
535
+ fileCollection = client.collection(FILES_DOCTYPE);
536
+
537
+ if (hasSourceAccountIdentifierReference(folder, sourceAccountIdentifier)) {
538
+ _context5.next = 5;
539
+ break;
540
+ }
541
+
542
+ _context5.next = 5;
543
+ return fileCollection.addReferencesTo({
544
+ _id: sourceAccountIdentifier,
545
+ _type: 'io.cozy.accounts.sourceAccountIdentifier'
546
+ }, [folder]);
547
+
548
+ case 5:
549
+ case "end":
550
+ return _context5.stop();
551
+ }
552
+ }
553
+ }, _callee5);
554
+ }));
555
+
556
+ return function ensureSourceAccountIdentifierReference(_x8) {
557
+ return _ref8.apply(this, arguments);
558
+ };
559
+ }();
560
+ /**
561
+ * try to find a konnector account folder using file references
562
+ *
563
+ * @param {object} options - options object
564
+ * @param {CozyClient} options.client - CozyClient instance
565
+ * @param {string} options.slug - konnector slug
566
+ * @param {string} options.sourceAccountIdentifier - source account identifier
567
+ * @returns {Promise<import('../types').IOCozyFolder>}
568
+ */
569
+
570
+
571
+ var findKonnectorAccountFolderByReference = /*#__PURE__*/function () {
572
+ var _ref10 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6(_ref9) {
573
+ var client, slug, sourceAccountIdentifier, _yield$client$query, folders;
574
+
575
+ return _regenerator.default.wrap(function _callee6$(_context6) {
576
+ while (1) {
577
+ switch (_context6.prev = _context6.next) {
578
+ case 0:
579
+ client = _ref9.client, slug = _ref9.slug, sourceAccountIdentifier = _ref9.sourceAccountIdentifier;
580
+ _context6.next = 3;
581
+ return client.query((0, _dsl.Q)(FILES_DOCTYPE).partialIndex({
582
+ type: 'directory',
583
+ trashed: false
584
+ }).referencedBy({
585
+ _type: 'io.cozy.konnectors',
586
+ _id: "io.cozy.konnectors/".concat(slug)
587
+ }));
588
+
589
+ case 3:
590
+ _yield$client$query = _context6.sent;
591
+ folders = _yield$client$query.included;
592
+ return _context6.abrupt("return", folders.find(function (folder) {
593
+ return hasSourceAccountIdentifierReference(folder, sourceAccountIdentifier);
594
+ }));
595
+
596
+ case 6:
597
+ case "end":
598
+ return _context6.stop();
599
+ }
600
+ }
601
+ }, _callee6);
602
+ }));
603
+
604
+ return function findKonnectorAccountFolderByReference(_x9) {
605
+ return _ref10.apply(this, arguments);
606
+ };
607
+ }();
608
+ /**
609
+ * try to find a konnector main folder using file references
610
+ *
611
+ * @param {object} options - options object
612
+ * @param {CozyClient} options.client - CozyClient instance
613
+ * @param {string} options.slug - konnector slug
614
+ * @returns {Promise<import('../types').IOCozyFolder>}
615
+ */
616
+
617
+
618
+ exports.findKonnectorAccountFolderByReference = findKonnectorAccountFolderByReference;
619
+
620
+ var findKonnectorMainFolderByReference = /*#__PURE__*/function () {
621
+ var _ref12 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee7(_ref11) {
622
+ var client, slug, _yield$client$query2, files;
623
+
624
+ return _regenerator.default.wrap(function _callee7$(_context7) {
625
+ while (1) {
626
+ switch (_context7.prev = _context7.next) {
627
+ case 0:
628
+ client = _ref11.client, slug = _ref11.slug;
629
+ _context7.next = 3;
630
+ return client.query((0, _dsl.Q)(FILES_DOCTYPE).partialIndex({
631
+ type: 'directory',
632
+ trashed: false
633
+ }).referencedBy({
634
+ _type: 'io.cozy.konnectors',
635
+ _id: "io.cozy.konnectors/".concat(slug)
636
+ }));
637
+
638
+ case 3:
639
+ _yield$client$query2 = _context7.sent;
640
+ files = _yield$client$query2.included;
641
+ return _context7.abrupt("return", files.find(function (file) {
642
+ return !hasSourceAccountIdentifierReference(file);
643
+ }));
644
+
645
+ case 6:
646
+ case "end":
647
+ return _context7.stop();
648
+ }
649
+ }
650
+ }, _callee7);
651
+ }));
652
+
653
+ return function findKonnectorMainFolderByReference(_x10) {
654
+ return _ref12.apply(this, arguments);
655
+ };
656
+ }();
657
+ /**
658
+ * Will create or update an account folder with proper references
659
+ *
660
+ * @param {object} options - options object
661
+ * @param {CozyClient} options.client - CozyClient instance
662
+ * @param {import('../types').IOCozyFolder} options.mainFolder - Main konnector folder where the account folder may be created
663
+ * @param {import('../types').IOCozyKonnector} options.konnector - Konnector associated to the main folder
664
+ * @param {string} options.sourceAccountIdentifier - source account identifier
665
+ * @returns {Promise<import('../types').IOCozyFolder>}
666
+ */
667
+
668
+
669
+ function saveKonnectorAccountFolder(_x11) {
670
+ return _saveKonnectorAccountFolder.apply(this, arguments);
671
+ }
672
+
673
+ function _saveKonnectorAccountFolder() {
674
+ _saveKonnectorAccountFolder = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8(_ref13) {
675
+ var client, mainFolder, konnector, sourceAccountIdentifier, path, folder;
676
+ return _regenerator.default.wrap(function _callee8$(_context8) {
677
+ while (1) {
678
+ switch (_context8.prev = _context8.next) {
679
+ case 0:
680
+ client = _ref13.client, mainFolder = _ref13.mainFolder, konnector = _ref13.konnector, sourceAccountIdentifier = _ref13.sourceAccountIdentifier;
681
+ path = mainFolder.path + '/' + sourceAccountIdentifier;
682
+ _context8.next = 4;
683
+ return statDirectoryByPath(client, path);
684
+
685
+ case 4:
686
+ _context8.t0 = _context8.sent;
687
+
688
+ if (_context8.t0) {
689
+ _context8.next = 9;
690
+ break;
691
+ }
692
+
693
+ _context8.next = 8;
694
+ return createDirectoryByPath(client, path);
695
+
696
+ case 8:
697
+ _context8.t0 = _context8.sent;
698
+
699
+ case 9:
700
+ folder = _context8.t0;
701
+ _context8.next = 12;
702
+ return ensureKonnectorReference({
703
+ client: client,
704
+ folder: folder,
705
+ konnector: konnector
706
+ });
707
+
708
+ case 12:
709
+ _context8.next = 14;
710
+ return ensureSourceAccountIdentifierReference({
711
+ client: client,
712
+ folder: folder,
713
+ sourceAccountIdentifier: sourceAccountIdentifier
714
+ });
715
+
716
+ case 14:
717
+ return _context8.abrupt("return", folder);
718
+
719
+ case 15:
720
+ case "end":
721
+ return _context8.stop();
722
+ }
723
+ }
724
+ }, _callee8);
725
+ }));
726
+ return _saveKonnectorAccountFolder.apply(this, arguments);
727
+ }
@@ -3,8 +3,9 @@ import { ensureKonnectorFolder } from './konnectorFolder'
3
3
  describe('ensureKonnectorFolder', () => {
4
4
  const mockClient = {
5
5
  collection: () => mockClient,
6
- findReferencedBy: jest.fn(),
6
+ query: jest.fn(),
7
7
  statByPath: jest.fn(),
8
+ statById: jest.fn(),
8
9
  add: jest.fn(),
9
10
  addReferencesTo: jest.fn(),
10
11
  createDirectoryByPath: jest.fn()
@@ -17,6 +18,7 @@ describe('ensureKonnectorFolder', () => {
17
18
  ]
18
19
  const konnector = {
19
20
  name: 'konnectorName',
21
+ slug: 'konnectorSlug',
20
22
  folders: [
21
23
  {
22
24
  defaultDir: '$administrative/$konnector/$account'
@@ -26,7 +28,10 @@ describe('ensureKonnectorFolder', () => {
26
28
 
27
29
  const account = { auth: { accountName: 'testAccountName' } }
28
30
  beforeEach(() => {
29
- mockClient.findReferencedBy.mockResolvedValue({
31
+ mockClient.query.mockResolvedValueOnce({
32
+ included: existingMagicFolder
33
+ })
34
+ mockClient.query.mockResolvedValueOnce({
30
35
  included: existingMagicFolder
31
36
  })
32
37
  })
@@ -36,10 +41,17 @@ describe('ensureKonnectorFolder', () => {
36
41
  })
37
42
 
38
43
  it('should create a folder when it does not exist and return it', async () => {
44
+ mockClient.query.mockResolvedValue({
45
+ included: []
46
+ })
39
47
  mockClient.statByPath.mockRejectedValueOnce({ status: 404 })
48
+ mockClient.statById.mockResolvedValueOnce({
49
+ data: { _id: 'parentfolderid' }
50
+ })
40
51
  mockClient.createDirectoryByPath.mockResolvedValueOnce({
41
52
  data: {
42
- _id: 'createdfolderid'
53
+ _id: 'createdfolderid',
54
+ dir_id: 'parentfolderid'
43
55
  }
44
56
  })
45
57
  const result = await ensureKonnectorFolder(mockClient, {
@@ -47,7 +59,10 @@ describe('ensureKonnectorFolder', () => {
47
59
  account,
48
60
  lang: 'fr'
49
61
  })
50
- expect(result).toStrictEqual({ _id: 'createdfolderid' })
62
+ expect(result).toStrictEqual({
63
+ _id: 'createdfolderid',
64
+ dir_id: 'parentfolderid'
65
+ })
51
66
  expect(mockClient.statByPath).toHaveBeenCalledWith(
52
67
  '/Administratif/konnectorName/testAccountName'
53
68
  )
@@ -62,9 +77,122 @@ describe('ensureKonnectorFolder', () => {
62
77
  }
63
78
  })
64
79
  expect(mockClient.addReferencesTo).toHaveBeenCalledWith(konnector, [
65
- { _id: 'createdfolderid' }
80
+ { _id: 'createdfolderid', dir_id: 'parentfolderid' }
66
81
  ])
67
82
  })
83
+ it('should not create any folder if account folder with proper references exists', async () => {
84
+ const existingAccountFolder = {
85
+ _id: 'acountFolderId',
86
+ type: 'directory',
87
+ referenced_by: [
88
+ {
89
+ type: 'io.cozy.konnectors',
90
+ id: 'io.cozy.konnectors/konnectorSlug'
91
+ },
92
+ {
93
+ type: 'io.cozy.accounts.sourceAccountIdentifier',
94
+ id: 'testAccountName'
95
+ }
96
+ ]
97
+ }
98
+ mockClient.query.mockResolvedValueOnce({
99
+ included: [existingAccountFolder]
100
+ })
101
+ const result = await ensureKonnectorFolder(mockClient, {
102
+ konnector,
103
+ account,
104
+ lang: 'fr'
105
+ })
106
+ expect(result).toStrictEqual(existingAccountFolder)
107
+ expect(mockClient.createDirectoryByPath).not.toHaveBeenCalled()
108
+ })
109
+ it('should create account folder as a child of folder with konnector reference if any', async () => {
110
+ const existingKonnectorFolder = {
111
+ _id: 'konnectorFolderId',
112
+ type: 'directory',
113
+ referenced_by: [
114
+ {
115
+ type: 'io.cozy.konnectors',
116
+ id: 'io.cozy.konnectors/konnectorSlug'
117
+ }
118
+ ]
119
+ }
120
+ mockClient.query.mockResolvedValue({
121
+ included: [existingKonnectorFolder]
122
+ })
123
+ mockClient.statByPath.mockRejectedValueOnce({
124
+ status: 404
125
+ })
126
+ const accountFolder = {
127
+ _id: 'accountFolderId',
128
+ dir_id: 'konnectorFolderId'
129
+ }
130
+ mockClient.createDirectoryByPath.mockResolvedValueOnce({
131
+ data: accountFolder
132
+ })
133
+
134
+ const result = await ensureKonnectorFolder(mockClient, {
135
+ konnector,
136
+ account,
137
+ lang: 'fr'
138
+ })
139
+ expect(result).toStrictEqual(accountFolder)
140
+ expect(mockClient.createDirectoryByPath).toHaveBeenCalledTimes(1)
141
+ expect(mockClient.addReferencesTo).toHaveBeenCalledTimes(2)
142
+ expect(mockClient.addReferencesTo).toHaveBeenNthCalledWith(1, konnector, [
143
+ accountFolder
144
+ ])
145
+ expect(mockClient.addReferencesTo).toHaveBeenNthCalledWith(
146
+ 2,
147
+ {
148
+ _id: 'testAccountName',
149
+ _type: 'io.cozy.accounts.sourceAccountIdentifier'
150
+ },
151
+ [accountFolder]
152
+ )
153
+ })
154
+ it('should add proper references to konnector folder child with proper name if any', async () => {
155
+ const existingKonnectorFolder = {
156
+ _id: 'konnectorFolderId',
157
+ type: 'directory',
158
+ referenced_by: [
159
+ {
160
+ type: 'io.cozy.konnectors',
161
+ id: 'io.cozy.konnectors/konnectorSlug'
162
+ }
163
+ ]
164
+ }
165
+ mockClient.query.mockResolvedValue({
166
+ included: [existingKonnectorFolder]
167
+ })
168
+ const accountFolder = {
169
+ _id: 'accountFolderId',
170
+ dir_id: 'konnectorFolderId'
171
+ }
172
+ mockClient.statByPath.mockResolvedValueOnce({
173
+ data: accountFolder
174
+ })
175
+
176
+ const result = await ensureKonnectorFolder(mockClient, {
177
+ konnector,
178
+ account,
179
+ lang: 'fr'
180
+ })
181
+ expect(result).toStrictEqual(accountFolder)
182
+ expect(mockClient.createDirectoryByPath).not.toHaveBeenCalled()
183
+ expect(mockClient.addReferencesTo).toHaveBeenCalledTimes(2)
184
+ expect(mockClient.addReferencesTo).toHaveBeenNthCalledWith(1, konnector, [
185
+ accountFolder
186
+ ])
187
+ expect(mockClient.addReferencesTo).toHaveBeenNthCalledWith(
188
+ 2,
189
+ {
190
+ _id: 'testAccountName',
191
+ _type: 'io.cozy.accounts.sourceAccountIdentifier'
192
+ },
193
+ [accountFolder]
194
+ )
195
+ })
68
196
  it('should not create a folder if it already exist', async () => {
69
197
  const existingMagicFolder = [
70
198
  {
@@ -72,12 +200,24 @@ describe('ensureKonnectorFolder', () => {
72
200
  created_at: '2023-03-02T14:57:07.661588+01:00'
73
201
  }
74
202
  ]
75
- mockClient.findReferencedBy.mockResolvedValue({
203
+ mockClient.query.mockResolvedValueOnce({
76
204
  included: existingMagicFolder
77
205
  })
206
+ mockClient.query.mockResolvedValueOnce({
207
+ included: []
208
+ })
209
+ mockClient.query.mockResolvedValueOnce({
210
+ included: []
211
+ })
212
+ mockClient.statById.mockResolvedValueOnce({
213
+ data: {
214
+ _id: 'parentfolderid'
215
+ }
216
+ })
78
217
  mockClient.statByPath.mockResolvedValueOnce({
79
218
  data: {
80
- _id: 'alreadyexistingfolderid'
219
+ _id: 'alreadyexistingfolderid',
220
+ dir_id: 'parentfolderid'
81
221
  }
82
222
  })
83
223
  const result = await ensureKonnectorFolder(mockClient, {
@@ -92,22 +232,34 @@ describe('ensureKonnectorFolder', () => {
92
232
  account: { auth: { accountName: 'testAccountName' } },
93
233
  lang: 'fr'
94
234
  })
95
- expect(result).toStrictEqual({ _id: 'alreadyexistingfolderid' })
235
+ expect(result).toStrictEqual({
236
+ _id: 'alreadyexistingfolderid',
237
+ dir_id: 'parentfolderid'
238
+ })
96
239
  expect(mockClient.statByPath).toHaveBeenCalledWith(
97
240
  '/Administratif/konnectorName/testAccountName'
98
241
  )
99
242
  expect(mockClient.createDirectoryByPath).not.toHaveBeenCalled()
100
243
  })
101
244
  it('should recreate a folder when the destination folder is in the trash', async () => {
245
+ mockClient.query.mockResolvedValue({
246
+ included: []
247
+ })
102
248
  mockClient.statByPath.mockResolvedValueOnce({
103
249
  data: {
104
250
  _id: 'folderintrash',
105
251
  trashed: true
106
252
  }
107
253
  })
254
+ mockClient.statById.mockResolvedValueOnce({
255
+ data: {
256
+ _id: 'parentfolderid'
257
+ }
258
+ })
108
259
  mockClient.createDirectoryByPath.mockResolvedValueOnce({
109
260
  data: {
110
- _id: 'createdfolderid'
261
+ _id: 'createdfolderid',
262
+ dir_id: 'parentfolderid'
111
263
  }
112
264
  })
113
265
  const result = await ensureKonnectorFolder(mockClient, {
@@ -115,7 +267,10 @@ describe('ensureKonnectorFolder', () => {
115
267
  account,
116
268
  lang: 'fr'
117
269
  })
118
- expect(result).toStrictEqual({ _id: 'createdfolderid' })
270
+ expect(result).toStrictEqual({
271
+ _id: 'createdfolderid',
272
+ dir_id: 'parentfolderid'
273
+ })
119
274
  expect(mockClient.statByPath).toHaveBeenCalledWith(
120
275
  '/Administratif/konnectorName/testAccountName'
121
276
  )
@@ -130,7 +285,7 @@ describe('ensureKonnectorFolder', () => {
130
285
  }
131
286
  })
132
287
  expect(mockClient.addReferencesTo).toHaveBeenCalledWith(konnector, [
133
- { _id: 'createdfolderid' }
288
+ { _id: 'createdfolderid', dir_id: 'parentfolderid' }
134
289
  ])
135
290
  })
136
291
  })
package/dist/types.js CHANGED
@@ -404,6 +404,7 @@ var _dsl = require("./queries/dsl");
404
404
  /**
405
405
  * @typedef {object} FolderDocument - An io.cozy.files document
406
406
  * @property {string} _id - Id of the folder
407
+ * @property {string} dir_id - Parent folder
407
408
  * @property {FilesDoctype} _type - Doctype of the folder
408
409
  * @property {string} name - Name of the folder
409
410
  * @property {object} metadata - Metadata of the folder
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-client",
3
- "version": "45.10.0",
3
+ "version": "45.11.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.node.js",
6
6
  "browser": "dist/index.js",
@@ -75,5 +75,5 @@
75
75
  "react-native-ios11-devicecheck": "https://github.com/cozy/react-native-devicecheck#app-attest-v0.1"
76
76
  },
77
77
  "sideEffects": false,
78
- "gitHead": "d981b243e5ea7d4b0e24cb5c6e28245e97f65983"
78
+ "gitHead": "79b5ecaf281762089293158d3a700ee5ef1bcf0f"
79
79
  }
@@ -9,4 +9,9 @@ export function buildFolderPath(konnector: import('../types').IOCozyKonnector, a
9
9
  [x: string]: string;
10
10
  }): string;
11
11
  export function buildFolderPermission(folder: import('../types').IOCozyFolder): any;
12
+ export function findKonnectorAccountFolderByReference({ client, slug, sourceAccountIdentifier }: {
13
+ client: CozyClient;
14
+ slug: string;
15
+ sourceAccountIdentifier: string;
16
+ }): Promise<import('../types').IOCozyFolder>;
12
17
  import CozyClient from "../CozyClient";
package/types/types.d.ts CHANGED
@@ -744,6 +744,10 @@ export type FolderDocument = {
744
744
  * - Id of the folder
745
745
  */
746
746
  _id: string;
747
+ /**
748
+ * - Parent folder
749
+ */
750
+ dir_id: string;
747
751
  /**
748
752
  * - Doctype of the folder
749
753
  */