Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .c8rc.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"src/jsutils/ObjMap.ts",
"src/jsutils/PromiseOrValue.ts",
"src/utilities/assertValidName.ts",
"src/utilities/typedQueryDocumentNode.ts"
"src/utilities/typedQueryDocumentNode.ts",
"src/**/__tests__/**/*.ts"
],
"clean": true,
"temp-directory": "coverage",
Expand Down
115 changes: 113 additions & 2 deletions src/execution/__tests__/union-interface-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { GraphQLBoolean, GraphQLString } from '../../type/scalars';
import { GraphQLSchema } from '../../type/schema';

import { executeSync } from '../execute';
import { execute, executeSync } from '../execute';

class Dog {
name: string;
Expand Down Expand Up @@ -118,7 +118,6 @@ const PetType = new GraphQLUnionType({
if (value instanceof Cat) {
return CatType.name;
}
/* c8 ignore next 3 */
// Not reachable, all possible types have been considered.
expect.fail('Not reachable');
},
Expand Down Expand Up @@ -154,6 +153,71 @@ odie.mother.progeny = [odie];
const liz = new Person('Liz');
const john = new Person('John', [garfield, odie], [liz, odie]);

const SearchableInterface = new GraphQLInterfaceType({
name: 'Searchable',
fields: {
id: { type: GraphQLString },
},
});

const TypeA = new GraphQLObjectType({
name: 'TypeA',
interfaces: [SearchableInterface],
fields: () => ({
id: { type: GraphQLString },
nameA: { type: GraphQLString },
}),
isTypeOf: (_value, _context, _info) =>
new Promise((_resolve, reject) =>
// eslint-disable-next-line
setTimeout(() => reject(new Error('TypeA_isTypeOf_rejected')), 10),
),
});

const TypeB = new GraphQLObjectType({
name: 'TypeB',
interfaces: [SearchableInterface],
fields: () => ({
id: { type: GraphQLString },
nameB: { type: GraphQLString },
}),
isTypeOf: (value: any, _context, _info) => value.id === 'b',
});

const queryTypeWithSearchable = new GraphQLObjectType({
name: 'Query',
fields: {
person: {
type: PersonType,
resolve: () => john,
},
search: {
type: SearchableInterface,
args: { id: { type: GraphQLString } },
resolve: (_source, { id }) => {
if (id === 'a') {
return { id: 'a', nameA: 'Object A' };
} else if (id === 'b') {
return { id: 'b', nameB: 'Object B' };
}
},
},
},
});

const schemaWithSearchable = new GraphQLSchema({
query: queryTypeWithSearchable,
types: [
PetType,
TypeA,
TypeB,
SearchableInterface,
PersonType,
DogType,
CatType,
],
});

describe('Execute: Union and intersection types', () => {
it('can introspect on union and intersection types', () => {
const document = parse(`
Expand Down Expand Up @@ -545,4 +609,51 @@ describe('Execute: Union and intersection types', () => {
expect(encounteredRootValue).to.equal(rootValue);
expect(encounteredContext).to.equal(contextValue);
});

it('handles promises from isTypeOf correctly when a later type matches synchronously', async () => {
const document = parse(`
query TestSearch {
search(id: "b") {
__typename
id
... on TypeA {
nameA
}
... on TypeB {
nameB
}
}
}
`);

let unhandledRejection: any = null;
const unhandledRejectionListener = (reason: any) => {
unhandledRejection = reason;
};
// eslint-disable-next-line
process.on('unhandledRejection', unhandledRejectionListener);

const result = await execute({
schema: schemaWithSearchable,
document,
});

expect(result.errors).to.equal(undefined);
expect(result.data).to.deep.equal({
search: {
__typename: 'TypeB',
id: 'b',
nameB: 'Object B',
},
});

// Give the TypeA promise a chance to reject and the listener to fire
// eslint-disable-next-line
await new Promise((resolve) => setTimeout(resolve, 20));

// eslint-disable-next-line
process.removeListener('unhandledRejection', unhandledRejectionListener);

expect(unhandledRejection).to.equal(null);
});
});
8 changes: 8 additions & 0 deletions src/execution/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,14 @@ export const defaultTypeResolver: GraphQLTypeResolver<unknown, unknown> =
if (isPromise(isTypeOfResult)) {
promisedIsTypeOfResults[i] = isTypeOfResult;
} else if (isTypeOfResult) {
if (promisedIsTypeOfResults.length) {
// Explicitly ignore any promise rejections
Promise.allSettled(promisedIsTypeOfResults)
/* c8 ignore next 3 */
.catch(() => {
// Do nothing
});
}
return type.name;
}
}
Expand Down
3 changes: 1 addition & 2 deletions website/pages/api-v16/error.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ The `graphql/error` module is responsible for creating and formatting
GraphQL errors. You can import either from the `graphql/error` module, or from the root `graphql` module. For example:

```js
import { GraphQLError } from 'graphql'; // ES6
const { GraphQLError } = require('graphql'); // CommonJS
import { GraphQLError } from 'graphql';
```

## Overview
Expand Down
3 changes: 1 addition & 2 deletions website/pages/api-v16/execution.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ The `graphql/execution` module is responsible for the execution phase of
fulfilling a GraphQL request. You can import either from the `graphql/execution` module, or from the root `graphql` module. For example:

```js
import { execute } from 'graphql'; // ES6
const { execute } = require('graphql'); // CommonJS
import { execute } from 'graphql';
```

## Overview
Expand Down
3 changes: 1 addition & 2 deletions website/pages/api-v16/graphql-http.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ The [official `graphql-http` package](https://github.com/graphql/graphql-http) p
## Express

```js
import { createHandler } from 'graphql-http/lib/use/express'; // ES6
const { createHandler } = require('graphql-http/lib/use/express'); // CommonJS
import { createHandler } from 'graphql-http/lib/use/express';
```

### createHandler
Expand Down
3 changes: 1 addition & 2 deletions website/pages/api-v16/graphql.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ The `graphql` module exports a core subset of GraphQL functionality for creation
of GraphQL type systems and servers.

```js
import { graphql } from 'graphql'; // ES6
const { graphql } = require('graphql'); // CommonJS
import { graphql } from 'graphql';
```

## Overview
Expand Down
3 changes: 1 addition & 2 deletions website/pages/api-v16/language.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ title: graphql/language
The `graphql/language` module is responsible for parsing and operating on the GraphQL language. You can import either from the `graphql/language` module, or from the root `graphql` module. For example:

```js
import { Source } from 'graphql'; // ES6
const { Source } = require('graphql'); // CommonJS
import { Source } from 'graphql';
```

## Overview
Expand Down
3 changes: 1 addition & 2 deletions website/pages/api-v16/type.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ title: graphql/type
The `graphql/type` module is responsible for defining GraphQL types and schema. You can import either from the `graphql/type` module, or from the root `graphql` module. For example:

```js
import { GraphQLSchema } from 'graphql'; // ES6
const { GraphQLSchema } = require('graphql'); // CommonJS
import { GraphQLSchema } from 'graphql';
```

## Overview
Expand Down
3 changes: 1 addition & 2 deletions website/pages/api-v16/utilities.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ The `graphql/utilities` module contains common useful computations to use with
the GraphQL language and type objects. You can import either from the `graphql/utilities` module, or from the root `graphql` module. For example:

```js
import { introspectionQuery } from 'graphql'; // ES6
const { introspectionQuery } = require('graphql'); // CommonJS
import { introspectionQuery } from 'graphql';
```

## Overview
Expand Down
3 changes: 1 addition & 2 deletions website/pages/api-v16/validation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ The `graphql/validation` module fulfills the Validation phase of fulfilling a
GraphQL result. You can import either from the `graphql/validation` module, or from the root `graphql` module. For example:

```js
import { validate } from 'graphql/validation'; // ES6
const { validate } = require('graphql/validation'); // CommonJS
import { validate } from 'graphql/validation';
```

## Overview
Expand Down
8 changes: 4 additions & 4 deletions website/pages/docs/abstract-types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ concrete type a given value corresponds to.
The following example defines a `ContentItem` interface for a publishing platform:

```js
const { GraphQLInterfaceType, GraphQLString, GraphQLNonNull } = require('graphql');
import { GraphQLInterfaceType, GraphQLString, GraphQLNonNull } from 'graphql';

const ContentItemInterface = new GraphQLInterfaceType({
name: 'ContentItem',
Expand Down Expand Up @@ -69,7 +69,7 @@ The following example implements the `Article` and `PodcastEpisode` types that
conform to the `ContentItem` interface:

```js
const { GraphQLObjectType, GraphQLString, GraphQLNonNull } = require('graphql');
import { GraphQLObjectType, GraphQLString, GraphQLNonNull } from 'graphql';

const ArticleType = new GraphQLObjectType({
name: 'Article',
Expand Down Expand Up @@ -114,7 +114,7 @@ A union requires:
The following example defines a `SearchResult` union:

```js
const { GraphQLUnionType } = require('graphql');
import { GraphQLUnionType } from 'graphql';

const SearchResultType = new GraphQLUnionType({
name: 'SearchResult',
Expand All @@ -134,7 +134,7 @@ const SearchResultType = new GraphQLUnionType({
});
```

Unlike interfaces, unions dont declare any fields of their own. Clients use inline fragments
Unlike interfaces, unions don't declare any fields of their own. Clients use inline fragments
to query fields from the concrete types.

## Resolving abstract types at runtime
Expand Down
10 changes: 5 additions & 5 deletions website/pages/docs/advanced-custom-scalars.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ schema, follow these best practices.

### Document expected formats and validation

Provide a clear description of the scalars accepted input and output formats. For example, a
Provide a clear description of the scalar's accepted input and output formats. For example, a
`DateTime` scalar should explain that it expects [ISO-8601](https://www.iso.org/iso-8601-date-and-time-format.html) strings ending with `Z`.

Clear descriptions help clients understand valid input and reduce mistakes.
Expand Down Expand Up @@ -100,8 +100,8 @@ describe('DateTime scalar', () => {
Integrate the scalar into a schema and run real GraphQL queries to validate end-to-end behavior.

```js
const { graphql, GraphQLSchema, GraphQLObjectType } = require('graphql');
const { DateTimeResolver as DateTime } = require('graphql-scalars');
import { graphql, GraphQLSchema, GraphQLObjectType } from 'graphql';
import { DateTimeResolver as DateTime } from 'graphql-scalars';

const Query = new GraphQLObjectType({
name: 'Query',
Expand Down Expand Up @@ -184,13 +184,13 @@ scalars for DateTime, EmailAddress, URL, UUID, and many others.
### Example: Handling email validation

Handling email validation correctly requires dealing with Unicode, quoted local parts, and
domain validation. Rather than writing your own regex, its better to use a library scalar
domain validation. Rather than writing your own regex, it's better to use a library scalar
that's already validated against standards.

If you need domain-specific behavior, you can wrap an existing scalar with custom rules:

```js
const { EmailAddressResolver } = require('graphql-scalars');
import { EmailAddressResolver } from 'graphql-scalars';

const StrictEmailAddress = new GraphQLScalarType({
...EmailAddressResolver,
Expand Down
16 changes: 8 additions & 8 deletions website/pages/docs/authentication-and-express-middleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ For example, let's say we wanted our server to log the IP address of every reque
<Tabs items={['SDL', 'Code']}>
<Tabs.Tab>
```js
const express = require('express');
const { createHandler } = require('graphql-http/lib/use/express');
const { buildSchema } = require('graphql');
import express from 'express';
import { createHandler } from 'graphql-http/lib/use/express';
import { buildSchema } from 'graphql';

const schema = buildSchema(`type Query { ip: String }`);

Expand Down Expand Up @@ -46,17 +46,17 @@ app.all(
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');

````
```
</Tabs.Tab>
<Tabs.Tab>
```js
const express = require('express');
const { createHandler } = require('graphql-http/lib/use/express');
const {
import express from 'express';
import { createHandler } from 'graphql-http/lib/use/express';
import {
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
} = require('graphql');
} from 'graphql';

const schema = new GraphQLSchema({
query: new GraphQLObjectType({
Expand Down
Loading
Loading