cozy-pouch-link 48.25.0 → 49.0.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.
Files changed (41) hide show
  1. package/dist/CozyPouchLink.js +593 -237
  2. package/dist/CozyPouchLink.spec.js +67 -42
  3. package/dist/PouchManager.js +317 -254
  4. package/dist/PouchManager.spec.js +91 -58
  5. package/dist/helpers.js +79 -0
  6. package/dist/helpers.spec.js +85 -1
  7. package/dist/jsonapi.js +54 -7
  8. package/dist/jsonapi.spec.js +57 -14
  9. package/dist/localStorage.js +646 -207
  10. package/dist/localStorage.spec.js +48 -0
  11. package/dist/mango.js +72 -20
  12. package/dist/mango.spec.js +1 -1
  13. package/dist/migrations/adapter.js +1 -1
  14. package/dist/platformWeb.js +120 -0
  15. package/dist/remote.js +39 -5
  16. package/dist/remote.spec.js +214 -0
  17. package/dist/replicateOnce.js +337 -0
  18. package/dist/startReplication.js +70 -45
  19. package/dist/startReplication.spec.js +374 -39
  20. package/dist/types.js +80 -0
  21. package/dist/utils.js +11 -2
  22. package/package.json +9 -5
  23. package/types/AccessToken.d.ts +16 -0
  24. package/types/CozyPouchLink.d.ts +228 -0
  25. package/types/PouchManager.d.ts +86 -0
  26. package/types/__tests__/fixtures.d.ts +48 -0
  27. package/types/__tests__/mocks.d.ts +4 -0
  28. package/types/helpers.d.ts +17 -0
  29. package/types/index.d.ts +1 -0
  30. package/types/jsonapi.d.ts +19 -0
  31. package/types/localStorage.d.ts +124 -0
  32. package/types/logger.d.ts +2 -0
  33. package/types/loop.d.ts +60 -0
  34. package/types/mango.d.ts +3 -0
  35. package/types/migrations/adapter.d.ts +18 -0
  36. package/types/platformWeb.d.ts +17 -0
  37. package/types/remote.d.ts +6 -0
  38. package/types/replicateOnce.d.ts +29 -0
  39. package/types/startReplication.d.ts +12 -0
  40. package/types/types.d.ts +104 -0
  41. package/types/utils.d.ts +3 -0
@@ -19,10 +19,17 @@ jest.mock('./remote', () => ({
19
19
 
20
20
  import * as rep from './startReplication'
21
21
  import PouchDB from 'pouchdb-browser'
22
- import * as ls from './localStorage'
22
+ import {
23
+ LOCALSTORAGE_SYNCED_KEY,
24
+ LOCALSTORAGE_WARMUPEDQUERIES_KEY,
25
+ PouchLocalStorage
26
+ } from './localStorage'
27
+ import { platformWeb } from './platformWeb'
23
28
 
24
29
  import { fetchRemoteLastSequence, fetchRemoteInstance } from './remote'
25
30
 
31
+ const ls = new PouchLocalStorage(platformWeb.storage)
32
+
26
33
  const sleep = delay => {
27
34
  return new Promise(resolve => {
28
35
  setTimeout(resolve, delay)
@@ -63,7 +70,7 @@ describe('PouchManager', () => {
63
70
  getReplicationURL,
64
71
  onSync = jest.fn()
65
72
 
66
- beforeEach(() => {
73
+ beforeEach(async () => {
67
74
  getReplicationURL = () => 'http://replicationURL.local'
68
75
  managerOptions = {
69
76
  replicationDelay: 16,
@@ -72,6 +79,7 @@ describe('PouchManager', () => {
72
79
  prefix: 'cozy.tools'
73
80
  }
74
81
  manager = new PouchManager(['io.cozy.todos'], managerOptions)
82
+ await manager.init()
75
83
  const pouch = manager.getPouch('io.cozy.todos')
76
84
  const replication = mocks.pouchReplication({
77
85
  direction: 'pull',
@@ -133,6 +141,7 @@ describe('PouchManager', () => {
133
141
  'io.cozy.readonly': { strategy: 'fromRemote' }
134
142
  }
135
143
  })
144
+ await manager.init()
136
145
  const normalPouch = manager.getPouch('io.cozy.todos')
137
146
  const readOnlyPouch = manager.getPouch('io.cozy.readonly')
138
147
  readOnlyPouch.replicate = {}
@@ -155,6 +164,7 @@ describe('PouchManager', () => {
155
164
  }
156
165
  }
157
166
  )
167
+ await manager.init()
158
168
  const normalPouch = manager.getPouch('io.cozy.todos')
159
169
  const readOnlyPouch = manager.getPouch('io.cozy.readonly')
160
170
  readOnlyPouch.replicate = {}
@@ -162,6 +172,9 @@ describe('PouchManager', () => {
162
172
  const writeOnlyPouch = manager.getPouch('io.cozy.writeonly')
163
173
  writeOnlyPouch.replicate = {}
164
174
  writeOnlyPouch.replicate.to = jest.fn()
175
+ manager.updateSyncInfo('io.cozy.todos')
176
+ manager.updateSyncInfo('io.cozy.readonly')
177
+ manager.updateSyncInfo('io.cozy.writeonly')
165
178
  manager.startReplicationLoop()
166
179
  await sleep(1000)
167
180
  expect(readOnlyPouch.replicate.from).toHaveBeenCalled()
@@ -214,6 +227,7 @@ describe('PouchManager', () => {
214
227
  it('should call on sync with doctype updates', async () => {
215
228
  jest.spyOn(manager, 'replicateOnce')
216
229
  onSync.mockReset()
230
+ manager.updateSyncInfo('io.cozy.todos')
217
231
  await manager.replicateOnce()
218
232
  expect(onSync).toHaveBeenCalledWith({
219
233
  'io.cozy.todos': [
@@ -231,14 +245,16 @@ describe('PouchManager', () => {
231
245
 
232
246
  it('should add pouch plugin', async () => {
233
247
  const options = { ...managerOptions, pouch: { plugins: ['myPlugin'] } }
234
- new PouchManager(['io.cozy.todos'], options)
248
+ const manager = new PouchManager(['io.cozy.todos'], options)
249
+ await manager.init()
235
250
  expect(PouchDB.plugin).toHaveBeenCalledTimes(1)
236
251
  })
237
252
 
238
253
  it('should instanciate pouch with options', async () => {
239
254
  const pouchOptions = { adapter: 'cordova-sqlite', location: 'default' }
240
255
  const options = { ...managerOptions, pouch: { options: pouchOptions } }
241
- new PouchManager(['io.cozy.todos'], options)
256
+ const manager = new PouchManager(['io.cozy.todos'], options)
257
+ await manager.init()
242
258
  expect(PouchDB).toHaveBeenCalledWith(
243
259
  'cozy.tools_io.cozy.todos',
244
260
  pouchOptions
@@ -246,33 +262,36 @@ describe('PouchManager', () => {
246
262
  })
247
263
 
248
264
  describe('getPersistedSyncedDoctypes', () => {
249
- it('should return an empty array if local storage is empty', () => {
250
- expect(ls.getPersistedSyncedDoctypes()).toEqual({})
265
+ it('should return an empty array if local storage is empty', async () => {
266
+ expect(await ls.getPersistedSyncedDoctypes()).toEqual({})
251
267
  })
252
268
 
253
- it('should return an empty array if local storage contains something that is not an array', () => {
254
- localStorage.__STORE__[ls.LOCALSTORAGE_SYNCED_KEY] = 'true'
255
- expect(ls.getPersistedSyncedDoctypes()).toEqual({})
269
+ it('should return an empty array if local storage contains something that is not an array', async () => {
270
+ localStorage.__STORE__[LOCALSTORAGE_SYNCED_KEY] = 'true'
271
+ expect(await ls.getPersistedSyncedDoctypes()).toEqual({})
256
272
  })
257
273
 
258
- it('should return the list of doctypes if local storage contains one', () => {
274
+ it('should return the list of doctypes if local storage contains one', async () => {
259
275
  const persistedSyncedDoctypes = {
260
276
  'io.cozy.todos': { date: '2021-08-11T13:48:06.085Z' }
261
277
  }
262
- localStorage.__STORE__[ls.LOCALSTORAGE_SYNCED_KEY] = JSON.stringify(
278
+ localStorage.__STORE__[LOCALSTORAGE_SYNCED_KEY] = JSON.stringify(
279
+ persistedSyncedDoctypes
280
+ )
281
+ expect(await ls.getPersistedSyncedDoctypes()).toEqual(
263
282
  persistedSyncedDoctypes
264
283
  )
265
- expect(ls.getPersistedSyncedDoctypes()).toEqual(persistedSyncedDoctypes)
266
284
  })
267
285
  })
268
286
 
269
287
  describe('persistSyncedDoctypes', () => {
270
- it('should put the list of synced doctypes in localStorage', () => {
288
+ it('should put the list of synced doctypes in localStorage', async () => {
271
289
  const manager = new PouchManager(['io.cozy.todos'], managerOptions)
290
+ await manager.init()
272
291
  manager.syncedDoctypes = ['io.cozy.todos']
273
292
  ls.persistSyncedDoctypes(manager.syncedDoctypes)
274
293
 
275
- expect(localStorage.__STORE__[ls.LOCALSTORAGE_SYNCED_KEY]).toEqual(
294
+ expect(localStorage.__STORE__[LOCALSTORAGE_SYNCED_KEY]).toEqual(
276
295
  JSON.stringify(manager.syncedDoctypes)
277
296
  )
278
297
  })
@@ -287,126 +306,139 @@ describe('PouchManager', () => {
287
306
  MockDate.reset()
288
307
  })
289
308
 
290
- it('should add the doctype to synced doctypes', () => {
309
+ it('should add the doctype to synced doctypes', async () => {
291
310
  const manager = new PouchManager(['io.cozy.todos'], managerOptions)
292
- manager.updateSyncInfo('io.cozy.todos')
311
+ await manager.init()
312
+ await manager.updateSyncInfo('io.cozy.todos')
293
313
  expect(Object.keys(manager.syncedDoctypes)).toEqual(['io.cozy.todos'])
294
314
  })
295
315
 
296
- it('should persist the new synced doctypes list', () => {
316
+ it('should persist the new synced doctypes list', async () => {
297
317
  const manager = new PouchManager(['io.cozy.todos'], managerOptions)
318
+ await manager.init()
298
319
 
299
- manager.updateSyncInfo('io.cozy.todos')
300
- expect(localStorage.__STORE__[ls.LOCALSTORAGE_SYNCED_KEY]).toEqual(
320
+ await manager.updateSyncInfo('io.cozy.todos')
321
+ expect(localStorage.__STORE__[LOCALSTORAGE_SYNCED_KEY]).toEqual(
301
322
  JSON.stringify({
302
- 'io.cozy.todos': { date: '2021-08-01T00:00:00.000Z' }
323
+ 'io.cozy.todos': {
324
+ date: '2021-08-01T00:00:00.000Z',
325
+ status: 'synced'
326
+ }
303
327
  })
304
328
  )
305
329
  })
306
330
  })
307
331
 
308
- describe('isSynced', () => {
332
+ describe('getSyncStatus', () => {
309
333
  let manager
310
334
 
311
- beforeEach(() => {
335
+ beforeEach(async () => {
312
336
  manager = new PouchManager(['io.cozy.todos'], managerOptions)
337
+ await manager.init()
313
338
  })
314
339
 
315
- it('should return true if the doctype is synced', () => {
316
- manager.updateSyncInfo('io.cozy.todos')
317
- expect(manager.isSynced('io.cozy.todos')).toBe(true)
340
+ it(`should return 'synced' if the doctype is synced`, async () => {
341
+ await manager.updateSyncInfo('io.cozy.todos')
342
+ expect(manager.getSyncStatus('io.cozy.todos')).toBe('synced')
318
343
  })
319
344
 
320
- it('should return false if the doctype is not synced', () => {
321
- expect(manager.isSynced('io.cozy.todos')).toBe(false)
345
+ it(`should return 'not_synced' if the doctype is not synced`, () => {
346
+ expect(manager.getSyncStatus('io.cozy.todos')).toBe('not_synced')
347
+ })
348
+
349
+ it('should return status if updateSyncInfo was called with custom status', async () => {
350
+ await manager.updateSyncInfo('io.cozy.todos', 'not_complete')
351
+ expect(manager.getSyncStatus('io.cozy.todos')).toBe('not_complete')
322
352
  })
323
353
  })
324
354
 
325
355
  describe('destroySyncedDoctypes', () => {
326
- it('should destroy the local storage item', () => {
327
- ls.destroySyncedDoctypes()
356
+ it('should destroy the local storage item', async () => {
357
+ await ls.destroySyncedDoctypes()
328
358
 
329
359
  expect(localStorage.removeItem).toHaveBeenLastCalledWith(
330
- ls.LOCALSTORAGE_SYNCED_KEY
360
+ LOCALSTORAGE_SYNCED_KEY
331
361
  )
332
362
  })
333
- it('should reset syncedDoctypes', () => {
363
+ it('should reset syncedDoctypes', async () => {
334
364
  manager.syncedDoctypes = {
335
365
  'io.cozy.todos': { date: '2021-08-11T13:48:06.085Z' }
336
366
  }
337
- manager.clearSyncedDoctypes()
367
+ await manager.clearSyncedDoctypes()
338
368
  expect(manager.syncedDoctypes).toEqual({})
339
369
  })
340
370
  })
341
371
 
342
372
  describe('getPersistedWarmedUpQueriess', () => {
343
- it('should return an empty object if local storage is empty', () => {
344
- expect(ls.getPersistedWarmedUpQueries()).toEqual({})
373
+ it('should return an empty object if local storage is empty', async () => {
374
+ expect(await ls.getPersistedWarmedUpQueries()).toEqual({})
345
375
  })
346
376
 
347
- it('should return the list of queries if local storage contains ones', () => {
377
+ it('should return the list of queries if local storage contains ones', async () => {
348
378
  const persistedQueries = [query().options.as]
349
- localStorage.__STORE__[
350
- ls.LOCALSTORAGE_WARMUPEDQUERIES_KEY
351
- ] = JSON.stringify(persistedQueries)
352
- expect(ls.getPersistedWarmedUpQueries()).toEqual(persistedQueries)
379
+ localStorage.__STORE__[LOCALSTORAGE_WARMUPEDQUERIES_KEY] = JSON.stringify(
380
+ persistedQueries
381
+ )
382
+ expect(await ls.getPersistedWarmedUpQueries()).toEqual(persistedQueries)
353
383
  })
354
384
  })
355
385
 
356
386
  describe('persistWarmedUpQueries', () => {
357
- it('should put the list of warmedUpQueries in localStorage', () => {
387
+ it('should put the list of warmedUpQueries in localStorage', async () => {
358
388
  const manager = new PouchManager(['io.cozy.todos'], managerOptions)
389
+ await manager.init()
359
390
  manager.warmedUpQueries = { 'io.cozy.todos': ['query1', 'query2'] }
360
- ls.persistWarmedUpQueries(manager.warmedUpQueries)
391
+ await ls.persistWarmedUpQueries(manager.warmedUpQueries)
361
392
 
362
- expect(
363
- localStorage.__STORE__[ls.LOCALSTORAGE_WARMUPEDQUERIES_KEY]
364
- ).toEqual(JSON.stringify(manager.warmedUpQueries))
393
+ expect(localStorage.__STORE__[LOCALSTORAGE_WARMUPEDQUERIES_KEY]).toEqual(
394
+ JSON.stringify(manager.warmedUpQueries)
395
+ )
365
396
  })
366
397
  })
367
398
 
368
399
  describe('areQueriesWarmedUp', () => {
369
400
  let manager
370
401
 
371
- beforeEach(() => {
402
+ beforeEach(async () => {
372
403
  manager = new PouchManager(['io.cozy.todos'], managerOptions)
404
+ await manager.init()
373
405
  })
374
406
 
375
- it('should return true if all the queries are warmuped', () => {
407
+ it('should return true if all the queries are warmuped', async () => {
376
408
  manager.warmedUpQueries = {
377
409
  'io.cozy.todos': [query1().options.as, query2().options.as]
378
410
  }
379
- ls.persistWarmedUpQueries(manager.warmedUpQueries)
411
+ await ls.persistWarmedUpQueries(manager.warmedUpQueries)
380
412
 
381
413
  expect(
382
- manager.areQueriesWarmedUp('io.cozy.todos', [query1(), query2()])
414
+ await manager.areQueriesWarmedUp('io.cozy.todos', [query1(), query2()])
383
415
  ).toBe(true)
384
416
  })
385
417
 
386
- it('should return false if at least one query is not warmuped', () => {
418
+ it('should return false if at least one query is not warmuped', async () => {
387
419
  manager.warmedUpQueries = {
388
420
  'io.cozy.todos': [query2().options.as]
389
421
  }
390
- ls.persistWarmedUpQueries()
422
+ await ls.persistWarmedUpQueries()
391
423
 
392
424
  expect(
393
- manager.areQueriesWarmedUp('io.cozy.todos', [query1(), query2()])
425
+ await manager.areQueriesWarmedUp('io.cozy.todos', [query1(), query2()])
394
426
  ).toBe(false)
395
427
  })
396
428
 
397
- it('should return false if the queries are not been done', () => {
429
+ it('should return false if the queries are not been done', async () => {
398
430
  expect(
399
- manager.areQueriesWarmedUp('io.cozy.todos', [query1(), query2()])
431
+ await manager.areQueriesWarmedUp('io.cozy.todos', [query1(), query2()])
400
432
  ).toBe(false)
401
433
  })
402
434
  })
403
435
 
404
436
  describe('clearWarmedupQueries', () => {
405
- it('should clear the local storage item', () => {
437
+ it('should clear the local storage item', async () => {
406
438
  manager.clearWarmedUpQueries()
407
439
 
408
440
  expect(localStorage.removeItem).toHaveBeenLastCalledWith(
409
- ls.LOCALSTORAGE_WARMUPEDQUERIES_KEY
441
+ LOCALSTORAGE_WARMUPEDQUERIES_KEY
410
442
  )
411
443
  })
412
444
  it('should reset warmedupQueries', () => {
@@ -490,7 +522,7 @@ describe('PouchManager', () => {
490
522
  describe('warmupQueries', () => {
491
523
  let manager
492
524
  const executeMock = jest.fn()
493
- beforeEach(() => {
525
+ beforeEach(async () => {
494
526
  let newManagerOptions = {
495
527
  ...managerOptions,
496
528
  executeQuery: executeMock,
@@ -502,6 +534,7 @@ describe('PouchManager', () => {
502
534
  }
503
535
  }
504
536
  manager = new PouchManager(['io.cozy.todos'], newManagerOptions)
537
+ await manager.init()
505
538
  })
506
539
 
507
540
  it('should executes warmeupQueries on the first replicationLoop only', async () => {
@@ -524,7 +557,7 @@ describe('PouchManager', () => {
524
557
  .definition()
525
558
  .toDefinition()
526
559
  )
527
- expect(ls.getPersistedWarmedUpQueries()).toEqual({
560
+ expect(await ls.getPersistedWarmedUpQueries()).toEqual({
528
561
  'io.cozy.todos': ['query1', 'query2']
529
562
  })
530
563
  //Simulation of a loop. Let's replicate again
@@ -541,7 +574,7 @@ describe('PouchManager', () => {
541
574
 
542
575
  await manager.replicateOnce()
543
576
  await sleep(10)
544
- expect(ls.getPersistedWarmedUpQueries()).toEqual({})
577
+ expect(await ls.getPersistedWarmedUpQueries()).toEqual({})
545
578
  expect(manager.warmedUpQueries['io.cozy.todos']).toBeUndefined()
546
579
  })
547
580
  })
package/dist/helpers.js CHANGED
@@ -15,8 +15,18 @@ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/
15
15
 
16
16
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
17
17
 
18
+ var _merge = _interopRequireDefault(require("lodash/merge"));
19
+
18
20
  var _startsWith = _interopRequireDefault(require("lodash/startsWith"));
19
21
 
22
+ var _logger = _interopRequireDefault(require("./logger"));
23
+
24
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
25
+
26
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
27
+
28
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
29
+
20
30
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
21
31
 
22
32
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
@@ -186,5 +196,74 @@ helpers.insertBulkDocs = /*#__PURE__*/function () {
186
196
  };
187
197
  }();
188
198
 
199
+ helpers.normalizeFindSelector = function (_ref5) {
200
+ var selector = _ref5.selector,
201
+ sort = _ref5.sort,
202
+ indexedFields = _ref5.indexedFields,
203
+ partialFilter = _ref5.partialFilter;
204
+ var findSelector = selector || {};
205
+
206
+ if (indexedFields) {
207
+ var _iterator = _createForOfIteratorHelper(indexedFields),
208
+ _step;
209
+
210
+ try {
211
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
212
+ var indexedField = _step.value;
213
+
214
+ if (!Object.keys(findSelector).includes(indexedField)) {
215
+ var selectorJson = JSON.stringify(selector);
216
+
217
+ _logger.default.warn("".concat(indexedField, " was missing in selector, it has been automatically added from indexed fields. Please consider adding this field to your query's selector as required by PouchDB. The query's selector is: ").concat(selectorJson));
218
+
219
+ findSelector[indexedField] = {
220
+ $gt: null
221
+ };
222
+ }
223
+ }
224
+ } catch (err) {
225
+ _iterator.e(err);
226
+ } finally {
227
+ _iterator.f();
228
+ }
229
+ }
230
+
231
+ if (sort) {
232
+ var sortedFields = sort.flatMap(function (s) {
233
+ return Object.keys(s);
234
+ });
235
+
236
+ var _iterator2 = _createForOfIteratorHelper(sortedFields),
237
+ _step2;
238
+
239
+ try {
240
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
241
+ var sortedField = _step2.value;
242
+
243
+ if (!Object.keys(findSelector).includes(sortedField)) {
244
+ var _selectorJson = JSON.stringify(selector);
245
+
246
+ _logger.default.warn("".concat(sortedField, " was missing in selector, it has been automatically added from sorted fields. Please consider adding this field to your query's selector as required by PouchDB. The query's selector is: ").concat(_selectorJson));
247
+
248
+ findSelector[sortedField] = {
249
+ $gt: null
250
+ };
251
+ }
252
+ }
253
+ } catch (err) {
254
+ _iterator2.e(err);
255
+ } finally {
256
+ _iterator2.f();
257
+ }
258
+ }
259
+
260
+ var mergedSelector = partialFilter ? (0, _merge.default)(_objectSpread({}, findSelector), partialFilter) : findSelector;
261
+ return Object.keys(mergedSelector).length > 0 ? mergedSelector : {
262
+ _id: {
263
+ $gt: null
264
+ }
265
+ }; // PouchDB does not accept empty selector
266
+ };
267
+
189
268
  var _default = helpers;
190
269
  exports.default = _default;
@@ -1,5 +1,10 @@
1
1
  import helpers from './helpers'
2
- const { withoutDesignDocuments, isDeletedDocument, isDesignDocument } = helpers
2
+ const {
3
+ withoutDesignDocuments,
4
+ isDeletedDocument,
5
+ isDesignDocument,
6
+ normalizeFindSelector
7
+ } = helpers
3
8
 
4
9
  import PouchDB from 'pouchdb-browser'
5
10
  import PouchDBFind from 'pouchdb-find'
@@ -113,4 +118,83 @@ describe('Helpers', () => {
113
118
  expect(isDeletedDocument({ _id: 'notdeleted' })).toBeFalsy()
114
119
  })
115
120
  })
121
+
122
+ describe('normalizeFindSelector', () => {
123
+ it('should add indexed fields in the selector if they are missing', () => {
124
+ const selector = {
125
+ SOME_FIELD: { $gt: null }
126
+ }
127
+ const sort = undefined
128
+ const indexedFields = ['SOME_INDEXED_FIELD']
129
+
130
+ const findSelector = normalizeFindSelector({
131
+ selector,
132
+ sort,
133
+ indexedFields
134
+ })
135
+ expect(findSelector).toStrictEqual({
136
+ SOME_FIELD: { $gt: null },
137
+ SOME_INDEXED_FIELD: { $gt: null }
138
+ })
139
+ })
140
+
141
+ it('should add sorted fields in the selector if they are missing', () => {
142
+ const selector = {}
143
+ const sort = [{ SOME_SORTED_FIELD: 'asc' }]
144
+ const indexedFields = undefined
145
+
146
+ const findSelector = normalizeFindSelector({
147
+ selector,
148
+ sort,
149
+ indexedFields
150
+ })
151
+ expect(findSelector).toStrictEqual({
152
+ SOME_SORTED_FIELD: { $gt: null }
153
+ })
154
+ })
155
+
156
+ it('should add indexed fields AND sorted fields in the selector if they are missing', () => {
157
+ const selector = undefined
158
+ const sort = [{ SOME_SORTED_FIELD: 'asc' }]
159
+ const indexedFields = ['SOME_INDEXED_FIELD']
160
+
161
+ const findSelector = normalizeFindSelector({
162
+ selector,
163
+ sort,
164
+ indexedFields
165
+ })
166
+ expect(findSelector).toStrictEqual({
167
+ SOME_INDEXED_FIELD: { $gt: null },
168
+ SOME_SORTED_FIELD: { $gt: null }
169
+ })
170
+ })
171
+
172
+ it('should prevent empty selector by adding a selector on _id when no selector is provided', () => {
173
+ const selector = undefined
174
+ const sort = undefined
175
+ const indexedFields = undefined
176
+
177
+ const findSelector = normalizeFindSelector({
178
+ selector,
179
+ sort,
180
+ indexedFields
181
+ })
182
+ expect(findSelector).toStrictEqual({ _id: { $gt: null } })
183
+ })
184
+
185
+ it('should not add selector on _id when no selector is provided but there are some indexed fields', () => {
186
+ const selector = undefined
187
+ const sort = undefined
188
+ const indexedFields = ['SOME_INDEXED_FIELD']
189
+
190
+ const findSelector = normalizeFindSelector({
191
+ selector,
192
+ sort,
193
+ indexedFields
194
+ })
195
+ expect(findSelector).toStrictEqual({
196
+ SOME_INDEXED_FIELD: { $gt: null }
197
+ })
198
+ })
199
+ })
116
200
  })
package/dist/jsonapi.js CHANGED
@@ -9,12 +9,16 @@ exports.fromPouchResult = exports.normalizeDoc = void 0;
9
9
 
10
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
11
 
12
+ var _cozyClient = require("cozy-client");
13
+
12
14
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
13
15
 
14
16
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
15
17
 
16
- var normalizeDoc = function normalizeDoc(doc, doctype) {
17
- var id = doc._id || doc.id; // PouchDB sends back .rev attribute but we do not want to
18
+ var normalizeDoc = function normalizeDoc(doc, doctype, client) {
19
+ var id = doc._id || doc.id;
20
+ var relationships = doc.relationships,
21
+ referenced_by = doc.referenced_by; // PouchDB sends back .rev attribute but we do not want to
18
22
  // keep it on the server. It is potentially higher than the
19
23
  // _rev.
20
24
 
@@ -24,23 +28,66 @@ var normalizeDoc = function normalizeDoc(doc, doctype) {
24
28
  id: id,
25
29
  _id: id,
26
30
  _rev: _rev,
27
- _type: doctype
31
+ _type: doctype,
32
+ relationships: _objectSpread(_objectSpread({}, relationships), {}, {
33
+ referenced_by: referenced_by
34
+ })
28
35
  });
29
36
 
30
37
  if (normalizedDoc.rev) {
31
38
  delete normalizedDoc.rev;
32
39
  }
33
40
 
41
+ normalizeAppsLinks(normalizedDoc, doctype, client);
34
42
  return normalizedDoc;
35
43
  };
36
44
 
37
45
  exports.normalizeDoc = normalizeDoc;
38
46
 
47
+ var normalizeAppsLinks = function normalizeAppsLinks(docRef, doctype, client) {
48
+ if (doctype !== 'io.cozy.apps') {
49
+ return;
50
+ }
51
+
52
+ var webLink = (0, _cozyClient.generateWebLink)({
53
+ cozyUrl: client.getStackClient().uri,
54
+ slug: docRef.slug,
55
+ subDomainType: client.capabilities.flat_subdomains ? 'flat' : 'nested',
56
+ pathname: '',
57
+ hash: '',
58
+ searchParams: []
59
+ });
60
+ docRef.links = {
61
+ self: "/apps/".concat(docRef.slug),
62
+ related: webLink,
63
+ icon: "/apps/".concat(docRef.slug, "/icon/").concat(docRef.version)
64
+ };
65
+ };
66
+
39
67
  var filterDeletedDocumentsFromRows = function filterDeletedDocumentsFromRows(doc) {
40
68
  return !!doc;
41
69
  };
42
70
 
43
- var fromPouchResult = function fromPouchResult(res, withRows, doctype) {
71
+ var fromPouchResult = function fromPouchResult(_ref) {
72
+ var res = _ref.res,
73
+ withRows = _ref.withRows,
74
+ doctype = _ref.doctype,
75
+ client = _ref.client;
76
+
77
+ // Sometimes, queries are transformed by Collections and they call a dedicated
78
+ // cozy-stack route. When this is the case, we want to be able to replicate the same
79
+ // query from cozy-pouch-link. It is not possible as-is because the received data
80
+ // is not the same as the one stored in the Couch database
81
+ // To handle this, we store the received data in the Pouch with a dedicated id and
82
+ // we store the query result in a `cozyPouchData` attribute
83
+ // So when `cozyPouchData` attribute exists, we know that we want to return its content
84
+ // as the result of the query
85
+ if (res.cozyPouchData) {
86
+ return {
87
+ data: res.cozyPouchData
88
+ };
89
+ }
90
+
44
91
  if (withRows) {
45
92
  var docs = res.rows ? res.rows.map(function (row) {
46
93
  return row.doc;
@@ -48,7 +95,7 @@ var fromPouchResult = function fromPouchResult(res, withRows, doctype) {
48
95
  var offset = res.offset || 0;
49
96
  return {
50
97
  data: docs.map(function (doc) {
51
- return normalizeDoc(doc, doctype);
98
+ return normalizeDoc(doc, doctype, client);
52
99
  }),
53
100
  meta: {
54
101
  count: docs.length
@@ -59,8 +106,8 @@ var fromPouchResult = function fromPouchResult(res, withRows, doctype) {
59
106
  } else {
60
107
  return {
61
108
  data: Array.isArray(res) ? res.map(function (doc) {
62
- return normalizeDoc(doc, doctype);
63
- }) : normalizeDoc(res, doctype)
109
+ return normalizeDoc(doc, doctype, client);
110
+ }) : normalizeDoc(res, doctype, client)
64
111
  };
65
112
  }
66
113
  };