packages/relay-runtime/mutations/RelayRecordProxy.js (127 lines of code) (raw):
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
// flowlint ambiguous-object-type:error
'use strict';
import type {RecordProxy} from '../store/RelayStoreTypes';
import type {Arguments} from '../store/RelayStoreUtils';
import type {DataID} from '../util/RelayRuntimeTypes';
import type RelayRecordSourceMutator from './RelayRecordSourceMutator';
import type RelayRecordSourceProxy from './RelayRecordSourceProxy';
const {generateClientID} = require('../store/ClientID');
const {getStableStorageKey} = require('../store/RelayStoreUtils');
const invariant = require('invariant');
/**
* @internal
*
* A helper class for manipulating a given record from a record source via an
* imperative/OO-style API.
*/
class RelayRecordProxy implements RecordProxy {
_dataID: DataID;
_mutator: RelayRecordSourceMutator;
_source: RelayRecordSourceProxy;
constructor(
source: RelayRecordSourceProxy,
mutator: RelayRecordSourceMutator,
dataID: DataID,
) {
this._dataID = dataID;
this._mutator = mutator;
this._source = source;
}
copyFieldsFrom(source: RecordProxy): void {
this._mutator.copyFields(source.getDataID(), this._dataID);
}
getDataID(): DataID {
return this._dataID;
}
getType(): string {
const type = this._mutator.getType(this._dataID);
invariant(
type != null,
'RelayRecordProxy: Cannot get the type of deleted record `%s`.',
this._dataID,
);
return type;
}
getValue(name: string, args?: ?Arguments): mixed {
const storageKey = getStableStorageKey(name, args);
return this._mutator.getValue(this._dataID, storageKey);
}
setValue(value: mixed, name: string, args?: ?Arguments): RecordProxy {
invariant(
isValidLeafValue(value),
'RelayRecordProxy#setValue(): Expected a scalar or array of scalars, ' +
'got `%s`.',
JSON.stringify(value),
);
const storageKey = getStableStorageKey(name, args);
this._mutator.setValue(this._dataID, storageKey, value);
return this;
}
getLinkedRecord(name: string, args?: ?Arguments): ?RecordProxy {
const storageKey = getStableStorageKey(name, args);
const linkedID = this._mutator.getLinkedRecordID(this._dataID, storageKey);
return linkedID != null ? this._source.get(linkedID) : linkedID;
}
setLinkedRecord(
record: RecordProxy,
name: string,
args?: ?Arguments,
): RecordProxy {
invariant(
record instanceof RelayRecordProxy,
'RelayRecordProxy#setLinkedRecord(): Expected a record, got `%s`.',
record,
);
const storageKey = getStableStorageKey(name, args);
const linkedID = record.getDataID();
this._mutator.setLinkedRecordID(this._dataID, storageKey, linkedID);
return this;
}
getOrCreateLinkedRecord(
name: string,
typeName: string,
args?: ?Arguments,
): RecordProxy {
let linkedRecord = this.getLinkedRecord(name, args);
if (!linkedRecord) {
const storageKey = getStableStorageKey(name, args);
const clientID = generateClientID(this.getDataID(), storageKey);
// NOTE: it's possible that a client record for this field exists
// but the field itself was unset.
linkedRecord =
this._source.get(clientID) ?? this._source.create(clientID, typeName);
this.setLinkedRecord(linkedRecord, name, args);
}
return linkedRecord;
}
getLinkedRecords(name: string, args?: ?Arguments): ?Array<?RecordProxy> {
const storageKey = getStableStorageKey(name, args);
const linkedIDs = this._mutator.getLinkedRecordIDs(
this._dataID,
storageKey,
);
if (linkedIDs == null) {
return linkedIDs;
}
return linkedIDs.map(linkedID => {
return linkedID != null ? this._source.get(linkedID) : linkedID;
});
}
setLinkedRecords(
records: Array<?RecordProxy>,
name: string,
args?: ?Arguments,
): RecordProxy {
invariant(
Array.isArray(records),
'RelayRecordProxy#setLinkedRecords(): Expected records to be an array, got `%s`.',
records,
);
const storageKey = getStableStorageKey(name, args);
const linkedIDs = records.map(record => record && record.getDataID());
this._mutator.setLinkedRecordIDs(this._dataID, storageKey, linkedIDs);
return this;
}
invalidateRecord(): void {
this._source.markIDForInvalidation(this._dataID);
}
}
function isValidLeafValue(value: mixed): boolean {
return (
value == null ||
typeof value !== 'object' ||
(Array.isArray(value) && value.every(isValidLeafValue))
);
}
module.exports = RelayRecordProxy;