@graphql-box/cache-manager 2.1.4 → 2.3.1
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 +24 -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 +495 -488
- 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 +12 -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 +492 -486
- 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 +11 -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 +15 -22
- package/lib/types/main/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__snapshots__/index.test.ts.snap +17466 -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 +10 -0
- package/src/helpers/validTypeIDValue.ts +11 -0
- package/src/index.test.ts +179 -3
- package/src/main/index.ts +540 -524
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,67 +99,33 @@ 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;
|
|
205
114
|
}
|
|
206
115
|
|
|
207
|
-
private static
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (!partialQueryResponse) return cacheMetadata;
|
|
212
|
-
|
|
213
|
-
return new Map([...partialQueryResponse.cacheMetadata, ...cacheMetadata]);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
private static _isDataEntity(fieldTypeInfo?: FieldTypeInfo): boolean {
|
|
217
|
-
if (!fieldTypeInfo) return false;
|
|
116
|
+
private static _isNodeEntity(fieldTypeInfo?: FieldTypeInfo): boolean {
|
|
117
|
+
if (!fieldTypeInfo) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
218
120
|
|
|
219
121
|
const { isEntity, possibleTypes } = fieldTypeInfo;
|
|
220
122
|
return isEntity || possibleTypes.some(type => !!type.isEntity);
|
|
221
123
|
}
|
|
222
124
|
|
|
223
|
-
private static
|
|
125
|
+
private static _isNodeRequestFieldPath(fieldTypeInfo?: FieldTypeInfo): boolean {
|
|
224
126
|
return (
|
|
225
127
|
!!fieldTypeInfo &&
|
|
226
|
-
(this.
|
|
128
|
+
(this._isNodeEntity(fieldTypeInfo) || fieldTypeInfo.hasArguments || fieldTypeInfo.hasDirectives)
|
|
227
129
|
);
|
|
228
130
|
}
|
|
229
131
|
|
|
@@ -232,6 +134,17 @@ export class CacheManager implements CacheManagerDef {
|
|
|
232
134
|
return !noCache && cacheability.checkTTL();
|
|
233
135
|
}
|
|
234
136
|
|
|
137
|
+
private static _mergeResponseCacheMetadata(
|
|
138
|
+
cacheMetadata: CacheMetadata,
|
|
139
|
+
partialQueryResponse?: PartialQueryResponse,
|
|
140
|
+
): CacheMetadata {
|
|
141
|
+
if (!partialQueryResponse) {
|
|
142
|
+
return cacheMetadata;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return new Map([...partialQueryResponse.cacheMetadata, ...cacheMetadata]);
|
|
146
|
+
}
|
|
147
|
+
|
|
235
148
|
private static _setCachedData(
|
|
236
149
|
requestData: PlainObjectMap,
|
|
237
150
|
{ data }: MergedCachedFieldData,
|
|
@@ -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,34 +322,59 @@ 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, path } = await this._resolveRequest(
|
|
334
|
+
updatedRequestData,
|
|
335
|
+
rawResponseData,
|
|
336
|
+
options,
|
|
337
|
+
cacheManagerContext,
|
|
338
|
+
);
|
|
389
339
|
|
|
390
340
|
let partialQueryResponse: PartialQueryResponse | undefined;
|
|
391
341
|
|
|
392
|
-
if (
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
342
|
+
if (cacheManagerContext.queryFiltered) {
|
|
343
|
+
if (!(rawResponseData.hasNext || rawResponseData.path)) {
|
|
344
|
+
dataCaching.push(
|
|
345
|
+
this._setQueryResponseCacheEntry(
|
|
346
|
+
updatedRequestData.hash,
|
|
347
|
+
{ cacheMetadata, data },
|
|
348
|
+
options,
|
|
349
|
+
cacheManagerContext,
|
|
350
|
+
),
|
|
351
|
+
);
|
|
352
|
+
}
|
|
396
353
|
|
|
397
|
-
|
|
354
|
+
if (!rawResponseData.path) {
|
|
355
|
+
partialQueryResponse = this._getPartialQueryResponse(requestData.hash);
|
|
356
|
+
}
|
|
398
357
|
}
|
|
399
358
|
|
|
400
|
-
const responseCacheMetadata = CacheManager.
|
|
401
|
-
const responseData = this.
|
|
359
|
+
const responseCacheMetadata = CacheManager._mergeResponseCacheMetadata(cacheMetadata, partialQueryResponse);
|
|
360
|
+
const responseData = this._mergeResponseData(data, partialQueryResponse);
|
|
402
361
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
362
|
+
if (!(rawResponseData.hasNext || rawResponseData.path)) {
|
|
363
|
+
dataCaching.push(
|
|
364
|
+
this._setQueryResponseCacheEntry(
|
|
365
|
+
requestData.hash,
|
|
366
|
+
{ cacheMetadata: responseCacheMetadata, data: responseData },
|
|
367
|
+
options,
|
|
368
|
+
cacheManagerContext,
|
|
369
|
+
),
|
|
370
|
+
);
|
|
371
|
+
}
|
|
411
372
|
|
|
412
|
-
if (options.awaitDataCaching)
|
|
373
|
+
if (options.awaitDataCaching) {
|
|
374
|
+
await Promise.all(dataCaching);
|
|
375
|
+
}
|
|
413
376
|
|
|
414
|
-
return { cacheMetadata: responseCacheMetadata, data: responseData };
|
|
377
|
+
return { cacheMetadata: responseCacheMetadata, data: responseData, hasNext, path };
|
|
415
378
|
}
|
|
416
379
|
|
|
417
380
|
public async resolveRequest(
|
|
@@ -420,82 +383,141 @@ export class CacheManager implements CacheManagerDef {
|
|
|
420
383
|
options: RequestOptions,
|
|
421
384
|
context: RequestContext,
|
|
422
385
|
): Promise<ResponseData> {
|
|
423
|
-
|
|
386
|
+
const cacheManagerContext: CacheManagerContext = {
|
|
387
|
+
...context,
|
|
388
|
+
fragmentDefinitions: getFragmentDefinitions(requestData.ast),
|
|
389
|
+
typeIDKey: this._typeIDKey,
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
return this._resolveRequest(requestData, rawResponseData, options, cacheManagerContext);
|
|
424
393
|
}
|
|
425
394
|
|
|
426
|
-
private async
|
|
427
|
-
|
|
395
|
+
private async _analyzeFieldNode(
|
|
396
|
+
fieldNode: FieldNode,
|
|
428
397
|
cachedAncestorFieldData: CachedAncestorFieldData,
|
|
429
398
|
cachedResponseData: CachedResponseData,
|
|
430
399
|
options: RequestOptions,
|
|
431
|
-
context:
|
|
400
|
+
context: CacheManagerContext,
|
|
432
401
|
): Promise<void> {
|
|
433
|
-
if (hasChildFields(
|
|
434
|
-
await this.
|
|
402
|
+
if (hasChildFields(fieldNode)) {
|
|
403
|
+
await this._analyzeParentFieldNode(fieldNode, cachedAncestorFieldData, cachedResponseData, options, context);
|
|
435
404
|
} else {
|
|
436
|
-
await
|
|
405
|
+
await this._analyzeLeafFieldNode(fieldNode, cachedAncestorFieldData, cachedResponseData, options, context);
|
|
437
406
|
}
|
|
438
407
|
}
|
|
439
408
|
|
|
440
|
-
private async
|
|
441
|
-
|
|
409
|
+
private async _analyzeLeafFieldNode(
|
|
410
|
+
fieldNode: FieldNode,
|
|
442
411
|
cachedAncestorFieldData: CachedAncestorFieldData,
|
|
443
412
|
cachedResponseData: CachedResponseData,
|
|
444
413
|
options: RequestOptions,
|
|
445
|
-
context:
|
|
414
|
+
context: CacheManagerContext,
|
|
446
415
|
): Promise<void> {
|
|
447
|
-
const keysAndPaths =
|
|
448
|
-
const { hashedRequestFieldCacheKey, propNameOrIndex,
|
|
416
|
+
const keysAndPaths = buildFieldKeysAndPaths(fieldNode, cachedAncestorFieldData, context);
|
|
417
|
+
const { hashedRequestFieldCacheKey, propNameOrIndex, requestFieldPath } = keysAndPaths;
|
|
449
418
|
const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath);
|
|
419
|
+
const { entityData, fragmentKind, fragmentName, requestFieldPathData, typeName } = cachedAncestorFieldData;
|
|
450
420
|
|
|
451
|
-
const {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const cachedFieldData: CachedFieldData = {
|
|
458
|
-
dataEntityData: CacheManager._getFieldDataFromAncestor(ancestorDataEntityData, propNameOrIndex),
|
|
459
|
-
requestFieldPathData: CacheManager._getFieldDataFromAncestor(ancestorRequestFieldPathData, propNameOrIndex),
|
|
421
|
+
const typeNamesAndKind = {
|
|
422
|
+
dataTypeName: entityData?.__typename || requestFieldPathData?.__typename,
|
|
423
|
+
fieldTypeName: typeName,
|
|
424
|
+
fragmentKind,
|
|
425
|
+
fragmentName,
|
|
460
426
|
};
|
|
461
427
|
|
|
462
|
-
if (CacheManager.
|
|
463
|
-
await this.
|
|
464
|
-
|
|
428
|
+
if (CacheManager._isNodeRequestFieldPath(fieldTypeInfo)) {
|
|
429
|
+
const { cacheability, entry } = await this._retrieveCachedRequestFieldPathData(
|
|
430
|
+
hashedRequestFieldCacheKey,
|
|
431
|
+
options,
|
|
432
|
+
context,
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
CacheManager._setCachedResponseData(
|
|
436
|
+
{ cacheability, data: entry },
|
|
437
|
+
cachedResponseData,
|
|
438
|
+
keysAndPaths,
|
|
439
|
+
typeNamesAndKind,
|
|
440
|
+
options,
|
|
441
|
+
context,
|
|
442
|
+
);
|
|
443
|
+
} else {
|
|
444
|
+
const cachedFieldData =
|
|
445
|
+
CacheManager._getFieldDataFromAncestor(entityData, propNameOrIndex) ||
|
|
446
|
+
CacheManager._getFieldDataFromAncestor(requestFieldPathData, propNameOrIndex);
|
|
447
|
+
|
|
448
|
+
CacheManager._setFieldPathChecklist(
|
|
449
|
+
cachedResponseData.fieldPathChecklist,
|
|
450
|
+
{ data: cachedFieldData },
|
|
451
|
+
requestFieldPath,
|
|
452
|
+
typeNamesAndKind,
|
|
453
|
+
);
|
|
465
454
|
|
|
466
|
-
|
|
467
|
-
await this._setDataEntityData(cachedFieldData, fieldTypeInfo as FieldTypeInfo, options, context);
|
|
455
|
+
CacheManager._setCachedData(cachedResponseData.data, { data: cachedFieldData }, propNameOrIndex);
|
|
468
456
|
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
private async _analyzeParentFieldNode(
|
|
460
|
+
fieldNode: FieldNode,
|
|
461
|
+
cachedAncestorFieldData: CachedAncestorFieldData,
|
|
462
|
+
cachedResponseData: CachedResponseData,
|
|
463
|
+
options: RequestOptions,
|
|
464
|
+
context: CacheManagerContext,
|
|
465
|
+
): Promise<void> {
|
|
466
|
+
const keysAndPaths = buildFieldKeysAndPaths(fieldNode, cachedAncestorFieldData, context);
|
|
467
|
+
const { propNameOrIndex, requestFieldCacheKey, requestFieldPath } = keysAndPaths;
|
|
468
|
+
const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath) as FieldTypeInfo;
|
|
469
469
|
|
|
470
|
-
const { cacheability,
|
|
470
|
+
const { cacheability, data, entityData, requestFieldPathData } = await this._retrieveCachedParentNodeData(
|
|
471
|
+
cachedAncestorFieldData,
|
|
472
|
+
keysAndPaths,
|
|
473
|
+
fieldTypeInfo,
|
|
474
|
+
options,
|
|
475
|
+
context,
|
|
476
|
+
);
|
|
471
477
|
|
|
472
|
-
const
|
|
473
|
-
!isUndefined(requestFieldPathData) || !isUndefined(dataEntityData)
|
|
474
|
-
? this._mergeObjects(requestFieldPathData, dataEntityData)
|
|
475
|
-
: undefined;
|
|
478
|
+
const { fragmentKind, fragmentName, typeName } = cachedAncestorFieldData;
|
|
476
479
|
|
|
477
480
|
CacheManager._setCachedResponseData(
|
|
478
481
|
{ cacheability, data },
|
|
479
482
|
cachedResponseData,
|
|
480
483
|
keysAndPaths,
|
|
481
|
-
{ dataTypeName: get(data, TYPE_NAME_KEY), fieldTypeName: typeName },
|
|
484
|
+
{ dataTypeName: get(data, TYPE_NAME_KEY), fieldTypeName: typeName, fragmentKind, fragmentName },
|
|
482
485
|
options,
|
|
483
486
|
context,
|
|
484
487
|
);
|
|
485
488
|
|
|
486
|
-
if (!isObjectLike(data))
|
|
489
|
+
if (!isObjectLike(data)) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
487
492
|
|
|
488
493
|
const objectLikeData = data as PlainObjectMap | any[];
|
|
489
494
|
const promises: Promise<void>[] = [];
|
|
490
495
|
|
|
491
496
|
iterateChildFields(
|
|
492
|
-
|
|
497
|
+
fieldNode,
|
|
493
498
|
objectLikeData,
|
|
494
|
-
|
|
499
|
+
context.fragmentDefinitions,
|
|
500
|
+
(
|
|
501
|
+
childField: FieldNode,
|
|
502
|
+
childTypeName: string | undefined,
|
|
503
|
+
childFragmentKind: string | undefined,
|
|
504
|
+
childFragmentName: string | undefined,
|
|
505
|
+
childIndex?: number,
|
|
506
|
+
) => {
|
|
495
507
|
promises.push(
|
|
496
|
-
this.
|
|
508
|
+
this._analyzeFieldNode(
|
|
497
509
|
childField,
|
|
498
|
-
{
|
|
510
|
+
{
|
|
511
|
+
cacheability,
|
|
512
|
+
entityData,
|
|
513
|
+
fragmentKind: childFragmentKind,
|
|
514
|
+
fragmentName: childFragmentName,
|
|
515
|
+
index: childIndex,
|
|
516
|
+
requestFieldCacheKey,
|
|
517
|
+
requestFieldPath,
|
|
518
|
+
requestFieldPathData,
|
|
519
|
+
typeName: childTypeName,
|
|
520
|
+
},
|
|
499
521
|
{ ...cachedResponseData, data: cachedResponseData.data[propNameOrIndex] },
|
|
500
522
|
options,
|
|
501
523
|
context,
|
|
@@ -511,12 +533,15 @@ export class CacheManager implements CacheManagerDef {
|
|
|
511
533
|
{ ast }: RequestData,
|
|
512
534
|
{ data, ...otherProps }: RawResponseDataWithMaybeCacheMetadata,
|
|
513
535
|
options: RequestOptions,
|
|
514
|
-
context:
|
|
536
|
+
context: CacheManagerContext,
|
|
515
537
|
): CacheMetadata {
|
|
516
538
|
const cacheMetadata = this._createCacheMetadata({ data, ...otherProps }, context);
|
|
517
539
|
const queryNode = getOperationDefinitions(ast, context.operation)[0];
|
|
518
540
|
const fieldsAndTypeNames = getChildFields(queryNode);
|
|
519
|
-
|
|
541
|
+
|
|
542
|
+
if (!fieldsAndTypeNames) {
|
|
543
|
+
return cacheMetadata;
|
|
544
|
+
}
|
|
520
545
|
|
|
521
546
|
fieldsAndTypeNames.forEach(({ fieldNode }) =>
|
|
522
547
|
this._setFieldCacheability(
|
|
@@ -535,16 +560,20 @@ export class CacheManager implements CacheManagerDef {
|
|
|
535
560
|
cacheType: CacheTypes,
|
|
536
561
|
hash: string,
|
|
537
562
|
options: RequestOptions,
|
|
538
|
-
context:
|
|
563
|
+
context: CacheManagerContext,
|
|
539
564
|
): Promise<CheckCacheEntryResult | false> {
|
|
540
565
|
try {
|
|
541
566
|
const cacheability = await this._hasCacheEntry(cacheType, hash);
|
|
542
567
|
|
|
543
|
-
if (!cacheability || !CacheManager._isValid(cacheability))
|
|
568
|
+
if (!cacheability || !CacheManager._isValid(cacheability)) {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
544
571
|
|
|
545
572
|
const entry = await this._getCacheEntry(cacheType, hash, options, context);
|
|
546
573
|
|
|
547
|
-
if (
|
|
574
|
+
if (isUndefined(entry)) {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
548
577
|
|
|
549
578
|
return { cacheability, entry };
|
|
550
579
|
} catch (error) {
|
|
@@ -554,7 +583,7 @@ export class CacheManager implements CacheManagerDef {
|
|
|
554
583
|
|
|
555
584
|
private _createCacheMetadata(
|
|
556
585
|
{ _cacheMetadata, headers }: RawResponseDataWithMaybeCacheMetadata,
|
|
557
|
-
{ operation }:
|
|
586
|
+
{ operation }: CacheManagerContext,
|
|
558
587
|
): CacheMetadata {
|
|
559
588
|
const cacheMetadata = new Map();
|
|
560
589
|
|
|
@@ -573,151 +602,13 @@ export class CacheManager implements CacheManagerDef {
|
|
|
573
602
|
return cacheMetadata;
|
|
574
603
|
}
|
|
575
604
|
|
|
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
605
|
@logCacheQuery()
|
|
715
606
|
private async _getCacheEntry(
|
|
716
607
|
cacheType: CacheTypes,
|
|
717
608
|
hash: string,
|
|
718
609
|
_options: RequestOptions,
|
|
719
|
-
_context:
|
|
720
|
-
): Promise<
|
|
610
|
+
_context: CacheManagerContext,
|
|
611
|
+
): Promise<any> {
|
|
721
612
|
try {
|
|
722
613
|
return await this._cache.get(`${cacheType}::${hash}`);
|
|
723
614
|
} catch (errors) {
|
|
@@ -731,12 +622,6 @@ export class CacheManager implements CacheManagerDef {
|
|
|
731
622
|
return partialQueryResponse;
|
|
732
623
|
}
|
|
733
624
|
|
|
734
|
-
private _getResponseData(responseData: PlainObjectMap, partialQueryResponse?: PartialQueryResponse): PlainObjectMap {
|
|
735
|
-
if (!partialQueryResponse) return responseData;
|
|
736
|
-
|
|
737
|
-
return this._mergeObjects(partialQueryResponse.data, responseData);
|
|
738
|
-
}
|
|
739
|
-
|
|
740
625
|
private async _hasCacheEntry(cacheType: CacheTypes, hash: string): Promise<Cacheability | false> {
|
|
741
626
|
try {
|
|
742
627
|
return await this._cache.has(`${cacheType}::${hash}`);
|
|
@@ -746,9 +631,18 @@ export class CacheManager implements CacheManagerDef {
|
|
|
746
631
|
}
|
|
747
632
|
|
|
748
633
|
private _isFieldEntity(fieldData: any, { isEntity, possibleTypes }: FieldTypeInfo): boolean {
|
|
749
|
-
if (!get(fieldData, this._typeIDKey, null))
|
|
750
|
-
|
|
751
|
-
|
|
634
|
+
if (!get(fieldData, this._typeIDKey, null)) {
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (isEntity) {
|
|
639
|
+
return true;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (!possibleTypes.length) {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
|
|
752
646
|
return possibleTypes.some(type => type.typeName === fieldData.__typename);
|
|
753
647
|
}
|
|
754
648
|
|
|
@@ -758,43 +652,66 @@ export class CacheManager implements CacheManagerDef {
|
|
|
758
652
|
});
|
|
759
653
|
}
|
|
760
654
|
|
|
761
|
-
private
|
|
655
|
+
private _mergeResponseData(
|
|
656
|
+
responseData: PlainObjectMap,
|
|
657
|
+
partialQueryResponse?: PartialQueryResponse,
|
|
658
|
+
): PlainObjectMap {
|
|
659
|
+
if (!partialQueryResponse) {
|
|
660
|
+
return responseData;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
return this._mergeObjects(partialQueryResponse.data, responseData);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
private async _parseEntityAndRequestFieldPathCacheEntryData(
|
|
762
667
|
field: FieldNode,
|
|
763
668
|
ancestorKeysAndPaths: AncestorKeysAndPaths,
|
|
764
|
-
{ cacheMetadata,
|
|
669
|
+
{ cacheMetadata, entityData, requestFieldPathData }: ResponseDataForCaching,
|
|
765
670
|
options: RequestOptions,
|
|
766
|
-
context:
|
|
671
|
+
context: CacheManagerContext,
|
|
767
672
|
): Promise<void> {
|
|
768
|
-
const keysAndPaths =
|
|
673
|
+
const keysAndPaths = buildFieldKeysAndPaths(field, ancestorKeysAndPaths, context);
|
|
769
674
|
const { requestFieldCacheKey, requestFieldPath, responseDataPath } = keysAndPaths;
|
|
770
|
-
const fieldData = get(requestFieldPathData, responseDataPath
|
|
771
|
-
|
|
675
|
+
const fieldData = get(requestFieldPathData, responseDataPath);
|
|
676
|
+
const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath);
|
|
772
677
|
|
|
773
|
-
|
|
774
|
-
|
|
678
|
+
if (!isObjectLike(fieldData) && !fieldTypeInfo?.hasDirectives) {
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
775
681
|
|
|
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
|
-
);
|
|
682
|
+
if (isObjectLike(fieldData)) {
|
|
683
|
+
const promises: Promise<void>[] = [];
|
|
791
684
|
|
|
792
|
-
|
|
685
|
+
iterateChildFields(
|
|
686
|
+
field,
|
|
687
|
+
fieldData as PlainObjectMap | any[],
|
|
688
|
+
context.fragmentDefinitions,
|
|
689
|
+
(
|
|
690
|
+
childField: FieldNode,
|
|
691
|
+
_typeName: string | undefined,
|
|
692
|
+
_fragmentKind: string | undefined,
|
|
693
|
+
_fragmentName: string | undefined,
|
|
694
|
+
childIndex?: number,
|
|
695
|
+
) => {
|
|
696
|
+
promises.push(
|
|
697
|
+
this._parseEntityAndRequestFieldPathCacheEntryData(
|
|
698
|
+
childField,
|
|
699
|
+
{ index: childIndex, requestFieldCacheKey, requestFieldPath, responseDataPath },
|
|
700
|
+
{ cacheMetadata, entityData, requestFieldPathData },
|
|
701
|
+
options,
|
|
702
|
+
context,
|
|
703
|
+
),
|
|
704
|
+
);
|
|
705
|
+
},
|
|
706
|
+
);
|
|
707
|
+
|
|
708
|
+
await Promise.all(promises);
|
|
709
|
+
}
|
|
793
710
|
|
|
794
|
-
await this.
|
|
711
|
+
await this._setEntityAndRequestFieldPathCacheEntry(
|
|
795
712
|
field,
|
|
796
713
|
keysAndPaths,
|
|
797
|
-
{ cacheMetadata,
|
|
714
|
+
{ cacheMetadata, entityData, requestFieldPathData },
|
|
798
715
|
options,
|
|
799
716
|
context,
|
|
800
717
|
);
|
|
@@ -804,24 +721,158 @@ export class CacheManager implements CacheManagerDef {
|
|
|
804
721
|
requestData: RequestData,
|
|
805
722
|
rawResponseData: RawResponseDataWithMaybeCacheMetadata,
|
|
806
723
|
options: RequestOptions,
|
|
807
|
-
context:
|
|
724
|
+
context: CacheManagerContext,
|
|
808
725
|
): Promise<ResponseData> {
|
|
726
|
+
const normalizedResponseData = rawResponseData.path ? normalizeResponseData(rawResponseData) : rawResponseData;
|
|
809
727
|
const dataCaching: Promise<void>[] = [];
|
|
810
|
-
const cacheMetadata = this._buildCacheMetadata(requestData,
|
|
811
|
-
const { data } =
|
|
728
|
+
const cacheMetadata = this._buildCacheMetadata(requestData, normalizedResponseData, options, context);
|
|
729
|
+
const { data, hasNext, path } = normalizedResponseData;
|
|
812
730
|
|
|
813
731
|
dataCaching.push(
|
|
814
|
-
this.
|
|
732
|
+
this._setEntityAndRequestFieldPathCacheEntries(
|
|
815
733
|
requestData,
|
|
816
|
-
{ cacheMetadata,
|
|
734
|
+
{ cacheMetadata, entityData: cloneDeep(data), requestFieldPathData: cloneDeep(data) },
|
|
817
735
|
options,
|
|
818
736
|
context,
|
|
819
737
|
),
|
|
820
738
|
);
|
|
821
739
|
|
|
822
|
-
if (options.awaitDataCaching)
|
|
740
|
+
if (options.awaitDataCaching) {
|
|
741
|
+
await Promise.all(dataCaching);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
return { cacheMetadata, data, hasNext, path };
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
private async _retrieveCachedEntityData(
|
|
748
|
+
validTypeIDValue: string | number,
|
|
749
|
+
{ possibleTypes, typeName }: FieldTypeInfo,
|
|
750
|
+
options: RequestOptions,
|
|
751
|
+
context: CacheManagerContext,
|
|
752
|
+
) {
|
|
753
|
+
const typeNames = [...possibleTypes.map(type => type.typeName), typeName];
|
|
754
|
+
|
|
755
|
+
const checkResults = await Promise.all(
|
|
756
|
+
typeNames.map(name => this._checkCacheEntry(DATA_ENTITIES, `${name}::${validTypeIDValue}`, options, context)),
|
|
757
|
+
);
|
|
758
|
+
|
|
759
|
+
const validResults = checkResults.filter(result => !!result) as CheckCacheEntryResult[];
|
|
760
|
+
let validResult: CheckCacheEntryResult | undefined;
|
|
761
|
+
|
|
762
|
+
if (validResults.length === 1) {
|
|
763
|
+
validResult = validResults[0];
|
|
764
|
+
} else if (validResults.length > 1) {
|
|
765
|
+
validResults.sort(({ cacheability: a }, { cacheability: b }) => a.metadata.ttl - b.metadata.ttl);
|
|
766
|
+
|
|
767
|
+
validResult = {
|
|
768
|
+
cacheability: validResults[0].cacheability,
|
|
769
|
+
entry: validResults.reduce((obj, { entry }) => this._mergeObjects(obj, entry), {}),
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
return (validResult || {}) as Partial<CheckCacheEntryResult>;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
private async _retrieveCachedParentNodeData(
|
|
777
|
+
{ entityData: ancestorEntityData, requestFieldPathData: ancestorRequestFieldPathData }: CachedAncestorFieldData,
|
|
778
|
+
{ hashedRequestFieldCacheKey, propNameOrIndex }: KeysAndPaths,
|
|
779
|
+
fieldTypeInfo: FieldTypeInfo,
|
|
780
|
+
options: RequestOptions,
|
|
781
|
+
context: CacheManagerContext,
|
|
782
|
+
) {
|
|
783
|
+
let entityData = CacheManager._getFieldDataFromAncestor(ancestorEntityData, propNameOrIndex);
|
|
784
|
+
let requestFieldPathData = CacheManager._getFieldDataFromAncestor(ancestorRequestFieldPathData, propNameOrIndex);
|
|
785
|
+
let cacheability: Cacheability | undefined;
|
|
786
|
+
|
|
787
|
+
if (CacheManager._isNodeRequestFieldPath(fieldTypeInfo)) {
|
|
788
|
+
const { cacheability: entryCacheability, entry } = await this._retrieveCachedRequestFieldPathData(
|
|
789
|
+
hashedRequestFieldCacheKey,
|
|
790
|
+
options,
|
|
791
|
+
context,
|
|
792
|
+
);
|
|
793
|
+
|
|
794
|
+
if (entry) {
|
|
795
|
+
requestFieldPathData = this._mergeObjects(requestFieldPathData, entry);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (entryCacheability) {
|
|
799
|
+
cacheability = entryCacheability;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
const validTypeIDValue = getValidTypeIDValue(requestFieldPathData, fieldTypeInfo, this._typeIDKey);
|
|
804
|
+
|
|
805
|
+
if (CacheManager._isNodeEntity(fieldTypeInfo) && validTypeIDValue) {
|
|
806
|
+
const { cacheability: entryCacheability, entry } = await this._retrieveCachedEntityData(
|
|
807
|
+
validTypeIDValue,
|
|
808
|
+
fieldTypeInfo,
|
|
809
|
+
options,
|
|
810
|
+
context,
|
|
811
|
+
);
|
|
812
|
+
|
|
813
|
+
if (entry) {
|
|
814
|
+
entityData = this._mergeObjects(entityData, entry);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
if (entryCacheability && (!cacheability || entryCacheability.metadata.ttl > cacheability?.metadata.ttl)) {
|
|
818
|
+
cacheability = entryCacheability;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const data =
|
|
823
|
+
!isUndefined(requestFieldPathData) || !isUndefined(entityData)
|
|
824
|
+
? this._mergeObjects(requestFieldPathData, entityData)
|
|
825
|
+
: entityData ?? requestFieldPathData;
|
|
826
|
+
|
|
827
|
+
return {
|
|
828
|
+
cacheability,
|
|
829
|
+
data,
|
|
830
|
+
entityData,
|
|
831
|
+
requestFieldPathData,
|
|
832
|
+
};
|
|
833
|
+
}
|
|
823
834
|
|
|
824
|
-
|
|
835
|
+
private async _retrieveCachedRequestFieldPathData(
|
|
836
|
+
hash: string,
|
|
837
|
+
options: RequestOptions,
|
|
838
|
+
context: CacheManagerContext,
|
|
839
|
+
) {
|
|
840
|
+
return (this._checkCacheEntry(REQUEST_FIELD_PATHS, hash, options, context) || {}) as Partial<CheckCacheEntryResult>;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
private async _retrieveCachedResponseData(
|
|
844
|
+
{ ast }: RequestData,
|
|
845
|
+
options: RequestOptions,
|
|
846
|
+
context: CacheManagerContext,
|
|
847
|
+
): Promise<CachedResponseData> {
|
|
848
|
+
const cachedResponseData: CachedResponseData = {
|
|
849
|
+
cacheMetadata: new Map(),
|
|
850
|
+
data: {},
|
|
851
|
+
fieldCount: { missing: 0, total: 0 },
|
|
852
|
+
fieldPathChecklist: new Map(),
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
const queryNode = getOperationDefinitions(ast, context.operation)[0];
|
|
856
|
+
const fieldsAndTypeNames = getChildFields(queryNode);
|
|
857
|
+
|
|
858
|
+
if (!fieldsAndTypeNames) {
|
|
859
|
+
return cachedResponseData;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
await Promise.all(
|
|
863
|
+
fieldsAndTypeNames.map(({ fieldNode }) =>
|
|
864
|
+
this._analyzeFieldNode(
|
|
865
|
+
fieldNode,
|
|
866
|
+
{ requestFieldPath: context.operation },
|
|
867
|
+
cachedResponseData,
|
|
868
|
+
options,
|
|
869
|
+
context,
|
|
870
|
+
),
|
|
871
|
+
),
|
|
872
|
+
);
|
|
873
|
+
|
|
874
|
+
cachedResponseData.fieldCount = CacheManager._countFieldPathChecklist(cachedResponseData.fieldPathChecklist);
|
|
875
|
+
return cachedResponseData;
|
|
825
876
|
}
|
|
826
877
|
|
|
827
878
|
@logCacheEntry()
|
|
@@ -831,7 +882,7 @@ export class CacheManager implements CacheManagerDef {
|
|
|
831
882
|
value: any,
|
|
832
883
|
cachemapOptions: CachemapOptions,
|
|
833
884
|
_options: RequestOptions,
|
|
834
|
-
_context:
|
|
885
|
+
_context: CacheManagerContext,
|
|
835
886
|
): Promise<void> {
|
|
836
887
|
try {
|
|
837
888
|
await this._cache.set(`${cacheType}::${hash}`, cloneDeep(value), cachemapOptions);
|
|
@@ -840,19 +891,22 @@ export class CacheManager implements CacheManagerDef {
|
|
|
840
891
|
}
|
|
841
892
|
}
|
|
842
893
|
|
|
843
|
-
private async
|
|
894
|
+
private async _setEntityAndRequestFieldPathCacheEntries(
|
|
844
895
|
requestData: RequestData,
|
|
845
896
|
responseData: ResponseDataForCaching,
|
|
846
897
|
options: RequestOptions,
|
|
847
|
-
context:
|
|
898
|
+
context: CacheManagerContext,
|
|
848
899
|
): Promise<void> {
|
|
849
900
|
const operationNode = getOperationDefinitions(requestData.ast, context.operation)[0];
|
|
850
901
|
const fieldsAndTypeNames = getChildFields(operationNode);
|
|
851
|
-
|
|
902
|
+
|
|
903
|
+
if (!fieldsAndTypeNames) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
852
906
|
|
|
853
907
|
await Promise.all(
|
|
854
908
|
fieldsAndTypeNames.map(({ fieldNode }) => {
|
|
855
|
-
return this.
|
|
909
|
+
return this._parseEntityAndRequestFieldPathCacheEntryData(
|
|
856
910
|
fieldNode,
|
|
857
911
|
{ requestFieldPath: context.operation },
|
|
858
912
|
responseData,
|
|
@@ -863,80 +917,74 @@ export class CacheManager implements CacheManagerDef {
|
|
|
863
917
|
);
|
|
864
918
|
}
|
|
865
919
|
|
|
866
|
-
private async
|
|
867
|
-
|
|
868
|
-
|
|
920
|
+
private async _setEntityAndRequestFieldPathCacheEntry(
|
|
921
|
+
field: FieldNode,
|
|
922
|
+
keysAndPaths: KeysAndPaths,
|
|
923
|
+
{ cacheMetadata, entityData, requestFieldPathData }: ResponseDataForCaching,
|
|
869
924
|
options: RequestOptions,
|
|
870
|
-
context:
|
|
925
|
+
context: CacheManagerContext,
|
|
871
926
|
) {
|
|
872
|
-
const
|
|
873
|
-
|
|
874
|
-
const
|
|
927
|
+
const { requestFieldPath, responseDataPath } = keysAndPaths;
|
|
928
|
+
const fieldData = get(entityData, responseDataPath);
|
|
929
|
+
const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath);
|
|
930
|
+
const cacheability = cacheMetadata.get(requestFieldPath);
|
|
875
931
|
|
|
876
|
-
if (!
|
|
877
|
-
|
|
932
|
+
if (isUndefined(fieldData) || !fieldTypeInfo || !cacheability) {
|
|
933
|
+
return;
|
|
878
934
|
}
|
|
879
935
|
|
|
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
|
-
}
|
|
936
|
+
const promises: Promise<void>[] = [];
|
|
888
937
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
{
|
|
938
|
+
promises.push(
|
|
939
|
+
this._setRequestFieldPathCacheEntry(
|
|
940
|
+
field,
|
|
941
|
+
keysAndPaths,
|
|
942
|
+
{ cacheability, data: requestFieldPathData, fieldTypeInfo },
|
|
894
943
|
options,
|
|
895
944
|
context,
|
|
896
|
-
)
|
|
945
|
+
),
|
|
946
|
+
);
|
|
947
|
+
|
|
948
|
+
const isEntity = this._isFieldEntity(fieldData, fieldTypeInfo);
|
|
897
949
|
|
|
898
|
-
|
|
950
|
+
if (!isEntity && fieldTypeInfo.hasArguments) {
|
|
951
|
+
unset(entityData, responseDataPath);
|
|
899
952
|
}
|
|
953
|
+
|
|
954
|
+
if (isEntity) {
|
|
955
|
+
promises.push(
|
|
956
|
+
this._setEntityCacheEntry(keysAndPaths, { cacheability, data: entityData, fieldTypeInfo }, options, context),
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
await Promise.all(promises);
|
|
900
961
|
}
|
|
901
962
|
|
|
902
|
-
private async
|
|
903
|
-
|
|
904
|
-
{
|
|
963
|
+
private async _setEntityCacheEntry(
|
|
964
|
+
{ responseDataPath }: KeysAndPaths,
|
|
965
|
+
{ cacheability, data, fieldTypeInfo }: DataForCachingEntry,
|
|
905
966
|
options: RequestOptions,
|
|
906
|
-
context:
|
|
907
|
-
)
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
const validTypeIDValue = typeIDValue || requestFieldPathDataIDValue;
|
|
913
|
-
if (!validTypeIDValue) return;
|
|
967
|
+
context: CacheManagerContext,
|
|
968
|
+
) {
|
|
969
|
+
let fieldData = get(data, responseDataPath);
|
|
970
|
+
const fieldTypeName = fieldTypeInfo.isEntity ? fieldTypeInfo.typeName : fieldData.__typename;
|
|
971
|
+
const entityDataKey = `${fieldTypeName}::${fieldData[this._typeIDKey]}`;
|
|
972
|
+
const result = await this._checkCacheEntry(DATA_ENTITIES, entityDataKey, options, context);
|
|
914
973
|
|
|
915
|
-
|
|
974
|
+
if (result) {
|
|
975
|
+
fieldData = this._mergeObjects(result.entry, fieldData);
|
|
976
|
+
}
|
|
916
977
|
|
|
917
|
-
|
|
918
|
-
|
|
978
|
+
await this._setCacheEntry(
|
|
979
|
+
DATA_ENTITIES,
|
|
980
|
+
entityDataKey,
|
|
981
|
+
fieldData,
|
|
982
|
+
{ cacheHeaders: { cacheControl: cacheability.printCacheControl() }, tag: options.tag },
|
|
983
|
+
options,
|
|
984
|
+
context,
|
|
919
985
|
);
|
|
920
986
|
|
|
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
|
-
}
|
|
987
|
+
set(data, responseDataPath, { __cacheKey: `${DATA_ENTITIES}::${entityDataKey}` });
|
|
940
988
|
}
|
|
941
989
|
|
|
942
990
|
private _setFieldCacheability(
|
|
@@ -944,74 +992,52 @@ export class CacheManager implements CacheManagerDef {
|
|
|
944
992
|
ancestorKeysAndPaths: AncestorKeysAndPaths,
|
|
945
993
|
{ cacheMetadata, data }: ResponseData,
|
|
946
994
|
options: RequestOptions,
|
|
947
|
-
context:
|
|
995
|
+
context: CacheManagerContext,
|
|
948
996
|
): void {
|
|
949
997
|
const { requestFieldPath: ancestorRequestFieldPath } = ancestorKeysAndPaths;
|
|
950
|
-
const keysAndPaths =
|
|
998
|
+
const keysAndPaths = buildFieldKeysAndPaths(field, ancestorKeysAndPaths, context);
|
|
951
999
|
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;
|
|
1000
|
+
const fieldData = get(data, responseDataPath);
|
|
981
1001
|
const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath);
|
|
982
|
-
const cacheability = cacheMetadata.get(requestFieldPath);
|
|
983
|
-
if (!fieldTypeInfo || !cacheability) return;
|
|
984
1002
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
this._setRequestFieldPathCacheEntry(
|
|
989
|
-
field,
|
|
990
|
-
keysAndPaths,
|
|
991
|
-
{ cacheability, data: requestFieldPathData, fieldTypeInfo },
|
|
992
|
-
options,
|
|
993
|
-
context,
|
|
994
|
-
),
|
|
995
|
-
);
|
|
1003
|
+
if (!isObjectLike(fieldData) && !fieldTypeInfo?.hasDirectives) {
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
996
1006
|
|
|
997
|
-
|
|
998
|
-
this._setDataEntityCacheEntry(
|
|
999
|
-
keysAndPaths,
|
|
1000
|
-
{ cacheability, data: dataEntityData, fieldTypeInfo },
|
|
1001
|
-
options,
|
|
1002
|
-
context,
|
|
1003
|
-
),
|
|
1004
|
-
);
|
|
1007
|
+
this._setFieldTypeCacheDirective(cacheMetadata, { ancestorRequestFieldPath, requestFieldPath }, context);
|
|
1005
1008
|
|
|
1006
|
-
|
|
1009
|
+
if (isObjectLike(fieldData)) {
|
|
1010
|
+
iterateChildFields(
|
|
1011
|
+
field,
|
|
1012
|
+
fieldData as PlainObjectMap | any[],
|
|
1013
|
+
context.fragmentDefinitions,
|
|
1014
|
+
(
|
|
1015
|
+
childField: FieldNode,
|
|
1016
|
+
_typeName: string | undefined,
|
|
1017
|
+
_fragmentKind: string | undefined,
|
|
1018
|
+
_fragmentName: string | undefined,
|
|
1019
|
+
childIndex?: number,
|
|
1020
|
+
) => {
|
|
1021
|
+
this._setFieldCacheability(
|
|
1022
|
+
childField,
|
|
1023
|
+
{ index: childIndex, requestFieldPath, responseDataPath },
|
|
1024
|
+
{ cacheMetadata, data },
|
|
1025
|
+
options,
|
|
1026
|
+
context,
|
|
1027
|
+
);
|
|
1028
|
+
},
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
1007
1031
|
}
|
|
1008
1032
|
|
|
1009
1033
|
private _setFieldTypeCacheDirective(
|
|
1010
1034
|
cacheMetadata: CacheMetadata,
|
|
1011
1035
|
{ ancestorRequestFieldPath, requestFieldPath }: { ancestorRequestFieldPath?: string; requestFieldPath: string },
|
|
1012
|
-
{ fieldTypeMap, operation }:
|
|
1036
|
+
{ fieldTypeMap, operation }: CacheManagerContext,
|
|
1013
1037
|
): void {
|
|
1014
|
-
if (cacheMetadata.has(requestFieldPath))
|
|
1038
|
+
if (cacheMetadata.has(requestFieldPath)) {
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1015
1041
|
|
|
1016
1042
|
const fieldTypeInfo = fieldTypeMap.get(requestFieldPath);
|
|
1017
1043
|
|
|
@@ -1033,7 +1059,7 @@ export class CacheManager implements CacheManagerDef {
|
|
|
1033
1059
|
hash: string,
|
|
1034
1060
|
partialQueryResponse: PartialQueryResponse,
|
|
1035
1061
|
_options: RequestOptions,
|
|
1036
|
-
_context:
|
|
1062
|
+
_context: CacheManagerContext,
|
|
1037
1063
|
): Promise<void> {
|
|
1038
1064
|
this._partialQueryResponses.set(hash, partialQueryResponse);
|
|
1039
1065
|
}
|
|
@@ -1042,7 +1068,7 @@ export class CacheManager implements CacheManagerDef {
|
|
|
1042
1068
|
hash: string,
|
|
1043
1069
|
{ cacheMetadata, data }: ResponseData,
|
|
1044
1070
|
options: RequestOptions,
|
|
1045
|
-
context:
|
|
1071
|
+
context: CacheManagerContext,
|
|
1046
1072
|
): Promise<void> {
|
|
1047
1073
|
const dehydratedCacheMetadata = dehydrateCacheMetadata(cacheMetadata);
|
|
1048
1074
|
const cacheControl = CacheManager._getOperationCacheControl(cacheMetadata, context.operation);
|
|
@@ -1059,19 +1085,24 @@ export class CacheManager implements CacheManagerDef {
|
|
|
1059
1085
|
|
|
1060
1086
|
private async _setRequestFieldPathCacheEntry(
|
|
1061
1087
|
field: FieldNode,
|
|
1062
|
-
|
|
1088
|
+
keysAndPaths: KeysAndPaths,
|
|
1063
1089
|
{ cacheability, data, fieldTypeInfo }: DataForCachingEntry,
|
|
1064
1090
|
options: RequestOptions,
|
|
1065
|
-
context:
|
|
1091
|
+
context: CacheManagerContext,
|
|
1066
1092
|
): Promise<void> {
|
|
1067
|
-
const
|
|
1068
|
-
let fieldData = get(data, responseDataPath
|
|
1093
|
+
const { hashedRequestFieldCacheKey, responseDataPath } = keysAndPaths;
|
|
1094
|
+
let fieldData = get(data, responseDataPath);
|
|
1069
1095
|
const isEntity = this._isFieldEntity(fieldData, fieldTypeInfo);
|
|
1096
|
+
const hasArgsOrDirectives = fieldTypeInfo.hasArguments || fieldTypeInfo.hasDirectives;
|
|
1070
1097
|
|
|
1071
1098
|
if (context.operation === QUERY && (isEntity || hasArgsOrDirectives)) {
|
|
1099
|
+
if (isPlainObject(fieldData) && field.selectionSet?.selections) {
|
|
1100
|
+
fieldData = filterOutPropsWithArgsOrDirectives(fieldData, field.selectionSet.selections, keysAndPaths, context);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1072
1103
|
const result = await this._checkCacheEntry(REQUEST_FIELD_PATHS, hashedRequestFieldCacheKey, options, context);
|
|
1073
1104
|
|
|
1074
|
-
if (result) {
|
|
1105
|
+
if (result && isObjectLike(fieldData)) {
|
|
1075
1106
|
fieldData = this._mergeObjects(result.entry, fieldData);
|
|
1076
1107
|
}
|
|
1077
1108
|
|
|
@@ -1093,21 +1124,6 @@ export class CacheManager implements CacheManagerDef {
|
|
|
1093
1124
|
}
|
|
1094
1125
|
}
|
|
1095
1126
|
}
|
|
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
1127
|
}
|
|
1112
1128
|
|
|
1113
1129
|
export default function init(userOptions: UserOptions): CacheManagerInit {
|