From cee01d4b621bae84b4196448bc61b219f4d2c5e3 Mon Sep 17 00:00:00 2001 From: Dan Adajian Date: Tue, 23 Apr 2024 20:25:08 -0500 Subject: [PATCH] BREAKING CHANGE: generate marker interfaces for union types by default (#39) --- bun.lockb | Bin 236510 -> 237834 bytes docs/docs/configuration.md | 2 +- eslint.config.js | 1 + package.json | 4 +- src/config.ts | 13 +++- src/definitions/enum.ts | 8 +-- src/definitions/input.ts | 5 +- src/definitions/interface.ts | 4 +- src/definitions/object.ts | 26 +++++--- src/definitions/union.ts | 22 +++++-- .../add-dependent-types-to-only-types.ts | 36 +++++++++++ src/helpers/add-dependent-types.ts | 35 ---------- src/helpers/build-annotations.ts | 53 +++++----------- src/helpers/build-config-with-defaults.ts | 23 +++++++ src/helpers/build-description-annotation.ts | 60 ++++++++++++++++++ src/helpers/build-directive-annotations.ts | 43 ++----------- src/helpers/build-field-definition.ts | 4 +- src/helpers/build-type-metadata.ts | 9 +-- src/helpers/dependent-type-is-in-scope.ts | 24 ------- src/helpers/dependent-type-utils.ts | 32 ++++++++-- src/helpers/get-dependent-type-names.ts | 10 ++- src/helpers/is-resolver-type.ts | 4 +- src/helpers/should-include-type-definition.ts | 4 +- src/plugin.ts | 37 +++++------ src/visitor.ts | 12 ++-- test/plugin.test.ts | 4 +- .../expected.kt | 23 +++---- .../schema.graphql | 2 +- .../expected.kt | 7 +- .../schema.graphql | 7 +- .../expected.kt | 24 ++----- .../expected.kt | 18 ++---- .../expected.kt | 18 ++---- .../expected.kt | 15 ++--- .../expected.kt | 15 ++--- .../externalClasses.kt | 2 +- .../expected.kt | 10 +-- .../should_honor_onlyTypes_config/expected.kt | 8 +-- .../schema.graphql | 8 +-- .../codegen.config.ts | 5 ++ .../expected.kt | 37 +++++++++++ .../schema.graphql | 32 ++++++++++ .../expected.kt | 25 ++------ 43 files changed, 403 insertions(+), 328 deletions(-) create mode 100644 src/helpers/add-dependent-types-to-only-types.ts delete mode 100644 src/helpers/add-dependent-types.ts create mode 100644 src/helpers/build-config-with-defaults.ts create mode 100644 src/helpers/build-description-annotation.ts delete mode 100644 src/helpers/dependent-type-is-in-scope.ts create mode 100644 test/unit/should_honor_union_generation_config/codegen.config.ts create mode 100644 test/unit/should_honor_union_generation_config/expected.kt create mode 100644 test/unit/should_honor_union_generation_config/schema.graphql diff --git a/bun.lockb b/bun.lockb index 3ae2d195f26cb0b9c9b209286ca7bb2cb871ffae..90df4322c5fb16582e052d553b7f100ab9c9dd92 100755 GIT binary patch delta 12289 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 UPkzpQ@TJi?x9F*1K1ZVe8>7ejRR910 diff --git a/docs/docs/configuration.md b/docs/docs/configuration.md index 105da11..052ebe4 100644 --- a/docs/docs/configuration.md +++ b/docs/docs/configuration.md @@ -5,5 +5,5 @@ sidebar_position: 3 # Configuration ```ts reference title="Config Schema" -https://github.com/ExpediaGroup/graphql-kotlin-codegen/blob/main/src/config.ts#L17-L100000 +https://github.com/ExpediaGroup/graphql-kotlin-codegen/blob/main/src/config.ts#L1-L100000 ``` diff --git a/eslint.config.js b/eslint.config.js index 03d7dab..09c7cc4 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -13,6 +13,7 @@ export default [ }, }, rules: { + "no-console": "error", "@typescript-eslint/no-non-null-assertion": "error", "@typescript-eslint/no-unsafe-argument": "error", "@typescript-eslint/no-unsafe-call": "error", diff --git a/package.json b/package.json index 45be384..fd1913f 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@graphql-codegen/cli": "5.0.2", "@total-typescript/ts-reset": "0.5.1", "bun-types": "1.1.4", - "eslint": "9.1.0", + "eslint": "9.1.1", "husky": "9.0.11", "prettier": "3.2.5", "tsup": "8.0.2", @@ -39,7 +39,7 @@ "build": "tsup src/plugin.ts --clean --dts --external graphql", "format": "prettier --write .", "format-check": "prettier --check .", - "integration": "bun run build && graphql-codegen && ./gradlew compileTestKotlin", + "integration": "bun run build && graphql-codegen && ./gradlew build", "lint": "eslint .", "prepack": "bun run build", "prepare": "husky", diff --git a/src/config.ts b/src/config.ts index 5a3a217..fa9f41b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -15,6 +15,7 @@ import { array, boolean, enum_, + literal, object, optional, string, @@ -44,11 +45,11 @@ export const configSchema = object({ dependentTypesInScope: optional(array(string())), /** * Denotes Kotlin classes representing union types to be treated as interfaces rather than annotation classes. - * This should be used for types outside `dependentTypesInScope` that are not generated by the plugin. + * @description This should be used for types outside `dependentTypesInScope` that are not generated by the plugin. Only use when unionGeneration is set to `ANNOTATION_CLASS`. */ externalUnionsAsInterfaces: optional(array(string())), /** - * Additional imports to add to the generated file. + * Additional imports to add to the generated file. GraphQL Kotlin annotations are always imported. * @example ["com.example.additional.import.*"] */ extraImports: optional(array(string())), @@ -129,4 +130,12 @@ export const configSchema = object({ }), ), ), + /** + * Denotes the generation strategy for union types. Defaults to `MARKER_INTERFACE`. + * @description The `MARKER_INTERFACE` option is highly recommended, since it is more type-safe than using annotation classes. + * @link https://opensource.expediagroup.com/graphql-kotlin/docs/schema-generator/writing-schemas/unions/ + */ + unionGeneration: optional( + union([literal("ANNOTATION_CLASS"), literal("MARKER_INTERFACE")]), + ), }); diff --git a/src/definitions/enum.ts b/src/definitions/enum.ts index c4e7a7b..a06e352 100644 --- a/src/definitions/enum.ts +++ b/src/definitions/enum.ts @@ -15,11 +15,11 @@ import { EnumTypeDefinitionNode, EnumValueDefinitionNode } from "graphql"; import { indentMultiline } from "@graphql-codegen/visitor-plugin-common"; import { buildAnnotations } from "../helpers/build-annotations"; import { shouldIncludeTypeDefinition } from "../helpers/should-include-type-definition"; -import { CodegenConfig } from "../plugin"; +import { CodegenConfigWithDefaults } from "../helpers/build-config-with-defaults"; export function buildEnumTypeDefinition( node: EnumTypeDefinitionNode, - config: CodegenConfig, + config: CodegenConfigWithDefaults, ) { if (!shouldIncludeTypeDefinition(node, config)) { return ""; @@ -46,11 +46,11 @@ ${indentMultiline(enumValues.join(",\n") + ";", 2)} function buildEnumValueDefinition( node: EnumValueDefinitionNode, - config: CodegenConfig, + config: CodegenConfigWithDefaults, ) { const annotations = buildAnnotations({ config, definitionNode: node, }); - return `${annotations}${config.convert(node)}`; + return `${annotations}${config.convert?.(node)}`; } diff --git a/src/definitions/input.ts b/src/definitions/input.ts index 8d34a58..ddca970 100644 --- a/src/definitions/input.ts +++ b/src/definitions/input.ts @@ -16,12 +16,12 @@ import { shouldIncludeTypeDefinition } from "../helpers/should-include-type-defi import { buildTypeMetadata } from "../helpers/build-type-metadata"; import { buildAnnotations } from "../helpers/build-annotations"; import { indent } from "@graphql-codegen/visitor-plugin-common"; -import { CodegenConfig } from "../plugin"; +import { CodegenConfigWithDefaults } from "../helpers/build-config-with-defaults"; export function buildInputObjectDefinition( node: InputObjectTypeDefinitionNode, schema: GraphQLSchema, - config: CodegenConfig, + config: CodegenConfigWithDefaults, ) { if (!shouldIncludeTypeDefinition(node, config)) { return ""; @@ -47,7 +47,6 @@ export function buildInputObjectDefinition( const annotations = buildAnnotations({ config, - inputDescription: node.description?.value, definitionNode: node, }); diff --git a/src/definitions/interface.ts b/src/definitions/interface.ts index 9072eb6..bf302c0 100644 --- a/src/definitions/interface.ts +++ b/src/definitions/interface.ts @@ -17,12 +17,12 @@ import { indent } from "@graphql-codegen/visitor-plugin-common"; import { buildTypeMetadata } from "../helpers/build-type-metadata"; import { shouldIncludeTypeDefinition } from "../helpers/should-include-type-definition"; import { buildFieldDefinition } from "../helpers/build-field-definition"; -import { CodegenConfig } from "../plugin"; +import { CodegenConfigWithDefaults } from "../helpers/build-config-with-defaults"; export function buildInterfaceDefinition( node: InterfaceTypeDefinitionNode, schema: GraphQLSchema, - config: CodegenConfig, + config: CodegenConfigWithDefaults, ) { if (!shouldIncludeTypeDefinition(node, config)) { return ""; diff --git a/src/definitions/object.ts b/src/definitions/object.ts index 56885d1..29dbcb5 100644 --- a/src/definitions/object.ts +++ b/src/definitions/object.ts @@ -16,16 +16,19 @@ import { buildAnnotations } from "../helpers/build-annotations"; import { indent } from "@graphql-codegen/visitor-plugin-common"; import { buildTypeMetadata } from "../helpers/build-type-metadata"; import { shouldIncludeTypeDefinition } from "../helpers/should-include-type-definition"; -import { getDependentInterfaceNames } from "../helpers/dependent-type-utils"; +import { + getDependentInterfaceNames, + getDependentUnionsForType, +} from "../helpers/dependent-type-utils"; import { isResolverType } from "../helpers/is-resolver-type"; import { buildFieldDefinition } from "../helpers/build-field-definition"; import { isExternalField } from "../helpers/is-external-field"; -import { CodegenConfig } from "../plugin"; +import { CodegenConfigWithDefaults } from "../helpers/build-config-with-defaults"; export function buildObjectTypeDefinition( node: ObjectTypeDefinitionNode, schema: GraphQLSchema, - config: CodegenConfig, + config: CodegenConfigWithDefaults, ) { if (!shouldIncludeTypeDefinition(node, config)) { return ""; @@ -36,25 +39,30 @@ export function buildObjectTypeDefinition( definitionNode: node, }); const name = node.name.value; - const interfacesToInherit = getDependentInterfaceNames(node); + const dependentInterfaces = getDependentInterfaceNames(node); + const dependentUnions = getDependentUnionsForType(schema, node); + const interfacesToInherit = + config.unionGeneration === "MARKER_INTERFACE" + ? dependentInterfaces.concat(dependentUnions) + : dependentInterfaces; const interfaceInheritance = `${interfacesToInherit.length ? ` : ${interfacesToInherit.join(", ")}` : ""}`; if (isResolverType(node, config)) { return `${annotations}@GraphQLIgnore\ninterface ${name}${interfaceInheritance} { -${getClassMembers({ node, schema, config })} +${getDataClassMembers({ node, schema, config })} } ${annotations}@GraphQLIgnore\ninterface ${name}CompletableFuture { -${getClassMembers({ node, schema, config, completableFuture: true })} +${getDataClassMembers({ node, schema, config, completableFuture: true })} }`; } return `${annotations}data class ${name}( -${getClassMembers({ node, schema, config })} +${getDataClassMembers({ node, schema, config })} )${interfaceInheritance}`; } -function getClassMembers({ +function getDataClassMembers({ node, schema, config, @@ -62,7 +70,7 @@ function getClassMembers({ }: { node: ObjectTypeDefinitionNode; schema: GraphQLSchema; - config: CodegenConfig; + config: CodegenConfigWithDefaults; completableFuture?: boolean; }) { const resolverType = isResolverType(node, config); diff --git a/src/definitions/union.ts b/src/definitions/union.ts index e0a98f1..d7c55f2 100644 --- a/src/definitions/union.ts +++ b/src/definitions/union.ts @@ -13,25 +13,33 @@ limitations under the License. import { UnionTypeDefinitionNode } from "graphql"; import { shouldIncludeTypeDefinition } from "../helpers/should-include-type-definition"; -import { buildDirectiveAnnotations } from "../helpers/build-directive-annotations"; -import { CodegenConfig } from "../plugin"; -import { trimDescription } from "../helpers/build-annotations"; +import { CodegenConfigWithDefaults } from "../helpers/build-config-with-defaults"; +import { + buildAnnotations, + trimDescription, +} from "../helpers/build-annotations"; export function buildUnionTypeDefinition( node: UnionTypeDefinitionNode, - config: CodegenConfig, + config: CodegenConfigWithDefaults, ) { if (!shouldIncludeTypeDefinition(node, config)) { return ""; } + const annotations = buildAnnotations({ + config, + definitionNode: node, + }); + if (config.unionGeneration === "MARKER_INTERFACE") { + return `${annotations}interface ${node.name.value}`; + } - const directiveAnnotations = buildDirectiveAnnotations(node, config); const possibleTypes = node.types?.map((type) => `${type.name.value}::class`).join(", ") || ""; - return `${directiveAnnotations}@GraphQLUnion( + return `${annotations}@GraphQLUnion( name = "${node.name.value}", possibleTypes = [${possibleTypes}], - description = "${node.description?.value ? trimDescription(node.description.value) : ""}" + description = "${trimDescription(node.description?.value)}" ) annotation class ${node.name.value}`; } diff --git a/src/helpers/add-dependent-types-to-only-types.ts b/src/helpers/add-dependent-types-to-only-types.ts new file mode 100644 index 0000000..ab97959 --- /dev/null +++ b/src/helpers/add-dependent-types-to-only-types.ts @@ -0,0 +1,36 @@ +/* +Copyright 2024 Expedia, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + https://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { CodegenConfigWithDefaults } from "./build-config-with-defaults"; +import { getDependentTypeNames } from "./get-dependent-type-names"; +import { GraphQLSchema } from "graphql"; + +export function addDependentTypesToOnlyTypes( + config: CodegenConfigWithDefaults, + schema: GraphQLSchema, +) { + if (!config.onlyTypes) { + throw new Error(`onlyTypes config is required to add dependent types`); + } + const onlyTypesNodes = config.onlyTypes + .map((typeName) => schema.getType(typeName)?.astNode) + .filter(Boolean); + const dependentTypeNames = onlyTypesNodes.flatMap((node) => + getDependentTypeNames(schema, node, config), + ); + const typesInScope = config.dependentTypesInScope; + const dependentTypesInScope = typesInScope + ? dependentTypeNames.filter((typeName) => typesInScope.includes(typeName)) + : dependentTypeNames; + config.onlyTypes.push(...dependentTypesInScope); +} diff --git a/src/helpers/add-dependent-types.ts b/src/helpers/add-dependent-types.ts deleted file mode 100644 index a5097e4..0000000 --- a/src/helpers/add-dependent-types.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2024 Expedia, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { CodegenConfig } from "../plugin"; -import { getDependentTypeNames } from "./get-dependent-type-names"; -import { dependentTypeIsInScope } from "./dependent-type-is-in-scope"; -import { GraphQLSchema } from "graphql"; - -export function addDependentTypes( - config: CodegenConfig, - schema: GraphQLSchema, -) { - if (config.onlyTypes && (config.includeDependentTypes ?? true)) { - const onlyTypesNodes = config.onlyTypes - .map((typeName) => schema.getType(typeName)?.astNode) - .filter(Boolean); - const dependentTypeNames = onlyTypesNodes.flatMap((node) => - getDependentTypeNames(schema, node, config.dependentTypesInScope), - ); - const dependentTypesInScope = dependentTypeNames.filter((typeName) => - dependentTypeIsInScope(typeName, config), - ); - config.onlyTypes.push(...dependentTypesInScope); - } -} diff --git a/src/helpers/build-annotations.ts b/src/helpers/build-annotations.ts index 0a0d12b..b74d737 100644 --- a/src/helpers/build-annotations.ts +++ b/src/helpers/build-annotations.ts @@ -18,8 +18,9 @@ import { InputValueDefinitionNode, TypeDefinitionNode, } from "graphql"; +import { buildDescriptionAnnotation } from "./build-description-annotation"; import { buildDirectiveAnnotations } from "./build-directive-annotations"; -import { CodegenConfig } from "../plugin"; +import { CodegenConfigWithDefaults } from "./build-config-with-defaults"; import { TypeMetadata } from "./build-type-metadata"; export type DefinitionNode = @@ -30,39 +31,24 @@ export type DefinitionNode = export function buildAnnotations({ config, - inputDescription, definitionNode, resolvedType, }: { - config: CodegenConfig; - inputDescription?: string; - definitionNode?: DefinitionNode; + config: CodegenConfigWithDefaults; + definitionNode: DefinitionNode; resolvedType?: TypeMetadata; }) { - const description = - inputDescription ?? definitionNode?.description?.value ?? ""; - const descriptionAnnotator = isDeprecatedDescription( + const description = definitionNode?.description?.value ?? ""; + const descriptionAnnotation = buildDescriptionAnnotation( description, + definitionNode, + config, resolvedType, - ) - ? "@Deprecated" - : "@GraphQLDescription"; - const descriptionValue = isDeprecatedDescription(description, resolvedType) - ? description.replace("DEPRECATED: ", "") - : description; - const trimmedDescription = trimDescription(descriptionValue); - const descriptionAnnotation = description - ? `${descriptionAnnotator}("${trimmedDescription}")\n` - : ""; - - const directiveAnnotations = definitionNode - ? buildDirectiveAnnotations( - definitionNode, - config, - description, - resolvedType, - ) - : ""; + ); + const directiveAnnotations = buildDirectiveAnnotations( + definitionNode, + config, + ); const unionAnnotation = resolvedType?.unionAnnotation ? `@${resolvedType.unionAnnotation}\n` : ""; @@ -86,19 +72,10 @@ export function buildAnnotations({ ); } -export function isDeprecatedDescription( - description?: string, - resolvedType?: TypeMetadata, -) { - return ( - description?.startsWith("DEPRECATED: ") && !resolvedType?.unionAnnotation - ); -} - -export function trimDescription(description: string) { +export function trimDescription(description?: string) { return ( description - .split("\n") + ?.split("\n") .map((str) => str.trim().replaceAll('"', "").replaceAll("\\", "")) .find((str) => str.match(/^[a-zA-Z]/)) ?? "" ); diff --git a/src/helpers/build-config-with-defaults.ts b/src/helpers/build-config-with-defaults.ts new file mode 100644 index 0000000..e58fd41 --- /dev/null +++ b/src/helpers/build-config-with-defaults.ts @@ -0,0 +1,23 @@ +import { GraphQLKotlinCodegenConfig } from "../plugin"; +import { buildPackageNameFromPath } from "@graphql-codegen/java-common"; +import { dirname, normalize } from "path"; + +export function buildConfigWithDefaults( + config: GraphQLKotlinCodegenConfig, + outputFile: string, +) { + return { + packageName: buildPackageNameFromPath(dirname(normalize(outputFile))), + includeDependentTypes: true, + unionGeneration: "MARKER_INTERFACE", + ...config, + extraImports: [ + "com.expediagroup.graphql.generator.annotations.*", + ...(config.extraImports ?? []), + ], + } as const; +} + +export type CodegenConfigWithDefaults = ReturnType< + typeof buildConfigWithDefaults +>; diff --git a/src/helpers/build-description-annotation.ts b/src/helpers/build-description-annotation.ts new file mode 100644 index 0000000..24129b9 --- /dev/null +++ b/src/helpers/build-description-annotation.ts @@ -0,0 +1,60 @@ +import { CodegenConfigWithDefaults } from "./build-config-with-defaults"; +import { TypeMetadata } from "./build-type-metadata"; +import { indent } from "@graphql-codegen/visitor-plugin-common"; +import { Kind } from "graphql/index"; +import { DefinitionNode, trimDescription } from "./build-annotations"; + +export function buildDescriptionAnnotation( + description: string, + definitionNode: DefinitionNode, + config: CodegenConfigWithDefaults, + resolvedType?: TypeMetadata, +) { + const trimmedDescription = trimDescription(description); + const isDeprecatedDescription = trimmedDescription.startsWith( + deprecatedDescriptionPrefix, + ); + if (isDeprecatedDescription && resolvedType?.unionAnnotation) { + return `@GraphQLDescription("${trimmedDescription}")\n`; + } else if (isDeprecatedDescription) { + const descriptionValue = description.replace( + deprecatedDescriptionPrefix, + "", + ); + return `@Deprecated("${trimDescription(descriptionValue)}")\n`; + } + + const deprecatedDirective = definitionNode.directives?.find( + (directive) => directive.name.value === "deprecated", + ); + const deprecatedReasonNode = deprecatedDirective?.arguments?.find( + (arg) => arg.name.value === "reason", + )?.value; + const deprecatedReason = + deprecatedReasonNode?.kind === "StringValue" + ? deprecatedReasonNode.value + : ""; + const trimmedDeprecatedReason = trimDescription(deprecatedReason); + + if (deprecatedDirective && resolvedType?.unionAnnotation) { + return `@GraphQLDescription("${trimmedDeprecatedReason}")\n`; + } else if (deprecatedDirective) { + const graphqlDescription = trimmedDescription + ? `@GraphQLDescription("${trimmedDescription}")\n` + : ""; + const deprecatedDescription = `@Deprecated("${trimmedDeprecatedReason}")\n`; + return `${graphqlDescription}${graphqlDescription ? indent(deprecatedDescription, 2) : deprecatedDescription}`; + } + + if ( + trimmedDescription && + (config.unionGeneration === "MARKER_INTERFACE" || + definitionNode?.kind !== Kind.UNION_TYPE_DEFINITION) + ) { + return `@GraphQLDescription("${trimmedDescription}")\n`; + } + + return ""; +} + +const deprecatedDescriptionPrefix = "DEPRECATED: "; diff --git a/src/helpers/build-directive-annotations.ts b/src/helpers/build-directive-annotations.ts index 4c447ff..e1c3908 100644 --- a/src/helpers/build-directive-annotations.ts +++ b/src/helpers/build-directive-annotations.ts @@ -11,48 +11,19 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { CodegenConfig, GraphQLKotlinCodegenConfig } from "../plugin"; -import { - DefinitionNode, - isDeprecatedDescription, - trimDescription, -} from "./build-annotations"; +import { CodegenConfigWithDefaults } from "./build-config-with-defaults"; +import { DefinitionNode } 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, - config: CodegenConfig, - description?: string, - resolvedType?: TypeMetadata, + definitionNode: DefinitionNode, + config: CodegenConfigWithDefaults, ) { - const kind = incomingNode.kind; - const directives = incomingNode.directives ?? []; - + const directives = definitionNode.directives ?? []; return directives .map((directive) => { const directiveName = directive.name.value; - if ( - directiveName === "deprecated" && - !isDeprecatedDescription(description) - ) { - const deprecatedReasonNode = directive.arguments?.find( - (arg) => arg.name.value === "reason", - )?.value; - const deprecatedReason = - deprecatedReasonNode?.kind === "StringValue" - ? deprecatedReasonNode.value - : ""; - if (incomingNode.description?.value && resolvedType?.unionAnnotation) { - return ""; - } - const descriptionAnnotator = resolvedType?.unionAnnotation - ? "@GraphQLDescription" - : "@Deprecated"; - const trimmedDeprecatedReason = trimDescription(deprecatedReason); - return `${descriptionAnnotator}("${trimmedDeprecatedReason}")\n`; - } const federationReplacement = getFederationDirectiveReplacement(directive); if (federationReplacement) return federationReplacement + "\n"; @@ -60,7 +31,7 @@ export function buildDirectiveAnnotations( const directiveReplacementFromConfig = config.directiveReplacements?.find( ({ directive, definitionType }) => directive === directiveName && - (!definitionType || definitionType === kind), + (!definitionType || definitionType === definitionNode.kind), ); if (!directiveReplacementFromConfig) return ""; const kotlinAnnotations = buildKotlinAnnotations( @@ -75,7 +46,7 @@ export function buildDirectiveAnnotations( function buildKotlinAnnotations( directive: ConstDirectiveNode, kotlinAnnotations: NonNullable< - GraphQLKotlinCodegenConfig["directiveReplacements"] + CodegenConfigWithDefaults["directiveReplacements"] >[number]["kotlinAnnotations"], ) { return kotlinAnnotations.map((kotlinAnnotation) => { diff --git a/src/helpers/build-field-definition.ts b/src/helpers/build-field-definition.ts index ee71e0f..ca7b22c 100644 --- a/src/helpers/build-field-definition.ts +++ b/src/helpers/build-field-definition.ts @@ -21,12 +21,12 @@ import { } from "graphql"; import { isResolverType } from "./is-resolver-type"; import { isExternalField } from "./is-external-field"; -import { CodegenConfig } from "../plugin"; +import { CodegenConfigWithDefaults } from "./build-config-with-defaults"; export function buildFieldDefinition( fieldNode: FieldDefinitionNode, definitionNode: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, - config: CodegenConfig, + config: CodegenConfigWithDefaults, completableFuture?: boolean, ) { const shouldUseFunction = diff --git a/src/helpers/build-type-metadata.ts b/src/helpers/build-type-metadata.ts index 2682774..a704738 100644 --- a/src/helpers/build-type-metadata.ts +++ b/src/helpers/build-type-metadata.ts @@ -13,15 +13,15 @@ limitations under the License. import { GraphQLSchema, - isScalarType, - isUnionType, Kind, NamedTypeNode, TypeNode, + isScalarType, + isUnionType, } from "graphql"; import { getBaseTypeNode } from "@graphql-codegen/visitor-plugin-common"; import { wrapTypeWithModifiers } from "@graphql-codegen/java-common"; -import { CodegenConfig } from "../plugin"; +import { CodegenConfigWithDefaults } from "./build-config-with-defaults"; export interface TypeMetadata { typeName: string; @@ -33,7 +33,7 @@ export interface TypeMetadata { export function buildTypeMetadata( typeNode: TypeNode, schema: GraphQLSchema, - config: CodegenConfig, + config: CodegenConfigWithDefaults, ): TypeMetadata { const innerType = getBaseTypeNode(typeNode); const schemaType = schema.getType(innerType.name.value); @@ -60,6 +60,7 @@ export function buildTypeMetadata( }; } else if (isUnionType(schemaType)) { const shouldTreatUnionAsInterface = + config.unionGeneration === "MARKER_INTERFACE" || config.externalUnionsAsInterfaces?.includes(schemaType.name); return { ...commonMetadata, diff --git a/src/helpers/dependent-type-is-in-scope.ts b/src/helpers/dependent-type-is-in-scope.ts deleted file mode 100644 index ab79b03..0000000 --- a/src/helpers/dependent-type-is-in-scope.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2024 Expedia, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { CodegenConfig } from "../plugin"; - -export function dependentTypeIsInScope( - typeName: string, - config: CodegenConfig, -) { - return ( - !config.dependentTypesInScope || - config.dependentTypesInScope.includes(typeName) - ); -} diff --git a/src/helpers/dependent-type-utils.ts b/src/helpers/dependent-type-utils.ts index d8d6e65..f9da022 100644 --- a/src/helpers/dependent-type-utils.ts +++ b/src/helpers/dependent-type-utils.ts @@ -11,19 +11,26 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Kind, TypeDefinitionNode, TypeNode } from "graphql"; -import { CodegenConfig } from "../plugin"; +import { + GraphQLSchema, + GraphQLUnionType, + Kind, + TypeDefinitionNode, + TypeNode, +} from "graphql"; +import { CodegenConfigWithDefaults } from "./build-config-with-defaults"; export function getDependentFieldTypeNames( node: TypeDefinitionNode, - dependentTypesInScope: CodegenConfig["dependentTypesInScope"], + config: CodegenConfigWithDefaults, ) { return "fields" in node && node.fields ? node.fields .map((field) => getFieldTypeName(field.type)) .filter( (typeName) => - !dependentTypesInScope || dependentTypesInScope.includes(typeName), + !config.dependentTypesInScope || + config.dependentTypesInScope.includes(typeName), ) : []; } @@ -55,3 +62,20 @@ export function getDependentUnionNames(node: TypeDefinitionNode) { ? node.types?.map((type) => type.name.value) ?? [] : []; } + +export function getDependentUnionsForType( + schema: GraphQLSchema, + node: TypeDefinitionNode, +) { + const typeMap = schema.getTypeMap(); + const unions = Object.keys(typeMap) + .filter( + (type) => typeMap[type]?.astNode?.kind === Kind.UNION_TYPE_DEFINITION, + ) + .map((type) => typeMap[type] as GraphQLUnionType); + return unions + .filter((union) => + union.getTypes().some((type) => type.name === node.name.value), + ) + .map((union) => union.name); +} diff --git a/src/helpers/get-dependent-type-names.ts b/src/helpers/get-dependent-type-names.ts index f5c26a8..60d3e49 100644 --- a/src/helpers/get-dependent-type-names.ts +++ b/src/helpers/get-dependent-type-names.ts @@ -18,21 +18,19 @@ import { getDependentInterfaceNames, getDependentUnionNames, } from "./dependent-type-utils"; -import { CodegenConfig } from "../plugin"; +import { CodegenConfigWithDefaults } from "./build-config-with-defaults"; export function getDependentTypeNames( schema: GraphQLSchema, node: TypeDefinitionNode, - dependentTypesInScope: CodegenConfig["dependentTypesInScope"], + config: CodegenConfigWithDefaults, ): string[] { - const namedTypes = getDependentFieldTypeNames(node, dependentTypesInScope) + const namedTypes = getDependentFieldTypeNames(node, config) .concat(getDependentUnionNames(node)) .concat(getDependentInterfaceNames(node)); const recursivelyFoundTypes = namedTypes .map((typeName) => schema.getType(typeName)?.astNode) .filter(Boolean) - .flatMap((node) => - getDependentTypeNames(schema, node, dependentTypesInScope), - ); + .flatMap((node) => getDependentTypeNames(schema, node, config)); return namedTypes.concat(recursivelyFoundTypes); } diff --git a/src/helpers/is-resolver-type.ts b/src/helpers/is-resolver-type.ts index a7ca626..559ed46 100644 --- a/src/helpers/is-resolver-type.ts +++ b/src/helpers/is-resolver-type.ts @@ -12,11 +12,11 @@ limitations under the License. */ import { InterfaceTypeDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; -import { CodegenConfig } from "../plugin"; +import { CodegenConfigWithDefaults } from "./build-config-with-defaults"; export function isResolverType( node: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, - config: CodegenConfig, + config: CodegenConfigWithDefaults, ) { return ( node.fields?.some((fieldNode) => fieldNode.arguments?.length) || diff --git a/src/helpers/should-include-type-definition.ts b/src/helpers/should-include-type-definition.ts index fecdf5b..2187d1b 100644 --- a/src/helpers/should-include-type-definition.ts +++ b/src/helpers/should-include-type-definition.ts @@ -12,11 +12,11 @@ limitations under the License. */ import { TypeDefinitionNode } from "graphql"; -import { CodegenConfig } from "../plugin"; +import { CodegenConfigWithDefaults } from "./build-config-with-defaults"; export function shouldIncludeTypeDefinition( node: TypeDefinitionNode, - config: CodegenConfig, + config: CodegenConfigWithDefaults, ) { return !config.onlyTypes || config.onlyTypes.includes(node.name.value); } diff --git a/src/plugin.ts b/src/plugin.ts index 25c62d1..09d5ceb 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -11,12 +11,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { dirname, normalize } from "path"; import { - getCachedDocumentNodeFromSchema, PluginFunction, + getCachedDocumentNodeFromSchema, } from "@graphql-codegen/plugin-helpers"; -import { buildPackageNameFromPath } from "@graphql-codegen/java-common"; import { KotlinVisitor } from "./visitor"; import { ParsedConfig, @@ -24,15 +22,14 @@ import { } from "@graphql-codegen/visitor-plugin-common"; import { Input, safeParse } from "valibot"; import { configSchema } from "./config"; -import { addDependentTypes } from "./helpers/add-dependent-types"; +import { addDependentTypesToOnlyTypes } from "./helpers/add-dependent-types-to-only-types"; import { visit } from "graphql"; +import { buildConfigWithDefaults } from "./helpers/build-config-with-defaults"; export type GraphQLKotlinCodegenConfig = Partial & Input; -export type CodegenConfig = RawConfig & - ParsedConfig & - Input; -export const plugin: PluginFunction = ( + +export const plugin: PluginFunction = ( schema, _, config, @@ -41,7 +38,6 @@ export const plugin: PluginFunction = ( if (!info?.outputFile) { throw new Error("Missing outputFile in config"); } - const relevantPath = dirname(normalize(info.outputFile)); const { issues } = safeParse(configSchema, config); if (issues) { throw new Error( @@ -54,21 +50,26 @@ export const plugin: PluginFunction = ( ); } - addDependentTypes(config, schema); - const visitor = new KotlinVisitor(config, schema); + const configWithDefaults = buildConfigWithDefaults(config, info.outputFile); + if ( + configWithDefaults.onlyTypes && + configWithDefaults.includeDependentTypes + ) { + addDependentTypesToOnlyTypes(configWithDefaults, schema); + } + const visitor = new KotlinVisitor(configWithDefaults, schema); const astNode = getCachedDocumentNodeFromSchema(schema); const { definitions } = visit(astNode, visitor); - const packageName = `package ${ - config.packageName ?? buildPackageNameFromPath(relevantPath) - }\n`; - const defaultImports = ["com.expediagroup.graphql.generator.annotations.*"]; + const packageName = `package ${configWithDefaults.packageName}\n`; const imports = - defaultImports - .concat(config.extraImports ?? []) + configWithDefaults.extraImports .map((annotation) => `import ${annotation}`) .join("\n") + "\n"; const typeDefinitions = definitions - .filter((d: unknown) => typeof d === "string" && d.length) + .filter( + (definition: unknown) => + typeof definition === "string" && definition.length, + ) .join("\n\n"); return [packageName, imports, typeDefinitions].join("\n") + "\n"; diff --git a/src/visitor.ts b/src/visitor.ts index c2b57b8..52e9082 100644 --- a/src/visitor.ts +++ b/src/visitor.ts @@ -15,21 +15,25 @@ import { BaseVisitor, RawConfig } from "@graphql-codegen/visitor-plugin-common"; import { EnumTypeDefinitionNode, GraphQLSchema, - InterfaceTypeDefinitionNode, InputObjectTypeDefinitionNode, + InterfaceTypeDefinitionNode, ObjectTypeDefinitionNode, UnionTypeDefinitionNode, } from "graphql"; -import { CodegenConfig } from "./plugin"; +import { CodegenConfigWithDefaults } from "./helpers/build-config-with-defaults"; import { buildEnumTypeDefinition } from "./definitions/enum"; import { buildInterfaceDefinition } from "./definitions/interface"; import { buildInputObjectDefinition } from "./definitions/input"; import { buildObjectTypeDefinition } from "./definitions/object"; import { buildUnionTypeDefinition } from "./definitions/union"; +import { ParsedConfig } from "@graphql-codegen/visitor-plugin-common/typings/base-visitor"; -export class KotlinVisitor extends BaseVisitor { +export class KotlinVisitor extends BaseVisitor< + RawConfig, + ParsedConfig & CodegenConfigWithDefaults +> { constructor( - rawConfig: CodegenConfig, + rawConfig: CodegenConfigWithDefaults, protected _schema: GraphQLSchema, ) { super(rawConfig, rawConfig); diff --git a/test/plugin.test.ts b/test/plugin.test.ts index 6767630..ab1d934 100644 --- a/test/plugin.test.ts +++ b/test/plugin.test.ts @@ -1,5 +1,5 @@ import { buildSchema } from "graphql"; -import { CodegenConfig, plugin } from "../src/plugin"; +import { GraphQLKotlinCodegenConfig, plugin } from "../src/plugin"; import { describe, expect, it } from "bun:test"; import { Types } from "@graphql-codegen/plugin-helpers"; import * as glob from "glob"; @@ -9,7 +9,7 @@ function buildUnitTest({ config, }: { testName: string; - config: CodegenConfig; + config: GraphQLKotlinCodegenConfig; }) { it(testName, async () => { const filePath = `./test/unit/${testName}`; diff --git a/test/unit/should_annotate_types_properly/expected.kt b/test/unit/should_annotate_types_properly/expected.kt index 93ef4cf..1ad5a55 100644 --- a/test/unit/should_annotate_types_properly/expected.kt +++ b/test/unit/should_annotate_types_properly/expected.kt @@ -17,22 +17,15 @@ data class TypeThatShouldBeProperlyAnnotated( val deprecated3: String? = null, @Deprecated("It only takes the first one") val deprecated4: String? = null, - @UnionThatShouldBeProperlyAnnotated - @GraphQLDescription("DEPRECATED: It uses the GraphQLDescription annotation for union types") - val deprecated5: Any? = null, - @UnionThatShouldBeProperlyAnnotated - @GraphQLDescription("It uses the GraphQLDescription annotation for union types") - val deprecated6: Any? = null, - @UnionThatShouldBeProperlyAnnotated + @Deprecated("It uses the GraphQLDescription annotation for union types") + val deprecated5: UnionThatShouldBeProperlyAnnotated? = null, + @Deprecated("It uses the GraphQLDescription annotation for union types") + val deprecated6: UnionThatShouldBeProperlyAnnotated? = null, @GraphQLDescription("When there is a description") - val deprecated7: Any? = null, + @Deprecated("It uses the @Deprecated annotation for the reason") + val deprecated7: UnionThatShouldBeProperlyAnnotated? = null, @Deprecated("Multiline reason") val deprecated8: String? = null -) +) : UnionThatShouldBeProperlyAnnotated -@GraphQLUnion( - name = "UnionThatShouldBeProperlyAnnotated", - possibleTypes = [TypeThatShouldBeProperlyAnnotated::class], - description = "" -) -annotation class UnionThatShouldBeProperlyAnnotated +interface UnionThatShouldBeProperlyAnnotated diff --git a/test/unit/should_annotate_types_properly/schema.graphql b/test/unit/should_annotate_types_properly/schema.graphql index d039336..ffdde85 100644 --- a/test/unit/should_annotate_types_properly/schema.graphql +++ b/test/unit/should_annotate_types_properly/schema.graphql @@ -26,7 +26,7 @@ type TypeThatShouldBeProperlyAnnotated { ) "When there is a description" deprecated7: UnionThatShouldBeProperlyAnnotated - @deprecated(reason: "It omits the @Deprecated annotation for now") + @deprecated(reason: "It uses the @Deprecated annotation for the reason") deprecated8: String @deprecated( reason: "\n Multiline reason\n with spaces\n " diff --git a/test/unit/should_generate_input_types_properly/expected.kt b/test/unit/should_generate_input_types_properly/expected.kt index f5bb75c..ac9316d 100644 --- a/test/unit/should_generate_input_types_properly/expected.kt +++ b/test/unit/should_generate_input_types_properly/expected.kt @@ -4,8 +4,7 @@ import com.expediagroup.graphql.generator.annotations.* @GraphQLDescription("A description for MyInputType") data class InputTypeThatShouldBeGeneratedProperly( - val username: String? = null, - @GraphQLDescription("A description for email") - val email: String? = null, - val name: String? = null + val field1: String? = null, + @GraphQLDescription("A description for field2") + val field2: String? = null ) diff --git a/test/unit/should_generate_input_types_properly/schema.graphql b/test/unit/should_generate_input_types_properly/schema.graphql index 0072b3e..996f362 100644 --- a/test/unit/should_generate_input_types_properly/schema.graphql +++ b/test/unit/should_generate_input_types_properly/schema.graphql @@ -1,7 +1,6 @@ "A description for MyInputType" input InputTypeThatShouldBeGeneratedProperly { - username: String - "A description for email" - email: String - name: String + field1: String + "A description for field2" + field2: String } diff --git a/test/unit/should_generate_multi-union_types_properly/expected.kt b/test/unit/should_generate_multi-union_types_properly/expected.kt index a84a223..f5bd4a8 100644 --- a/test/unit/should_generate_multi-union_types_properly/expected.kt +++ b/test/unit/should_generate_multi-union_types_properly/expected.kt @@ -4,29 +4,17 @@ import com.expediagroup.graphql.generator.annotations.* data class MyType3( val field: String? = null -) +) : MyUnion1, MyUnion2 data class MyType4( val field: String? = null -) +) : MyUnion1, MyUnion2 -@GraphQLUnion( - name = "MyUnion1", - possibleTypes = [MyType3::class, MyType4::class], - description = "" -) -annotation class MyUnion1 +interface MyUnion1 -@GraphQLUnion( - name = "MyUnion2", - possibleTypes = [MyType3::class, MyType4::class], - description = "" -) -annotation class MyUnion2 +interface MyUnion2 data class MyMultiUnionType( - @MyUnion1 - val field: Any? = null, - @MyUnion2 - val field2: Any? = null + val field: MyUnion1? = null, + val field2: MyUnion2? = null ) diff --git a/test/unit/should_generate_non-nullable_union_list_types_properly/expected.kt b/test/unit/should_generate_non-nullable_union_list_types_properly/expected.kt index 3cf852b..aa7142b 100644 --- a/test/unit/should_generate_non-nullable_union_list_types_properly/expected.kt +++ b/test/unit/should_generate_non-nullable_union_list_types_properly/expected.kt @@ -5,22 +5,16 @@ import com.expediagroup.graphql.generator.annotations.* @GraphQLDescription("A description for MyType1") data class TypeForNonNullableUnionList1( val field: String? = null -) +) : UnionForNonNullableList data class TypeForNonNullableUnionList2( val field: String? = null -) +) : UnionForNonNullableList -@GraphQLUnion( - name = "UnionForNonNullableList", - possibleTypes = [TypeForNonNullableUnionList1::class, TypeForNonNullableUnionList2::class], - description = "A description for UnionForNonNullableList" -) -annotation class UnionForNonNullableList +@GraphQLDescription("A description for UnionForNonNullableList") +interface UnionForNonNullableList data class MyNonNullableUnionListType( - @UnionForNonNullableList - val field: List = emptyList(), - @UnionForNonNullableList - val field2: List = emptyList() + val field: List = emptyList(), + val field2: List = emptyList() ) diff --git a/test/unit/should_generate_union_list_types_properly/expected.kt b/test/unit/should_generate_union_list_types_properly/expected.kt index 1777385..d35f292 100644 --- a/test/unit/should_generate_union_list_types_properly/expected.kt +++ b/test/unit/should_generate_union_list_types_properly/expected.kt @@ -5,23 +5,17 @@ import com.expediagroup.graphql.generator.annotations.* @GraphQLDescription("A description for TypeForGeneratingUnionListTypes1") data class TypeForGeneratingUnionListTypes1( val field: String? = null -) +) : UnionForGeneratingUnionListTypes data class TypeForGeneratingUnionListTypes2( val field: String? = null -) +) : UnionForGeneratingUnionListTypes -@GraphQLUnion( - name = "UnionForGeneratingUnionListTypes", - possibleTypes = [TypeForGeneratingUnionListTypes1::class, TypeForGeneratingUnionListTypes2::class], - description = "A description for UnionForGeneratingUnionListTypes" -) -annotation class UnionForGeneratingUnionListTypes +@GraphQLDescription("A description for UnionForGeneratingUnionListTypes") +interface UnionForGeneratingUnionListTypes data class MyUnionListType( - @UnionForGeneratingUnionListTypes @GraphQLDescription("A description for field") - val field: List? = null, - @UnionForGeneratingUnionListTypes - val field2: List? = null + val field: List? = null, + val field2: List? = null ) diff --git a/test/unit/should_generate_union_types_properly/expected.kt b/test/unit/should_generate_union_types_properly/expected.kt index 229f757..a06a063 100644 --- a/test/unit/should_generate_union_types_properly/expected.kt +++ b/test/unit/should_generate_union_types_properly/expected.kt @@ -5,22 +5,17 @@ import com.expediagroup.graphql.generator.annotations.* @GraphQLDescription("A description for MyType1") data class TypeForGeneratingUnionTypesProperly1( val field: String? = null -) +) : UnionForGeneratingUnionsProperly data class TypeForGeneratingUnionTypesProperly2( val field: String? = null -) +) : UnionForGeneratingUnionsProperly -@GraphQLUnion( - name = "UnionForGeneratingUnionsProperly", - possibleTypes = [TypeForGeneratingUnionTypesProperly1::class, TypeForGeneratingUnionTypesProperly2::class], - description = "A trimmed description for UnionForGeneratingUnionsProperly" -) -annotation class UnionForGeneratingUnionsProperly +@GraphQLDescription("A trimmed description for UnionForGeneratingUnionsProperly") +interface UnionForGeneratingUnionsProperly data class MyUnionType( - @UnionForGeneratingUnionsProperly @GraphQLDescription("A description for field") - val field: Any? = null, + val field: UnionForGeneratingUnionsProperly? = null, val field2: String? = null ) diff --git a/test/unit/should_honor_dependentTypesInScope_config/expected.kt b/test/unit/should_honor_dependentTypesInScope_config/expected.kt index 6096c18..e7ea92e 100644 --- a/test/unit/should_honor_dependentTypesInScope_config/expected.kt +++ b/test/unit/should_honor_dependentTypesInScope_config/expected.kt @@ -10,20 +10,13 @@ data class MyTypeInOnlyTypes( data class TypeInScope( val field: String? = null, - @UnionInScope - val unionInScopeField: Any? = null, - @UnionOutOfScope - val unionOutOfScopeField: Any? = null, + val unionInScopeField: UnionInScope? = null, + val unionOutOfScopeField: UnionOutOfScope? = null, val externalUnionAsInterfaceField: ExternalUnionAsInterface? = null ) -@GraphQLUnion( - name = "UnionInScope", - possibleTypes = [Type1::class], - description = "" -) -annotation class UnionInScope +interface UnionInScope data class Type1( val field: String? = null -) +) : UnionInScope, ExternalUnionAsInterface diff --git a/test/unit/should_honor_dependentTypesInScope_config/externalClasses.kt b/test/unit/should_honor_dependentTypesInScope_config/externalClasses.kt index cd9d127..92abfe7 100644 --- a/test/unit/should_honor_dependentTypesInScope_config/externalClasses.kt +++ b/test/unit/should_honor_dependentTypesInScope_config/externalClasses.kt @@ -4,4 +4,4 @@ data class TypeOutOfScope(val value: String) interface ExternalUnionAsInterface -annotation class UnionOutOfScope +interface UnionOutOfScope diff --git a/test/unit/should_honor_directiveReplacements_config/expected.kt b/test/unit/should_honor_directiveReplacements_config/expected.kt index 08dd3dc..b353397 100644 --- a/test/unit/should_honor_directiveReplacements_config/expected.kt +++ b/test/unit/should_honor_directiveReplacements_config/expected.kt @@ -9,13 +9,9 @@ import should_honor_directiveReplacements_config.* @SomeAnnotationWithArgs(arg1 = "arg1", arg2 = 0) data class TypeHonoringDirectiveReplacements( val field: String? = null -) +) : MyDirectiveUnion +@GraphQLDescription("A description for MyDirectiveUnion") @SomeAnnotation1 @SomeAnnotation2 -@GraphQLUnion( - name = "MyDirectiveUnion", - possibleTypes = [TypeHonoringDirectiveReplacements::class], - description = "A description for MyDirectiveUnion" -) -annotation class MyDirectiveUnion +interface MyDirectiveUnion diff --git a/test/unit/should_honor_onlyTypes_config/expected.kt b/test/unit/should_honor_onlyTypes_config/expected.kt index 9bea03e..6997a70 100644 --- a/test/unit/should_honor_onlyTypes_config/expected.kt +++ b/test/unit/should_honor_onlyTypes_config/expected.kt @@ -3,11 +3,11 @@ package com.kotlin.generated import com.expediagroup.graphql.generator.annotations.* data class TypeHonoringOnlyTypesConfig( - val username: String? = null, - @GraphQLDescription("A description for email") - val email: String? = null, + val field1: String? = null, + @GraphQLDescription("A description for field2") + val field2: String? = null, @GraphQLDescription("A `weird` description for name") - val name: String? = null + val field3: String? = null ) @GraphQLDescription("A description for MyEnum") diff --git a/test/unit/should_honor_onlyTypes_config/schema.graphql b/test/unit/should_honor_onlyTypes_config/schema.graphql index e21a829..4ae6837 100644 --- a/test/unit/should_honor_onlyTypes_config/schema.graphql +++ b/test/unit/should_honor_onlyTypes_config/schema.graphql @@ -1,11 +1,11 @@ type TypeHonoringOnlyTypesConfig { - username: String - "A description for email" - email: String + field1: String + "A description for field2" + field2: String """ A \`weird\` description for name """ - name: String + field3: String } "A description for MyEnum" diff --git a/test/unit/should_honor_union_generation_config/codegen.config.ts b/test/unit/should_honor_union_generation_config/codegen.config.ts new file mode 100644 index 0000000..8b31606 --- /dev/null +++ b/test/unit/should_honor_union_generation_config/codegen.config.ts @@ -0,0 +1,5 @@ +import { GraphQLKotlinCodegenConfig } from "../../../src/plugin"; + +export default { + unionGeneration: "ANNOTATION_CLASS", +} satisfies GraphQLKotlinCodegenConfig; diff --git a/test/unit/should_honor_union_generation_config/expected.kt b/test/unit/should_honor_union_generation_config/expected.kt new file mode 100644 index 0000000..faab3b8 --- /dev/null +++ b/test/unit/should_honor_union_generation_config/expected.kt @@ -0,0 +1,37 @@ +package com.kotlin.generated + +import com.expediagroup.graphql.generator.annotations.* + +@GraphQLDescription("A description for TypeForHonoringUnionGenerationConfig1") +data class TypeForHonoringUnionGenerationConfig1( + val field: String? = null +) + +data class TypeForHonoringUnionGenerationConfig2( + val field: String? = null +) + +@GraphQLUnion( + name = "UnionAsAnnotation", + possibleTypes = [TypeForHonoringUnionGenerationConfig1::class, TypeForHonoringUnionGenerationConfig2::class], + description = "A description for UnionAsAnnotation" +) +annotation class UnionAsAnnotation + +data class UnionForHonoringUnionGenerationConfig( + @UnionAsAnnotation + @GraphQLDescription("A description for field") + val field: Any? = null, + @UnionAsAnnotation + @GraphQLDescription("DEPRECATED: It uses the GraphQLDescription annotation for union annotations") + val deprecated1: Any? = null, + @UnionAsAnnotation + @GraphQLDescription("DEPRECATED: It uses the GraphQLDescription annotation for union types") + val deprecated2: Any? = null, + @UnionAsAnnotation + @GraphQLDescription("It uses the GraphQLDescription annotation for union types") + val deprecated3: Any? = null, + @UnionAsAnnotation + @GraphQLDescription("It omits the @Deprecated annotation for now") + val deprecated4: Any? = null +) diff --git a/test/unit/should_honor_union_generation_config/schema.graphql b/test/unit/should_honor_union_generation_config/schema.graphql new file mode 100644 index 0000000..8756c26 --- /dev/null +++ b/test/unit/should_honor_union_generation_config/schema.graphql @@ -0,0 +1,32 @@ +"A description for TypeForHonoringUnionGenerationConfig1" +type TypeForHonoringUnionGenerationConfig1 { + field: String +} + +type TypeForHonoringUnionGenerationConfig2 { + field: String +} + +""" +A description for UnionAsAnnotation +""" +union UnionAsAnnotation = + | TypeForHonoringUnionGenerationConfig1 + | TypeForHonoringUnionGenerationConfig2 + +type UnionForHonoringUnionGenerationConfig { + "A description for field" + field: UnionAsAnnotation + "DEPRECATED: It uses the GraphQLDescription annotation for union annotations" + deprecated1: UnionAsAnnotation + @deprecated(reason: "when you have multiple deprecated annotations") + "DEPRECATED: It uses the GraphQLDescription annotation for union types" + deprecated2: UnionAsAnnotation + deprecated3: UnionAsAnnotation + @deprecated( + reason: "It uses the GraphQLDescription annotation for union types" + ) + "When there is a description" + deprecated4: UnionAsAnnotation + @deprecated(reason: "It omits the @Deprecated annotation for now") +} diff --git a/test/unit/should_include_dependent_types_in_onlyTypes_config/expected.kt b/test/unit/should_include_dependent_types_in_onlyTypes_config/expected.kt index 05c4797..1895564 100644 --- a/test/unit/should_include_dependent_types_in_onlyTypes_config/expected.kt +++ b/test/unit/should_include_dependent_types_in_onlyTypes_config/expected.kt @@ -6,8 +6,7 @@ data class MyType( val typeField: MyNestedType, val typeListField: List = emptyList(), val enumField: MyEnum, - @MyUnion - val unionField: Any, + val unionField: MyUnion, val unionImplementationField: UnionImplementation, val interfaceField: MyInterface1, val interfaceImplementationField: MyImplementedInterface @@ -34,24 +33,19 @@ enum class MyEnum { } } -@GraphQLUnion( - name = "MyUnion", - possibleTypes = [MyType1::class, MyType2::class], - description = "" -) -annotation class MyUnion +interface MyUnion data class MyType1( val field: String? = null -) +) : MyUnion data class MyType2( val field: String? = null -) +) : MyUnion data class UnionImplementation( val field: String? = null -) +) : MyUnion2 interface MyInterface1 { val field: String? @@ -73,16 +67,11 @@ data class MyNestedInput( val field: String? = null ) -@GraphQLUnion( - name = "MyStandaloneUnion", - possibleTypes = [StandaloneUnionType::class], - description = "" -) -annotation class MyStandaloneUnion +interface MyStandaloneUnion data class StandaloneUnionType( val field: StandaloneNestedType? = null -) +) : MyStandaloneUnion data class StandaloneNestedType( val field: String? = null