Skip to content

Commit

Permalink
Normalize enum value passed as a string (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilkisiela authored Jun 13, 2024
1 parent 17b938a commit 2f7fef1
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changeset/a-b-c.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@theguild/federation-composition": patch
---

Normalize enum values to be printed as enum values in Supergraph SDL, even if the user's subgraph schema has them as strings
17 changes: 16 additions & 1 deletion __tests__/ast.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ConstDirectiveNode, Kind, parse } from 'graphql';
import { ConstDirectiveNode, Kind, TypeKind, parse } from 'graphql';
import { describe, expect, test } from 'vitest';
import {
createEnumTypeNode,
Expand All @@ -11,6 +11,7 @@ import {
createUnionTypeNode,
stripFederation,
} from '../src/supergraph/composition/ast.js';
import { ArgumentKind } from '../src/subgraph/state.js';

function createDirective(name: string): ConstDirectiveNode {
return {
Expand Down Expand Up @@ -366,6 +367,7 @@ describe('object type', () => {
{
name: 'units',
type: 'String!',
kind: ArgumentKind.SCALAR,
},
],
},
Expand Down Expand Up @@ -409,6 +411,7 @@ describe('object type', () => {
{
name: 'units',
type: 'Int!',
kind: TypeKind.SCALAR,
defaultValue: '1',
},
],
Expand Down Expand Up @@ -479,6 +482,7 @@ describe('object type', () => {
{
name: 'limit',
type: 'Int',
kind: ArgumentKind.SCALAR,
tags: ['public'],
},
],
Expand Down Expand Up @@ -506,6 +510,7 @@ describe('object type', () => {
{
name: 'limit',
type: 'Int',
kind: ArgumentKind.SCALAR,
inaccessible: true,
},
],
Expand Down Expand Up @@ -615,6 +620,7 @@ describe('interface type', () => {
{
name: 'units',
type: 'Int!',
kind: ArgumentKind.SCALAR,
},
],
},
Expand Down Expand Up @@ -659,6 +665,7 @@ describe('interface type', () => {
{
name: 'units',
type: 'Int!',
kind: ArgumentKind.SCALAR,
defaultValue: '1',
},
],
Expand All @@ -685,6 +692,7 @@ describe('interface type', () => {
{
name: 'units',
type: 'Int!',
kind: ArgumentKind.SCALAR,
tags: ['public'],
},
],
Expand Down Expand Up @@ -712,6 +720,7 @@ describe('interface type', () => {
{
name: 'limit',
type: 'Int',
kind: ArgumentKind.SCALAR,
inaccessible: true,
},
],
Expand Down Expand Up @@ -875,10 +884,16 @@ describe('input object type', () => {
type: 'Int',
defaultValue: '2',
},
{
name: 'obj',
type: 'Obj',
defaultValue: '{limit: 1}',
},
],
}),
).toEqualGraphQL(/* GraphQL */ `
input Filter {
obj: Obj = {limit: 1}
limit: Int = 2
}
`);
Expand Down
34 changes: 34 additions & 0 deletions __tests__/composition.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,40 @@ expect.addSnapshotSerializer({
testImplementations(api => {
const composeServices = api.composeServices;

test('enum value as string as default value', () => {
const result = composeServices([
{
name: 'a',
typeDefs: parse(/* GraphQL */ `
extend schema @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key"])
type User @key(fields: "id") {
id: ID!
name: String
}
type Query {
users(type: UserType! = "Regular"): [User!]!
}
enum UserType {
Regular
Admin
}
`),
},
]);

assertCompositionSuccess(result);


expect(result.supergraphSdl).toContainGraphQL(/* GraphQL */ `
type Query @join__type(graph: A) {
users(type: UserType! = Regular): [User!]!
}
`);
});

test('duplicated Query fields', () => {
const result = composeServices([
{
Expand Down
85 changes: 81 additions & 4 deletions src/subgraph/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ export enum TypeKind {
DIRECTIVE = 'DIRECTIVE',
}

export enum ArgumentKind {
SCALAR = 'SCALAR',
OBJECT = 'OBJECT',
ENUM = 'ENUM',
}

type SubgraphID = string;

export interface Directive {
Expand Down Expand Up @@ -185,6 +191,7 @@ export interface Field {
export interface InputField {
name: string;
type: string;
kind: ArgumentKind;
inaccessible: boolean;
tags: Set<string>;
defaultValue?: string;
Expand All @@ -206,6 +213,7 @@ export interface EnumValue {
export interface Argument {
name: string;
type: string;
kind: ArgumentKind;
inaccessible: boolean;
tags: Set<string>;
defaultValue?: string;
Expand Down Expand Up @@ -308,15 +316,23 @@ export function createSubgraphStateBuilder(
const schemaDef = typeDefs.definitions.find(isSchemaDefinition);

const leafTypeNames = new Set<string>(specifiedScalars);
const enumTypeNames = new Set<string>();
const inputObjectTypeNames = new Set<string>();

for (const typeDef of typeDefs.definitions) {
if (
if (typeDef.kind === Kind.ENUM_TYPE_DEFINITION || typeDef.kind === Kind.ENUM_TYPE_EXTENSION) {
enumTypeNames.add(typeDef.name.value);
leafTypeNames.add(typeDef.name.value);
} else if (
typeDef.kind === Kind.SCALAR_TYPE_DEFINITION ||
typeDef.kind === Kind.SCALAR_TYPE_EXTENSION ||
typeDef.kind === Kind.ENUM_TYPE_DEFINITION ||
typeDef.kind === Kind.ENUM_TYPE_EXTENSION
typeDef.kind === Kind.SCALAR_TYPE_EXTENSION
) {
leafTypeNames.add(typeDef.name.value);
} else if (
typeDef.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION ||
typeDef.kind === Kind.INPUT_OBJECT_TYPE_EXTENSION
) {
inputObjectTypeNames.add(typeDef.name.value);
}
}

Expand Down Expand Up @@ -350,6 +366,18 @@ export function createSubgraphStateBuilder(
const unionTypeBuilder = unionTypeFactory(state);
const enumTypeBuilder = enumTypeFactory(state);

function resolveArgumentKind(typeName: string): ArgumentKind {
if (enumTypeNames.has(typeName)) {
return ArgumentKind.ENUM;
}

if (inputObjectTypeNames.has(typeName)) {
return ArgumentKind.OBJECT;
}

return ArgumentKind.SCALAR;
}

function renameObjectType(typeName: string) {
if (typeName === expectedQueryTypeName) {
return 'Query';
Expand Down Expand Up @@ -500,6 +528,12 @@ export function createSubgraphStateBuilder(
printOutputType(node.type),
);

inputObjectTypeBuilder.field.setKind(
typeDef.name.value,
node.name.value,
resolveArgumentKind(outputTypeName),
);

if (referencesEnumType) {
enumTypeBuilder.setReferencedByInputType(
outputTypeName,
Expand All @@ -525,6 +559,13 @@ export function createSubgraphStateBuilder(
printOutputType(node.type),
);

objectTypeBuilder.field.arg.setKind(
typeDef.name.value,
fieldDef.name.value,
node.name.value,
resolveArgumentKind(outputTypeName),
);

if (node.defaultValue) {
objectTypeBuilder.field.arg.setDefaultValue(
typeDef.name.value,
Expand All @@ -551,6 +592,13 @@ export function createSubgraphStateBuilder(
printOutputType(node.type),
);

interfaceTypeBuilder.field.arg.setKind(
typeDef.name.value,
fieldDef.name.value,
node.name.value,
resolveArgumentKind(outputTypeName),
);

if (node.defaultValue) {
interfaceTypeBuilder.field.arg.setDefaultValue(
typeDef.name.value,
Expand Down Expand Up @@ -875,6 +923,14 @@ export function createSubgraphStateBuilder(
printOutputType(arg.type),
);

const outputTypeName = resolveTypeName(arg.type);

directiveBuilder.arg.setKind(
directiveName,
arg.name.value,
resolveArgumentKind(outputTypeName),
);

if (typeof arg.defaultValue !== 'undefined') {
directiveBuilder.arg.setDefaultValue(
directiveName,
Expand Down Expand Up @@ -1020,6 +1076,9 @@ function directiveFactory(state: SubgraphState) {
setType(directiveName: string, argName: string, argType: string) {
getOrCreateDirectiveArg(state, directiveName, argName).type = argType;
},
setKind(directiveName: string, argName: string, argKind: ArgumentKind) {
getOrCreateDirectiveArg(state, directiveName, argName).kind = argKind;
},
setDirective(typeName: string, argName: string, directive: DirectiveNode) {
getOrCreateDirectiveArg(state, typeName, argName).ast.directives.push(directive);
},
Expand Down Expand Up @@ -1352,6 +1411,14 @@ function objectTypeFactory(
getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).type =
argType;
},
setKind(typeName: string, fieldName: string, argName: string, argKind: ArgumentKind) {
if (isInterfaceObject(typeName)) {
return interfaceTypeBuilder.field.arg.setKind(typeName, fieldName, argName, argKind);
}

getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).kind =
argKind;
},
setDescription(
typeName: string,
fieldName: string,
Expand Down Expand Up @@ -1583,6 +1650,9 @@ function interfaceTypeFactory(state: SubgraphState) {
setType(typeName: string, fieldName: string, argName: string, argType: string) {
getOrCreateInterfaceFieldArgument(state, typeName, fieldName, argName).type = argType;
},
setKind(typeName: string, fieldName: string, argName: string, argKind: ArgumentKind) {
getOrCreateInterfaceFieldArgument(state, typeName, fieldName, argName).kind = argKind;
},
setDefaultValue(
typeName: string,
fieldName: string,
Expand Down Expand Up @@ -1661,6 +1731,9 @@ function inputObjectTypeFactory(state: SubgraphState) {
setType(typeName: string, fieldName: string, fieldType: string) {
getOrCreateInputObjectField(state, typeName, fieldName).type = fieldType;
},
setKind(typeName: string, fieldName: string, fieldKind: ArgumentKind) {
getOrCreateInputObjectField(state, typeName, fieldName).kind = fieldKind;
},
setDescription(typeName: string, fieldName: string, description: Description) {
getOrCreateInputObjectField(state, typeName, fieldName).description = description;
},
Expand Down Expand Up @@ -1800,6 +1873,7 @@ function getOrCreateDirectiveArg(
const arg: Argument = {
name: argName,
type: MISSING,
kind: ArgumentKind.SCALAR,
inaccessible: false,
tags: new Set(),
ast: {
Expand Down Expand Up @@ -2110,6 +2184,7 @@ function getOrCreateInputObjectField(
const field: InputField = {
name: fieldName,
type: MISSING,
kind: ArgumentKind.SCALAR,
inaccessible: false,
tags: new Set(),
ast: {
Expand Down Expand Up @@ -2164,6 +2239,7 @@ function getOrCreateObjectFieldArgument(
const arg: Argument = {
name: argName,
type: MISSING,
kind: ArgumentKind.SCALAR,
inaccessible: false,
tags: new Set(),
ast: {
Expand Down Expand Up @@ -2193,6 +2269,7 @@ function getOrCreateInterfaceFieldArgument(
const arg: Argument = {
name: argName,
type: MISSING,
kind: ArgumentKind.SCALAR,
inaccessible: false,
tags: new Set(),
ast: {
Expand Down
Loading

0 comments on commit 2f7fef1

Please sign in to comment.