Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cspell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ ignoreRegExpList:

words:
- graphiql
- metafield
- uncoerce
- uncoerced

Expand Down
54 changes: 54 additions & 0 deletions src/language/__tests__/parser-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,60 @@ describe('Parser', () => {
});
});

it('parses __Type', () => {
const result = parseSchemaCoordinate('__Type');
expectJSON(result).toDeepEqual({
kind: Kind.TYPE_COORDINATE,
loc: { start: 0, end: 6 },
name: {
kind: Kind.NAME,
loc: { start: 0, end: 6 },
value: '__Type',
},
});
});

it('parses Type.__metafield', () => {
const result = parseSchemaCoordinate('Type.__metafield');
expectJSON(result).toDeepEqual({
kind: Kind.MEMBER_COORDINATE,
loc: { start: 0, end: 16 },
name: {
kind: Kind.NAME,
loc: { start: 0, end: 4 },
value: 'Type',
},
memberName: {
kind: Kind.NAME,
loc: { start: 5, end: 16 },
value: '__metafield',
},
});
});

it('parses Type.__metafield(arg:)', () => {
const result = parseSchemaCoordinate('Type.__metafield(arg:)');
expectJSON(result).toDeepEqual({
kind: Kind.ARGUMENT_COORDINATE,
loc: { start: 0, end: 22 },
name: {
kind: Kind.NAME,
loc: { start: 0, end: 4 },
value: 'Type',
},
fieldName: {
kind: Kind.NAME,
loc: { start: 5, end: 16 },
value: '__metafield',
},
argumentName: {
kind: Kind.NAME,
loc: { start: 17, end: 20 },
value: 'arg',
},
});
});

it('rejects @ Name . Name', () => {
expect(() => parseSchemaCoordinate('@myDirective.field'))
.to.throw()
Expand Down
7 changes: 7 additions & 0 deletions src/language/__tests__/printer-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,13 @@ describe('Printer: Query document', () => {
);
expect(print(parseSchemaCoordinate('@name'))).to.equal('@name');
expect(print(parseSchemaCoordinate('@name(arg:)'))).to.equal('@name(arg:)');
expect(print(parseSchemaCoordinate('__Type'))).to.equal('__Type');
expect(print(parseSchemaCoordinate('Type.__metafield'))).to.equal(
'Type.__metafield',
);
expect(print(parseSchemaCoordinate('Type.__metafield(arg:)'))).to.equal(
'Type.__metafield(arg:)',
);
});

it('throws syntax error for ignored tokens in schema coordinates', () => {
Expand Down
62 changes: 36 additions & 26 deletions src/utilities/__tests__/resolveSchemaCoordinate-test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect } from 'chai';
import { assert, expect } from 'chai';
import { describe, it } from 'mocha';

import type {
Expand All @@ -12,32 +12,32 @@ import type { GraphQLDirective } from '../../type/directives.js';
import { buildSchema } from '../buildASTSchema.js';
import { resolveSchemaCoordinate } from '../resolveSchemaCoordinate.js';

describe('resolveSchemaCoordinate', () => {
const schema = buildSchema(`
type Query {
searchBusiness(criteria: SearchCriteria!): [Business]
}

input SearchCriteria {
name: String
filter: SearchFilter
}

enum SearchFilter {
OPEN_NOW
DELIVERS_TAKEOUT
VEGETARIAN_MENU
}

type Business {
id: ID
name: String
email: String @private(scope: "loggedIn")
}

directive @private(scope: String!) on FIELD_DEFINITION
`);
const schema = buildSchema(`
type Query {
searchBusiness(criteria: SearchCriteria!): [Business]
}

input SearchCriteria {
name: String
filter: SearchFilter
}

enum SearchFilter {
OPEN_NOW
DELIVERS_TAKEOUT
VEGETARIAN_MENU
}

type Business {
id: ID
name: String
email: String @private(scope: "loggedIn")
}

directive @private(scope: String!) on FIELD_DEFINITION
`);

describe('resolveSchemaCoordinate', () => {
it('resolves a Named Type', () => {
expect(resolveSchemaCoordinate(schema, 'Business')).to.deep.equal({
kind: 'NamedType',
Expand Down Expand Up @@ -181,10 +181,20 @@ describe('resolveSchemaCoordinate', () => {
'Expected "unknown" to be defined as a directive in the schema.',
);
});
});

/*
* NOTE: the following are not required for spec compliance; resolution
* of meta-fields is implementation-defined.
*
* These tests are here to ensure a change of behavior will only be made
* in a semver-major release of GraphQL.js.
*/
describe('resolveSchemaCoordinate (meta-fields and introspection types)', () => {
it('resolves a meta-field', () => {
const type = schema.getType('Business') as GraphQLObjectType;
const field = schema.getField(type, '__typename');
assert.ok(field);
expect(
resolveSchemaCoordinate(schema, 'Business.__typename'),
).to.deep.equal({
Expand Down
18 changes: 12 additions & 6 deletions src/utilities/resolveSchemaCoordinate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ import type { Source } from '../language/source.js';

import type {
GraphQLArgument,
GraphQLEnumType,
GraphQLEnumValue,
GraphQLField,
GraphQLInputField,
GraphQLInputObjectType,
GraphQLInterfaceType,
GraphQLNamedType,
GraphQLObjectType,
} from '../type/definition.js';
import {
isEnumType,
Expand All @@ -38,25 +42,25 @@ export interface ResolvedNamedType {

export interface ResolvedField {
readonly kind: 'Field';
readonly type: GraphQLNamedType;
readonly type: GraphQLObjectType | GraphQLInterfaceType;
readonly field: GraphQLField<unknown, unknown>;
}

export interface ResolvedInputField {
readonly kind: 'InputField';
readonly type: GraphQLNamedType;
readonly type: GraphQLInputObjectType;
readonly inputField: GraphQLInputField;
}

export interface ResolvedEnumValue {
readonly kind: 'EnumValue';
readonly type: GraphQLNamedType;
readonly type: GraphQLEnumType;
readonly enumValue: GraphQLEnumValue;
}

export interface ResolvedFieldArgument {
readonly kind: 'FieldArgument';
readonly type: GraphQLNamedType;
readonly type: GraphQLObjectType | GraphQLInterfaceType;
readonly field: GraphQLField<unknown, unknown>;
readonly fieldArgument: GraphQLArgument;
}
Expand All @@ -83,8 +87,10 @@ export type ResolvedSchemaElement =

/**
* A schema coordinate is resolved in the context of a GraphQL schema to
* uniquely identifies a schema element. It returns undefined if the schema
* coordinate does not resolve to a schema element.
* uniquely identify a schema element. It returns undefined if the schema
* coordinate does not resolve to a schema element, meta-field, or introspection
* schema element. It will throw if the containing schema element (if
* applicable) does not exist.
*
* https://spec.graphql.org/draft/#sec-Schema-Coordinates.Semantics
*/
Expand Down
Loading