Skip to content
Open
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 src/decorators/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ export function registerEnumType<TEnum extends object>(
name: enumConfig.name,
description: enumConfig.description,
valuesConfig: enumConfig.valuesConfig || {},
directives: enumConfig.directives?.map(nameOrDefinition => ({ nameOrDefinition, args: {} })),
});
}
1 change: 1 addition & 0 deletions src/decorators/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export interface EnumConfig<TEnum extends object> {
name: string;
description?: string;
valuesConfig?: EnumValuesConfig<TEnum>;
directives?: string[];
}
export type EnumValuesConfig<TEnum extends object> = Partial<
Record<keyof TEnum, DescriptionOptions & DeprecationOptions>
Expand Down
2 changes: 2 additions & 0 deletions src/metadata/definitions/enum-metadata.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { type EnumValuesConfig } from "@/decorators/types";
import { type DirectiveMetadata } from "./directive-metadata";

export interface EnumMetadata {
enumObj: object;
name: string;
description: string | undefined;
valuesConfig: EnumValuesConfig<any>;
directives?: DirectiveMetadata[];
}
19 changes: 19 additions & 0 deletions src/schema/definition-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
type ConstArgumentNode,
type ConstDirectiveNode,
type DocumentNode,
type EnumTypeDefinitionNode,
type FieldDefinitionNode,
type GraphQLInputType,
type GraphQLOutputType,
Expand Down Expand Up @@ -181,3 +182,21 @@ export function getInterfaceTypeDefinitionNode(
directives: directiveMetadata.map(getDirectiveNode),
};
}

export function getEnumTypeDefinitionNode(
name: string,
directiveMetadata?: DirectiveMetadata[],
): EnumTypeDefinitionNode | undefined {
if (!directiveMetadata || !directiveMetadata.length) {
return undefined;
}

return {
kind: Kind.ENUM_TYPE_DEFINITION,
name: {
kind: Kind.NAME,
value: name,
},
directives: directiveMetadata.map(getDirectiveNode),
};
}
2 changes: 2 additions & 0 deletions src/schema/schema-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import {
import { ensureInstalledCorrectGraphQLPackage } from "@/utils/graphql-version";
import { BuildContext, type BuildContextOptions } from "./build-context";
import {
getEnumTypeDefinitionNode,
getFieldDefinitionNode,
getInputObjectTypeDefinitionNode,
getInputValueDefinitionNode,
Expand Down Expand Up @@ -255,6 +256,7 @@ export abstract class SchemaGenerator {
type: new GraphQLEnumType({
name: enumMetadata.name,
description: enumMetadata.description,
astNode: getEnumTypeDefinitionNode(enumMetadata.name, enumMetadata.directives),
values: Object.keys(enumMap).reduce<GraphQLEnumValueConfigMap>(
(enumConfig, enumKey) => {
const valueConfig = enumMetadata.valuesConfig[enumKey] || {};
Expand Down
94 changes: 94 additions & 0 deletions tests/functional/enums.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import "reflect-metadata";
import {
type GraphQLEnumType,
type GraphQLSchema,
type IntrospectionEnumType,
type IntrospectionInputObjectType,
Expand All @@ -10,6 +11,7 @@ import {
} from "graphql";
import { Arg, Field, InputType, Query, registerEnumType } from "type-graphql";
import { getMetadataStorage } from "@/metadata/getMetadataStorage";
import { assertValidDirective } from "../helpers/directives/assertValidDirective";
import {
getInnerInputFieldType,
getInnerTypeOfNonNullableType,
Expand Down Expand Up @@ -51,6 +53,15 @@ describe("Enums", () => {
},
});

enum DirectiveEnum {
Active = "ACTIVE",
Inactive = "INACTIVE",
}
registerEnumType(DirectiveEnum, {
name: "DirectiveEnum",
directives: ["@test"],
});

@InputType()
class NumberEnumInput {
@Field(() => NumberEnum)
Expand Down Expand Up @@ -88,6 +99,11 @@ describe("Enums", () => {
isStringEnumEqualOne(@Arg("enum", () => StringEnum) stringEnum: StringEnum): boolean {
return stringEnum === StringEnum.One;
}

@Query(() => DirectiveEnum)
getDirectiveEnumValue(): DirectiveEnum {
return DirectiveEnum.Active;
}
}

const schemaInfo = await getSchemaInfo({
Expand Down Expand Up @@ -201,6 +217,84 @@ describe("Enums", () => {
"Two field deprecation reason",
);
});

it("should properly emit directive in AST when directives are provided", async () => {
const enumType = schema.getType("DirectiveEnum") as GraphQLEnumType;

expect(enumType).toBeDefined();
expect(enumType.astNode).toBeDefined();
assertValidDirective(enumType.astNode, "test");
});

it("should leave astNode undefined when no directives are provided", async () => {
const enumType = schema.getType("NumberEnum") as GraphQLEnumType;

expect(enumType).toBeDefined();
expect(enumType.astNode).toBeUndefined();
});

it("should properly emit directive with args in AST", async () => {
getMetadataStorage().clear();

enum ArgsDirectiveEnum {
On = "ON",
Off = "OFF",
}
registerEnumType(ArgsDirectiveEnum, {
name: "ArgsDirectiveEnum",
directives: ['@test(argNonNullDefault: "custom", argNull: "value")'],
});

class ArgsDirectiveEnumResolver {
@Query(() => ArgsDirectiveEnum)
getArgsDirectiveEnumValue(): ArgsDirectiveEnum {
return ArgsDirectiveEnum.On;
}
}

const { schema: argsSchema } = await getSchemaInfo({
resolvers: [ArgsDirectiveEnumResolver],
});

const enumType = argsSchema.getType("ArgsDirectiveEnum") as GraphQLEnumType;
expect(enumType.astNode).toBeDefined();
assertValidDirective(enumType.astNode, "test", {
argNonNullDefault: `"custom"`,
argNull: `"value"`,
});
});

it("should properly emit multiple directives in AST", async () => {
getMetadataStorage().clear();

enum MultiDirectiveEnum {
Yes = "YES",
No = "NO",
}
registerEnumType(MultiDirectiveEnum, {
name: "MultiDirectiveEnum",
directives: ["@test", '@deprecated(reason: "use something else")'],
});

class MultiDirectiveEnumResolver {
@Query(() => MultiDirectiveEnum)
getMultiDirectiveEnumValue(): MultiDirectiveEnum {
return MultiDirectiveEnum.Yes;
}
}

const { schema: multiSchema } = await getSchemaInfo({
resolvers: [MultiDirectiveEnumResolver],
});

const enumType = multiSchema.getType("MultiDirectiveEnum") as GraphQLEnumType;
expect(enumType.astNode).toBeDefined();
expect(enumType.astNode!.directives).toHaveLength(2);
assertValidDirective(enumType.astNode, "test");
assertValidDirective(enumType.astNode, "deprecated", {
reason: `"use something else"`,
});
});
});

describe("Functional", () => {
Expand Down
2 changes: 2 additions & 0 deletions tests/helpers/directives/assertValidDirective.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
type EnumTypeDefinitionNode,
type FieldDefinitionNode,
type InputObjectTypeDefinitionNode,
type InputValueDefinitionNode,
Expand All @@ -10,6 +11,7 @@ import { type Maybe } from "@/typings";

export function assertValidDirective(
astNode: Maybe<
| EnumTypeDefinitionNode
| FieldDefinitionNode
| ObjectTypeDefinitionNode
| InputObjectTypeDefinitionNode
Expand Down