cozy-pouch-link 60.24.0 → 60.24.2

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.
@@ -235,7 +235,7 @@ describe('PouchManager', () => {
235
235
  manager.updateSyncInfo('io.cozy.todos')
236
236
  await manager.replicateOnce()
237
237
  expect(onSync).toHaveBeenCalledWith({
238
- 'cozy.tools__doctype__io.cozy.todos': [
238
+ 'io.cozy.todos': [
239
239
  {
240
240
  _id: '1',
241
241
  name: 'Make replication work'
package/dist/files.js CHANGED
@@ -23,19 +23,23 @@ exports.TYPE_FILE = TYPE_FILE;
23
23
 
24
24
  var queryFileById = /*#__PURE__*/function () {
25
25
  var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(client, id) {
26
- var queryOpts;
26
+ var definition, options;
27
27
  return _regenerator.default.wrap(function _callee$(_context) {
28
28
  while (1) {
29
29
  switch (_context.prev = _context.next) {
30
30
  case 0:
31
- queryOpts = {
31
+ definition = (0, _cozyClient.Q)('io.cozy.files').getById(id);
32
+ options = {
32
33
  as: "io.cozy.files/".concat(id),
33
34
  fetchPolicy: defaultFetchPolicy,
34
35
  singleDocData: true
35
36
  };
36
- return _context.abrupt("return", client.query((0, _cozyClient.Q)('io.cozy.files').getById(id), queryOpts));
37
+ return _context.abrupt("return", client.fetchQueryAndGetFromState({
38
+ definition: definition,
39
+ options: options
40
+ }));
37
41
 
38
- case 2:
42
+ case 3:
39
43
  case "end":
40
44
  return _context.stop();
41
45
  }
@@ -0,0 +1,25 @@
1
+ import { Q } from 'cozy-client'
2
+
3
+ import { queryFileById } from './files'
4
+
5
+ describe('queryFileById', () => {
6
+ it('should call client.fetchQueryAndGetFromState with the expected definition and options', async () => {
7
+ const expectedResult = { data: { _id: 'parent', path: '/parent' } }
8
+ const client = {
9
+ fetchQueryAndGetFromState: jest.fn().mockResolvedValue(expectedResult)
10
+ }
11
+
12
+ const res = await queryFileById(client, 'parent')
13
+
14
+ expect(res).toBe(expectedResult)
15
+ expect(client.fetchQueryAndGetFromState).toHaveBeenCalledTimes(1)
16
+ expect(client.fetchQueryAndGetFromState).toHaveBeenCalledWith({
17
+ definition: expect.objectContaining(Q('io.cozy.files').getById('parent')),
18
+ options: {
19
+ as: 'io.cozy.files/parent',
20
+ fetchPolicy: expect.any(Function),
21
+ singleDocData: true
22
+ }
23
+ })
24
+ })
25
+ })
package/dist/jsonapi.js CHANGED
@@ -167,7 +167,7 @@ var buildPathWithName = function buildPathWithName(parentPath, fileName) {
167
167
 
168
168
  var computeFileFullpath = /*#__PURE__*/function () {
169
169
  var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(client, file) {
170
- var _file$path, newPath, filePath, parentPath, builtPath, path, _yield$queryFileById, parentDir, _path;
170
+ var _file$path, newPath, filePath, parentPath, builtPath, path, result, parentDir, _path;
171
171
 
172
172
  return _regenerator.default.wrap(function _callee$(_context) {
173
173
  while (1) {
@@ -246,8 +246,8 @@ var computeFileFullpath = /*#__PURE__*/function () {
246
246
  return (0, _files.queryFileById)(client, file.dir_id);
247
247
 
248
248
  case 27:
249
- _yield$queryFileById = _context.sent;
250
- parentDir = _yield$queryFileById.data;
249
+ result = _context.sent;
250
+ parentDir = result === null || result === void 0 ? void 0 : result.data;
251
251
 
252
252
  if (parentDir !== null && parentDir !== void 0 && parentDir.path) {
253
253
  _path = buildPathWithName(parentDir.path, file.name);
@@ -719,4 +719,35 @@ describe('computeFileFullpath', () => {
719
719
  const res2 = await computeFileFullpath(client, updFile)
720
720
  expect(res2.path).toEqual('ROOT/MYDIR/file3.1')
721
721
  })
722
+
723
+ it('should return the file unchanged when queryFileById resolves to undefined', async () => {
724
+ // This happens offline when the link chain returns nothing for the
725
+ // parent dir lookup. Before the fix, destructuring `.data` from
726
+ // `undefined` crashed with "Cannot read property 'data' of undefined".
727
+ queryFileById.mockResolvedValue(undefined)
728
+ const file = {
729
+ _id: 'offline-file-1',
730
+ _type: 'io.cozy.files',
731
+ type: 'file',
732
+ dir_id: 'unknown-dir',
733
+ name: 'offline.txt'
734
+ }
735
+ const res = await computeFileFullpath(client, file)
736
+ expect(res).toBe(file)
737
+ expect(res.path).toBeUndefined()
738
+ })
739
+
740
+ it('should return the file unchanged when queryFileById resolves with null data', async () => {
741
+ queryFileById.mockResolvedValue({ data: null })
742
+ const file = {
743
+ _id: 'offline-file-2',
744
+ _type: 'io.cozy.files',
745
+ type: 'file',
746
+ dir_id: 'unknown-dir',
747
+ name: 'offline2.txt'
748
+ }
749
+ const res = await computeFileFullpath(client, file)
750
+ expect(res).toBe(file)
751
+ expect(res.path).toBeUndefined()
752
+ })
722
753
  })
@@ -176,7 +176,9 @@ var replicateOnce = /*#__PURE__*/function () {
176
176
  };
177
177
  }()); // Waiting on each replication
178
178
 
179
- doctypes = Object.keys(pouchManager.pouches);
179
+ doctypes = Object.keys(pouchManager.pouches).map(function (dbName) {
180
+ return (0, _utils.getDoctypeFromDatabaseName)(dbName);
181
+ });
180
182
  promises = Object.values(pouchManager.replications);
181
183
  _context2.prev = 9;
182
184
  _context2.next = 12;
@@ -0,0 +1,104 @@
1
+ /* global jest */
2
+
3
+ jest.mock('./remote', () => ({
4
+ fetchRemoteLastSequence: jest.fn().mockResolvedValue('0'),
5
+ isDatabaseNotFoundError: jest.fn().mockReturnValue(false),
6
+ isDatabaseUnradableError: jest.fn().mockReturnValue(false)
7
+ }))
8
+
9
+ jest.mock('./startReplication', () => ({
10
+ startReplication: jest.fn()
11
+ }))
12
+
13
+ import { replicateOnce } from './replicateOnce'
14
+ import { startReplication } from './startReplication'
15
+
16
+ describe('replicateOnce', () => {
17
+ beforeEach(() => {
18
+ jest.clearAllMocks()
19
+ })
20
+
21
+ it('should call onSync with clean doctype keys, not prefixed pouch dbNames', async () => {
22
+ const dbName = 'instance__doctype__io.cozy.todos'
23
+ const docs = [{ _id: 'doc-1', name: 'a' }]
24
+
25
+ startReplication.mockResolvedValue(docs)
26
+
27
+ const pouchManager = {
28
+ pouches: { [dbName]: { id: 'fake-pouch' } },
29
+ replications: {},
30
+ doctypesReplicationOptions: {},
31
+ options: {
32
+ onSync: jest.fn()
33
+ },
34
+ storage: {
35
+ persistDoctypeLastSequence: jest.fn().mockResolvedValue(),
36
+ getDoctypeLastSequence: jest.fn().mockResolvedValue(''),
37
+ destroyDoctypeLastSequence: jest.fn().mockResolvedValue()
38
+ },
39
+ client: {},
40
+ isOnline: jest.fn().mockResolvedValue(true),
41
+ getReplicationURL: jest.fn().mockReturnValue('http://example/db'),
42
+ getSyncStatus: jest.fn().mockReturnValue('synced'),
43
+ updateSyncInfo: jest.fn().mockResolvedValue(),
44
+ checkToWarmupDoctype: jest.fn(),
45
+ handleReplicationError: jest.fn(),
46
+ cancelCurrentReplications: jest.fn()
47
+ }
48
+
49
+ await replicateOnce(pouchManager)
50
+
51
+ expect(pouchManager.options.onSync).toHaveBeenCalledTimes(1)
52
+ const payload = pouchManager.options.onSync.mock.calls[0][0]
53
+
54
+ // The bug: payload was keyed by the full prefixed dbName
55
+ // The fix: payload must be keyed by the clean doctype
56
+ expect(Object.keys(payload)).toEqual(['io.cozy.todos'])
57
+ expect(Object.keys(payload)).not.toContain(dbName)
58
+ expect(payload['io.cozy.todos']).toEqual(docs)
59
+ })
60
+
61
+ it('should preserve clean doctype keys for multiple pouches', async () => {
62
+ const dbNameA = 'instance__doctype__io.cozy.todos'
63
+ const dbNameB = 'instance__doctype__io.cozy.files'
64
+ const docsA = [{ _id: 'a1' }]
65
+ const docsB = [{ _id: 'b1' }]
66
+
67
+ startReplication.mockImplementation(pouch => {
68
+ if (pouch === 'pouchA') return Promise.resolve(docsA)
69
+ if (pouch === 'pouchB') return Promise.resolve(docsB)
70
+ return Promise.resolve([])
71
+ })
72
+
73
+ const pouchManager = {
74
+ pouches: { [dbNameA]: 'pouchA', [dbNameB]: 'pouchB' },
75
+ replications: {},
76
+ doctypesReplicationOptions: {},
77
+ options: { onSync: jest.fn() },
78
+ storage: {
79
+ persistDoctypeLastSequence: jest.fn().mockResolvedValue(),
80
+ getDoctypeLastSequence: jest.fn().mockResolvedValue(''),
81
+ destroyDoctypeLastSequence: jest.fn().mockResolvedValue()
82
+ },
83
+ client: {},
84
+ isOnline: jest.fn().mockResolvedValue(true),
85
+ getReplicationURL: jest.fn().mockReturnValue('http://example/db'),
86
+ getSyncStatus: jest.fn().mockReturnValue('synced'),
87
+ updateSyncInfo: jest.fn().mockResolvedValue(),
88
+ checkToWarmupDoctype: jest.fn(),
89
+ handleReplicationError: jest.fn(),
90
+ cancelCurrentReplications: jest.fn()
91
+ }
92
+
93
+ await replicateOnce(pouchManager)
94
+
95
+ expect(pouchManager.options.onSync).toHaveBeenCalledTimes(1)
96
+ const payload = pouchManager.options.onSync.mock.calls[0][0]
97
+ expect(Object.keys(payload).sort()).toEqual([
98
+ 'io.cozy.files',
99
+ 'io.cozy.todos'
100
+ ])
101
+ expect(payload['io.cozy.todos']).toEqual(docsA)
102
+ expect(payload['io.cozy.files']).toEqual(docsB)
103
+ })
104
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-pouch-link",
3
- "version": "60.24.0",
3
+ "version": "60.24.2",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "types": "types/index.d.ts",
@@ -41,5 +41,5 @@
41
41
  "typecheck": "tsc -p tsconfig.json"
42
42
  },
43
43
  "sideEffects": false,
44
- "gitHead": "72de2e80e0d0ac89d71b7fca60c2352b07136151"
44
+ "gitHead": "e621858d1b3d16f79d75bb79d3f183d204f2eabe"
45
45
  }