Skip to content

Commit

Permalink
#1380 Include __typename in fragments and metadata fields
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilkisiela authored and dotansimha committed Mar 6, 2019
1 parent e884404 commit 1cbb746
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 4 deletions.
134 changes: 134 additions & 0 deletions packages/plugins/typescript-documents/tests/ts-documents.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -711,5 +711,139 @@ describe('TypeScript Documents Plugin', async () => {
`)
);
});

it('should use __typename in fragments when requested', async () => {
const testSchema = makeExecutableSchema({
typeDefs: parse(/* GraphQL */ `
type Post {
title: String
}
type Query {
post: Post!
}
`)
});
const query = parse(/* GraphQL */ `
query Post {
post {
... on Post {
__typename
}
}
}
`);

const content = await plugin(
testSchema,
[{ filePath: '', content: query }],
{},
{
outputFile: 'graphql.ts'
}
);

expect(format(content)).toBeSimilarStringTo(
format(`
export type PostQuery = { __typename?: 'Query' } & { post: { __typename?: 'Post' } & ({ __typename: 'Post' }) };
`)
);
});

it('should handle introspection types (__schema)', async () => {
const testSchema = makeExecutableSchema({
typeDefs: parse(/* GraphQL */ `
type Post {
title: String
}
type Query {
post: Post!
}
`)
});
const query = parse(/* GraphQL */ `
query Info {
__schema {
queryType {
fields {
name
}
}
}
}
`);

const content = await plugin(
testSchema,
[{ filePath: '', content: query }],
{},
{
outputFile: 'graphql.ts'
}
);

expect(format(content)).toBeSimilarStringTo(
format(`
export type InfoQuery = { __typename?: 'Query' } & {
__schema: { __typename?: '__Schema' } & {
queryType: { __typename?: '__Type' } & { fields: Maybe<Array<{ __typename?: '__Field' } & Pick<__Field, 'name'>>> };
};
};
`)
);
});

it('should handle introspection types (__type)', async () => {
const testSchema = makeExecutableSchema({
typeDefs: parse(/* GraphQL */ `
type Post {
title: String
}
type Query {
post: Post!
}
`)
});
const query = parse(/* GraphQL */ `
query Info {
__type(name: "Post") {
name
fields {
name
type {
name
kind
}
}
}
}
`);

const content = await plugin(
testSchema,
[{ filePath: '', content: query }],
{},
{
outputFile: 'graphql.ts'
}
);

expect(format(content)).toBeSimilarStringTo(
format(`
export type InfoQuery = { __typename?: 'Query' } & {
__type: Maybe<
{ __typename?: '__Type' } & Pick<__Type, 'name'> & {
fields: Maybe<
Array<
{ __typename?: '__Field' } & Pick<__Field, 'name'> & {
type: { __typename?: '__Type' } & Pick<__Type, 'name' | 'kind'>;
}
>
>;
}
>;
};
`)
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,23 @@ import {
FragmentDefinitionNode,
GraphQLObjectType,
OperationDefinitionNode,
VariableDefinitionNode
VariableDefinitionNode,
OperationTypeNode
} from 'graphql';
import { SelectionSetToObject } from './selection-set-to-object';
import { OperationVariablesToObject } from './variables-to-object';

function getRootType(operation: OperationTypeNode, schema: GraphQLSchema) {
switch (operation) {
case 'query':
return schema.getQueryType();
case 'mutation':
return schema.getMutationType();
case 'subscription':
return schema.getSubscriptionType();
}
}

export interface ParsedDocumentsConfig {
scalars: ScalarsMap;
convert: (str: string) => string;
Expand Down Expand Up @@ -108,7 +120,7 @@ export class BaseDocumentsVisitor<

OperationDefinition = (node: OperationDefinitionNode): string => {
const name = this.handleAnonymouseOperation(node.name && node.name.value ? node.name.value : null);
const operationRootType = this._schema.getType(toPascalCase(node.operation)) as GraphQLObjectType;
const operationRootType = getRootType(node.operation, this._schema);
const selectionSet = this._selectionSetToObject.createNext(operationRootType, node.selectionSet);
const visitedOperationVariables = this._variablesTransfomer.transform<VariableDefinitionNode>(
node.variableDefinitions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import {
isUnionType,
isInterfaceType,
isEnumType,
GraphQLSchema
GraphQLSchema,
isEqualType,
GraphQLField,
SchemaMetaFieldDef,
TypeMetaFieldDef
} from 'graphql';
import { getBaseType, quoteIfNeeded } from './utils';
import { ScalarsMap, ConvertNameFn } from './types';
Expand All @@ -21,6 +25,23 @@ export type LinkField = { alias: string; name: string; type: string; selectionSe
export type FragmentSpreadField = string;
export type InlineFragmentField = { [onType: string]: string[] };

function isMetadataFieldName(name: string) {
return ['__schema', '__type'].includes(name);
}

function isRootType(type: GraphQLNamedType, schema: GraphQLSchema): type is GraphQLObjectType {
return (
isEqualType(type, schema.getQueryType()) ||
isEqualType(type, schema.getMutationType()) ||
isEqualType(type, schema.getSubscriptionType())
);
}

const metadataFieldMap: Record<string, GraphQLField<any, any>> = {
__schema: SchemaMetaFieldDef,
__type: TypeMetaFieldDef
};

export class SelectionSetToObject {
protected _primitiveFields: PrimitiveField[] = [];
protected _primitiveAliasedFields: PrimitiveAliasedFields[] = [];
Expand Down Expand Up @@ -57,7 +78,14 @@ export class SelectionSetToObject {
}

if (isObjectType(this._parentSchemaType) || isInterfaceType(this._parentSchemaType)) {
const schemaField = this._parentSchemaType.getFields()[field.name.value];
let schemaField: GraphQLField<any, any>;

if (isRootType(this._parentSchemaType, this._schema) && isMetadataFieldName(field.name.value)) {
schemaField = metadataFieldMap[field.name.value];
} else {
schemaField = this._parentSchemaType.getFields()[field.name.value];
}

const rawType = schemaField.type as any;
const baseType = getBaseType(rawType);
const typeName = baseType.name;
Expand Down

0 comments on commit 1cbb746

Please sign in to comment.