diff --git a/src/config.ts b/src/config.ts index f0c214a..5a3a217 100644 --- a/src/config.ts +++ b/src/config.ts @@ -11,7 +11,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { array, boolean, enum_, object, optional, string } from "valibot"; +import { + array, + boolean, + enum_, + object, + optional, + string, + union, +} from "valibot"; import { Kind } from "graphql"; export const configSchema = object({ @@ -70,7 +78,22 @@ export const configSchema = object({ /** * A list of Kotlin annotations to replace the directive with. */ - kotlinAnnotations: array(string()), + kotlinAnnotations: array( + union([ + string(), + object({ + /** + * The name of the annotation to replace the directive with. + */ + annotationName: string(), + /** + * The arguments to forward from the directive directly to the Kotlin annotation. Can be INT, FLOAT, STRING, BOOLEAN, or ENUM. + * @example @YourGraphQLDirective(arg1: "value1") -> @YourKotlinAnnotation(arg1 = "value1") + */ + argumentsToRetain: array(string()), + }), + ]), + ), /** * The type definition to apply the directive replacement to. If omitted, the replacement will apply to all definition types. */ diff --git a/src/helpers/build-directive-annotations.ts b/src/helpers/build-directive-annotations.ts index 1c219d0..349c74c 100644 --- a/src/helpers/build-directive-annotations.ts +++ b/src/helpers/build-directive-annotations.ts @@ -11,10 +11,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { CodegenConfig } from "../plugin"; +import { CodegenConfig, GraphQLKotlinCodegenConfig } from "../plugin"; import { DefinitionNode, isDeprecatedDescription } from "./build-annotations"; import { getFederationDirectiveReplacement } from "./get-federation-directive-replacement"; import { TypeMetadata } from "./build-type-metadata"; +import { ConstDirectiveNode } from "graphql/language"; export function buildDirectiveAnnotations( incomingNode: DefinitionNode, @@ -57,7 +58,43 @@ export function buildDirectiveAnnotations( (!definitionType || definitionType === kind), ); if (!directiveReplacementFromConfig) return ""; - return directiveReplacementFromConfig.kotlinAnnotations.join("\n") + "\n"; + const kotlinAnnotations = buildKotlinAnnotations( + directive, + directiveReplacementFromConfig.kotlinAnnotations, + ); + return kotlinAnnotations.join("\n") + "\n"; }) .join(""); } + +function buildKotlinAnnotations( + directive: ConstDirectiveNode, + kotlinAnnotations: NonNullable< + GraphQLKotlinCodegenConfig["directiveReplacements"] + >[number]["kotlinAnnotations"], +) { + return kotlinAnnotations.map((kotlinAnnotation) => { + if (typeof kotlinAnnotation === "string") return kotlinAnnotation; + const directiveArguments = kotlinAnnotation.argumentsToRetain + ?.map((argumentToRetain) => { + const argumentValueNode = directive.arguments?.find( + (argument) => argument.name.value === argumentToRetain, + )?.value; + if (!argumentValueNode) + throw new Error( + `Argument ${argumentToRetain} was provided in argumentsToRetain config but was not found in directive ${directive.name.value}`, + ); + if (!("value" in argumentValueNode)) + throw new Error( + `Directive argument ${argumentToRetain} in directive ${directive.name.value} has an unsupported type. Only INT, FLOAT, STRING, BOOLEAN, and ENUM are supported.`, + ); + const argumentValue = + argumentValueNode.kind === "StringValue" + ? `"${argumentValueNode.value}"` + : argumentValueNode.value; + return `${argumentToRetain} = ${argumentValue}`; + }) + .join(", "); + return `@${kotlinAnnotation.annotationName}(${directiveArguments})`; + }); +} diff --git a/test/unit/should_honor_directiveReplacements_config/codegen.config.ts b/test/unit/should_honor_directiveReplacements_config/codegen.config.ts index 169959f..b1cb0e6 100644 --- a/test/unit/should_honor_directiveReplacements_config/codegen.config.ts +++ b/test/unit/should_honor_directiveReplacements_config/codegen.config.ts @@ -6,5 +6,14 @@ export default { directive: "directive1", kotlinAnnotations: ["@SomeAnnotation1", "@SomeAnnotation2"], }, + { + directive: "directiveWithArgs", + kotlinAnnotations: [ + { + annotationName: "SomeAnnotation3", + argumentsToRetain: ["arg1", "arg2"], + }, + ], + }, ], } satisfies GraphQLKotlinCodegenConfig; diff --git a/test/unit/should_honor_directiveReplacements_config/expected.kt b/test/unit/should_honor_directiveReplacements_config/expected.kt index c600ec0..70bde82 100644 --- a/test/unit/should_honor_directiveReplacements_config/expected.kt +++ b/test/unit/should_honor_directiveReplacements_config/expected.kt @@ -5,6 +5,7 @@ import com.expediagroup.graphql.generator.annotations.* @GraphQLDescription("A description for MyDirectiveType") @SomeAnnotation1 @SomeAnnotation2 +@SomeAnnotation3(arg1 = "arg1", arg2 = 0) data class MyDirectiveType( val field: String? = null ) diff --git a/test/unit/should_honor_directiveReplacements_config/schema.graphql b/test/unit/should_honor_directiveReplacements_config/schema.graphql index 393835a..4444e17 100644 --- a/test/unit/should_honor_directiveReplacements_config/schema.graphql +++ b/test/unit/should_honor_directiveReplacements_config/schema.graphql @@ -1,7 +1,8 @@ directive @directive1 on OBJECT | UNION +directive @directiveWithArgs(arg1: String, arg2: Int) on OBJECT | UNION "A description for MyDirectiveType" -type MyDirectiveType @directive1 { +type MyDirectiveType @directive1 @directiveWithArgs(arg1: "arg1", arg2: 0) { field: String }