relay-runtime
Version:
A core runtime for building GraphQL-driven applications.
247 lines (232 loc) • 6.96 kB
Flow
/**
* 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
* @format
* @oncall relay
*/
'use strict';
import type {
HandleFieldPayload,
Handler,
RecordProxy,
RecordSourceProxy,
} from '../../store/RelayStoreTypes';
const ConnectionHandler = require('./ConnectionHandler');
const ConnectionInterface = require('./ConnectionInterface');
const invariant = require('invariant');
const warning = require('warning');
const DeleteRecordHandler = {
update: (store: RecordSourceProxy, payload: HandleFieldPayload) => {
const record = store.get(payload.dataID);
if (record != null) {
const idOrIds = record.getValue(payload.fieldKey);
if (typeof idOrIds === 'string') {
store.delete(idOrIds);
} else if (Array.isArray(idOrIds)) {
idOrIds.forEach(id => {
if (typeof id === 'string') {
store.delete(id);
}
});
}
}
},
};
const DeleteEdgeHandler = {
update: (store: RecordSourceProxy, payload: HandleFieldPayload) => {
const record = store.get(payload.dataID);
if (record == null) {
return;
}
const {connections} = payload.handleArgs;
invariant(
connections != null,
'MutationHandlers: Expected connection IDs to be specified.',
);
const idOrIds = record.getValue(payload.fieldKey);
const idList = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
idList.forEach(id => {
if (typeof id === 'string') {
for (const connectionID of connections) {
const connection = store.get(connectionID);
if (connection == null) {
warning(
false,
"[Relay] The connection with id `%s` doesn't exist.",
connectionID,
);
continue;
}
ConnectionHandler.deleteNode(connection, id);
}
}
});
},
};
const AppendEdgeHandler: Handler = {
update: edgeUpdater(ConnectionHandler.insertEdgeAfter),
};
const PrependEdgeHandler: Handler = {
update: edgeUpdater(ConnectionHandler.insertEdgeBefore),
};
const AppendNodeHandler: Handler = {
update: nodeUpdater(ConnectionHandler.insertEdgeAfter),
};
const PrependNodeHandler: Handler = {
update: nodeUpdater(ConnectionHandler.insertEdgeBefore),
};
function edgeUpdater(
insertFn: (RecordProxy, RecordProxy, ?string) => void,
): (RecordSourceProxy, HandleFieldPayload) => void {
return (store: RecordSourceProxy, payload: HandleFieldPayload) => {
const record = store.get(payload.dataID);
if (record == null) {
return;
}
const {connections} = payload.handleArgs;
invariant(
connections != null,
'MutationHandlers: Expected connection IDs to be specified.',
);
let singleServerEdge, serverEdges;
try {
singleServerEdge = record.getLinkedRecord(payload.fieldKey);
} catch {}
if (!singleServerEdge) {
try {
serverEdges = record.getLinkedRecords(payload.fieldKey);
} catch {}
}
if (singleServerEdge == null && serverEdges == null) {
warning(
false,
'MutationHandlers: Expected the server edge to be non-null.',
);
return;
}
const {NODE, EDGES} = ConnectionInterface.get();
const serverEdgeList = serverEdges ?? [singleServerEdge];
for (const serverEdge of serverEdgeList) {
if (serverEdge == null) {
continue;
}
const serverNode = serverEdge.getLinkedRecord('node');
if (!serverNode) {
continue;
}
const serverNodeId = serverNode.getDataID();
for (const connectionID of connections) {
const connection = store.get(connectionID);
if (connection == null) {
warning(
false,
"[Relay] The connection with id `%s` doesn't exist.",
connectionID,
);
continue;
}
const nodeAlreadyExistsInConnection = connection
.getLinkedRecords(EDGES)
?.some(
edge => edge?.getLinkedRecord(NODE)?.getDataID() === serverNodeId,
);
if (nodeAlreadyExistsInConnection) {
continue;
}
const clientEdge = ConnectionHandler.buildConnectionEdge(
store,
connection,
serverEdge,
);
invariant(
clientEdge != null,
'MutationHandlers: Failed to build the edge.',
);
insertFn(connection, clientEdge);
}
}
};
}
function nodeUpdater(
insertFn: (RecordProxy, RecordProxy, ?string) => void,
): (RecordSourceProxy, HandleFieldPayload) => void {
return (store: RecordSourceProxy, payload: HandleFieldPayload) => {
const record = store.get(payload.dataID);
if (record == null) {
return;
}
const {connections, edgeTypeName} = payload.handleArgs;
invariant(
connections != null,
'MutationHandlers: Expected connection IDs to be specified.',
);
invariant(
edgeTypeName != null,
'MutationHandlers: Expected edge typename to be specified.',
);
let singleServerNode;
let serverNodes;
try {
singleServerNode = record.getLinkedRecord(payload.fieldKey);
} catch {}
if (!singleServerNode) {
try {
serverNodes = record.getLinkedRecords(payload.fieldKey);
} catch {}
}
if (singleServerNode == null && serverNodes == null) {
warning(false, 'MutationHandlers: Expected target node to exist.');
return;
}
const {NODE, EDGES} = ConnectionInterface.get();
const serverNodeList = serverNodes ?? [singleServerNode];
for (const serverNode of serverNodeList) {
if (serverNode == null) {
continue;
}
const serverNodeId = serverNode.getDataID();
for (const connectionID of connections) {
const connection = store.get(connectionID);
if (connection == null) {
warning(
false,
"[Relay] The connection with id `%s` doesn't exist.",
connectionID,
);
continue;
}
const nodeAlreadyExistsInConnection = connection
.getLinkedRecords(EDGES)
?.some(
edge => edge?.getLinkedRecord(NODE)?.getDataID() === serverNodeId,
);
if (nodeAlreadyExistsInConnection) {
continue;
}
const clientEdge = ConnectionHandler.createEdge(
store,
connection,
serverNode,
edgeTypeName,
);
invariant(
clientEdge != null,
'MutationHandlers: Failed to build the edge.',
);
insertFn(connection, clientEdge);
}
}
};
}
module.exports = {
AppendEdgeHandler,
DeleteRecordHandler,
PrependEdgeHandler,
AppendNodeHandler,
PrependNodeHandler,
DeleteEdgeHandler,
};