cozy-client 48.12.0 → 48.13.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.
@@ -1262,11 +1262,13 @@ var CozyClient = /*#__PURE__*/function () {
1262
1262
 
1263
1263
  var _ref5,
1264
1264
  update,
1265
+ executeFromStore,
1265
1266
  options,
1266
1267
  queryId,
1267
1268
  existingQuery,
1268
1269
  shouldFetch,
1269
1270
  backgroundFetching,
1271
+ requestFn,
1270
1272
  response,
1271
1273
  _args8 = arguments;
1272
1274
 
@@ -1275,7 +1277,7 @@ var CozyClient = /*#__PURE__*/function () {
1275
1277
  switch (_context8.prev = _context8.next) {
1276
1278
  case 0:
1277
1279
  _ref5 = _args8.length > 1 && _args8[1] !== undefined ? _args8[1] : {};
1278
- update = _ref5.update, options = (0, _objectWithoutProperties2.default)(_ref5, ["update"]);
1280
+ update = _ref5.update, executeFromStore = _ref5.executeFromStore, options = (0, _objectWithoutProperties2.default)(_ref5, ["update", "executeFromStore"]);
1279
1281
  this.ensureStore();
1280
1282
  queryId = options.as || this.queryIdGenerator.generateId(queryDefinition);
1281
1283
  existingQuery = this.getQueryFromState(queryId, options);
@@ -1337,14 +1339,17 @@ var CozyClient = /*#__PURE__*/function () {
1337
1339
  this.dispatch((0, _store.loadQuery)(queryId, {
1338
1340
  backgroundFetching: backgroundFetching
1339
1341
  }));
1340
- _context8.next = 23;
1341
- return this._promiseCache.exec(function () {
1342
+ requestFn = executeFromStore ? function () {
1343
+ return Promise.resolve((0, _store.executeQueryFromState)(_this4.store.getState(), queryDefinition));
1344
+ } : function () {
1342
1345
  return _this4.requestQuery(queryDefinition);
1343
- }, function () {
1346
+ };
1347
+ _context8.next = 24;
1348
+ return this._promiseCache.exec(requestFn, function () {
1344
1349
  return (0, _jsonStableStringify.default)(queryDefinition);
1345
1350
  });
1346
1351
 
1347
- case 23:
1352
+ case 24:
1348
1353
  response = _context8.sent;
1349
1354
  this.dispatch((0, _store.receiveQueryResult)(queryId, response, {
1350
1355
  update: update,
@@ -1352,40 +1357,40 @@ var CozyClient = /*#__PURE__*/function () {
1352
1357
  }));
1353
1358
  return _context8.abrupt("return", response);
1354
1359
 
1355
- case 28:
1356
- _context8.prev = 28;
1360
+ case 29:
1361
+ _context8.prev = 29;
1357
1362
  _context8.t0 = _context8["catch"](18);
1358
1363
  this.dispatch((0, _store.receiveQueryError)(queryId, _context8.t0)); // specific onError
1359
1364
 
1360
1365
  if (!options.onError) {
1361
- _context8.next = 35;
1366
+ _context8.next = 36;
1362
1367
  break;
1363
1368
  }
1364
1369
 
1365
1370
  options.onError(_context8.t0); // defaulted onError
1366
1371
 
1367
- _context8.next = 40;
1372
+ _context8.next = 41;
1368
1373
  break;
1369
1374
 
1370
- case 35:
1375
+ case 36:
1371
1376
  if (!this.options.onError) {
1372
- _context8.next = 39;
1377
+ _context8.next = 40;
1373
1378
  break;
1374
1379
  }
1375
1380
 
1376
1381
  this.options.onError(_context8.t0);
1377
- _context8.next = 40;
1382
+ _context8.next = 41;
1378
1383
  break;
1379
1384
 
1380
- case 39:
1385
+ case 40:
1381
1386
  throw _context8.t0;
1382
1387
 
1383
- case 40:
1388
+ case 41:
1384
1389
  case "end":
1385
1390
  return _context8.stop();
1386
1391
  }
1387
1392
  }
1388
- }, _callee8, this, [[18, 28]]);
1393
+ }, _callee8, this, [[18, 29]]);
1389
1394
  }));
1390
1395
 
1391
1396
  function query(_x9) {
@@ -2839,7 +2844,7 @@ var CozyClient = /*#__PURE__*/function () {
2839
2844
  CozyClient.hooks = CozyClient.hooks || {};
2840
2845
  CozyClient.fetchPolicies = _policies.default; //COZY_CLIENT_VERSION_PACKAGE in replaced by babel. See babel config
2841
2846
 
2842
- CozyClient.version = "48.12.0";
2847
+ CozyClient.version = "48.13.0";
2843
2848
 
2844
2849
  _microee.default.mixin(CozyClient);
2845
2850
 
@@ -1,5 +1,4 @@
1
1
  import MockDate from 'mockdate'
2
-
3
2
  import {
4
3
  SCHEMA,
5
4
  TODO_1,
@@ -32,12 +31,14 @@ import {
32
31
  getRawQueryFromState,
33
32
  isQueryExisting,
34
33
  loadQuery,
35
- resetQuery
34
+ resetQuery,
35
+ executeQueryFromState
36
36
  } from './store'
37
37
  import { HasManyFiles, Association, HasMany } from './associations'
38
38
  import mapValues from 'lodash/mapValues'
39
39
  import FileCollection from 'cozy-stack-client/dist/FileCollection'
40
40
  import logger from './logger'
41
+
41
42
  const normalizeData = data =>
42
43
  mapValues(data, (docs, doctype) => {
43
44
  return docs.map(doc => ({
@@ -54,7 +55,8 @@ jest.mock('./store', () => ({
54
55
  ...jest.requireActual('./store'),
55
56
  isQueryExisting: jest.fn().mockReturnValue(false),
56
57
  getQueryFromState: jest.fn().mockReturnValue({}),
57
- getRawQueryFromState: jest.fn().mockReturnValue({})
58
+ getRawQueryFromState: jest.fn().mockReturnValue({}),
59
+ executeQueryFromState: jest.fn().mockReturnValue([])
58
60
  }))
59
61
 
60
62
  describe('CozyClient initialization', () => {
@@ -1228,6 +1230,7 @@ describe('CozyClient', () => {
1228
1230
  beforeEach(() => {
1229
1231
  query = Q('io.cozy.todos')
1230
1232
  fakeResponse = { data: 'FAKE!!!' }
1233
+ jest.clearAllMocks()
1231
1234
  })
1232
1235
  it('should throw an error if the option.enabled is not a boolean', async () => {
1233
1236
  await expect(
@@ -1390,6 +1393,13 @@ describe('CozyClient', () => {
1390
1393
  expect(client.requestQuery).toHaveBeenCalledTimes(1)
1391
1394
  })
1392
1395
 
1396
+ it('should handle the executeFromStore option', async () => {
1397
+ executeQueryFromState.mockReturnValueOnce([])
1398
+ await client.query(query, { executeFromStore: true })
1399
+ expect(requestHandler).toHaveBeenCalledTimes(0)
1400
+ expect(executeQueryFromState).toHaveBeenCalledTimes(1)
1401
+ })
1402
+
1393
1403
  describe('relationship with query failure', () => {
1394
1404
  beforeEach(() => {
1395
1405
  jest.spyOn(HasManyFiles, 'query').mockImplementation(() => {
@@ -28,7 +28,7 @@ var useInstanceInfo = function useInstanceInfo() {
28
28
  var diskUsageQuery = buildSettingsByIdQuery('io.cozy.settings.disk-usage');
29
29
  var diskUsageResult = (0, _useQuery.default)(diskUsageQuery.definition, diskUsageQuery.options);
30
30
  return {
31
- isLoaded: !!(0, _utils.hasQueryBeenLoaded)(instanceResult) !== null && !!(0, _utils.hasQueryBeenLoaded)(contextResult) !== null && !!(0, _utils.hasQueryBeenLoaded)(diskUsageResult) !== null,
31
+ isLoaded: (0, _utils.hasQueryBeenLoaded)(instanceResult) !== null && (0, _utils.hasQueryBeenLoaded)(contextResult) !== null && (0, _utils.hasQueryBeenLoaded)(diskUsageResult) !== null,
32
32
  instance: {
33
33
  data: instanceResult.data
34
34
  },
@@ -37,6 +37,12 @@ Object.defineProperty(exports, "receiveQueryError", {
37
37
  return _queries.receiveQueryError;
38
38
  }
39
39
  });
40
+ Object.defineProperty(exports, "executeQueryFromState", {
41
+ enumerable: true,
42
+ get: function get() {
43
+ return _queries.executeQueryFromState;
44
+ }
45
+ });
40
46
  Object.defineProperty(exports, "initMutation", {
41
47
  enumerable: true,
42
48
  get: function get() {
@@ -5,7 +5,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.QueryIDGenerator = exports.getQueryFromSlice = exports.resetQuery = exports.receiveQueryError = exports.receiveQueryResult = exports.loadQuery = exports.initQuery = exports.default = exports.updateData = exports.makeSorterFromDefinition = exports.mergeSelectorAndPartialIndex = exports.convert$gtNullSelectors = exports.sortAndLimitDocsIds = exports.isReceivingData = exports.isQueryAction = void 0;
8
+ exports.QueryIDGenerator = exports.getQueryFromSlice = exports.resetQuery = exports.receiveQueryError = exports.receiveQueryResult = exports.loadQuery = exports.initQuery = exports.default = exports.updateData = exports.makeSorterFromDefinition = exports.executeQueryFromState = exports.mergeSelectorAndPartialIndex = exports.convert$gtNullSelectors = exports.sortAndLimitDocsIds = exports.isReceivingData = exports.isQueryAction = void 0;
9
9
 
10
10
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
11
11
 
@@ -357,25 +357,59 @@ var getSelectorFilterFn = function getSelectorFilterFn(queryDefinition) {
357
357
  return null;
358
358
  }
359
359
  };
360
+ /**
361
+ * Execute the given query against the document state.
362
+ *
363
+ * @param {import('../types').DocumentsStateSlice} state - The documents state
364
+ * @param {QueryDefinition} queryDefinition - The query definition to execute
365
+ * @returns {import("../types").QueryStateData} - The returned documents from the query
366
+ */
367
+
368
+
369
+ var executeQueryFromState = function executeQueryFromState(state, queryDefinition) {
370
+ var documents = (0, _documents.getCollectionFromSlice)(state, queryDefinition.doctype);
371
+ var isSingleObjectResponse = !!queryDefinition.id;
372
+
373
+ if (!documents) {
374
+ return {
375
+ data: isSingleObjectResponse ? null : []
376
+ };
377
+ }
378
+
379
+ var res = documents.filter(makeFilterDocumentFn(queryDefinition));
380
+
381
+ if (isSingleObjectResponse) {
382
+ return {
383
+ data: res.length > 0 ? res[0] : null
384
+ };
385
+ }
386
+
387
+ return {
388
+ data: res
389
+ };
390
+ };
360
391
  /**
361
392
  *
362
393
  * Returns a predicate function that checks if a document should be
363
394
  * included in the result of the query.
364
395
  *
365
- * @param {import("../types").QueryState} query - Definition of the query
396
+ * @param {QueryDefinition} queryDefinition - Definition of the query
366
397
  * @returns {function(import("../types").CozyClientDocument): boolean} Predicate function
367
398
  */
368
399
 
369
400
 
370
- var getQueryDocumentsChecker = function getQueryDocumentsChecker(query) {
371
- var qdoctype = query.definition.doctype;
372
- var selectorFilterFn = getSelectorFilterFn(query.definition);
401
+ exports.executeQueryFromState = executeQueryFromState;
402
+
403
+ var makeFilterDocumentFn = function makeFilterDocumentFn(queryDefinition) {
404
+ var qdoctype = queryDefinition.doctype;
405
+ var selectorFilterFn = getSelectorFilterFn(queryDefinition);
373
406
  return function (datum) {
374
407
  var ddoctype = datum._type;
375
408
  if (ddoctype !== qdoctype) return false;
376
409
  if (datum._deleted) return false;
377
- if (!selectorFilterFn) return true;
378
- return !!selectorFilterFn(datum);
410
+ if (!selectorFilterFn) return true; // no selector: query all the docs
411
+
412
+ return !!selectorFilterFn(datum); // evaluate the sift function
379
413
  };
380
414
  };
381
415
 
@@ -439,7 +473,7 @@ var makeSorterFromDefinition = function makeSorterFromDefinition(definition) {
439
473
  exports.makeSorterFromDefinition = makeSorterFromDefinition;
440
474
 
441
475
  var updateData = function updateData(query, newData, documents) {
442
- var belongsToQuery = getQueryDocumentsChecker(query);
476
+ var belongsToQuery = makeFilterDocumentFn(query.definition);
443
477
  var res = (0, _mapValues.default)((0, _groupBy.default)(newData, belongsToQuery), function (docs) {
444
478
  return docs.map(_helpers.properId);
445
479
  });
@@ -7,7 +7,8 @@ import queries, {
7
7
  sortAndLimitDocsIds,
8
8
  loadQuery,
9
9
  receiveQueryError,
10
- updateData
10
+ updateData,
11
+ executeQueryFromState
11
12
  } from './queries'
12
13
  import { Q } from '../queries/dsl'
13
14
  import { TODO_1, TODO_2, TODO_3 } from '../__tests__/fixtures'
@@ -671,3 +672,121 @@ describe('updateData', () => {
671
672
  expect(updatedDataToCheck.count).toEqual(1)
672
673
  })
673
674
  })
675
+
676
+ describe('execute query from state', () => {
677
+ const docState = {
678
+ 'io.cozy.files': {
679
+ '123': {
680
+ _id: '123',
681
+ _type: 'io.cozy.files',
682
+ name: 'well',
683
+ created_at: '2024-01-01'
684
+ },
685
+ '456': {
686
+ _id: '456',
687
+ _type: 'io.cozy.files',
688
+ name: 'hello',
689
+ created_at: '2024-02-01'
690
+ },
691
+ '789': {
692
+ _id: '789',
693
+ _type: 'io.cozy.files',
694
+ name: 'there',
695
+ created_at: '2024-03-01'
696
+ }
697
+ }
698
+ }
699
+ it('should get the correct filtered results from state thanks to selector', () => {
700
+ const query1 = {
701
+ doctype: 'io.cozy.files',
702
+ selector: {
703
+ created_at: {
704
+ $gt: '2024-01-31'
705
+ }
706
+ }
707
+ }
708
+ const res1 = executeQueryFromState(docState, query1)
709
+ expect(res1.data.length).toEqual(2)
710
+ expect(res1.data[0]).toEqual(docState['io.cozy.files']['456'])
711
+ expect(res1.data[1]).toEqual(docState['io.cozy.files']['789'])
712
+
713
+ const query2 = {
714
+ doctype: 'io.cozy.files',
715
+ selector: {
716
+ name: 'well'
717
+ }
718
+ }
719
+ const res2 = executeQueryFromState(docState, query2)
720
+ expect(res2.data.length).toEqual(1)
721
+ expect(res2.data[0]).toEqual(docState['io.cozy.files']['123'])
722
+
723
+ const query3 = {
724
+ doctype: 'io.cozy.files',
725
+ selector: {
726
+ created_at: {
727
+ $gt: '2024-01-31'
728
+ },
729
+ name: 'hello'
730
+ }
731
+ }
732
+ const res3 = executeQueryFromState(docState, query3)
733
+ expect(res3.data.length).toEqual(1)
734
+ expect(res3.data[0]).toEqual(docState['io.cozy.files']['456'])
735
+
736
+ const query4 = {
737
+ doctype: 'io.cozy.files',
738
+ selector: {
739
+ created_at: {
740
+ $gt: '2024-01-30',
741
+ $lt: '2024-01-31'
742
+ }
743
+ }
744
+ }
745
+ const res4 = executeQueryFromState(docState, query4)
746
+ expect(res4.data.length).toEqual(0)
747
+ })
748
+
749
+ it('should get the correct filtered results from state thanks to id', () => {
750
+ const query1 = {
751
+ doctype: 'io.cozy.files',
752
+ id: '123'
753
+ }
754
+ const res1 = executeQueryFromState(docState, query1)
755
+ expect(res1.data).toEqual(docState['io.cozy.files']['123'])
756
+
757
+ const query2 = {
758
+ doctype: 'io.cozy.files',
759
+ ids: ['123', '789']
760
+ }
761
+ const res2 = executeQueryFromState(docState, query2)
762
+ expect(res2.data.length).toEqual(2)
763
+ expect(res2.data[0]).toEqual(docState['io.cozy.files']['123'])
764
+ expect(res2.data[1]).toEqual(docState['io.cozy.files']['789'])
765
+
766
+ const query3 = {
767
+ doctype: 'io.cozy.files',
768
+ id: '-1'
769
+ }
770
+ const res3 = executeQueryFromState(docState, query3)
771
+ expect(res3.data).toEqual(null)
772
+ })
773
+
774
+ it('should get all the docs from state for the doctype when no filter', () => {
775
+ const query1 = {
776
+ doctype: 'io.cozy.files'
777
+ }
778
+ const res1 = executeQueryFromState(docState, query1)
779
+ expect(res1.data.length).toEqual(3)
780
+ })
781
+
782
+ it('should correctly return when no doc is available', () => {
783
+ const res1 = executeQueryFromState({}, { doctype: 'io.cozy.files' })
784
+ expect(res1.data).toEqual([])
785
+
786
+ const res2 = executeQueryFromState(
787
+ {},
788
+ { doctype: 'io.cozy.files', id: '123' }
789
+ )
790
+ expect(res2.data).toEqual(null)
791
+ })
792
+ })
package/dist/types.js CHANGED
@@ -297,6 +297,7 @@ var _dsl = require("./queries/dsl");
297
297
  * @property {boolean} [singleDocData] - If true, the "data" returned will be
298
298
  * a single doc instead of an array for single doc queries. Defaults to false for backward
299
299
  * compatibility but will be set to true in the future.
300
+ * @property {boolean} [executeFromStore=false] - If set to true, the query will be run directly on the current store's state
300
301
  */
301
302
 
302
303
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-client",
3
- "version": "48.12.0",
3
+ "version": "48.13.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.node.js",
6
6
  "browser": "dist/index.js",
@@ -75,5 +75,5 @@
75
75
  "react-native-ios11-devicecheck": "https://github.com/cozy/react-native-devicecheck#app-attest-v0.1"
76
76
  },
77
77
  "sideEffects": false,
78
- "gitHead": "4b5b757b6b1cc8029453eab2d981214be6f33256"
78
+ "gitHead": "f010d354bd83022be6bfb94f9aff58bcba10f1e5"
79
79
  }
@@ -422,7 +422,7 @@ declare class CozyClient {
422
422
  * @param {import("./types").QueryOptions} [options] - Options
423
423
  * @returns {Promise<import("./types").QueryResult>}
424
424
  */
425
- query(queryDefinition: QueryDefinition, { update, ...options }?: import("./types").QueryOptions): Promise<import("./types").QueryResult>;
425
+ query(queryDefinition: QueryDefinition, { update, executeFromStore, ...options }?: import("./types").QueryOptions): Promise<import("./types").QueryResult>;
426
426
  /**
427
427
  * Will fetch all documents for a `queryDefinition`, automatically fetching more
428
428
  * documents if the total of documents is superior to the pagination limit. Can
@@ -25,5 +25,5 @@ declare function combinedReducer(state: {
25
25
  export function resetState(): {
26
26
  type: string;
27
27
  };
28
- export { initQuery, loadQuery, resetQuery, receiveQueryResult, receiveQueryError } from "./queries";
28
+ export { initQuery, loadQuery, resetQuery, receiveQueryResult, receiveQueryError, executeQueryFromState } from "./queries";
29
29
  export { initMutation, receiveMutationResult, receiveMutationError } from "./mutations";
@@ -6,6 +6,7 @@ export function sortAndLimitDocsIds(queryState: import("../types").QueryState, d
6
6
  }): Array<string>;
7
7
  export function convert$gtNullSelectors(selector: any): object;
8
8
  export function mergeSelectorAndPartialIndex(queryDefinition: object): object;
9
+ export function executeQueryFromState(state: import('../types').DocumentsStateSlice, queryDefinition: QueryDefinition): import("../types").QueryStateData;
9
10
  export function makeSorterFromDefinition(definition: QueryDefinition): (arg0: Array<import("../types").CozyClientDocument>) => Array<import("../types").CozyClientDocument>;
10
11
  export function updateData(query: import("../types").QueryState, newData: Array<import("../types").CozyClientDocument>, documents: import("../types").DocumentsStateSlice): import("../types").QueryState;
11
12
  export default queries;
package/types/types.d.ts CHANGED
@@ -542,6 +542,10 @@ export type QueryOptions = {
542
542
  * compatibility but will be set to true in the future.
543
543
  */
544
544
  singleDocData?: boolean;
545
+ /**
546
+ * - If set to true, the query will be run directly on the current store's state
547
+ */
548
+ executeFromStore?: boolean;
545
549
  };
546
550
  export type Query = {
547
551
  definition: QueryDefinition;