cozy-client 57.2.0 → 57.4.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.
@@ -85,6 +85,8 @@ var _promiseCache = _interopRequireDefault(require("./promise-cache"));
85
85
 
86
86
  var _flagshipCertification = require("./flagship-certification/flagship-certification");
87
87
 
88
+ var _storage = require("./storage");
89
+
88
90
  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; }
89
91
 
90
92
  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; }
@@ -1618,7 +1620,7 @@ var CozyClient = /*#__PURE__*/function () {
1618
1620
  case 2:
1619
1621
  mainResponse = _context11.sent;
1620
1622
  _context11.next = 5;
1621
- return this.persistVirtualDocuments(definition, mainResponse.data);
1623
+ return (0, _storage.persistVirtualDocuments)(this, definition, mainResponse.data);
1622
1624
 
1623
1625
  case 5:
1624
1626
  if (definition.includes) {
@@ -1662,7 +1664,7 @@ var CozyClient = /*#__PURE__*/function () {
1662
1664
  key: "persistVirtualDocuments",
1663
1665
  value: function () {
1664
1666
  var _persistVirtualDocuments = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee12(definition, data) {
1665
- var enforceList, enforce, _iterator5, _step5, _document;
1667
+ var enforceList, enforce, documentsToPersist, _iterator5, _step5, _document;
1666
1668
 
1667
1669
  return _regenerator.default.wrap(function _callee12$(_context12) {
1668
1670
  while (1) {
@@ -1697,52 +1699,65 @@ var CozyClient = /*#__PURE__*/function () {
1697
1699
  return this.persistVirtualDocument(data, enforce);
1698
1700
 
1699
1701
  case 9:
1700
- _context12.next = 28;
1702
+ _context12.next = 29;
1701
1703
  break;
1702
1704
 
1703
1705
  case 11:
1704
- _iterator5 = _createForOfIteratorHelper(data);
1705
- _context12.prev = 12;
1706
+ documentsToPersist = data.filter(function (document) {
1707
+ var _document$meta;
1708
+
1709
+ if (!document || document.cozyLocalOnly) {
1710
+ return false;
1711
+ }
1712
+
1713
+ if (!((_document$meta = document.meta) !== null && _document$meta !== void 0 && _document$meta.rev) && !document._rev || enforce) {
1714
+ return true;
1715
+ }
1716
+
1717
+ return false;
1718
+ });
1719
+ _iterator5 = _createForOfIteratorHelper(documentsToPersist);
1720
+ _context12.prev = 13;
1706
1721
 
1707
1722
  _iterator5.s();
1708
1723
 
1709
- case 14:
1724
+ case 15:
1710
1725
  if ((_step5 = _iterator5.n()).done) {
1711
- _context12.next = 20;
1726
+ _context12.next = 21;
1712
1727
  break;
1713
1728
  }
1714
1729
 
1715
1730
  _document = _step5.value;
1716
- _context12.next = 18;
1731
+ _context12.next = 19;
1717
1732
  return this.persistVirtualDocument(_document, enforce);
1718
1733
 
1719
- case 18:
1720
- _context12.next = 14;
1734
+ case 19:
1735
+ _context12.next = 15;
1721
1736
  break;
1722
1737
 
1723
- case 20:
1724
- _context12.next = 25;
1738
+ case 21:
1739
+ _context12.next = 26;
1725
1740
  break;
1726
1741
 
1727
- case 22:
1728
- _context12.prev = 22;
1729
- _context12.t0 = _context12["catch"](12);
1742
+ case 23:
1743
+ _context12.prev = 23;
1744
+ _context12.t0 = _context12["catch"](13);
1730
1745
 
1731
1746
  _iterator5.e(_context12.t0);
1732
1747
 
1733
- case 25:
1734
- _context12.prev = 25;
1748
+ case 26:
1749
+ _context12.prev = 26;
1735
1750
 
1736
1751
  _iterator5.f();
1737
1752
 
1738
- return _context12.finish(25);
1753
+ return _context12.finish(26);
1739
1754
 
1740
- case 28:
1755
+ case 29:
1741
1756
  case "end":
1742
1757
  return _context12.stop();
1743
1758
  }
1744
1759
  }
1745
- }, _callee12, this, [[12, 22, 25, 28]]);
1760
+ }, _callee12, this, [[13, 23, 26, 29]]);
1746
1761
  }));
1747
1762
 
1748
1763
  function persistVirtualDocuments(_x14, _x15) {
@@ -1764,7 +1779,7 @@ var CozyClient = /*#__PURE__*/function () {
1764
1779
  key: "persistVirtualDocument",
1765
1780
  value: function () {
1766
1781
  var _persistVirtualDocument = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee13(document, enforce) {
1767
- var _document$meta;
1782
+ var _document$meta2;
1768
1783
 
1769
1784
  return _regenerator.default.wrap(function _callee13$(_context13) {
1770
1785
  while (1) {
@@ -1778,7 +1793,7 @@ var CozyClient = /*#__PURE__*/function () {
1778
1793
  return _context13.abrupt("return");
1779
1794
 
1780
1795
  case 2:
1781
- if (!(!((_document$meta = document.meta) !== null && _document$meta !== void 0 && _document$meta.rev) && !document._rev || enforce)) {
1796
+ if (!(!((_document$meta2 = document.meta) !== null && _document$meta2 !== void 0 && _document$meta2.rev) && !document._rev || enforce)) {
1782
1797
  _context13.next = 5;
1783
1798
  break;
1784
1799
  }
@@ -3097,7 +3112,7 @@ var CozyClient = /*#__PURE__*/function () {
3097
3112
  CozyClient.hooks = CozyClient.hooks || {};
3098
3113
  CozyClient.fetchPolicies = _policies.default; //COZY_CLIENT_VERSION_PACKAGE in replaced by babel. See babel config
3099
3114
 
3100
- CozyClient.version = "57.2.0";
3115
+ CozyClient.version = "57.4.0";
3101
3116
 
3102
3117
  _microee.default.mixin(CozyClient);
3103
3118
 
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.persistVirtualDocuments = exports.shouldDocumentBePersisted = exports.shouldEnforcePersist = exports.ENFORCE_LIST = void 0;
9
+
10
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
11
+
12
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
+
14
+ var _CozyClient = _interopRequireDefault(require("./CozyClient"));
15
+
16
+ var _dsl = require("./queries/dsl");
17
+
18
+ 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; } } }; }
19
+
20
+ 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); }
21
+
22
+ 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; }
23
+
24
+ var ENFORCE_LIST = ['io.cozy.files.shortcuts'];
25
+ /**
26
+ * @typedef {import("./types").CozyClientDocument} CozyClientDocument
27
+ */
28
+
29
+ exports.ENFORCE_LIST = ENFORCE_LIST;
30
+
31
+ var shouldEnforcePersist = function shouldEnforcePersist(definition) {
32
+ return ENFORCE_LIST.includes(definition.doctype);
33
+ };
34
+ /**
35
+ * Whether or not a document should be locally persisted
36
+ *
37
+ * @param {CozyClientDocument} document - The document to evaluate
38
+ * @returns {boolean} whether or not the doc should be persisted
39
+ */
40
+
41
+
42
+ exports.shouldEnforcePersist = shouldEnforcePersist;
43
+
44
+ var shouldDocumentBePersisted = function shouldDocumentBePersisted(document) {
45
+ var _document$meta;
46
+
47
+ var shouldEnforce = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
48
+
49
+ if (!document || document.cozyLocalOnly) {
50
+ return false;
51
+ }
52
+
53
+ if (!((_document$meta = document.meta) !== null && _document$meta !== void 0 && _document$meta.rev) && !document._rev || shouldEnforce) {
54
+ return true;
55
+ }
56
+
57
+ return false;
58
+ };
59
+ /**
60
+ * Save the document or array of documents into the persisted storage (if any)
61
+ *
62
+ * @param {CozyClient} client - Cozy client instance
63
+ * @param {QueryDefinition} definition - The query definition
64
+ * @param {CozyClientDocument | Array<CozyClientDocument>} data - Document or array of documents to be saved
65
+ * @returns {Promise<void>}
66
+ */
67
+
68
+
69
+ exports.shouldDocumentBePersisted = shouldDocumentBePersisted;
70
+
71
+ var persistVirtualDocuments = /*#__PURE__*/function () {
72
+ var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(client, definition, data) {
73
+ var enforce, documentsToPersist, _iterator, _step, document;
74
+
75
+ return _regenerator.default.wrap(function _callee$(_context) {
76
+ while (1) {
77
+ switch (_context.prev = _context.next) {
78
+ case 0:
79
+ enforce = shouldEnforcePersist(definition);
80
+
81
+ if (!(definition.doctype === 'io.cozy.apps_registry')) {
82
+ _context.next = 5;
83
+ break;
84
+ }
85
+
86
+ _context.next = 4;
87
+ return persistVirtualDocument(client, {
88
+ _type: 'io.cozy.apps_registry',
89
+ _id: definition.id,
90
+ // @ts-ignore
91
+ cozyPouchData: data
92
+ }, enforce);
93
+
94
+ case 4:
95
+ return _context.abrupt("return", _context.sent);
96
+
97
+ case 5:
98
+ if (Array.isArray(data)) {
99
+ _context.next = 10;
100
+ break;
101
+ }
102
+
103
+ _context.next = 8;
104
+ return persistVirtualDocument(client, data, enforce);
105
+
106
+ case 8:
107
+ _context.next = 28;
108
+ break;
109
+
110
+ case 10:
111
+ documentsToPersist = data.filter(function (document) {
112
+ return shouldDocumentBePersisted(document, enforce);
113
+ });
114
+ _iterator = _createForOfIteratorHelper(documentsToPersist);
115
+ _context.prev = 12;
116
+
117
+ _iterator.s();
118
+
119
+ case 14:
120
+ if ((_step = _iterator.n()).done) {
121
+ _context.next = 20;
122
+ break;
123
+ }
124
+
125
+ document = _step.value;
126
+ _context.next = 18;
127
+ return persistVirtualDocument(client, document, enforce);
128
+
129
+ case 18:
130
+ _context.next = 14;
131
+ break;
132
+
133
+ case 20:
134
+ _context.next = 25;
135
+ break;
136
+
137
+ case 22:
138
+ _context.prev = 22;
139
+ _context.t0 = _context["catch"](12);
140
+
141
+ _iterator.e(_context.t0);
142
+
143
+ case 25:
144
+ _context.prev = 25;
145
+
146
+ _iterator.f();
147
+
148
+ return _context.finish(25);
149
+
150
+ case 28:
151
+ case "end":
152
+ return _context.stop();
153
+ }
154
+ }
155
+ }, _callee, null, [[12, 22, 25, 28]]);
156
+ }));
157
+
158
+ return function persistVirtualDocuments(_x, _x2, _x3) {
159
+ return _ref.apply(this, arguments);
160
+ };
161
+ }();
162
+ /**
163
+ * Save the document or array of documents into the persisted storage (if any)
164
+ *
165
+ * This relies on the fact that there are some "virtual" documents returned
166
+ * by the stack, in the sense that they are not CouchDB documents, but rather
167
+ * are computed on the fly by the server.
168
+ * To ensure we can use those documents locally, i.e. without the stack,
169
+ * we allow the possibility to persist them.
170
+ *
171
+ * @param {CozyClient} client - Cozy client instance
172
+ * @param {CozyClientDocument} document - Document to be saved
173
+ * @param {boolean} enforce - When true, save the document no matter its content
174
+ * @returns {Promise<void>}
175
+ */
176
+
177
+
178
+ exports.persistVirtualDocuments = persistVirtualDocuments;
179
+
180
+ var persistVirtualDocument = /*#__PURE__*/function () {
181
+ var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(client, document, enforce) {
182
+ return _regenerator.default.wrap(function _callee2$(_context2) {
183
+ while (1) {
184
+ switch (_context2.prev = _context2.next) {
185
+ case 0:
186
+ if (!shouldDocumentBePersisted(document, enforce)) {
187
+ _context2.next = 3;
188
+ break;
189
+ }
190
+
191
+ _context2.next = 3;
192
+ return client.chain.persistCozyData(document);
193
+
194
+ case 3:
195
+ case "end":
196
+ return _context2.stop();
197
+ }
198
+ }
199
+ }, _callee2);
200
+ }));
201
+
202
+ return function persistVirtualDocument(_x4, _x5, _x6) {
203
+ return _ref2.apply(this, arguments);
204
+ };
205
+ }();
@@ -0,0 +1,31 @@
1
+ import { shouldDocumentBePersisted } from './storage'
2
+
3
+ const BASE_DOC = {
4
+ _id: '123',
5
+ name: 'saymy'
6
+ }
7
+
8
+ describe('shouldDocumentBePersisted', () => {
9
+ it('should not persist a doc with cozyLocalOnly flag', () => {
10
+ const doc = { ...BASE_DOC, cozyLocalOnly: true }
11
+ expect(shouldDocumentBePersisted(doc)).toBe(false)
12
+ })
13
+
14
+ it('should not persist a doc with rev', () => {
15
+ const doc1 = { ...BASE_DOC, _rev: '123' }
16
+ expect(shouldDocumentBePersisted(doc1)).toBe(false)
17
+
18
+ const doc2 = { ...BASE_DOC, meta: { rev: '123' } }
19
+ expect(shouldDocumentBePersisted(doc2)).toBe(false)
20
+ })
21
+
22
+ it('should persist if we enforce it', () => {
23
+ const doc = { ...BASE_DOC, _rev: '123' }
24
+ expect(shouldDocumentBePersisted(doc, true)).toBe(true)
25
+ })
26
+
27
+ it('should persist if there is not revision', () => {
28
+ const doc = { ...BASE_DOC }
29
+ expect(shouldDocumentBePersisted(doc)).toBe(true)
30
+ })
31
+ })
@@ -9,15 +9,13 @@ exports.extractAndMergeDocument = exports.getCollectionFromSlice = exports.getDo
9
9
 
10
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
11
 
12
- var _keyBy = _interopRequireDefault(require("lodash/keyBy"));
13
-
14
12
  var _get = _interopRequireDefault(require("lodash/get"));
15
13
 
16
- var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
17
-
18
14
  var _omit = _interopRequireDefault(require("lodash/omit"));
19
15
 
20
- var _merge = _interopRequireDefault(require("lodash/merge"));
16
+ var _fastDeepEqual = _interopRequireDefault(require("fast-deep-equal"));
17
+
18
+ var _deepmerge = _interopRequireDefault(require("@fastify/deepmerge"));
21
19
 
22
20
  var _logger = _interopRequireDefault(require("../logger"));
23
21
 
@@ -33,6 +31,8 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
33
31
 
34
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; }
35
33
 
34
+ var deepmergeFn = (0, _deepmerge.default)();
35
+
36
36
  var storeDocument = function storeDocument(state, document) {
37
37
  var type = document._type;
38
38
 
@@ -54,7 +54,7 @@ var storeDocument = function storeDocument(state, document) {
54
54
 
55
55
  var existingDoc = (0, _get.default)(state, [type, (0, _helpers.properId)(document)]);
56
56
 
57
- if ((0, _isEqual.default)(existingDoc, document)) {
57
+ if ((0, _fastDeepEqual.default)(existingDoc, document)) {
58
58
  return state;
59
59
  } else {
60
60
  return _objectSpread(_objectSpread({}, state), {}, (0, _defineProperty2.default)({}, type, _objectSpread(_objectSpread({}, state[type]), {}, (0, _defineProperty2.default)({}, (0, _helpers.properId)(document), mergeDocumentsWithRelationships(existingDoc, document)))));
@@ -203,44 +203,76 @@ var getCollectionFromSlice = function getCollectionFromSlice() {
203
203
 
204
204
  return Object.values(state[doctype]);
205
205
  };
206
- /*
207
- This method has been created in order to get a returned object
208
- in `data` with the full set on information coming potentially from
206
+ /**
207
+ *
208
+ * This method has been created in order to get a returned object
209
+ in `data` with the full set on information coming potentially from
209
210
  `included`
210
-
211
+
211
212
  This method should be somewhere else. The `document` shall not be
212
213
  dealt with included / data and so on.
213
-
214
+
214
215
  This method takes `data` and `included` and merge both sources
215
216
  together. It should be always up-to-date. The returned object
216
217
  will be as full of information as it can be.
217
- */
218
+
219
+ * @param {Array<import('../types').CozyClientDocument>} newData - Queries documents
220
+ * @param {import("../types").DocumentsStateSlice} updatedStateWithIncluded documents' state
221
+ * @returns {import("../types").DocumentsStateSlice} the merged documents state
222
+ */
218
223
 
219
224
 
220
225
  exports.getCollectionFromSlice = getCollectionFromSlice;
221
226
 
222
- var extractAndMergeDocument = function extractAndMergeDocument(data, updatedStateWithIncluded) {
223
- var doctype = data[0]._type;
227
+ var extractAndMergeDocument = function extractAndMergeDocument(newData, updatedStateWithIncluded) {
228
+ var doctype = newData[0]._type;
224
229
 
225
230
  if (!doctype) {
226
- _logger.default.info('Document without _type', data[0]);
231
+ _logger.default.info('Document without _type', newData[0]);
227
232
 
228
233
  throw new Error('Document without _type');
229
- }
234
+ } // Index docs by id, for faster retrieval
235
+
236
+
237
+ var newDocsMap = new Map(newData.map(function (doc) {
238
+ return [(0, _helpers.properId)(doc), doc];
239
+ }));
240
+ var mergedState = Object.assign({}, updatedStateWithIncluded); // FIXME: this was introduced for a bug in banks, but it does not look useful
241
+
242
+ mergedState[doctype] = Object.assign({}, updatedStateWithIncluded[doctype]);
230
243
 
231
- var sortedData = (0, _keyBy.default)(data, _helpers.properId);
232
- var mergedData = Object.assign({}, updatedStateWithIncluded);
233
- mergedData[doctype] = Object.assign({}, updatedStateWithIncluded[doctype]);
234
- Object.values(sortedData).map(function (data) {
235
- var id = (0, _helpers.properId)(data);
244
+ if (!mergedState[doctype]) {
245
+ mergedState[doctype] = {};
246
+ }
236
247
 
237
- if (mergedData[doctype][id]) {
238
- mergedData[doctype][id] = (0, _merge.default)({}, mergedData[doctype][id], data);
239
- } else {
240
- mergedData[doctype][id] = data;
248
+ var haveDocumentsChanged = false;
249
+ newDocsMap.forEach(function (newDoc, key) {
250
+ var id = key;
251
+ var currentDoc = mergedState[doctype][id];
252
+
253
+ if (!currentDoc) {
254
+ mergedState[doctype][id] = newDoc;
255
+ haveDocumentsChanged = true;
256
+ } else if (!(0, _fastDeepEqual.default)(currentDoc, newDoc)) {
257
+ // XXX Here we check if the doc has changed. Note this would be way faster
258
+ // by just relying on revision change, but:
259
+ // - it does not work if the new doc is partial (i.e. retrieved with a .select query)
260
+ // - some documents might change without a rev change: typically the thumbnails links
261
+ // in io.cozy.files, and the io.cozy.settings.instance doc, with additional
262
+ // fields added by the stack
263
+ // - some documents don't have any revision
264
+ mergedState[doctype][id] = deepmergeFn(mergedState[doctype][id], newDoc);
265
+ haveDocumentsChanged = true;
241
266
  }
242
- });
243
- return mergedData;
267
+ }); // XXX If there is any state change, the updated state should have a new reference,
268
+ // as we heavily rely on referential equality.
269
+ // Conversely, if there is no change, the same reference should be kept
270
+
271
+ if (haveDocumentsChanged) {
272
+ return mergedState;
273
+ }
274
+
275
+ return updatedStateWithIncluded;
244
276
  };
245
277
 
246
278
  exports.extractAndMergeDocument = extractAndMergeDocument;
@@ -4,11 +4,9 @@ import {
4
4
  } from './documents'
5
5
 
6
6
  describe('extractAndMerge', () => {
7
- const data = {
8
- 0: {
9
- id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
7
+ const data = [
8
+ {
10
9
  _id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
11
- type: 'io.cozy.files',
12
10
  _type: 'io.cozy.files',
13
11
  blabla: 'new field',
14
12
  cozyMetadata: {
@@ -18,24 +16,20 @@ describe('extractAndMerge', () => {
18
16
  }
19
17
  }
20
18
  },
21
- 1: {
22
- id: 'b6ff135b34e041ffb2d4a4865f3e235f',
23
- type: 'io.cozy.files',
24
- _type: 'io.cozy.files',
19
+ {
25
20
  _id: 'b6ff135b34e041ffb2d4a4865f3e235f',
21
+ _type: 'io.cozy.files',
26
22
  blibli: 'another new field'
27
23
  }
28
- }
24
+ ]
29
25
  const updatedStateWithIncluded = {
30
26
  'io.cozy.files': {
31
27
  b6ff135b34e041ffb2d4a4865f3e0a53: {
32
- attributes: {
33
- type: 'file',
34
- name: 'IMG_0016.PNG',
35
- dir_id: '7d2f9c24cd345ce3171bf71f401e80c6'
36
- },
37
- id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
38
28
  _id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
29
+ _type: 'io.cozy.files',
30
+ name: 'IMG_0016.PNG',
31
+ type: 'file',
32
+ dir_id: '7d2f9c24cd345ce3171bf71f401e80c6',
39
33
  links: { self: '/files/b6ff135b34e041ffb2d4a4865f3e0a53' },
40
34
  meta: { rev: '5-87840eceaab358e38aa8b6bb0d4577b1' },
41
35
  relationships: {
@@ -43,8 +37,6 @@ describe('extractAndMerge', () => {
43
37
  links: { related: '/files/7d2f9c24cd345ce3171bf71f401e80c6' }
44
38
  }
45
39
  },
46
- type: 'io.cozy.files',
47
- _type: 'io.cozy.files',
48
40
  cozyMetadata: {
49
41
  updated_at: '987654',
50
42
  updatedByApps: {
@@ -53,22 +45,18 @@ describe('extractAndMerge', () => {
53
45
  }
54
46
  },
55
47
  b6ff135b34e041ffb2d4a4865f3e235f: {
56
- attributes: {
57
- type: 'file',
58
- name: 'IMG_0054.PNG',
59
- dir_id: '7d2f9c24cd345ce3171bf71f401e80c6'
60
- },
61
- id: 'b6ff135b34e041ffb2d4a4865f3e235f',
62
48
  _id: 'b6ff135b34e041ffb2d4a4865f3e235f',
49
+ _type: 'io.cozy.files',
50
+ type: 'file',
51
+ name: 'IMG_0054.PNG',
52
+ dir_id: '7d2f9c24cd345ce3171bf71f401e80c6',
63
53
  links: { self: '/files/b6ff135b34e041ffb2d4a4865f3e235f' },
64
54
  meta: { rev: '5-ca91c0dc02dafb38eb56070c1d80d62c' },
65
55
  relationships: {
66
56
  parent: {
67
57
  links: { related: '/files/7d2f9c24cd345ce3171bf71f401e80c6' }
68
58
  }
69
- },
70
- type: 'io.cozy.files',
71
- _type: 'io.cozy.files'
59
+ }
72
60
  }
73
61
  }
74
62
  }
@@ -84,11 +72,9 @@ describe('extractAndMerge', () => {
84
72
  b6ff135b34e041ffb2d4a4865f3e0a53: {
85
73
  _id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
86
74
  _type: 'io.cozy.files',
87
- attributes: {
88
- dir_id: '7d2f9c24cd345ce3171bf71f401e80c6',
89
- name: 'IMG_0016.PNG',
90
- type: 'file'
91
- },
75
+ dir_id: '7d2f9c24cd345ce3171bf71f401e80c6',
76
+ name: 'IMG_0016.PNG',
77
+ type: 'file',
92
78
  blabla: 'new field',
93
79
  cozyMetadata: {
94
80
  created_at: '123456',
@@ -98,34 +84,28 @@ describe('extractAndMerge', () => {
98
84
  date: '2019-06-11T01:02:03Z'
99
85
  }
100
86
  },
101
- id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
102
87
  links: { self: '/files/b6ff135b34e041ffb2d4a4865f3e0a53' },
103
88
  meta: { rev: '5-87840eceaab358e38aa8b6bb0d4577b1' },
104
89
  relationships: {
105
90
  parent: {
106
91
  links: { related: '/files/7d2f9c24cd345ce3171bf71f401e80c6' }
107
92
  }
108
- },
109
- type: 'io.cozy.files'
93
+ }
110
94
  },
111
95
  b6ff135b34e041ffb2d4a4865f3e235f: {
112
96
  _id: 'b6ff135b34e041ffb2d4a4865f3e235f',
113
97
  _type: 'io.cozy.files',
114
- attributes: {
115
- dir_id: '7d2f9c24cd345ce3171bf71f401e80c6',
116
- name: 'IMG_0054.PNG',
117
- type: 'file'
118
- },
98
+ dir_id: '7d2f9c24cd345ce3171bf71f401e80c6',
99
+ name: 'IMG_0054.PNG',
100
+ type: 'file',
119
101
  blibli: 'another new field',
120
- id: 'b6ff135b34e041ffb2d4a4865f3e235f',
121
102
  links: { self: '/files/b6ff135b34e041ffb2d4a4865f3e235f' },
122
103
  meta: { rev: '5-ca91c0dc02dafb38eb56070c1d80d62c' },
123
104
  relationships: {
124
105
  parent: {
125
106
  links: { related: '/files/7d2f9c24cd345ce3171bf71f401e80c6' }
126
107
  }
127
- },
128
- type: 'io.cozy.files'
108
+ }
129
109
  }
130
110
  }
131
111
  }
@@ -171,20 +151,16 @@ describe('extractAndMerge', () => {
171
151
  'even if the mergedData is the same, because reselect createSelector update ' +
172
152
  'according to object reference, not only value',
173
153
  () => {
174
- const dataAlreadyIncludedInUpdatedStateWithIncluded = {
175
- 0: {
176
- id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
154
+ const dataAlreadyIncludedInUpdatedStateWithIncluded = [
155
+ {
177
156
  _id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
178
- type: 'io.cozy.files',
179
157
  _type: 'io.cozy.files'
180
158
  },
181
- 1: {
182
- id: 'b6ff135b34e041ffb2d4a4865f3e235f',
183
- type: 'io.cozy.files',
184
- _type: 'io.cozy.files',
185
- _id: 'b6ff135b34e041ffb2d4a4865f3e235f'
159
+ {
160
+ _id: 'b6ff135b34e041ffb2d4a4865f3e235f',
161
+ _type: 'io.cozy.files'
186
162
  }
187
- }
163
+ ]
188
164
 
189
165
  const returnedDatas = extractAndMergeDocument(
190
166
  dataAlreadyIncludedInUpdatedStateWithIncluded,
@@ -202,6 +178,44 @@ describe('extractAndMerge', () => {
202
178
  )
203
179
  }
204
180
  )
181
+ it('should keep state reference in case no document has changed', () => {
182
+ const newDoc = { ...data[0] }
183
+
184
+ const mergedState = extractAndMergeDocument(
185
+ [newDoc],
186
+ updatedStateWithIncluded
187
+ )
188
+ expect(mergedState === updatedStateWithIncluded)
189
+ })
190
+
191
+ it('should have different state reference if a new document is added', () => {
192
+ const newDoc = {
193
+ _id: '1234',
194
+ _type: 'io.cozy.files',
195
+ name: 'New doc'
196
+ }
197
+ const mergedState = extractAndMergeDocument(
198
+ [newDoc],
199
+ updatedStateWithIncluded
200
+ )
201
+ expect(mergedState !== updatedStateWithIncluded)
202
+ })
203
+
204
+ it('should have different state reference if an existing document have changed', () => {
205
+ const newDoc = {
206
+ ...data[0],
207
+ cozyMetadata: {
208
+ ...data[0].cozyMetadata,
209
+ updatedByApps: '2025-01-01'
210
+ }
211
+ }
212
+
213
+ const mergedState = extractAndMergeDocument(
214
+ [newDoc],
215
+ updatedStateWithIncluded
216
+ )
217
+ expect(mergedState !== updatedStateWithIncluded)
218
+ })
205
219
  })
206
220
 
207
221
  describe('mergeDocumentsWithRelationships', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-client",
3
- "version": "57.2.0",
3
+ "version": "57.4.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.node.js",
6
6
  "browser": "dist/index.js",
@@ -15,11 +15,13 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@cozy/minilog": "1.0.0",
18
+ "@fastify/deepmerge": "^2.0.2",
18
19
  "@types/jest": "^26.0.20",
19
20
  "@types/lodash": "^4.14.170",
20
21
  "btoa": "^1.2.1",
21
22
  "cozy-stack-client": "^57.2.0",
22
23
  "date-fns": "2.29.3",
24
+ "fast-deep-equal": "^3.1.3",
23
25
  "json-stable-stringify": "^1.0.1",
24
26
  "lodash": "^4.17.13",
25
27
  "microee": "^0.0.6",
@@ -72,5 +74,5 @@
72
74
  "react-native-ios11-devicecheck": "https://github.com/cozy/react-native-devicecheck#app-attest-v0.1"
73
75
  },
74
76
  "sideEffects": false,
75
- "gitHead": "f9c7c5c422ae18a648278cecf820f9a367447263"
77
+ "gitHead": "97e956957de48f36a29d0d55adf3cb0e248f0141"
76
78
  }
@@ -0,0 +1,48 @@
1
+ export const ENFORCE_LIST: string[];
2
+ export function shouldEnforcePersist(definition: any): boolean;
3
+ export function shouldDocumentBePersisted(document: CozyClientDocument, shouldEnforce?: boolean): boolean;
4
+ export function persistVirtualDocuments(client: CozyClient, definition: QueryDefinition, data: CozyClientDocument | Array<CozyClientDocument>): Promise<void>;
5
+ export type CozyClientDocument = {
6
+ /**
7
+ * - Id of the document
8
+ */
9
+ _id?: string;
10
+ /**
11
+ * - Id of the document
12
+ */
13
+ id?: string;
14
+ /**
15
+ * - Type of the document
16
+ */
17
+ _type?: string;
18
+ /**
19
+ * - Current revision of the document
20
+ */
21
+ _rev?: string;
22
+ /**
23
+ * - When the document has been deleted
24
+ */
25
+ _deleted?: boolean;
26
+ /**
27
+ * - Relationships of the document
28
+ */
29
+ relationships?: import("./types").ReferencedByRelationship;
30
+ /**
31
+ * - referenced by of another document
32
+ */
33
+ referenced_by?: import("./types").Reference[];
34
+ /**
35
+ * - Cozy Metadata
36
+ */
37
+ cozyMetadata?: import("./types").CozyMetadata;
38
+ /**
39
+ * - Pouch Metadata
40
+ */
41
+ meta?: import("./types").CozyClientDocumentMeta;
42
+ /**
43
+ * - When true the document should NOT be replicated to the remote database
44
+ */
45
+ cozyLocalOnly?: boolean;
46
+ };
47
+ import CozyClient from "./CozyClient";
48
+ import { QueryDefinition } from "./queries/dsl";
@@ -3,5 +3,5 @@ export default documents;
3
3
  export function getDocumentFromSlice(state: import('../types').DocumentsStateSlice, doctype: string, id: string): import('../types').CozyClientDocument;
4
4
  export function getDocumentsFromSlice(state: import('../types').DocumentsStateSlice, doctype: string, ids: Array<string>): Array<import('../types').CozyClientDocument>;
5
5
  export function getCollectionFromSlice(state: {}, doctype: any): any[];
6
- export function extractAndMergeDocument(data: any, updatedStateWithIncluded: any): any;
6
+ export function extractAndMergeDocument(newData: Array<import('../types').CozyClientDocument>, updatedStateWithIncluded: import("../types").DocumentsStateSlice): import("../types").DocumentsStateSlice;
7
7
  declare function documents(state: {}, action: any): any;