Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RootEntityType of some inline fragments has a wrong type #2949

Closed
martin-muller opened this issue Apr 11, 2023 · 12 comments · Fixed by #2956
Closed

RootEntityType of some inline fragments has a wrong type #2949

martin-muller opened this issue Apr 11, 2023 · 12 comments · Fixed by #2956
Assignees
Labels
bug Generally incorrect behavior codegen Issues related to or arising from code generation

Comments

@martin-muller
Copy link

Summary

I've tried migrating from 1.0.7 to 1.1.1, but stumbled upon an issue with generated models

Some inline fragments generated using ApolloCodegen 1.1.1 point to a type that doesn't exist in that context resulting in a '...' is not a member type of struct 'GQLClient.SomeQuery.Data....' compile-time error.

Version

1.1.1

Steps to reproduce the behavior

The issue does not exhibit with all models, so far it seems like its only inline union types:

query Article($id: ID!) {
  article(where: { id: $id }) {
    article {
      ... on SomeArticle {
        id
        title
      }
    }
  }
}

(made up query that's not far off the one we're having issue with, happy to provide the concrete query in private channels)

Maybe worth mentioning that some root fields share the same name as our query types, maybe that's confusing the code gen?

Providing the full path to the type fixes the issue, eg. replacing public typealias RootEntityType = Article.Article with public typealias RootEntityType = ArticleQuery.Data.Article.

Logs

No response

Anything else?

In one query we have the same issue with a type in __mergedSources.

@martin-muller martin-muller added bug Generally incorrect behavior needs investigation labels Apr 11, 2023
@calvincestari
Copy link
Member

Thanks for reporting this @martin-muller, we'll take a look into it asap.

cc. @AnthonyMDev

@calvincestari calvincestari added the codegen Issues related to or arising from code generation label Apr 11, 2023
@AnthonyMDev
Copy link
Contributor

We can probably just fully qualify these names, but I'd really like a reproduction case so our unit tests can ensure this doesn't regress.

I haven't been able to come up with a reproduction quickly with the examples I have. @martin-muller could you send me your codegen configuration file so I can try to replicate this? If you don't want to post it here, sending it to me at Anthony@apollographql.com would be great!

@tahirmt
Copy link
Contributor

tahirmt commented Apr 12, 2023

I have a similar issue where some inline fragments will have a type like this public typealias RootEntityType = Query.ExpectedType or public typealias RootEntityType = Mutation.ExpectedType

So far I haven't been able to create an isolated schema and queries to to be able to send over.

@martin-muller
Copy link
Author

Thanks for taking a look @AnthonyMDev, I've sent you an email with our config and query.

@AnthonyMDev
Copy link
Contributor

This fix for this is merged into main! A new patch version will likely go out early next week.

@AnthonyMDev AnthonyMDev added this to the Patch Releases (1.1.x) milestone Apr 14, 2023
@tahirmt
Copy link
Contributor

tahirmt commented Apr 15, 2023

@AnthonyMDev I'm trying out the main branch now and the codegen still produces my issue as I described above. The code does not compile because instead of having the operation name from the same file it just has Query in the RootEntityType generation. Where Node here represents an interface

public typealias RootEntityType = Query.Node // causes a build error here due to not being able to find the type

I was able to reproduce it in an isolated project with only one query and interface. Would you be able to take a look?

Schema
schema {
  query: RootQueryType
}

type RootQueryType {
  node(
    id: ID!
  ): Node
}

interface Node {
"""The ID of the object."""
  id: ID!
}

type SomeSpecialNode implements Node {
  id: ID!
}
Query
query SomeSpecialNode($sectionId: ID!) {
  node(id: $sectionId) {
      ... on SomeSpecialNode {
          id
      }
  }
}
Config
{
"schemaNamespace" : "API",
"input" : {
  "operationSearchPaths" : [
    "**/*.graphql"
  ],
  "schemaSearchPaths" : [
    "**/*.graphqls"
  ]
},
"output" : {
  "testMocks" : {
    "none" : {
    }
  },
  "schemaTypes" : {
    "path" : "./API",
    "moduleType" : {
      "swiftPackageManager" : {
        "name" : "API"
      }
    }
  },
  "operations" : {
    "inSchemaModule" : {
    }
  }
},
"options": {
  "apqs": "automaticallyPersist",
  "cocoapodsCompatibleImportStatements": false,
  "selectionSetInitializers" : {
    "operations": true,
    "namedFragments": true,
    "localCacheMutations" : true
  }
}
}
Generated
// @generated
// This file was automatically generated and should not be edited.

@_exported import ApolloAPI

public class SomeSpecialNodeQuery: GraphQLQuery {
  public static let operationName: String = "SomeSpecialNode"
  public static let document: ApolloAPI.DocumentType = .automaticallyPersisted(
    operationIdentifier: "cf7bd27f38c5fb6a45210e0e90be0acb2f7e5d31e3161507b47d22ca4d74df77",
    definition: .init(
      #"""
      query SomeSpecialNode($sectionId: ID!) {
        node(id: $sectionId) {
          __typename
          ... on SomeSpecialNode {
            id
          }
        }
      }
      """#
    ))

  public var sectionId: ID

  public init(sectionId: ID) {
    self.sectionId = sectionId
  }

  public var __variables: Variables? { ["sectionId": sectionId] }

  public struct Data: API.SelectionSet {
    public let __data: DataDict
    public init(_dataDict: DataDict) { __data = _dataDict }

    public static var __parentType: ApolloAPI.ParentType { API.Objects.RootQueryType }
    public static var __selections: [ApolloAPI.Selection] { [
      .field("__typename", String.self),
      .field("node", Node?.self, arguments: ["id": .variable("sectionId")]),
    ] }

    public var node: Node? { __data["node"] }

    public init(
      node: Node? = nil
    ) {
      self.init(_dataDict: DataDict(data: [
        "__typename": API.Objects.RootQueryType.typename,
        "node": node._fieldData,
        "__fulfilled": Set([
          ObjectIdentifier(Self.self)
        ])
      ]))
    }

    /// Node
    ///
    /// Parent Type: `Node`
    public struct Node: API.SelectionSet {
      public let __data: DataDict
      public init(_dataDict: DataDict) { __data = _dataDict }

      public static var __parentType: ApolloAPI.ParentType { API.Interfaces.Node }
      public static var __selections: [ApolloAPI.Selection] { [
        .field("__typename", String.self),
        .inlineFragment(AsSomeSpecialNode.self),
      ] }

      public var asSomeSpecialNode: AsSomeSpecialNode? { _asInlineFragment() }

      public init(
        __typename: String
      ) {
        self.init(_dataDict: DataDict(data: [
          "__typename": __typename,
          "__fulfilled": Set([
            ObjectIdentifier(Self.self)
          ])
        ]))
      }

      /// Node.AsSomeSpecialNode
      ///
      /// Parent Type: `SomeSpecialNode`
      public struct AsSomeSpecialNode: API.InlineFragment {
        public let __data: DataDict
        public init(_dataDict: DataDict) { __data = _dataDict }

        public typealias RootEntityType = Query.Node
        public static var __parentType: ApolloAPI.ParentType { API.Objects.SomeSpecialNode }
        public static var __selections: [ApolloAPI.Selection] { [
          .field("id", API.ID.self),
        ] }

        public var id: API.ID { __data["id"] }

        public init(
          id: API.ID
        ) {
          self.init(_dataDict: DataDict(data: [
            "__typename": API.Objects.SomeSpecialNode.typename,
            "id": id,
            "__fulfilled": Set([
              ObjectIdentifier(Self.self),
              ObjectIdentifier(Query.Node.self)
            ])
          ]))
        }
      }
    }
  }
}

sample project
apollo-codegen-issue.zip

@AnthonyMDev
Copy link
Contributor

Thank you so so much for the reproduction case! Will look into this ASAP.

@ay8s
Copy link

ay8s commented Apr 17, 2023

@AnthonyMDev Fix is working well for us. Thanks 👍

@tahirmt
Copy link
Contributor

tahirmt commented Apr 17, 2023

Thank you! I suspect it's due to the RootQueryType in our schema and it is related to #2955

@martin-muller
Copy link
Author

Thanks for the fix! But I think I'm facing similar issue when I enable selectionSetInitializers. Types that don't exist in the given context are being passed to ObjectIdentifier for the __fulfilled field.

eg. codegen generates:

"__fulfilled": Set([
   ObjectIdentifier(Self.self),
   ObjectIdentifier(Cat.Cat.self),
]),

but it should generate:

"__fulfilled": Set([
   ObjectIdentifier(Self.self),
   ObjectIdentifier(Query.Data.Cat.Cat.self),
]),

Happy to make a separate issue or provide examples from our project @AnthonyMDev

@AnthonyMDev
Copy link
Contributor

Looking into this one now. Sorry I missed this.

Think I should have a fix for it shortly.

AnthonyMDev added a commit that referenced this issue May 25, 2023
AnthonyMDev added a commit that referenced this issue May 30, 2023
@AnthonyMDev
Copy link
Contributor

@martin-muller PR #3045 should fix the issue you're having here as well. Should be merged into main soon and included in our next patch release.

AnthonyMDev added a commit that referenced this issue May 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Generally incorrect behavior codegen Issues related to or arising from code generation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants