Skip to content

Conversation

@devin-ai-integration
Copy link

📝 Description

This PR implements shared response type classes in the GraphQL Kotlin client generator to eliminate duplicate type generation across operations. Previously, when multiple queries used the same GraphQL types, the generator would create separate classes in operation-specific packages (e.g., com.example.generated.firstquery.User and com.example.generated.secondquery.User). This PR changes the generator to create shared type classes in a common .types package when operations select identical fields.

Key Changes:

  1. Generator-level type caching: Moved classNameCache, typeToSelectionSetMap, and enumClassToTypeSpecs from context-level to generator-level so they can be shared across all operations during generation.

  2. Package structure change: Response types now use ${packageName}.types instead of ${packageName}.${operationName.lowercase()}, following the same pattern as existing shared types (.inputs, .enums).

  3. Enhanced type collection:

    • Polymorphic types (interfaces/unions) are collected differently based on serialization strategy:
      • Kotlinx: Sealed class with all implementations in a single file (required by kotlinx.serialization)
      • Jackson: Interface and implementations in separate files
    • Non-polymorphic response types in the .types package are collected into shared types
    • Query processing is now sorted by filename for deterministic type numbering
  4. Preserved functionality:

    • Type numbering logic (User, User2, User3) still works when different operations select different fields from the same GraphQL type
    • Enum default values (__UNKNOWN_VALUE) are preserved across operations
    • Fallback implementation classes for interfaces/unions use the shared package

Impact:

  • Breaking change: Fully qualified type names have changed from operation-specific to shared packages
  • Benefit: Reduces code duplication and allows operations to share type instances directly
  • Test coverage: Updated 157 test expectation files to reflect new package structure

Example:

Before:

// FirstQuery.kt
import com.expediagroup.graphql.generated.firstquery.ComplexObject

// SecondQuery.kt  
import com.expediagroup.graphql.generated.secondquery.ComplexObject

After:

// Both queries import from shared package
import com.expediagroup.graphql.generated.types.ComplexObject
import com.expediagroup.graphql.generated.types.ComplexObject2  // Different field selection

🔗 Related Issues

Requested by: @arthurkkp-cog (Arthur Poon)
Link to Devin run: https://app.devin.ai/sessions/46723e01f5414e538819c44c6ee54652

🔍 Review Checklist

Critical areas to review:

  1. Package structure change: This is a breaking change. Verify the new .types package naming is acceptable and aligns with project conventions.

  2. Polymorphic type handling: Check the different logic paths for Kotlinx (sealed classes) vs Jackson (separate files). Pay special attention to:

    • GraphQLClientGenerator.kt lines 203-234
    • Verify sealed class implementations are all collected in the same file for Kotlinx
  3. Type deduplication correctness: Verify that:

    • Types with identical field selections are actually shared (e.g., multiple_queries test case)
    • Types with different field selections get numbered variants (User2, User3)
    • The selection set comparison logic correctly identifies duplicates
  4. Enum default values: Check that __UNKNOWN_VALUE enum defaults are preserved across all operations (see SecondQuery.kt test data).

  5. Test expectations: With 157 test files updated, spot-check a few test cases to ensure expectations match actual generated output:

    • kotlinx/multiple_queries/* - shared types across queries
    • generator/interface_* - polymorphic types
    • generator/union_* - union type handling
  6. Edge cases:

    • Nested object types with different selection sets
    • Named fragments
    • Interface/union fallback implementations
    • Custom scalars in shared types

- Move classNameCache, typeToSelectionSetMap, and enumClassToTypeSpecs to generator level for cross-operation sharing
- Update package naming for response types to use .types package instead of operation-specific packages
- Add shared type collection logic to merge response types from all operations
- Handle polymorphic types (interfaces/unions) with different strategies for Kotlinx vs Jackson serializers
- Update interface/union fallback generation to use shared package
- Update all test expectations to reflect new shared package structure
- Preserve enum default values (__UNKNOWN_VALUE) across multiple operations

This enables multiple queries/mutations to share the same response type classes when they select identical fields from GraphQL types, reducing code duplication while maintaining support for different field selections through numbered type variants (User, User2, etc.).

Co-Authored-By: Arthur Poon <[email protected]>
@devin-ai-integration
Copy link
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant