cozy-client 41.2.1 → 41.3.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/models/document/locales/en.json +4 -0
- package/dist/models/document/locales/fr.json +4 -0
- package/dist/models/document/locales/index.js +16 -4
- package/dist/models/folder.js +1 -1
- package/dist/models/folder.spec.js +6 -18
- package/dist/models/index.js +5 -1
- package/dist/models/konnectorFolder.js +380 -0
- package/dist/models/konnectorFolder.spec.js +136 -0
- package/dist/types.js +1 -0
- package/package.json +2 -2
- package/types/models/document/locales/index.d.ts +5 -0
- package/types/models/index.d.ts +2 -1
- package/types/models/konnectorFolder.d.ts +12 -0
- package/types/models/permission.d.ts +1 -1
- package/types/types.d.ts +4 -0
package/dist/CozyClient.js
CHANGED
|
@@ -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 = "41.
|
|
2706
|
+
CozyClient.version = "41.3.0";
|
|
2707
2707
|
|
|
2708
2708
|
_microee.default.mixin(CozyClient);
|
|
2709
2709
|
|
|
@@ -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.getBoundT = void 0;
|
|
8
|
+
exports.getLocalizer = exports.getBoundT = void 0;
|
|
9
9
|
|
|
10
10
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
11
11
|
|
|
@@ -40,8 +40,7 @@ for (var _i = 0, _langs = langs; _i < _langs.length; _i++) {
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
var getBoundT = function getBoundT(lang) {
|
|
43
|
-
var
|
|
44
|
-
var t = polyglot.t.bind(polyglot);
|
|
43
|
+
var t = getLocalizer(lang);
|
|
45
44
|
return function (label) {
|
|
46
45
|
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
47
46
|
|
|
@@ -53,5 +52,18 @@ var getBoundT = function getBoundT(lang) {
|
|
|
53
52
|
return emojiCountry ? "".concat(t(label, newOpts), " ").concat(emojiCountry) : t(label, newOpts);
|
|
54
53
|
};
|
|
55
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* @param {string} lang - fr, en, etc
|
|
57
|
+
* @returns {Function} - localization function
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
exports.getBoundT = getBoundT;
|
|
62
|
+
|
|
63
|
+
var getLocalizer = function getLocalizer(lang) {
|
|
64
|
+
var polyglot = polyglots[lang] || polyglots['en'];
|
|
65
|
+
var t = polyglot.t.bind(polyglot);
|
|
66
|
+
return t;
|
|
67
|
+
};
|
|
56
68
|
|
|
57
|
-
exports.
|
|
69
|
+
exports.getLocalizer = getLocalizer;
|
package/dist/models/folder.js
CHANGED
|
@@ -181,7 +181,7 @@ var getReferencedFolder = /*#__PURE__*/function () {
|
|
|
181
181
|
_yield$client$collect = _context3.sent;
|
|
182
182
|
included = _yield$client$collect.included;
|
|
183
183
|
foldersOutsideTrash = included.filter(function (folder) {
|
|
184
|
-
return !/^\/\.cozy_trash/.test(folder.
|
|
184
|
+
return !/^\/\.cozy_trash/.test(folder.path);
|
|
185
185
|
}); // there can be multiple folders with the same reference in some edge cases
|
|
186
186
|
// when this happens we return the most recent one
|
|
187
187
|
|
|
@@ -29,15 +29,11 @@ describe('Folder model', () => {
|
|
|
29
29
|
it('should return first existing magic folder', async () => {
|
|
30
30
|
const existingMagicFolders = [
|
|
31
31
|
{
|
|
32
|
-
|
|
33
|
-
path: '/Administrative'
|
|
34
|
-
},
|
|
32
|
+
path: '/Administrative',
|
|
35
33
|
created_at: '2020-03-02T14:57:07.661588+01:00'
|
|
36
34
|
},
|
|
37
35
|
{
|
|
38
|
-
|
|
39
|
-
path: '/Administrative2'
|
|
40
|
-
},
|
|
36
|
+
path: '/Administrative2',
|
|
41
37
|
created_at: '2019-03-02T14:57:07.661588+01:00'
|
|
42
38
|
}
|
|
43
39
|
]
|
|
@@ -96,15 +92,11 @@ describe('Folder model', () => {
|
|
|
96
92
|
describe('getReferencedFolder', () => {
|
|
97
93
|
it('should filter trashed folders', async () => {
|
|
98
94
|
const referencedFolder = {
|
|
99
|
-
|
|
100
|
-
path: '/Reference/Folder'
|
|
101
|
-
}
|
|
95
|
+
path: '/Reference/Folder'
|
|
102
96
|
}
|
|
103
97
|
|
|
104
98
|
const trashFolder = {
|
|
105
|
-
|
|
106
|
-
path: '/.cozy_trash/Old/Reference/Folder'
|
|
107
|
-
}
|
|
99
|
+
path: '/.cozy_trash/Old/Reference/Folder'
|
|
108
100
|
}
|
|
109
101
|
|
|
110
102
|
mockClient.findReferencedBy.mockResolvedValue({
|
|
@@ -118,15 +110,11 @@ describe('Folder model', () => {
|
|
|
118
110
|
|
|
119
111
|
it('should return the most recently created folder', async () => {
|
|
120
112
|
const oldFolder = {
|
|
121
|
-
|
|
122
|
-
path: '/Old/folder'
|
|
123
|
-
},
|
|
113
|
+
path: '/Old/folder',
|
|
124
114
|
created_at: '2019-03-02T14:57:07.661588+01:00'
|
|
125
115
|
}
|
|
126
116
|
const recentFolder = {
|
|
127
|
-
|
|
128
|
-
path: '/Recent/folder'
|
|
129
|
-
},
|
|
117
|
+
path: '/Recent/folder',
|
|
130
118
|
created_at: '2020-03-02T14:57:07.661588+01:00'
|
|
131
119
|
}
|
|
132
120
|
|
package/dist/models/index.js
CHANGED
|
@@ -5,7 +5,7 @@ var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWild
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", {
|
|
6
6
|
value: true
|
|
7
7
|
});
|
|
8
|
-
exports.user = exports.paper = exports.dacc = exports.sharing = exports.timeseries = exports.document = exports.contact = exports.utils = exports.permission = exports.note = exports.account = exports.folder = exports.file = exports.applications = exports.instance = exports.trigger = exports.accounts = exports.triggers = void 0;
|
|
8
|
+
exports.user = exports.paper = exports.dacc = exports.sharing = exports.timeseries = exports.document = exports.contact = exports.utils = exports.permission = exports.note = exports.account = exports.konnectorFolder = exports.folder = exports.file = exports.applications = exports.instance = exports.trigger = exports.accounts = exports.triggers = void 0;
|
|
9
9
|
|
|
10
10
|
var trigger = _interopRequireWildcard(require("./trigger"));
|
|
11
11
|
|
|
@@ -27,6 +27,10 @@ var folder = _interopRequireWildcard(require("./folder"));
|
|
|
27
27
|
|
|
28
28
|
exports.folder = folder;
|
|
29
29
|
|
|
30
|
+
var konnectorFolder = _interopRequireWildcard(require("./konnectorFolder"));
|
|
31
|
+
|
|
32
|
+
exports.konnectorFolder = konnectorFolder;
|
|
33
|
+
|
|
30
34
|
var account = _interopRequireWildcard(require("./account"));
|
|
31
35
|
|
|
32
36
|
exports.account = account;
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.buildFolderPermission = exports.buildFolderPath = exports.statDirectoryByPath = exports.createDirectoryByPath = exports.ensureKonnectorFolder = void 0;
|
|
9
|
+
|
|
10
|
+
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
11
|
+
|
|
12
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
13
|
+
|
|
14
|
+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
15
|
+
|
|
16
|
+
var _folder = require("./folder");
|
|
17
|
+
|
|
18
|
+
var _CozyClient = _interopRequireDefault(require("../CozyClient"));
|
|
19
|
+
|
|
20
|
+
var _account = require("./account");
|
|
21
|
+
|
|
22
|
+
var _locales = require("./document/locales");
|
|
23
|
+
|
|
24
|
+
//@ts-check
|
|
25
|
+
var FILES_DOCTYPE = 'io.cozy.files';
|
|
26
|
+
var PERMISSIONS_DOCTYPE = 'io.cozy.permissions'; // Default name for base directory
|
|
27
|
+
|
|
28
|
+
var DEFAULT_LOCALIZED_BASE_DIR = 'Administrative';
|
|
29
|
+
/**
|
|
30
|
+
* Ensures the destination folder of a konnector exists and is initiated with proper permissions and references
|
|
31
|
+
*
|
|
32
|
+
* @param {CozyClient} client - CozyClient instance
|
|
33
|
+
* @param {Object} options - options object
|
|
34
|
+
* @param {import('../types').IOCozyKonnector} options.konnector - io.cozy.konnectors document
|
|
35
|
+
* @param {import('../types').IOCozyAccount} options.account - io.cozy.accounts document
|
|
36
|
+
* @param {String} options.lang - instance current language. ex: 'fr'
|
|
37
|
+
* @returns {Promise<import('../types').IOCozyFolder>}
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
var ensureKonnectorFolder = /*#__PURE__*/function () {
|
|
41
|
+
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;
|
|
43
|
+
|
|
44
|
+
return _regenerator.default.wrap(function _callee$(_context) {
|
|
45
|
+
while (1) {
|
|
46
|
+
switch (_context.prev = _context.next) {
|
|
47
|
+
case 0:
|
|
48
|
+
konnector = _ref.konnector, account = _ref.account, lang = _ref.lang;
|
|
49
|
+
permissions = client.collection(PERMISSIONS_DOCTYPE);
|
|
50
|
+
files = client.collection(FILES_DOCTYPE);
|
|
51
|
+
t = (0, _locales.getLocalizer)(lang);
|
|
52
|
+
_context.next = 6;
|
|
53
|
+
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'))]);
|
|
54
|
+
|
|
55
|
+
case 6:
|
|
56
|
+
_yield$Promise$all = _context.sent;
|
|
57
|
+
_yield$Promise$all2 = (0, _slicedToArray2.default)(_yield$Promise$all, 2);
|
|
58
|
+
adminFolder = _yield$Promise$all2[0];
|
|
59
|
+
photosFolder = _yield$Promise$all2[1];
|
|
60
|
+
path = buildFolderPath(konnector, account, {
|
|
61
|
+
administrative: adminFolder.path,
|
|
62
|
+
photos: photosFolder.path
|
|
63
|
+
});
|
|
64
|
+
_context.next = 13;
|
|
65
|
+
return statDirectoryByPath(client, path);
|
|
66
|
+
|
|
67
|
+
case 13:
|
|
68
|
+
_context.t0 = _context.sent;
|
|
69
|
+
|
|
70
|
+
if (_context.t0) {
|
|
71
|
+
_context.next = 18;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
_context.next = 17;
|
|
76
|
+
return createDirectoryByPath(client, path);
|
|
77
|
+
|
|
78
|
+
case 17:
|
|
79
|
+
_context.t0 = _context.sent;
|
|
80
|
+
|
|
81
|
+
case 18:
|
|
82
|
+
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:
|
|
91
|
+
return _context.abrupt("return", folder);
|
|
92
|
+
|
|
93
|
+
case 24:
|
|
94
|
+
case "end":
|
|
95
|
+
return _context.stop();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}, _callee);
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
return function ensureKonnectorFolder(_x, _x2) {
|
|
102
|
+
return _ref2.apply(this, arguments);
|
|
103
|
+
};
|
|
104
|
+
}();
|
|
105
|
+
/**
|
|
106
|
+
* Creates a directory from a given path
|
|
107
|
+
*
|
|
108
|
+
* @param {CozyClient} client CozyClient
|
|
109
|
+
* @param {string} path Directory path
|
|
110
|
+
* @returns {Promise<import('../types').IOCozyFolder>} Directory attributes
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
exports.ensureKonnectorFolder = ensureKonnectorFolder;
|
|
115
|
+
|
|
116
|
+
var createDirectoryByPath = /*#__PURE__*/function () {
|
|
117
|
+
var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(client, path) {
|
|
118
|
+
var _yield$client$collect, data;
|
|
119
|
+
|
|
120
|
+
return _regenerator.default.wrap(function _callee2$(_context2) {
|
|
121
|
+
while (1) {
|
|
122
|
+
switch (_context2.prev = _context2.next) {
|
|
123
|
+
case 0:
|
|
124
|
+
_context2.next = 2;
|
|
125
|
+
return client.collection(FILES_DOCTYPE).createDirectoryByPath(path);
|
|
126
|
+
|
|
127
|
+
case 2:
|
|
128
|
+
_yield$client$collect = _context2.sent;
|
|
129
|
+
data = _yield$client$collect.data;
|
|
130
|
+
return _context2.abrupt("return", data);
|
|
131
|
+
|
|
132
|
+
case 5:
|
|
133
|
+
case "end":
|
|
134
|
+
return _context2.stop();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}, _callee2);
|
|
138
|
+
}));
|
|
139
|
+
|
|
140
|
+
return function createDirectoryByPath(_x3, _x4) {
|
|
141
|
+
return _ref3.apply(this, arguments);
|
|
142
|
+
};
|
|
143
|
+
}();
|
|
144
|
+
/**
|
|
145
|
+
* Retrieves a directory from its path
|
|
146
|
+
*
|
|
147
|
+
* @param {CozyClient} client CozyClient
|
|
148
|
+
* @param {string} path Directory path
|
|
149
|
+
* @returns {Promise<import('../types').IOCozyFolder|null>} Created io.cozy.files document
|
|
150
|
+
* @throws will throw an error on any error without status === 404
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
exports.createDirectoryByPath = createDirectoryByPath;
|
|
155
|
+
|
|
156
|
+
var statDirectoryByPath = /*#__PURE__*/function () {
|
|
157
|
+
var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(client, path) {
|
|
158
|
+
var response;
|
|
159
|
+
return _regenerator.default.wrap(function _callee3$(_context3) {
|
|
160
|
+
while (1) {
|
|
161
|
+
switch (_context3.prev = _context3.next) {
|
|
162
|
+
case 0:
|
|
163
|
+
_context3.prev = 0;
|
|
164
|
+
_context3.next = 3;
|
|
165
|
+
return client.collection(FILES_DOCTYPE).statByPath(path);
|
|
166
|
+
|
|
167
|
+
case 3:
|
|
168
|
+
response = _context3.sent;
|
|
169
|
+
|
|
170
|
+
if (!response.data.trashed) {
|
|
171
|
+
_context3.next = 6;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return _context3.abrupt("return", null);
|
|
176
|
+
|
|
177
|
+
case 6:
|
|
178
|
+
return _context3.abrupt("return", response.data);
|
|
179
|
+
|
|
180
|
+
case 9:
|
|
181
|
+
_context3.prev = 9;
|
|
182
|
+
_context3.t0 = _context3["catch"](0);
|
|
183
|
+
|
|
184
|
+
if (!(_context3.t0 && _context3.t0.status === 404)) {
|
|
185
|
+
_context3.next = 13;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return _context3.abrupt("return", null);
|
|
190
|
+
|
|
191
|
+
case 13:
|
|
192
|
+
throw new Error(_context3.t0.message);
|
|
193
|
+
|
|
194
|
+
case 14:
|
|
195
|
+
case "end":
|
|
196
|
+
return _context3.stop();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}, _callee3, null, [[0, 9]]);
|
|
200
|
+
}));
|
|
201
|
+
|
|
202
|
+
return function statDirectoryByPath(_x5, _x6) {
|
|
203
|
+
return _ref4.apply(this, arguments);
|
|
204
|
+
};
|
|
205
|
+
}();
|
|
206
|
+
/**
|
|
207
|
+
* Build folder path for a given konnector and a given account.
|
|
208
|
+
*
|
|
209
|
+
* If konnector.folders[0].defaultDir exists, it is used as default directory.
|
|
210
|
+
*
|
|
211
|
+
* Occurrences of following strings in base directory are replaced by:
|
|
212
|
+
* * `$administrative`: Administrative folder
|
|
213
|
+
* * `$photos`: Photos folder
|
|
214
|
+
*
|
|
215
|
+
* Occurrences of following strings in path are replaced by:
|
|
216
|
+
* * `$account: Account label (id or name)`
|
|
217
|
+
* * `$konnector`: Konnector name
|
|
218
|
+
*
|
|
219
|
+
* If no konnectors.folders[0].defaultDir is set, the default dir used is
|
|
220
|
+
* * `$administrative/$konnector/$account`
|
|
221
|
+
*
|
|
222
|
+
* @param {import('../types').IOCozyKonnector} konnector Konnector document
|
|
223
|
+
* @param {import('../types').IOCozyAccount} account Account document
|
|
224
|
+
* @param {Object<string, string>} magicFolders Object containing a mapping from folder
|
|
225
|
+
* identifiers (ex: $administrative) to their localized values (ex:
|
|
226
|
+
* Administratif).
|
|
227
|
+
* @returns {String} The result path
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
exports.statDirectoryByPath = statDirectoryByPath;
|
|
232
|
+
|
|
233
|
+
var buildFolderPath = function buildFolderPath(konnector, account) {
|
|
234
|
+
var _konnector$folders, _konnector$folders$;
|
|
235
|
+
|
|
236
|
+
var magicFolders = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
237
|
+
var fullPath = (konnector === null || konnector === void 0 ? void 0 : (_konnector$folders = konnector.folders) === null || _konnector$folders === void 0 ? void 0 : (_konnector$folders$ = _konnector$folders[0]) === null || _konnector$folders$ === void 0 ? void 0 : _konnector$folders$.defaultDir) || '$administrative/$konnector/$account'; // Trim `/` and avoid multiple `/` characters with regexp
|
|
238
|
+
|
|
239
|
+
var sanitizedPath = trim(fullPath.replace(/(\/+)/g, '/'), '/'); // If the konnector doesn't have any of our base dir, we set it to $administrative
|
|
240
|
+
|
|
241
|
+
if (!hasBaseDir(sanitizedPath)) {
|
|
242
|
+
sanitizedPath = '$administrative/' + sanitizedPath;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Now that we have our sanitizedPath, we can split it in two strings
|
|
246
|
+
* * `baseDir` containing the baseDir path
|
|
247
|
+
* * `buildedSubDir` containing the rest of the path (ie the path without baseDir)
|
|
248
|
+
*/
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
var baseDir = sanitizedPath.split('/', 1);
|
|
252
|
+
var buildedSubDir = buildSubDir(sanitizedPath, baseDir[0]);
|
|
253
|
+
var renderedBaseDir = renderBaseDir(baseDir[0], magicFolders);
|
|
254
|
+
var renderedPath = renderSubDir(buildedSubDir, {
|
|
255
|
+
// When adding a new allowed variable here, please keep documentation
|
|
256
|
+
// of `renderSubDir` function up to date.
|
|
257
|
+
konnector: konnector.name,
|
|
258
|
+
account: (0, _account.getAccountName)(account).replace(sanitizeAccountIdentifierRx, '-')
|
|
259
|
+
});
|
|
260
|
+
return "/".concat(renderedBaseDir, "/").concat(renderedPath);
|
|
261
|
+
};
|
|
262
|
+
/**
|
|
263
|
+
* Check if the provided Path start withs our allowedBaseDirPath to see
|
|
264
|
+
*
|
|
265
|
+
* @param {String} path - path to test
|
|
266
|
+
* @returns {Boolean}
|
|
267
|
+
*/
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
exports.buildFolderPath = buildFolderPath;
|
|
271
|
+
|
|
272
|
+
var hasBaseDir = function hasBaseDir(path) {
|
|
273
|
+
return allowedBaseDirVariables.some(function (baseDirVar) {
|
|
274
|
+
return path.startsWith(baseDirVar);
|
|
275
|
+
});
|
|
276
|
+
};
|
|
277
|
+
/**
|
|
278
|
+
* Base directories are directory where konnector may copy their data.
|
|
279
|
+
* They are expressed as variables which then need to be localized.
|
|
280
|
+
* Default is `$administrative`.
|
|
281
|
+
*/
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
var allowedBaseDirVariables = ['$administrative', '$photos'];
|
|
285
|
+
/**
|
|
286
|
+
* This method creates the subDir. We can't have an empty subDir, so we set
|
|
287
|
+
* it to our default '$konnector/$account'
|
|
288
|
+
*
|
|
289
|
+
* @param {String} fullPath String containing potentially the defaultDir
|
|
290
|
+
* @param {String} defaultDir String to remove from the fullPath
|
|
291
|
+
* @returns {String}
|
|
292
|
+
*/
|
|
293
|
+
|
|
294
|
+
var buildSubDir = function buildSubDir(fullPath, defaultDir) {
|
|
295
|
+
var buildedSubDir = fullPath.substring(defaultDir.length);
|
|
296
|
+
|
|
297
|
+
if (buildedSubDir === '') {
|
|
298
|
+
buildedSubDir = '$konnector/$account';
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return buildedSubDir;
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* Render base directory, based on given magicFolders object.
|
|
305
|
+
* For example, it will render `$administrative` with the given value passed in
|
|
306
|
+
* folders object. We expect to find in folders a localized value.
|
|
307
|
+
*
|
|
308
|
+
* @param {String} baseDir base directory variable, expects `$administrative`
|
|
309
|
+
* or `$photos`
|
|
310
|
+
* @param {Object<string, string>} magicFolders Object indexing base directory variable with
|
|
311
|
+
* corresponding localized name.
|
|
312
|
+
* @returns {String} Localized directory
|
|
313
|
+
*/
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
var renderBaseDir = function renderBaseDir(baseDir) {
|
|
317
|
+
var magicFolders = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
318
|
+
// Look for variable name into folders but without $ prefix
|
|
319
|
+
var renderedBaseDir = magicFolders[baseDir.slice(1)] || DEFAULT_LOCALIZED_BASE_DIR; // Trim `/` and avoid multiple `/` characters with regexp
|
|
320
|
+
|
|
321
|
+
return trim(renderedBaseDir.replace(/(\/+)/g, '/'), '/');
|
|
322
|
+
};
|
|
323
|
+
/**
|
|
324
|
+
* Render the given folder path using the given `variables` object.
|
|
325
|
+
* Available variable are `$konnector` (konnector name) and `$account`
|
|
326
|
+
* (account label, i.e. id or name)
|
|
327
|
+
*
|
|
328
|
+
* @param {String} path Path to render : ex '/Administratif/$konnector/$account'
|
|
329
|
+
* @param {Object} variables Object mapping variable to actual values
|
|
330
|
+
* @param {import('../types').IOCozyKonnector['name']} variables.konnector - konnector name
|
|
331
|
+
* @param {String} variables.account - account name
|
|
332
|
+
* @returns {String} Rendered path
|
|
333
|
+
*/
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
var renderSubDir = function renderSubDir(path, variables) {
|
|
337
|
+
// Trim `/` and avoid multiple `/` characters with regexp
|
|
338
|
+
var sanitizedPath = trim(path.replace(/(\/+)/g, '/'), '/'); // Let's get only full variable name limited by '/'. We want to avoid false
|
|
339
|
+
// positive like parsing `$variableInString` to `valueInString`
|
|
340
|
+
|
|
341
|
+
var segments = sanitizedPath.split('/');
|
|
342
|
+
return segments.map(function (segment) {
|
|
343
|
+
return (variables === null || variables === void 0 ? void 0 : variables[segment.slice(1)]) || segment;
|
|
344
|
+
}).join('/');
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
var sanitizeAccountIdentifierRx = /\//g;
|
|
348
|
+
/**
|
|
349
|
+
* Returns a permission ready to be passed to
|
|
350
|
+
* client.collection('io.cozy.permissions').add().
|
|
351
|
+
*
|
|
352
|
+
* @param {import('../types').IOCozyFolder} folder The folder which the konnector should have access
|
|
353
|
+
* @returns {Object} Permission object
|
|
354
|
+
*/
|
|
355
|
+
|
|
356
|
+
var buildFolderPermission = function buildFolderPermission(folder) {
|
|
357
|
+
return {
|
|
358
|
+
// Legacy name
|
|
359
|
+
saveFolder: {
|
|
360
|
+
type: 'io.cozy.files',
|
|
361
|
+
values: [folder._id],
|
|
362
|
+
verbs: ['GET', 'PATCH', 'POST']
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
};
|
|
366
|
+
/**
|
|
367
|
+
* Replacer of the lodash/trim function
|
|
368
|
+
*
|
|
369
|
+
* @param {String} str - Input string
|
|
370
|
+
* @param {String} c - String to trim from the input string
|
|
371
|
+
* @returns {String}
|
|
372
|
+
*/
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
exports.buildFolderPermission = buildFolderPermission;
|
|
376
|
+
|
|
377
|
+
var trim = function trim(str) {
|
|
378
|
+
var c = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '\\s';
|
|
379
|
+
return str.replace(new RegExp("^([".concat(c, "]*)(.*?)([").concat(c, "]*)$")), '$2');
|
|
380
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { ensureKonnectorFolder } from './konnectorFolder'
|
|
2
|
+
|
|
3
|
+
describe('ensureKonnectorFolder', () => {
|
|
4
|
+
const mockClient = {
|
|
5
|
+
collection: () => mockClient,
|
|
6
|
+
findReferencedBy: jest.fn(),
|
|
7
|
+
statByPath: jest.fn(),
|
|
8
|
+
add: jest.fn(),
|
|
9
|
+
addReferencesTo: jest.fn(),
|
|
10
|
+
createDirectoryByPath: jest.fn()
|
|
11
|
+
}
|
|
12
|
+
const existingMagicFolder = [
|
|
13
|
+
{
|
|
14
|
+
path: '/Administratif',
|
|
15
|
+
created_at: '2023-03-02T14:57:07.661588+01:00'
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
const konnector = {
|
|
19
|
+
name: 'konnectorName',
|
|
20
|
+
folders: [
|
|
21
|
+
{
|
|
22
|
+
defaultDir: '$administrative/$konnector/$account'
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const account = { auth: { accountName: 'testAccountName' } }
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
mockClient.findReferencedBy.mockResolvedValue({
|
|
30
|
+
included: existingMagicFolder
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
jest.resetAllMocks()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should create a folder when it does not exist and return it', async () => {
|
|
39
|
+
mockClient.statByPath.mockRejectedValueOnce({ status: 404 })
|
|
40
|
+
mockClient.createDirectoryByPath.mockResolvedValueOnce({
|
|
41
|
+
data: {
|
|
42
|
+
_id: 'createdfolderid'
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
const result = await ensureKonnectorFolder(mockClient, {
|
|
46
|
+
konnector,
|
|
47
|
+
account,
|
|
48
|
+
lang: 'fr'
|
|
49
|
+
})
|
|
50
|
+
expect(result).toStrictEqual({ _id: 'createdfolderid' })
|
|
51
|
+
expect(mockClient.statByPath).toHaveBeenCalledWith(
|
|
52
|
+
'/Administratif/konnectorName/testAccountName'
|
|
53
|
+
)
|
|
54
|
+
expect(mockClient.createDirectoryByPath).toHaveBeenCalledWith(
|
|
55
|
+
'/Administratif/konnectorName/testAccountName'
|
|
56
|
+
)
|
|
57
|
+
expect(mockClient.add).toHaveBeenCalledWith(konnector, {
|
|
58
|
+
saveFolder: {
|
|
59
|
+
type: 'io.cozy.files',
|
|
60
|
+
values: ['createdfolderid'],
|
|
61
|
+
verbs: ['GET', 'PATCH', 'POST']
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
expect(mockClient.addReferencesTo).toHaveBeenCalledWith(konnector, [
|
|
65
|
+
{ _id: 'createdfolderid' }
|
|
66
|
+
])
|
|
67
|
+
})
|
|
68
|
+
it('should not create a folder if it already exist', async () => {
|
|
69
|
+
const existingMagicFolder = [
|
|
70
|
+
{
|
|
71
|
+
path: '/Administratif',
|
|
72
|
+
created_at: '2023-03-02T14:57:07.661588+01:00'
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
mockClient.findReferencedBy.mockResolvedValue({
|
|
76
|
+
included: existingMagicFolder
|
|
77
|
+
})
|
|
78
|
+
mockClient.statByPath.mockResolvedValueOnce({
|
|
79
|
+
data: {
|
|
80
|
+
_id: 'alreadyexistingfolderid'
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
const result = await ensureKonnectorFolder(mockClient, {
|
|
84
|
+
konnector: {
|
|
85
|
+
name: 'konnectorName',
|
|
86
|
+
folders: [
|
|
87
|
+
{
|
|
88
|
+
defaultDir: '$administrative/$konnector/$account'
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
account: { auth: { accountName: 'testAccountName' } },
|
|
93
|
+
lang: 'fr'
|
|
94
|
+
})
|
|
95
|
+
expect(result).toStrictEqual({ _id: 'alreadyexistingfolderid' })
|
|
96
|
+
expect(mockClient.statByPath).toHaveBeenCalledWith(
|
|
97
|
+
'/Administratif/konnectorName/testAccountName'
|
|
98
|
+
)
|
|
99
|
+
expect(mockClient.createDirectoryByPath).not.toHaveBeenCalled()
|
|
100
|
+
})
|
|
101
|
+
it('should recreate a folder when the destination folder is in the trash', async () => {
|
|
102
|
+
mockClient.statByPath.mockResolvedValueOnce({
|
|
103
|
+
data: {
|
|
104
|
+
_id: 'folderintrash',
|
|
105
|
+
trashed: true
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
mockClient.createDirectoryByPath.mockResolvedValueOnce({
|
|
109
|
+
data: {
|
|
110
|
+
_id: 'createdfolderid'
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
const result = await ensureKonnectorFolder(mockClient, {
|
|
114
|
+
konnector,
|
|
115
|
+
account,
|
|
116
|
+
lang: 'fr'
|
|
117
|
+
})
|
|
118
|
+
expect(result).toStrictEqual({ _id: 'createdfolderid' })
|
|
119
|
+
expect(mockClient.statByPath).toHaveBeenCalledWith(
|
|
120
|
+
'/Administratif/konnectorName/testAccountName'
|
|
121
|
+
)
|
|
122
|
+
expect(mockClient.createDirectoryByPath).toHaveBeenCalledWith(
|
|
123
|
+
'/Administratif/konnectorName/testAccountName'
|
|
124
|
+
)
|
|
125
|
+
expect(mockClient.add).toHaveBeenCalledWith(konnector, {
|
|
126
|
+
saveFolder: {
|
|
127
|
+
type: 'io.cozy.files',
|
|
128
|
+
values: ['createdfolderid'],
|
|
129
|
+
verbs: ['GET', 'PATCH', 'POST']
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
expect(mockClient.addReferencesTo).toHaveBeenCalledWith(konnector, [
|
|
133
|
+
{ _id: 'createdfolderid' }
|
|
134
|
+
])
|
|
135
|
+
})
|
|
136
|
+
})
|
package/dist/types.js
CHANGED
|
@@ -312,6 +312,7 @@ var _dsl = require("./queries/dsl");
|
|
|
312
312
|
* @property {string} name - Name of the folder
|
|
313
313
|
* @property {object} metadata - Metadata of the folder
|
|
314
314
|
* @property {object} type - Type of the folder
|
|
315
|
+
* @property {string} path - Folder path
|
|
315
316
|
* @typedef {CozyClientDocument & FolderDocument} IOCozyFolder - An io.cozy.files document
|
|
316
317
|
*/
|
|
317
318
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-client",
|
|
3
|
-
"version": "41.
|
|
3
|
+
"version": "41.3.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": "
|
|
78
|
+
"gitHead": "b69af4c4c28456f2d24e3dffa19f7921cb489e4e"
|
|
79
79
|
}
|
|
@@ -6,3 +6,8 @@ export function getBoundT(lang: string): (label: string, opts?: {
|
|
|
6
6
|
country?: string;
|
|
7
7
|
smart_count?: number;
|
|
8
8
|
}) => string;
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} lang - fr, en, etc
|
|
11
|
+
* @returns {Function} - localization function
|
|
12
|
+
*/
|
|
13
|
+
export function getLocalizer(lang: string): Function;
|
package/types/models/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import * as instance from "./instance";
|
|
|
4
4
|
import * as applications from "./applications";
|
|
5
5
|
import * as file from "./file";
|
|
6
6
|
import * as folder from "./folder";
|
|
7
|
+
import * as konnectorFolder from "./konnectorFolder";
|
|
7
8
|
import * as note from "./note";
|
|
8
9
|
import * as account from "./account";
|
|
9
10
|
export const accounts: typeof account;
|
|
@@ -16,4 +17,4 @@ import * as sharing from "./sharing";
|
|
|
16
17
|
import * as dacc from "./dacc";
|
|
17
18
|
import * as paper from "./paper";
|
|
18
19
|
import * as user from "./user";
|
|
19
|
-
export { trigger, instance, applications, file, folder, note, account, permission, utils, contact, document, timeseries, sharing, dacc, paper, user };
|
|
20
|
+
export { trigger, instance, applications, file, folder, konnectorFolder, note, account, permission, utils, contact, document, timeseries, sharing, dacc, paper, user };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function ensureKonnectorFolder(client: CozyClient, { konnector, account, lang }: {
|
|
2
|
+
konnector: import('../types').IOCozyKonnector;
|
|
3
|
+
account: import('../types').IOCozyAccount;
|
|
4
|
+
lang: string;
|
|
5
|
+
}): Promise<import('../types').IOCozyFolder>;
|
|
6
|
+
export function createDirectoryByPath(client: CozyClient, path: string): Promise<import('../types').IOCozyFolder>;
|
|
7
|
+
export function statDirectoryByPath(client: CozyClient, path: string): Promise<import('../types').IOCozyFolder | null>;
|
|
8
|
+
export function buildFolderPath(konnector: import('../types').IOCozyKonnector, account: import('../types').IOCozyAccount, magicFolders?: {
|
|
9
|
+
[x: string]: string;
|
|
10
|
+
}): string;
|
|
11
|
+
export function buildFolderPermission(folder: import('../types').IOCozyFolder): any;
|
|
12
|
+
import CozyClient from "../CozyClient";
|
|
@@ -52,7 +52,7 @@ export type Document = {
|
|
|
52
52
|
_type: string;
|
|
53
53
|
type: string;
|
|
54
54
|
};
|
|
55
|
-
export type PermissionVerb = "POST" | "DELETE" | "PUT" | "GET" | "
|
|
55
|
+
export type PermissionVerb = "POST" | "DELETE" | "PUT" | "GET" | "PATCH" | "ALL";
|
|
56
56
|
export type PermissionItem = {
|
|
57
57
|
/**
|
|
58
58
|
* - ALL, GET, PUT, PATCH, DELETE, POST…
|