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