packages/relay-runtime/mutations/commitMutation.js (136 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
* @format
*/
// flowlint ambiguous-object-type:error
'use strict';
import type {PayloadError, UploadableMap} from '../network/RelayNetworkTypes';
import type {GraphQLTaggedNode} from '../query/GraphQLTag';
import type {
IEnvironment,
MutationParameters,
SelectorStoreUpdater,
} from '../store/RelayStoreTypes';
import type {
CacheConfig,
Disposable,
Variables,
} from '../util/RelayRuntimeTypes';
import type {DeclarativeMutationConfig} from './RelayDeclarativeMutationConfig';
const {getRequest} = require('../query/GraphQLTag');
const {generateUniqueClientID} = require('../store/ClientID');
const isRelayModernEnvironment = require('../store/isRelayModernEnvironment');
const {
createOperationDescriptor,
} = require('../store/RelayModernOperationDescriptor');
const RelayDeclarativeMutationConfig = require('./RelayDeclarativeMutationConfig');
const validateMutation = require('./validateMutation');
const invariant = require('invariant');
const warning = require('warning');
export type MutationConfig<TMutation: MutationParameters> = {|
cacheConfig?: CacheConfig,
configs?: Array<DeclarativeMutationConfig>,
mutation: GraphQLTaggedNode,
onCompleted?: ?(
response: TMutation['response'],
errors: ?Array<PayloadError>,
) => void,
onError?: ?(error: Error) => void,
onNext?: ?() => void,
onUnsubscribe?: ?() => void,
optimisticResponse?: {
+rawResponse?: {...},
...TMutation,
...
}['rawResponse'],
optimisticUpdater?: ?SelectorStoreUpdater<TMutation['response']>,
updater?: ?SelectorStoreUpdater<TMutation['response']>,
uploadables?: UploadableMap,
variables: TMutation['variables'],
|};
export type DEPRECATED_MutationConfig<TMutationResponse> = MutationConfig<{|
response: TMutationResponse,
rawResponse: any,
variables: Variables,
|}>;
/**
* Higher-level helper function to execute a mutation against a specific
* environment.
*/
function commitMutation<TMutation: MutationParameters>(
environment: IEnvironment,
config: MutationConfig<TMutation>,
): Disposable {
invariant(
isRelayModernEnvironment(environment),
'commitMutation: expected `environment` to be an instance of ' +
'`RelayModernEnvironment`.',
);
const mutation = getRequest(config.mutation);
if (mutation.params.operationKind !== 'mutation') {
throw new Error('commitMutation: Expected mutation operation');
}
if (mutation.kind !== 'Request') {
throw new Error('commitMutation: Expected mutation to be of type request');
}
let {optimisticResponse, optimisticUpdater, updater} = config;
const {configs, cacheConfig, onError, onUnsubscribe, variables, uploadables} =
config;
const operation = createOperationDescriptor(
mutation,
variables,
cacheConfig,
generateUniqueClientID(),
);
// TODO: remove this check after we fix flow.
if (typeof optimisticResponse === 'function') {
optimisticResponse = optimisticResponse();
warning(
false,
'commitMutation: Expected `optimisticResponse` to be an object, ' +
'received a function.',
);
}
if (__DEV__) {
if (optimisticResponse instanceof Object) {
validateMutation(optimisticResponse, mutation, variables);
}
}
if (configs) {
({optimisticUpdater, updater} = RelayDeclarativeMutationConfig.convert(
configs,
mutation,
optimisticUpdater,
updater,
));
}
const errors = [];
const subscription = environment
.executeMutation({
operation,
optimisticResponse,
optimisticUpdater,
updater,
uploadables,
})
.subscribe({
next: payload => {
if (Array.isArray(payload)) {
payload.forEach(item => {
if (item.errors) {
errors.push(...item.errors);
}
});
} else {
if (payload.errors) {
errors.push(...payload.errors);
}
}
config.onNext?.();
},
complete: () => {
const {onCompleted} = config;
if (onCompleted) {
const snapshot = environment.lookup(operation.fragment);
onCompleted(
(snapshot.data: $FlowFixMe),
errors.length !== 0 ? errors : null,
);
}
},
error: onError,
unsubscribe: onUnsubscribe,
});
return {dispose: subscription.unsubscribe};
}
module.exports = commitMutation;