@graphql-yoga/plugin-persisted-operations
Version:
Persisted Operations plugin for GraphQL Yoga.
106 lines (105 loc) • 4.63 kB
JavaScript
import { createGraphQLError, } from 'graphql-yoga';
import { handleMaybePromise } from '@whatwg-node/promise-helpers';
export const defaultExtractPersistedOperationId = (params) => {
if (params.extensions != null &&
typeof params.extensions === 'object' &&
params.extensions?.['persistedQuery'] != null &&
typeof params.extensions?.['persistedQuery'] === 'object' &&
params.extensions?.['persistedQuery']?.['version'] === 1 &&
typeof params.extensions?.['persistedQuery']?.['sha256Hash'] === 'string') {
return params.extensions?.['persistedQuery']?.['sha256Hash'];
}
return null;
};
export function usePersistedOperations({ allowArbitraryOperations = false, extractPersistedOperationId = defaultExtractPersistedOperationId, getPersistedOperation, skipDocumentValidation = false, customErrors, }) {
const operationASTByCtx = new WeakMap();
const persistedOperationCtx = new WeakSet();
const notFoundErrorFactory = createErrorFactory('PersistedQueryNotFound', customErrors?.notFound || {
message: 'PersistedQueryNotFound',
extensions: {
code: 'PERSISTED_QUERY_NOT_IN_LIST',
},
});
const keyNotFoundErrorFactory = createErrorFactory('PersistedQueryKeyNotFound', customErrors?.keyNotFound || {
message: 'PersistedQueryKeyNotFound',
extensions: {
code: 'PERSISTED_QUERY_ID_REQUIRED',
},
});
const persistentQueryOnlyErrorFactory = createErrorFactory('PersistedQueryOnly', customErrors?.persistedQueryOnly || {
message: 'PersistedQueryOnly',
extensions: {
code: 'CANNOT_SEND_PQ_ID_AND_BODY',
},
});
return {
onParams(payload) {
const { request, params, setParams, context } = payload;
if (params.query) {
if (allowArbitraryOperations === false) {
throw persistentQueryOnlyErrorFactory(payload);
}
if (typeof allowArbitraryOperations === 'function') {
return handleMaybePromise(() => allowArbitraryOperations(request), result => {
if (!result) {
throw persistentQueryOnlyErrorFactory(payload);
}
});
}
return;
}
const persistedOperationKey = extractPersistedOperationId(params, request, payload.context);
if (persistedOperationKey == null) {
throw keyNotFoundErrorFactory(payload);
}
return handleMaybePromise(() => getPersistedOperation(persistedOperationKey, request, payload.context), persistedOperation => {
if (persistedOperation == null) {
throw notFoundErrorFactory(payload);
}
if (typeof persistedOperation === 'object') {
if (persistedOperation.kind !== 'Document') {
throw new Error('Persisted operation object is not a valid DocumentNode');
}
setParams({
query: `__PERSISTED_OPERATION_${persistedOperationKey}__`,
operationName: params.operationName,
variables: params.variables,
extensions: params.extensions,
});
operationASTByCtx.set(context, persistedOperation);
}
else {
setParams({
query: persistedOperation,
operationName: params.operationName,
variables: params.variables,
extensions: params.extensions,
});
}
persistedOperationCtx.add(context);
});
},
onParse({ setParsedDocument, context }) {
const ast = operationASTByCtx.get(context);
if (ast) {
setParsedDocument(ast);
}
},
onValidate({ setResult, context }) {
if (skipDocumentValidation && persistedOperationCtx.has(context)) {
setResult([]); // skip validation
}
},
};
}
function createErrorFactory(defaultMessage, options) {
if (typeof options === 'string') {
return () => createGraphQLError(options);
}
if (typeof options === 'function') {
return options;
}
return () => {
return createGraphQLError(options?.message ?? defaultMessage, options);
};
}