Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove support for positional args in graphql/execute/subscribe func #2904

Merged
merged 1 commit into from Feb 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 1 addition & 23 deletions src/__tests__/starWarsQuery-test.js
@@ -1,7 +1,7 @@
import { expect } from 'chai';
import { describe, it } from 'mocha';

import { graphql, graphqlSync } from '../graphql';
import { graphql } from '../graphql';

import { StarWarsSchema as schema } from './starWarsSchema';

Expand All @@ -26,28 +26,6 @@ describe('Star Wars Query Tests', () => {
});
});

it('Accepts positional arguments to graphql()', async () => {
const source = `
query HeroNameQuery {
hero {
name
}
}
`;

const result = await graphql(schema, source);
expect(result).to.deep.equal({
data: {
hero: {
name: 'R2-D2',
},
},
});

const syncResult = graphqlSync(schema, source);
expect(syncResult).to.deep.equal(result);
});

it('Allows us to query for the ID and friends of R2-D2', async () => {
const source = `
query HeroNameAndFriendsQuery {
Expand Down
24 changes: 0 additions & 24 deletions src/execution/__tests__/executor-test.js
Expand Up @@ -69,30 +69,6 @@ describe('Execute: Handles basic execution tasks', () => {
);
});

it('accepts positional arguments', () => {
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Type',
fields: {
a: {
type: GraphQLString,
resolve(rootValue) {
return rootValue;
},
},
},
}),
});

const document = parse('{ a }');
const rootValue = 'rootValue';
const result = execute(schema, document, rootValue);

expect(result).to.deep.equal({
data: { a: 'rootValue' },
});
});

it('executes arbitrary code', async () => {
const data = {
a: () => 'Apple',
Expand Down
77 changes: 17 additions & 60 deletions src/execution/execute.js
Expand Up @@ -146,67 +146,8 @@ export type ExecutionArgs = {|
*
* If the arguments to this function do not result in a legal execution context,
* a GraphQLError will be thrown immediately explaining the invalid input.
*
* Accepts either an object with named arguments, or individual arguments.
*/
declare function execute(
ExecutionArgs,
..._: []
): PromiseOrValue<ExecutionResult>;
/* eslint-disable no-redeclare */
declare function execute(
schema: GraphQLSchema,
document: DocumentNode,
rootValue?: mixed,
contextValue?: mixed,
variableValues?: ?{ +[variable: string]: mixed, ... },
operationName?: ?string,
fieldResolver?: ?GraphQLFieldResolver<any, any>,
typeResolver?: ?GraphQLTypeResolver<any, any>,
): PromiseOrValue<ExecutionResult>;
export function execute(
argsOrSchema,
document,
rootValue,
contextValue,
variableValues,
operationName,
fieldResolver,
typeResolver,
) {
/* eslint-enable no-redeclare */
// Extract arguments from object args if provided.
return arguments.length === 1
? executeImpl(argsOrSchema)
: executeImpl({
schema: argsOrSchema,
document,
rootValue,
contextValue,
variableValues,
operationName,
fieldResolver,
typeResolver,
});
}

/**
* Also implements the "Evaluating requests" section of the GraphQL specification.
* However, it guarantees to complete synchronously (or throw an error) assuming
* that all field resolvers are also synchronous.
*/
export function executeSync(args: ExecutionArgs): ExecutionResult {
const result = executeImpl(args);

// Assert that the execution was synchronous.
if (isPromise(result)) {
throw new Error('GraphQL execution failed to complete synchronously.');
}

return result;
}

function executeImpl(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
export function execute(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
const {
schema,
document,
Expand Down Expand Up @@ -250,6 +191,22 @@ function executeImpl(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
return buildResponse(exeContext, data);
}

/**
* Also implements the "Evaluating requests" section of the GraphQL specification.
* However, it guarantees to complete synchronously (or throw an error) assuming
* that all field resolvers are also synchronous.
*/
export function executeSync(args: ExecutionArgs): ExecutionResult {
const result = execute(args);

// Assert that the execution was synchronous.
if (isPromise(result)) {
throw new Error('GraphQL execution failed to complete synchronously.');
}

return result;
}

/**
* Given a completed execution context and data, build the { errors, data }
* response defined by the "Response" section of the GraphQL specification.
Expand Down
82 changes: 5 additions & 77 deletions src/graphql.js
Expand Up @@ -65,47 +65,10 @@ export type GraphQLArgs = {|
fieldResolver?: ?GraphQLFieldResolver<any, any>,
typeResolver?: ?GraphQLTypeResolver<any, any>,
|};
declare function graphql(GraphQLArgs, ..._: []): Promise<ExecutionResult>;
/* eslint-disable no-redeclare */
declare function graphql(
schema: GraphQLSchema,
source: Source | string,
rootValue?: mixed,
contextValue?: mixed,
variableValues?: ?{ +[variable: string]: mixed, ... },
operationName?: ?string,
fieldResolver?: ?GraphQLFieldResolver<any, any>,
typeResolver?: ?GraphQLTypeResolver<any, any>,
): Promise<ExecutionResult>;
export function graphql(
argsOrSchema,
source,
rootValue,
contextValue,
variableValues,
operationName,
fieldResolver,
typeResolver,
) {
/* eslint-enable no-redeclare */

export function graphql(args: GraphQLArgs): Promise<ExecutionResult> {
// Always return a Promise for a consistent API.
return new Promise((resolve) =>
resolve(
// Extract arguments from object args if provided.
arguments.length === 1
? graphqlImpl(argsOrSchema)
: graphqlImpl({
schema: argsOrSchema,
source,
rootValue,
contextValue,
variableValues,
operationName,
fieldResolver,
typeResolver,
}),
),
);
return new Promise((resolve) => resolve(graphqlImpl(args)));
}

/**
Expand All @@ -114,43 +77,8 @@ export function graphql(
* However, it guarantees to complete synchronously (or throw an error) assuming
* that all field resolvers are also synchronous.
*/
declare function graphqlSync(GraphQLArgs, ..._: []): ExecutionResult;
/* eslint-disable no-redeclare */
declare function graphqlSync(
schema: GraphQLSchema,
source: Source | string,
rootValue?: mixed,
contextValue?: mixed,
variableValues?: ?{ +[variable: string]: mixed, ... },
operationName?: ?string,
fieldResolver?: ?GraphQLFieldResolver<any, any>,
typeResolver?: ?GraphQLTypeResolver<any, any>,
): ExecutionResult;
export function graphqlSync(
argsOrSchema,
source,
rootValue,
contextValue,
variableValues,
operationName,
fieldResolver,
typeResolver,
) {
/* eslint-enable no-redeclare */
// Extract arguments from object args if provided.
const result =
arguments.length === 1
? graphqlImpl(argsOrSchema)
: graphqlImpl({
schema: argsOrSchema,
source,
rootValue,
contextValue,
variableValues,
operationName,
fieldResolver,
typeResolver,
});
export function graphqlSync(args: GraphQLArgs): ExecutionResult {
const result = graphqlImpl(args);

// Assert that the execution was synchronous.
if (isPromise(result)) {
Expand Down
26 changes: 3 additions & 23 deletions src/subscription/__tests__/subscribe-test.js
Expand Up @@ -152,26 +152,6 @@ async function expectPromiseToThrow(

// Check all error cases when initializing the subscription.
describe('Subscription Initialization Phase', () => {
it('accepts positional arguments', async () => {
const document = parse(`
subscription {
importantEmail
}
`);

async function* emptyAsyncIterator() {
// Empty
}

// $FlowFixMe[incompatible-call]
const ai = await subscribe(emailSchema, document, {
importantEmail: emptyAsyncIterator,
});

ai.next();
ai.return();
});

it('accepts multiple subscription fields defined in schema', async () => {
const pubsub = new SimplePubSub();
const SubscriptionTypeMultiple = new GraphQLObjectType({
Expand Down Expand Up @@ -328,7 +308,7 @@ describe('Subscription Initialization Phase', () => {
);

await expectPromiseToThrow(
// $FlowExpectedError[incompatible-call]
// $FlowExpectedError[prop-missing]
() => subscribe({ document }),
'Expected undefined to be a GraphQL schema.',
);
Expand All @@ -342,7 +322,7 @@ describe('Subscription Initialization Phase', () => {
);

await expectPromiseToThrow(
// $FlowExpectedError[incompatible-call]
// $FlowExpectedError[prop-missing]
() => subscribe({ schema: emailSchema }),
'Must provide document.',
);
Expand Down Expand Up @@ -371,7 +351,7 @@ describe('Subscription Initialization Phase', () => {
it('should pass through unexpected errors thrown in subscribe', async () => {
let expectedError;
try {
// $FlowExpectedError[incompatible-call]
// $FlowExpectedError[prop-missing]
await subscribe({ schema: emailSchema, document: {} });
} catch (error) {
expectedError = error;
Expand Down
65 changes: 12 additions & 53 deletions src/subscription/subscribe.js
Expand Up @@ -57,60 +57,7 @@ export type SubscriptionArgs = {|
*
* Accepts either an object with named arguments, or individual arguments.
*/
declare function subscribe(
SubscriptionArgs,
..._: []
): Promise<AsyncGenerator<ExecutionResult, void, void> | ExecutionResult>;
/* eslint-disable no-redeclare */
declare function subscribe(
schema: GraphQLSchema,
document: DocumentNode,
rootValue?: mixed,
contextValue?: mixed,
variableValues?: ?{ +[variable: string]: mixed, ... },
operationName?: ?string,
fieldResolver?: ?GraphQLFieldResolver<any, any>,
subscribeFieldResolver?: ?GraphQLFieldResolver<any, any>,
): Promise<AsyncIterator<ExecutionResult> | ExecutionResult>;
export function subscribe(
argsOrSchema,
document,
rootValue,
contextValue,
variableValues,
operationName,
fieldResolver,
subscribeFieldResolver,
) {
/* eslint-enable no-redeclare */
// Extract arguments from object args if provided.
return arguments.length === 1
? subscribeImpl(argsOrSchema)
: subscribeImpl({
schema: argsOrSchema,
document,
rootValue,
contextValue,
variableValues,
operationName,
fieldResolver,
subscribeFieldResolver,
});
}

/**
* This function checks if the error is a GraphQLError. If it is, report it as
* an ExecutionResult, containing only errors and no data. Otherwise treat the
* error as a system-class error and re-throw it.
*/
function reportGraphQLError(error: mixed): ExecutionResult {
if (error instanceof GraphQLError) {
return { errors: [error] };
}
throw error;
}

function subscribeImpl(
args: SubscriptionArgs,
): Promise<AsyncGenerator<ExecutionResult, void, void> | ExecutionResult> {
const {
Expand Down Expand Up @@ -165,6 +112,18 @@ function subscribeImpl(
);
}

/**
* This function checks if the error is a GraphQLError. If it is, report it as
* an ExecutionResult, containing only errors and no data. Otherwise treat the
* error as a system-class error and re-throw it.
*/
function reportGraphQLError(error: mixed): ExecutionResult {
if (error instanceof GraphQLError) {
return { errors: [error] };
}
throw error;
}

/**
* Implements the "CreateSourceEventStream" algorithm described in the
* GraphQL specification, resolving the subscription source event stream.
Expand Down