Skip to content

Commit

Permalink
introspection should match the ExecutorSchema (#170)
Browse files Browse the repository at this point in the history
...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.
  • Loading branch information
yaacovCR authored Mar 23, 2022
1 parent 223e19d commit 00f54c0
Show file tree
Hide file tree
Showing 8 changed files with 2,365 additions and 86 deletions.
7 changes: 7 additions & 0 deletions .changeset/thirty-lizards-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'graphql-executor': patch
---

introspection should track the ExecutorSchema rather than the GraphQLSchema

...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.
6 changes: 6 additions & 0 deletions src/__testUtils__/handlePre16.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { versionInfo } from 'graphql';

export function handlePre16<T>(postV16: T | undefined, preV16: T | undefined) {
/* c8 ignore next */
return versionInfo.major >= 16 ? postV16 : preV16;
}
4 changes: 2 additions & 2 deletions src/__tests__/starWarsIntrospection-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ describe('Star Wars Introspection Tests', () => {
],
[
{ name: 'Query' },
{ name: 'Episode' },
{ name: 'Character' },
{ name: 'String' },
{ name: 'Episode' },
{ name: 'Human' },
{ name: 'Droid' },
{ name: 'Boolean' },
{ name: '__Schema' },
{ name: '__Type' },
{ name: '__TypeKind' },
{ name: 'Boolean' },
{ name: '__Field' },
{ name: '__InputValue' },
{ name: '__EnumValue' },
Expand Down
55 changes: 37 additions & 18 deletions src/execution/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,10 @@ import {
GraphQLSkipDirective,
GraphQLError,
Kind,
SchemaMetaFieldDef,
TypeMetaFieldDef,
TypeNameMetaFieldDef,
locatedError,
} from 'graphql';

import {
GraphQLDeferDirective,
GraphQLStreamDirective,
} from '../type/directives';

import type { Path } from '../jsutils/Path';
import type { ObjMap } from '../jsutils/ObjMap';
import type { PromiseOrValue } from '../jsutils/PromiseOrValue';
Expand All @@ -54,6 +47,16 @@ import { isIterableObject } from '../jsutils/isIterableObject';
import { resolveAfterAll } from '../jsutils/resolveAfterAll';
import { toError } from '../jsutils/toError';

import {
GraphQLDeferDirective,
GraphQLStreamDirective,
} from '../type/directives';
import {
SchemaMetaFieldDef,
TypeMetaFieldDef,
DirectiveMetaFieldDef,
} from '../type/introspection';

import type { ExecutorSchema } from './executorSchema';
import { toExecutorSchema } from './toExecutorSchema';
import {
Expand Down Expand Up @@ -2336,31 +2339,47 @@ export class Executor {
* Returns: the field definition and a class for constructing the info
* argument for field resolvers.
*/
_getFieldContext(
_getFieldDef(
fieldName: string,
parentType: GraphQLObjectType,
fieldNodes: ReadonlyArray<FieldNode>,
): Maybe<FieldContext> {
const initialFieldNode = fieldNodes[0];
const fieldName = initialFieldNode.name.value;
): Maybe<GraphQLField<unknown, unknown>> {
const fieldDef = parentType.getFields()[fieldName];

if (fieldDef) {
return fieldDef;
}

let fieldDef: GraphQLField<unknown, unknown>;
if (
fieldName === SchemaMetaFieldDef.name &&
this._executorSchema.getRootType('query' as OperationTypeNode) ===
parentType
) {
fieldDef = SchemaMetaFieldDef;
return SchemaMetaFieldDef;
} else if (
fieldName === TypeMetaFieldDef.name &&
this._executorSchema.getRootType('query' as OperationTypeNode) ===
parentType
) {
fieldDef = TypeMetaFieldDef;
return TypeMetaFieldDef;
} else if (
fieldName === DirectiveMetaFieldDef.name &&
this._executorSchema.getRootType('query' as OperationTypeNode) ===
parentType
) {
return DirectiveMetaFieldDef;
} else if (fieldName === TypeNameMetaFieldDef.name) {
fieldDef = TypeNameMetaFieldDef;
} else {
fieldDef = parentType.getFields()[fieldName];
return TypeNameMetaFieldDef;
}
}

_getFieldContext(
parentType: GraphQLObjectType,
fieldNodes: ReadonlyArray<FieldNode>,
): Maybe<FieldContext> {
const initialFieldNode = fieldNodes[0];
const fieldName = initialFieldNode.name.value;

const fieldDef = this._getFieldDef(fieldName, parentType);

if (!fieldDef) {
return;
Expand Down
15 changes: 14 additions & 1 deletion src/execution/executorSchema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type {
GraphQLAbstractType,
GraphQLDirective,
GraphQLEnumType,
GraphQLInterfaceType,
GraphQLInputObjectType,
GraphQLObjectType,
Expand All @@ -10,12 +12,15 @@ import type {
GraphQLType,
GraphQLNullableType,
GraphQLOutputType,
GraphQLScalarType,
GraphQLList,
GraphQLNonNull,
OperationTypeNode,
TypeNode,
} from 'graphql';

import type { Maybe } from '../jsutils/Maybe';

export type GraphQLNullableInputType =
| GraphQLLeafType
| GraphQLInputObjectType
Expand All @@ -29,6 +34,7 @@ export type GraphQLNullableOutputType =
| GraphQLList<GraphQLOutputType>;

export interface ExecutorSchema {
description: Maybe<string>;
isListType: ((
type: GraphQLInputType,
) => type is GraphQLList<GraphQLInputType>) &
Expand All @@ -44,10 +50,17 @@ export interface ExecutorSchema {
isNamedType: (type: unknown) => type is GraphQLNamedType;
isInputType: (type: unknown) => type is GraphQLInputType;
isLeafType: (type: unknown) => type is GraphQLLeafType;
isScalarType: (type: unknown) => type is GraphQLScalarType;
isEnumType: (type: unknown) => type is GraphQLEnumType;
isAbstractType: (type: unknown) => type is GraphQLAbstractType;
isInterfaceType: (type: unknown) => type is GraphQLInterfaceType;
isUnionType: (type: unknown) => type is GraphQLUnionType;
isObjectType: (type: unknown) => type is GraphQLObjectType;
isInputObjectType: (type: unknown) => type is GraphQLInputObjectType;
getNamedType: (type: string) => GraphQLNamedType | undefined;
getDirectives: () => ReadonlyArray<GraphQLDirective>;
getDirective: (directiveName: string) => GraphQLDirective | undefined;
getNamedTypes: () => ReadonlyArray<GraphQLNamedType>;
getNamedType: (typeName: string) => GraphQLNamedType | undefined;
getType: (typeNode: TypeNode) => GraphQLType | undefined;
getRootType: (operation: OperationTypeNode) => GraphQLObjectType | undefined;
getPossibleTypes: (
Expand Down
Loading

0 comments on commit 00f54c0

Please sign in to comment.