relay-runtime 9.0.0 → 9.1.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/handlers/RelayDefaultHandlerProvider.js.flow +34 -0
- package/handlers/connection/ConnectionHandler.js.flow +549 -0
- package/handlers/connection/ConnectionInterface.js.flow +92 -0
- package/index.js +1 -1
- package/index.js.flow +314 -0
- package/lib/handlers/connection/ConnectionHandler.js +1 -3
- package/lib/index.js +1 -2
- package/lib/mutations/RelayDeclarativeMutationConfig.js +22 -45
- package/lib/mutations/RelayRecordProxy.js +1 -3
- package/lib/mutations/RelayRecordSourceMutator.js +1 -3
- package/lib/mutations/RelayRecordSourceProxy.js +1 -3
- package/lib/mutations/RelayRecordSourceSelectorProxy.js +1 -3
- package/lib/mutations/commitMutation.js +2 -0
- package/lib/mutations/validateMutation.js +13 -4
- package/lib/network/RelayObservable.js +9 -9
- package/lib/network/RelayQueryResponseCache.js +8 -6
- package/lib/query/fetchQueryInternal.js +1 -8
- package/lib/store/DataChecker.js +23 -51
- package/lib/store/RelayConcreteVariables.js +6 -2
- package/lib/store/RelayModernEnvironment.js +30 -12
- package/lib/store/RelayModernFragmentSpecResolver.js +9 -13
- package/lib/store/RelayModernQueryExecutor.js +73 -37
- package/lib/store/RelayModernRecord.js +14 -9
- package/lib/store/RelayModernStore.js +107 -70
- package/lib/store/RelayOperationTracker.js +35 -78
- package/lib/store/RelayOptimisticRecordSource.js +7 -5
- package/lib/store/RelayPublishQueue.js +1 -3
- package/lib/store/RelayReader.js +1 -3
- package/lib/store/RelayRecordSource.js +1 -3
- package/lib/store/RelayRecordSourceMapImpl.js +13 -18
- package/lib/store/RelayReferenceMarker.js +2 -6
- package/lib/store/RelayResponseNormalizer.js +9 -10
- package/lib/store/StoreInspector.js +7 -5
- package/lib/store/normalizeRelayPayload.js +6 -2
- package/lib/subscription/requestSubscription.js +4 -2
- package/lib/util/RelayFeatureFlags.js +1 -1
- package/lib/util/RelayReplaySubject.js +1 -3
- package/lib/util/createPayloadFor3DField.js +7 -2
- package/mutations/RelayDeclarativeMutationConfig.js.flow +380 -0
- package/mutations/RelayRecordProxy.js.flow +165 -0
- package/mutations/RelayRecordSourceMutator.js.flow +238 -0
- package/mutations/RelayRecordSourceProxy.js.flow +164 -0
- package/mutations/RelayRecordSourceSelectorProxy.js.flow +119 -0
- package/mutations/applyOptimisticMutation.js.flow +76 -0
- package/mutations/commitLocalUpdate.js.flow +24 -0
- package/mutations/commitMutation.js.flow +184 -0
- package/mutations/validateMutation.js.flow +211 -0
- package/network/ConvertToExecuteFunction.js.flow +49 -0
- package/network/RelayNetwork.js.flow +84 -0
- package/network/RelayNetworkTypes.js.flow +123 -0
- package/network/RelayObservable.js.flow +634 -0
- package/network/RelayQueryResponseCache.js.flow +111 -0
- package/package.json +1 -1
- package/query/GraphQLTag.js.flow +166 -0
- package/query/fetchQuery.js.flow +47 -0
- package/query/fetchQueryInternal.js.flow +349 -0
- package/relay-runtime.js +2 -2
- package/relay-runtime.min.js +2 -2
- package/store/ClientID.js.flow +43 -0
- package/store/DataChecker.js.flow +426 -0
- package/store/RelayConcreteVariables.js.flow +96 -0
- package/store/RelayModernEnvironment.js.flow +526 -0
- package/store/RelayModernFragmentSpecResolver.js.flow +426 -0
- package/store/RelayModernOperationDescriptor.js.flow +88 -0
- package/store/RelayModernQueryExecutor.js.flow +1327 -0
- package/store/RelayModernRecord.js.flow +403 -0
- package/store/RelayModernSelector.js.flow +444 -0
- package/store/RelayModernStore.js.flow +757 -0
- package/store/RelayOperationTracker.js.flow +164 -0
- package/store/RelayOptimisticRecordSource.js.flow +119 -0
- package/store/RelayPublishQueue.js.flow +401 -0
- package/store/RelayReader.js.flow +376 -0
- package/store/RelayRecordSource.js.flow +29 -0
- package/store/RelayRecordSourceMapImpl.js.flow +87 -0
- package/store/RelayRecordState.js.flow +37 -0
- package/store/RelayReferenceMarker.js.flow +236 -0
- package/store/RelayResponseNormalizer.js.flow +556 -0
- package/store/RelayStoreTypes.js.flow +873 -0
- package/store/RelayStoreUtils.js.flow +218 -0
- package/store/StoreInspector.js.flow +173 -0
- package/store/ViewerPattern.js.flow +26 -0
- package/store/cloneRelayHandleSourceField.js.flow +66 -0
- package/store/createFragmentSpecResolver.js.flow +55 -0
- package/store/createRelayContext.js.flow +44 -0
- package/store/defaultGetDataID.js.flow +27 -0
- package/store/hasOverlappingIDs.js.flow +34 -0
- package/store/isRelayModernEnvironment.js.flow +27 -0
- package/store/normalizeRelayPayload.js.flow +51 -0
- package/store/readInlineData.js.flow +75 -0
- package/subscription/requestSubscription.js.flow +100 -0
- package/util/JSResourceTypes.flow.js.flow +20 -0
- package/util/NormalizationNode.js.flow +191 -0
- package/util/ReaderNode.js.flow +208 -0
- package/util/RelayConcreteNode.js.flow +80 -0
- package/util/RelayDefaultHandleKey.js.flow +17 -0
- package/util/RelayError.js.flow +33 -0
- package/util/RelayFeatureFlags.js.flow +30 -0
- package/util/RelayProfiler.js.flow +284 -0
- package/util/RelayReplaySubject.js.flow +134 -0
- package/util/RelayRuntimeTypes.js.flow +70 -0
- package/util/createPayloadFor3DField.js.flow +43 -0
- package/util/deepFreeze.js.flow +36 -0
- package/util/generateID.js.flow +21 -0
- package/util/getFragmentIdentifier.js.flow +52 -0
- package/util/getRelayHandleKey.js.flow +41 -0
- package/util/getRequestIdentifier.js.flow +41 -0
- package/util/isPromise.js.flow +21 -0
- package/util/isScalarAndEqual.js.flow +26 -0
- package/util/recycleNodesInto.js.flow +80 -0
- package/util/resolveImmediate.js.flow +30 -0
- package/util/stableCopy.js.flow +35 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @flow strict-local
|
|
8
|
+
* @format
|
|
9
|
+
* @emails oncall+relay
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// flowlint ambiguous-object-type:error
|
|
13
|
+
|
|
14
|
+
'use strict';
|
|
15
|
+
|
|
16
|
+
const RelayConcreteNode = require('../util/RelayConcreteNode');
|
|
17
|
+
const RelayModernRecord = require('./RelayModernRecord');
|
|
18
|
+
const RelayRecordSourceMutator = require('../mutations/RelayRecordSourceMutator');
|
|
19
|
+
const RelayRecordSourceProxy = require('../mutations/RelayRecordSourceProxy');
|
|
20
|
+
const RelayStoreUtils = require('./RelayStoreUtils');
|
|
21
|
+
|
|
22
|
+
const cloneRelayHandleSourceField = require('./cloneRelayHandleSourceField');
|
|
23
|
+
const invariant = require('invariant');
|
|
24
|
+
|
|
25
|
+
const {isClientID} = require('./ClientID');
|
|
26
|
+
const {EXISTENT, UNKNOWN} = require('./RelayRecordState');
|
|
27
|
+
|
|
28
|
+
import type {
|
|
29
|
+
NormalizationField,
|
|
30
|
+
NormalizationLinkedField,
|
|
31
|
+
NormalizationModuleImport,
|
|
32
|
+
NormalizationNode,
|
|
33
|
+
NormalizationScalarField,
|
|
34
|
+
NormalizationSelection,
|
|
35
|
+
} from '../util/NormalizationNode';
|
|
36
|
+
import type {DataID, Variables} from '../util/RelayRuntimeTypes';
|
|
37
|
+
import type {GetDataID} from './RelayResponseNormalizer';
|
|
38
|
+
import type {
|
|
39
|
+
MissingFieldHandler,
|
|
40
|
+
MutableRecordSource,
|
|
41
|
+
NormalizationSelector,
|
|
42
|
+
OperationLoader,
|
|
43
|
+
Record,
|
|
44
|
+
RecordSource,
|
|
45
|
+
} from './RelayStoreTypes';
|
|
46
|
+
|
|
47
|
+
export type Availability = {|
|
|
48
|
+
+status: 'available' | 'missing',
|
|
49
|
+
+mostRecentlyInvalidatedAt: ?number,
|
|
50
|
+
|};
|
|
51
|
+
|
|
52
|
+
const {
|
|
53
|
+
CONDITION,
|
|
54
|
+
CLIENT_EXTENSION,
|
|
55
|
+
DEFER,
|
|
56
|
+
FRAGMENT_SPREAD,
|
|
57
|
+
INLINE_FRAGMENT,
|
|
58
|
+
LINKED_FIELD,
|
|
59
|
+
LINKED_HANDLE,
|
|
60
|
+
MODULE_IMPORT,
|
|
61
|
+
SCALAR_FIELD,
|
|
62
|
+
SCALAR_HANDLE,
|
|
63
|
+
STREAM,
|
|
64
|
+
} = RelayConcreteNode;
|
|
65
|
+
const {
|
|
66
|
+
getModuleOperationKey,
|
|
67
|
+
getStorageKey,
|
|
68
|
+
getArgumentValues,
|
|
69
|
+
} = RelayStoreUtils;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Synchronously check whether the records required to fulfill the given
|
|
73
|
+
* `selector` are present in `source`.
|
|
74
|
+
*
|
|
75
|
+
* If a field is missing, it uses the provided handlers to attempt to substitute
|
|
76
|
+
* data. The `target` will store all records that are modified because of a
|
|
77
|
+
* successful substitution.
|
|
78
|
+
*
|
|
79
|
+
* If all records are present, returns `true`, otherwise `false`.
|
|
80
|
+
*/
|
|
81
|
+
function check(
|
|
82
|
+
source: RecordSource,
|
|
83
|
+
target: MutableRecordSource,
|
|
84
|
+
selector: NormalizationSelector,
|
|
85
|
+
handlers: $ReadOnlyArray<MissingFieldHandler>,
|
|
86
|
+
operationLoader: ?OperationLoader,
|
|
87
|
+
getDataID: GetDataID,
|
|
88
|
+
): Availability {
|
|
89
|
+
const {dataID, node, variables} = selector;
|
|
90
|
+
const checker = new DataChecker(
|
|
91
|
+
source,
|
|
92
|
+
target,
|
|
93
|
+
variables,
|
|
94
|
+
handlers,
|
|
95
|
+
operationLoader,
|
|
96
|
+
getDataID,
|
|
97
|
+
);
|
|
98
|
+
return checker.check(node, dataID);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @private
|
|
103
|
+
*/
|
|
104
|
+
class DataChecker {
|
|
105
|
+
_handlers: $ReadOnlyArray<MissingFieldHandler>;
|
|
106
|
+
_mostRecentlyInvalidatedAt: number | null;
|
|
107
|
+
_mutator: RelayRecordSourceMutator;
|
|
108
|
+
_operationLoader: OperationLoader | null;
|
|
109
|
+
_operationLastWrittenAt: ?number;
|
|
110
|
+
_recordSourceProxy: RelayRecordSourceProxy;
|
|
111
|
+
_recordWasMissing: boolean;
|
|
112
|
+
_source: RecordSource;
|
|
113
|
+
_variables: Variables;
|
|
114
|
+
|
|
115
|
+
constructor(
|
|
116
|
+
source: RecordSource,
|
|
117
|
+
target: MutableRecordSource,
|
|
118
|
+
variables: Variables,
|
|
119
|
+
handlers: $ReadOnlyArray<MissingFieldHandler>,
|
|
120
|
+
operationLoader: ?OperationLoader,
|
|
121
|
+
getDataID: GetDataID,
|
|
122
|
+
) {
|
|
123
|
+
const mutator = new RelayRecordSourceMutator(source, target);
|
|
124
|
+
this._mostRecentlyInvalidatedAt = null;
|
|
125
|
+
this._handlers = handlers;
|
|
126
|
+
this._mutator = mutator;
|
|
127
|
+
this._operationLoader = operationLoader ?? null;
|
|
128
|
+
this._recordSourceProxy = new RelayRecordSourceProxy(mutator, getDataID);
|
|
129
|
+
this._recordWasMissing = false;
|
|
130
|
+
this._source = source;
|
|
131
|
+
this._variables = variables;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
check(node: NormalizationNode, dataID: DataID): Availability {
|
|
135
|
+
this._traverse(node, dataID);
|
|
136
|
+
|
|
137
|
+
return this._recordWasMissing === true
|
|
138
|
+
? {
|
|
139
|
+
status: 'missing',
|
|
140
|
+
mostRecentlyInvalidatedAt: this._mostRecentlyInvalidatedAt,
|
|
141
|
+
}
|
|
142
|
+
: {
|
|
143
|
+
status: 'available',
|
|
144
|
+
mostRecentlyInvalidatedAt: this._mostRecentlyInvalidatedAt,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
_getVariableValue(name: string): mixed {
|
|
149
|
+
invariant(
|
|
150
|
+
this._variables.hasOwnProperty(name),
|
|
151
|
+
'RelayAsyncLoader(): Undefined variable `%s`.',
|
|
152
|
+
name,
|
|
153
|
+
);
|
|
154
|
+
return this._variables[name];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
_handleMissing(): void {
|
|
158
|
+
this._recordWasMissing = true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
_getDataForHandlers(
|
|
162
|
+
field: NormalizationField,
|
|
163
|
+
dataID: DataID,
|
|
164
|
+
): {
|
|
165
|
+
args: Variables,
|
|
166
|
+
record: ?Record,
|
|
167
|
+
...
|
|
168
|
+
} {
|
|
169
|
+
return {
|
|
170
|
+
args: field.args ? getArgumentValues(field.args, this._variables) : {},
|
|
171
|
+
// Getting a snapshot of the record state is potentially expensive since
|
|
172
|
+
// we will need to merge the sink and source records. Since we do not create
|
|
173
|
+
// any new records in this process, it is probably reasonable to provide
|
|
174
|
+
// handlers with a copy of the source record.
|
|
175
|
+
// The only thing that the provided record will not contain is fields
|
|
176
|
+
// added by previous handlers.
|
|
177
|
+
record: this._source.get(dataID),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
_handleMissingScalarField(
|
|
182
|
+
field: NormalizationScalarField,
|
|
183
|
+
dataID: DataID,
|
|
184
|
+
): mixed {
|
|
185
|
+
if (field.name === 'id' && field.alias == null && isClientID(dataID)) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
const {args, record} = this._getDataForHandlers(field, dataID);
|
|
189
|
+
for (const handler of this._handlers) {
|
|
190
|
+
if (handler.kind === 'scalar') {
|
|
191
|
+
const newValue = handler.handle(
|
|
192
|
+
field,
|
|
193
|
+
record,
|
|
194
|
+
args,
|
|
195
|
+
this._recordSourceProxy,
|
|
196
|
+
);
|
|
197
|
+
if (newValue !== undefined) {
|
|
198
|
+
return newValue;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
this._handleMissing();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
_handleMissingLinkField(
|
|
206
|
+
field: NormalizationLinkedField,
|
|
207
|
+
dataID: DataID,
|
|
208
|
+
): ?DataID {
|
|
209
|
+
const {args, record} = this._getDataForHandlers(field, dataID);
|
|
210
|
+
for (const handler of this._handlers) {
|
|
211
|
+
if (handler.kind === 'linked') {
|
|
212
|
+
const newValue = handler.handle(
|
|
213
|
+
field,
|
|
214
|
+
record,
|
|
215
|
+
args,
|
|
216
|
+
this._recordSourceProxy,
|
|
217
|
+
);
|
|
218
|
+
if (
|
|
219
|
+
newValue != null &&
|
|
220
|
+
this._mutator.getStatus(newValue) === EXISTENT
|
|
221
|
+
) {
|
|
222
|
+
return newValue;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
this._handleMissing();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
_handleMissingPluralLinkField(
|
|
230
|
+
field: NormalizationLinkedField,
|
|
231
|
+
dataID: DataID,
|
|
232
|
+
): ?Array<?DataID> {
|
|
233
|
+
const {args, record} = this._getDataForHandlers(field, dataID);
|
|
234
|
+
for (const handler of this._handlers) {
|
|
235
|
+
if (handler.kind === 'pluralLinked') {
|
|
236
|
+
const newValue = handler.handle(
|
|
237
|
+
field,
|
|
238
|
+
record,
|
|
239
|
+
args,
|
|
240
|
+
this._recordSourceProxy,
|
|
241
|
+
);
|
|
242
|
+
if (newValue != null) {
|
|
243
|
+
const allItemsKnown = newValue.every(
|
|
244
|
+
linkedID =>
|
|
245
|
+
linkedID != null &&
|
|
246
|
+
this._mutator.getStatus(linkedID) === EXISTENT,
|
|
247
|
+
);
|
|
248
|
+
if (allItemsKnown) {
|
|
249
|
+
return newValue;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
this._handleMissing();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
_traverse(node: NormalizationNode, dataID: DataID): void {
|
|
258
|
+
const status = this._mutator.getStatus(dataID);
|
|
259
|
+
if (status === UNKNOWN) {
|
|
260
|
+
this._handleMissing();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (status === EXISTENT) {
|
|
264
|
+
const record = this._source.get(dataID);
|
|
265
|
+
const invalidatedAt = RelayModernRecord.getInvalidationEpoch(record);
|
|
266
|
+
if (invalidatedAt != null) {
|
|
267
|
+
this._mostRecentlyInvalidatedAt =
|
|
268
|
+
this._mostRecentlyInvalidatedAt != null
|
|
269
|
+
? Math.max(this._mostRecentlyInvalidatedAt, invalidatedAt)
|
|
270
|
+
: invalidatedAt;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
this._traverseSelections(node.selections, dataID);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
_traverseSelections(
|
|
278
|
+
selections: $ReadOnlyArray<NormalizationSelection>,
|
|
279
|
+
dataID: DataID,
|
|
280
|
+
): void {
|
|
281
|
+
selections.forEach(selection => {
|
|
282
|
+
switch (selection.kind) {
|
|
283
|
+
case SCALAR_FIELD:
|
|
284
|
+
this._checkScalar(selection, dataID);
|
|
285
|
+
break;
|
|
286
|
+
case LINKED_FIELD:
|
|
287
|
+
if (selection.plural) {
|
|
288
|
+
this._checkPluralLink(selection, dataID);
|
|
289
|
+
} else {
|
|
290
|
+
this._checkLink(selection, dataID);
|
|
291
|
+
}
|
|
292
|
+
break;
|
|
293
|
+
case CONDITION:
|
|
294
|
+
const conditionValue = this._getVariableValue(selection.condition);
|
|
295
|
+
if (conditionValue === selection.passingValue) {
|
|
296
|
+
this._traverseSelections(selection.selections, dataID);
|
|
297
|
+
}
|
|
298
|
+
break;
|
|
299
|
+
case INLINE_FRAGMENT:
|
|
300
|
+
const typeName = this._mutator.getType(dataID);
|
|
301
|
+
if (typeName != null && typeName === selection.type) {
|
|
302
|
+
this._traverseSelections(selection.selections, dataID);
|
|
303
|
+
}
|
|
304
|
+
break;
|
|
305
|
+
case LINKED_HANDLE:
|
|
306
|
+
// Handles have no selections themselves; traverse the original field
|
|
307
|
+
// where the handle was set-up instead.
|
|
308
|
+
const handleField = cloneRelayHandleSourceField(
|
|
309
|
+
selection,
|
|
310
|
+
selections,
|
|
311
|
+
this._variables,
|
|
312
|
+
);
|
|
313
|
+
if (handleField.plural) {
|
|
314
|
+
this._checkPluralLink(handleField, dataID);
|
|
315
|
+
} else {
|
|
316
|
+
this._checkLink(handleField, dataID);
|
|
317
|
+
}
|
|
318
|
+
break;
|
|
319
|
+
case MODULE_IMPORT:
|
|
320
|
+
this._checkModuleImport(selection, dataID);
|
|
321
|
+
break;
|
|
322
|
+
case DEFER:
|
|
323
|
+
case STREAM:
|
|
324
|
+
this._traverseSelections(selection.selections, dataID);
|
|
325
|
+
break;
|
|
326
|
+
case SCALAR_HANDLE:
|
|
327
|
+
case FRAGMENT_SPREAD:
|
|
328
|
+
invariant(
|
|
329
|
+
false,
|
|
330
|
+
'RelayAsyncLoader(): Unexpected ast kind `%s`.',
|
|
331
|
+
selection.kind,
|
|
332
|
+
);
|
|
333
|
+
// $FlowExpectedError - we need the break; for OSS linter
|
|
334
|
+
break;
|
|
335
|
+
case CLIENT_EXTENSION:
|
|
336
|
+
const recordWasMissing = this._recordWasMissing;
|
|
337
|
+
this._traverseSelections(selection.selections, dataID);
|
|
338
|
+
this._recordWasMissing = recordWasMissing;
|
|
339
|
+
break;
|
|
340
|
+
default:
|
|
341
|
+
(selection: empty);
|
|
342
|
+
invariant(
|
|
343
|
+
false,
|
|
344
|
+
'RelayAsyncLoader(): Unexpected ast kind `%s`.',
|
|
345
|
+
selection.kind,
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
_checkModuleImport(
|
|
352
|
+
moduleImport: NormalizationModuleImport,
|
|
353
|
+
dataID: DataID,
|
|
354
|
+
): void {
|
|
355
|
+
const operationLoader = this._operationLoader;
|
|
356
|
+
invariant(
|
|
357
|
+
operationLoader !== null,
|
|
358
|
+
'DataChecker: Expected an operationLoader to be configured when using `@module`.',
|
|
359
|
+
);
|
|
360
|
+
const operationKey = getModuleOperationKey(moduleImport.documentName);
|
|
361
|
+
const operationReference = this._mutator.getValue(dataID, operationKey);
|
|
362
|
+
if (operationReference == null) {
|
|
363
|
+
if (operationReference === undefined) {
|
|
364
|
+
this._handleMissing();
|
|
365
|
+
}
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const operation = operationLoader.get(operationReference);
|
|
369
|
+
if (operation != null) {
|
|
370
|
+
this._traverse(operation, dataID);
|
|
371
|
+
} else {
|
|
372
|
+
// If the fragment is not available, we assume that the data cannot have been
|
|
373
|
+
// processed yet and must therefore be missing.
|
|
374
|
+
this._handleMissing();
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
_checkScalar(field: NormalizationScalarField, dataID: DataID): void {
|
|
379
|
+
const storageKey = getStorageKey(field, this._variables);
|
|
380
|
+
let fieldValue = this._mutator.getValue(dataID, storageKey);
|
|
381
|
+
if (fieldValue === undefined) {
|
|
382
|
+
fieldValue = this._handleMissingScalarField(field, dataID);
|
|
383
|
+
if (fieldValue !== undefined) {
|
|
384
|
+
this._mutator.setValue(dataID, storageKey, fieldValue);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
_checkLink(field: NormalizationLinkedField, dataID: DataID): void {
|
|
390
|
+
const storageKey = getStorageKey(field, this._variables);
|
|
391
|
+
let linkedID = this._mutator.getLinkedRecordID(dataID, storageKey);
|
|
392
|
+
|
|
393
|
+
if (linkedID === undefined) {
|
|
394
|
+
linkedID = this._handleMissingLinkField(field, dataID);
|
|
395
|
+
if (linkedID != null) {
|
|
396
|
+
this._mutator.setLinkedRecordID(dataID, storageKey, linkedID);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (linkedID != null) {
|
|
400
|
+
this._traverse(field, linkedID);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
_checkPluralLink(field: NormalizationLinkedField, dataID: DataID): void {
|
|
405
|
+
const storageKey = getStorageKey(field, this._variables);
|
|
406
|
+
let linkedIDs = this._mutator.getLinkedRecordIDs(dataID, storageKey);
|
|
407
|
+
|
|
408
|
+
if (linkedIDs === undefined) {
|
|
409
|
+
linkedIDs = this._handleMissingPluralLinkField(field, dataID);
|
|
410
|
+
if (linkedIDs != null) {
|
|
411
|
+
this._mutator.setLinkedRecordIDs(dataID, storageKey, linkedIDs);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (linkedIDs) {
|
|
415
|
+
linkedIDs.forEach(linkedID => {
|
|
416
|
+
if (linkedID != null) {
|
|
417
|
+
this._traverse(field, linkedID);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
module.exports = {
|
|
425
|
+
check,
|
|
426
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @flow strict-local
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// flowlint ambiguous-object-type:error
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const invariant = require('invariant');
|
|
16
|
+
|
|
17
|
+
import type {NormalizationOperation} from '../util/NormalizationNode';
|
|
18
|
+
import type {ReaderFragment} from '../util/ReaderNode';
|
|
19
|
+
import type {Variables} from '../util/RelayRuntimeTypes';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Determines the variables that are in scope for a fragment given the variables
|
|
23
|
+
* in scope at the root query as well as any arguments applied at the fragment
|
|
24
|
+
* spread via `@arguments`.
|
|
25
|
+
*
|
|
26
|
+
* Note that this is analagous to determining function arguments given a function call.
|
|
27
|
+
*/
|
|
28
|
+
function getFragmentVariables(
|
|
29
|
+
fragment: ReaderFragment,
|
|
30
|
+
rootVariables: Variables,
|
|
31
|
+
argumentVariables: Variables,
|
|
32
|
+
): Variables {
|
|
33
|
+
let variables;
|
|
34
|
+
fragment.argumentDefinitions.forEach(definition => {
|
|
35
|
+
if (argumentVariables.hasOwnProperty(definition.name)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
variables = variables || {...argumentVariables};
|
|
39
|
+
switch (definition.kind) {
|
|
40
|
+
case 'LocalArgument':
|
|
41
|
+
variables[definition.name] = definition.defaultValue;
|
|
42
|
+
break;
|
|
43
|
+
case 'RootArgument':
|
|
44
|
+
if (!rootVariables.hasOwnProperty(definition.name)) {
|
|
45
|
+
/*
|
|
46
|
+
* Global variables passed as values of @arguments are not required to
|
|
47
|
+
* be declared unless they are used by the callee fragment or a
|
|
48
|
+
* descendant. In this case, the root variable may not be defined when
|
|
49
|
+
* resolving the callee's variables. The value is explicitly set to
|
|
50
|
+
* undefined to conform to the check in
|
|
51
|
+
* RelayStoreUtils.getStableVariableValue() that variable keys are all
|
|
52
|
+
* present.
|
|
53
|
+
*/
|
|
54
|
+
variables[definition.name] = undefined;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
variables[definition.name] = rootVariables[definition.name];
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
(definition: empty);
|
|
61
|
+
invariant(
|
|
62
|
+
false,
|
|
63
|
+
'RelayConcreteVariables: Unexpected node kind `%s` in fragment `%s`.',
|
|
64
|
+
definition.kind,
|
|
65
|
+
fragment.name,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return variables || argumentVariables;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Determines the variables that are in scope for a given operation given values
|
|
74
|
+
* for some/all of its arguments. Extraneous input variables are filtered from
|
|
75
|
+
* the output, and missing variables are set to default values (if given in the
|
|
76
|
+
* operation's definition).
|
|
77
|
+
*/
|
|
78
|
+
function getOperationVariables(
|
|
79
|
+
operation: NormalizationOperation,
|
|
80
|
+
variables: Variables,
|
|
81
|
+
): Variables {
|
|
82
|
+
const operationVariables = {};
|
|
83
|
+
operation.argumentDefinitions.forEach(def => {
|
|
84
|
+
let value = def.defaultValue;
|
|
85
|
+
if (variables[def.name] != null) {
|
|
86
|
+
value = variables[def.name];
|
|
87
|
+
}
|
|
88
|
+
operationVariables[def.name] = value;
|
|
89
|
+
});
|
|
90
|
+
return operationVariables;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = {
|
|
94
|
+
getFragmentVariables,
|
|
95
|
+
getOperationVariables,
|
|
96
|
+
};
|