import { getOperationName } from 'apollo-utilities'; import Observable from 'zen-observable-ts'; import { print } from 'graphql/language/printer'; import { GraphQLRequest, Operation } from './types'; import { ApolloLink } from './link'; export function validateOperation(operation: GraphQLRequest): GraphQLRequest { const OPERATION_FIELDS = [ 'query', 'operationName', 'variables', 'extensions', 'context', ]; for (let key of Object.keys(operation)) { if (OPERATION_FIELDS.indexOf(key) < 0) { throw new Error(`illegal argument: ${key}`); } } return operation; } export class LinkError extends Error { public link: ApolloLink; constructor(message?: string, link?: ApolloLink) { super(message); this.link = link; } } export function isTerminating(link: ApolloLink): boolean { return link.request.length <= 1; } export function toPromise<R>(observable: Observable<R>): Promise<R> { let completed = false; return new Promise<R>((resolve, reject) => { observable.subscribe({ next: data => { if (completed) { console.warn( `Promise Wrapper does not support multiple results from Observable`, ); } else { completed = true; resolve(data); } }, error: reject, }); }); } // backwards compat export const makePromise = toPromise; export function fromPromise<T>(promise: Promise<T>): Observable<T> { return new Observable<T>(observer => { promise .then((value: T) => { observer.next(value); observer.complete(); }) .catch(observer.error.bind(observer)); }); } export function fromError<T>(errorValue: any): Observable<T> { return new Observable<T>(observer => { observer.error(errorValue); }); } export function transformOperation(operation: GraphQLRequest): GraphQLRequest { const transformedOperation: GraphQLRequest = { variables: operation.variables || {}, extensions: operation.extensions || {}, operationName: operation.operationName, query: operation.query, }; // best guess at an operation name if (!transformedOperation.operationName) { transformedOperation.operationName = typeof transformedOperation.query !== 'string' ? getOperationName(transformedOperation.query) : ''; } return transformedOperation as Operation; } export function createOperation( starting: any, operation: GraphQLRequest, ): Operation { let context = { ...starting }; const setContext = next => { if (typeof next === 'function') { context = { ...context, ...next(context) }; } else { context = { ...context, ...next }; } }; const getContext = () => ({ ...context }); Object.defineProperty(operation, 'setContext', { enumerable: false, value: setContext, }); Object.defineProperty(operation, 'getContext', { enumerable: false, value: getContext, }); Object.defineProperty(operation, 'toKey', { enumerable: false, value: () => getKey(operation), }); return operation as Operation; } export function getKey(operation: GraphQLRequest) { // XXX we're assuming here that variables will be serialized in the same order. // that might not always be true return `${print(operation.query)}|${JSON.stringify(operation.variables)}|${ operation.operationName }`; }