import { inspect } from '../jsutils/inspect';
import type {
ArgumentCoordinateNode,
DirectiveArgumentCoordinateNode,
DirectiveCoordinateNode,
MemberCoordinateNode,
SchemaCoordinateNode,
TypeCoordinateNode,
} from '../language/ast';
import { Kind } from '../language/kinds';
import { parseSchemaCoordinate } from '../language/parser';
import type { Source } from '../language/source';
import type {
GraphQLArgument,
GraphQLEnumType,
GraphQLEnumValue,
GraphQLField,
GraphQLInputField,
GraphQLInputObjectType,
GraphQLInterfaceType,
GraphQLNamedType,
GraphQLObjectType,
} from '../type/definition';
import {
isEnumType,
isInputObjectType,
isInterfaceType,
isObjectType,
} from '../type/definition';
import type { GraphQLDirective } from '../type/directives';
import type { GraphQLSchema } from '../type/schema';
export interface ResolvedNamedType {
readonly kind: 'NamedType';
readonly type: GraphQLNamedType;
}
export interface ResolvedField {
readonly kind: 'Field';
readonly type: GraphQLObjectType | GraphQLInterfaceType;
readonly field: GraphQLField<unknown, unknown>;
}
export interface ResolvedInputField {
readonly kind: 'InputField';
readonly type: GraphQLInputObjectType;
readonly inputField: GraphQLInputField;
}
export interface ResolvedEnumValue {
readonly kind: 'EnumValue';
readonly type: GraphQLEnumType;
readonly enumValue: GraphQLEnumValue;
}
export interface ResolvedFieldArgument {
readonly kind: 'FieldArgument';
readonly type: GraphQLObjectType | GraphQLInterfaceType;
readonly field: GraphQLField<unknown, unknown>;
readonly fieldArgument: GraphQLArgument;
}
export interface ResolvedDirective {
readonly kind: 'Directive';
readonly directive: GraphQLDirective;
}
export interface ResolvedDirectiveArgument {
readonly kind: 'DirectiveArgument';
readonly directive: GraphQLDirective;
readonly directiveArgument: GraphQLArgument;
}
export type ResolvedSchemaElement =
| ResolvedNamedType
| ResolvedField
| ResolvedInputField
| ResolvedEnumValue
| ResolvedFieldArgument
| ResolvedDirective
| ResolvedDirectiveArgument;
export function resolveSchemaCoordinate(
schema: GraphQLSchema,
schemaCoordinate: string | Source,
): ResolvedSchemaElement | undefined {
return resolveASTSchemaCoordinate(
schema,
parseSchemaCoordinate(schemaCoordinate),
);
}
function resolveTypeCoordinate(
schema: GraphQLSchema,
schemaCoordinate: TypeCoordinateNode,
): ResolvedNamedType | undefined {
const typeName = schemaCoordinate.name.value;
const type = schema.getType(typeName);
if (type == null) {
return;
}
return { kind: 'NamedType', type };
}
function resolveMemberCoordinate(
schema: GraphQLSchema,
schemaCoordinate: MemberCoordinateNode,
): ResolvedField | ResolvedInputField | ResolvedEnumValue | undefined {
const typeName = schemaCoordinate.name.value;
const type = schema.getType(typeName);
if (!type) {
throw new Error(
`Expected ${inspect(typeName)} to be defined as a type in the schema.`,
);
}
if (
!isEnumType(type) &&
!isInputObjectType(type) &&
!isObjectType(type) &&
!isInterfaceType(type)
) {
throw new Error(
`Expected ${inspect(
typeName,
)} to be an Enum, Input Object, Object or Interface type.`,
);
}
if (isEnumType(type)) {
const enumValueName = schemaCoordinate.memberName.value;
const enumValue = type.getValue(enumValueName);
if (enumValue == null) {
return;
}
return { kind: 'EnumValue', type, enumValue };
}
if (isInputObjectType(type)) {
const inputFieldName = schemaCoordinate.memberName.value;
const inputField = type.getFields()[inputFieldName];
if (inputField == null) {
return;
}
return { kind: 'InputField', type, inputField };
}
const fieldName = schemaCoordinate.memberName.value;
const field = type.getFields()[fieldName];
if (field == null) {
return;
}
return { kind: 'Field', type, field };
}
function resolveArgumentCoordinate(
schema: GraphQLSchema,
schemaCoordinate: ArgumentCoordinateNode,
): ResolvedFieldArgument | undefined {
const typeName = schemaCoordinate.name.value;
const type = schema.getType(typeName);
if (type == null) {
throw new Error(
`Expected ${inspect(typeName)} to be defined as a type in the schema.`,
);
}
if (!isObjectType(type) && !isInterfaceType(type)) {
throw new Error(
`Expected ${inspect(typeName)} to be an object type or interface type.`,
);
}
const fieldName = schemaCoordinate.fieldName.value;
const field = type.getFields()[fieldName];
if (field == null) {
throw new Error(
`Expected ${inspect(fieldName)} to exist as a field of type ${inspect(
typeName,
)} in the schema.`,
);
}
const fieldArgumentName = schemaCoordinate.argumentName.value;
const fieldArgument = field.args.find(
(arg: GraphQLArgument) => arg.name === fieldArgumentName,
);
if (fieldArgument == null) {
return;
}
return { kind: 'FieldArgument', type, field, fieldArgument };
}
function resolveDirectiveCoordinate(
schema: GraphQLSchema,
schemaCoordinate: DirectiveCoordinateNode,
): ResolvedDirective | undefined {
const directiveName = schemaCoordinate.name.value;
const directive = schema.getDirective(directiveName);
if (!directive) {
return;
}
return { kind: 'Directive', directive };
}
function resolveDirectiveArgumentCoordinate(
schema: GraphQLSchema,
schemaCoordinate: DirectiveArgumentCoordinateNode,
): ResolvedDirectiveArgument | undefined {
const directiveName = schemaCoordinate.name.value;
const directive = schema.getDirective(directiveName);
if (!directive) {
throw new Error(
`Expected ${inspect(
directiveName,
)} to be defined as a directive in the schema.`,
);
}
const {
argumentName: { value: directiveArgumentName },
} = schemaCoordinate;
const directiveArgument = directive.args.find(
(arg) => arg.name === directiveArgumentName,
);
if (!directiveArgument) {
return;
}
return { kind: 'DirectiveArgument', directive, directiveArgument };
}
export function resolveASTSchemaCoordinate(
schema: GraphQLSchema,
schemaCoordinate: SchemaCoordinateNode,
): ResolvedSchemaElement | undefined {
switch (schemaCoordinate.kind) {
case Kind.TYPE_COORDINATE:
return resolveTypeCoordinate(schema, schemaCoordinate);
case Kind.MEMBER_COORDINATE:
return resolveMemberCoordinate(schema, schemaCoordinate);
case Kind.ARGUMENT_COORDINATE:
return resolveArgumentCoordinate(schema, schemaCoordinate);
case Kind.DIRECTIVE_COORDINATE:
return resolveDirectiveCoordinate(schema, schemaCoordinate);
case Kind.DIRECTIVE_ARGUMENT_COORDINATE:
return resolveDirectiveArgumentCoordinate(schema, schemaCoordinate);
}
}