400 lines
13 KiB
JavaScript
400 lines
13 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* strict-local
|
|
* @format
|
|
* @emails oncall+relay
|
|
*/
|
|
'use strict';
|
|
|
|
/**
|
|
* Synchronously check whether the records required to fulfill the given
|
|
* `selector` are present in `source`.
|
|
*
|
|
* If a field is missing, it uses the provided handlers to attempt to substitute
|
|
* data. The `target` will store all records that are modified because of a
|
|
* successful substitution.
|
|
*
|
|
* If all records are present, returns `true`, otherwise `false`.
|
|
*/
|
|
function check(source, target, selector, handlers, operationLoader) {
|
|
var dataID = selector.dataID,
|
|
node = selector.node,
|
|
variables = selector.variables;
|
|
var checker = new DataChecker(source, target, variables, handlers, operationLoader);
|
|
return checker.check(node, dataID);
|
|
}
|
|
/**
|
|
* @private
|
|
*/
|
|
|
|
|
|
var DataChecker =
|
|
/*#__PURE__*/
|
|
function () {
|
|
function DataChecker(source, target, variables, handlers, operationLoader) {
|
|
var _operationLoader;
|
|
|
|
this._operationLoader = (_operationLoader = operationLoader) !== null && _operationLoader !== void 0 ? _operationLoader : null;
|
|
this._handlers = handlers;
|
|
this._mutator = new (require("./RelayRecordSourceMutator"))(source, target);
|
|
this._recordWasMissing = false;
|
|
this._source = source;
|
|
this._variables = variables;
|
|
this._recordSourceProxy = new (require("./RelayRecordSourceProxy"))(this._mutator);
|
|
}
|
|
|
|
var _proto = DataChecker.prototype;
|
|
|
|
_proto.check = function check(node, dataID) {
|
|
this._traverse(node, dataID);
|
|
|
|
return !this._recordWasMissing;
|
|
};
|
|
|
|
_proto._getVariableValue = function _getVariableValue(name) {
|
|
!this._variables.hasOwnProperty(name) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayAsyncLoader(): Undefined variable `%s`.', name) : require("fbjs/lib/invariant")(false) : void 0;
|
|
return this._variables[name];
|
|
};
|
|
|
|
_proto._handleMissing = function _handleMissing() {
|
|
this._recordWasMissing = true;
|
|
};
|
|
|
|
_proto._getDataForHandlers = function _getDataForHandlers(field, dataID) {
|
|
return {
|
|
args: field.args ? require("./RelayStoreUtils").getArgumentValues(field.args, this._variables) : {},
|
|
// Getting a snapshot of the record state is potentially expensive since
|
|
// we will need to merge the sink and source records. Since we do not create
|
|
// any new records in this process, it is probably reasonable to provide
|
|
// handlers with a copy of the source record.
|
|
// The only thing that the provided record will not contain is fields
|
|
// added by previous handlers.
|
|
record: this._source.get(dataID)
|
|
};
|
|
};
|
|
|
|
_proto._handleMissingScalarField = function _handleMissingScalarField(field, dataID) {
|
|
var _this$_getDataForHand = this._getDataForHandlers(field, dataID),
|
|
args = _this$_getDataForHand.args,
|
|
record = _this$_getDataForHand.record;
|
|
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = this._handlers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var handler = _step.value;
|
|
|
|
if (handler.kind === 'scalar') {
|
|
var newValue = handler.handle(field, record, args, this._recordSourceProxy);
|
|
|
|
if (newValue !== undefined) {
|
|
return newValue;
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator["return"] != null) {
|
|
_iterator["return"]();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
|
|
this._handleMissing();
|
|
};
|
|
|
|
_proto._handleMissingLinkField = function _handleMissingLinkField(field, dataID) {
|
|
var _this$_getDataForHand2 = this._getDataForHandlers(field, dataID),
|
|
args = _this$_getDataForHand2.args,
|
|
record = _this$_getDataForHand2.record;
|
|
|
|
var _iteratorNormalCompletion2 = true;
|
|
var _didIteratorError2 = false;
|
|
var _iteratorError2 = undefined;
|
|
|
|
try {
|
|
for (var _iterator2 = this._handlers[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
var handler = _step2.value;
|
|
|
|
if (handler.kind === 'linked') {
|
|
var newValue = handler.handle(field, record, args, this._recordSourceProxy);
|
|
|
|
if (newValue != null && this._mutator.getStatus(newValue) === require("./RelayRecordState").EXISTENT) {
|
|
return newValue;
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError2 = true;
|
|
_iteratorError2 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) {
|
|
_iterator2["return"]();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError2) {
|
|
throw _iteratorError2;
|
|
}
|
|
}
|
|
}
|
|
|
|
this._handleMissing();
|
|
};
|
|
|
|
_proto._handleMissingPluralLinkField = function _handleMissingPluralLinkField(field, dataID) {
|
|
var _this = this;
|
|
|
|
var _this$_getDataForHand3 = this._getDataForHandlers(field, dataID),
|
|
args = _this$_getDataForHand3.args,
|
|
record = _this$_getDataForHand3.record;
|
|
|
|
var _iteratorNormalCompletion3 = true;
|
|
var _didIteratorError3 = false;
|
|
var _iteratorError3 = undefined;
|
|
|
|
try {
|
|
for (var _iterator3 = this._handlers[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
|
var handler = _step3.value;
|
|
|
|
if (handler.kind === 'pluralLinked') {
|
|
var newValue = handler.handle(field, record, args, this._recordSourceProxy);
|
|
|
|
if (newValue != null) {
|
|
return newValue.filter(function (linkedID) {
|
|
return linkedID != null && _this._mutator.getStatus(linkedID) === require("./RelayRecordState").EXISTENT;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError3 = true;
|
|
_iteratorError3 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion3 && _iterator3["return"] != null) {
|
|
_iterator3["return"]();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError3) {
|
|
throw _iteratorError3;
|
|
}
|
|
}
|
|
}
|
|
|
|
this._handleMissing();
|
|
};
|
|
|
|
_proto._traverse = function _traverse(node, dataID) {
|
|
var status = this._mutator.getStatus(dataID);
|
|
|
|
if (status === require("./RelayRecordState").UNKNOWN) {
|
|
this._handleMissing();
|
|
}
|
|
|
|
if (status === require("./RelayRecordState").EXISTENT) {
|
|
this._traverseSelections(node.selections, dataID);
|
|
}
|
|
};
|
|
|
|
_proto._traverseSelections = function _traverseSelections(selections, dataID) {
|
|
var _this2 = this;
|
|
|
|
selections.forEach(function (selection) {
|
|
switch (selection.kind) {
|
|
case require("./RelayConcreteNode").SCALAR_FIELD:
|
|
_this2._checkScalar(selection, dataID);
|
|
|
|
break;
|
|
|
|
case require("./RelayConcreteNode").LINKED_FIELD:
|
|
if (selection.plural) {
|
|
_this2._checkPluralLink(selection, dataID);
|
|
} else {
|
|
_this2._checkLink(selection, dataID);
|
|
}
|
|
|
|
break;
|
|
|
|
case require("./RelayConcreteNode").CONDITION:
|
|
var conditionValue = _this2._getVariableValue(selection.condition);
|
|
|
|
if (conditionValue === selection.passingValue) {
|
|
_this2._traverseSelections(selection.selections, dataID);
|
|
}
|
|
|
|
break;
|
|
|
|
case require("./RelayConcreteNode").INLINE_FRAGMENT:
|
|
var typeName = _this2._mutator.getType(dataID);
|
|
|
|
if (typeName != null && typeName === selection.type) {
|
|
_this2._traverseSelections(selection.selections, dataID);
|
|
}
|
|
|
|
break;
|
|
|
|
case require("./RelayConcreteNode").LINKED_HANDLE:
|
|
// Handles have no selections themselves; traverse the original field
|
|
// where the handle was set-up instead.
|
|
var handleField = require("./cloneRelayHandleSourceField")(selection, selections, _this2._variables);
|
|
|
|
if (handleField.plural) {
|
|
_this2._checkPluralLink(handleField, dataID);
|
|
} else {
|
|
_this2._checkLink(handleField, dataID);
|
|
}
|
|
|
|
break;
|
|
|
|
case require("./RelayConcreteNode").MATCH_FIELD:
|
|
_this2._checkMatch(selection, dataID);
|
|
|
|
break;
|
|
|
|
case require("./RelayConcreteNode").DEFER:
|
|
case require("./RelayConcreteNode").STREAM:
|
|
_this2._traverseSelections(selection.selections, dataID);
|
|
|
|
break;
|
|
|
|
case require("./RelayConcreteNode").SCALAR_HANDLE:
|
|
case require("./RelayConcreteNode").FRAGMENT_SPREAD:
|
|
!false ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayAsyncLoader(): Unexpected ast kind `%s`.', selection.kind) : require("fbjs/lib/invariant")(false) : void 0; // $FlowExpectedError - we need the break; for OSS linter
|
|
|
|
break;
|
|
|
|
default:
|
|
selection;
|
|
!false ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayAsyncLoader(): Unexpected ast kind `%s`.', selection.kind) : require("fbjs/lib/invariant")(false) : void 0;
|
|
}
|
|
});
|
|
};
|
|
|
|
_proto._checkMatch = function _checkMatch(field, dataID) {
|
|
var storageKey = require("./RelayStoreUtils").getStorageKey(field, this._variables);
|
|
|
|
var linkedID = this._mutator.getLinkedRecordID(dataID, storageKey);
|
|
|
|
if (linkedID === undefined) {
|
|
this._handleMissing();
|
|
} else if (linkedID !== null) {
|
|
var status = this._mutator.getStatus(linkedID);
|
|
|
|
if (status === require("./RelayRecordState").UNKNOWN) {
|
|
this._handleMissing();
|
|
|
|
return;
|
|
}
|
|
|
|
if (status !== require("./RelayRecordState").EXISTENT) {
|
|
return;
|
|
}
|
|
|
|
var typeName = this._mutator.getType(linkedID);
|
|
|
|
var match = typeName != null ? field.matchesByType[typeName] : null;
|
|
|
|
if (match != null) {
|
|
var operationLoader = this._operationLoader;
|
|
!(operationLoader !== null) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'DataChecker: Expected an operationLoader to be configured when using `@match`.') : require("fbjs/lib/invariant")(false) : void 0;
|
|
|
|
var operationReference = this._mutator.getValue(linkedID, require("./RelayStoreUtils").MATCH_FRAGMENT_KEY);
|
|
|
|
if (operationReference === undefined) {
|
|
this._handleMissing();
|
|
|
|
return;
|
|
} else if (operationReference === null) {
|
|
return;
|
|
}
|
|
|
|
var operation = operationLoader.get(operationReference);
|
|
|
|
if (operation != null) {
|
|
this._traverse(operation, linkedID);
|
|
} else {
|
|
// If the fragment is not available, we assume that the data cannot have been
|
|
// processed yet and must therefore be missing.
|
|
this._handleMissing();
|
|
}
|
|
} else {// TODO: warn: store is corrupt: the field should be null if the typename did not match
|
|
}
|
|
}
|
|
};
|
|
|
|
_proto._checkScalar = function _checkScalar(field, dataID) {
|
|
var storageKey = require("./RelayStoreUtils").getStorageKey(field, this._variables);
|
|
|
|
var fieldValue = this._mutator.getValue(dataID, storageKey);
|
|
|
|
if (fieldValue === undefined) {
|
|
fieldValue = this._handleMissingScalarField(field, dataID);
|
|
|
|
if (fieldValue !== undefined) {
|
|
this._mutator.setValue(dataID, storageKey, fieldValue);
|
|
}
|
|
}
|
|
};
|
|
|
|
_proto._checkLink = function _checkLink(field, dataID) {
|
|
var storageKey = require("./RelayStoreUtils").getStorageKey(field, this._variables);
|
|
|
|
var linkedID = this._mutator.getLinkedRecordID(dataID, storageKey);
|
|
|
|
if (linkedID === undefined) {
|
|
linkedID = this._handleMissingLinkField(field, dataID);
|
|
|
|
if (linkedID != null) {
|
|
this._mutator.setLinkedRecordID(dataID, storageKey, linkedID);
|
|
}
|
|
}
|
|
|
|
if (linkedID != null) {
|
|
this._traverse(field, linkedID);
|
|
}
|
|
};
|
|
|
|
_proto._checkPluralLink = function _checkPluralLink(field, dataID) {
|
|
var _this3 = this;
|
|
|
|
var storageKey = require("./RelayStoreUtils").getStorageKey(field, this._variables);
|
|
|
|
var linkedIDs = this._mutator.getLinkedRecordIDs(dataID, storageKey);
|
|
|
|
if (linkedIDs === undefined) {
|
|
linkedIDs = this._handleMissingPluralLinkField(field, dataID);
|
|
|
|
if (linkedIDs != null) {
|
|
this._mutator.setLinkedRecordIDs(dataID, storageKey, linkedIDs);
|
|
}
|
|
}
|
|
|
|
if (linkedIDs) {
|
|
linkedIDs.forEach(function (linkedID) {
|
|
if (linkedID != null) {
|
|
_this3._traverse(field, linkedID);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
return DataChecker;
|
|
}();
|
|
|
|
module.exports = {
|
|
check: check
|
|
}; |