@graphql-box/cache-manager 3.4.2 → 4.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/lib/browser/index.js +1 -1
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/production.analysis.txt +32 -23
- package/lib/main/helpers/areOnlyPopulatedFieldsTypeIdKeys.js +53 -0
- package/lib/main/helpers/areOnlyPopulatedFieldsTypeIdKeys.js.map +1 -0
- package/lib/main/index.js +4 -14
- package/lib/main/index.js.map +1 -1
- package/lib/main/main/index.js +8 -15
- package/lib/main/main/index.js.map +1 -1
- package/lib/module/helpers/areOnlyPopulatedFieldsTypeIdKeys.js +41 -0
- package/lib/module/helpers/areOnlyPopulatedFieldsTypeIdKeys.js.map +1 -0
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/main/index.js +8 -13
- package/lib/module/main/index.js.map +1 -1
- package/lib/types/defs/index.d.ts +5 -11
- package/lib/types/defs/index.d.ts.map +1 -1
- package/lib/types/helpers/areOnlyPopulatedFieldsTypeIdKeys.d.ts +4 -0
- package/lib/types/helpers/areOnlyPopulatedFieldsTypeIdKeys.d.ts.map +1 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/main/index.d.ts +3 -4
- package/lib/types/main/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__snapshots__/index.test.ts.snap +131 -0
- package/src/defs/index.ts +5 -13
- package/src/helpers/areOnlyPopulatedFieldsTypeIdKeys.ts +40 -0
- package/src/index.test.ts +55 -1
- package/src/index.ts +1 -1
- package/src/main/index.ts +9 -15
|
@@ -35193,6 +35193,137 @@ Object {
|
|
|
35193
35193
|
}
|
|
35194
35194
|
`;
|
|
35195
35195
|
|
|
35196
|
+
exports[`@graphql-box/cache-manager >> analyzeQuery >> some matching data >> single type with just ID match >> correct cache data 1`] = `
|
|
35197
|
+
Object {
|
|
35198
|
+
"entries": Array [
|
|
35199
|
+
Array [
|
|
35200
|
+
"queryResponses::fe7ca217a961a692d575fe9973836699",
|
|
35201
|
+
Object {
|
|
35202
|
+
"cacheMetadata": Object {
|
|
35203
|
+
"query": Object {
|
|
35204
|
+
"cacheControl": Object {
|
|
35205
|
+
"maxAge": 1,
|
|
35206
|
+
"public": true,
|
|
35207
|
+
},
|
|
35208
|
+
"ttl": 297475201000,
|
|
35209
|
+
},
|
|
35210
|
+
"query.organization": Object {
|
|
35211
|
+
"cacheControl": Object {
|
|
35212
|
+
"maxAge": 1,
|
|
35213
|
+
"public": true,
|
|
35214
|
+
},
|
|
35215
|
+
"ttl": 297475201000,
|
|
35216
|
+
},
|
|
35217
|
+
},
|
|
35218
|
+
"data": Object {
|
|
35219
|
+
"organization": Object {
|
|
35220
|
+
"description": "We are working to build community through open source technology. NB: members must have two-factor auth.",
|
|
35221
|
+
"email": "",
|
|
35222
|
+
"id": "MDEyOk9yZ2FuaXphdGlvbjY5NjMx",
|
|
35223
|
+
},
|
|
35224
|
+
},
|
|
35225
|
+
},
|
|
35226
|
+
],
|
|
35227
|
+
Array [
|
|
35228
|
+
"requestFieldPaths::5256db516a2438a7e5488a00413f4809",
|
|
35229
|
+
Object {
|
|
35230
|
+
"description": "We are working to build community through open source technology. NB: members must have two-factor auth.",
|
|
35231
|
+
"email": "",
|
|
35232
|
+
"id": "MDEyOk9yZ2FuaXphdGlvbjY5NjMx",
|
|
35233
|
+
},
|
|
35234
|
+
],
|
|
35235
|
+
Array [
|
|
35236
|
+
"dataEntities::Organization::MDEyOk9yZ2FuaXphdGlvbjY5NjMx",
|
|
35237
|
+
Object {
|
|
35238
|
+
"description": "We are working to build community through open source technology. NB: members must have two-factor auth.",
|
|
35239
|
+
"email": "",
|
|
35240
|
+
"id": "MDEyOk9yZ2FuaXphdGlvbjY5NjMx",
|
|
35241
|
+
},
|
|
35242
|
+
],
|
|
35243
|
+
],
|
|
35244
|
+
"metadata": Array [
|
|
35245
|
+
Object {
|
|
35246
|
+
"accessedCount": 1,
|
|
35247
|
+
"added": 297475200000,
|
|
35248
|
+
"cacheability": Cacheability {
|
|
35249
|
+
"metadata": Object {
|
|
35250
|
+
"cacheControl": Object {
|
|
35251
|
+
"maxAge": 1,
|
|
35252
|
+
"public": true,
|
|
35253
|
+
},
|
|
35254
|
+
"etag": undefined,
|
|
35255
|
+
"ttl": 297475201000,
|
|
35256
|
+
},
|
|
35257
|
+
},
|
|
35258
|
+
"key": "requestFieldPaths::5256db516a2438a7e5488a00413f4809",
|
|
35259
|
+
"lastAccessed": 297475200000,
|
|
35260
|
+
"lastUpdated": 297475200000,
|
|
35261
|
+
"size": 456,
|
|
35262
|
+
"tags": Array [],
|
|
35263
|
+
"updatedCount": 0,
|
|
35264
|
+
},
|
|
35265
|
+
Object {
|
|
35266
|
+
"accessedCount": 1,
|
|
35267
|
+
"added": 297475200000,
|
|
35268
|
+
"cacheability": Cacheability {
|
|
35269
|
+
"metadata": Object {
|
|
35270
|
+
"cacheControl": Object {
|
|
35271
|
+
"maxAge": 1,
|
|
35272
|
+
"public": true,
|
|
35273
|
+
},
|
|
35274
|
+
"etag": undefined,
|
|
35275
|
+
"ttl": 297475201000,
|
|
35276
|
+
},
|
|
35277
|
+
},
|
|
35278
|
+
"key": "dataEntities::Organization::MDEyOk9yZ2FuaXphdGlvbjY5NjMx",
|
|
35279
|
+
"lastAccessed": 297475200000,
|
|
35280
|
+
"lastUpdated": 297475200000,
|
|
35281
|
+
"size": 456,
|
|
35282
|
+
"tags": Array [],
|
|
35283
|
+
"updatedCount": 0,
|
|
35284
|
+
},
|
|
35285
|
+
Object {
|
|
35286
|
+
"accessedCount": 0,
|
|
35287
|
+
"added": 297475200000,
|
|
35288
|
+
"cacheability": Cacheability {
|
|
35289
|
+
"metadata": Object {
|
|
35290
|
+
"cacheControl": Object {
|
|
35291
|
+
"maxAge": 1,
|
|
35292
|
+
"public": true,
|
|
35293
|
+
},
|
|
35294
|
+
"etag": undefined,
|
|
35295
|
+
"ttl": 297475201000,
|
|
35296
|
+
},
|
|
35297
|
+
},
|
|
35298
|
+
"key": "queryResponses::fe7ca217a961a692d575fe9973836699",
|
|
35299
|
+
"lastAccessed": 297475200000,
|
|
35300
|
+
"lastUpdated": 297475200000,
|
|
35301
|
+
"size": 984,
|
|
35302
|
+
"tags": Array [],
|
|
35303
|
+
"updatedCount": 0,
|
|
35304
|
+
},
|
|
35305
|
+
],
|
|
35306
|
+
}
|
|
35307
|
+
`;
|
|
35308
|
+
|
|
35309
|
+
exports[`@graphql-box/cache-manager >> analyzeQuery >> some matching data >> single type with just ID match >> correct partial data 1`] = `Map {}`;
|
|
35310
|
+
|
|
35311
|
+
exports[`@graphql-box/cache-manager >> analyzeQuery >> some matching data >> single type with just ID match >> correct request data 1`] = `
|
|
35312
|
+
Object {
|
|
35313
|
+
"hash": "0f87f523d8cf0a8e383850d601aa3f86",
|
|
35314
|
+
"request": "
|
|
35315
|
+
{
|
|
35316
|
+
organization(login: \\"facebook\\") {
|
|
35317
|
+
login
|
|
35318
|
+
name
|
|
35319
|
+
url
|
|
35320
|
+
id
|
|
35321
|
+
}
|
|
35322
|
+
}
|
|
35323
|
+
",
|
|
35324
|
+
}
|
|
35325
|
+
`;
|
|
35326
|
+
|
|
35196
35327
|
exports[`@graphql-box/cache-manager >> resolveQuery >> filtered >> defer >> cascading cache control >> correct cache data 1`] = `
|
|
35197
35328
|
Object {
|
|
35198
35329
|
"entries": Array [
|
package/src/defs/index.ts
CHANGED
|
@@ -42,18 +42,12 @@ export interface UserOptions {
|
|
|
42
42
|
* directives used for caching object types.
|
|
43
43
|
*/
|
|
44
44
|
typeCacheDirectives?: PlainObjectStringMap;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface ClientOptions {
|
|
48
|
-
typeIDKey: string;
|
|
49
|
-
}
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
typeIDKey: string;
|
|
46
|
+
/**
|
|
47
|
+
* The name of the property thats value is used as the unique
|
|
48
|
+
* identifier for each type in the GraphQL schema.
|
|
49
|
+
*/
|
|
50
|
+
typeIDKey?: string;
|
|
57
51
|
}
|
|
58
52
|
|
|
59
53
|
export interface CacheManagerContext extends RequestContext {
|
|
@@ -194,5 +188,3 @@ export interface CacheManagerDef {
|
|
|
194
188
|
context: CacheManagerContext,
|
|
195
189
|
): Promise<void>;
|
|
196
190
|
}
|
|
197
|
-
|
|
198
|
-
export type CacheManagerInit = (options: ClientOptions) => CacheManagerDef;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { PlainObjectMap } from "@graphql-box/core";
|
|
2
|
+
import { isArray, isPlainObject } from "lodash";
|
|
3
|
+
|
|
4
|
+
const checkValue = (value: any, typeIDKey: string): boolean => {
|
|
5
|
+
if (isArray(value)) {
|
|
6
|
+
return value.reduce((accB: boolean, entry) => {
|
|
7
|
+
if (!accB) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return checkValue(entry, typeIDKey);
|
|
12
|
+
}, true);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (isPlainObject(value)) {
|
|
16
|
+
return recursivelyCheckProps(value, typeIDKey);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return false;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const recursivelyCheckProps = (data: PlainObjectMap, typeIDKey: string): boolean => {
|
|
23
|
+
const keys = Object.keys(data);
|
|
24
|
+
|
|
25
|
+
if (keys.length === 1 && !!data[typeIDKey]) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return keys.reduce((accA: boolean, key) => {
|
|
30
|
+
if (!accA) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return checkValue(data[key], typeIDKey);
|
|
35
|
+
}, true);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default (data: PlainObjectMap, typeIDKey: string) => {
|
|
39
|
+
return recursivelyCheckProps(data, typeIDKey);
|
|
40
|
+
};
|
package/src/index.test.ts
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
requestFieldTypeMaps,
|
|
18
18
|
responses,
|
|
19
19
|
} from "@graphql-box/test-utils";
|
|
20
|
-
import { AnalyzeQueryResult,
|
|
20
|
+
import CacheManager, { AnalyzeQueryResult, CacheManagerDef } from ".";
|
|
21
21
|
|
|
22
22
|
describe("@graphql-box/cache-manager >>", () => {
|
|
23
23
|
const realDateNow = Date.now.bind(global.Date);
|
|
@@ -1200,6 +1200,60 @@ describe("@graphql-box/cache-manager >>", () => {
|
|
|
1200
1200
|
});
|
|
1201
1201
|
});
|
|
1202
1202
|
|
|
1203
|
+
describe("single type with just ID match >>", () => {
|
|
1204
|
+
beforeAll(async () => {
|
|
1205
|
+
analyzeQueryResult = undefined;
|
|
1206
|
+
// @ts-ignore
|
|
1207
|
+
jest.spyOn(CacheManager, "_isValid").mockReturnValue(true);
|
|
1208
|
+
|
|
1209
|
+
cacheManager = new CacheManager({
|
|
1210
|
+
cache: new Cachemap({
|
|
1211
|
+
name: "cachemap",
|
|
1212
|
+
store: map(),
|
|
1213
|
+
type: "someType",
|
|
1214
|
+
}),
|
|
1215
|
+
typeCacheDirectives: {
|
|
1216
|
+
Organization: "public, max-age=1",
|
|
1217
|
+
},
|
|
1218
|
+
typeIDKey: DEFAULT_TYPE_ID_KEY,
|
|
1219
|
+
});
|
|
1220
|
+
|
|
1221
|
+
const requestData = getRequestData(parsedRequests.singleTypeQuerySmallA);
|
|
1222
|
+
|
|
1223
|
+
await cacheManager.cacheQuery(
|
|
1224
|
+
requestData,
|
|
1225
|
+
requestData,
|
|
1226
|
+
responses.singleTypeQuerySmallA,
|
|
1227
|
+
{ awaitDataCaching: true },
|
|
1228
|
+
getRequestContext({ fieldTypeMap: requestFieldTypeMaps.singleTypeQuery }),
|
|
1229
|
+
);
|
|
1230
|
+
|
|
1231
|
+
analyzeQueryResult = await cacheManager.analyzeQuery(
|
|
1232
|
+
getRequestData(parsedRequests.singleTypeQuerySmallB),
|
|
1233
|
+
{ awaitDataCaching: true },
|
|
1234
|
+
getRequestContext({ fieldTypeMap: requestFieldTypeMaps.singleTypeQuery }),
|
|
1235
|
+
);
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
it("correct request data", () => {
|
|
1239
|
+
const { ast, ...otherProps } = analyzeQueryResult?.updated as RequestData;
|
|
1240
|
+
expect(otherProps).toMatchSnapshot();
|
|
1241
|
+
});
|
|
1242
|
+
|
|
1243
|
+
it("no response data", () => {
|
|
1244
|
+
expect(analyzeQueryResult?.response).toBeUndefined();
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
it("correct cache data", async () => {
|
|
1248
|
+
expect(await cacheManager.cache.export()).toMatchSnapshot();
|
|
1249
|
+
});
|
|
1250
|
+
|
|
1251
|
+
it("correct partial data", () => {
|
|
1252
|
+
// @ts-ignore
|
|
1253
|
+
expect(cacheManager._partialQueryResponses).toMatchSnapshot();
|
|
1254
|
+
});
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1203
1257
|
describe("nested type with edges >", () => {
|
|
1204
1258
|
beforeAll(async () => {
|
|
1205
1259
|
analyzeQueryResult = undefined;
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from "./defs";
|
|
2
|
-
export { default
|
|
2
|
+
export { default } from "./main";
|
package/src/main/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
CacheTypes,
|
|
5
5
|
CachemapOptions,
|
|
6
6
|
DATA_ENTITIES,
|
|
7
|
+
DEFAULT_TYPE_ID_KEY,
|
|
7
8
|
FieldTypeInfo,
|
|
8
9
|
PlainObjectMap,
|
|
9
10
|
PlainObjectStringMap,
|
|
@@ -41,12 +42,9 @@ import {
|
|
|
41
42
|
AncestorKeysAndPaths,
|
|
42
43
|
CacheManagerContext,
|
|
43
44
|
CacheManagerDef,
|
|
44
|
-
CacheManagerInit,
|
|
45
45
|
CachedAncestorFieldData,
|
|
46
46
|
CachedResponseData,
|
|
47
47
|
CheckCacheEntryResult,
|
|
48
|
-
ClientOptions,
|
|
49
|
-
ConstructorOptions,
|
|
50
48
|
DataForCachingEntry,
|
|
51
49
|
FieldCount,
|
|
52
50
|
FieldPathChecklist,
|
|
@@ -59,6 +57,7 @@ import {
|
|
|
59
57
|
TypeNamesAndKind,
|
|
60
58
|
UserOptions,
|
|
61
59
|
} from "../defs";
|
|
60
|
+
import areOnlyPopulatedFieldsTypeIdKeys from "../helpers/areOnlyPopulatedFieldsTypeIdKeys";
|
|
62
61
|
import deriveOpCacheability from "../helpers/deriveOpCacheability";
|
|
63
62
|
import filterOutPropsWithArgsOrDirectives from "../helpers/filterOutPropsWithArgsOrDirectives";
|
|
64
63
|
import filterQuery from "../helpers/filterQuery";
|
|
@@ -69,7 +68,7 @@ import mergeResponseDataSets from "../helpers/mergeResponseDataSets";
|
|
|
69
68
|
import normalizePatchResponseData from "../helpers/normalizePatchResponseData";
|
|
70
69
|
import { getValidTypeIDValue } from "../helpers/validTypeIDValue";
|
|
71
70
|
|
|
72
|
-
export class CacheManager implements CacheManagerDef {
|
|
71
|
+
export default class CacheManager implements CacheManagerDef {
|
|
73
72
|
private static _countFieldPathChecklist(fieldPathChecklist: FieldPathChecklist): FieldCount {
|
|
74
73
|
const fieldCount: FieldCount = { missing: 0, total: 0 };
|
|
75
74
|
|
|
@@ -213,7 +212,7 @@ export class CacheManager implements CacheManagerDef {
|
|
|
213
212
|
private _typeCacheDirectives: PlainObjectStringMap;
|
|
214
213
|
private _typeIDKey: string;
|
|
215
214
|
|
|
216
|
-
constructor(options:
|
|
215
|
+
constructor(options: UserOptions) {
|
|
217
216
|
const errors: TypeError[] = [];
|
|
218
217
|
|
|
219
218
|
if (!options.cache) {
|
|
@@ -233,7 +232,7 @@ export class CacheManager implements CacheManagerDef {
|
|
|
233
232
|
this._cascadeCacheControl = options.cascadeCacheControl || false;
|
|
234
233
|
this._fallbackOperationCacheability = options.fallbackOperationCacheability || NO_CACHE;
|
|
235
234
|
this._typeCacheDirectives = options.typeCacheDirectives || {};
|
|
236
|
-
this._typeIDKey = options.typeIDKey;
|
|
235
|
+
this._typeIDKey = options.typeIDKey ?? DEFAULT_TYPE_ID_KEY;
|
|
237
236
|
}
|
|
238
237
|
|
|
239
238
|
get cache(): Cachemap {
|
|
@@ -260,7 +259,10 @@ export class CacheManager implements CacheManagerDef {
|
|
|
260
259
|
const cachedResponseData = await this._retrieveCachedResponseData(requestData, options, cacheManagerContext);
|
|
261
260
|
const { cacheMetadata, data, fieldCount } = cachedResponseData;
|
|
262
261
|
|
|
263
|
-
|
|
262
|
+
// Second half of check is required for the scenario where the only matching data is
|
|
263
|
+
// the typeIDKey field, i.e. "id", in which case there is no point settings a partial
|
|
264
|
+
// query reponse because we request the typeIDKey field with every request.
|
|
265
|
+
if (fieldCount.missing === fieldCount.total || areOnlyPopulatedFieldsTypeIdKeys(data, this._typeIDKey)) {
|
|
264
266
|
return { updated: requestData };
|
|
265
267
|
}
|
|
266
268
|
|
|
@@ -1179,11 +1181,3 @@ export class CacheManager implements CacheManagerDef {
|
|
|
1179
1181
|
}
|
|
1180
1182
|
}
|
|
1181
1183
|
}
|
|
1182
|
-
|
|
1183
|
-
export default function init(userOptions: UserOptions): CacheManagerInit {
|
|
1184
|
-
if (!isPlainObject(userOptions)) {
|
|
1185
|
-
throw new TypeError("@graphql-box/cache-manager expected userOptions to be a plain object.");
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
return (clientOptions: ClientOptions) => new CacheManager({ ...clientOptions, ...userOptions });
|
|
1189
|
-
}
|