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

Broken codegen for nested fragments with enum #2989

Closed
tgyhlsb opened this issue May 2, 2023 · 2 comments · Fixed by #3067
Closed

Broken codegen for nested fragments with enum #2989

tgyhlsb opened this issue May 2, 2023 · 2 comments · Fixed by #3067
Assignees
Labels
bug Generally incorrect behavior codegen Issues related to or arising from code generation needs investigation planned-next Slated to be included in the next release

Comments

@tgyhlsb
Copy link
Contributor

tgyhlsb commented May 2, 2023

Summary

When nesting fragments and enums, the "__fulfilled" set is missing some cases and .asSomething() operators fail.

Version

1.1.3

Steps to reproduce the behavior

Here is a simplified schema needed to reproduce:

fragment ConversationItemFragment on ConversationItem {
    ... on Message {
        ...MessageFragment
    }
    ... on ConversationActivity {
        ...ConversationActivityFragment
    }
}

fragment MessageFragment on Message {
    id
    content {
        ...MessageContentFragment
    }
}

fragment MessageContentFragment on MessageContent {
    ... on TextMessageContent {
        ...TextMessageContentFragment
    }
    ... on ImageMessageContent {
        ...ImageMessageContentFragment
    }
}

fragment TextMessageContentFragment on TextMessageContent {
    text
}

fragment ImageMessageContentFragment on ImageMessageContent {
    url
}

Here is the output of the codegen with:

                        schemaNamespace: "GQL",
                        options: .init(
                            selectionSetInitializers: [
                                .fragment(named: "ConversationItemFragment"),
                                .fragment(named: "MessageFragment"),
                            ],
extension GQL {
  struct ConversationItemFragment: GQL.SelectionSet, Fragment {
    struct AsMessage: GQL.InlineFragment {
      init(
        id: GQL.UUID,
        content: MessageFragment.Content,
      ) {
        self.init(_dataDict: DataDict(data: [
          "__typename": GQL.Objects.Message.typename,
          "id": id,
          "content": content._fieldData,
          "__fulfilled": Set([
            ObjectIdentifier(Self.self),
            ObjectIdentifier(ConversationItemFragment.self),
            ObjectIdentifier(MessageFragment.self)
          ])
        ]))
      }
    }
  }
}


extension GQL {
  struct MessageFragment: GQL.SelectionSet, Fragment {
    struct Content: GQL.SelectionSet {
      struct AsTextMessageContent: GQL.InlineFragment, ApolloAPI.CompositeInlineFragment {
        init(
          text: String
        ) {
          self.init(_dataDict: DataDict(data: [
            "__typename": GQL.Objects.TextMessageContent.typename,
            "text": text,
            "__fulfilled": Set([
              ObjectIdentifier(Self.self),
              ObjectIdentifier(MessageFragment.Content.self),
              ObjectIdentifier(MessageContentFragment.self),
              ObjectIdentifier(TextMessageContentFragment.self)
            ])
          ]))
        }
      }
    }
  }
}

Then when I try to use the generated initializer:

let item = GQL.ConversationItemFragment.AsMessage(
  id: GQL.UUID(),
  content: .AsTextMessageContent(text: "Hello world!").asRootEntityType
)

func foo(_ item: GQL.ConversationItemFragment) {
  guard let message = item.asMessage?.fragments.messageFragment else {
    print("asMessage is broken")
  }
  guard let textContent = message.content.fragments.messageContentFragment.asTextMessageContent else {
    print("asTextMessageContent is broken")
  }
  print("Success \(textContent)")
}

foo(item.asRootEntityType)

It prints asTextMessageContent is broken.

This is because the definition of asTextMessageContent expects some MessageContentFragment.AsTextMessageContent type. But such type is not listed in the __fulfilled set.

  struct MessageContentFragment: GQL.SelectionSet, Fragment {
    var asTextMessageContent: AsTextMessageContent? { _asInlineFragment() }
  }
(lldb) po ObjectIdentifier(GQL.MessageContentFragment.AsTextMessageContent) == ObjectIdentifier(GQL.TextMessageContentFragment)
false

(lldb) po ObjectIdentifier(GQL.MessageContentFragment.AsTextMessageContent) == ObjectIdentifier(GQL.MessageContentFragment)
false

(lldb) po ObjectIdentifier(GQL.MessageContentFragment.AsTextMessageContent) == ObjectIdentifier(GQL.MessageFragment.Content)
false

Logs

No response

Anything else?

No response

@tgyhlsb tgyhlsb added bug Generally incorrect behavior needs investigation labels May 2, 2023
@AnthonyMDev AnthonyMDev self-assigned this May 2, 2023
@AnthonyMDev AnthonyMDev added this to the Patch Releases (1.1.x) milestone May 2, 2023
@AnthonyMDev
Copy link
Contributor

Thank you for the detailed reproduction case! We'll look into this as soon as we can!

@AnthonyMDev AnthonyMDev added the codegen Issues related to or arising from code generation label May 2, 2023
@AnthonyMDev AnthonyMDev added the planned-next Slated to be included in the next release label May 18, 2023
@AnthonyMDev
Copy link
Contributor

The PR that fixes this is now up! Thanks for catching this @tgyhlsb!

We'll have it out in the next patch release. :)

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 needs investigation planned-next Slated to be included in the next release
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants