Skip to content

Commit 00f54c0

Browse files
authored
introspection should match the ExecutorSchema (#170)
...rather than the GraphQLSchema. Relevant only if an explicit ExecutorSchema is passed, otherwise the ExecutorSchema is generated from the GraphQLSchema. This also adds a new `__directive(name: "someDirective")` introspection root field to request info about a single named directive, parallel to the `__type(name: "someType")` introspection root field. This currently does not pass validation and is used only to facilitate testing.
1 parent 223e19d commit 00f54c0

File tree

8 files changed

+2365
-86
lines changed

8 files changed

+2365
-86
lines changed

.changeset/thirty-lizards-unite.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'graphql-executor': patch
3+
---
4+
5+
introspection should track the ExecutorSchema rather than the GraphQLSchema
6+
7+
...in case of any discrepancy. When an explicit ExecutorSchema is passed, the GraphQLSchema should essentially be ignored, required in essence only to satisfy TS typings. If an explicit ExecutorSchema is not passed, it is generated from the GraphQLSchema, and so there would be no discrepancy.

src/__testUtils__/handlePre16.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { versionInfo } from 'graphql';
2+
3+
export function handlePre16<T>(postV16: T | undefined, preV16: T | undefined) {
4+
/* c8 ignore next */
5+
return versionInfo.major >= 16 ? postV16 : preV16;
6+
}

src/__tests__/starWarsIntrospection-test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ describe('Star Wars Introspection Tests', () => {
5151
],
5252
[
5353
{ name: 'Query' },
54-
{ name: 'Episode' },
5554
{ name: 'Character' },
5655
{ name: 'String' },
56+
{ name: 'Episode' },
5757
{ name: 'Human' },
5858
{ name: 'Droid' },
59+
{ name: 'Boolean' },
5960
{ name: '__Schema' },
6061
{ name: '__Type' },
6162
{ name: '__TypeKind' },
62-
{ name: 'Boolean' },
6363
{ name: '__Field' },
6464
{ name: '__InputValue' },
6565
{ name: '__EnumValue' },

src/execution/executor.ts

+37-18
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,10 @@ import {
2424
GraphQLSkipDirective,
2525
GraphQLError,
2626
Kind,
27-
SchemaMetaFieldDef,
28-
TypeMetaFieldDef,
2927
TypeNameMetaFieldDef,
3028
locatedError,
3129
} from 'graphql';
3230

33-
import {
34-
GraphQLDeferDirective,
35-
GraphQLStreamDirective,
36-
} from '../type/directives';
37-
3831
import type { Path } from '../jsutils/Path';
3932
import type { ObjMap } from '../jsutils/ObjMap';
4033
import type { PromiseOrValue } from '../jsutils/PromiseOrValue';
@@ -54,6 +47,16 @@ import { isIterableObject } from '../jsutils/isIterableObject';
5447
import { resolveAfterAll } from '../jsutils/resolveAfterAll';
5548
import { toError } from '../jsutils/toError';
5649

50+
import {
51+
GraphQLDeferDirective,
52+
GraphQLStreamDirective,
53+
} from '../type/directives';
54+
import {
55+
SchemaMetaFieldDef,
56+
TypeMetaFieldDef,
57+
DirectiveMetaFieldDef,
58+
} from '../type/introspection';
59+
5760
import type { ExecutorSchema } from './executorSchema';
5861
import { toExecutorSchema } from './toExecutorSchema';
5962
import {
@@ -2336,31 +2339,47 @@ export class Executor {
23362339
* Returns: the field definition and a class for constructing the info
23372340
* argument for field resolvers.
23382341
*/
2339-
_getFieldContext(
2342+
_getFieldDef(
2343+
fieldName: string,
23402344
parentType: GraphQLObjectType,
2341-
fieldNodes: ReadonlyArray<FieldNode>,
2342-
): Maybe<FieldContext> {
2343-
const initialFieldNode = fieldNodes[0];
2344-
const fieldName = initialFieldNode.name.value;
2345+
): Maybe<GraphQLField<unknown, unknown>> {
2346+
const fieldDef = parentType.getFields()[fieldName];
2347+
2348+
if (fieldDef) {
2349+
return fieldDef;
2350+
}
23452351

2346-
let fieldDef: GraphQLField<unknown, unknown>;
23472352
if (
23482353
fieldName === SchemaMetaFieldDef.name &&
23492354
this._executorSchema.getRootType('query' as OperationTypeNode) ===
23502355
parentType
23512356
) {
2352-
fieldDef = SchemaMetaFieldDef;
2357+
return SchemaMetaFieldDef;
23532358
} else if (
23542359
fieldName === TypeMetaFieldDef.name &&
23552360
this._executorSchema.getRootType('query' as OperationTypeNode) ===
23562361
parentType
23572362
) {
2358-
fieldDef = TypeMetaFieldDef;
2363+
return TypeMetaFieldDef;
2364+
} else if (
2365+
fieldName === DirectiveMetaFieldDef.name &&
2366+
this._executorSchema.getRootType('query' as OperationTypeNode) ===
2367+
parentType
2368+
) {
2369+
return DirectiveMetaFieldDef;
23592370
} else if (fieldName === TypeNameMetaFieldDef.name) {
2360-
fieldDef = TypeNameMetaFieldDef;
2361-
} else {
2362-
fieldDef = parentType.getFields()[fieldName];
2371+
return TypeNameMetaFieldDef;
23632372
}
2373+
}
2374+
2375+
_getFieldContext(
2376+
parentType: GraphQLObjectType,
2377+
fieldNodes: ReadonlyArray<FieldNode>,
2378+
): Maybe<FieldContext> {
2379+
const initialFieldNode = fieldNodes[0];
2380+
const fieldName = initialFieldNode.name.value;
2381+
2382+
const fieldDef = this._getFieldDef(fieldName, parentType);
23642383

23652384
if (!fieldDef) {
23662385
return;

src/execution/executorSchema.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type {
22
GraphQLAbstractType,
3+
GraphQLDirective,
4+
GraphQLEnumType,
35
GraphQLInterfaceType,
46
GraphQLInputObjectType,
57
GraphQLObjectType,
@@ -10,12 +12,15 @@ import type {
1012
GraphQLType,
1113
GraphQLNullableType,
1214
GraphQLOutputType,
15+
GraphQLScalarType,
1316
GraphQLList,
1417
GraphQLNonNull,
1518
OperationTypeNode,
1619
TypeNode,
1720
} from 'graphql';
1821

22+
import type { Maybe } from '../jsutils/Maybe';
23+
1924
export type GraphQLNullableInputType =
2025
| GraphQLLeafType
2126
| GraphQLInputObjectType
@@ -29,6 +34,7 @@ export type GraphQLNullableOutputType =
2934
| GraphQLList<GraphQLOutputType>;
3035

3136
export interface ExecutorSchema {
37+
description: Maybe<string>;
3238
isListType: ((
3339
type: GraphQLInputType,
3440
) => type is GraphQLList<GraphQLInputType>) &
@@ -44,10 +50,17 @@ export interface ExecutorSchema {
4450
isNamedType: (type: unknown) => type is GraphQLNamedType;
4551
isInputType: (type: unknown) => type is GraphQLInputType;
4652
isLeafType: (type: unknown) => type is GraphQLLeafType;
53+
isScalarType: (type: unknown) => type is GraphQLScalarType;
54+
isEnumType: (type: unknown) => type is GraphQLEnumType;
4755
isAbstractType: (type: unknown) => type is GraphQLAbstractType;
56+
isInterfaceType: (type: unknown) => type is GraphQLInterfaceType;
57+
isUnionType: (type: unknown) => type is GraphQLUnionType;
4858
isObjectType: (type: unknown) => type is GraphQLObjectType;
4959
isInputObjectType: (type: unknown) => type is GraphQLInputObjectType;
50-
getNamedType: (type: string) => GraphQLNamedType | undefined;
60+
getDirectives: () => ReadonlyArray<GraphQLDirective>;
61+
getDirective: (directiveName: string) => GraphQLDirective | undefined;
62+
getNamedTypes: () => ReadonlyArray<GraphQLNamedType>;
63+
getNamedType: (typeName: string) => GraphQLNamedType | undefined;
5164
getType: (typeNode: TypeNode) => GraphQLType | undefined;
5265
getRootType: (operation: OperationTypeNode) => GraphQLObjectType | undefined;
5366
getPossibleTypes: (

0 commit comments

Comments
 (0)