Skip to content

Instantly share code, notes, and snippets.

@robzhu
Last active July 14, 2017 18:35
Show Gist options
  • Save robzhu/d2d9e3238b586e67569646a5944154b8 to your computer and use it in GitHub Desktop.
Save robzhu/d2d9e3238b586e67569646a5944154b8 to your computer and use it in GitHub Desktop.
Alternate createSourceEventStream implementation using wrapped promise
function createSourceEventStream(
schema: GraphQLSchema,
document: DocumentNode,
rootValue?: mixed,
contextValue?: mixed,
variableValues?: ?{[key: string]: mixed},
operationName?: ?string,
fieldResolver?: ?GraphQLFieldResolver<any, any>
): Promise<AsyncIterable<mixed>> {
return new Promise((resolve, reject) => {
// If arguments are missing or incorrectly typed, this is an internal
// developer mistake which should throw an early error.
assertValidExecutionArguments(
schema,
document,
variableValues
);
// If a valid context cannot be created due to incorrect arguments,
// this will throw an error.
const exeContext = buildExecutionContext(
schema,
document,
rootValue,
contextValue,
variableValues,
operationName,
fieldResolver
);
const type = getOperationRootType(schema, exeContext.operation);
const fields = collectFields(
exeContext,
type,
exeContext.operation.selectionSet,
Object.create(null),
Object.create(null)
);
const responseNames = Object.keys(fields);
const responseName = responseNames[0];
const fieldNodes = fields[responseName];
const fieldNode = fieldNodes[0];
const fieldDef = getFieldDef(schema, type, fieldNode.name.value);
invariant(
fieldDef,
'This subscription is not defined by the schema.'
);
// Call the `subscribe()` resolver or the default resolver to produce an
// AsyncIterable yielding raw payloads.
const resolveFn = fieldDef.subscribe || exeContext.fieldResolver;
const path = addPath(undefined, responseName);
const info = buildResolveInfo(
exeContext,
fieldDef,
fieldNodes,
type,
path
);
// resolveFieldValueOrError implements the "ResolveFieldEventStream"
// algorithm from GraphQL specification. It differs from
// "ResolveFieldValue" due to providing a different `resolveFn`.
const subscription = resolveFieldValueOrError(
exeContext,
fieldDef,
fieldNodes,
resolveFn,
rootValue,
info
);
// at this point, subscription is potentially a promise, which we need to
// chain with the wrapping promise. This code could be a lot cleaner with
// async/await.
Promise.resolve(subscription).then(resolvedSubscription => {
resolve({
subscription: resolvedSubscription,
fieldNodes,
path
});
}, reject);
}).then(({subscription, fieldNodes, path}) => {
// Throw located GraphQLError if subscription source fails to resolve.
if (subscription instanceof Error) {
throw locatedError(
subscription,
fieldNodes,
responsePathAsArray(path),
);
}
if (!isAsyncIterable(subscription)) {
throw new Error(
'Subscription must return Async Iterable. ' +
'Received: ' + String(subscription)
);
}
return (subscription: any);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment