cozy-client 59.4.0 → 60.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.
- package/dist/CozyClient.js +37 -37
- package/dist/CozyClient.spec.js +69 -14
- package/dist/Query.spec.jsx +2 -0
- package/dist/__tests__/fixtures.js +17 -1
- package/dist/hooks/useQuery.js +3 -1
- package/dist/hooks/useQuery.spec.jsx +3 -1
- package/dist/store/__snapshots__/queries.spec.js.snap +40 -0
- package/dist/store/queries.js +3 -1
- package/dist/store/selector.js +138 -0
- package/dist/store/selector.spec.js +238 -0
- package/dist/store.spec.js +3 -1
- package/dist/types.js +8 -2
- package/package.json +3 -3
- package/types/CozyClient.d.ts +9 -9
- package/types/__tests__/fixtures.d.ts +39 -17
- package/types/store/selector.d.ts +1 -0
- package/types/types.d.ts +15 -2
package/dist/CozyClient.js
CHANGED
|
@@ -152,7 +152,7 @@ var DOC_UPDATE = 'update';
|
|
|
152
152
|
* @property {string} [uri]
|
|
153
153
|
* @property {object} [stackClient]
|
|
154
154
|
* @property {boolean} [warningForCustomHandlers]
|
|
155
|
-
* @property {boolean} [autoHydrate]
|
|
155
|
+
* @property {boolean} [autoHydrate] - If set to true, all documents will be hydrated w.r.t. the provided schema's relationships, even if the relationship does not exist on the doc.
|
|
156
156
|
* @property {boolean} [backgroundFetching] - If set to true, backgroundFetching will be enabled by default on every query. Meaning that, when the fetchStatus has already been loaded, it won't be updated during future fetches. Instead, a `isFetching` attribute will be used to indicate when background fetching is started.
|
|
157
157
|
* @property {object} [oauth]
|
|
158
158
|
* @property {Function} [onTokenRefresh]
|
|
@@ -163,8 +163,7 @@ var DOC_UPDATE = 'update';
|
|
|
163
163
|
* @property {import("./types").AppMetadata} [appMetadata] - Metadata about the application that will be used in ensureCozyMetadata
|
|
164
164
|
* @property {import("./types").ClientCapabilities} [capabilities] - Capabilities sent by the stack
|
|
165
165
|
* @property {boolean} [useCustomStore=false] - If set to true, the client will not instantiate a Redux store automatically. Use this if you want to merge cozy-client's store with your own redux store. Note will have to call `setStore` eventually. See [here](https://docs.cozy.io/en/cozy-client/react-integration/#1b-use-your-own-redux-store) for more information.
|
|
166
|
-
* @property {boolean} [disableStoreForQueries=false] - If set to true, the client will not leverage the redux store to execute queries and store data.
|
|
167
|
-
|
|
166
|
+
* @property {boolean} [disableStoreForQueries=false] - If set to true, the client will not leverage the redux store to execute queries and store data.
|
|
168
167
|
* @property {import('./performances/types').PerformanceAPI} [performanceApi] - The performance API that can be used to measure performances
|
|
169
168
|
*/
|
|
170
169
|
|
|
@@ -317,6 +316,7 @@ var CozyClient = /*#__PURE__*/function () {
|
|
|
317
316
|
this.ensureStore();
|
|
318
317
|
}
|
|
319
318
|
|
|
319
|
+
this.autoHydrate = _options.autoHydrate || false;
|
|
320
320
|
this.disableStoreForQueries = _options.disableStoreForQueries || false;
|
|
321
321
|
}
|
|
322
322
|
/**
|
|
@@ -2017,7 +2017,7 @@ var CozyClient = /*#__PURE__*/function () {
|
|
|
2017
2017
|
if (queryDef instanceof _dsl.QueryDefinition) {
|
|
2018
2018
|
definitions.push(queryDef);
|
|
2019
2019
|
} else {
|
|
2020
|
-
documents.push(
|
|
2020
|
+
documents.push(doc);
|
|
2021
2021
|
}
|
|
2022
2022
|
} catch (_unused) {// eslint-disable-next-line
|
|
2023
2023
|
// We do not crash completely if one the relationship behaves badly and
|
|
@@ -2159,10 +2159,6 @@ var CozyClient = /*#__PURE__*/function () {
|
|
|
2159
2159
|
value: function hydrateDocuments(doctype, documents) {
|
|
2160
2160
|
var _this8 = this;
|
|
2161
2161
|
|
|
2162
|
-
if (this.options.autoHydrate === false) {
|
|
2163
|
-
return documents;
|
|
2164
|
-
}
|
|
2165
|
-
|
|
2166
2162
|
var schema = this.schema.getDoctypeSchema(doctype);
|
|
2167
2163
|
var relationships = schema.relationships;
|
|
2168
2164
|
|
|
@@ -2193,29 +2189,22 @@ var CozyClient = /*#__PURE__*/function () {
|
|
|
2193
2189
|
}
|
|
2194
2190
|
|
|
2195
2191
|
var schema = schemaArg || this.schema.getDoctypeSchema(document._type);
|
|
2196
|
-
|
|
2192
|
+
var hydratedRelationships = this.hydrateRelationships(document, schema.relationships);
|
|
2193
|
+
return _objectSpread(_objectSpread({}, document), hydratedRelationships);
|
|
2197
2194
|
}
|
|
2198
2195
|
}, {
|
|
2199
2196
|
key: "hydrateRelationships",
|
|
2200
2197
|
value: function hydrateRelationships(document, schemaRelationships) {
|
|
2198
|
+
var _this9 = this;
|
|
2199
|
+
|
|
2201
2200
|
var methods = this.getRelationshipStoreAccessors();
|
|
2202
2201
|
return (0, _mapValues.default)(schemaRelationships, function (assoc, name) {
|
|
2203
|
-
|
|
2204
|
-
});
|
|
2205
|
-
}
|
|
2206
|
-
/**
|
|
2207
|
-
* Creates (locally) a new document for the given doctype.
|
|
2208
|
-
* This document is hydrated : its relationships are there
|
|
2209
|
-
* and working.
|
|
2210
|
-
*/
|
|
2202
|
+
var _this9$options, _document$relationshi;
|
|
2211
2203
|
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
_type: doctype
|
|
2217
|
-
};
|
|
2218
|
-
return this.hydrateDocument(obj);
|
|
2204
|
+
if ((_this9$options = _this9.options) !== null && _this9$options !== void 0 && _this9$options.autoHydrate || (_document$relationshi = document.relationships) !== null && _document$relationshi !== void 0 && _document$relationshi[name]) {
|
|
2205
|
+
return (0, _associations.create)(document, assoc, methods);
|
|
2206
|
+
}
|
|
2207
|
+
});
|
|
2219
2208
|
}
|
|
2220
2209
|
}, {
|
|
2221
2210
|
key: "generateRandomId",
|
|
@@ -2243,20 +2232,20 @@ var CozyClient = /*#__PURE__*/function () {
|
|
|
2243
2232
|
}, {
|
|
2244
2233
|
key: "getRelationshipStoreAccessors",
|
|
2245
2234
|
value: function getRelationshipStoreAccessors() {
|
|
2246
|
-
var
|
|
2235
|
+
var _this10 = this;
|
|
2247
2236
|
|
|
2248
2237
|
if (!this.storeAccesors) {
|
|
2249
2238
|
this.storeAccessors = {
|
|
2250
2239
|
get: this.getDocumentFromState.bind(this),
|
|
2251
2240
|
save: function save(document, opts) {
|
|
2252
|
-
return
|
|
2241
|
+
return _this10.save.call(_this10, document, opts);
|
|
2253
2242
|
},
|
|
2254
2243
|
dispatch: this.dispatch.bind(this),
|
|
2255
2244
|
query: function query(def, opts) {
|
|
2256
|
-
return
|
|
2245
|
+
return _this10.query.call(_this10, def, opts);
|
|
2257
2246
|
},
|
|
2258
2247
|
mutate: function mutate(def, opts) {
|
|
2259
|
-
return
|
|
2248
|
+
return _this10.mutate.call(_this10, def, opts);
|
|
2260
2249
|
}
|
|
2261
2250
|
};
|
|
2262
2251
|
}
|
|
@@ -2307,7 +2296,7 @@ var CozyClient = /*#__PURE__*/function () {
|
|
|
2307
2296
|
*
|
|
2308
2297
|
* @param {string} id - Id of the query (set via Query.props.as)
|
|
2309
2298
|
* @param {object} options - Options
|
|
2310
|
-
* @param {boolean} [options.hydrated] - Whether documents should be returned already hydrated
|
|
2299
|
+
* @param {boolean} [options.hydrated] - Whether documents should be returned already hydrated
|
|
2311
2300
|
* @param {object} [options.singleDocData] - If true, the "data" returned will be
|
|
2312
2301
|
* a single doc instead of an array for single doc queries. Defaults to false for backward
|
|
2313
2302
|
* compatibility but will be set to true in the future.
|
|
@@ -2319,22 +2308,33 @@ var CozyClient = /*#__PURE__*/function () {
|
|
|
2319
2308
|
key: "getQueryFromState",
|
|
2320
2309
|
value: function getQueryFromState(id) {
|
|
2321
2310
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
2322
|
-
var
|
|
2323
|
-
|
|
2311
|
+
var singleDocData = options.singleDocData || false; // `hydrated` has priority over global `autoHydrate`
|
|
2312
|
+
|
|
2313
|
+
var shouldHydrate = options.hydrated !== undefined ? options.hydrated : this.options.autoHydrate;
|
|
2324
2314
|
|
|
2325
2315
|
try {
|
|
2316
|
+
var _this$schema$getDocty;
|
|
2317
|
+
|
|
2326
2318
|
var queryResults = (0, _store.getQueryFromState)(this.store.getState(), id);
|
|
2327
2319
|
var doctype = queryResults.definition && queryResults.definition.doctype;
|
|
2328
2320
|
var isSingleDocQuery = queryResults.definition && queryResults.definition.id;
|
|
2329
2321
|
|
|
2330
|
-
if (!
|
|
2322
|
+
if (!shouldHydrate && !singleDocData) {
|
|
2331
2323
|
// Early return let's us preserve reference equality in the simple case
|
|
2332
2324
|
return queryResults;
|
|
2333
2325
|
}
|
|
2334
2326
|
|
|
2335
|
-
var
|
|
2327
|
+
var hydratedData = shouldHydrate && doctype ? this.hydrateDocuments(doctype, queryResults.data) : queryResults.data;
|
|
2328
|
+
var relationships = (_this$schema$getDocty = this.schema.getDoctypeSchema(doctype)) === null || _this$schema$getDocty === void 0 ? void 0 : _this$schema$getDocty.relationships;
|
|
2329
|
+
var relationshipNames = relationships ? Object.keys(relationships) : null; // The `data` array contains the hydrated data with the relationships, if any.
|
|
2330
|
+
// The `storeData` array contains the documents from the store: this is useful to preserve
|
|
2331
|
+
// referential equality, to be later evaluated to determine whether or not the
|
|
2332
|
+
// documents had changed.
|
|
2333
|
+
|
|
2336
2334
|
return _objectSpread(_objectSpread({}, queryResults), {}, {
|
|
2337
|
-
data: isSingleDocQuery && singleDocData ?
|
|
2335
|
+
data: isSingleDocQuery && singleDocData ? hydratedData[0] : hydratedData,
|
|
2336
|
+
storeData: isSingleDocQuery && singleDocData ? queryResults.data[0] : queryResults.data,
|
|
2337
|
+
relationshipNames: relationshipNames
|
|
2338
2338
|
});
|
|
2339
2339
|
} catch (e) {
|
|
2340
2340
|
_logger.default.warn("Could not get query from state. queryId: ".concat(id, ", error: ").concat(e.message));
|
|
@@ -2838,7 +2838,7 @@ var CozyClient = /*#__PURE__*/function () {
|
|
|
2838
2838
|
}, {
|
|
2839
2839
|
key: "setData",
|
|
2840
2840
|
value: function setData(data) {
|
|
2841
|
-
var
|
|
2841
|
+
var _this11 = this;
|
|
2842
2842
|
|
|
2843
2843
|
this.ensureStore();
|
|
2844
2844
|
Object.entries(data).forEach(function (_ref9) {
|
|
@@ -2846,7 +2846,7 @@ var CozyClient = /*#__PURE__*/function () {
|
|
|
2846
2846
|
doctype = _ref10[0],
|
|
2847
2847
|
data = _ref10[1];
|
|
2848
2848
|
|
|
2849
|
-
|
|
2849
|
+
_this11.dispatch((0, _store.receiveQueryResult)(null, {
|
|
2850
2850
|
data: data
|
|
2851
2851
|
}));
|
|
2852
2852
|
});
|
|
@@ -3264,7 +3264,7 @@ var CozyClient = /*#__PURE__*/function () {
|
|
|
3264
3264
|
CozyClient.hooks = CozyClient.hooks || {};
|
|
3265
3265
|
CozyClient.fetchPolicies = _policies.default; //COZY_CLIENT_VERSION_PACKAGE in replaced by babel. See babel config
|
|
3266
3266
|
|
|
3267
|
-
CozyClient.version = "
|
|
3267
|
+
CozyClient.version = "60.0.0";
|
|
3268
3268
|
|
|
3269
3269
|
_microee.default.mixin(CozyClient);
|
|
3270
3270
|
|
package/dist/CozyClient.spec.js
CHANGED
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
APP_VERSION,
|
|
12
12
|
SOURCE_ACCOUNT_ID,
|
|
13
13
|
SOURCE_ACCOUNT_IDENTIFIER,
|
|
14
|
-
FILE_1
|
|
14
|
+
FILE_1,
|
|
15
|
+
TODO_WITH_NONEXISTING_RELATIONSHIP
|
|
15
16
|
} from './__tests__/fixtures'
|
|
16
17
|
import { withIgnoreConsoleWarn } from './__tests__/console'
|
|
17
18
|
|
|
@@ -34,7 +35,7 @@ import {
|
|
|
34
35
|
resetQuery,
|
|
35
36
|
executeQueryFromState
|
|
36
37
|
} from './store'
|
|
37
|
-
import { HasManyFiles, Association
|
|
38
|
+
import { HasManyFiles, Association } from './associations'
|
|
38
39
|
import mapValues from 'lodash/mapValues'
|
|
39
40
|
import FileCollection from 'cozy-stack-client/dist/FileCollection'
|
|
40
41
|
import logger from './logger'
|
|
@@ -1016,7 +1017,16 @@ describe('CozyClient', () => {
|
|
|
1016
1017
|
const NEW_TODO = {
|
|
1017
1018
|
_type: 'io.cozy.todos',
|
|
1018
1019
|
label: 'Buy RAM',
|
|
1019
|
-
|
|
1020
|
+
relationships: {
|
|
1021
|
+
attachments: {
|
|
1022
|
+
data: [
|
|
1023
|
+
{
|
|
1024
|
+
_type: 'io.cozy.files',
|
|
1025
|
+
_id: 1
|
|
1026
|
+
}
|
|
1027
|
+
]
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1020
1030
|
}
|
|
1021
1031
|
const EXPECTED_CREATED_TODO = { _id: 67890, ...NEW_TODO }
|
|
1022
1032
|
const mutation = client.getDocumentSavePlan(NEW_TODO, {
|
|
@@ -1040,9 +1050,14 @@ describe('CozyClient', () => {
|
|
|
1040
1050
|
id: 67890,
|
|
1041
1051
|
type: 'io.cozy.files'
|
|
1042
1052
|
}
|
|
1043
|
-
]
|
|
1053
|
+
],
|
|
1054
|
+
relationships: { icons: [{ _id: 67890, _type: 'io.cozy.files' }] }
|
|
1055
|
+
}
|
|
1056
|
+
const EXPECTED_CREATED_FILE = {
|
|
1057
|
+
_id: 12345,
|
|
1058
|
+
_type: 'io.cozy.files',
|
|
1059
|
+
relationships: { icons: [{ _id: 67890, _type: 'io.cozy.files' }] }
|
|
1044
1060
|
}
|
|
1045
|
-
const EXPECTED_CREATED_FILE = { _id: 12345, _type: 'io.cozy.files' }
|
|
1046
1061
|
const mutation = client.getDocumentSavePlan(NEW_FILE, {
|
|
1047
1062
|
icons: [
|
|
1048
1063
|
{
|
|
@@ -1907,14 +1922,6 @@ describe('CozyClient', () => {
|
|
|
1907
1922
|
)
|
|
1908
1923
|
.shift()
|
|
1909
1924
|
expect(doc.attachments).toBeInstanceOf(HasManyFiles)
|
|
1910
|
-
expect(doc.authors).toBeInstanceOf(HasMany)
|
|
1911
|
-
})
|
|
1912
|
-
|
|
1913
|
-
it('makes new documents', () => {
|
|
1914
|
-
const newTodo = client.makeNewDocument('io.cozy.todos')
|
|
1915
|
-
expect(newTodo._type).toBe('io.cozy.todos')
|
|
1916
|
-
expect(newTodo.attachments).not.toBe(undefined)
|
|
1917
|
-
expect(newTodo.attachments instanceof HasManyFiles).toBe(true)
|
|
1918
1925
|
})
|
|
1919
1926
|
|
|
1920
1927
|
it('should not fail on null (when getting absent documents from the store)', () => {
|
|
@@ -1923,6 +1930,53 @@ describe('CozyClient', () => {
|
|
|
1923
1930
|
.shift()
|
|
1924
1931
|
expect(doc).toBe(null)
|
|
1925
1932
|
})
|
|
1933
|
+
|
|
1934
|
+
it('should hydrate relationships if the relationship exists', () => {
|
|
1935
|
+
const resp = client.hydrateDocument(TODO_WITH_AUTHOR)
|
|
1936
|
+
expect(resp.authors).not.toBe(undefined)
|
|
1937
|
+
expect(resp.attachments).toBe(undefined)
|
|
1938
|
+
})
|
|
1939
|
+
|
|
1940
|
+
it('should not hydrate relationships if the relationship does not exist', () => {
|
|
1941
|
+
const resp = client.hydrateDocument(TODO_WITH_NONEXISTING_RELATIONSHIP)
|
|
1942
|
+
expect(resp.authors).toBe(undefined)
|
|
1943
|
+
expect(resp.attachments).toBe(undefined)
|
|
1944
|
+
expect(resp.broken).toBe(undefined)
|
|
1945
|
+
})
|
|
1946
|
+
|
|
1947
|
+
it('should not hydrate relationships if not specified', () => {
|
|
1948
|
+
getQueryFromState.mockReturnValueOnce({
|
|
1949
|
+
definition: { doctype: 'io.cozy.todos' },
|
|
1950
|
+
data: [TODO_WITH_AUTHOR]
|
|
1951
|
+
})
|
|
1952
|
+
const resp = client.getQueryFromState('allTodos')
|
|
1953
|
+
expect(resp.data[0].authors).toBe(undefined)
|
|
1954
|
+
expect(resp.data[0].attachments).toBe(undefined)
|
|
1955
|
+
})
|
|
1956
|
+
|
|
1957
|
+
it('should hydrate relationships only if the relationship does exist ', () => {
|
|
1958
|
+
getQueryFromState.mockReturnValueOnce({
|
|
1959
|
+
definition: { doctype: 'io.cozy.todos' },
|
|
1960
|
+
data: [TODO_WITH_AUTHOR]
|
|
1961
|
+
})
|
|
1962
|
+
const resp = client.getQueryFromState('allTodos', { hydrated: true })
|
|
1963
|
+
expect(resp.data[0].authors).not.toBe(undefined)
|
|
1964
|
+
expect(resp.data[0].attachments).toBe(undefined)
|
|
1965
|
+
})
|
|
1966
|
+
|
|
1967
|
+
it('should hydrate relationships if the relationship does not exist but hydration is forced', () => {
|
|
1968
|
+
const newClient = new CozyClient({
|
|
1969
|
+
schema: SCHEMA,
|
|
1970
|
+
autoHydrate: true
|
|
1971
|
+
})
|
|
1972
|
+
getQueryFromState.mockReturnValueOnce({
|
|
1973
|
+
definition: { doctype: 'io.cozy.todos' },
|
|
1974
|
+
data: [TODO_WITH_AUTHOR]
|
|
1975
|
+
})
|
|
1976
|
+
const resp = newClient.getQueryFromState('allTodos')
|
|
1977
|
+
expect(resp.data[0].authors).not.toBe(undefined)
|
|
1978
|
+
expect(resp.data[0].attachments).not.toBe(undefined)
|
|
1979
|
+
})
|
|
1926
1980
|
})
|
|
1927
1981
|
|
|
1928
1982
|
describe('serialization', () => {
|
|
@@ -2122,7 +2176,8 @@ describe('file creation', () => {
|
|
|
2122
2176
|
.fn()
|
|
2123
2177
|
.mockResolvedValueOnce({
|
|
2124
2178
|
data: {
|
|
2125
|
-
_id: '1337'
|
|
2179
|
+
_id: '1337',
|
|
2180
|
+
relationships: { icons: [{ _id: 1, _type: 'io.cozy.files' }] }
|
|
2126
2181
|
}
|
|
2127
2182
|
})
|
|
2128
2183
|
.mockResolvedValueOnce({
|
package/dist/Query.spec.jsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.SCHEMA = exports.SOURCE_ACCOUNT_IDENTIFIER = exports.SOURCE_ACCOUNT_ID = exports.DOCTYPE_VERSION = exports.APP_VERSION = exports.APP_NAME = exports.FILE_2 = exports.FILE_1 = exports.TODO_WITH_AUTHOR = exports.AUTHORS = exports.TODO_WITH_RELATION = exports.TODOS = exports.TODO_4 = exports.TODO_3 = exports.TODO_2 = exports.TODO_1 = void 0;
|
|
6
|
+
exports.SCHEMA = exports.SOURCE_ACCOUNT_IDENTIFIER = exports.SOURCE_ACCOUNT_ID = exports.DOCTYPE_VERSION = exports.APP_VERSION = exports.APP_NAME = exports.FILE_2 = exports.FILE_1 = exports.TODO_WITH_NONEXISTING_RELATIONSHIP = exports.TODO_WITH_AUTHOR = exports.AUTHORS = exports.TODO_WITH_RELATION = exports.TODOS = exports.TODO_4 = exports.TODO_3 = exports.TODO_2 = exports.TODO_1 = void 0;
|
|
7
7
|
var TODO_1 = {
|
|
8
8
|
_id: 'todo_1',
|
|
9
9
|
_rev: '4-43bdf4f7123f4d748645ba9536a46daa',
|
|
@@ -88,6 +88,22 @@ var TODO_WITH_AUTHOR = {
|
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
90
|
exports.TODO_WITH_AUTHOR = TODO_WITH_AUTHOR;
|
|
91
|
+
var TODO_WITH_NONEXISTING_RELATIONSHIP = {
|
|
92
|
+
_id: 5,
|
|
93
|
+
_rev: '1-7f02bbb542fb4eb0805b130a80b9be1a',
|
|
94
|
+
_type: 'io.cozy.todos',
|
|
95
|
+
label: 'Todo with several authors',
|
|
96
|
+
relationships: {
|
|
97
|
+
broken: {
|
|
98
|
+
doctype: 'io.cozy.persons',
|
|
99
|
+
data: [{
|
|
100
|
+
_type: 'io.cozy.persons',
|
|
101
|
+
_id: 1
|
|
102
|
+
}]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
exports.TODO_WITH_NONEXISTING_RELATIONSHIP = TODO_WITH_NONEXISTING_RELATIONSHIP;
|
|
91
107
|
var FILE_1 = {
|
|
92
108
|
_id: 1,
|
|
93
109
|
_rev: '3-bd9bb7286a094842b954d9e86429090d',
|
package/dist/hooks/useQuery.js
CHANGED
|
@@ -25,6 +25,8 @@ var _context = require("../context");
|
|
|
25
25
|
|
|
26
26
|
var _dsl = require("../queries/dsl");
|
|
27
27
|
|
|
28
|
+
var _selector = require("../store/selector");
|
|
29
|
+
|
|
28
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; }
|
|
29
31
|
|
|
30
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; }
|
|
@@ -83,7 +85,7 @@ var useQuery = function useQuery(queryDefinition, options) {
|
|
|
83
85
|
hydrated: (0, _get.default)(options, 'hydrated', true),
|
|
84
86
|
singleDocData: (0, _get.default)(options, 'singleDocData', false)
|
|
85
87
|
});
|
|
86
|
-
});
|
|
88
|
+
}, _selector.equalityCheckForQuery);
|
|
87
89
|
(0, _react.useEffect)(function () {
|
|
88
90
|
if (enabled === false) {
|
|
89
91
|
return;
|
|
@@ -107,7 +107,7 @@ describe('use query', () => {
|
|
|
107
107
|
})
|
|
108
108
|
|
|
109
109
|
expect(Object.keys(current)).toEqual(
|
|
110
|
-
expect.
|
|
110
|
+
expect.arrayContaining([
|
|
111
111
|
'id',
|
|
112
112
|
'definition',
|
|
113
113
|
'fetchStatus',
|
|
@@ -120,6 +120,8 @@ describe('use query', () => {
|
|
|
120
120
|
'count',
|
|
121
121
|
'fetchedPagesCount',
|
|
122
122
|
'data',
|
|
123
|
+
'storeData',
|
|
124
|
+
'relationshipNames',
|
|
123
125
|
'bookmark',
|
|
124
126
|
'options',
|
|
125
127
|
'fetchMore',
|
|
@@ -32,6 +32,8 @@ Object {
|
|
|
32
32
|
"lastFetch": null,
|
|
33
33
|
"lastUpdate": null,
|
|
34
34
|
"options": null,
|
|
35
|
+
"relationshipNames": Array [],
|
|
36
|
+
"storeData": Array [],
|
|
35
37
|
},
|
|
36
38
|
}
|
|
37
39
|
`;
|
|
@@ -71,6 +73,8 @@ Object {
|
|
|
71
73
|
"lastFetch": 1337,
|
|
72
74
|
"lastUpdate": 1337,
|
|
73
75
|
"options": null,
|
|
76
|
+
"relationshipNames": Array [],
|
|
77
|
+
"storeData": Array [],
|
|
74
78
|
},
|
|
75
79
|
}
|
|
76
80
|
`;
|
|
@@ -109,6 +113,8 @@ Object {
|
|
|
109
113
|
"lastFetch": 1337,
|
|
110
114
|
"lastUpdate": 1337,
|
|
111
115
|
"options": null,
|
|
116
|
+
"relationshipNames": Array [],
|
|
117
|
+
"storeData": Array [],
|
|
112
118
|
},
|
|
113
119
|
"b": Object {
|
|
114
120
|
"bookmark": null,
|
|
@@ -147,6 +153,8 @@ Object {
|
|
|
147
153
|
"lastFetch": null,
|
|
148
154
|
"lastUpdate": 1337,
|
|
149
155
|
"options": null,
|
|
156
|
+
"relationshipNames": Array [],
|
|
157
|
+
"storeData": Array [],
|
|
150
158
|
},
|
|
151
159
|
}
|
|
152
160
|
`;
|
|
@@ -185,6 +193,8 @@ Object {
|
|
|
185
193
|
"lastFetch": 1337,
|
|
186
194
|
"lastUpdate": 1337,
|
|
187
195
|
"options": null,
|
|
196
|
+
"relationshipNames": Array [],
|
|
197
|
+
"storeData": Array [],
|
|
188
198
|
},
|
|
189
199
|
"b": Object {
|
|
190
200
|
"bookmark": null,
|
|
@@ -220,6 +230,8 @@ Object {
|
|
|
220
230
|
"lastFetch": null,
|
|
221
231
|
"lastUpdate": 1337,
|
|
222
232
|
"options": null,
|
|
233
|
+
"relationshipNames": Array [],
|
|
234
|
+
"storeData": Array [],
|
|
223
235
|
},
|
|
224
236
|
}
|
|
225
237
|
`;
|
|
@@ -264,6 +276,8 @@ Object {
|
|
|
264
276
|
"lastFetch": 1337,
|
|
265
277
|
"lastUpdate": 1337,
|
|
266
278
|
"options": null,
|
|
279
|
+
"relationshipNames": Array [],
|
|
280
|
+
"storeData": Array [],
|
|
267
281
|
},
|
|
268
282
|
"b": Object {
|
|
269
283
|
"bookmark": null,
|
|
@@ -297,6 +311,8 @@ Object {
|
|
|
297
311
|
"lastFetch": null,
|
|
298
312
|
"lastUpdate": null,
|
|
299
313
|
"options": null,
|
|
314
|
+
"relationshipNames": Array [],
|
|
315
|
+
"storeData": Array [],
|
|
300
316
|
},
|
|
301
317
|
}
|
|
302
318
|
`;
|
|
@@ -336,6 +352,8 @@ Object {
|
|
|
336
352
|
"lastFetch": 1337,
|
|
337
353
|
"lastUpdate": 1337,
|
|
338
354
|
"options": null,
|
|
355
|
+
"relationshipNames": Array [],
|
|
356
|
+
"storeData": Array [],
|
|
339
357
|
},
|
|
340
358
|
"b": Object {
|
|
341
359
|
"bookmark": null,
|
|
@@ -370,6 +388,8 @@ Object {
|
|
|
370
388
|
"lastFetch": null,
|
|
371
389
|
"lastUpdate": 1337,
|
|
372
390
|
"options": null,
|
|
391
|
+
"relationshipNames": Array [],
|
|
392
|
+
"storeData": Array [],
|
|
373
393
|
},
|
|
374
394
|
}
|
|
375
395
|
`;
|
|
@@ -408,6 +428,8 @@ Object {
|
|
|
408
428
|
"lastFetch": 1337,
|
|
409
429
|
"lastUpdate": 1337,
|
|
410
430
|
"options": null,
|
|
431
|
+
"relationshipNames": Array [],
|
|
432
|
+
"storeData": Array [],
|
|
411
433
|
},
|
|
412
434
|
"b": Object {
|
|
413
435
|
"bookmark": null,
|
|
@@ -445,6 +467,8 @@ Object {
|
|
|
445
467
|
"add": false,
|
|
446
468
|
},
|
|
447
469
|
},
|
|
470
|
+
"relationshipNames": Array [],
|
|
471
|
+
"storeData": Array [],
|
|
448
472
|
},
|
|
449
473
|
}
|
|
450
474
|
`;
|
|
@@ -481,6 +505,8 @@ Object {
|
|
|
481
505
|
"lastFetch": null,
|
|
482
506
|
"lastUpdate": null,
|
|
483
507
|
"options": null,
|
|
508
|
+
"relationshipNames": Array [],
|
|
509
|
+
"storeData": Array [],
|
|
484
510
|
},
|
|
485
511
|
"b": Object {
|
|
486
512
|
"bookmark": null,
|
|
@@ -512,6 +538,8 @@ Object {
|
|
|
512
538
|
"lastFetch": 1337,
|
|
513
539
|
"lastUpdate": 1337,
|
|
514
540
|
"options": null,
|
|
541
|
+
"relationshipNames": Array [],
|
|
542
|
+
"storeData": Array [],
|
|
515
543
|
},
|
|
516
544
|
}
|
|
517
545
|
`;
|
|
@@ -550,6 +578,8 @@ Object {
|
|
|
550
578
|
"lastFetch": 1337,
|
|
551
579
|
"lastUpdate": 1337,
|
|
552
580
|
"options": null,
|
|
581
|
+
"relationshipNames": Array [],
|
|
582
|
+
"storeData": Array [],
|
|
553
583
|
},
|
|
554
584
|
"b": Object {
|
|
555
585
|
"bookmark": null,
|
|
@@ -589,6 +619,8 @@ Object {
|
|
|
589
619
|
"remove": false,
|
|
590
620
|
},
|
|
591
621
|
},
|
|
622
|
+
"relationshipNames": Array [],
|
|
623
|
+
"storeData": Array [],
|
|
592
624
|
},
|
|
593
625
|
}
|
|
594
626
|
`;
|
|
@@ -627,6 +659,8 @@ Object {
|
|
|
627
659
|
"lastFetch": 1337,
|
|
628
660
|
"lastUpdate": 1337,
|
|
629
661
|
"options": null,
|
|
662
|
+
"relationshipNames": Array [],
|
|
663
|
+
"storeData": Array [],
|
|
630
664
|
},
|
|
631
665
|
"b": Object {
|
|
632
666
|
"bookmark": null,
|
|
@@ -660,6 +694,8 @@ Object {
|
|
|
660
694
|
"lastFetch": null,
|
|
661
695
|
"lastUpdate": null,
|
|
662
696
|
"options": null,
|
|
697
|
+
"relationshipNames": Array [],
|
|
698
|
+
"storeData": Array [],
|
|
663
699
|
},
|
|
664
700
|
}
|
|
665
701
|
`;
|
|
@@ -700,6 +736,8 @@ Object {
|
|
|
700
736
|
"lastFetch": 1337,
|
|
701
737
|
"lastUpdate": 1337,
|
|
702
738
|
"options": null,
|
|
739
|
+
"relationshipNames": Array [],
|
|
740
|
+
"storeData": Array [],
|
|
703
741
|
},
|
|
704
742
|
"b": Object {
|
|
705
743
|
"bookmark": null,
|
|
@@ -736,6 +774,8 @@ Object {
|
|
|
736
774
|
"lastFetch": null,
|
|
737
775
|
"lastUpdate": 1337,
|
|
738
776
|
"options": null,
|
|
777
|
+
"relationshipNames": Array [],
|
|
778
|
+
"storeData": Array [],
|
|
739
779
|
},
|
|
740
780
|
}
|
|
741
781
|
`;
|
package/dist/store/queries.js
CHANGED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.equalityCheckForQuery = void 0;
|
|
7
|
+
|
|
8
|
+
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; } } }; }
|
|
9
|
+
|
|
10
|
+
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); }
|
|
11
|
+
|
|
12
|
+
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; }
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Perform an equality check to determine whether or not two query results are equal.
|
|
16
|
+
*
|
|
17
|
+
* This is typically called by a `useSelector`, to determine if a render should be made because
|
|
18
|
+
* the query result has changed.
|
|
19
|
+
* See https://react-redux.js.org/api/hooks#equality-comparisons-and-updates
|
|
20
|
+
*
|
|
21
|
+
* We cannot use the default strict reference comparison here, because a new reference for the query
|
|
22
|
+
* result is made every time. However, the query result itself, in `storeData`, preserves references.
|
|
23
|
+
*
|
|
24
|
+
* Note we do not make a shallow equality check on documents, as it is much less efficient.
|
|
25
|
+
* It is however different for relationships: because of their hydratation, the references change
|
|
26
|
+
* even if the data is the same.
|
|
27
|
+
*
|
|
28
|
+
* @param {import("../types").QueryState} queryResA - A query result to compare
|
|
29
|
+
* @param {import("../types").QueryState} queryResB - A query result to compare
|
|
30
|
+
* @returns {boolean} whether or not the two query are equal
|
|
31
|
+
*/
|
|
32
|
+
var equalityCheckForQuery = function equalityCheckForQuery(queryResA, queryResB) {
|
|
33
|
+
if (queryResA === queryResB) {
|
|
34
|
+
// Referential equality
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (typeof queryResA !== 'object' || queryResA === null || typeof queryResB !== 'object' || queryResB === null) {
|
|
39
|
+
// queryResA or queryResB is not an object or null
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (queryResA.id !== queryResB.id) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (queryResA.fetchStatus !== queryResB.fetchStatus) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
var docsA = queryResA.storeData;
|
|
52
|
+
var docsB = queryResB.storeData;
|
|
53
|
+
|
|
54
|
+
if (!docsA || !docsB) {
|
|
55
|
+
// No data to check
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!Array.isArray(docsA) && !Array.isArray(docsB) && docsA !== docsB) {
|
|
60
|
+
// Only one doc
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (Array.isArray(docsA) && Array.isArray(docsB) && !arraysHaveSameLength(docsA, docsB)) {
|
|
65
|
+
// A document was added or removed
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (Array.isArray(docsA) && Array.isArray(docsB)) {
|
|
70
|
+
for (var i = 0; i < docsA.length; i++) {
|
|
71
|
+
if (docsA[i] !== docsB[i]) {
|
|
72
|
+
// References should be the same for non-updated documents
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (queryResA.relationshipNames) {
|
|
79
|
+
// In case of relationships, we cannot check referential equality, because we
|
|
80
|
+
// "hydrate" the data by creating a new instance of the related relationship class.
|
|
81
|
+
// Thus, we check the document revision instead.
|
|
82
|
+
var hydratedDataA = queryResA.data;
|
|
83
|
+
var hydratedDataB = queryResB.data;
|
|
84
|
+
|
|
85
|
+
if (!Array.isArray(hydratedDataA) && !Array.isArray(hydratedDataB)) {
|
|
86
|
+
// One doc with changed relationship
|
|
87
|
+
return revsAreEqual(hydratedDataA, hydratedDataB);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!arraysHaveSameLength(hydratedDataA, hydratedDataB)) {
|
|
91
|
+
// A relationship have been added or removed
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (Array.isArray(hydratedDataA) && Array.isArray(hydratedDataB)) {
|
|
96
|
+
for (var _i = 0; _i < hydratedDataA.length; _i++) {
|
|
97
|
+
var _iterator = _createForOfIteratorHelper(queryResA.relationshipNames),
|
|
98
|
+
_step;
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
102
|
+
var name = _step.value;
|
|
103
|
+
// Check hydrated relationship
|
|
104
|
+
var includedA = hydratedDataA[_i][name];
|
|
105
|
+
var includedB = hydratedDataB[_i][name];
|
|
106
|
+
|
|
107
|
+
if (includedA && includedB) {
|
|
108
|
+
if (!revsAreEqual(includedA, includedB)) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch (err) {
|
|
114
|
+
_iterator.e(err);
|
|
115
|
+
} finally {
|
|
116
|
+
_iterator.f();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} // The query results are indeed equal
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
return true;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
exports.equalityCheckForQuery = equalityCheckForQuery;
|
|
127
|
+
|
|
128
|
+
var revsAreEqual = function revsAreEqual(docA, docB) {
|
|
129
|
+
if (!docA._rev || !docB._rev) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return (docA === null || docA === void 0 ? void 0 : docA._rev) === (docB === null || docB === void 0 ? void 0 : docB._rev);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
var arraysHaveSameLength = function arraysHaveSameLength(arrayA, arrayB) {
|
|
137
|
+
return Array.isArray(arrayA) && Array.isArray(arrayB) && arrayA.length === arrayB.length;
|
|
138
|
+
};
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { equalityCheckForQuery } from './selector'
|
|
2
|
+
|
|
3
|
+
const mapIdsToDocuments = (state, doctype, ids) => {
|
|
4
|
+
return ids.map(id => state[doctype][id])
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const state = {
|
|
8
|
+
documents: {
|
|
9
|
+
'io.cozy.files': {
|
|
10
|
+
doc1: {
|
|
11
|
+
_id: 'doc1'
|
|
12
|
+
},
|
|
13
|
+
doc2: {
|
|
14
|
+
_id: 'doc2'
|
|
15
|
+
},
|
|
16
|
+
doc3: {
|
|
17
|
+
_id: 'doc3'
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
queries: {
|
|
22
|
+
query1: {
|
|
23
|
+
id: 'query1',
|
|
24
|
+
data: ['doc1', 'doc2']
|
|
25
|
+
},
|
|
26
|
+
query2: {
|
|
27
|
+
id: 'query2',
|
|
28
|
+
data: ['doc2']
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const defaultQueryResult = {
|
|
34
|
+
id: 1,
|
|
35
|
+
data: [],
|
|
36
|
+
fetchStatus: 'loaded',
|
|
37
|
+
relationshipNames: null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('equalityCheckForQuery', () => {
|
|
41
|
+
const queryResultA1 = {
|
|
42
|
+
id: 1,
|
|
43
|
+
storeData: mapIdsToDocuments(state.documents, 'io.cozy.files', [
|
|
44
|
+
'doc1',
|
|
45
|
+
'doc2'
|
|
46
|
+
]),
|
|
47
|
+
...defaultQueryResult
|
|
48
|
+
}
|
|
49
|
+
const queryResultA2 = {
|
|
50
|
+
id: 1,
|
|
51
|
+
storeData: mapIdsToDocuments(state.documents, 'io.cozy.files', [
|
|
52
|
+
'doc1',
|
|
53
|
+
'doc2'
|
|
54
|
+
]),
|
|
55
|
+
...defaultQueryResult
|
|
56
|
+
}
|
|
57
|
+
const queryResultA3 = {
|
|
58
|
+
id: 1,
|
|
59
|
+
storeData: mapIdsToDocuments(state.documents, 'io.cozy.files', [
|
|
60
|
+
'doc1',
|
|
61
|
+
'doc2',
|
|
62
|
+
'doc3'
|
|
63
|
+
]),
|
|
64
|
+
...defaultQueryResult
|
|
65
|
+
}
|
|
66
|
+
const queryResultA4 = {
|
|
67
|
+
id: 1,
|
|
68
|
+
storeData: mapIdsToDocuments(state.documents, 'io.cozy.files', [
|
|
69
|
+
'doc2',
|
|
70
|
+
'doc3'
|
|
71
|
+
]),
|
|
72
|
+
...defaultQueryResult
|
|
73
|
+
}
|
|
74
|
+
const queryResultB1 = {
|
|
75
|
+
id: 2,
|
|
76
|
+
storeData: mapIdsToDocuments(state.documents, 'io.cozy.files', ['doc2']),
|
|
77
|
+
...defaultQueryResult
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const queryResultB2 = {
|
|
81
|
+
id: 2,
|
|
82
|
+
storeData: mapIdsToDocuments(state.documents, 'io.cozy.files', ['doc3']),
|
|
83
|
+
...defaultQueryResult
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const queryResultC1 = {
|
|
87
|
+
id: 3,
|
|
88
|
+
storeData: state.documents['io.cozy.files'].doc1,
|
|
89
|
+
data: {},
|
|
90
|
+
...defaultQueryResult
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const queryResultC2 = {
|
|
94
|
+
id: 3,
|
|
95
|
+
storeData: state.documents['io.cozy.files'].doc1,
|
|
96
|
+
data: {},
|
|
97
|
+
...defaultQueryResult
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const queryResultC3 = {
|
|
101
|
+
id: 3,
|
|
102
|
+
storeData: state.documents['io.cozy.files'].doc2,
|
|
103
|
+
data: {},
|
|
104
|
+
...defaultQueryResult
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
it('should return true for referential equality', () => {
|
|
108
|
+
expect(equalityCheckForQuery(queryResultA1, queryResultA1)).toBe(true)
|
|
109
|
+
expect(equalityCheckForQuery(null, null)).toBe(true)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('should return false if one object is null', () => {
|
|
113
|
+
expect(equalityCheckForQuery(null, queryResultA1)).toBe(false)
|
|
114
|
+
expect(equalityCheckForQuery(queryResultA1, null)).toBe(false)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('should return false if one or both objects are not objects', () => {
|
|
118
|
+
// @ts-ignore
|
|
119
|
+
expect(equalityCheckForQuery('notAnObject', queryResultA1)).toBe(false)
|
|
120
|
+
// @ts-ignore
|
|
121
|
+
expect(equalityCheckForQuery(queryResultA1, 'notAnObject')).toBe(false)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('should return false if `id` properties are different', () => {
|
|
125
|
+
expect(equalityCheckForQuery(queryResultA1, queryResultB1)).toBe(false)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('should return false if one or both objects lack `data`', () => {
|
|
129
|
+
// @ts-ignore
|
|
130
|
+
expect(equalityCheckForQuery({ id: 1 }, queryResultA1)).toBe(false)
|
|
131
|
+
// @ts-ignore
|
|
132
|
+
expect(equalityCheckForQuery(queryResultA1, { id: 1 })).toBe(false)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('should return false if `data` lengths are different', () => {
|
|
136
|
+
expect(equalityCheckForQuery(queryResultA1, queryResultA3)).toBe(false)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('should return false if elements in `data` are different', () => {
|
|
140
|
+
expect(equalityCheckForQuery(queryResultA1, queryResultA3)).toBe(false)
|
|
141
|
+
expect(equalityCheckForQuery(queryResultA3, queryResultA4)).toBe(false)
|
|
142
|
+
expect(equalityCheckForQuery(queryResultB1, queryResultB2)).toBe(false)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('should return true for matching data array, with equal references ', () => {
|
|
146
|
+
expect(equalityCheckForQuery(queryResultA1, queryResultA2)).toBe(true)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('should return false for matching data array, with different references ', () => {
|
|
150
|
+
const queryResShallowCopyA1 = {
|
|
151
|
+
...queryResultA1,
|
|
152
|
+
storeData: JSON.parse(JSON.stringify(queryResultA1.storeData)) // Deep copy
|
|
153
|
+
}
|
|
154
|
+
expect(equalityCheckForQuery(queryResultA1, queryResShallowCopyA1)).toBe(
|
|
155
|
+
false
|
|
156
|
+
)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('should return true for matching object data', () => {
|
|
160
|
+
expect(equalityCheckForQuery(queryResultC1, queryResultC2)).toBe(true)
|
|
161
|
+
})
|
|
162
|
+
it('should return false for different object data', () => {
|
|
163
|
+
expect(equalityCheckForQuery(queryResultC1, queryResultC3)).toBe(false)
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
describe('equalityCheckForQuery with relationships', () => {
|
|
168
|
+
const queryResA = {
|
|
169
|
+
...defaultQueryResult,
|
|
170
|
+
relationshipNames: ['relation1'],
|
|
171
|
+
storeData: mapIdsToDocuments(state.documents, 'io.cozy.files', [
|
|
172
|
+
'doc1',
|
|
173
|
+
'doc2'
|
|
174
|
+
]),
|
|
175
|
+
data: [{ relation1: { _rev: 'rev1' } }]
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const queryResB = {
|
|
179
|
+
...defaultQueryResult,
|
|
180
|
+
relationshipNames: ['relation1'],
|
|
181
|
+
storeData: mapIdsToDocuments(state.documents, 'io.cozy.files', [
|
|
182
|
+
'doc1',
|
|
183
|
+
'doc2'
|
|
184
|
+
]),
|
|
185
|
+
data: [{ relation1: { _rev: 'rev2' } }]
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const queryResC = {
|
|
189
|
+
...defaultQueryResult,
|
|
190
|
+
relationshipNames: ['relation1'],
|
|
191
|
+
storeData: mapIdsToDocuments(state.documents, 'io.cozy.files', [
|
|
192
|
+
'doc1',
|
|
193
|
+
'doc2'
|
|
194
|
+
]),
|
|
195
|
+
data: [{ relation1: { _rev: 'rev1' } }, { relation1: { _rev: 'rev2' } }]
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const queryResD = {
|
|
199
|
+
...defaultQueryResult,
|
|
200
|
+
relationshipNames: ['relation1', 'relation2'],
|
|
201
|
+
storeData: mapIdsToDocuments(state.documents, 'io.cozy.files', [
|
|
202
|
+
'doc1',
|
|
203
|
+
'doc2'
|
|
204
|
+
]),
|
|
205
|
+
data: [{ relation1: { _rev: 'rev1' } }, { relation2: { _rev: 'rev2' } }]
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const queryResE = {
|
|
209
|
+
...defaultQueryResult,
|
|
210
|
+
relationshipNames: ['relation1'],
|
|
211
|
+
storeData: mapIdsToDocuments(state.documents, 'io.cozy.files', ['doc1']),
|
|
212
|
+
data: [{ relation1: { _id: '123' } }]
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const queryResF = {
|
|
216
|
+
...defaultQueryResult,
|
|
217
|
+
relationshipNames: ['relation1'],
|
|
218
|
+
storeData: mapIdsToDocuments(state.documents, 'io.cozy.files', ['doc1']),
|
|
219
|
+
data: [{ relation1: { _id: '123' } }]
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
it('returns true when data and relationship revisions match', () => {
|
|
223
|
+
expect(equalityCheckForQuery(queryResA, queryResA)).toBe(true)
|
|
224
|
+
expect(equalityCheckForQuery(queryResD, queryResD)).toBe(true)
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('returns false when relationship revisions differ', () => {
|
|
228
|
+
expect(equalityCheckForQuery(queryResA, queryResB)).toBe(false)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('returns false when relationship revisions are missing', () => {
|
|
232
|
+
expect(equalityCheckForQuery(queryResE, queryResF)).toBe(false)
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('returns false when data lengths differ', () => {
|
|
236
|
+
expect(equalityCheckForQuery(queryResA, queryResC)).toBe(false)
|
|
237
|
+
})
|
|
238
|
+
})
|
package/dist/store.spec.js
CHANGED
package/dist/types.js
CHANGED
|
@@ -276,16 +276,22 @@ var _dsl = require("./queries/dsl");
|
|
|
276
276
|
* @property {number} fetchedPagesCount
|
|
277
277
|
* @property {string} bookmark
|
|
278
278
|
* @property {object} [execution_stats]
|
|
279
|
+
* @property {Array<string>} relationshipNames - The relationships names, used to check hydrated documents
|
|
279
280
|
* @property {QueryOptions} [options]
|
|
280
281
|
*/
|
|
281
282
|
|
|
282
283
|
/**
|
|
283
284
|
* @typedef {object} QueryStateData
|
|
284
|
-
* @property {object|Array} data
|
|
285
|
+
* @property {object|Array} data - Collection of hydrated documents
|
|
285
286
|
*/
|
|
286
287
|
|
|
287
288
|
/**
|
|
288
|
-
* @typedef {
|
|
289
|
+
* @typedef {object} QueryStateStoreData
|
|
290
|
+
* @property {object|Array} storeData - Collection of store's documents
|
|
291
|
+
*/
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* @typedef {QueryStateWithoutData & QueryStateData & QueryStateStoreData} QueryState
|
|
289
295
|
*/
|
|
290
296
|
|
|
291
297
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-client",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "60.0.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.node.js",
|
|
6
6
|
"browser": "dist/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"@types/jest": "^26.0.20",
|
|
20
20
|
"@types/lodash": "^4.14.170",
|
|
21
21
|
"btoa": "^1.2.1",
|
|
22
|
-
"cozy-stack-client": "^
|
|
22
|
+
"cozy-stack-client": "^60.0.0",
|
|
23
23
|
"date-fns": "2.29.3",
|
|
24
24
|
"fast-deep-equal": "^3.1.3",
|
|
25
25
|
"json-stable-stringify": "^1.0.1",
|
|
@@ -74,5 +74,5 @@
|
|
|
74
74
|
"react-native-ios11-devicecheck": "^0.0.3"
|
|
75
75
|
},
|
|
76
76
|
"sideEffects": false,
|
|
77
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "a89eb840497357fb366bc97d92e4a5880fd55099"
|
|
78
78
|
}
|
package/types/CozyClient.d.ts
CHANGED
|
@@ -49,6 +49,9 @@ export type ClientOptions = {
|
|
|
49
49
|
uri?: string;
|
|
50
50
|
stackClient?: object;
|
|
51
51
|
warningForCustomHandlers?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* - If set to true, all documents will be hydrated w.r.t. the provided schema's relationships, even if the relationship does not exist on the doc.
|
|
54
|
+
*/
|
|
52
55
|
autoHydrate?: boolean;
|
|
53
56
|
/**
|
|
54
57
|
* - If set to true, backgroundFetching will be enabled by default on every query. Meaning that, when the fetchStatus has already been loaded, it won't be updated during future fetches. Instead, a `isFetching` attribute will be used to indicate when background fetching is started.
|
|
@@ -96,7 +99,7 @@ export type ClientOptions = {
|
|
|
96
99
|
* @property {string} [uri]
|
|
97
100
|
* @property {object} [stackClient]
|
|
98
101
|
* @property {boolean} [warningForCustomHandlers]
|
|
99
|
-
* @property {boolean} [autoHydrate]
|
|
102
|
+
* @property {boolean} [autoHydrate] - If set to true, all documents will be hydrated w.r.t. the provided schema's relationships, even if the relationship does not exist on the doc.
|
|
100
103
|
* @property {boolean} [backgroundFetching] - If set to true, backgroundFetching will be enabled by default on every query. Meaning that, when the fetchStatus has already been loaded, it won't be updated during future fetches. Instead, a `isFetching` attribute will be used to indicate when background fetching is started.
|
|
101
104
|
* @property {object} [oauth]
|
|
102
105
|
* @property {Function} [onTokenRefresh]
|
|
@@ -108,7 +111,6 @@ export type ClientOptions = {
|
|
|
108
111
|
* @property {import("./types").ClientCapabilities} [capabilities] - Capabilities sent by the stack
|
|
109
112
|
* @property {boolean} [useCustomStore=false] - If set to true, the client will not instantiate a Redux store automatically. Use this if you want to merge cozy-client's store with your own redux store. Note will have to call `setStore` eventually. See [here](https://docs.cozy.io/en/cozy-client/react-integration/#1b-use-your-own-redux-store) for more information.
|
|
110
113
|
* @property {boolean} [disableStoreForQueries=false] - If set to true, the client will not leverage the redux store to execute queries and store data.
|
|
111
|
-
|
|
112
114
|
* @property {import('./performances/types').PerformanceAPI} [performanceApi] - The performance API that can be used to measure performances
|
|
113
115
|
*/
|
|
114
116
|
/**
|
|
@@ -210,6 +212,9 @@ declare class CozyClient {
|
|
|
210
212
|
uri?: string;
|
|
211
213
|
stackClient?: object;
|
|
212
214
|
warningForCustomHandlers?: boolean;
|
|
215
|
+
/**
|
|
216
|
+
* - If set to true, all documents will be hydrated w.r.t. the provided schema's relationships, even if the relationship does not exist on the doc.
|
|
217
|
+
*/
|
|
213
218
|
autoHydrate?: boolean;
|
|
214
219
|
/**
|
|
215
220
|
* - If set to true, backgroundFetching will be enabled by default on every query. Meaning that, when the fetchStatus has already been loaded, it won't be updated during future fetches. Instead, a `isFetching` attribute will be used to indicate when background fetching is started.
|
|
@@ -255,6 +260,7 @@ declare class CozyClient {
|
|
|
255
260
|
*/
|
|
256
261
|
storeAccesors: object;
|
|
257
262
|
useCustomStore: boolean;
|
|
263
|
+
autoHydrate: boolean;
|
|
258
264
|
disableStoreForQueries: boolean;
|
|
259
265
|
/**
|
|
260
266
|
* Gets overrided by MicroEE.mixin
|
|
@@ -607,12 +613,6 @@ declare class CozyClient {
|
|
|
607
613
|
hydrateRelationships(document: any, schemaRelationships: any): {
|
|
608
614
|
[x: string]: any;
|
|
609
615
|
};
|
|
610
|
-
/**
|
|
611
|
-
* Creates (locally) a new document for the given doctype.
|
|
612
|
-
* This document is hydrated : its relationships are there
|
|
613
|
-
* and working.
|
|
614
|
-
*/
|
|
615
|
-
makeNewDocument(doctype: any): any;
|
|
616
616
|
generateRandomId(): string;
|
|
617
617
|
/**
|
|
618
618
|
* Creates an association that is linked to the store.
|
|
@@ -662,7 +662,7 @@ declare class CozyClient {
|
|
|
662
662
|
*
|
|
663
663
|
* @param {string} id - Id of the query (set via Query.props.as)
|
|
664
664
|
* @param {object} options - Options
|
|
665
|
-
* @param {boolean} [options.hydrated] - Whether documents should be returned already hydrated
|
|
665
|
+
* @param {boolean} [options.hydrated] - Whether documents should be returned already hydrated
|
|
666
666
|
* @param {object} [options.singleDocData] - If true, the "data" returned will be
|
|
667
667
|
* a single doc instead of an array for single doc queries. Defaults to false for backward
|
|
668
668
|
* compatibility but will be set to true in the future.
|
|
@@ -94,7 +94,7 @@ export namespace TODO_WITH_AUTHOR {
|
|
|
94
94
|
}
|
|
95
95
|
export { relationships_1 as relationships };
|
|
96
96
|
}
|
|
97
|
-
export namespace
|
|
97
|
+
export namespace TODO_WITH_NONEXISTING_RELATIONSHIP {
|
|
98
98
|
const _id_6: number;
|
|
99
99
|
export { _id_6 as _id };
|
|
100
100
|
const _rev_6: string;
|
|
@@ -103,9 +103,20 @@ export namespace FILE_1 {
|
|
|
103
103
|
export { _type_6 as _type };
|
|
104
104
|
const label_6: string;
|
|
105
105
|
export { label_6 as label };
|
|
106
|
-
export
|
|
106
|
+
export namespace relationships_2 {
|
|
107
|
+
namespace broken {
|
|
108
|
+
const doctype_2: string;
|
|
109
|
+
export { doctype_2 as doctype };
|
|
110
|
+
const data_1: {
|
|
111
|
+
_type: string;
|
|
112
|
+
_id: number;
|
|
113
|
+
}[];
|
|
114
|
+
export { data_1 as data };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export { relationships_2 as relationships };
|
|
107
118
|
}
|
|
108
|
-
export namespace
|
|
119
|
+
export namespace FILE_1 {
|
|
109
120
|
const _id_7: number;
|
|
110
121
|
export { _id_7 as _id };
|
|
111
122
|
const _rev_7: string;
|
|
@@ -114,6 +125,17 @@ export namespace FILE_2 {
|
|
|
114
125
|
export { _type_7 as _type };
|
|
115
126
|
const label_7: string;
|
|
116
127
|
export { label_7 as label };
|
|
128
|
+
export const name: string;
|
|
129
|
+
}
|
|
130
|
+
export namespace FILE_2 {
|
|
131
|
+
const _id_8: number;
|
|
132
|
+
export { _id_8 as _id };
|
|
133
|
+
const _rev_8: string;
|
|
134
|
+
export { _rev_8 as _rev };
|
|
135
|
+
const _type_8: string;
|
|
136
|
+
export { _type_8 as _type };
|
|
137
|
+
const label_8: string;
|
|
138
|
+
export { label_8 as label };
|
|
117
139
|
}
|
|
118
140
|
export const APP_NAME: "cozy-client-test";
|
|
119
141
|
export const APP_VERSION: 2;
|
|
@@ -122,38 +144,38 @@ export const SOURCE_ACCOUNT_ID: "123-456-abc";
|
|
|
122
144
|
export const SOURCE_ACCOUNT_IDENTIFIER: "testSourceAccountIdentifier";
|
|
123
145
|
export namespace SCHEMA {
|
|
124
146
|
export namespace todos {
|
|
125
|
-
const
|
|
126
|
-
export {
|
|
147
|
+
const doctype_3: string;
|
|
148
|
+
export { doctype_3 as doctype };
|
|
127
149
|
export { DOCTYPE_VERSION as doctypeVersion };
|
|
128
|
-
export namespace
|
|
150
|
+
export namespace relationships_3 {
|
|
129
151
|
export namespace attachments_1 {
|
|
130
152
|
export const type: string;
|
|
131
|
-
const
|
|
132
|
-
export {
|
|
153
|
+
const doctype_4: string;
|
|
154
|
+
export { doctype_4 as doctype };
|
|
133
155
|
}
|
|
134
156
|
export { attachments_1 as attachments };
|
|
135
157
|
export namespace authors_1 {
|
|
136
158
|
const type_1: string;
|
|
137
159
|
export { type_1 as type };
|
|
138
|
-
const
|
|
139
|
-
export {
|
|
160
|
+
const doctype_5: string;
|
|
161
|
+
export { doctype_5 as doctype };
|
|
140
162
|
}
|
|
141
163
|
export { authors_1 as authors };
|
|
142
164
|
}
|
|
143
|
-
export {
|
|
165
|
+
export { relationships_3 as relationships };
|
|
144
166
|
}
|
|
145
167
|
export namespace files_1 {
|
|
146
|
-
const
|
|
147
|
-
export {
|
|
148
|
-
export namespace
|
|
168
|
+
const doctype_6: string;
|
|
169
|
+
export { doctype_6 as doctype };
|
|
170
|
+
export namespace relationships_4 {
|
|
149
171
|
namespace icons {
|
|
150
172
|
const type_2: string;
|
|
151
173
|
export { type_2 as type };
|
|
152
|
-
const
|
|
153
|
-
export {
|
|
174
|
+
const doctype_7: string;
|
|
175
|
+
export { doctype_7 as doctype };
|
|
154
176
|
}
|
|
155
177
|
}
|
|
156
|
-
export {
|
|
178
|
+
export { relationships_4 as relationships };
|
|
157
179
|
}
|
|
158
180
|
export { files_1 as files };
|
|
159
181
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function equalityCheckForQuery(queryResA: import("../types").QueryState, queryResB: import("../types").QueryState): boolean;
|
package/types/types.d.ts
CHANGED
|
@@ -503,12 +503,25 @@ export type QueryStateWithoutData = {
|
|
|
503
503
|
fetchedPagesCount: number;
|
|
504
504
|
bookmark: string;
|
|
505
505
|
execution_stats?: object;
|
|
506
|
+
/**
|
|
507
|
+
* - The relationships names, used to check hydrated documents
|
|
508
|
+
*/
|
|
509
|
+
relationshipNames: Array<string>;
|
|
506
510
|
options?: QueryOptions;
|
|
507
511
|
};
|
|
508
512
|
export type QueryStateData = {
|
|
513
|
+
/**
|
|
514
|
+
* - Collection of hydrated documents
|
|
515
|
+
*/
|
|
509
516
|
data: object | any[];
|
|
510
517
|
};
|
|
511
|
-
export type
|
|
518
|
+
export type QueryStateStoreData = {
|
|
519
|
+
/**
|
|
520
|
+
* - Collection of store's documents
|
|
521
|
+
*/
|
|
522
|
+
storeData: object | any[];
|
|
523
|
+
};
|
|
524
|
+
export type QueryState = QueryStateWithoutData & QueryStateData & QueryStateStoreData;
|
|
512
525
|
export type AutoUpdateOptions = any;
|
|
513
526
|
export type QueryOptions = {
|
|
514
527
|
/**
|
|
@@ -568,7 +581,7 @@ export type FetchMoreAble = {
|
|
|
568
581
|
export type FetchAble = {
|
|
569
582
|
fetch: Function;
|
|
570
583
|
};
|
|
571
|
-
export type UseQueryReturnValue = QueryStateWithoutData & QueryStateData & FetchMoreAble & FetchAble;
|
|
584
|
+
export type UseQueryReturnValue = QueryStateWithoutData & QueryStateData & QueryStateStoreData & FetchMoreAble & FetchAble;
|
|
572
585
|
export type UseMutationWithoutMutate = {
|
|
573
586
|
/**
|
|
574
587
|
* - Status of the current mutation
|