diff --git a/src/execution/__tests__/nonnull-test.ts b/src/execution/__tests__/nonnull-test.ts index 6a321931b9..ac92de1a30 100644 --- a/src/execution/__tests__/nonnull-test.ts +++ b/src/execution/__tests__/nonnull-test.ts @@ -683,7 +683,7 @@ describe('Execute: handles non-nullable types', () => { errors: [ { message: - 'Argument "cannotBeNull" of required type "String!" was not provided.', + 'Argument "Query.withNonNullArg(cannotBeNull:)" of required type "String!" was not provided.', locations: [{ line: 3, column: 13 }], path: ['withNonNullArg'], }, @@ -710,7 +710,7 @@ describe('Execute: handles non-nullable types', () => { errors: [ { message: - 'Argument "cannotBeNull" has invalid value: Expected value of non-null type "String!" not to be null.', + 'Argument "Query.withNonNullArg(cannotBeNull:)" has invalid value: Expected value of non-null type "String!" not to be null.', locations: [{ line: 3, column: 42 }], path: ['withNonNullArg'], }, @@ -740,7 +740,7 @@ describe('Execute: handles non-nullable types', () => { errors: [ { message: - 'Argument "cannotBeNull" has invalid value: Expected variable "$testVar" provided to type "String!" to provide a runtime value.', + 'Argument "Query.withNonNullArg(cannotBeNull:)" has invalid value: Expected variable "$testVar" provided to type "String!" to provide a runtime value.', locations: [{ line: 3, column: 42 }], path: ['withNonNullArg'], }, @@ -768,7 +768,7 @@ describe('Execute: handles non-nullable types', () => { errors: [ { message: - 'Argument "cannotBeNull" has invalid value: Expected variable "$testVar" provided to non-null type "String!" not to be null.', + 'Argument "Query.withNonNullArg(cannotBeNull:)" has invalid value: Expected variable "$testVar" provided to non-null type "String!" not to be null.', locations: [{ line: 3, column: 43 }], path: ['withNonNullArg'], }, diff --git a/src/execution/__tests__/oneof-test.ts b/src/execution/__tests__/oneof-test.ts index eed65ae580..c010a21b72 100644 --- a/src/execution/__tests__/oneof-test.ts +++ b/src/execution/__tests__/oneof-test.ts @@ -88,7 +88,7 @@ describe('Execute: Handles OneOf Input Objects', () => { message: // This type of error would be caught at validation-time // hence the vague error message here. - 'Argument "input" has invalid value: Expected variable "$input" provided to type "TestInputObject!" to provide a runtime value.', + 'Argument "Query.test(input:)" has invalid value: Expected variable "$input" provided to type "TestInputObject!" to provide a runtime value.', path: ['test'], }, ], @@ -229,7 +229,7 @@ describe('Execute: Handles OneOf Input Objects', () => { // A nullable variable in a oneOf field position would be caught at validation-time // hence the vague error message here. message: - 'Argument "input" has invalid value: Expected variable "$a" provided to field "a" for OneOf Input Object type "TestInputObject" not to be null.', + 'Argument "Query.test(input:)" has invalid value: Expected variable "$a" provided to field "a" for OneOf Input Object type "TestInputObject" not to be null.', locations: [{ line: 3, column: 23 }], path: ['test'], }, @@ -257,7 +257,7 @@ describe('Execute: Handles OneOf Input Objects', () => { // A nullable variable in a oneOf field position would be caught at validation-time // hence the vague error message here. message: - 'Argument "input" has invalid value: Expected variable "$a" provided to field "a" for OneOf Input Object type "TestInputObject" to provide a runtime value.', + 'Argument "Query.test(input:)" has invalid value: Expected variable "$a" provided to field "a" for OneOf Input Object type "TestInputObject" to provide a runtime value.', locations: [{ line: 3, column: 23 }], path: ['test'], }, @@ -288,7 +288,7 @@ describe('Execute: Handles OneOf Input Objects', () => { // A nullable variable in a oneOf field position would be caught at validation-time // hence the vague error message here. message: - 'Argument "input" has invalid value: Expected variable "$a" provided to field "a" for OneOf Input Object type "TestInputObject" not to be null.', + 'Argument "Query.test(input:)" has invalid value: Expected variable "$a" provided to field "a" for OneOf Input Object type "TestInputObject" not to be null.', locations: [{ line: 6, column: 23 }], path: ['test'], }, @@ -319,7 +319,7 @@ describe('Execute: Handles OneOf Input Objects', () => { // A nullable variable in a oneOf field position would be caught at validation-time // hence the vague error message here. message: - 'Argument "input" has invalid value: Expected variable "$a" provided to field "a" for OneOf Input Object type "TestInputObject" to provide a runtime value.', + 'Argument "Query.test(input:)" has invalid value: Expected variable "$a" provided to field "a" for OneOf Input Object type "TestInputObject" to provide a runtime value.', locations: [{ line: 6, column: 23 }], path: ['test'], }, diff --git a/src/execution/__tests__/variables-test.ts b/src/execution/__tests__/variables-test.ts index ca729d0248..f6f1a0a0aa 100644 --- a/src/execution/__tests__/variables-test.ts +++ b/src/execution/__tests__/variables-test.ts @@ -284,7 +284,7 @@ describe('Execute: Handles inputs', () => { errors: [ { message: - 'Argument "input" has invalid value: Expected value of type "TestInputObject" to be an object, found: ["foo", "bar", "baz"].', + 'Argument "TestType.fieldWithObjectInput(input:)" has invalid value: Expected value of type "TestInputObject" to be an object, found: ["foo", "bar", "baz"].', path: ['fieldWithObjectInput'], locations: [{ line: 3, column: 41 }], }, @@ -320,7 +320,7 @@ describe('Execute: Handles inputs', () => { errors: [ { message: - 'Argument "input" has invalid value at .e: FaultyScalarErrorMessage', + 'Argument "TestType.fieldWithObjectInput(input:)" has invalid value at .e: FaultyScalarErrorMessage', path: ['fieldWithObjectInput'], locations: [{ line: 3, column: 13 }], extensions: { code: 'FaultyScalarErrorExtensionCode' }, @@ -477,7 +477,7 @@ describe('Execute: Handles inputs', () => { errors: [ { message: - 'Variable "$input" has invalid value at .e: Argument "input" has invalid value at .e: FaultyScalarErrorMessage', + 'Variable "$input" has invalid value at .e: Argument "TestType.fieldWithObjectInput(input:)" has invalid value at .e: FaultyScalarErrorMessage', locations: [{ line: 2, column: 16 }], extensions: { code: 'FaultyScalarErrorExtensionCode' }, }, @@ -802,7 +802,7 @@ describe('Execute: Handles inputs', () => { errors: [ { message: - 'Argument "input" of required type "String!" was not provided.', + 'Argument "TestType.fieldWithNonNullableStringInput(input:)" of required type "String!" was not provided.', locations: [{ line: 1, column: 3 }], path: ['fieldWithNonNullableStringInput'], }, @@ -850,7 +850,7 @@ describe('Execute: Handles inputs', () => { errors: [ { message: - 'Argument "input" has invalid value: Expected variable "$foo" provided to type "String!" to provide a runtime value.', + 'Argument "TestType.fieldWithNonNullableStringInput(input:)" has invalid value: Expected variable "$foo" provided to type "String!" to provide a runtime value.', locations: [{ line: 3, column: 50 }], path: ['fieldWithNonNullableStringInput'], }, @@ -1102,7 +1102,7 @@ describe('Execute: Handles inputs', () => { errors: [ { message: - 'Argument "input" has invalid value: String cannot represent a non string value: WRONG_TYPE', + 'Argument "TestType.fieldWithDefaultArgumentValue(input:)" has invalid value: String cannot represent a non string value: WRONG_TYPE', locations: [{ line: 3, column: 48 }], path: ['fieldWithDefaultArgumentValue'], }, diff --git a/src/execution/values.ts b/src/execution/values.ts index ed869e10bd..560948cc8a 100644 --- a/src/execution/values.ts +++ b/src/execution/values.ts @@ -13,8 +13,12 @@ import type { } from '../language/ast.js'; import { Kind } from '../language/kinds.js'; -import type { GraphQLArgument, GraphQLField } from '../type/definition.js'; -import { isNonNullType, isRequiredArgument } from '../type/definition.js'; +import type { GraphQLField } from '../type/definition.js'; +import { + GraphQLArgument, + isNonNullType, + isRequiredArgument, +} from '../type/definition.js'; import type { GraphQLDirective } from '../type/directives.js'; import type { GraphQLSchema } from '../type/schema.js'; @@ -222,7 +226,7 @@ export function experimentalGetArgumentValues( // execution. This is a runtime check to ensure execution does not // continue with an invalid argument value. throw new GraphQLError( - `Argument "${argDef.name}" of required type "${argType}" was not provided.`, + `Argument "${argDef instanceof GraphQLArgument ? argDef : argDef.name}" of required type "${argType}" was not provided.`, { nodes: node }, ); } @@ -272,7 +276,7 @@ export function experimentalGetArgumentValues( valueNode, argType, (error, path) => { - error.message = `Argument "${argDef.name}" has invalid value${printPathArray( + error.message = `Argument "${argDef instanceof GraphQLArgument ? argDef : argDef.name}" has invalid value${printPathArray( path, )}: ${error.message}`; throw error; diff --git a/src/index.ts b/src/index.ts index b2dd705f0b..52ddb6440b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,12 +40,17 @@ export { // Definitions GraphQLSchema, GraphQLDirective, + GraphQLSchemaElement, GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, + GraphQLField, + GraphQLArgument, + GraphQLEnumValue, + GraphQLInputField, GraphQLList, GraphQLNonNull, // Standard GraphQL Scalars @@ -161,23 +166,19 @@ export type { GraphQLSchemaExtensions, GraphQLDirectiveConfig, GraphQLDirectiveExtensions, - GraphQLArgument, GraphQLArgumentConfig, GraphQLArgumentExtensions, GraphQLEnumTypeConfig, GraphQLEnumTypeExtensions, - GraphQLEnumValue, GraphQLEnumValueConfig, GraphQLEnumValueConfigMap, GraphQLEnumValueExtensions, - GraphQLField, GraphQLFieldConfig, GraphQLFieldConfigArgumentMap, GraphQLFieldConfigMap, GraphQLFieldExtensions, GraphQLFieldMap, GraphQLFieldResolver, - GraphQLInputField, GraphQLInputFieldConfig, GraphQLInputFieldConfigMap, GraphQLInputFieldExtensions, diff --git a/src/type/__tests__/definition-test.ts b/src/type/__tests__/definition-test.ts index ad35af937f..a95a7f9250 100644 --- a/src/type/__tests__/definition-test.ts +++ b/src/type/__tests__/definition-test.ts @@ -229,11 +229,11 @@ describe('Type System: Objects', () => { }, }; const testObject1 = new GraphQLObjectType({ - name: 'Test1', + name: 'Test', fields: outputFields, }); const testObject2 = new GraphQLObjectType({ - name: 'Test2', + name: 'Test', fields: outputFields, }); @@ -255,11 +255,11 @@ describe('Type System: Objects', () => { field2: { type: ScalarType }, }; const testInputObject1 = new GraphQLInputObjectType({ - name: 'Test1', + name: 'Test', fields: inputFields, }); const testInputObject2 = new GraphQLInputObjectType({ - name: 'Test2', + name: 'Test', fields: inputFields, }); @@ -305,18 +305,17 @@ describe('Type System: Objects', () => { f: { type: ScalarType }, }), }); - expect(objType.getFields()).to.deep.equal({ - f: { - name: 'f', - description: undefined, - type: ScalarType, - args: [], - resolve: undefined, - subscribe: undefined, - deprecationReason: undefined, - extensions: {}, - astNode: undefined, - }, + expect(objType.getFields().f).to.deep.include({ + coordinate: 'SomeObject.f', + name: 'f', + description: undefined, + type: ScalarType, + args: [], + resolve: undefined, + subscribe: undefined, + deprecationReason: undefined, + extensions: {}, + astNode: undefined, }); }); @@ -332,28 +331,32 @@ describe('Type System: Objects', () => { }, }, }); - expect(objType.getFields()).to.deep.equal({ - f: { - name: 'f', - description: undefined, - type: ScalarType, - args: [ - { - name: 'arg', - description: undefined, - type: ScalarType, - defaultValue: undefined, - deprecationReason: undefined, - extensions: {}, - astNode: undefined, - }, - ], - resolve: undefined, - subscribe: undefined, - deprecationReason: undefined, - extensions: {}, - astNode: undefined, - }, + + const f = objType.getFields().f; + + expect(f).to.deep.include({ + coordinate: 'SomeObject.f', + name: 'f', + description: undefined, + type: ScalarType, + resolve: undefined, + subscribe: undefined, + deprecationReason: undefined, + extensions: {}, + astNode: undefined, + }); + + expect(f.args).to.have.lengthOf(1); + + expect(f.args[0]).to.deep.include({ + coordinate: 'SomeObject.f(arg:)', + name: 'arg', + description: undefined, + type: ScalarType, + defaultValue: undefined, + deprecationReason: undefined, + extensions: {}, + astNode: undefined, }); }); @@ -506,18 +509,16 @@ describe('Type System: Interfaces', () => { f: { type: ScalarType }, }), }); - expect(interfaceType.getFields()).to.deep.equal({ - f: { - name: 'f', - description: undefined, - type: ScalarType, - args: [], - resolve: undefined, - subscribe: undefined, - deprecationReason: undefined, - extensions: {}, - astNode: undefined, - }, + expect(interfaceType.getFields().f).to.deep.include({ + name: 'f', + description: undefined, + type: ScalarType, + args: [], + resolve: undefined, + subscribe: undefined, + deprecationReason: undefined, + extensions: {}, + astNode: undefined, }); }); @@ -706,32 +707,39 @@ describe('Type System: Enums', () => { }, }); - expect(EnumTypeWithNullishValue.getValues()).to.deep.equal([ - { - name: 'NULL', - description: undefined, - value: null, - deprecationReason: undefined, - extensions: {}, - astNode: undefined, - }, - { - name: 'NAN', - description: undefined, - value: NaN, - deprecationReason: undefined, - extensions: {}, - astNode: undefined, - }, - { - name: 'NO_CUSTOM_VALUE', - description: undefined, - value: 'NO_CUSTOM_VALUE', - deprecationReason: undefined, - extensions: {}, - astNode: undefined, - }, - ]); + const values = EnumTypeWithNullishValue.getValues(); + + expect(values).to.have.lengthOf(3); + + expect(values[0]).to.deep.include({ + coordinate: 'EnumWithNullishValue.NULL', + name: 'NULL', + description: undefined, + value: null, + deprecationReason: undefined, + extensions: {}, + astNode: undefined, + }); + + expect(values[1]).to.deep.include({ + coordinate: 'EnumWithNullishValue.NAN', + name: 'NAN', + description: undefined, + value: NaN, + deprecationReason: undefined, + extensions: {}, + astNode: undefined, + }); + + expect(values[2]).to.deep.include({ + coordinate: 'EnumWithNullishValue.NO_CUSTOM_VALUE', + name: 'NO_CUSTOM_VALUE', + description: undefined, + value: 'NO_CUSTOM_VALUE', + deprecationReason: undefined, + extensions: {}, + astNode: undefined, + }); }); it('accepts a well defined Enum type with empty value definition', () => { @@ -826,16 +834,15 @@ describe('Type System: Input Objects', () => { f: { type: ScalarType }, }, }); - expect(inputObjType.getFields()).to.deep.equal({ - f: { - name: 'f', - description: undefined, - type: ScalarType, - defaultValue: undefined, - deprecationReason: undefined, - extensions: {}, - astNode: undefined, - }, + expect(inputObjType.getFields().f).to.deep.include({ + coordinate: 'SomeInputObject.f', + name: 'f', + description: undefined, + type: ScalarType, + defaultValue: undefined, + deprecationReason: undefined, + extensions: {}, + astNode: undefined, }); }); @@ -846,16 +853,15 @@ describe('Type System: Input Objects', () => { f: { type: ScalarType }, }), }); - expect(inputObjType.getFields()).to.deep.equal({ - f: { - name: 'f', - description: undefined, - type: ScalarType, - defaultValue: undefined, - extensions: {}, - deprecationReason: undefined, - astNode: undefined, - }, + expect(inputObjType.getFields().f).to.deep.include({ + coordinate: 'SomeInputObject.f', + name: 'f', + description: undefined, + type: ScalarType, + defaultValue: undefined, + extensions: {}, + deprecationReason: undefined, + astNode: undefined, }); }); diff --git a/src/type/__tests__/directive-test.ts b/src/type/__tests__/directive-test.ts index 03af5e1fd9..9b9ca31695 100644 --- a/src/type/__tests__/directive-test.ts +++ b/src/type/__tests__/directive-test.ts @@ -33,29 +33,33 @@ describe('Type System: Directive', () => { expect(directive).to.deep.include({ name: 'Foo', - args: [ - { - name: 'foo', - description: undefined, - type: GraphQLString, - defaultValue: undefined, - deprecationReason: undefined, - extensions: {}, - astNode: undefined, - }, - { - name: 'bar', - description: undefined, - type: GraphQLInt, - defaultValue: undefined, - deprecationReason: undefined, - extensions: {}, - astNode: undefined, - }, - ], isRepeatable: false, locations: ['QUERY'], }); + + expect(directive.args).to.have.lengthOf(2); + + expect(directive.args[0]).to.deep.include({ + coordinate: '@Foo(foo:)', + name: 'foo', + description: undefined, + type: GraphQLString, + defaultValue: undefined, + deprecationReason: undefined, + extensions: {}, + astNode: undefined, + }); + + expect(directive.args[1]).to.deep.include({ + coordinate: '@Foo(bar:)', + name: 'bar', + description: undefined, + type: GraphQLInt, + defaultValue: undefined, + deprecationReason: undefined, + extensions: {}, + astNode: undefined, + }); }); it('defines a repeatable directive', () => { diff --git a/src/type/__tests__/enumType-test.ts b/src/type/__tests__/enumType-test.ts index e4f7219908..004140d728 100644 --- a/src/type/__tests__/enumType-test.ts +++ b/src/type/__tests__/enumType-test.ts @@ -381,24 +381,28 @@ describe('Type System: Enum Values', () => { it('presents a getValues() API for complex enums', () => { const values = ComplexEnum.getValues(); - expect(values).to.have.deep.ordered.members([ - { - name: 'ONE', - description: undefined, - value: Complex1, - deprecationReason: undefined, - extensions: {}, - astNode: undefined, - }, - { - name: 'TWO', - description: undefined, - value: Complex2, - deprecationReason: undefined, - extensions: {}, - astNode: undefined, - }, - ]); + + expect(values).to.have.lengthOf(2); + + expect(values[0]).to.deep.include({ + coordinate: 'Complex.ONE', + name: 'ONE', + description: undefined, + value: Complex1, + deprecationReason: undefined, + extensions: {}, + astNode: undefined, + }); + + expect(values[1]).to.deep.include({ + coordinate: 'Complex.TWO', + name: 'TWO', + description: undefined, + value: Complex2, + deprecationReason: undefined, + extensions: {}, + astNode: undefined, + }); }); it('presents a getValue() API for complex enums', () => { diff --git a/src/type/__tests__/predicate-test.ts b/src/type/__tests__/predicate-test.ts index a71ce8c012..5bd680d767 100644 --- a/src/type/__tests__/predicate-test.ts +++ b/src/type/__tests__/predicate-test.ts @@ -3,11 +3,7 @@ import { describe, it } from 'mocha'; import { DirectiveLocation } from '../../language/directiveLocation.js'; -import type { - GraphQLArgument, - GraphQLInputField, - GraphQLInputType, -} from '../definition.js'; +import type { GraphQLInputType } from '../definition.js'; import { assertAbstractType, assertCompositeType, @@ -28,7 +24,9 @@ import { assertWrappingType, getNamedType, getNullableType, + GraphQLArgument, GraphQLEnumType, + GraphQLInputField, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLList, @@ -570,18 +568,7 @@ describe('Type predicates', () => { type: GraphQLInputType; defaultValue?: unknown; }): GraphQLArgument { - return { - name: 'someArg', - type: config.type, - description: undefined, - defaultValue: - config.defaultValue !== undefined - ? { value: config.defaultValue } - : undefined, - deprecationReason: null, - extensions: Object.create(null), - astNode: undefined, - }; + return new GraphQLArgument('SomeType.someField', 'someArg', config); } it('returns true for required arguments', () => { @@ -621,18 +608,7 @@ describe('Type predicates', () => { type: GraphQLInputType; defaultValue?: unknown; }): GraphQLInputField { - return { - name: 'someInputField', - type: config.type, - description: undefined, - defaultValue: - config.defaultValue !== undefined - ? { value: config.defaultValue } - : undefined, - deprecationReason: null, - extensions: Object.create(null), - astNode: undefined, - }; + return new GraphQLInputField('SomeType', 'someInputField', config); } it('returns true for required input field', () => { diff --git a/src/type/definition.ts b/src/type/definition.ts index 8d51201070..3911bd2794 100644 --- a/src/type/definition.ts +++ b/src/type/definition.ts @@ -485,6 +485,31 @@ export function getNamedType( } } +/** + * The base class for all Schema Elements. + */ +export class GraphQLSchemaElement { + readonly coordinate: string; + + constructor(coordinate: string) { + this.coordinate = coordinate; + } + + // TODO: add coverage + /* c8 ignore next 3*/ + get [Symbol.toStringTag]() { + return 'GraphQLSchemaElement'; + } + + toString(): string { + return this.coordinate; + } + + toJSON(): string { + return this.toString(); + } +} + /** * Used while defining GraphQL types to allow for circular references in * otherwise immutable type definitions. @@ -590,7 +615,10 @@ export interface GraphQLScalarTypeExtensions { * `coerceInputLiteral()` method. * */ -export class GraphQLScalarType { +export class GraphQLScalarType< + TInternal = unknown, + TExternal = TInternal, +> extends GraphQLSchemaElement { name: string; description: Maybe; specifiedByURL: Maybe; @@ -609,6 +637,8 @@ export class GraphQLScalarType { extensionASTNodes: ReadonlyArray; constructor(config: Readonly>) { + super(config.name); + this.name = assertName(config.name); this.description = config.description; this.specifiedByURL = config.specifiedByURL; @@ -649,7 +679,7 @@ export class GraphQLScalarType { } } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLScalarType'; } @@ -670,14 +700,6 @@ export class GraphQLScalarType { extensionASTNodes: this.extensionASTNodes, }; } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } } /* @deprecated in favor of GraphQLScalarOutputValueCoercer, will be removed in v18 */ @@ -806,7 +828,10 @@ export interface GraphQLObjectTypeExtensions<_TSource = any, _TContext = any> { * }); * ``` */ -export class GraphQLObjectType { +export class GraphQLObjectType< + TSource = any, + TContext = any, +> extends GraphQLSchemaElement { name: string; description: Maybe; isTypeOf: Maybe>; @@ -818,20 +843,23 @@ export class GraphQLObjectType { private _interfaces: ThunkReadonlyArray; constructor(config: Readonly>) { + super(config.name); this.name = assertName(config.name); this.description = config.description; this.isTypeOf = config.isTypeOf; this.extensions = toObjMapWithSymbols(config.extensions); this.astNode = config.astNode; this.extensionASTNodes = config.extensionASTNodes ?? []; + this._fields = (defineFieldMap).bind( undefined, + this.name, config.fields, ); this._interfaces = defineInterfaces.bind(undefined, config.interfaces); } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLObjectType'; } @@ -854,21 +882,13 @@ export class GraphQLObjectType { name: this.name, description: this.description, interfaces: this.getInterfaces(), - fields: fieldsToFieldsConfig(this.getFields()), + fields: mapValue(this.getFields(), (field) => field.toConfig()), isTypeOf: this.isTypeOf, extensions: this.extensions, astNode: this.astNode, extensionASTNodes: this.extensionASTNodes, }; } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } } function defineInterfaces( @@ -878,73 +898,15 @@ function defineInterfaces( } function defineFieldMap( + parentCoordinate: string, fields: ThunkObjMap>, ): GraphQLFieldMap { const fieldMap = resolveObjMapThunk(fields); - return mapValue(fieldMap, (fieldConfig, fieldName) => { - const argsConfig = fieldConfig.args ?? {}; - return { - name: assertName(fieldName), - description: fieldConfig.description, - type: fieldConfig.type, - args: defineArguments(argsConfig), - resolve: fieldConfig.resolve, - subscribe: fieldConfig.subscribe, - deprecationReason: fieldConfig.deprecationReason, - extensions: toObjMapWithSymbols(fieldConfig.extensions), - astNode: fieldConfig.astNode, - }; - }); -} - -export function defineArguments( - args: GraphQLFieldConfigArgumentMap, -): ReadonlyArray { - return Object.entries(args).map(([argName, argConfig]) => ({ - name: assertName(argName), - description: argConfig.description, - type: argConfig.type, - defaultValue: defineDefaultValue(argName, argConfig), - deprecationReason: argConfig.deprecationReason, - extensions: toObjMapWithSymbols(argConfig.extensions), - astNode: argConfig.astNode, - })); -} - -function fieldsToFieldsConfig( - fields: GraphQLFieldMap, -): GraphQLFieldConfigMap { - return mapValue(fields, (field) => ({ - description: field.description, - type: field.type, - args: argsToArgsConfig(field.args), - resolve: field.resolve, - subscribe: field.subscribe, - deprecationReason: field.deprecationReason, - extensions: field.extensions, - astNode: field.astNode, - })); -} - -/** - * @internal - */ -export function argsToArgsConfig( - args: ReadonlyArray, -): GraphQLFieldConfigArgumentMap { - return keyValMap( - args, - (arg) => arg.name, - (arg) => ({ - description: arg.description, - type: arg.type, - defaultValue: arg.defaultValue?.value, - defaultValueLiteral: arg.defaultValue?.literal, - deprecationReason: arg.deprecationReason, - extensions: arg.extensions, - astNode: arg.astNode, - }), + return mapValue( + fieldMap, + (fieldConfig, fieldName) => + new GraphQLField(parentCoordinate, fieldName, fieldConfig), ); } @@ -1064,7 +1026,11 @@ export type GraphQLFieldConfigMap = ObjMap< GraphQLFieldConfig >; -export interface GraphQLField { +export class GraphQLField< + TSource, + TContext, + TArgs = { [argument: string]: any }, +> extends GraphQLSchemaElement { name: string; description: Maybe; type: GraphQLOutputType; @@ -1074,9 +1040,56 @@ export interface GraphQLField { deprecationReason: Maybe; extensions: Readonly>; astNode: Maybe; + + constructor( + parentCoordinate: string, + name: string, + config: GraphQLFieldConfig, + ) { + const coordinate = `${parentCoordinate}.${name}`; + super(coordinate); + this.name = assertName(name); + this.description = config.description; + this.type = config.type; + + const argsConfig = config.args; + this.args = argsConfig + ? Object.entries(argsConfig).map( + ([argName, argConfig]) => + new GraphQLArgument(coordinate, argName, argConfig), + ) + : []; + + this.resolve = config.resolve; + this.subscribe = config.subscribe; + this.deprecationReason = config.deprecationReason; + this.extensions = toObjMapWithSymbols(config.extensions); + this.astNode = config.astNode; + } + + override get [Symbol.toStringTag]() { + return 'GraphQLField'; + } + + toConfig(): GraphQLFieldConfig { + return { + description: this.description, + type: this.type, + args: keyValMap( + this.args, + (arg) => arg.name, + (arg) => arg.toConfig(), + ), + resolve: this.resolve, + subscribe: this.subscribe, + deprecationReason: this.deprecationReason, + extensions: this.extensions, + astNode: this.astNode, + }; + } } -export interface GraphQLArgument { +export class GraphQLArgument extends GraphQLSchemaElement { name: string; description: Maybe; type: GraphQLInputType; @@ -1084,6 +1097,38 @@ export interface GraphQLArgument { deprecationReason: Maybe; extensions: Readonly; astNode: Maybe; + + constructor( + parentCoordinate: string, + name: string, + config: GraphQLArgumentConfig, + ) { + const coordinate = `${parentCoordinate}(${name}:)`; + super(coordinate); + this.name = assertName(name); + this.description = config.description; + this.type = config.type; + this.defaultValue = defineDefaultValue(name, config); + this.deprecationReason = config.deprecationReason; + this.extensions = toObjMapWithSymbols(config.extensions); + this.astNode = config.astNode; + } + + override get [Symbol.toStringTag]() { + return 'GraphQLArgument'; + } + + toConfig(): GraphQLArgumentConfig { + return { + description: this.description, + type: this.type, + defaultValue: this.defaultValue?.value, + defaultValueLiteral: this.defaultValue?.literal, + deprecationReason: this.deprecationReason, + extensions: this.extensions, + astNode: this.astNode, + }; + } } export function isRequiredArgument( @@ -1148,7 +1193,10 @@ export interface GraphQLInterfaceTypeExtensions { * }); * ``` */ -export class GraphQLInterfaceType { +export class GraphQLInterfaceType< + TSource = any, + TContext = any, +> extends GraphQLSchemaElement { name: string; description: Maybe; resolveType: Maybe>; @@ -1160,6 +1208,7 @@ export class GraphQLInterfaceType { private _interfaces: ThunkReadonlyArray; constructor(config: Readonly>) { + super(config.name); this.name = assertName(config.name); this.description = config.description; this.resolveType = config.resolveType; @@ -1168,12 +1217,13 @@ export class GraphQLInterfaceType { this.extensionASTNodes = config.extensionASTNodes ?? []; this._fields = (defineFieldMap).bind( undefined, + this.name, config.fields, ); this._interfaces = defineInterfaces.bind(undefined, config.interfaces); } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLInterfaceType'; } @@ -1196,21 +1246,13 @@ export class GraphQLInterfaceType { name: this.name, description: this.description, interfaces: this.getInterfaces(), - fields: fieldsToFieldsConfig(this.getFields()), + fields: mapValue(this.getFields(), (field) => field.toConfig()), resolveType: this.resolveType, extensions: this.extensions, astNode: this.astNode, extensionASTNodes: this.extensionASTNodes, }; } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } } export interface GraphQLInterfaceTypeConfig { @@ -1274,7 +1316,7 @@ export interface GraphQLUnionTypeExtensions { * }); * ``` */ -export class GraphQLUnionType { +export class GraphQLUnionType extends GraphQLSchemaElement { name: string; description: Maybe; resolveType: Maybe>; @@ -1285,6 +1327,7 @@ export class GraphQLUnionType { private _types: ThunkReadonlyArray; constructor(config: Readonly>) { + super(config.name); this.name = assertName(config.name); this.description = config.description; this.resolveType = config.resolveType; @@ -1295,7 +1338,7 @@ export class GraphQLUnionType { this._types = defineTypes.bind(undefined, config.types); } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLUnionType'; } @@ -1317,14 +1360,6 @@ export class GraphQLUnionType { extensionASTNodes: this.extensionASTNodes, }; } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } } function defineTypes( @@ -1368,17 +1403,6 @@ export interface GraphQLEnumTypeExtensions { [attributeName: string | symbol]: unknown; } -function enumValuesFromConfig(values: GraphQLEnumValueConfigMap) { - return Object.entries(values).map(([valueName, valueConfig]) => ({ - name: assertEnumValueName(valueName), - description: valueConfig.description, - value: valueConfig.value !== undefined ? valueConfig.value : valueName, - deprecationReason: valueConfig.deprecationReason, - extensions: toObjMapWithSymbols(valueConfig.extensions), - astNode: valueConfig.astNode, - })); -} - /** * Enum Type Definition * @@ -1402,7 +1426,7 @@ function enumValuesFromConfig(values: GraphQLEnumValueConfigMap) { * Note: If a value is not provided in a definition, the name of the enum value * will be used as its internal value. */ -export class GraphQLEnumType /* */ { +export class GraphQLEnumType /* */ extends GraphQLSchemaElement { name: string; description: Maybe; extensions: Readonly; @@ -1417,6 +1441,7 @@ export class GraphQLEnumType /* */ { private _nameLookup: ObjMap | null; constructor(config: Readonly */>) { + super(config.name); this.name = assertName(config.name); this.description = config.description; this.extensions = toObjMapWithSymbols(config.extensions); @@ -1426,18 +1451,24 @@ export class GraphQLEnumType /* */ { this._values = typeof config.values === 'function' ? config.values - : enumValuesFromConfig(config.values); + : Object.entries(config.values).map( + ([valueName, valueConfig]) => + new GraphQLEnumValue(config.name, valueName, valueConfig), + ); this._valueLookup = null; this._nameLookup = null; } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLEnumType'; } getValues(): ReadonlyArray */> { if (typeof this._values === 'function') { - this._values = enumValuesFromConfig(this._values()); + this._values = Object.entries(this._values()).map( + ([valueName, valueConfig]) => + new GraphQLEnumValue(this.name, valueName, valueConfig), + ); } return this._values; } @@ -1544,35 +1575,19 @@ export class GraphQLEnumType /* */ { } toConfig(): GraphQLEnumTypeNormalizedConfig { - const values = keyValMap( - this.getValues(), - (value) => value.name, - (value) => ({ - description: value.description, - value: value.value, - deprecationReason: value.deprecationReason, - extensions: value.extensions, - astNode: value.astNode, - }), - ); - return { name: this.name, description: this.description, - values, + values: keyValMap( + this.getValues(), + (value) => value.name, + (value) => value.toConfig(), + ), extensions: this.extensions, astNode: this.astNode, extensionASTNodes: this.extensionASTNodes, }; } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } } function didYouMeanEnumValue( @@ -1624,13 +1639,43 @@ export interface GraphQLEnumValueConfig { astNode?: Maybe; } -export interface GraphQLEnumValue { +export class GraphQLEnumValue extends GraphQLSchemaElement { name: string; description: Maybe; value: any /* T */; deprecationReason: Maybe; extensions: Readonly; astNode: Maybe; + + constructor( + parentCoordinate: string, + name: string, + config: GraphQLEnumValueConfig, + ) { + const coordinate = `${parentCoordinate}.${name}`; + super(coordinate); + + this.name = assertEnumValueName(name); + this.description = config.description; + this.value = config.value !== undefined ? config.value : name; + this.deprecationReason = config.deprecationReason; + this.extensions = toObjMapWithSymbols(config.extensions); + this.astNode = config.astNode; + } + + override get [Symbol.toStringTag]() { + return 'GraphQLEnumValue'; + } + + toConfig(): GraphQLEnumValueConfig { + return { + description: this.description, + value: this.value, + deprecationReason: this.deprecationReason, + extensions: this.extensions, + astNode: this.astNode, + }; + } } /** @@ -1667,7 +1712,7 @@ export interface GraphQLInputObjectTypeExtensions { * }); * ``` */ -export class GraphQLInputObjectType { +export class GraphQLInputObjectType extends GraphQLSchemaElement { name: string; description: Maybe; extensions: Readonly; @@ -1678,6 +1723,7 @@ export class GraphQLInputObjectType { private _fields: ThunkObjMap; constructor(config: Readonly) { + super(config.name); this.name = assertName(config.name); this.description = config.description; this.extensions = toObjMapWithSymbols(config.extensions); @@ -1685,10 +1731,14 @@ export class GraphQLInputObjectType { this.extensionASTNodes = config.extensionASTNodes ?? []; this.isOneOf = config.isOneOf ?? false; - this._fields = defineInputFieldMap.bind(undefined, config.fields); + this._fields = defineInputFieldMap.bind( + undefined, + this.name, + config.fields, + ); } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLInputObjectType'; } @@ -1700,49 +1750,28 @@ export class GraphQLInputObjectType { } toConfig(): GraphQLInputObjectTypeNormalizedConfig { - const fields = mapValue(this.getFields(), (field) => ({ - description: field.description, - type: field.type, - defaultValue: field.defaultValue?.value, - defaultValueLiteral: field.defaultValue?.literal, - deprecationReason: field.deprecationReason, - extensions: field.extensions, - astNode: field.astNode, - })); - return { name: this.name, description: this.description, - fields, + fields: mapValue(this.getFields(), (field) => field.toConfig()), extensions: this.extensions, astNode: this.astNode, extensionASTNodes: this.extensionASTNodes, isOneOf: this.isOneOf, }; } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } } function defineInputFieldMap( + parentCoordinate: string, fields: ThunkObjMap, ): GraphQLInputFieldMap { const fieldMap = resolveObjMapThunk(fields); - return mapValue(fieldMap, (fieldConfig, fieldName) => ({ - name: assertName(fieldName), - description: fieldConfig.description, - type: fieldConfig.type, - defaultValue: defineDefaultValue(fieldName, fieldConfig), - deprecationReason: fieldConfig.deprecationReason, - extensions: toObjMapWithSymbols(fieldConfig.extensions), - astNode: fieldConfig.astNode, - })); + return mapValue( + fieldMap, + (fieldConfig, fieldName) => + new GraphQLInputField(parentCoordinate, fieldName, fieldConfig), + ); } export interface GraphQLInputObjectTypeConfig { @@ -1787,7 +1816,7 @@ export interface GraphQLInputFieldConfig { export type GraphQLInputFieldConfigMap = ObjMap; -export interface GraphQLInputField { +export class GraphQLInputField extends GraphQLSchemaElement { name: string; description: Maybe; type: GraphQLInputType; @@ -1795,6 +1824,44 @@ export interface GraphQLInputField { deprecationReason: Maybe; extensions: Readonly; astNode: Maybe; + + constructor( + parentCoordinate: string, + name: string, + config: GraphQLInputFieldConfig, + ) { + const coordinate = `${parentCoordinate}.${name}`; + + devAssert( + !('resolve' in config), + `${coordinate} field has a resolve property, but Input Types cannot define resolvers.`, + ); + + super(coordinate); + this.name = assertName(name); + this.description = config.description; + this.type = config.type; + this.defaultValue = defineDefaultValue(name, config); + this.deprecationReason = config.deprecationReason; + this.extensions = toObjMapWithSymbols(config.extensions); + this.astNode = config.astNode; + } + + override get [Symbol.toStringTag]() { + return 'GraphQLInputField'; + } + + toConfig(): GraphQLInputFieldConfig { + return { + description: this.description, + type: this.type, + defaultValue: this.defaultValue?.value, + defaultValueLiteral: this.defaultValue?.literal, + deprecationReason: this.deprecationReason, + extensions: this.extensions, + astNode: this.astNode, + }; + } } export function isRequiredInputField(field: GraphQLInputField): boolean { diff --git a/src/type/directives.ts b/src/type/directives.ts index 23faa33717..bc4dbfa32d 100644 --- a/src/type/directives.ts +++ b/src/type/directives.ts @@ -1,20 +1,21 @@ +import { devAssert } from '../jsutils/devAssert.js'; import { inspect } from '../jsutils/inspect.js'; import { instanceOf } from '../jsutils/instanceOf.js'; +import { isObjectLike } from '../jsutils/isObjectLike.js'; +import { keyValMap } from '../jsutils/keyValMap.js'; import type { Maybe } from '../jsutils/Maybe.js'; +import type { ObjMap } from '../jsutils/ObjMap.js'; import { toObjMapWithSymbols } from '../jsutils/toObjMap.js'; import type { DirectiveDefinitionNode } from '../language/ast.js'; import { DirectiveLocation } from '../language/directiveLocation.js'; import { assertName } from './assertName.js'; -import type { - GraphQLArgument, - GraphQLFieldConfigArgumentMap, -} from './definition.js'; +import type { GraphQLArgumentConfig } from './definition.js'; import { - argsToArgsConfig, - defineArguments, + GraphQLArgument, GraphQLNonNull, + GraphQLSchemaElement, } from './definition.js'; import { GraphQLBoolean, GraphQLInt, GraphQLString } from './scalars.js'; @@ -51,7 +52,7 @@ export interface GraphQLDirectiveExtensions { * Directives are used by the GraphQL runtime as a way of modifying execution * behavior. Type system creators will usually not create these directly. */ -export class GraphQLDirective { +export class GraphQLDirective extends GraphQLSchemaElement { name: string; description: Maybe; locations: ReadonlyArray; @@ -61,6 +62,8 @@ export class GraphQLDirective { astNode: Maybe; constructor(config: Readonly) { + const coordinate = `@${config.name}`; + super(coordinate); this.name = assertName(config.name); this.description = config.description; this.locations = config.locations; @@ -68,8 +71,21 @@ export class GraphQLDirective { this.extensions = toObjMapWithSymbols(config.extensions); this.astNode = config.astNode; + devAssert( + Array.isArray(config.locations), + `${coordinate} locations must be an Array.`, + ); + const args = config.args ?? {}; - this.args = defineArguments(args); + devAssert( + isObjectLike(args) && !Array.isArray(args), + `${coordinate} args must be an object with argument names as keys.`, + ); + + this.args = Object.entries(args).map( + ([argName, argConfig]) => + new GraphQLArgument(coordinate, argName, argConfig), + ); } get [Symbol.toStringTag]() { @@ -81,34 +97,30 @@ export class GraphQLDirective { name: this.name, description: this.description, locations: this.locations, - args: argsToArgsConfig(this.args), + args: keyValMap( + this.args, + (arg) => arg.name, + (arg) => arg.toConfig(), + ), isRepeatable: this.isRepeatable, extensions: this.extensions, astNode: this.astNode, }; } - - toString(): string { - return '@' + this.name; - } - - toJSON(): string { - return this.toString(); - } } export interface GraphQLDirectiveConfig { name: string; description?: Maybe; locations: ReadonlyArray; - args?: Maybe; + args?: Maybe>; isRepeatable?: Maybe; extensions?: Maybe>; astNode?: Maybe; } interface GraphQLDirectiveNormalizedConfig extends GraphQLDirectiveConfig { - args: GraphQLFieldConfigArgumentMap; + args: ObjMap; isRepeatable: boolean; extensions: Readonly; } diff --git a/src/type/index.ts b/src/type/index.ts index 25b59d6177..ba6acd188d 100644 --- a/src/type/index.ts +++ b/src/type/index.ts @@ -55,12 +55,17 @@ export { getNullableType, getNamedType, // Definitions + GraphQLSchemaElement, GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, + GraphQLField, + GraphQLArgument, + GraphQLEnumValue, + GraphQLInputField, // Type Wrappers GraphQLList, GraphQLNonNull, @@ -82,23 +87,19 @@ export type { GraphQLNamedOutputType, ThunkReadonlyArray, ThunkObjMap, - GraphQLArgument, GraphQLArgumentConfig, GraphQLArgumentExtensions, GraphQLEnumTypeConfig, GraphQLEnumTypeExtensions, - GraphQLEnumValue, GraphQLEnumValueConfig, GraphQLEnumValueConfigMap, GraphQLEnumValueExtensions, - GraphQLField, GraphQLFieldConfig, GraphQLFieldConfigArgumentMap, GraphQLFieldConfigMap, GraphQLFieldExtensions, GraphQLFieldMap, GraphQLFieldResolver, - GraphQLInputField, GraphQLInputFieldConfig, GraphQLInputFieldConfigMap, GraphQLInputFieldExtensions, diff --git a/src/type/introspection.ts b/src/type/introspection.ts index 2f6c498f9a..60435e68f5 100644 --- a/src/type/introspection.ts +++ b/src/type/introspection.ts @@ -8,7 +8,6 @@ import { valueToLiteral } from '../utilities/valueToLiteral.js'; import type { GraphQLEnumValue, - GraphQLField, GraphQLFieldConfigMap, GraphQLInputField, GraphQLNamedType, @@ -16,6 +15,7 @@ import type { } from './definition.js'; import { GraphQLEnumType, + GraphQLField, GraphQLList, GraphQLNonNull, GraphQLObjectType, @@ -511,53 +511,24 @@ export const __TypeKind: GraphQLEnumType = new GraphQLEnumType({ }, }); -/** - * Note that these are GraphQLField and not GraphQLFieldConfig, - * so the format for args is different. - */ - -export const SchemaMetaFieldDef: GraphQLField = { - name: '__schema', +export const SchemaMetaFieldDef = new GraphQLField('', '__schema', { type: new GraphQLNonNull(__Schema), description: 'Access the current type schema of this server.', - args: [], resolve: (_source, _args, _context, { schema }) => schema, - deprecationReason: undefined, - extensions: Object.create(null), - astNode: undefined, -}; +}); -export const TypeMetaFieldDef: GraphQLField = { - name: '__type', +export const TypeMetaFieldDef = new GraphQLField('', '__type', { type: __Type, description: 'Request the type information of a single type.', - args: [ - { - name: 'name', - description: undefined, - type: new GraphQLNonNull(GraphQLString), - defaultValue: undefined, - deprecationReason: undefined, - extensions: Object.create(null), - astNode: undefined, - }, - ], + args: { name: { type: new GraphQLNonNull(GraphQLString) } }, resolve: (_source, { name }, _context, { schema }) => schema.getType(name), - deprecationReason: undefined, - extensions: Object.create(null), - astNode: undefined, -}; +}); -export const TypeNameMetaFieldDef: GraphQLField = { - name: '__typename', +export const TypeNameMetaFieldDef = new GraphQLField('', '__typename', { type: new GraphQLNonNull(GraphQLString), description: 'The name of the current Object type at runtime.', - args: [], resolve: (_source, _args, _context, { parentType }) => parentType.name, - deprecationReason: undefined, - extensions: Object.create(null), - astNode: undefined, -}; +}); export const introspectionTypes: ReadonlyArray = Object.freeze([ diff --git a/src/type/validate.ts b/src/type/validate.ts index 48cc70299a..4224f8586f 100644 --- a/src/type/validate.ts +++ b/src/type/validate.ts @@ -210,25 +210,23 @@ function validateDirectives(context: SchemaValidationContext): void { // Ensure they are named correctly. validateName(context, arg); - const argStr = `${directive}(${arg.name}:)`; - // Ensure the type is an input type. if (!isInputType(arg.type)) { context.reportError( - `The type of ${argStr} must be Input Type ` + + `The type of ${arg} must be Input Type ` + `but got: ${inspect(arg.type)}.`, arg.astNode, ); } if (isRequiredArgument(arg) && arg.deprecationReason != null) { - context.reportError( - `Required argument ${argStr} cannot be deprecated.`, - [getDeprecatedDirectiveNode(arg.astNode), arg.astNode?.type], - ); + context.reportError(`Required argument ${arg} cannot be deprecated.`, [ + getDeprecatedDirectiveNode(arg.astNode), + arg.astNode?.type, + ]); } - validateDefaultValue(context, arg, argStr); + validateDefaultValue(context, arg); } } } @@ -236,7 +234,6 @@ function validateDirectives(context: SchemaValidationContext): void { function validateDefaultValue( context: SchemaValidationContext, inputValue: GraphQLArgument | GraphQLInputField, - argStr: string, ): void { const defaultValue = inputValue.defaultValue; @@ -250,7 +247,7 @@ function validateDefaultValue( inputValue.type, (error, path) => { context.reportError( - `${argStr} has invalid default value${printPathArray(path)}: ${ + `${inputValue} has invalid default value${printPathArray(path)}: ${ error.message }`, error.nodes, @@ -280,7 +277,7 @@ function validateDefaultValue( if (uncoercedErrors.length === 0) { context.reportError( - `${argStr} has invalid default value: ${inspect( + `${inputValue} has invalid default value: ${inspect( defaultValue.value, )}. Did you mean: ${inspect(uncoercedValue)}?`, inputValue.astNode?.defaultValue, @@ -295,7 +292,7 @@ function validateDefaultValue( // Otherwise report the original set of errors. for (const [error, path] of errors) { context.reportError( - `${argStr} has invalid default value${printPathArray(path)}: ${ + `${inputValue} has invalid default value${printPathArray(path)}: ${ error.message }`, inputValue.astNode?.defaultValue, @@ -437,7 +434,7 @@ function validateFields( // Ensure the type is an output type if (!isOutputType(field.type)) { context.reportError( - `The type of ${type}.${field.name} must be Output Type ` + + `The type of ${field} must be Output Type ` + `but got: ${inspect(field.type)}.`, field.astNode?.type, ); @@ -445,29 +442,25 @@ function validateFields( // Ensure the arguments are valid for (const arg of field.args) { - const argName = arg.name; - // Ensure they are named correctly. validateName(context, arg); - const argStr = `${type}.${field.name}(${argName}:)`; - // Ensure the type is an input type if (!isInputType(arg.type)) { context.reportError( - `The type of ${argStr} must be Input Type but got: ${inspect(arg.type)}.`, + `The type of ${arg} must be Input Type but got: ${inspect(arg.type)}.`, arg.astNode?.type, ); } if (isRequiredArgument(arg) && arg.deprecationReason != null) { - context.reportError( - `Required argument ${type}.${field.name}(${argName}:) cannot be deprecated.`, - [getDeprecatedDirectiveNode(arg.astNode), arg.astNode?.type], - ); + context.reportError(`Required argument ${arg} cannot be deprecated.`, [ + getDeprecatedDirectiveNode(arg.astNode), + arg.astNode?.type, + ]); } - validateDefaultValue(context, arg, argStr); + validateDefaultValue(context, arg); } } } @@ -480,7 +473,7 @@ function validateInterfaces( for (const iface of type.getInterfaces()) { if (!isInterfaceType(iface)) { context.reportError( - `Type ${inspect(type)} must only implement Interface types, ` + + `Type ${type} must only implement Interface types, ` + `it cannot implement ${inspect(iface)}.`, getAllImplementsInterfaceNodes(type, iface), ); @@ -497,7 +490,7 @@ function validateInterfaces( if (ifaceTypeNames.has(iface.name)) { context.reportError( - `Type ${type} can only implement ${iface.name} once.`, + `Type ${type} can only implement ${iface} once.`, getAllImplementsInterfaceNodes(type, iface), ); continue; @@ -519,13 +512,12 @@ function validateTypeImplementsInterface( // Assert each interface field is implemented. for (const ifaceField of Object.values(iface.getFields())) { - const fieldName = ifaceField.name; - const typeField = typeFieldMap[fieldName]; + const typeField = typeFieldMap[ifaceField.name]; // Assert interface field exists on type. if (typeField == null) { context.reportError( - `Interface field ${iface.name}.${fieldName} expected but ${type} does not provide it.`, + `Interface field ${ifaceField} expected but ${type} does not provide it.`, [ifaceField.astNode, type.astNode, ...type.extensionASTNodes], ); continue; @@ -535,22 +527,20 @@ function validateTypeImplementsInterface( // a valid subtype. (covariant) if (!isTypeSubTypeOf(context.schema, typeField.type, ifaceField.type)) { context.reportError( - `Interface field ${iface.name}.${fieldName} expects type ` + - `${inspect(ifaceField.type)} but ${type}.${fieldName} ` + - `is type ${inspect(typeField.type)}.`, + `Interface field ${ifaceField} expects type ${ifaceField.type} ` + + `but ${typeField} is type ${typeField.type}.`, [ifaceField.astNode?.type, typeField.astNode?.type], ); } // Assert each interface field arg is implemented. for (const ifaceArg of ifaceField.args) { - const argName = ifaceArg.name; - const typeArg = typeField.args.find((arg) => arg.name === argName); + const typeArg = typeField.args.find((arg) => arg.name === ifaceArg.name); // Assert interface field arg exists on object field. if (!typeArg) { context.reportError( - `Interface field argument ${iface.name}.${fieldName}(${argName}:) expected but ${type}.${fieldName} does not provide it.`, + `Interface field argument ${ifaceArg} expected but ${typeField} does not provide it.`, [ifaceArg.astNode, typeField.astNode], ); continue; @@ -561,10 +551,8 @@ function validateTypeImplementsInterface( // TODO: change to contravariant? if (!isEqualType(ifaceArg.type, typeArg.type)) { context.reportError( - `Interface field argument ${iface.name}.${fieldName}(${argName}:) ` + - `expects type ${inspect(ifaceArg.type)} but ` + - `${type}.${fieldName}(${argName}:) is type ` + - `${inspect(typeArg.type)}.`, + `Interface field argument ${ifaceArg} expects type ${ifaceArg.type} ` + + `but ${typeArg} is type ${typeArg.type}.`, [ifaceArg.astNode?.type, typeArg.astNode?.type], ); } @@ -572,17 +560,17 @@ function validateTypeImplementsInterface( // Assert additional arguments must not be required. for (const typeArg of typeField.args) { - const argName = typeArg.name; - const ifaceArg = ifaceField.args.find((arg) => arg.name === argName); - if (!ifaceArg && isRequiredArgument(typeArg)) { - context.reportError( - `Argument "${type}.${fieldName}(${argName}:)" must not be required type "${inspect( - typeArg.type, - )}" if not provided by the Interface field "${ - iface.name - }.${fieldName}".`, - [typeArg.astNode, ifaceField.astNode], + if (isRequiredArgument(typeArg)) { + const ifaceArg = ifaceField.args.find( + (arg) => arg.name === typeArg.name, ); + if (!ifaceArg) { + context.reportError( + `Argument "${typeArg}" must not be required type "${typeArg.type}" ` + + `if not provided by the Interface field "${ifaceField}".`, + [typeArg.astNode, ifaceField.astNode], + ); + } } } } @@ -598,8 +586,8 @@ function validateTypeImplementsAncestors( if (!ifaceInterfaces.includes(transitive)) { context.reportError( transitive === type - ? `Type ${type} cannot implement ${iface.name} because it would create a circular reference.` - : `Type ${type} must implement ${transitive.name} because it is implemented by ${iface.name}.`, + ? `Type ${type} cannot implement ${iface} because it would create a circular reference.` + : `Type ${type} must implement ${transitive} because it is implemented by ${iface}.`, [ ...getAllImplementsInterfaceNodes(iface, transitive), ...getAllImplementsInterfaceNodes(type, iface), @@ -617,7 +605,7 @@ function validateUnionMembers( if (memberTypes.length === 0) { context.reportError( - `Union type ${union.name} must define one or more member types.`, + `Union type ${union} must define one or more member types.`, [union.astNode, ...union.extensionASTNodes], ); } @@ -626,7 +614,7 @@ function validateUnionMembers( for (const memberType of memberTypes) { if (includedTypeNames.has(memberType.name)) { context.reportError( - `Union type ${union.name} can only include type ${memberType} once.`, + `Union type ${union} can only include type ${memberType} once.`, getUnionMemberTypeNodes(union, memberType.name), ); continue; @@ -634,7 +622,7 @@ function validateUnionMembers( includedTypeNames.add(memberType.name); if (!isObjectType(memberType)) { context.reportError( - `Union type ${union.name} can only include Object types, ` + + `Union type ${union} can only include Object types, ` + `it cannot include ${inspect(memberType)}.`, getUnionMemberTypeNodes(union, String(memberType)), ); @@ -669,7 +657,7 @@ function validateInputFields( if (fields.length === 0) { context.reportError( - `Input Object type ${inputObj.name} must define one or more fields.`, + `Input Object type ${inputObj} must define one or more fields.`, [inputObj.astNode, ...inputObj.extensionASTNodes], ); } @@ -682,22 +670,20 @@ function validateInputFields( // Ensure the type is an input type if (!isInputType(field.type)) { context.reportError( - `The type of ${inputObj.name}.${field.name} must be Input Type ` + + `The type of ${field} must be Input Type ` + `but got: ${inspect(field.type)}.`, field.astNode?.type, ); } - const fieldStr = `${inputObj.name}.${field.name}`; - if (isRequiredInputField(field) && field.deprecationReason != null) { context.reportError( - `Required input field ${fieldStr} cannot be deprecated.`, + `Required input field ${field} cannot be deprecated.`, [getDeprecatedDirectiveNode(field.astNode), field.astNode?.type], ); } - validateDefaultValue(context, field, fieldStr); + validateDefaultValue(context, field); if (inputObj.isOneOf) { validateOneOfInputObjectField(inputObj, field, context); diff --git a/src/utilities/__tests__/buildClientSchema-test.ts b/src/utilities/__tests__/buildClientSchema-test.ts index 4fd2dbcd34..2f85191039 100644 --- a/src/utilities/__tests__/buildClientSchema-test.ts +++ b/src/utilities/__tests__/buildClientSchema-test.ts @@ -377,32 +377,38 @@ describe('Type System: build schema from introspection', () => { // Client types do not get server-only values, so `value` mirrors `name`, // rather than using the integers defined in the "server" schema. - expect(clientFoodEnum.getValues()).to.deep.equal([ - { - name: 'VEGETABLES', - description: 'Foods that are vegetables.', - value: 'VEGETABLES', - deprecationReason: null, - extensions: {}, - astNode: undefined, - }, - { - name: 'FRUITS', - description: null, - value: 'FRUITS', - deprecationReason: null, - extensions: {}, - astNode: undefined, - }, - { - name: 'OILS', - description: null, - value: 'OILS', - deprecationReason: 'Too fatty', - extensions: {}, - astNode: undefined, - }, - ]); + const values = clientFoodEnum.getValues(); + expect(values).to.have.lengthOf(3); + + expect(values[0]).to.deep.include({ + coordinate: 'Food.VEGETABLES', + name: 'VEGETABLES', + description: 'Foods that are vegetables.', + value: 'VEGETABLES', + deprecationReason: null, + extensions: {}, + astNode: undefined, + }); + + expect(values[1]).to.deep.include({ + coordinate: 'Food.FRUITS', + name: 'FRUITS', + description: null, + value: 'FRUITS', + deprecationReason: null, + extensions: {}, + astNode: undefined, + }); + + expect(values[2]).to.deep.include({ + coordinate: 'Food.OILS', + name: 'OILS', + description: null, + value: 'OILS', + deprecationReason: 'Too fatty', + extensions: {}, + astNode: undefined, + }); }); it('builds a schema with an input object', () => { diff --git a/src/utilities/__tests__/findSchemaChanges-test.ts b/src/utilities/__tests__/findSchemaChanges-test.ts index 4aa351707b..7bcdf32264 100644 --- a/src/utilities/__tests__/findSchemaChanges-test.ts +++ b/src/utilities/__tests__/findSchemaChanges-test.ts @@ -149,7 +149,7 @@ describe('findSchemaChanges', () => { { type: SafeChangeType.DESCRIPTION_CHANGED, description: - 'Description of argument Query.foo(x) has changed to "New Description".', + 'Description of argument Query.foo(x:) has changed to "New Description".', }, ]); }); diff --git a/src/utilities/findSchemaChanges.ts b/src/utilities/findSchemaChanges.ts index 4cffd5aad6..fb87cd494d 100644 --- a/src/utilities/findSchemaChanges.ts +++ b/src/utilities/findSchemaChanges.ts @@ -160,7 +160,7 @@ function findDirectiveChanges( for (const oldDirective of directivesDiff.removed) { schemaChanges.push({ type: BreakingChangeType.DIRECTIVE_REMOVED, - description: `Directive @${oldDirective.name} was removed.`, + description: `Directive ${oldDirective} was removed.`, }); } @@ -178,7 +178,7 @@ function findDirectiveChanges( if (isRequiredArgument(newArg)) { schemaChanges.push({ type: BreakingChangeType.REQUIRED_DIRECTIVE_ARG_ADDED, - description: `A required argument @${oldDirective.name}(${newArg.name}:) was added.`, + description: `A required argument ${newArg} was added.`, }); } else { schemaChanges.push({ @@ -191,7 +191,7 @@ function findDirectiveChanges( for (const oldArg of argsDiff.removed) { schemaChanges.push({ type: BreakingChangeType.DIRECTIVE_ARG_REMOVED, - description: `Argument @${oldDirective.name}(${oldArg.name}:) was removed.`, + description: `Argument ${oldArg} was removed.`, }); } @@ -257,7 +257,7 @@ function findDirectiveChanges( if (oldDirective.isRepeatable && !newDirective.isRepeatable) { schemaChanges.push({ type: BreakingChangeType.DIRECTIVE_REPEATABLE_REMOVED, - description: `Repeatable flag was removed from @${oldDirective.name}.`, + description: `Repeatable flag was removed from ${oldDirective}.`, }); } else if (newDirective.isRepeatable && !oldDirective.isRepeatable) { schemaChanges.push({ @@ -277,7 +277,7 @@ function findDirectiveChanges( if (!newDirective.locations.includes(location)) { schemaChanges.push({ type: BreakingChangeType.DIRECTIVE_LOCATION_REMOVED, - description: `${location} was removed from @${oldDirective.name}.`, + description: `${location} was removed from ${oldDirective}.`, }); } } @@ -349,9 +349,9 @@ function findTypeChanges( } else if (oldType.constructor !== newType.constructor) { schemaChanges.push({ type: BreakingChangeType.TYPE_CHANGED_KIND, - description: - `${oldType} changed from ` + - `${typeKindName(oldType)} to ${typeKindName(newType)}.`, + description: `${oldType} changed from ${typeKindName( + oldType, + )} to ${typeKindName(newType)}.`, }); } } @@ -373,12 +373,12 @@ function findInputObjectTypeChanges( if (isRequiredInputField(newField)) { schemaChanges.push({ type: BreakingChangeType.REQUIRED_INPUT_FIELD_ADDED, - description: `A required field ${oldType}.${newField.name} was added.`, + description: `A required field ${newField} was added.`, }); } else { schemaChanges.push({ type: DangerousChangeType.OPTIONAL_INPUT_FIELD_ADDED, - description: `An optional field ${oldType}.${newField.name} was added.`, + description: `An optional field ${newField} was added.`, }); } } @@ -386,7 +386,7 @@ function findInputObjectTypeChanges( for (const oldField of fieldsDiff.removed) { schemaChanges.push({ type: BreakingChangeType.FIELD_REMOVED, - description: `Field ${oldType}.${oldField.name} was removed.`, + description: `Field ${oldField} was removed.`, }); } @@ -398,9 +398,7 @@ function findInputObjectTypeChanges( if (!isSafe) { schemaChanges.push({ type: BreakingChangeType.FIELD_CHANGED_KIND, - description: - `Field ${oldType}.${oldField.name} changed type from ` + - `${String(oldField.type)} to ${String(newField.type)}.`, + description: `Field ${newField} changed type from ${oldField.type} to ${newField.type}.`, }); } else if (oldField.type.toString() !== newField.type.toString()) { schemaChanges.push({ @@ -456,14 +454,14 @@ function findEnumTypeChanges( for (const newValue of valuesDiff.added) { schemaChanges.push({ type: DangerousChangeType.VALUE_ADDED_TO_ENUM, - description: `Enum value ${oldType}.${newValue.name} was added.`, + description: `Enum value ${newValue} was added.`, }); } for (const oldValue of valuesDiff.removed) { schemaChanges.push({ type: BreakingChangeType.VALUE_REMOVED_FROM_ENUM, - description: `Enum value ${oldType}.${oldValue.name} was removed.`, + description: `Enum value ${oldValue} was removed.`, }); } @@ -489,14 +487,14 @@ function findImplementedInterfacesChanges( for (const newInterface of interfacesDiff.added) { schemaChanges.push({ type: DangerousChangeType.IMPLEMENTED_INTERFACE_ADDED, - description: `${newInterface.name} added to interfaces implemented by ${oldType}.`, + description: `${newInterface} added to interfaces implemented by ${oldType}.`, }); } for (const oldInterface of interfacesDiff.removed) { schemaChanges.push({ type: BreakingChangeType.IMPLEMENTED_INTERFACE_REMOVED, - description: `${oldType} no longer implements interface ${oldInterface.name}.`, + description: `${oldType} no longer implements interface ${oldInterface}.`, }); } @@ -516,7 +514,7 @@ function findFieldChanges( for (const oldField of fieldsDiff.removed) { schemaChanges.push({ type: BreakingChangeType.FIELD_REMOVED, - description: `Field ${oldType}.${oldField.name} was removed.`, + description: `Field ${oldField} was removed.`, }); } @@ -528,7 +526,7 @@ function findFieldChanges( } for (const [oldField, newField] of fieldsDiff.persisted) { - schemaChanges.push(...findArgChanges(oldType, oldField, newField)); + schemaChanges.push(...findArgChanges(oldField, newField)); const isSafe = isChangeSafeForObjectOrInterfaceField( oldField.type, @@ -537,9 +535,7 @@ function findFieldChanges( if (!isSafe) { schemaChanges.push({ type: BreakingChangeType.FIELD_CHANGED_KIND, - description: - `Field ${oldType}.${oldField.name} changed type from ` + - `${String(oldField.type)} to ${String(newField.type)}.`, + description: `Field ${newField} changed type from ${oldField.type} to ${newField.type}.`, }); } else if (oldField.type.toString() !== newField.type.toString()) { schemaChanges.push({ @@ -562,7 +558,6 @@ function findFieldChanges( } function findArgChanges( - oldType: GraphQLObjectType | GraphQLInterfaceType, oldField: GraphQLField, newField: GraphQLField, ): Array { @@ -572,7 +567,7 @@ function findArgChanges( for (const oldArg of argsDiff.removed) { schemaChanges.push({ type: BreakingChangeType.ARG_REMOVED, - description: `Argument ${oldType}.${oldField.name}(${oldArg.name}:) was removed.`, + description: `Argument ${oldArg} was removed.`, }); } @@ -585,15 +580,13 @@ function findArgChanges( if (!isSafe) { schemaChanges.push({ type: BreakingChangeType.ARG_CHANGED_KIND, - description: - `Argument ${oldType}.${oldField.name}(${oldArg.name}:) has changed type from ` + - `${String(oldArg.type)} to ${String(newArg.type)}.`, + description: `Argument ${newArg} has changed type from ${oldArg.type} to ${newArg.type}.`, }); } else if (oldArg.defaultValue !== undefined) { if (newArg.defaultValue === undefined) { schemaChanges.push({ type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE, - description: `${oldType}.${oldField.name}(${oldArg.name}:) defaultValue was removed.`, + description: `${oldArg} defaultValue was removed.`, }); } else { // Since we looking only for client's observable changes we should @@ -605,7 +598,7 @@ function findArgChanges( if (oldValueStr !== newValueStr) { schemaChanges.push({ type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE, - description: `${oldType}.${oldField.name}(${oldArg.name}:) has changed defaultValue from ${oldValueStr} to ${newValueStr}.`, + description: `${oldArg} has changed defaultValue from ${oldValueStr} to ${newValueStr}.`, }); } } @@ -616,13 +609,13 @@ function findArgChanges( const newValueStr = stringifyValue(newArg.defaultValue, newArg.type); schemaChanges.push({ type: SafeChangeType.ARG_DEFAULT_VALUE_ADDED, - description: `${oldType}.${oldField.name}(${oldArg.name}:) added a defaultValue ${newValueStr}.`, + description: `${oldArg} added a defaultValue ${newValueStr}.`, }); } else if (oldArg.type.toString() !== newArg.type.toString()) { schemaChanges.push({ type: SafeChangeType.ARG_CHANGED_KIND_SAFE, description: - `Argument ${oldType}.${oldField.name}(${oldArg.name}:) has changed type from ` + + `Argument ${oldArg} has changed type from ` + `${String(oldArg.type)} to ${String(newArg.type)}.`, }); } @@ -630,7 +623,7 @@ function findArgChanges( if (oldArg.description !== newArg.description) { schemaChanges.push({ type: SafeChangeType.DESCRIPTION_CHANGED, - description: `Description of argument ${oldType}.${oldField.name}(${oldArg.name}) has changed to "${newArg.description}".`, + description: `Description of argument ${oldArg} has changed to "${newArg.description}".`, }); } } @@ -639,12 +632,12 @@ function findArgChanges( if (isRequiredArgument(newArg)) { schemaChanges.push({ type: BreakingChangeType.REQUIRED_ARG_ADDED, - description: `A required argument ${oldType}.${oldField.name}(${newArg.name}:) was added.`, + description: `A required argument ${newArg} was added.`, }); } else { schemaChanges.push({ type: DangerousChangeType.OPTIONAL_ARG_ADDED, - description: `An optional argument ${oldType}.${oldField.name}(${newArg.name}:) was added.`, + description: `An optional argument ${newArg} was added.`, }); } } diff --git a/src/validation/rules/KnownArgumentNamesRule.ts b/src/validation/rules/KnownArgumentNamesRule.ts index 8db00b1497..40b925aab1 100644 --- a/src/validation/rules/KnownArgumentNamesRule.ts +++ b/src/validation/rules/KnownArgumentNamesRule.ts @@ -55,9 +55,8 @@ export function KnownArgumentNamesRule(context: ValidationContext): ASTVisitor { Argument(argNode) { const argDef = context.getArgument(); const fieldDef = context.getFieldDef(); - const parentType = context.getParentType(); - if (!argDef && fieldDef && parentType) { + if (!argDef && fieldDef) { const argName = argNode.name.value; const suggestions = context.hideSuggestions ? [] @@ -67,7 +66,7 @@ export function KnownArgumentNamesRule(context: ValidationContext): ASTVisitor { ); context.reportError( new GraphQLError( - `Unknown argument "${argName}" on field "${parentType}.${fieldDef.name}".` + + `Unknown argument "${argName}" on field "${fieldDef}".` + didYouMean(suggestions), { nodes: argNode }, ), diff --git a/src/validation/rules/ProvidedRequiredArgumentsRule.ts b/src/validation/rules/ProvidedRequiredArgumentsRule.ts index 5576b4f2d6..27bbccd4a4 100644 --- a/src/validation/rules/ProvidedRequiredArgumentsRule.ts +++ b/src/validation/rules/ProvidedRequiredArgumentsRule.ts @@ -12,12 +12,10 @@ import type { ASTVisitor } from '../../language/visitor.js'; import type { GraphQLArgument } from '../../type/definition.js'; import { - getNamedType, isRequiredArgument, isType, } from '../../type/definition.js'; import { specifiedDirectives } from '../../type/directives.js'; -import { isIntrospectionType } from '../../type/introspection.js'; import { typeFromAST } from '../../utilities/typeFromAST.js'; @@ -51,20 +49,9 @@ export function ProvidedRequiredArgumentsRule( ); for (const argDef of fieldDef.args) { if (!providedArgs.has(argDef.name) && isRequiredArgument(argDef)) { - const fieldType = getNamedType(context.getType()); - let parentTypeStr: string | undefined; - if (fieldType && isIntrospectionType(fieldType)) { - parentTypeStr = '.'; - } else { - const parentType = context.getParentType(); - if (parentType) { - parentTypeStr = `${context.getParentType()}.`; - } - } - const argTypeStr = inspect(argDef.type); context.reportError( new GraphQLError( - `Argument "${parentTypeStr}${fieldDef.name}(${argDef.name}:)" of type "${argTypeStr}" is required, but it was not provided.`, + `Argument "${argDef}" of type "${argDef.type}" is required, but it was not provided.`, { nodes: fieldNode }, ), ); diff --git a/src/validation/rules/VariablesInAllowedPositionRule.ts b/src/validation/rules/VariablesInAllowedPositionRule.ts index dfcedbe547..d5e24d13d4 100644 --- a/src/validation/rules/VariablesInAllowedPositionRule.ts +++ b/src/validation/rules/VariablesInAllowedPositionRule.ts @@ -1,4 +1,3 @@ -import { inspect } from '../../jsutils/inspect.js'; import type { Maybe } from '../../jsutils/Maybe.js'; import { GraphQLError } from '../../error/GraphQLError.js'; @@ -74,11 +73,9 @@ export function VariablesInAllowedPositionRule( defaultValue, ) ) { - const varTypeStr = inspect(varType); - const typeStr = inspect(type); context.reportError( new GraphQLError( - `Variable "$${varName}" of type "${varTypeStr}" used in position expecting type "${typeStr}".`, + `Variable "$${varName}" of type "${varType}" used in position expecting type "${type}".`, { nodes: [varDef, node] }, ), ); @@ -89,11 +86,9 @@ export function VariablesInAllowedPositionRule( parentType.isOneOf && isNullableType(varType) ) { - const varTypeStr = inspect(varType); - const parentTypeStr = inspect(parentType); context.reportError( new GraphQLError( - `Variable "$${varName}" is of type "${varTypeStr}" but must be non-nullable to be used for OneOf Input Object "${parentTypeStr}".`, + `Variable "$${varName}" is of type "${varType}" but must be non-nullable to be used for OneOf Input Object "${parentType}".`, { nodes: [varDef, node] }, ), ); diff --git a/src/validation/rules/custom/NoDeprecatedCustomRule.ts b/src/validation/rules/custom/NoDeprecatedCustomRule.ts index 1e3328039e..6fcf5651a7 100644 --- a/src/validation/rules/custom/NoDeprecatedCustomRule.ts +++ b/src/validation/rules/custom/NoDeprecatedCustomRule.ts @@ -1,5 +1,3 @@ -import { invariant } from '../../../jsutils/invariant.js'; - import { GraphQLError } from '../../../error/GraphQLError.js'; import type { ASTVisitor } from '../../../language/visitor.js'; @@ -24,11 +22,9 @@ export function NoDeprecatedCustomRule(context: ValidationContext): ASTVisitor { const fieldDef = context.getFieldDef(); const deprecationReason = fieldDef?.deprecationReason; if (fieldDef && deprecationReason != null) { - const parentType = context.getParentType(); - invariant(parentType != null); context.reportError( new GraphQLError( - `The field ${parentType}.${fieldDef.name} is deprecated. ${deprecationReason}`, + `The field ${fieldDef} is deprecated. ${deprecationReason}`, { nodes: node }, ), ); @@ -38,25 +34,12 @@ export function NoDeprecatedCustomRule(context: ValidationContext): ASTVisitor { const argDef = context.getArgument(); const deprecationReason = argDef?.deprecationReason; if (argDef && deprecationReason != null) { - const directiveDef = context.getDirective(); - if (directiveDef != null) { - context.reportError( - new GraphQLError( - `The argument "@${directiveDef.name}(${argDef.name}:)" is deprecated. ${deprecationReason}`, - { nodes: node }, - ), - ); - } else { - const parentType = context.getParentType(); - const fieldDef = context.getFieldDef(); - invariant(parentType != null && fieldDef != null); - context.reportError( - new GraphQLError( - `The argument "${parentType}.${fieldDef.name}(${argDef.name}:)" is deprecated. ${deprecationReason}`, - { nodes: node }, - ), - ); - } + context.reportError( + new GraphQLError( + `The argument "${argDef}" is deprecated. ${deprecationReason}`, + { nodes: node }, + ), + ); } }, ObjectField(node) { @@ -67,7 +50,7 @@ export function NoDeprecatedCustomRule(context: ValidationContext): ASTVisitor { if (deprecationReason != null) { context.reportError( new GraphQLError( - `The input field ${inputObjectDef.name}.${inputFieldDef.name} is deprecated. ${deprecationReason}`, + `The input field ${inputFieldDef} is deprecated. ${deprecationReason}`, { nodes: node }, ), ); @@ -78,11 +61,9 @@ export function NoDeprecatedCustomRule(context: ValidationContext): ASTVisitor { const enumValueDef = context.getEnumValue(); const deprecationReason = enumValueDef?.deprecationReason; if (enumValueDef && deprecationReason != null) { - const enumTypeDef = getNamedType(context.getInputType()); - invariant(enumTypeDef != null); context.reportError( new GraphQLError( - `The enum value "${enumTypeDef.name}.${enumValueDef.name}" is deprecated. ${deprecationReason}`, + `The enum value "${enumValueDef}" is deprecated. ${deprecationReason}`, { nodes: node }, ), );