cozy-client 24.10.2 → 25.0.3
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 +47 -0
- package/dist/CozyClient.js +29 -8
- package/dist/CozyClient.spec.js +14 -1
- package/dist/RealTimeQueries.js +1 -0
- package/dist/RealTimeQueries.spec.jsx +8 -0
- package/dist/associations/HasMany.js +32 -10
- package/dist/associations/HasManyFiles.js +4 -6
- package/dist/associations/HasManyFiles.spec.js +0 -4
- package/dist/models/file.js +561 -11
- package/dist/models/file.spec.js +414 -48
- package/dist/types.js +1 -0
- package/package.json +3 -3
- package/types/CozyClient.d.ts +8 -1
- package/types/associations/HasMany.d.ts +12 -0
- package/types/models/file.d.ts +33 -0
- package/types/types.d.ts +4 -0
package/dist/models/file.spec.js
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as fileModel from './file'
|
|
2
2
|
import { Qualification } from './document'
|
|
3
|
+
import { QueryDefinition } from '../queries/dsl'
|
|
4
|
+
const CozyClient = require('cozy-client/dist/CozyClient').default
|
|
5
|
+
const CozyStackClient = require('cozy-stack-client').default
|
|
6
|
+
|
|
7
|
+
jest.mock('cozy-stack-client')
|
|
8
|
+
|
|
9
|
+
const cozyClient = new CozyClient({
|
|
10
|
+
stackClient: new CozyStackClient()
|
|
11
|
+
})
|
|
12
|
+
const destroySpy = jest.fn().mockName('destroy')
|
|
13
|
+
const getSpy = jest.fn().mockName('get')
|
|
14
|
+
const statByPathSpy = jest.fn().mockName('statByPath')
|
|
15
|
+
const updateFileMetadataSpy = jest.fn().mockName('updateFileMetadata')
|
|
16
|
+
const createFileSpy = jest.fn().mockName('createFileSpy')
|
|
17
|
+
const updateFileSpy = jest.fn().mockName('updateFileSpy')
|
|
18
|
+
|
|
19
|
+
beforeAll(() => {
|
|
20
|
+
cozyClient.stackClient.collection.mockReturnValue({
|
|
21
|
+
destroy: destroySpy,
|
|
22
|
+
get: getSpy,
|
|
23
|
+
statByPath: statByPathSpy,
|
|
24
|
+
updateFileMetadata: updateFileMetadataSpy,
|
|
25
|
+
createFile: createFileSpy,
|
|
26
|
+
updateFile: updateFileSpy
|
|
27
|
+
})
|
|
28
|
+
})
|
|
3
29
|
|
|
4
30
|
describe('File Model', () => {
|
|
5
31
|
it('should test if a file is a note or not', () => {
|
|
@@ -7,7 +33,7 @@ describe('File Model', () => {
|
|
|
7
33
|
type: 'file',
|
|
8
34
|
_id: 1
|
|
9
35
|
}
|
|
10
|
-
expect(
|
|
36
|
+
expect(fileModel.isNote(fileDocument)).toBe(false)
|
|
11
37
|
const note = {
|
|
12
38
|
type: 'file',
|
|
13
39
|
name: 'test.cozy-note',
|
|
@@ -18,7 +44,7 @@ describe('File Model', () => {
|
|
|
18
44
|
version: 1
|
|
19
45
|
}
|
|
20
46
|
}
|
|
21
|
-
expect(
|
|
47
|
+
expect(fileModel.isNote(note)).toBe(true)
|
|
22
48
|
const note2 = {
|
|
23
49
|
type: 'file',
|
|
24
50
|
name: 'test.cozy-note',
|
|
@@ -29,7 +55,7 @@ describe('File Model', () => {
|
|
|
29
55
|
version: 0
|
|
30
56
|
}
|
|
31
57
|
}
|
|
32
|
-
expect(
|
|
58
|
+
expect(fileModel.isNote(note2)).toBe(true)
|
|
33
59
|
})
|
|
34
60
|
|
|
35
61
|
it('should test if a file is a shortcut or not', () => {
|
|
@@ -43,8 +69,8 @@ describe('File Model', () => {
|
|
|
43
69
|
class: 'image'
|
|
44
70
|
}
|
|
45
71
|
|
|
46
|
-
expect(
|
|
47
|
-
expect(
|
|
72
|
+
expect(fileModel.isShortcut(shortcut)).toBe(true)
|
|
73
|
+
expect(fileModel.isShortcut(image)).toBe(false)
|
|
48
74
|
})
|
|
49
75
|
|
|
50
76
|
it('should test if a file is an onlyoffice file or not', () => {
|
|
@@ -88,13 +114,13 @@ describe('File Model', () => {
|
|
|
88
114
|
class: 'spreadsheet'
|
|
89
115
|
}
|
|
90
116
|
|
|
91
|
-
expect(
|
|
92
|
-
expect(
|
|
93
|
-
expect(
|
|
94
|
-
expect(
|
|
95
|
-
expect(
|
|
96
|
-
expect(
|
|
97
|
-
expect(
|
|
117
|
+
expect(fileModel.isOnlyOfficeFile(text)).toBe(true)
|
|
118
|
+
expect(fileModel.isOnlyOfficeFile(slide)).toBe(true)
|
|
119
|
+
expect(fileModel.isOnlyOfficeFile(spreadsheet)).toBe(true)
|
|
120
|
+
expect(fileModel.isOnlyOfficeFile(directory)).toBe(false)
|
|
121
|
+
expect(fileModel.isOnlyOfficeFile(shortcut)).toBe(false)
|
|
122
|
+
expect(fileModel.isOnlyOfficeFile(image)).toBe(false)
|
|
123
|
+
expect(fileModel.isOnlyOfficeFile(note)).toBe(false)
|
|
98
124
|
})
|
|
99
125
|
|
|
100
126
|
it('should test if a file should be opened by only office', () => {
|
|
@@ -108,9 +134,9 @@ describe('File Model', () => {
|
|
|
108
134
|
|
|
109
135
|
const mdFile = { ...docxFile, name: 'test.md' }
|
|
110
136
|
|
|
111
|
-
expect(
|
|
112
|
-
expect(
|
|
113
|
-
expect(
|
|
137
|
+
expect(fileModel.shouldBeOpenedByOnlyOffice(docxFile)).toBe(true)
|
|
138
|
+
expect(fileModel.shouldBeOpenedByOnlyOffice(txtFile)).toBe(false)
|
|
139
|
+
expect(fileModel.shouldBeOpenedByOnlyOffice(mdFile)).toBe(false)
|
|
114
140
|
})
|
|
115
141
|
|
|
116
142
|
it('should test if a file has a specific metadata', () => {
|
|
@@ -121,10 +147,10 @@ describe('File Model', () => {
|
|
|
121
147
|
}
|
|
122
148
|
|
|
123
149
|
expect(
|
|
124
|
-
|
|
150
|
+
fileModel.hasMetadataAttribute({ file: doc, attribute: 'carbonCopy' })
|
|
125
151
|
).toBe(true)
|
|
126
152
|
expect(
|
|
127
|
-
|
|
153
|
+
fileModel.hasMetadataAttribute({ file: doc, attribute: 'electronicSafe' })
|
|
128
154
|
).toBe(false)
|
|
129
155
|
})
|
|
130
156
|
|
|
@@ -134,25 +160,25 @@ describe('File Model', () => {
|
|
|
134
160
|
|
|
135
161
|
it('should allow both `id` and `_id`', () => {
|
|
136
162
|
const doc = { id, type }
|
|
137
|
-
expect(
|
|
163
|
+
expect(fileModel.normalize(doc)).toHaveProperty('_id', id)
|
|
138
164
|
|
|
139
165
|
const _doc = { _id: id, type }
|
|
140
|
-
expect(
|
|
166
|
+
expect(fileModel.normalize(_doc)).toHaveProperty('_id', id)
|
|
141
167
|
})
|
|
142
168
|
|
|
143
169
|
it('should enforce a `_type`', () => {
|
|
144
170
|
const doc = { id, type }
|
|
145
|
-
expect(
|
|
146
|
-
expect(
|
|
171
|
+
expect(fileModel.normalize(doc)).toHaveProperty('_type', 'io.cozy.files')
|
|
172
|
+
expect(fileModel.normalize(doc)).toHaveProperty('type', type)
|
|
147
173
|
|
|
148
174
|
const doc2 = { id, _type: 'io.cozy.something.else' }
|
|
149
|
-
expect(
|
|
175
|
+
expect(fileModel.normalize(doc2)).toHaveProperty(
|
|
150
176
|
'_type',
|
|
151
177
|
'io.cozy.something.else'
|
|
152
178
|
)
|
|
153
179
|
|
|
154
180
|
const doc3 = { id }
|
|
155
|
-
expect(
|
|
181
|
+
expect(fileModel.normalize(doc3)).toHaveProperty('_type', 'io.cozy.files')
|
|
156
182
|
})
|
|
157
183
|
})
|
|
158
184
|
|
|
@@ -179,21 +205,23 @@ describe('File Model', () => {
|
|
|
179
205
|
it(`splits ${stringify(joinName(expectation))} into ${stringify(
|
|
180
206
|
expectation
|
|
181
207
|
)}`, () => {
|
|
182
|
-
expect(
|
|
208
|
+
expect(fileModel.splitFilename(mkFile(expectation))).toEqual(
|
|
209
|
+
expectation
|
|
210
|
+
)
|
|
183
211
|
})
|
|
184
212
|
}
|
|
185
213
|
|
|
186
214
|
it('should throw an error if the file is not correct', () => {
|
|
187
|
-
expect(() =>
|
|
188
|
-
expect(() =>
|
|
189
|
-
expect(() =>
|
|
215
|
+
expect(() => fileModel.splitFilename({})).toThrow()
|
|
216
|
+
expect(() => fileModel.splitFilename({ name: null })).toThrow()
|
|
217
|
+
expect(() => fileModel.splitFilename({ name: '' })).not.toThrow()
|
|
190
218
|
})
|
|
191
219
|
it('should return only the folder name if its a folder', () => {
|
|
192
220
|
const dir = {
|
|
193
221
|
name: 'foo',
|
|
194
222
|
type: 'directory'
|
|
195
223
|
}
|
|
196
|
-
const result =
|
|
224
|
+
const result = fileModel.splitFilename(dir)
|
|
197
225
|
expect(result).toEqual({
|
|
198
226
|
filename: 'foo',
|
|
199
227
|
extension: ''
|
|
@@ -208,7 +236,7 @@ describe('File Model', () => {
|
|
|
208
236
|
it('should define the path if parent is provided', () => {
|
|
209
237
|
const doc = { id, name: 'myfile.ext' }
|
|
210
238
|
const parent = { path: '/my/path' }
|
|
211
|
-
expect(
|
|
239
|
+
expect(fileModel.ensureFilePath(doc, parent)).toHaveProperty(
|
|
212
240
|
'path',
|
|
213
241
|
'/my/path/myfile.ext'
|
|
214
242
|
)
|
|
@@ -216,13 +244,13 @@ describe('File Model', () => {
|
|
|
216
244
|
|
|
217
245
|
it('should not require a `parent` parameter if one is present', () => {
|
|
218
246
|
const doc = { id, type, path: '/test/file' }
|
|
219
|
-
expect(
|
|
247
|
+
expect(fileModel.ensureFilePath(doc)).toHaveProperty('path', '/test/file')
|
|
220
248
|
})
|
|
221
249
|
|
|
222
250
|
it('should not buid a `path` if the one is already present', () => {
|
|
223
251
|
const doc = { id, type, path: '/test/file' }
|
|
224
252
|
const parent = { id, type, path: '/test/parent' }
|
|
225
|
-
expect(
|
|
253
|
+
expect(fileModel.ensureFilePath(doc, parent)).toHaveProperty(
|
|
226
254
|
'path',
|
|
227
255
|
'/test/file'
|
|
228
256
|
)
|
|
@@ -256,47 +284,385 @@ describe('File Model', () => {
|
|
|
256
284
|
}
|
|
257
285
|
|
|
258
286
|
it('identifies a sharing shortcut', () => {
|
|
259
|
-
expect(
|
|
260
|
-
expect(
|
|
287
|
+
expect(fileModel.isSharingShortcut(sharingShortcutDocument)).toBe(true)
|
|
288
|
+
expect(fileModel.isSharingShortcut(nonSharingShortcutDocument)).toBe(
|
|
289
|
+
false
|
|
290
|
+
)
|
|
261
291
|
})
|
|
262
292
|
|
|
263
293
|
it('detects if a sharing shortcut is new', () => {
|
|
264
|
-
expect(
|
|
265
|
-
expect(
|
|
266
|
-
|
|
294
|
+
expect(fileModel.isSharingShortcutNew(newShortcutDocument)).toBe(true)
|
|
295
|
+
expect(fileModel.isSharingShortcutNew(nonSharingShortcutDocument)).toBe(
|
|
296
|
+
false
|
|
297
|
+
)
|
|
298
|
+
expect(fileModel.isSharingShortcutNew(sharingShortcutDocument)).toBe(
|
|
299
|
+
false
|
|
300
|
+
)
|
|
267
301
|
})
|
|
268
302
|
|
|
269
303
|
it('returns the sharring shortcut status', () => {
|
|
270
|
-
expect(
|
|
271
|
-
|
|
304
|
+
expect(fileModel.getSharingShortcutStatus(newShortcutDocument)).toBe(
|
|
305
|
+
'new'
|
|
306
|
+
)
|
|
307
|
+
expect(fileModel.getSharingShortcutStatus(sharingShortcutDocument)).toBe(
|
|
272
308
|
'seen'
|
|
273
309
|
)
|
|
274
310
|
expect(
|
|
275
|
-
|
|
311
|
+
fileModel.getSharingShortcutStatus(nonSharingShortcutDocument)
|
|
276
312
|
).toBeUndefined()
|
|
277
313
|
})
|
|
278
314
|
|
|
279
315
|
it('returns the sharring shortcut target mime type', () => {
|
|
280
|
-
expect(
|
|
316
|
+
expect(fileModel.getSharingShortcutTargetMime(newShortcutDocument)).toBe(
|
|
281
317
|
'target-mimetype-2'
|
|
282
318
|
)
|
|
283
|
-
expect(file.getSharingShortcutTargetMime(sharingShortcutDocument)).toBe(
|
|
284
|
-
'target-mimetype'
|
|
285
|
-
)
|
|
286
319
|
expect(
|
|
287
|
-
|
|
320
|
+
fileModel.getSharingShortcutTargetMime(sharingShortcutDocument)
|
|
321
|
+
).toBe('target-mimetype')
|
|
322
|
+
expect(
|
|
323
|
+
fileModel.getSharingShortcutTargetMime(nonSharingShortcutDocument)
|
|
288
324
|
).toBeUndefined()
|
|
289
325
|
})
|
|
290
326
|
|
|
291
327
|
it('returns the sharring shortcut target doctype', () => {
|
|
292
328
|
expect(
|
|
293
|
-
|
|
329
|
+
fileModel.getSharingShortcutTargetDoctype(sharingShortcutDocument)
|
|
294
330
|
).toBe('io.cozy.files')
|
|
295
331
|
expect(
|
|
296
|
-
|
|
332
|
+
fileModel.getSharingShortcutTargetMime(nonSharingShortcutDocument)
|
|
297
333
|
).toBeUndefined()
|
|
298
334
|
})
|
|
299
335
|
})
|
|
336
|
+
describe('getFullpath', () => {
|
|
337
|
+
let parentDirPath
|
|
338
|
+
|
|
339
|
+
beforeEach(() => {
|
|
340
|
+
getSpy.mockImplementation(() =>
|
|
341
|
+
Promise.resolve({
|
|
342
|
+
data: {
|
|
343
|
+
_id: '123',
|
|
344
|
+
_type: 'io.cozy.files',
|
|
345
|
+
path: parentDirPath
|
|
346
|
+
}
|
|
347
|
+
})
|
|
348
|
+
)
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
afterEach(() => {
|
|
352
|
+
jest.restoreAllMocks()
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
it('should return the full path of the file', async () => {
|
|
356
|
+
parentDirPath = '/GrandParent/Parent'
|
|
357
|
+
const result = await fileModel.getFullpath(
|
|
358
|
+
cozyClient,
|
|
359
|
+
'parent',
|
|
360
|
+
'mydoc.odt'
|
|
361
|
+
)
|
|
362
|
+
expect(cozyClient.stackClient.collection).toHaveBeenCalledWith(
|
|
363
|
+
'io.cozy.files'
|
|
364
|
+
)
|
|
365
|
+
expect(getSpy).toHaveBeenCalledWith(
|
|
366
|
+
'parent',
|
|
367
|
+
new QueryDefinition({ doctype: 'io.cozy.files', id: 'parent' })
|
|
368
|
+
)
|
|
369
|
+
expect(result).toEqual('/GrandParent/Parent/mydoc.odt')
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
it('should return the full path of the file if it is in root directory', async () => {
|
|
373
|
+
parentDirPath = '/'
|
|
374
|
+
const result = await fileModel.getFullpath(
|
|
375
|
+
cozyClient,
|
|
376
|
+
'parent',
|
|
377
|
+
'mydoc.odt'
|
|
378
|
+
)
|
|
379
|
+
expect(cozyClient.stackClient.collection).toHaveBeenCalledWith(
|
|
380
|
+
'io.cozy.files'
|
|
381
|
+
)
|
|
382
|
+
expect(getSpy).toHaveBeenCalledWith(
|
|
383
|
+
'parent',
|
|
384
|
+
new QueryDefinition({ doctype: 'io.cozy.files', id: 'parent' })
|
|
385
|
+
)
|
|
386
|
+
expect(result).toEqual('/mydoc.odt')
|
|
387
|
+
})
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
describe('move', () => {
|
|
391
|
+
const fileId = 'file-2295478c'
|
|
392
|
+
const folderId = 'dir-b1e1c256'
|
|
393
|
+
|
|
394
|
+
afterEach(() => {
|
|
395
|
+
jest.restoreAllMocks()
|
|
396
|
+
jest.clearAllMocks()
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
it('should move a file to another destination', async () => {
|
|
400
|
+
updateFileMetadataSpy.mockResolvedValue({
|
|
401
|
+
data: {
|
|
402
|
+
id: fileId,
|
|
403
|
+
dir_id: folderId,
|
|
404
|
+
_type: 'io.cozy.files'
|
|
405
|
+
}
|
|
406
|
+
})
|
|
407
|
+
const result = await fileModel.move(cozyClient, fileId, { folderId })
|
|
408
|
+
expect(updateFileMetadataSpy).toHaveBeenCalled()
|
|
409
|
+
expect(result).toEqual({
|
|
410
|
+
deleted: null,
|
|
411
|
+
moved: {
|
|
412
|
+
id: 'file-2295478c',
|
|
413
|
+
dir_id: 'dir-b1e1c256',
|
|
414
|
+
_type: 'io.cozy.files'
|
|
415
|
+
}
|
|
416
|
+
})
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
it('should fail with an error if there is a conflict and force is false', async () => {
|
|
420
|
+
updateFileMetadataSpy.mockRejectedValueOnce({
|
|
421
|
+
status: 409
|
|
422
|
+
})
|
|
423
|
+
try {
|
|
424
|
+
await fileModel.move(cozyClient, fileId, { folderId })
|
|
425
|
+
} catch (e) {
|
|
426
|
+
expect(e).toEqual({ status: 409 })
|
|
427
|
+
}
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
it('should overwrite the destination if there is a conflict and force is true', async () => {
|
|
431
|
+
const DELETED_FILE_ID = 'deleted-c097ffca'
|
|
432
|
+
|
|
433
|
+
updateFileMetadataSpy.mockRejectedValueOnce({
|
|
434
|
+
status: 409
|
|
435
|
+
})
|
|
436
|
+
updateFileMetadataSpy.mockResolvedValue({
|
|
437
|
+
data: {
|
|
438
|
+
id: fileId,
|
|
439
|
+
dir_id: folderId,
|
|
440
|
+
_type: 'io.cozy.files'
|
|
441
|
+
}
|
|
442
|
+
})
|
|
443
|
+
getSpy.mockResolvedValue({
|
|
444
|
+
data: {
|
|
445
|
+
id: fileId,
|
|
446
|
+
name: 'mydoc.odt',
|
|
447
|
+
_type: 'io.cozy.files'
|
|
448
|
+
}
|
|
449
|
+
})
|
|
450
|
+
statByPathSpy.mockResolvedValue({
|
|
451
|
+
data: {
|
|
452
|
+
id: DELETED_FILE_ID,
|
|
453
|
+
_type: 'io.cozy.files'
|
|
454
|
+
}
|
|
455
|
+
})
|
|
456
|
+
const result = await fileModel.move(
|
|
457
|
+
cozyClient,
|
|
458
|
+
fileId,
|
|
459
|
+
{ folderId },
|
|
460
|
+
true
|
|
461
|
+
)
|
|
462
|
+
expect(updateFileMetadataSpy).toHaveBeenCalled()
|
|
463
|
+
expect(destroySpy).toHaveBeenCalled()
|
|
464
|
+
expect(result).toEqual({
|
|
465
|
+
deleted: DELETED_FILE_ID,
|
|
466
|
+
moved: {
|
|
467
|
+
id: 'file-2295478c',
|
|
468
|
+
dir_id: 'dir-b1e1c256',
|
|
469
|
+
_type: 'io.cozy.files'
|
|
470
|
+
}
|
|
471
|
+
})
|
|
472
|
+
})
|
|
473
|
+
|
|
474
|
+
it('should use destination.path if it is given', async () => {
|
|
475
|
+
const DELETED_FILE_ID = 'deleted-c097ffca'
|
|
476
|
+
|
|
477
|
+
updateFileMetadataSpy.mockRejectedValueOnce({
|
|
478
|
+
status: 409
|
|
479
|
+
})
|
|
480
|
+
updateFileMetadataSpy.mockResolvedValue({
|
|
481
|
+
data: {
|
|
482
|
+
id: fileId,
|
|
483
|
+
dir_id: folderId,
|
|
484
|
+
_type: 'io.cozy.files'
|
|
485
|
+
}
|
|
486
|
+
})
|
|
487
|
+
statByPathSpy.mockResolvedValue({
|
|
488
|
+
data: {
|
|
489
|
+
id: DELETED_FILE_ID,
|
|
490
|
+
_type: 'io.cozy.files'
|
|
491
|
+
}
|
|
492
|
+
})
|
|
493
|
+
const result = await fileModel.move(
|
|
494
|
+
cozyClient,
|
|
495
|
+
fileId,
|
|
496
|
+
{ folderId, path: '/mydir/mydoc.odt' },
|
|
497
|
+
true
|
|
498
|
+
)
|
|
499
|
+
expect(getSpy).not.toHaveBeenCalled()
|
|
500
|
+
expect(updateFileMetadataSpy).toHaveBeenCalled()
|
|
501
|
+
expect(destroySpy).toHaveBeenCalled()
|
|
502
|
+
expect(result).toEqual({
|
|
503
|
+
deleted: DELETED_FILE_ID,
|
|
504
|
+
moved: {
|
|
505
|
+
id: 'file-2295478c',
|
|
506
|
+
dir_id: 'dir-b1e1c256',
|
|
507
|
+
_type: 'io.cozy.files'
|
|
508
|
+
}
|
|
509
|
+
})
|
|
510
|
+
})
|
|
511
|
+
})
|
|
512
|
+
|
|
513
|
+
describe('splitFilename', () => {
|
|
514
|
+
const name = ({ filename, extension }) => filename + extension
|
|
515
|
+
const file = expectation => ({ type: 'file', name: name(expectation) })
|
|
516
|
+
const { stringify } = JSON
|
|
517
|
+
|
|
518
|
+
const scenarios = [
|
|
519
|
+
{ filename: 'file', extension: '.ext' },
|
|
520
|
+
{ filename: 'file', extension: '' },
|
|
521
|
+
{ filename: 'file.html', extension: '.ejs' },
|
|
522
|
+
{ filename: 'file', extension: '.' },
|
|
523
|
+
{ filename: 'file.', extension: '.' },
|
|
524
|
+
{ filename: 'file.', extension: '.ext' },
|
|
525
|
+
{ filename: '.file', extension: '' },
|
|
526
|
+
{ filename: '.file', extension: '.ext' }
|
|
527
|
+
]
|
|
528
|
+
|
|
529
|
+
for (const expectation of scenarios) {
|
|
530
|
+
it(`splits ${stringify(name(expectation))} into ${stringify(
|
|
531
|
+
expectation
|
|
532
|
+
)}`, () => {
|
|
533
|
+
expect(fileModel.splitFilename(file(expectation))).toEqual(expectation)
|
|
534
|
+
})
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
it('should throw an error if the file is not correct', () => {
|
|
538
|
+
const file = {}
|
|
539
|
+
|
|
540
|
+
expect(() => fileModel.splitFilename(file)).toThrow()
|
|
541
|
+
})
|
|
542
|
+
it('should return only the folder name if its a folder', () => {
|
|
543
|
+
const file = {
|
|
544
|
+
name: 'foo',
|
|
545
|
+
type: 'directory'
|
|
546
|
+
}
|
|
547
|
+
const result = fileModel.splitFilename(file)
|
|
548
|
+
expect(result).toEqual({
|
|
549
|
+
filename: 'foo',
|
|
550
|
+
extension: ''
|
|
551
|
+
})
|
|
552
|
+
})
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
describe('generateNewFileNameOnConflict', () => {
|
|
556
|
+
it('should generate the right file name with _X', () => {
|
|
557
|
+
const filename1 = fileModel.generateNewFileNameOnConflict('test')
|
|
558
|
+
expect(filename1).toEqual('test_1')
|
|
559
|
+
const filename2 = fileModel.generateNewFileNameOnConflict('test_1')
|
|
560
|
+
expect(filename2).toEqual('test_2')
|
|
561
|
+
const filename3 = fileModel.generateNewFileNameOnConflict('test_1_1_test')
|
|
562
|
+
expect(filename3).toEqual('test_1_1_test_1')
|
|
563
|
+
const filename4 = fileModel.generateNewFileNameOnConflict('test_')
|
|
564
|
+
expect(filename4).toEqual('test__1')
|
|
565
|
+
})
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
describe('generateFileNameForRevision', () => {
|
|
569
|
+
it('should generate the right file name for a revision', () => {
|
|
570
|
+
const MOCKED_DATE = '2018-01-01T12:00:00.210Z'
|
|
571
|
+
const expectedFilename = 'test_01 January - 12h00.pdf'
|
|
572
|
+
const date = new Date(MOCKED_DATE)
|
|
573
|
+
const result = fileModel.generateFileNameForRevision(
|
|
574
|
+
{ name: 'test.pdf', type: 'file' },
|
|
575
|
+
{ updated_at: date },
|
|
576
|
+
() => '01 January - 12h00'
|
|
577
|
+
)
|
|
578
|
+
expect(result).toEqual(expectedFilename)
|
|
579
|
+
})
|
|
580
|
+
})
|
|
581
|
+
|
|
582
|
+
describe('uploadFileWithConflictStrategy', () => {
|
|
583
|
+
beforeEach(() => {
|
|
584
|
+
getSpy.mockImplementation(() =>
|
|
585
|
+
Promise.resolve({
|
|
586
|
+
data: {
|
|
587
|
+
path: '/GrandParent/Parent',
|
|
588
|
+
_type: 'io.cozy.files',
|
|
589
|
+
_id: '123'
|
|
590
|
+
}
|
|
591
|
+
})
|
|
592
|
+
)
|
|
593
|
+
})
|
|
594
|
+
afterEach(() => {
|
|
595
|
+
jest.restoreAllMocks()
|
|
596
|
+
jest.clearAllMocks()
|
|
597
|
+
})
|
|
598
|
+
|
|
599
|
+
it('should call the upload method if no conflict', async () => {
|
|
600
|
+
statByPathSpy.mockRejectedValueOnce(new Error('Not Found'))
|
|
601
|
+
createFileSpy.mockResolvedValue({
|
|
602
|
+
data: {
|
|
603
|
+
id: 'jj',
|
|
604
|
+
dir_id: 'jj',
|
|
605
|
+
_type: 'io.cozy.files'
|
|
606
|
+
}
|
|
607
|
+
})
|
|
608
|
+
try {
|
|
609
|
+
await fileModel.uploadFileWithConflictStrategy(
|
|
610
|
+
cozyClient,
|
|
611
|
+
{},
|
|
612
|
+
{ name: 'test', dirId: 'a', conflictStrategy: 'erase' }
|
|
613
|
+
)
|
|
614
|
+
} catch (e) {
|
|
615
|
+
expect(createFileSpy).toHaveBeenCalled()
|
|
616
|
+
}
|
|
617
|
+
})
|
|
618
|
+
|
|
619
|
+
it('should rename the file if there is a conflict', async () => {
|
|
620
|
+
const dirId = 'toto'
|
|
621
|
+
//first call we return an existing file => conflict
|
|
622
|
+
//second call, we reject as not found
|
|
623
|
+
statByPathSpy
|
|
624
|
+
.mockReturnValueOnce({
|
|
625
|
+
data: {
|
|
626
|
+
_id: 'file_id',
|
|
627
|
+
_type: 'io.cozy.files'
|
|
628
|
+
}
|
|
629
|
+
})
|
|
630
|
+
.mockRejectedValueOnce(new Error('Not Found'))
|
|
631
|
+
|
|
632
|
+
const opts = {
|
|
633
|
+
name: 'filename',
|
|
634
|
+
dirId,
|
|
635
|
+
conflictStrategy: 'rename',
|
|
636
|
+
contentType: 'image/jpeg'
|
|
637
|
+
}
|
|
638
|
+
await fileModel.uploadFileWithConflictStrategy(cozyClient, '', opts)
|
|
639
|
+
expect(createFileSpy).toHaveBeenCalledWith('', {
|
|
640
|
+
...opts,
|
|
641
|
+
name: 'filename_1'
|
|
642
|
+
})
|
|
643
|
+
})
|
|
644
|
+
|
|
645
|
+
it('should erase the file if there is a conflict', async () => {
|
|
646
|
+
const dirId = 'toto'
|
|
647
|
+
//first call we return an existing file => conflict
|
|
648
|
+
//second call, we reject as not found
|
|
649
|
+
statByPathSpy.mockReturnValueOnce({
|
|
650
|
+
data: {
|
|
651
|
+
id: 'file_id'
|
|
652
|
+
}
|
|
653
|
+
})
|
|
654
|
+
const opts = {
|
|
655
|
+
name: 'filename',
|
|
656
|
+
dirId,
|
|
657
|
+
conflictStrategy: 'erase'
|
|
658
|
+
}
|
|
659
|
+
await fileModel.uploadFileWithConflictStrategy(cozyClient, '', opts)
|
|
660
|
+
expect(updateFileSpy).toHaveBeenCalledWith('', {
|
|
661
|
+
...opts,
|
|
662
|
+
fileId: 'file_id'
|
|
663
|
+
})
|
|
664
|
+
})
|
|
665
|
+
})
|
|
300
666
|
})
|
|
301
667
|
|
|
302
668
|
describe('File qualification', () => {
|
|
@@ -322,7 +688,7 @@ describe('File qualification', () => {
|
|
|
322
688
|
}
|
|
323
689
|
}
|
|
324
690
|
const qualification = Qualification.getByLabel('health_invoice')
|
|
325
|
-
const updFile = await
|
|
691
|
+
const updFile = await fileModel.saveFileQualification(
|
|
326
692
|
mockedClient,
|
|
327
693
|
fileDoc,
|
|
328
694
|
qualification
|
package/dist/types.js
CHANGED
|
@@ -120,6 +120,7 @@ var _dsl = require("./queries/dsl");
|
|
|
120
120
|
/**
|
|
121
121
|
* @typedef {object} CozyClientDocument - A document
|
|
122
122
|
* @property {string} [_id] - Id of the document
|
|
123
|
+
* @property {string} [id] - Id of the document
|
|
123
124
|
* @property {string} [_type] - Type of the document
|
|
124
125
|
* @property {string} [_rev] - Current revision of the document
|
|
125
126
|
* @property {boolean} [_deleted] - When the document has been deleted
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-client",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "25.0.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.node.js",
|
|
6
6
|
"browser": "dist/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"cozy-device-helper": "^1.12.0",
|
|
22
22
|
"cozy-flags": "2.7.1",
|
|
23
23
|
"cozy-logger": "^1.6.0",
|
|
24
|
-
"cozy-stack-client": "^
|
|
24
|
+
"cozy-stack-client": "^25.0.2",
|
|
25
25
|
"json-stable-stringify": "^1.0.1",
|
|
26
26
|
"lodash": "^4.17.13",
|
|
27
27
|
"microee": "^0.0.6",
|
|
@@ -68,5 +68,5 @@
|
|
|
68
68
|
"react-native-inappbrowser-reborn": "^3.5.1"
|
|
69
69
|
},
|
|
70
70
|
"sideEffects": false,
|
|
71
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "537b851a02c87e78bfa84957300f28bbb6db388d"
|
|
72
72
|
}
|
package/types/CozyClient.d.ts
CHANGED
|
@@ -308,7 +308,14 @@ declare class CozyClient {
|
|
|
308
308
|
*/
|
|
309
309
|
create(type: string, doc: object, references?: ReferenceMap, options?: object): Promise<any>;
|
|
310
310
|
validate(document: any): Promise<{}>;
|
|
311
|
-
|
|
311
|
+
/**
|
|
312
|
+
* Create or update a document on the server
|
|
313
|
+
*
|
|
314
|
+
* @param {object} doc - Document to save
|
|
315
|
+
* @param {object} mutationOptions - Mutation options
|
|
316
|
+
* @returns {Promise}
|
|
317
|
+
*/
|
|
318
|
+
save(doc: object, mutationOptions?: object): Promise<any>;
|
|
312
319
|
/**
|
|
313
320
|
* Saves multiple documents in one batch
|
|
314
321
|
* - Can only be called with documents from the same doctype
|
|
@@ -101,6 +101,12 @@ declare class HasMany extends Association {
|
|
|
101
101
|
* @returns {CozyClientDocument} The saved target document
|
|
102
102
|
*/
|
|
103
103
|
remove(docsArg: CozyClientDocument[]): CozyClientDocument;
|
|
104
|
+
/**
|
|
105
|
+
* Update target document with relationships
|
|
106
|
+
*
|
|
107
|
+
* @param {string[]} idsArg - The ids to add as a relationship
|
|
108
|
+
*/
|
|
109
|
+
addTargetRelationships(idsArg: string[]): void;
|
|
104
110
|
/**
|
|
105
111
|
* Add a referenced document by id. You need to call save()
|
|
106
112
|
* in order to synchronize your document with the store.
|
|
@@ -111,6 +117,12 @@ declare class HasMany extends Association {
|
|
|
111
117
|
*
|
|
112
118
|
*/
|
|
113
119
|
addById(idsArg: any): any;
|
|
120
|
+
/**
|
|
121
|
+
* Remove relationships from target document
|
|
122
|
+
*
|
|
123
|
+
* @param {string[]} idsArg - The ids to remove from the target relationships
|
|
124
|
+
*/
|
|
125
|
+
removeTargetRelationships(idsArg: string[]): void;
|
|
114
126
|
removeById(idsArg: any): any;
|
|
115
127
|
updateMetaCount(): void;
|
|
116
128
|
getRelationship(): any;
|