@graphql-box/cache-manager 2.1.1 → 2.2.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 +139 -17
- package/lib/main/debug/log-cache-entry/index.js.map +1 -1
- package/lib/main/debug/log-partial-compiled/index.js.map +1 -1
- package/lib/main/helpers/buildKeysAndPaths.js +73 -0
- package/lib/main/helpers/buildKeysAndPaths.js.map +1 -0
- package/lib/main/helpers/checkFieldPathChecklist.js +40 -0
- package/lib/main/helpers/checkFieldPathChecklist.js.map +1 -0
- package/lib/main/helpers/createFragmentSpreadChecklist.js +28 -0
- package/lib/main/helpers/createFragmentSpreadChecklist.js.map +1 -0
- package/lib/main/helpers/deriveOpCacheability.js +46 -0
- package/lib/main/helpers/deriveOpCacheability.js.map +1 -0
- package/lib/main/helpers/filterField.js +97 -0
- package/lib/main/helpers/filterField.js.map +1 -0
- package/lib/main/helpers/filterFragmentDefinitions.js +50 -0
- package/lib/main/helpers/filterFragmentDefinitions.js.map +1 -0
- package/lib/main/helpers/filterFragmentSpreads.js +37 -0
- package/lib/main/helpers/filterFragmentSpreads.js.map +1 -0
- package/lib/main/helpers/filterIDsAndTypeNames.js +47 -0
- package/lib/main/helpers/filterIDsAndTypeNames.js.map +1 -0
- package/lib/main/helpers/filterInlineFragments.js +42 -0
- package/lib/main/helpers/filterInlineFragments.js.map +1 -0
- package/lib/main/helpers/filterOutPropsWithArgsOrDirectives.js +39 -0
- package/lib/main/helpers/filterOutPropsWithArgsOrDirectives.js.map +1 -0
- package/lib/main/helpers/filterQuery.js +59 -0
- package/lib/main/helpers/filterQuery.js.map +1 -0
- package/lib/main/helpers/normalizeResponseData.js +23 -0
- package/lib/main/helpers/normalizeResponseData.js.map +1 -0
- package/lib/main/helpers/validTypeIDValue.js +20 -0
- package/lib/main/helpers/validTypeIDValue.js.map +1 -0
- package/lib/main/main/index.js +477 -476
- package/lib/main/main/index.js.map +1 -1
- package/lib/module/debug/log-cache-entry/index.js.map +1 -1
- package/lib/module/debug/log-partial-compiled/index.js.map +1 -1
- package/lib/module/helpers/buildKeysAndPaths.js +54 -0
- package/lib/module/helpers/buildKeysAndPaths.js.map +1 -0
- package/lib/module/helpers/checkFieldPathChecklist.js +31 -0
- package/lib/module/helpers/checkFieldPathChecklist.js.map +1 -0
- package/lib/module/helpers/createFragmentSpreadChecklist.js +15 -0
- package/lib/module/helpers/createFragmentSpreadChecklist.js.map +1 -0
- package/lib/module/helpers/deriveOpCacheability.js +32 -0
- package/lib/module/helpers/deriveOpCacheability.js.map +1 -0
- package/lib/module/helpers/filterField.js +81 -0
- package/lib/module/helpers/filterField.js.map +1 -0
- package/lib/module/helpers/filterFragmentDefinitions.js +39 -0
- package/lib/module/helpers/filterFragmentDefinitions.js.map +1 -0
- package/lib/module/helpers/filterFragmentSpreads.js +23 -0
- package/lib/module/helpers/filterFragmentSpreads.js.map +1 -0
- package/lib/module/helpers/filterIDsAndTypeNames.js +36 -0
- package/lib/module/helpers/filterIDsAndTypeNames.js.map +1 -0
- package/lib/module/helpers/filterInlineFragments.js +32 -0
- package/lib/module/helpers/filterInlineFragments.js.map +1 -0
- package/lib/module/helpers/filterOutPropsWithArgsOrDirectives.js +25 -0
- package/lib/module/helpers/filterOutPropsWithArgsOrDirectives.js.map +1 -0
- package/lib/module/helpers/filterQuery.js +43 -0
- package/lib/module/helpers/filterQuery.js.map +1 -0
- package/lib/module/helpers/normalizeResponseData.js +11 -0
- package/lib/module/helpers/normalizeResponseData.js.map +1 -0
- package/lib/module/helpers/validTypeIDValue.js +8 -0
- package/lib/module/helpers/validTypeIDValue.js.map +1 -0
- package/lib/module/main/index.js +474 -475
- package/lib/module/main/index.js.map +1 -1
- package/lib/types/debug/log-cache-entry/index.d.ts.map +1 -1
- package/lib/types/debug/log-cache-query/index.d.ts.map +1 -1
- package/lib/types/debug/log-partial-compiled/index.d.ts.map +1 -1
- package/lib/types/defs/index.d.ts +19 -9
- package/lib/types/defs/index.d.ts.map +1 -1
- package/lib/types/helpers/buildKeysAndPaths.d.ts +10 -0
- package/lib/types/helpers/buildKeysAndPaths.d.ts.map +1 -0
- package/lib/types/helpers/checkFieldPathChecklist.d.ts +4 -0
- package/lib/types/helpers/checkFieldPathChecklist.d.ts.map +1 -0
- package/lib/types/helpers/createFragmentSpreadChecklist.d.ts +11 -0
- package/lib/types/helpers/createFragmentSpreadChecklist.d.ts.map +1 -0
- package/lib/types/helpers/deriveOpCacheability.d.ts +10 -0
- package/lib/types/helpers/deriveOpCacheability.d.ts.map +1 -0
- package/lib/types/helpers/filterField.d.ts +6 -0
- package/lib/types/helpers/filterField.d.ts.map +1 -0
- package/lib/types/helpers/filterFragmentDefinitions.d.ts +10 -0
- package/lib/types/helpers/filterFragmentDefinitions.d.ts.map +1 -0
- package/lib/types/helpers/filterFragmentSpreads.d.ts +6 -0
- package/lib/types/helpers/filterFragmentSpreads.d.ts.map +1 -0
- package/lib/types/helpers/filterIDsAndTypeNames.d.ts +5 -0
- package/lib/types/helpers/filterIDsAndTypeNames.d.ts.map +1 -0
- package/lib/types/helpers/filterInlineFragments.d.ts +5 -0
- package/lib/types/helpers/filterInlineFragments.d.ts.map +1 -0
- package/lib/types/helpers/filterOutPropsWithArgsOrDirectives.d.ts +6 -0
- package/lib/types/helpers/filterOutPropsWithArgsOrDirectives.d.ts.map +1 -0
- package/lib/types/helpers/filterQuery.d.ts +5 -0
- package/lib/types/helpers/filterQuery.d.ts.map +1 -0
- package/lib/types/helpers/normalizeResponseData.d.ts +10 -0
- package/lib/types/helpers/normalizeResponseData.d.ts.map +1 -0
- package/lib/types/helpers/validTypeIDValue.d.ts +3 -0
- package/lib/types/helpers/validTypeIDValue.d.ts.map +1 -0
- package/lib/types/main/index.d.ts +13 -20
- package/lib/types/main/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__snapshots__/index.test.ts.snap +17449 -7185
- package/src/debug/log-cache-entry/index.ts +1 -1
- package/src/debug/log-partial-compiled/index.ts +1 -1
- package/src/defs/index.ts +18 -10
- package/src/helpers/buildKeysAndPaths.ts +71 -0
- package/src/helpers/checkFieldPathChecklist.ts +21 -0
- package/src/helpers/createFragmentSpreadChecklist.ts +17 -0
- package/src/helpers/deriveOpCacheability.ts +32 -0
- package/src/helpers/filterField.ts +73 -0
- package/src/helpers/filterFragmentDefinitions.ts +40 -0
- package/src/helpers/filterFragmentSpreads.ts +28 -0
- package/src/helpers/filterIDsAndTypeNames.ts +31 -0
- package/src/helpers/filterInlineFragments.ts +29 -0
- package/src/helpers/filterOutPropsWithArgsOrDirectives.ts +30 -0
- package/src/helpers/filterQuery.ts +38 -0
- package/src/helpers/normalizeResponseData.ts +9 -0
- package/src/helpers/validTypeIDValue.ts +11 -0
- package/src/index.test.ts +179 -3
- package/src/main/index.ts +516 -499
package/src/main/index.ts
CHANGED
|
@@ -17,15 +17,10 @@ import {
|
|
|
17
17
|
TYPE_NAME_KEY,
|
|
18
18
|
} from "@graphql-box/core";
|
|
19
19
|
import {
|
|
20
|
+
FRAGMENT_SPREAD,
|
|
20
21
|
dehydrateCacheMetadata,
|
|
21
|
-
deleteChildFields,
|
|
22
|
-
deleteInlineFragments,
|
|
23
|
-
getAlias,
|
|
24
|
-
getArguments,
|
|
25
22
|
getChildFields,
|
|
26
|
-
|
|
27
|
-
getInlineFragments,
|
|
28
|
-
getName,
|
|
23
|
+
getFragmentDefinitions,
|
|
29
24
|
getOperationDefinitions,
|
|
30
25
|
hasChildFields,
|
|
31
26
|
hashRequest,
|
|
@@ -35,20 +30,19 @@ import {
|
|
|
35
30
|
} from "@graphql-box/helpers";
|
|
36
31
|
import Cacheability from "cacheability";
|
|
37
32
|
import { FieldNode, print } from "graphql";
|
|
38
|
-
import { cloneDeep, get, isArray,
|
|
39
|
-
import { CACHE_CONTROL,
|
|
33
|
+
import { assign, cloneDeep, get, isArray, isObjectLike, isPlainObject, isUndefined, set, unset } from "lodash";
|
|
34
|
+
import { CACHE_CONTROL, HEADER_NO_CACHE, METADATA, NO_CACHE } from "../consts";
|
|
40
35
|
import { logCacheEntry, logCacheQuery, logPartialCompiled } from "../debug";
|
|
41
36
|
import {
|
|
42
37
|
AnalyzeQueryResult,
|
|
43
38
|
AncestorKeysAndPaths,
|
|
39
|
+
CacheManagerContext,
|
|
44
40
|
CacheManagerDef,
|
|
45
41
|
CacheManagerInit,
|
|
46
42
|
CachedAncestorFieldData,
|
|
47
|
-
CachedFieldData,
|
|
48
43
|
CachedResponseData,
|
|
49
44
|
CachemapOptions,
|
|
50
45
|
CheckCacheEntryResult,
|
|
51
|
-
CheckFieldPathChecklistResult,
|
|
52
46
|
ClientOptions,
|
|
53
47
|
ConstructorOptions,
|
|
54
48
|
DataForCachingEntry,
|
|
@@ -57,15 +51,20 @@ import {
|
|
|
57
51
|
FieldPathChecklistValue,
|
|
58
52
|
InitOptions,
|
|
59
53
|
KeysAndPaths,
|
|
60
|
-
KeysAndPathsOptions,
|
|
61
54
|
MergedCachedFieldData,
|
|
62
55
|
PartialQueryResponse,
|
|
63
56
|
PartialQueryResponses,
|
|
64
57
|
QueryResponseCacheEntry,
|
|
65
58
|
ResponseDataForCaching,
|
|
66
|
-
|
|
59
|
+
TypeNamesAndKind,
|
|
67
60
|
UserOptions,
|
|
68
61
|
} from "../defs";
|
|
62
|
+
import { buildFieldKeysAndPaths } from "../helpers/buildKeysAndPaths";
|
|
63
|
+
import deriveOpCacheability from "../helpers/deriveOpCacheability";
|
|
64
|
+
import filterOutPropsWithArgsOrDirectives from "../helpers/filterOutPropsWithArgsOrDirectives";
|
|
65
|
+
import filterQuery from "../helpers/filterQuery";
|
|
66
|
+
import normalizeResponseData from "../helpers/normalizeResponseData";
|
|
67
|
+
import { getValidTypeIDValue } from "../helpers/validTypeIDValue";
|
|
69
68
|
|
|
70
69
|
export class CacheManager implements CacheManagerDef {
|
|
71
70
|
public static async init(options: InitOptions): Promise<CacheManager> {
|
|
@@ -80,73 +79,11 @@ export class CacheManager implements CacheManagerDef {
|
|
|
80
79
|
errors.push(new TypeError(message));
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
if (errors.length)
|
|
84
|
-
|
|
85
|
-
return new CacheManager(options);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
private static _analyzeLeafField(
|
|
89
|
-
field: FieldNode,
|
|
90
|
-
cachedAncestorFieldData: CachedAncestorFieldData,
|
|
91
|
-
{ data, fieldPathChecklist }: CachedResponseData,
|
|
92
|
-
_options: RequestOptions,
|
|
93
|
-
_context: RequestContext,
|
|
94
|
-
): void {
|
|
95
|
-
const keysAndPaths = CacheManager._getFieldKeysAndPaths(field, cachedAncestorFieldData);
|
|
96
|
-
const { propNameOrIndex, requestFieldPath } = keysAndPaths;
|
|
97
|
-
const { dataEntityData, requestFieldPathData, typeName } = cachedAncestorFieldData;
|
|
98
|
-
|
|
99
|
-
const cachedFieldData =
|
|
100
|
-
CacheManager._getFieldDataFromAncestor(dataEntityData, propNameOrIndex) ||
|
|
101
|
-
CacheManager._getFieldDataFromAncestor(requestFieldPathData, propNameOrIndex);
|
|
102
|
-
|
|
103
|
-
const typeNames = {
|
|
104
|
-
dataTypeName: dataEntityData.__typename || requestFieldPathData.__typename,
|
|
105
|
-
fieldTypeName: typeName,
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
CacheManager._setFieldPathChecklist(fieldPathChecklist, { data: cachedFieldData }, requestFieldPath, typeNames);
|
|
109
|
-
CacheManager._setCachedData(data, { data: cachedFieldData }, propNameOrIndex);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
private static _buildKey(key: string | number, path: string): string {
|
|
113
|
-
const paths: (string | number)[] = [];
|
|
114
|
-
if (path.length) paths.push(path);
|
|
115
|
-
paths.push(key);
|
|
116
|
-
return paths.join(".");
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
private static _buildRequestFieldCacheKey(
|
|
120
|
-
name: string,
|
|
121
|
-
requestFieldCacheKey: string,
|
|
122
|
-
args?: PlainObjectMap,
|
|
123
|
-
directives?: PlainObjectMap,
|
|
124
|
-
index?: number,
|
|
125
|
-
): string {
|
|
126
|
-
let key = `${isNumber(index) ? index : name}`;
|
|
127
|
-
if (args) key = `${key}(${JSON.stringify(args)})`;
|
|
128
|
-
if (directives) key = `${key}(${JSON.stringify(directives)})`;
|
|
129
|
-
return CacheManager._buildKey(key, requestFieldCacheKey);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
private static _checkFieldPathChecklist(
|
|
133
|
-
fieldPathChecklistValues: FieldPathChecklistValue[] | undefined,
|
|
134
|
-
fieldTypeName: string | undefined,
|
|
135
|
-
): CheckFieldPathChecklistResult {
|
|
136
|
-
if (!fieldPathChecklistValues || !fieldPathChecklistValues.length) {
|
|
137
|
-
return { hasData: false, typeUnused: !!fieldTypeName };
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (fieldPathChecklistValues.length === 1) {
|
|
141
|
-
const { hasData, typeName } = fieldPathChecklistValues[0];
|
|
142
|
-
const typeUnused = !typeName ? undefined : typeName !== fieldTypeName;
|
|
143
|
-
return { hasData, typeUnused };
|
|
82
|
+
if (errors.length) {
|
|
83
|
+
return Promise.reject(errors);
|
|
144
84
|
}
|
|
145
85
|
|
|
146
|
-
return
|
|
147
|
-
hasData: fieldPathChecklistValues.some(({ hasData, typeName }) => typeName === fieldTypeName && hasData),
|
|
148
|
-
typeUnused: !fieldPathChecklistValues.every(({ typeName }) => typeName === fieldTypeName),
|
|
149
|
-
};
|
|
86
|
+
return new CacheManager(options);
|
|
150
87
|
}
|
|
151
88
|
|
|
152
89
|
private static _countFieldPathChecklist(fieldPathChecklist: FieldPathChecklist): FieldCount {
|
|
@@ -162,42 +99,15 @@ export class CacheManager implements CacheManagerDef {
|
|
|
162
99
|
}
|
|
163
100
|
|
|
164
101
|
private static _getFieldDataFromAncestor(ancestorFieldData: any, propNameOrIndex: string | number): any {
|
|
165
|
-
return isObjectLike(ancestorFieldData) ? ancestorFieldData[propNameOrIndex] : undefined;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
private static _getFieldKeysAndPaths(field: FieldNode, options: KeysAndPathsOptions): KeysAndPaths {
|
|
169
|
-
const { index, requestFieldCacheKey = "", requestFieldPath = "", responseDataPath = "" } = options;
|
|
170
|
-
const name = getName(field) as string;
|
|
171
|
-
|
|
172
|
-
const updatedRequestFieldCacheKey = CacheManager._buildRequestFieldCacheKey(
|
|
173
|
-
name,
|
|
174
|
-
requestFieldCacheKey,
|
|
175
|
-
getArguments(field),
|
|
176
|
-
getDirectives(field),
|
|
177
|
-
index,
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
const fieldAliasOrName = getAlias(field) || name;
|
|
181
|
-
|
|
182
|
-
const updatedRequestFieldPath = isNumber(index)
|
|
183
|
-
? requestFieldPath
|
|
184
|
-
: CacheManager._buildKey(fieldAliasOrName, requestFieldPath);
|
|
185
|
-
|
|
186
|
-
const propNameOrIndex = isNumber(index) ? index : fieldAliasOrName;
|
|
187
|
-
const updatedResponseDataPath = CacheManager._buildKey(propNameOrIndex, responseDataPath);
|
|
188
|
-
|
|
189
|
-
return {
|
|
190
|
-
hashedRequestFieldCacheKey: hashRequest(updatedRequestFieldCacheKey),
|
|
191
|
-
propNameOrIndex,
|
|
192
|
-
requestFieldCacheKey: updatedRequestFieldCacheKey,
|
|
193
|
-
requestFieldPath: updatedRequestFieldPath,
|
|
194
|
-
responseDataPath: updatedResponseDataPath,
|
|
195
|
-
};
|
|
102
|
+
return isObjectLike(ancestorFieldData) ? cloneDeep(ancestorFieldData[propNameOrIndex]) : undefined;
|
|
196
103
|
}
|
|
197
104
|
|
|
198
105
|
private static _getOperationCacheControl(cacheMetadata: CacheMetadata | undefined, operation: string): string {
|
|
199
106
|
const defaultCacheControl = HEADER_NO_CACHE;
|
|
200
|
-
|
|
107
|
+
|
|
108
|
+
if (!cacheMetadata) {
|
|
109
|
+
return defaultCacheControl;
|
|
110
|
+
}
|
|
201
111
|
|
|
202
112
|
const cacheability = cacheMetadata.get(operation);
|
|
203
113
|
return cacheability ? cacheability.printCacheControl() : defaultCacheControl;
|
|
@@ -207,22 +117,26 @@ export class CacheManager implements CacheManagerDef {
|
|
|
207
117
|
cacheMetadata: CacheMetadata,
|
|
208
118
|
partialQueryResponse?: PartialQueryResponse,
|
|
209
119
|
): CacheMetadata {
|
|
210
|
-
if (!partialQueryResponse)
|
|
120
|
+
if (!partialQueryResponse) {
|
|
121
|
+
return cacheMetadata;
|
|
122
|
+
}
|
|
211
123
|
|
|
212
124
|
return new Map([...partialQueryResponse.cacheMetadata, ...cacheMetadata]);
|
|
213
125
|
}
|
|
214
126
|
|
|
215
|
-
private static
|
|
216
|
-
if (!fieldTypeInfo)
|
|
127
|
+
private static _isNodeEntity(fieldTypeInfo?: FieldTypeInfo): boolean {
|
|
128
|
+
if (!fieldTypeInfo) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
217
131
|
|
|
218
132
|
const { isEntity, possibleTypes } = fieldTypeInfo;
|
|
219
133
|
return isEntity || possibleTypes.some(type => !!type.isEntity);
|
|
220
134
|
}
|
|
221
135
|
|
|
222
|
-
private static
|
|
136
|
+
private static _isNodeRequestFieldPath(fieldTypeInfo?: FieldTypeInfo): boolean {
|
|
223
137
|
return (
|
|
224
138
|
!!fieldTypeInfo &&
|
|
225
|
-
(this.
|
|
139
|
+
(this._isNodeEntity(fieldTypeInfo) || fieldTypeInfo.hasArguments || fieldTypeInfo.hasDirectives)
|
|
226
140
|
);
|
|
227
141
|
}
|
|
228
142
|
|
|
@@ -248,12 +162,12 @@ export class CacheManager implements CacheManagerDef {
|
|
|
248
162
|
cachedFieldData: MergedCachedFieldData,
|
|
249
163
|
{ cacheMetadata, data, fieldPathChecklist }: CachedResponseData,
|
|
250
164
|
{ propNameOrIndex, requestFieldPath }: KeysAndPaths,
|
|
251
|
-
|
|
165
|
+
typeNamesAndKind: TypeNamesAndKind,
|
|
252
166
|
_options: RequestOptions,
|
|
253
|
-
{ operation }:
|
|
167
|
+
{ operation }: CacheManagerContext,
|
|
254
168
|
) {
|
|
255
169
|
CacheManager._setCacheMetadata(cacheMetadata, cachedFieldData.cacheability, requestFieldPath, operation);
|
|
256
|
-
CacheManager._setFieldPathChecklist(fieldPathChecklist, cachedFieldData, requestFieldPath,
|
|
170
|
+
CacheManager._setFieldPathChecklist(fieldPathChecklist, cachedFieldData, requestFieldPath, typeNamesAndKind);
|
|
257
171
|
CacheManager._setCachedData(data, cachedFieldData, propNameOrIndex);
|
|
258
172
|
}
|
|
259
173
|
|
|
@@ -263,7 +177,9 @@ export class CacheManager implements CacheManagerDef {
|
|
|
263
177
|
requestFieldPath: string,
|
|
264
178
|
operation: string,
|
|
265
179
|
): void {
|
|
266
|
-
if (!cacheability)
|
|
180
|
+
if (!cacheability) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
267
183
|
|
|
268
184
|
cacheMetadata.set(requestFieldPath, cacheability);
|
|
269
185
|
const operationCacheability = cacheMetadata.get(operation);
|
|
@@ -277,23 +193,31 @@ export class CacheManager implements CacheManagerDef {
|
|
|
277
193
|
fieldPathChecklist: FieldPathChecklist,
|
|
278
194
|
{ data }: MergedCachedFieldData,
|
|
279
195
|
requestFieldPath: string,
|
|
280
|
-
{ dataTypeName, fieldTypeName }:
|
|
196
|
+
{ dataTypeName, fieldTypeName, fragmentKind, fragmentName }: TypeNamesAndKind,
|
|
281
197
|
): void {
|
|
282
|
-
if (isUndefined(fieldTypeName)) {
|
|
283
|
-
if (fieldPathChecklist.has(requestFieldPath))
|
|
284
|
-
|
|
198
|
+
if (isUndefined(fieldTypeName) || fragmentKind === FRAGMENT_SPREAD) {
|
|
199
|
+
if (fieldPathChecklist.has(requestFieldPath)) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
fieldPathChecklist.set(requestFieldPath, [{ fragmentKind, fragmentName, hasData: !isUndefined(data) }]);
|
|
285
204
|
return;
|
|
286
205
|
}
|
|
287
206
|
|
|
288
|
-
if (dataTypeName !== fieldTypeName)
|
|
207
|
+
if (dataTypeName !== fieldTypeName) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
289
210
|
|
|
290
211
|
const entry = fieldPathChecklist.get(requestFieldPath);
|
|
291
212
|
const checklistValues = entry ? (entry as FieldPathChecklistValue[]) : [];
|
|
292
|
-
|
|
213
|
+
|
|
214
|
+
if (checklistValues.some(({ typeName }) => typeName === dataTypeName)) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
293
217
|
|
|
294
218
|
fieldPathChecklist.set(requestFieldPath, [
|
|
295
219
|
...checklistValues,
|
|
296
|
-
{ hasData: !isUndefined(data), typeName: dataTypeName as string },
|
|
220
|
+
{ fragmentKind, fragmentName, hasData: !isUndefined(data), typeName: dataTypeName as string },
|
|
297
221
|
]);
|
|
298
222
|
}
|
|
299
223
|
|
|
@@ -328,22 +252,35 @@ export class CacheManager implements CacheManagerDef {
|
|
|
328
252
|
return Promise.reject(new TypeError("@graphql-box/cache-manager expected an AST."));
|
|
329
253
|
}
|
|
330
254
|
|
|
331
|
-
const
|
|
255
|
+
const cacheManagerContext: CacheManagerContext = {
|
|
256
|
+
...context,
|
|
257
|
+
fragmentDefinitions: getFragmentDefinitions(ast),
|
|
258
|
+
typeIDKey: this._typeIDKey,
|
|
259
|
+
};
|
|
332
260
|
|
|
261
|
+
const cachedResponseData = await this._retrieveCachedResponseData(requestData, options, cacheManagerContext);
|
|
333
262
|
const { cacheMetadata, data, fieldCount } = cachedResponseData;
|
|
334
|
-
|
|
263
|
+
|
|
264
|
+
if (fieldCount.missing === fieldCount.total) {
|
|
265
|
+
return { updated: requestData };
|
|
266
|
+
}
|
|
335
267
|
|
|
336
268
|
if (!fieldCount.missing) {
|
|
337
|
-
const dataCaching = this._setQueryResponseCacheEntry(hash, { cacheMetadata, data }, options,
|
|
338
|
-
|
|
269
|
+
const dataCaching = this._setQueryResponseCacheEntry(hash, { cacheMetadata, data }, options, cacheManagerContext);
|
|
270
|
+
|
|
271
|
+
if (options.awaitDataCaching) {
|
|
272
|
+
await dataCaching;
|
|
273
|
+
}
|
|
339
274
|
|
|
340
275
|
return { response: { cacheMetadata, data } };
|
|
341
276
|
}
|
|
342
277
|
|
|
343
|
-
this._setPartialQueryResponse(hash, { cacheMetadata, data }, options,
|
|
344
|
-
|
|
345
|
-
const
|
|
346
|
-
|
|
278
|
+
this._setPartialQueryResponse(hash, { cacheMetadata, data }, options, cacheManagerContext);
|
|
279
|
+
const filteredAST = filterQuery(requestData, cachedResponseData, cacheManagerContext);
|
|
280
|
+
const { fragmentDefinitions, typeIDKey, ...rest } = cacheManagerContext;
|
|
281
|
+
assign(context, rest);
|
|
282
|
+
const request = print(filteredAST);
|
|
283
|
+
return { updated: { ast: filteredAST, hash: hashRequest(request), request } };
|
|
347
284
|
}
|
|
348
285
|
|
|
349
286
|
public async checkCacheEntry(
|
|
@@ -362,7 +299,9 @@ export class CacheManager implements CacheManagerDef {
|
|
|
362
299
|
): Promise<ResponseData | false> {
|
|
363
300
|
const result = await this._checkCacheEntry(QUERY_RESPONSES, hash, options, context);
|
|
364
301
|
|
|
365
|
-
if (!result)
|
|
302
|
+
if (!result) {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
366
305
|
|
|
367
306
|
const { cacheMetadata, data } = result.entry as QueryResponseCacheEntry;
|
|
368
307
|
|
|
@@ -383,14 +322,31 @@ export class CacheManager implements CacheManagerDef {
|
|
|
383
322
|
options: RequestOptions,
|
|
384
323
|
context: RequestContext,
|
|
385
324
|
): Promise<ResponseData> {
|
|
325
|
+
const cacheManagerContext: CacheManagerContext = {
|
|
326
|
+
...context,
|
|
327
|
+
fragmentDefinitions: getFragmentDefinitions(updatedRequestData.ast),
|
|
328
|
+
typeIDKey: this._typeIDKey,
|
|
329
|
+
};
|
|
330
|
+
|
|
386
331
|
const dataCaching: Promise<void>[] = [];
|
|
387
|
-
|
|
332
|
+
|
|
333
|
+
const { cacheMetadata, data, hasNext } = await this._resolveRequest(
|
|
334
|
+
updatedRequestData,
|
|
335
|
+
rawResponseData,
|
|
336
|
+
options,
|
|
337
|
+
cacheManagerContext,
|
|
338
|
+
);
|
|
388
339
|
|
|
389
340
|
let partialQueryResponse: PartialQueryResponse | undefined;
|
|
390
341
|
|
|
391
|
-
if (
|
|
342
|
+
if (cacheManagerContext.queryFiltered) {
|
|
392
343
|
dataCaching.push(
|
|
393
|
-
this._setQueryResponseCacheEntry(
|
|
344
|
+
this._setQueryResponseCacheEntry(
|
|
345
|
+
updatedRequestData.hash,
|
|
346
|
+
{ cacheMetadata, data },
|
|
347
|
+
options,
|
|
348
|
+
cacheManagerContext,
|
|
349
|
+
),
|
|
394
350
|
);
|
|
395
351
|
|
|
396
352
|
partialQueryResponse = this._getPartialQueryResponse(requestData.hash);
|
|
@@ -404,13 +360,15 @@ export class CacheManager implements CacheManagerDef {
|
|
|
404
360
|
requestData.hash,
|
|
405
361
|
{ cacheMetadata: responseCacheMetadata, data: responseData },
|
|
406
362
|
options,
|
|
407
|
-
|
|
363
|
+
cacheManagerContext,
|
|
408
364
|
),
|
|
409
365
|
);
|
|
410
366
|
|
|
411
|
-
if (options.awaitDataCaching)
|
|
367
|
+
if (options.awaitDataCaching) {
|
|
368
|
+
await Promise.all(dataCaching);
|
|
369
|
+
}
|
|
412
370
|
|
|
413
|
-
return { cacheMetadata: responseCacheMetadata, data: responseData };
|
|
371
|
+
return { cacheMetadata: responseCacheMetadata, data: responseData, hasNext };
|
|
414
372
|
}
|
|
415
373
|
|
|
416
374
|
public async resolveRequest(
|
|
@@ -419,82 +377,141 @@ export class CacheManager implements CacheManagerDef {
|
|
|
419
377
|
options: RequestOptions,
|
|
420
378
|
context: RequestContext,
|
|
421
379
|
): Promise<ResponseData> {
|
|
422
|
-
|
|
380
|
+
const cacheManagerContext: CacheManagerContext = {
|
|
381
|
+
...context,
|
|
382
|
+
fragmentDefinitions: getFragmentDefinitions(requestData.ast),
|
|
383
|
+
typeIDKey: this._typeIDKey,
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
return this._resolveRequest(requestData, rawResponseData, options, cacheManagerContext);
|
|
423
387
|
}
|
|
424
388
|
|
|
425
|
-
private async
|
|
426
|
-
|
|
389
|
+
private async _analyzeFieldNode(
|
|
390
|
+
fieldNode: FieldNode,
|
|
427
391
|
cachedAncestorFieldData: CachedAncestorFieldData,
|
|
428
392
|
cachedResponseData: CachedResponseData,
|
|
429
393
|
options: RequestOptions,
|
|
430
|
-
context:
|
|
394
|
+
context: CacheManagerContext,
|
|
431
395
|
): Promise<void> {
|
|
432
|
-
if (hasChildFields(
|
|
433
|
-
await this.
|
|
396
|
+
if (hasChildFields(fieldNode)) {
|
|
397
|
+
await this._analyzeParentFieldNode(fieldNode, cachedAncestorFieldData, cachedResponseData, options, context);
|
|
434
398
|
} else {
|
|
435
|
-
await
|
|
399
|
+
await this._analyzeLeafFieldNode(fieldNode, cachedAncestorFieldData, cachedResponseData, options, context);
|
|
436
400
|
}
|
|
437
401
|
}
|
|
438
402
|
|
|
439
|
-
private async
|
|
440
|
-
|
|
403
|
+
private async _analyzeLeafFieldNode(
|
|
404
|
+
fieldNode: FieldNode,
|
|
441
405
|
cachedAncestorFieldData: CachedAncestorFieldData,
|
|
442
406
|
cachedResponseData: CachedResponseData,
|
|
443
407
|
options: RequestOptions,
|
|
444
|
-
context:
|
|
408
|
+
context: CacheManagerContext,
|
|
445
409
|
): Promise<void> {
|
|
446
|
-
const keysAndPaths =
|
|
447
|
-
const { hashedRequestFieldCacheKey, propNameOrIndex,
|
|
410
|
+
const keysAndPaths = buildFieldKeysAndPaths(fieldNode, cachedAncestorFieldData, context);
|
|
411
|
+
const { hashedRequestFieldCacheKey, propNameOrIndex, requestFieldPath } = keysAndPaths;
|
|
448
412
|
const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath);
|
|
413
|
+
const { entityData, fragmentKind, fragmentName, requestFieldPathData, typeName } = cachedAncestorFieldData;
|
|
449
414
|
|
|
450
|
-
const {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const cachedFieldData: CachedFieldData = {
|
|
457
|
-
dataEntityData: CacheManager._getFieldDataFromAncestor(ancestorDataEntityData, propNameOrIndex),
|
|
458
|
-
requestFieldPathData: CacheManager._getFieldDataFromAncestor(ancestorRequestFieldPathData, propNameOrIndex),
|
|
415
|
+
const typeNamesAndKind = {
|
|
416
|
+
dataTypeName: entityData?.__typename || requestFieldPathData?.__typename,
|
|
417
|
+
fieldTypeName: typeName,
|
|
418
|
+
fragmentKind,
|
|
419
|
+
fragmentName,
|
|
459
420
|
};
|
|
460
421
|
|
|
461
|
-
if (CacheManager.
|
|
462
|
-
await this.
|
|
463
|
-
|
|
422
|
+
if (CacheManager._isNodeRequestFieldPath(fieldTypeInfo)) {
|
|
423
|
+
const { cacheability, entry } = await this._retrieveCachedRequestFieldPathData(
|
|
424
|
+
hashedRequestFieldCacheKey,
|
|
425
|
+
options,
|
|
426
|
+
context,
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
CacheManager._setCachedResponseData(
|
|
430
|
+
{ cacheability, data: entry },
|
|
431
|
+
cachedResponseData,
|
|
432
|
+
keysAndPaths,
|
|
433
|
+
typeNamesAndKind,
|
|
434
|
+
options,
|
|
435
|
+
context,
|
|
436
|
+
);
|
|
437
|
+
} else {
|
|
438
|
+
const cachedFieldData =
|
|
439
|
+
CacheManager._getFieldDataFromAncestor(entityData, propNameOrIndex) ||
|
|
440
|
+
CacheManager._getFieldDataFromAncestor(requestFieldPathData, propNameOrIndex);
|
|
464
441
|
|
|
465
|
-
|
|
466
|
-
|
|
442
|
+
CacheManager._setFieldPathChecklist(
|
|
443
|
+
cachedResponseData.fieldPathChecklist,
|
|
444
|
+
{ data: cachedFieldData },
|
|
445
|
+
requestFieldPath,
|
|
446
|
+
typeNamesAndKind,
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
CacheManager._setCachedData(cachedResponseData.data, { data: cachedFieldData }, propNameOrIndex);
|
|
467
450
|
}
|
|
451
|
+
}
|
|
468
452
|
|
|
469
|
-
|
|
453
|
+
private async _analyzeParentFieldNode(
|
|
454
|
+
fieldNode: FieldNode,
|
|
455
|
+
cachedAncestorFieldData: CachedAncestorFieldData,
|
|
456
|
+
cachedResponseData: CachedResponseData,
|
|
457
|
+
options: RequestOptions,
|
|
458
|
+
context: CacheManagerContext,
|
|
459
|
+
): Promise<void> {
|
|
460
|
+
const keysAndPaths = buildFieldKeysAndPaths(fieldNode, cachedAncestorFieldData, context);
|
|
461
|
+
const { propNameOrIndex, requestFieldCacheKey, requestFieldPath } = keysAndPaths;
|
|
462
|
+
const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath) as FieldTypeInfo;
|
|
470
463
|
|
|
471
|
-
const data =
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
464
|
+
const { cacheability, data, entityData, requestFieldPathData } = await this._retrieveCachedParentNodeData(
|
|
465
|
+
cachedAncestorFieldData,
|
|
466
|
+
keysAndPaths,
|
|
467
|
+
fieldTypeInfo,
|
|
468
|
+
options,
|
|
469
|
+
context,
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
const { fragmentKind, fragmentName, typeName } = cachedAncestorFieldData;
|
|
475
473
|
|
|
476
474
|
CacheManager._setCachedResponseData(
|
|
477
475
|
{ cacheability, data },
|
|
478
476
|
cachedResponseData,
|
|
479
477
|
keysAndPaths,
|
|
480
|
-
{ dataTypeName: get(data, TYPE_NAME_KEY), fieldTypeName: typeName },
|
|
478
|
+
{ dataTypeName: get(data, TYPE_NAME_KEY), fieldTypeName: typeName, fragmentKind, fragmentName },
|
|
481
479
|
options,
|
|
482
480
|
context,
|
|
483
481
|
);
|
|
484
482
|
|
|
485
|
-
if (!isObjectLike(data))
|
|
483
|
+
if (!isObjectLike(data)) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
486
|
|
|
487
487
|
const objectLikeData = data as PlainObjectMap | any[];
|
|
488
488
|
const promises: Promise<void>[] = [];
|
|
489
489
|
|
|
490
490
|
iterateChildFields(
|
|
491
|
-
|
|
491
|
+
fieldNode,
|
|
492
492
|
objectLikeData,
|
|
493
|
-
|
|
493
|
+
context.fragmentDefinitions,
|
|
494
|
+
(
|
|
495
|
+
childField: FieldNode,
|
|
496
|
+
childTypeName: string | undefined,
|
|
497
|
+
childFragmentKind: string | undefined,
|
|
498
|
+
childFragmentName: string | undefined,
|
|
499
|
+
childIndex?: number,
|
|
500
|
+
) => {
|
|
494
501
|
promises.push(
|
|
495
|
-
this.
|
|
502
|
+
this._analyzeFieldNode(
|
|
496
503
|
childField,
|
|
497
|
-
{
|
|
504
|
+
{
|
|
505
|
+
cacheability,
|
|
506
|
+
entityData,
|
|
507
|
+
fragmentKind: childFragmentKind,
|
|
508
|
+
fragmentName: childFragmentName,
|
|
509
|
+
index: childIndex,
|
|
510
|
+
requestFieldCacheKey,
|
|
511
|
+
requestFieldPath,
|
|
512
|
+
requestFieldPathData,
|
|
513
|
+
typeName: childTypeName,
|
|
514
|
+
},
|
|
498
515
|
{ ...cachedResponseData, data: cachedResponseData.data[propNameOrIndex] },
|
|
499
516
|
options,
|
|
500
517
|
context,
|
|
@@ -510,12 +527,15 @@ export class CacheManager implements CacheManagerDef {
|
|
|
510
527
|
{ ast }: RequestData,
|
|
511
528
|
{ data, ...otherProps }: RawResponseDataWithMaybeCacheMetadata,
|
|
512
529
|
options: RequestOptions,
|
|
513
|
-
context:
|
|
530
|
+
context: CacheManagerContext,
|
|
514
531
|
): CacheMetadata {
|
|
515
532
|
const cacheMetadata = this._createCacheMetadata({ data, ...otherProps }, context);
|
|
516
533
|
const queryNode = getOperationDefinitions(ast, context.operation)[0];
|
|
517
534
|
const fieldsAndTypeNames = getChildFields(queryNode);
|
|
518
|
-
|
|
535
|
+
|
|
536
|
+
if (!fieldsAndTypeNames) {
|
|
537
|
+
return cacheMetadata;
|
|
538
|
+
}
|
|
519
539
|
|
|
520
540
|
fieldsAndTypeNames.forEach(({ fieldNode }) =>
|
|
521
541
|
this._setFieldCacheability(
|
|
@@ -534,16 +554,20 @@ export class CacheManager implements CacheManagerDef {
|
|
|
534
554
|
cacheType: CacheTypes,
|
|
535
555
|
hash: string,
|
|
536
556
|
options: RequestOptions,
|
|
537
|
-
context:
|
|
557
|
+
context: CacheManagerContext,
|
|
538
558
|
): Promise<CheckCacheEntryResult | false> {
|
|
539
559
|
try {
|
|
540
560
|
const cacheability = await this._hasCacheEntry(cacheType, hash);
|
|
541
561
|
|
|
542
|
-
if (!cacheability || !CacheManager._isValid(cacheability))
|
|
562
|
+
if (!cacheability || !CacheManager._isValid(cacheability)) {
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
543
565
|
|
|
544
566
|
const entry = await this._getCacheEntry(cacheType, hash, options, context);
|
|
545
567
|
|
|
546
|
-
if (
|
|
568
|
+
if (isUndefined(entry)) {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
547
571
|
|
|
548
572
|
return { cacheability, entry };
|
|
549
573
|
} catch (error) {
|
|
@@ -553,152 +577,23 @@ export class CacheManager implements CacheManagerDef {
|
|
|
553
577
|
|
|
554
578
|
private _createCacheMetadata(
|
|
555
579
|
{ _cacheMetadata, headers }: RawResponseDataWithMaybeCacheMetadata,
|
|
556
|
-
{ operation }:
|
|
580
|
+
{ operation }: CacheManagerContext,
|
|
557
581
|
): CacheMetadata {
|
|
558
582
|
const cacheMetadata = new Map();
|
|
559
|
-
const cacheControl = (headers && headers.get(HEADER_CACHE_CONTROL)) || this._fallbackOperationCacheability;
|
|
560
|
-
const cacheability = new Cacheability({ cacheControl });
|
|
561
|
-
cacheMetadata.set(operation, cacheability);
|
|
562
|
-
if (_cacheMetadata) rehydrateCacheMetadata(_cacheMetadata, cacheMetadata);
|
|
563
|
-
return cacheMetadata;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
private _filterField(
|
|
567
|
-
field: FieldNode,
|
|
568
|
-
fieldPathChecklist: FieldPathChecklist,
|
|
569
|
-
ancestorRequestFieldPath: string,
|
|
570
|
-
context: RequestContext,
|
|
571
|
-
): boolean {
|
|
572
|
-
const fieldsAndTypeNames = getChildFields(field);
|
|
573
|
-
if (!fieldsAndTypeNames) return false;
|
|
574
|
-
|
|
575
|
-
for (let i = fieldsAndTypeNames.length - 1; i >= 0; i -= 1) {
|
|
576
|
-
const { fieldNode: childField, typeName: childTypeName } = fieldsAndTypeNames[i];
|
|
577
|
-
const childFieldName = getName(childField);
|
|
578
|
-
|
|
579
|
-
if (childFieldName === this._typeIDKey || childFieldName === TYPE_NAME_KEY) continue;
|
|
580
|
-
|
|
581
|
-
const { requestFieldPath } = CacheManager._getFieldKeysAndPaths(childField, {
|
|
582
|
-
requestFieldPath: ancestorRequestFieldPath,
|
|
583
|
-
});
|
|
584
|
-
|
|
585
|
-
const { hasData, typeUnused } = CacheManager._checkFieldPathChecklist(
|
|
586
|
-
fieldPathChecklist.get(requestFieldPath),
|
|
587
|
-
childTypeName,
|
|
588
|
-
);
|
|
589
|
-
|
|
590
|
-
if (hasData || typeUnused) {
|
|
591
|
-
if (!hasChildFields(childField)) {
|
|
592
|
-
deleteChildFields(field, childField);
|
|
593
|
-
} else if (this._filterField(childField, fieldPathChecklist, requestFieldPath, context)) {
|
|
594
|
-
deleteChildFields(field, childField);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
this._filterInlineFragments(field);
|
|
600
|
-
this._filterIDsAndTypeNames(field);
|
|
601
|
-
return !hasChildFields(field);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
private _filterIDsAndTypeNames(field: FieldNode): boolean {
|
|
605
|
-
const fieldsAndTypeNames = getChildFields(field);
|
|
606
|
-
if (!fieldsAndTypeNames || fieldsAndTypeNames.length > 3) return false;
|
|
607
|
-
|
|
608
|
-
const fieldNames = fieldsAndTypeNames.map(({ fieldNode }) => getName(fieldNode) as string);
|
|
609
|
-
|
|
610
|
-
if (fieldNames.length === 2 && fieldNames.every(name => name === this._typeIDKey || name === TYPE_NAME_KEY)) {
|
|
611
|
-
deleteChildFields(
|
|
612
|
-
field,
|
|
613
|
-
fieldsAndTypeNames.map(({ fieldNode }) => fieldNode),
|
|
614
|
-
);
|
|
615
|
-
|
|
616
|
-
return true;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
if ((fieldNames.length === 1 && fieldNames[0] === this._typeIDKey) || fieldNames[0] === TYPE_NAME_KEY) {
|
|
620
|
-
const { fieldNode } = fieldsAndTypeNames[0];
|
|
621
|
-
deleteChildFields(field, fieldNode);
|
|
622
|
-
return true;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
return false;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
private _filterInlineFragments(field: FieldNode): boolean {
|
|
629
|
-
const inlineFragments = getInlineFragments(field);
|
|
630
|
-
let filtered = false;
|
|
631
|
-
|
|
632
|
-
inlineFragments.forEach(fragment => {
|
|
633
|
-
const fieldsAndTypeNames = getChildFields(fragment);
|
|
634
|
-
|
|
635
|
-
if (!fieldsAndTypeNames || !fieldsAndTypeNames.length) {
|
|
636
|
-
deleteInlineFragments(field, fragment);
|
|
637
|
-
filtered = true;
|
|
638
|
-
return;
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
if (fieldsAndTypeNames.length === 1) {
|
|
642
|
-
const { fieldNode } = fieldsAndTypeNames[0];
|
|
643
583
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
}
|
|
584
|
+
const cacheability = deriveOpCacheability({
|
|
585
|
+
_cacheMetadata,
|
|
586
|
+
fallback: this._fallbackOperationCacheability,
|
|
587
|
+
headers,
|
|
649
588
|
});
|
|
650
589
|
|
|
651
|
-
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
private _filterQuery(
|
|
655
|
-
{ ast }: RequestData,
|
|
656
|
-
{ fieldPathChecklist }: CachedResponseData,
|
|
657
|
-
context: RequestContext,
|
|
658
|
-
): void {
|
|
659
|
-
const queryNode = getOperationDefinitions(ast, context.operation)[0];
|
|
660
|
-
const fieldsAndTypeNames = getChildFields(queryNode);
|
|
661
|
-
if (!fieldsAndTypeNames) return;
|
|
662
|
-
|
|
663
|
-
for (let i = fieldsAndTypeNames.length - 1; i >= 0; i -= 1) {
|
|
664
|
-
const { fieldNode } = fieldsAndTypeNames[i];
|
|
665
|
-
|
|
666
|
-
const { requestFieldPath } = CacheManager._getFieldKeysAndPaths(fieldNode, {
|
|
667
|
-
requestFieldPath: context.operation,
|
|
668
|
-
});
|
|
590
|
+
cacheMetadata.set(operation, cacheability);
|
|
669
591
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
}
|
|
592
|
+
if (_cacheMetadata) {
|
|
593
|
+
rehydrateCacheMetadata(_cacheMetadata, cacheMetadata);
|
|
673
594
|
}
|
|
674
595
|
|
|
675
|
-
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
private async _getCachedResponseData(
|
|
679
|
-
{ ast }: RequestData,
|
|
680
|
-
options: RequestOptions,
|
|
681
|
-
context: RequestContext,
|
|
682
|
-
): Promise<CachedResponseData> {
|
|
683
|
-
const cachedResponseData: CachedResponseData = {
|
|
684
|
-
cacheMetadata: new Map(),
|
|
685
|
-
data: {},
|
|
686
|
-
fieldCount: { missing: 0, total: 0 },
|
|
687
|
-
fieldPathChecklist: new Map(),
|
|
688
|
-
};
|
|
689
|
-
|
|
690
|
-
const queryNode = getOperationDefinitions(ast, context.operation)[0];
|
|
691
|
-
const fieldsAndTypeNames = getChildFields(queryNode);
|
|
692
|
-
if (!fieldsAndTypeNames) return cachedResponseData;
|
|
693
|
-
|
|
694
|
-
await Promise.all(
|
|
695
|
-
fieldsAndTypeNames.map(({ fieldNode }) =>
|
|
696
|
-
this._analyzeField(fieldNode, { requestFieldPath: context.operation }, cachedResponseData, options, context),
|
|
697
|
-
),
|
|
698
|
-
);
|
|
699
|
-
|
|
700
|
-
cachedResponseData.fieldCount = CacheManager._countFieldPathChecklist(cachedResponseData.fieldPathChecklist);
|
|
701
|
-
return cachedResponseData;
|
|
596
|
+
return cacheMetadata;
|
|
702
597
|
}
|
|
703
598
|
|
|
704
599
|
@logCacheQuery()
|
|
@@ -706,8 +601,8 @@ export class CacheManager implements CacheManagerDef {
|
|
|
706
601
|
cacheType: CacheTypes,
|
|
707
602
|
hash: string,
|
|
708
603
|
_options: RequestOptions,
|
|
709
|
-
_context:
|
|
710
|
-
): Promise<
|
|
604
|
+
_context: CacheManagerContext,
|
|
605
|
+
): Promise<any> {
|
|
711
606
|
try {
|
|
712
607
|
return await this._cache.get(`${cacheType}::${hash}`);
|
|
713
608
|
} catch (errors) {
|
|
@@ -722,7 +617,9 @@ export class CacheManager implements CacheManagerDef {
|
|
|
722
617
|
}
|
|
723
618
|
|
|
724
619
|
private _getResponseData(responseData: PlainObjectMap, partialQueryResponse?: PartialQueryResponse): PlainObjectMap {
|
|
725
|
-
if (!partialQueryResponse)
|
|
620
|
+
if (!partialQueryResponse) {
|
|
621
|
+
return responseData;
|
|
622
|
+
}
|
|
726
623
|
|
|
727
624
|
return this._mergeObjects(partialQueryResponse.data, responseData);
|
|
728
625
|
}
|
|
@@ -736,9 +633,18 @@ export class CacheManager implements CacheManagerDef {
|
|
|
736
633
|
}
|
|
737
634
|
|
|
738
635
|
private _isFieldEntity(fieldData: any, { isEntity, possibleTypes }: FieldTypeInfo): boolean {
|
|
739
|
-
if (!get(fieldData, this._typeIDKey, null))
|
|
740
|
-
|
|
741
|
-
|
|
636
|
+
if (!get(fieldData, this._typeIDKey, null)) {
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (isEntity) {
|
|
641
|
+
return true;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
if (!possibleTypes.length) {
|
|
645
|
+
return false;
|
|
646
|
+
}
|
|
647
|
+
|
|
742
648
|
return possibleTypes.some(type => type.typeName === fieldData.__typename);
|
|
743
649
|
}
|
|
744
650
|
|
|
@@ -748,43 +654,55 @@ export class CacheManager implements CacheManagerDef {
|
|
|
748
654
|
});
|
|
749
655
|
}
|
|
750
656
|
|
|
751
|
-
private async
|
|
657
|
+
private async _parseEntityAndRequestFieldPathCacheEntryData(
|
|
752
658
|
field: FieldNode,
|
|
753
659
|
ancestorKeysAndPaths: AncestorKeysAndPaths,
|
|
754
|
-
{ cacheMetadata,
|
|
660
|
+
{ cacheMetadata, entityData, requestFieldPathData }: ResponseDataForCaching,
|
|
755
661
|
options: RequestOptions,
|
|
756
|
-
context:
|
|
662
|
+
context: CacheManagerContext,
|
|
757
663
|
): Promise<void> {
|
|
758
|
-
const keysAndPaths =
|
|
664
|
+
const keysAndPaths = buildFieldKeysAndPaths(field, ancestorKeysAndPaths, context);
|
|
759
665
|
const { requestFieldCacheKey, requestFieldPath, responseDataPath } = keysAndPaths;
|
|
760
|
-
const fieldData = get(requestFieldPathData, responseDataPath
|
|
761
|
-
|
|
666
|
+
const fieldData = get(requestFieldPathData, responseDataPath);
|
|
667
|
+
const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath);
|
|
762
668
|
|
|
763
|
-
|
|
764
|
-
|
|
669
|
+
if (!isObjectLike(fieldData) && !fieldTypeInfo?.hasDirectives) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
765
672
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
objectLikeFieldData,
|
|
769
|
-
(childField: FieldNode, _typeName: string | undefined, childIndex?: number) => {
|
|
770
|
-
promises.push(
|
|
771
|
-
this._parseFieldDataEntityAndRequestFieldPathCacheEntryData(
|
|
772
|
-
childField,
|
|
773
|
-
{ index: childIndex, requestFieldCacheKey, requestFieldPath, responseDataPath },
|
|
774
|
-
{ cacheMetadata, dataEntityData, requestFieldPathData },
|
|
775
|
-
options,
|
|
776
|
-
context,
|
|
777
|
-
),
|
|
778
|
-
);
|
|
779
|
-
},
|
|
780
|
-
);
|
|
673
|
+
if (isObjectLike(fieldData)) {
|
|
674
|
+
const promises: Promise<void>[] = [];
|
|
781
675
|
|
|
782
|
-
|
|
676
|
+
iterateChildFields(
|
|
677
|
+
field,
|
|
678
|
+
fieldData as PlainObjectMap | any[],
|
|
679
|
+
context.fragmentDefinitions,
|
|
680
|
+
(
|
|
681
|
+
childField: FieldNode,
|
|
682
|
+
_typeName: string | undefined,
|
|
683
|
+
_fragmentKind: string | undefined,
|
|
684
|
+
_fragmentName: string | undefined,
|
|
685
|
+
childIndex?: number,
|
|
686
|
+
) => {
|
|
687
|
+
promises.push(
|
|
688
|
+
this._parseEntityAndRequestFieldPathCacheEntryData(
|
|
689
|
+
childField,
|
|
690
|
+
{ index: childIndex, requestFieldCacheKey, requestFieldPath, responseDataPath },
|
|
691
|
+
{ cacheMetadata, entityData, requestFieldPathData },
|
|
692
|
+
options,
|
|
693
|
+
context,
|
|
694
|
+
),
|
|
695
|
+
);
|
|
696
|
+
},
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
await Promise.all(promises);
|
|
700
|
+
}
|
|
783
701
|
|
|
784
|
-
await this.
|
|
702
|
+
await this._setEntityAndRequestFieldPathCacheEntry(
|
|
785
703
|
field,
|
|
786
704
|
keysAndPaths,
|
|
787
|
-
{ cacheMetadata,
|
|
705
|
+
{ cacheMetadata, entityData, requestFieldPathData },
|
|
788
706
|
options,
|
|
789
707
|
context,
|
|
790
708
|
);
|
|
@@ -794,24 +712,158 @@ export class CacheManager implements CacheManagerDef {
|
|
|
794
712
|
requestData: RequestData,
|
|
795
713
|
rawResponseData: RawResponseDataWithMaybeCacheMetadata,
|
|
796
714
|
options: RequestOptions,
|
|
797
|
-
context:
|
|
715
|
+
context: CacheManagerContext,
|
|
798
716
|
): Promise<ResponseData> {
|
|
717
|
+
const normalizedResponseData = rawResponseData.path ? normalizeResponseData(rawResponseData) : rawResponseData;
|
|
799
718
|
const dataCaching: Promise<void>[] = [];
|
|
800
|
-
const cacheMetadata = this._buildCacheMetadata(requestData,
|
|
801
|
-
const { data } =
|
|
719
|
+
const cacheMetadata = this._buildCacheMetadata(requestData, normalizedResponseData, options, context);
|
|
720
|
+
const { data, hasNext } = normalizedResponseData;
|
|
802
721
|
|
|
803
722
|
dataCaching.push(
|
|
804
|
-
this.
|
|
723
|
+
this._setEntityAndRequestFieldPathCacheEntries(
|
|
805
724
|
requestData,
|
|
806
|
-
{ cacheMetadata,
|
|
725
|
+
{ cacheMetadata, entityData: cloneDeep(data), requestFieldPathData: cloneDeep(data) },
|
|
807
726
|
options,
|
|
808
727
|
context,
|
|
809
728
|
),
|
|
810
729
|
);
|
|
811
730
|
|
|
812
|
-
if (options.awaitDataCaching)
|
|
731
|
+
if (options.awaitDataCaching) {
|
|
732
|
+
await Promise.all(dataCaching);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
return { cacheMetadata, data, hasNext };
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
private async _retrieveCachedEntityData(
|
|
739
|
+
validTypeIDValue: string | number,
|
|
740
|
+
{ possibleTypes, typeName }: FieldTypeInfo,
|
|
741
|
+
options: RequestOptions,
|
|
742
|
+
context: CacheManagerContext,
|
|
743
|
+
) {
|
|
744
|
+
const typeNames = [...possibleTypes.map(type => type.typeName), typeName];
|
|
745
|
+
|
|
746
|
+
const checkResults = await Promise.all(
|
|
747
|
+
typeNames.map(name => this._checkCacheEntry(DATA_ENTITIES, `${name}::${validTypeIDValue}`, options, context)),
|
|
748
|
+
);
|
|
749
|
+
|
|
750
|
+
const validResults = checkResults.filter(result => !!result) as CheckCacheEntryResult[];
|
|
751
|
+
let validResult: CheckCacheEntryResult | undefined;
|
|
752
|
+
|
|
753
|
+
if (validResults.length === 1) {
|
|
754
|
+
validResult = validResults[0];
|
|
755
|
+
} else if (validResults.length > 1) {
|
|
756
|
+
validResults.sort(({ cacheability: a }, { cacheability: b }) => a.metadata.ttl - b.metadata.ttl);
|
|
757
|
+
|
|
758
|
+
validResult = {
|
|
759
|
+
cacheability: validResults[0].cacheability,
|
|
760
|
+
entry: validResults.reduce((obj, { entry }) => this._mergeObjects(obj, entry), {}),
|
|
761
|
+
};
|
|
762
|
+
}
|
|
813
763
|
|
|
814
|
-
return {
|
|
764
|
+
return (validResult || {}) as Partial<CheckCacheEntryResult>;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
private async _retrieveCachedParentNodeData(
|
|
768
|
+
{ entityData: ancestorEntityData, requestFieldPathData: ancestorRequestFieldPathData }: CachedAncestorFieldData,
|
|
769
|
+
{ hashedRequestFieldCacheKey, propNameOrIndex }: KeysAndPaths,
|
|
770
|
+
fieldTypeInfo: FieldTypeInfo,
|
|
771
|
+
options: RequestOptions,
|
|
772
|
+
context: CacheManagerContext,
|
|
773
|
+
) {
|
|
774
|
+
let entityData = CacheManager._getFieldDataFromAncestor(ancestorEntityData, propNameOrIndex);
|
|
775
|
+
let requestFieldPathData = CacheManager._getFieldDataFromAncestor(ancestorRequestFieldPathData, propNameOrIndex);
|
|
776
|
+
let cacheability: Cacheability | undefined;
|
|
777
|
+
|
|
778
|
+
if (CacheManager._isNodeRequestFieldPath(fieldTypeInfo)) {
|
|
779
|
+
const { cacheability: entryCacheability, entry } = await this._retrieveCachedRequestFieldPathData(
|
|
780
|
+
hashedRequestFieldCacheKey,
|
|
781
|
+
options,
|
|
782
|
+
context,
|
|
783
|
+
);
|
|
784
|
+
|
|
785
|
+
if (entry) {
|
|
786
|
+
requestFieldPathData = this._mergeObjects(requestFieldPathData, entry);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (entryCacheability) {
|
|
790
|
+
cacheability = entryCacheability;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const validTypeIDValue = getValidTypeIDValue(requestFieldPathData, fieldTypeInfo, this._typeIDKey);
|
|
795
|
+
|
|
796
|
+
if (CacheManager._isNodeEntity(fieldTypeInfo) && validTypeIDValue) {
|
|
797
|
+
const { cacheability: entryCacheability, entry } = await this._retrieveCachedEntityData(
|
|
798
|
+
validTypeIDValue,
|
|
799
|
+
fieldTypeInfo,
|
|
800
|
+
options,
|
|
801
|
+
context,
|
|
802
|
+
);
|
|
803
|
+
|
|
804
|
+
if (entry) {
|
|
805
|
+
entityData = this._mergeObjects(entityData, entry);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (entryCacheability && (!cacheability || entryCacheability.metadata.ttl > cacheability?.metadata.ttl)) {
|
|
809
|
+
cacheability = entryCacheability;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
const data =
|
|
814
|
+
!isUndefined(requestFieldPathData) || !isUndefined(entityData)
|
|
815
|
+
? this._mergeObjects(requestFieldPathData, entityData)
|
|
816
|
+
: entityData ?? requestFieldPathData;
|
|
817
|
+
|
|
818
|
+
return {
|
|
819
|
+
cacheability,
|
|
820
|
+
data,
|
|
821
|
+
entityData,
|
|
822
|
+
requestFieldPathData,
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
private async _retrieveCachedRequestFieldPathData(
|
|
827
|
+
hash: string,
|
|
828
|
+
options: RequestOptions,
|
|
829
|
+
context: CacheManagerContext,
|
|
830
|
+
) {
|
|
831
|
+
return (this._checkCacheEntry(REQUEST_FIELD_PATHS, hash, options, context) || {}) as Partial<CheckCacheEntryResult>;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
private async _retrieveCachedResponseData(
|
|
835
|
+
{ ast }: RequestData,
|
|
836
|
+
options: RequestOptions,
|
|
837
|
+
context: CacheManagerContext,
|
|
838
|
+
): Promise<CachedResponseData> {
|
|
839
|
+
const cachedResponseData: CachedResponseData = {
|
|
840
|
+
cacheMetadata: new Map(),
|
|
841
|
+
data: {},
|
|
842
|
+
fieldCount: { missing: 0, total: 0 },
|
|
843
|
+
fieldPathChecklist: new Map(),
|
|
844
|
+
};
|
|
845
|
+
|
|
846
|
+
const queryNode = getOperationDefinitions(ast, context.operation)[0];
|
|
847
|
+
const fieldsAndTypeNames = getChildFields(queryNode);
|
|
848
|
+
|
|
849
|
+
if (!fieldsAndTypeNames) {
|
|
850
|
+
return cachedResponseData;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
await Promise.all(
|
|
854
|
+
fieldsAndTypeNames.map(({ fieldNode }) =>
|
|
855
|
+
this._analyzeFieldNode(
|
|
856
|
+
fieldNode,
|
|
857
|
+
{ requestFieldPath: context.operation },
|
|
858
|
+
cachedResponseData,
|
|
859
|
+
options,
|
|
860
|
+
context,
|
|
861
|
+
),
|
|
862
|
+
),
|
|
863
|
+
);
|
|
864
|
+
|
|
865
|
+
cachedResponseData.fieldCount = CacheManager._countFieldPathChecklist(cachedResponseData.fieldPathChecklist);
|
|
866
|
+
return cachedResponseData;
|
|
815
867
|
}
|
|
816
868
|
|
|
817
869
|
@logCacheEntry()
|
|
@@ -821,7 +873,7 @@ export class CacheManager implements CacheManagerDef {
|
|
|
821
873
|
value: any,
|
|
822
874
|
cachemapOptions: CachemapOptions,
|
|
823
875
|
_options: RequestOptions,
|
|
824
|
-
_context:
|
|
876
|
+
_context: CacheManagerContext,
|
|
825
877
|
): Promise<void> {
|
|
826
878
|
try {
|
|
827
879
|
await this._cache.set(`${cacheType}::${hash}`, cloneDeep(value), cachemapOptions);
|
|
@@ -830,19 +882,22 @@ export class CacheManager implements CacheManagerDef {
|
|
|
830
882
|
}
|
|
831
883
|
}
|
|
832
884
|
|
|
833
|
-
private async
|
|
885
|
+
private async _setEntityAndRequestFieldPathCacheEntries(
|
|
834
886
|
requestData: RequestData,
|
|
835
887
|
responseData: ResponseDataForCaching,
|
|
836
888
|
options: RequestOptions,
|
|
837
|
-
context:
|
|
889
|
+
context: CacheManagerContext,
|
|
838
890
|
): Promise<void> {
|
|
839
891
|
const operationNode = getOperationDefinitions(requestData.ast, context.operation)[0];
|
|
840
892
|
const fieldsAndTypeNames = getChildFields(operationNode);
|
|
841
|
-
|
|
893
|
+
|
|
894
|
+
if (!fieldsAndTypeNames) {
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
842
897
|
|
|
843
898
|
await Promise.all(
|
|
844
899
|
fieldsAndTypeNames.map(({ fieldNode }) => {
|
|
845
|
-
return this.
|
|
900
|
+
return this._parseEntityAndRequestFieldPathCacheEntryData(
|
|
846
901
|
fieldNode,
|
|
847
902
|
{ requestFieldPath: context.operation },
|
|
848
903
|
responseData,
|
|
@@ -853,80 +908,74 @@ export class CacheManager implements CacheManagerDef {
|
|
|
853
908
|
);
|
|
854
909
|
}
|
|
855
910
|
|
|
856
|
-
private async
|
|
857
|
-
|
|
858
|
-
|
|
911
|
+
private async _setEntityAndRequestFieldPathCacheEntry(
|
|
912
|
+
field: FieldNode,
|
|
913
|
+
keysAndPaths: KeysAndPaths,
|
|
914
|
+
{ cacheMetadata, entityData, requestFieldPathData }: ResponseDataForCaching,
|
|
859
915
|
options: RequestOptions,
|
|
860
|
-
context:
|
|
916
|
+
context: CacheManagerContext,
|
|
861
917
|
) {
|
|
862
|
-
const
|
|
863
|
-
|
|
864
|
-
const
|
|
918
|
+
const { requestFieldPath, responseDataPath } = keysAndPaths;
|
|
919
|
+
const fieldData = get(entityData, responseDataPath);
|
|
920
|
+
const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath);
|
|
921
|
+
const cacheability = cacheMetadata.get(requestFieldPath);
|
|
865
922
|
|
|
866
|
-
if (!
|
|
867
|
-
|
|
923
|
+
if (isUndefined(fieldData) || !fieldTypeInfo || !cacheability) {
|
|
924
|
+
return;
|
|
868
925
|
}
|
|
869
926
|
|
|
870
|
-
|
|
871
|
-
const fieldTypeName = fieldTypeInfo.isEntity ? fieldTypeInfo.typeName : fieldData.__typename;
|
|
872
|
-
const entityDataKey = `${fieldTypeName}::${fieldData[this._typeIDKey]}`;
|
|
873
|
-
const result = await this._checkCacheEntry(DATA_ENTITIES, entityDataKey, options, context);
|
|
874
|
-
|
|
875
|
-
if (result) {
|
|
876
|
-
fieldData = this._mergeObjects(result.entry, fieldData);
|
|
877
|
-
}
|
|
927
|
+
const promises: Promise<void>[] = [];
|
|
878
928
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
{
|
|
929
|
+
promises.push(
|
|
930
|
+
this._setRequestFieldPathCacheEntry(
|
|
931
|
+
field,
|
|
932
|
+
keysAndPaths,
|
|
933
|
+
{ cacheability, data: requestFieldPathData, fieldTypeInfo },
|
|
884
934
|
options,
|
|
885
935
|
context,
|
|
886
|
-
)
|
|
936
|
+
),
|
|
937
|
+
);
|
|
938
|
+
|
|
939
|
+
const isEntity = this._isFieldEntity(fieldData, fieldTypeInfo);
|
|
887
940
|
|
|
888
|
-
|
|
941
|
+
if (!isEntity && fieldTypeInfo.hasArguments) {
|
|
942
|
+
unset(entityData, responseDataPath);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
if (isEntity) {
|
|
946
|
+
promises.push(
|
|
947
|
+
this._setEntityCacheEntry(keysAndPaths, { cacheability, data: entityData, fieldTypeInfo }, options, context),
|
|
948
|
+
);
|
|
889
949
|
}
|
|
950
|
+
|
|
951
|
+
await Promise.all(promises);
|
|
890
952
|
}
|
|
891
953
|
|
|
892
|
-
private async
|
|
893
|
-
|
|
894
|
-
{
|
|
954
|
+
private async _setEntityCacheEntry(
|
|
955
|
+
{ responseDataPath }: KeysAndPaths,
|
|
956
|
+
{ cacheability, data, fieldTypeInfo }: DataForCachingEntry,
|
|
895
957
|
options: RequestOptions,
|
|
896
|
-
context:
|
|
897
|
-
)
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
const validTypeIDValue = typeIDValue || requestFieldPathDataIDValue;
|
|
903
|
-
if (!validTypeIDValue) return;
|
|
958
|
+
context: CacheManagerContext,
|
|
959
|
+
) {
|
|
960
|
+
let fieldData = get(data, responseDataPath);
|
|
961
|
+
const fieldTypeName = fieldTypeInfo.isEntity ? fieldTypeInfo.typeName : fieldData.__typename;
|
|
962
|
+
const entityDataKey = `${fieldTypeName}::${fieldData[this._typeIDKey]}`;
|
|
963
|
+
const result = await this._checkCacheEntry(DATA_ENTITIES, entityDataKey, options, context);
|
|
904
964
|
|
|
905
|
-
|
|
965
|
+
if (result) {
|
|
966
|
+
fieldData = this._mergeObjects(result.entry, fieldData);
|
|
967
|
+
}
|
|
906
968
|
|
|
907
|
-
|
|
908
|
-
|
|
969
|
+
await this._setCacheEntry(
|
|
970
|
+
DATA_ENTITIES,
|
|
971
|
+
entityDataKey,
|
|
972
|
+
fieldData,
|
|
973
|
+
{ cacheHeaders: { cacheControl: cacheability.printCacheControl() }, tag: options.tag },
|
|
974
|
+
options,
|
|
975
|
+
context,
|
|
909
976
|
);
|
|
910
977
|
|
|
911
|
-
|
|
912
|
-
let validResult: CheckCacheEntryResult | undefined;
|
|
913
|
-
|
|
914
|
-
if (validResults.length === 1) {
|
|
915
|
-
validResult = validResults[0];
|
|
916
|
-
} else if (validResults.length > 1) {
|
|
917
|
-
validResults.sort(({ cacheability: a }, { cacheability: b }) => a.metadata.ttl - b.metadata.ttl);
|
|
918
|
-
|
|
919
|
-
validResult = {
|
|
920
|
-
cacheability: validResults[0].cacheability,
|
|
921
|
-
entry: validResults.reduce((obj, { entry }) => this._mergeObjects(obj, entry), {}),
|
|
922
|
-
};
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
if (validResult) {
|
|
926
|
-
const { cacheability, entry } = validResult;
|
|
927
|
-
if (cacheability && !cachedFieldData.cacheability) cachedFieldData.cacheability = cacheability;
|
|
928
|
-
if (entry) cachedFieldData.dataEntityData = entry;
|
|
929
|
-
}
|
|
978
|
+
set(data, responseDataPath, { __cacheKey: `${DATA_ENTITIES}::${entityDataKey}` });
|
|
930
979
|
}
|
|
931
980
|
|
|
932
981
|
private _setFieldCacheability(
|
|
@@ -934,74 +983,52 @@ export class CacheManager implements CacheManagerDef {
|
|
|
934
983
|
ancestorKeysAndPaths: AncestorKeysAndPaths,
|
|
935
984
|
{ cacheMetadata, data }: ResponseData,
|
|
936
985
|
options: RequestOptions,
|
|
937
|
-
context:
|
|
986
|
+
context: CacheManagerContext,
|
|
938
987
|
): void {
|
|
939
988
|
const { requestFieldPath: ancestorRequestFieldPath } = ancestorKeysAndPaths;
|
|
940
|
-
const keysAndPaths =
|
|
989
|
+
const keysAndPaths = buildFieldKeysAndPaths(field, ancestorKeysAndPaths, context);
|
|
941
990
|
const { requestFieldPath, responseDataPath } = keysAndPaths;
|
|
942
|
-
const fieldData = get(data, responseDataPath
|
|
943
|
-
if (!isObjectLike(fieldData)) return;
|
|
944
|
-
|
|
945
|
-
const objectLikeFieldData = fieldData as PlainObjectMap | any[];
|
|
946
|
-
this._setFieldTypeCacheDirective(cacheMetadata, { ancestorRequestFieldPath, requestFieldPath }, context);
|
|
947
|
-
|
|
948
|
-
iterateChildFields(
|
|
949
|
-
field,
|
|
950
|
-
objectLikeFieldData,
|
|
951
|
-
(childField: FieldNode, _typeName: string | undefined, childIndex?: number) => {
|
|
952
|
-
this._setFieldCacheability(
|
|
953
|
-
childField,
|
|
954
|
-
{ index: childIndex, requestFieldPath, responseDataPath },
|
|
955
|
-
{ cacheMetadata, data },
|
|
956
|
-
options,
|
|
957
|
-
context,
|
|
958
|
-
);
|
|
959
|
-
},
|
|
960
|
-
);
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
private async _setFieldDataEntityAndRequestFieldPathCacheEntry(
|
|
964
|
-
field: FieldNode,
|
|
965
|
-
keysAndPaths: KeysAndPaths,
|
|
966
|
-
{ cacheMetadata, dataEntityData, requestFieldPathData }: ResponseDataForCaching,
|
|
967
|
-
options: RequestOptions,
|
|
968
|
-
context: RequestContext,
|
|
969
|
-
) {
|
|
970
|
-
const { requestFieldPath } = keysAndPaths;
|
|
991
|
+
const fieldData = get(data, responseDataPath);
|
|
971
992
|
const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath);
|
|
972
|
-
const cacheability = cacheMetadata.get(requestFieldPath);
|
|
973
|
-
if (!fieldTypeInfo || !cacheability) return;
|
|
974
993
|
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
this._setRequestFieldPathCacheEntry(
|
|
979
|
-
field,
|
|
980
|
-
keysAndPaths,
|
|
981
|
-
{ cacheability, data: requestFieldPathData, fieldTypeInfo },
|
|
982
|
-
options,
|
|
983
|
-
context,
|
|
984
|
-
),
|
|
985
|
-
);
|
|
994
|
+
if (!isObjectLike(fieldData) && !fieldTypeInfo?.hasDirectives) {
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
986
997
|
|
|
987
|
-
|
|
988
|
-
this._setDataEntityCacheEntry(
|
|
989
|
-
keysAndPaths,
|
|
990
|
-
{ cacheability, data: dataEntityData, fieldTypeInfo },
|
|
991
|
-
options,
|
|
992
|
-
context,
|
|
993
|
-
),
|
|
994
|
-
);
|
|
998
|
+
this._setFieldTypeCacheDirective(cacheMetadata, { ancestorRequestFieldPath, requestFieldPath }, context);
|
|
995
999
|
|
|
996
|
-
|
|
1000
|
+
if (isObjectLike(fieldData)) {
|
|
1001
|
+
iterateChildFields(
|
|
1002
|
+
field,
|
|
1003
|
+
fieldData as PlainObjectMap | any[],
|
|
1004
|
+
context.fragmentDefinitions,
|
|
1005
|
+
(
|
|
1006
|
+
childField: FieldNode,
|
|
1007
|
+
_typeName: string | undefined,
|
|
1008
|
+
_fragmentKind: string | undefined,
|
|
1009
|
+
_fragmentName: string | undefined,
|
|
1010
|
+
childIndex?: number,
|
|
1011
|
+
) => {
|
|
1012
|
+
this._setFieldCacheability(
|
|
1013
|
+
childField,
|
|
1014
|
+
{ index: childIndex, requestFieldPath, responseDataPath },
|
|
1015
|
+
{ cacheMetadata, data },
|
|
1016
|
+
options,
|
|
1017
|
+
context,
|
|
1018
|
+
);
|
|
1019
|
+
},
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
997
1022
|
}
|
|
998
1023
|
|
|
999
1024
|
private _setFieldTypeCacheDirective(
|
|
1000
1025
|
cacheMetadata: CacheMetadata,
|
|
1001
1026
|
{ ancestorRequestFieldPath, requestFieldPath }: { ancestorRequestFieldPath?: string; requestFieldPath: string },
|
|
1002
|
-
{ fieldTypeMap, operation }:
|
|
1027
|
+
{ fieldTypeMap, operation }: CacheManagerContext,
|
|
1003
1028
|
): void {
|
|
1004
|
-
if (cacheMetadata.has(requestFieldPath))
|
|
1029
|
+
if (cacheMetadata.has(requestFieldPath)) {
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1005
1032
|
|
|
1006
1033
|
const fieldTypeInfo = fieldTypeMap.get(requestFieldPath);
|
|
1007
1034
|
|
|
@@ -1023,7 +1050,7 @@ export class CacheManager implements CacheManagerDef {
|
|
|
1023
1050
|
hash: string,
|
|
1024
1051
|
partialQueryResponse: PartialQueryResponse,
|
|
1025
1052
|
_options: RequestOptions,
|
|
1026
|
-
_context:
|
|
1053
|
+
_context: CacheManagerContext,
|
|
1027
1054
|
): Promise<void> {
|
|
1028
1055
|
this._partialQueryResponses.set(hash, partialQueryResponse);
|
|
1029
1056
|
}
|
|
@@ -1032,7 +1059,7 @@ export class CacheManager implements CacheManagerDef {
|
|
|
1032
1059
|
hash: string,
|
|
1033
1060
|
{ cacheMetadata, data }: ResponseData,
|
|
1034
1061
|
options: RequestOptions,
|
|
1035
|
-
context:
|
|
1062
|
+
context: CacheManagerContext,
|
|
1036
1063
|
): Promise<void> {
|
|
1037
1064
|
const dehydratedCacheMetadata = dehydrateCacheMetadata(cacheMetadata);
|
|
1038
1065
|
const cacheControl = CacheManager._getOperationCacheControl(cacheMetadata, context.operation);
|
|
@@ -1049,19 +1076,24 @@ export class CacheManager implements CacheManagerDef {
|
|
|
1049
1076
|
|
|
1050
1077
|
private async _setRequestFieldPathCacheEntry(
|
|
1051
1078
|
field: FieldNode,
|
|
1052
|
-
|
|
1079
|
+
keysAndPaths: KeysAndPaths,
|
|
1053
1080
|
{ cacheability, data, fieldTypeInfo }: DataForCachingEntry,
|
|
1054
1081
|
options: RequestOptions,
|
|
1055
|
-
context:
|
|
1082
|
+
context: CacheManagerContext,
|
|
1056
1083
|
): Promise<void> {
|
|
1057
|
-
const
|
|
1058
|
-
let fieldData = get(data, responseDataPath
|
|
1084
|
+
const { hashedRequestFieldCacheKey, responseDataPath } = keysAndPaths;
|
|
1085
|
+
let fieldData = get(data, responseDataPath);
|
|
1059
1086
|
const isEntity = this._isFieldEntity(fieldData, fieldTypeInfo);
|
|
1087
|
+
const hasArgsOrDirectives = fieldTypeInfo.hasArguments || fieldTypeInfo.hasDirectives;
|
|
1060
1088
|
|
|
1061
1089
|
if (context.operation === QUERY && (isEntity || hasArgsOrDirectives)) {
|
|
1090
|
+
if (isPlainObject(fieldData) && field.selectionSet?.selections) {
|
|
1091
|
+
fieldData = filterOutPropsWithArgsOrDirectives(fieldData, field.selectionSet.selections, keysAndPaths, context);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1062
1094
|
const result = await this._checkCacheEntry(REQUEST_FIELD_PATHS, hashedRequestFieldCacheKey, options, context);
|
|
1063
1095
|
|
|
1064
|
-
if (result) {
|
|
1096
|
+
if (result && isObjectLike(fieldData)) {
|
|
1065
1097
|
fieldData = this._mergeObjects(result.entry, fieldData);
|
|
1066
1098
|
}
|
|
1067
1099
|
|
|
@@ -1083,21 +1115,6 @@ export class CacheManager implements CacheManagerDef {
|
|
|
1083
1115
|
}
|
|
1084
1116
|
}
|
|
1085
1117
|
}
|
|
1086
|
-
|
|
1087
|
-
private async _setRequestFieldPathData(
|
|
1088
|
-
cachedFieldData: CachedFieldData,
|
|
1089
|
-
hash: string,
|
|
1090
|
-
options: RequestOptions,
|
|
1091
|
-
context: RequestContext,
|
|
1092
|
-
): Promise<void> {
|
|
1093
|
-
const checkResult = await this._checkCacheEntry(REQUEST_FIELD_PATHS, hash, options, context);
|
|
1094
|
-
|
|
1095
|
-
if (checkResult) {
|
|
1096
|
-
const { cacheability, entry } = checkResult;
|
|
1097
|
-
if (cacheability) cachedFieldData.cacheability = cacheability;
|
|
1098
|
-
if (entry) cachedFieldData.requestFieldPathData = entry;
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
1118
|
}
|
|
1102
1119
|
|
|
1103
1120
|
export default function init(userOptions: UserOptions): CacheManagerInit {
|