Skip to content

Commit 0b109a9

Browse files
authored
feat: support custom directives (#134)
1 parent d6294c5 commit 0b109a9

File tree

7 files changed

+151
-6
lines changed

7 files changed

+151
-6
lines changed

src/annotations/build-directive-annotations.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { ConstDirectiveNode } from "graphql/language";
1717
import { GraphQLSchema, isInputObjectType, Kind } from "graphql";
1818
import { shouldConsolidateTypes } from "../utils/should-consolidate-types";
1919
import { sanitizeName } from "../utils/sanitize-name";
20+
import { titleCase } from "../utils/title-case";
2021

2122
export function buildDirectiveAnnotations(
2223
definitionNode: DefinitionNode,
@@ -50,17 +51,32 @@ export function buildDirectiveAnnotations(
5051
return !typeWillBeConsolidated;
5152
},
5253
);
53-
if (!directiveReplacementFromConfig) return "";
54-
const kotlinAnnotations = buildKotlinAnnotations(
55-
directive,
56-
directiveReplacementFromConfig.kotlinAnnotations,
54+
55+
if (directiveReplacementFromConfig) {
56+
return (
57+
buildKotlinAnnotationsFromConfig(
58+
directive,
59+
directiveReplacementFromConfig.kotlinAnnotations,
60+
).join("\n") + "\n"
61+
);
62+
}
63+
const customDirectiveFromConfig = config.customDirectives?.find(
64+
(directive) => directive === directiveName,
5765
);
58-
return kotlinAnnotations.join("\n") + "\n";
66+
if (customDirectiveFromConfig) {
67+
return buildCustomDirective(directive);
68+
}
69+
return "";
5970
})
6071
.join("");
6172
}
6273

63-
function buildKotlinAnnotations(
74+
function buildCustomDirective(directive: ConstDirectiveNode) {
75+
const directiveName = directive.name.value;
76+
return `@${titleCase(directiveName)}\n`;
77+
}
78+
79+
function buildKotlinAnnotationsFromConfig(
6480
directive: ConstDirectiveNode,
6581
kotlinAnnotations: NonNullable<
6682
CodegenConfigWithDefaults["directiveReplacements"]

src/config/schema.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ export const configSchema = object({
3232
* @link https://opensource.expediagroup.com/graphql-kotlin-codegen/docs/class-consolidation
3333
*/
3434
classConsolidationEnabled: optional(boolean()),
35+
/**
36+
* Denotes directives to generate as @GraphQLDirective annotations.
37+
*
38+
* Directive arguments are not yet supported and will be ignored.
39+
*
40+
* @example ["myCustomDirective"]
41+
*
42+
* @link https://opensource.expediagroup.com/graphql-kotlin/docs/schema-generator/customizing-schemas/directives/#custom-directives
43+
*/
44+
customDirectives: optional(array(string())),
3545
/**
3646
* Limits dependent types to include from `onlyTypes` list. Can be used to exclude classes that are imported from external packages.
3747
*

src/definitions/directive.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
Copyright 2024 Expedia, Inc.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
https://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
import { DirectiveDefinitionNode } from "graphql";
15+
import { CodegenConfigWithDefaults } from "../config/build-config-with-defaults";
16+
import { titleCase } from "../utils/title-case";
17+
18+
export function buildDirectiveDefinition(
19+
node: DirectiveDefinitionNode,
20+
config: CodegenConfigWithDefaults,
21+
): string {
22+
const directiveName = node.name.value;
23+
const isCustomDirective = config.customDirectives?.includes(directiveName);
24+
if (!isCustomDirective) {
25+
return "";
26+
}
27+
return `@GraphQLDirective(
28+
name = "${titleCase(directiveName)}",
29+
description = "${node.description?.value ?? ""}",
30+
locations = [${node.locations.map((location) => `graphql.introspection.Introspection.DirectiveLocation.${location.value}`).join(", ")}]
31+
)
32+
annotation class ${titleCase(directiveName)}`;
33+
}

src/visitor.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ limitations under the License.
1313

1414
import { BaseVisitor, RawConfig } from "@graphql-codegen/visitor-plugin-common";
1515
import {
16+
DirectiveDefinitionNode,
1617
EnumTypeDefinitionNode,
1718
GraphQLSchema,
1819
InputObjectTypeDefinitionNode,
@@ -21,6 +22,7 @@ import {
2122
UnionTypeDefinitionNode,
2223
} from "graphql";
2324
import { CodegenConfigWithDefaults } from "./config/build-config-with-defaults";
25+
import { buildDirectiveDefinition } from "./definitions/directive";
2426
import { buildEnumTypeDefinition } from "./definitions/enum";
2527
import { buildInterfaceDefinition } from "./definitions/interface";
2628
import { buildInputObjectDefinition } from "./definitions/input";
@@ -39,6 +41,10 @@ export class KotlinVisitor extends BaseVisitor<
3941
super(rawConfig, rawConfig);
4042
}
4143

44+
DirectiveDefinition(node: DirectiveDefinitionNode): string {
45+
return buildDirectiveDefinition(node, this.config);
46+
}
47+
4248
EnumTypeDefinition(node: EnumTypeDefinitionNode): string {
4349
return buildEnumTypeDefinition(node, this._schema, this.config);
4450
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { GraphQLKotlinCodegenConfig } from "../../../src/plugin";
2+
3+
export default {
4+
customDirectives: ["myCustomDirective", "myCustomDirective2"],
5+
extraImports: ["should_honor_directiveReplacements_config.*"],
6+
directiveReplacements: [
7+
{
8+
directive: "someDirective1",
9+
kotlinAnnotations: ["@SomeAnnotation1"],
10+
},
11+
],
12+
} satisfies GraphQLKotlinCodegenConfig;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.kotlin.generated
2+
3+
import com.expediagroup.graphql.generator.annotations.*
4+
import should_honor_directiveReplacements_config.*
5+
6+
@GraphQLDirective(
7+
name = "MyCustomDirective",
8+
description = "A description for MyCustomDirective",
9+
locations = [graphql.introspection.Introspection.DirectiveLocation.OBJECT, graphql.introspection.Introspection.DirectiveLocation.FIELD_DEFINITION, graphql.introspection.Introspection.DirectiveLocation.INPUT_OBJECT, graphql.introspection.Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION]
10+
)
11+
annotation class MyCustomDirective
12+
13+
@GraphQLDirective(
14+
name = "MyCustomDirective2",
15+
description = "",
16+
locations = [graphql.introspection.Introspection.DirectiveLocation.OBJECT, graphql.introspection.Introspection.DirectiveLocation.FIELD_DEFINITION, graphql.introspection.Introspection.DirectiveLocation.INPUT_OBJECT, graphql.introspection.Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION]
17+
)
18+
annotation class MyCustomDirective2
19+
20+
@MyCustomDirective
21+
@MyCustomDirective2
22+
@SomeAnnotation1
23+
@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
24+
data class MyTypeWithCustomDirectiveOnObject(
25+
val field: String? = null
26+
)
27+
28+
@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
29+
data class MyTypeWithCustomDirectiveOnField(
30+
@MyCustomDirective
31+
val field: String? = null
32+
)
33+
34+
@MyCustomDirective
35+
@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
36+
data class MyInputWithCustomDirectiveOnObject(
37+
val field: String? = null
38+
)
39+
40+
@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
41+
data class MyInputWithCustomDirectiveOnField(
42+
@MyCustomDirective
43+
val field: String? = null
44+
)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"A description for MyCustomDirective"
2+
directive @myCustomDirective on OBJECT | FIELD_DEFINITION | INPUT_OBJECT | INPUT_FIELD_DEFINITION
3+
directive @myCustomDirective2 on OBJECT | FIELD_DEFINITION | INPUT_OBJECT | INPUT_FIELD_DEFINITION
4+
5+
directive @someDirective1 on OBJECT
6+
7+
type MyTypeWithCustomDirectiveOnObject
8+
@myCustomDirective
9+
@myCustomDirective2
10+
@someDirective1 {
11+
field: String
12+
}
13+
14+
type MyTypeWithCustomDirectiveOnField {
15+
field: String @myCustomDirective
16+
}
17+
18+
input MyInputWithCustomDirectiveOnObject @myCustomDirective {
19+
field: String
20+
}
21+
22+
input MyInputWithCustomDirectiveOnField {
23+
field: String @myCustomDirective
24+
}

0 commit comments

Comments
 (0)