Skip to content

Commit

Permalink
feat: make class consolidation configurable (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
danadajian authored Aug 18, 2024
1 parent 5df5385 commit e92c452
Show file tree
Hide file tree
Showing 9 changed files with 350 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/config/build-config-with-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export function buildConfigWithDefaults(
unionGeneration: "MARKER_INTERFACE",
extraImports: ["com.expediagroup.graphql.generator.annotations.*"],
resolverInterfaces: [{ typeName: "Query" }, { typeName: "Mutation" }],
classConsolidationEnabled: true,
} as const satisfies GraphQLKotlinCodegenConfig;

return merge(defaultConfig, config) as GraphQLKotlinCodegenConfig &
Expand Down
8 changes: 8 additions & 0 deletions src/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ export const configSchema = object({
}),
),
),
/**
* Denotes whether to consolidate classes for input and output types whose fields are equivalent.
*
* @default true
*
* @link https://opensource.expediagroup.com/graphql-kotlin-codegen/docs/class-consolidation
*/
classConsolidationEnabled: optional(boolean()),
/**
* Denotes Kotlin classes representing union types to be treated as interfaces rather than annotation classes.
*
Expand Down
4 changes: 2 additions & 2 deletions src/definitions/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { buildTypeMetadata } from "../utils/build-type-metadata";
import { buildAnnotations } from "../annotations/build-annotations";
import { indent } from "@graphql-codegen/visitor-plugin-common";
import { CodegenConfigWithDefaults } from "../config/build-config-with-defaults";
import { inputTypeHasMatchingOutputType } from "../utils/input-type-has-matching-output-type";
import { shouldConsolidateTypes } from "../utils/should-consolidate-types";
import { sanitizeName } from "../utils/sanitize-name";

export function buildInputObjectDefinition(
Expand All @@ -29,7 +29,7 @@ export function buildInputObjectDefinition(
return "";
}

const typeWillBeConsolidated = inputTypeHasMatchingOutputType(node, schema);
const typeWillBeConsolidated = shouldConsolidateTypes(node, schema, config);
if (typeWillBeConsolidated) {
return "";
}
Expand Down
4 changes: 2 additions & 2 deletions src/definitions/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
buildObjectFieldDefinition,
} from "./field";
import { CodegenConfigWithDefaults } from "../config/build-config-with-defaults";
import { inputTypeHasMatchingOutputType } from "../utils/input-type-has-matching-output-type";
import { shouldConsolidateTypes } from "../utils/should-consolidate-types";
import { findTypeInResolverInterfacesConfig } from "../config/find-type-in-resolver-interfaces-config";
import { sanitizeName } from "../utils/sanitize-name";

Expand Down Expand Up @@ -61,7 +61,7 @@ export function buildObjectTypeDefinition(
const typeWillBeConsolidated =
isInputObjectType(potentialMatchingInputType) &&
potentialMatchingInputType.astNode &&
inputTypeHasMatchingOutputType(potentialMatchingInputType.astNode, schema);
shouldConsolidateTypes(potentialMatchingInputType.astNode, schema, config);
const outputRestrictionAnnotation = typeWillBeConsolidated
? ""
: "@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])\n";
Expand Down
7 changes: 4 additions & 3 deletions src/utils/build-type-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import { wrapTypeWithModifiers } from "@graphql-codegen/java-common";
import { CodegenConfigWithDefaults } from "../config/build-config-with-defaults";
import {
getTypeNameWithoutInput,
inputTypeHasMatchingOutputType,
} from "./input-type-has-matching-output-type";
shouldConsolidateTypes,
} from "./should-consolidate-types";

export interface TypeMetadata {
typeName: string;
Expand Down Expand Up @@ -78,9 +78,10 @@ export function buildTypeMetadata(
),
};
} else if (isInputObjectType(schemaType) && schemaType.astNode) {
const typeWillBeConsolidated = inputTypeHasMatchingOutputType(
const typeWillBeConsolidated = shouldConsolidateTypes(
schemaType.astNode,
schema,
config,
);
const typeName = typeWillBeConsolidated
? getTypeNameWithoutInput(schemaType.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,17 @@ import {
isObjectType,
} from "graphql";
import { getBaseTypeNode } from "@graphql-codegen/visitor-plugin-common";
import { CodegenConfigWithDefaults } from "../config/build-config-with-defaults";

export function inputTypeHasMatchingOutputType(
export function shouldConsolidateTypes(
inputNode: InputObjectTypeDefinitionNode,
schema: GraphQLSchema,
config: CodegenConfigWithDefaults,
) {
if (!config.classConsolidationEnabled) {
return false;
}

const inputName = inputNode.name.value;
const typeNameWithoutInput = getTypeNameWithoutInput(inputName);
const matchingTypeName =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { GraphQLKotlinCodegenConfig } from "../../../src/plugin";

export default {
classConsolidationEnabled: false,
} satisfies GraphQLKotlinCodegenConfig;
170 changes: 170 additions & 0 deletions test/unit/should_honor_class_consolidation_config/expected.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package com.kotlin.generated

import com.expediagroup.graphql.generator.annotations.*

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
data class MyTypeToConsolidateWithConfig(
val field: List<String>? = null,
val field2: NestedTypeToConsolidateWithConfig? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
data class MyTypeToConsolidateWithConfigInput(
val field: List<String>? = null,
val field2: NestedTypeToConsolidateWithConfigInput? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
data class NestedTypeToConsolidateWithConfig(
val field: String? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
data class NestedTypeToConsolidateWithConfigInput(
val field: String? = null
)

@GraphQLDescription("A description for MyTypeToConsolidate2")
@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
data class MyTypeToConsolidate2WithConfig(
val field: String? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
data class MyTypeToConsolidate2WithConfigInput(
val field: String? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
data class MyTypeToConsolidate3WithConfig(
val field: String? = null
)

@GraphQLDescription("It ignores the description on the input when consolidating")
@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
data class MyTypeToConsolidate3WithConfigInput(
val field: String? = null
)

@GraphQLDescription("It always uses the type description when consolidating")
@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
data class MyTypeToConsolidate4WithConfig(
val field: String? = null
)

@GraphQLDescription("A description for MyTypeToConsolidateInput4")
@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
data class MyTypeToConsolidate4WithConfigInput(
val field: String? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
data class MyTypeNotToConsolidateWithConfig(
val field: String? = null
)

@GraphQLDescription("The type name must exactly match in order to consolidate")
@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
data class MyTypeToNotConsolidateWithConfigInput(
val field: String? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
data class MyTypeToNotConsolidate2WithConfig(
val field: String? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
data class MyTypeInputToNotConsolidate2WithConfig(
val field: String? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
data class MyTypeWhereFieldsDoNotMatchWithConfig(
val field: String? = null,
val field2: String? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
data class MyTypeWhereFieldsDoNotMatchWithConfigInput(
val field: String? = null,
val field2: Int? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
data class MyTypeToConsolidateParentWithConfig(
val field: MyTypeToConsolidateWithConfig? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
data class MyTypeToConsolidateInputParentWithConfig(
val field: MyTypeToConsolidateWithConfigInput? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
open class MyTypeToConsolidateParent2WithConfig {
open fun field(input: MyTypeToConsolidateWithConfigInput, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("MyTypeToConsolidateParent2WithConfig.field must be implemented.")
}

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
data class MyTypeNotToConsolidateParentWithConfig(
val field: MyTypeNotToConsolidate2WithConfig? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
data class MyTypeNotToConsolidateParentWithConfigInput(
val field: MyTypeNotToConsolidate2WithConfigInput? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
data class MyTypeNotToConsolidate2WithConfig(
val field1: String? = null,
val field2: String? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
data class MyTypeNotToConsolidate2WithConfigInput(
val field1: String? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
data class MySuperSetTypeWithConfig(
val field: String? = null,
val field2: String? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
data class MySuperSetTypeWithConfigInput(
val field: String? = null,
val field2: String? = null,
val field3: Int? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
data class MyTypeWithEnumsWithConfig(
val field1: List<Enum1WithConfig>? = null,
val field2: List<Enum2WithConfig>? = null
)

@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT])
data class MyTypeWithEnumsWithConfigInput(
val field1: List<Enum1WithConfig>? = null,
val field2: List<Enum2WithConfig>? = null
)

enum class Enum1WithConfig {
This,
That;

companion object {
fun findByName(name: String, ignoreCase: Boolean = false): Enum1WithConfig? = values().find { it.name.equals(name, ignoreCase = ignoreCase) }
}
}

enum class Enum2WithConfig {
The_Other;

companion object {
fun findByName(name: String, ignoreCase: Boolean = false): Enum2WithConfig? = values().find { it.name.equals(name, ignoreCase = ignoreCase) }
}
}
Loading

0 comments on commit e92c452

Please sign in to comment.