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

Migration from 0.53 to 1.15 missing factory methods & alternatives #3482

Closed
bsrz opened this issue Dec 3, 2024 · 6 comments
Closed

Migration from 0.53 to 1.15 missing factory methods & alternatives #3482

bsrz opened this issue Dec 3, 2024 · 6 comments
Labels
question Issues that have a question which should be addressed

Comments

@bsrz
Copy link

bsrz commented Dec 3, 2024

Question

We migrating our giant code base from 0.53 to 1.15.
There used to be generated factory methods to create types. We used those for tests. For example:

ButtonsRowFragment(
    buttons: [
        .makeAppPresentation_PrimaryButtonComponent(icon: "time", link: .init(
            unsafeResultMap: InternalLinkFragment(
                text: .init(text: "AppAccountSupport"),
                route: .init(unsafeResultMap: RoutingRouteFragment(
                    page: "AppAccountSupport",
                    params: [:]
                ).resultMap)
            ).resultMap
        )),
        .makeAppPresentation_BorderlessButton(link: .init(
            unsafeResultMap: InternalLinkFragment(
                text: .init(text: "AppAccountSupport"),
                route: .init(unsafeResultMap: RoutingRouteFragment(
                    page: "AppAccountSupport",
                    params: [:]
                ).resultMap)
            ).resultMap
        )),
        .makeAppPresentation_SecondaryButtonComponent(link: .init(
            unsafeResultMap: InternalLinkFragment(
                text: .init(text: "AppAccountSupport"),
                route: .init(unsafeResultMap: RoutingRouteFragment(
                    page: "AppAccountSupport",
                    params: [:]
                ).resultMap)
            ).resultMap
        )),
        .makeAppPresentation_TertiaryCommerceButton(link: .init(
            unsafeResultMap: InternalLinkFragment(
                text: .init(text: "AppAccountSupport"),
                route: .init(unsafeResultMap: RoutingRouteFragment(
                    page: "AppAccountSupport",
                    params: [:]
                ).resultMap)
            ).resultMap
        )),
    ]
)

I'm trying to recreate the first button from the example above:

ButtonsRowFragment.Button(
    _dataDict: ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent(
        icon: "time",
        link: ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent.Link(
            _dataDict: ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent.Link.AsAppPresentation_InternalOrExternalLink(
                _dataDict: InternalOrExternalLinkFragment(
                    _dataDict: InternalOrExternalLinkFragment.AsAppPresentation_InternalLink(
                        text: InternalOrExternalLinkFragment.AsAppPresentation_InternalLink.Text(text: "AppAccountSupport"),
                        route: InternalOrExternalLinkFragment.AsAppPresentation_InternalLink.Route(
                            page: "AppAccountSupport",
                            params: .dictionary([:])
                        )
                    ).__data
                ).__data
            ).__data
        )
    ).__data
)

and not matter what I do, value.asAppPresentation_PrimaryButtonComponent?.link?.asAppPresentation_InternalOrExternalLink always ends up being nil. I feel like I've tried a ton of permutations but none of them work. I'm clearly missing something. Am I missing something completely obvious? It appears that using .__data doesn't cary the typenames over? Are there any docs on how resultMap compares to DataDict?

Any help would be very appreciated 🙌

@bsrz bsrz added the question Issues that have a question which should be addressed label Dec 3, 2024
@calvincestari
Copy link
Member

Hi @bsrz - it sounds like you're looking for the Selection Set Initializers feature. This provides an equivalent way to instantiate the generated models.

and not matter what I do, value.asAppPresentation_PrimaryButtonComponent?.link?.asAppPresentation_InternalOrExternalLink always ends up being nil. I feel like I've tried a ton of permutations but none of them work. I'm clearly missing something. Am I missing something completely obvious? It appears that using .__data doesn't cary the typenames over? Are there any docs on how resultMap compares to DataDict?

It's recommended to steer clear of the underscored properties/functions, such as the _dataDict initializer. While they're public they are meant for internal use and could change without notice.

As an FYI - the reason you're always getting nil is because DataDict also contains a fulfilledFragments property which needs to be correctly set for the underlying type logic to work correctly. Once you re-generate your models with the selection set initializers you'll be able to see what I'm talking about.

Try that and let me know if it still doesn't resolve your issue.

@calvincestari calvincestari closed this as not planned Won't fix, can't repro, duplicate, stale Dec 4, 2024
Copy link
Contributor

github-actions bot commented Dec 4, 2024

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo iOS usage and allow us to serve you better.

@bsrz
Copy link
Author

bsrz commented Dec 5, 2024

@calvincestari thank you for the reply.
This is the config I'm working with at the moment:

{
    "schemaNamespace": "GraphQL",
    "operationManifest": {
        "generateManifestOnCodeGeneration": true,
        "path": "../../Generated/operation_ids.json",
        "version": "persistedQueries"
    },
    "experimentalFeatures": {
        "fieldMerging": [
            "all"
        ],
        "legacySafelistingCompatibleOperations": true
    },
    "input": {
        "schemaSearchPaths": [
            "./*.graphqls"
        ],
        "operationSearchPaths": [
            "../**/*.graphql"
        ]
    },
    "options": {
        "pruneGeneratedFiles": true,
        "selectionSetInitializers": {
            "operations": true,
            "namedFragments": true
        },
    },
    "output": {
        "schemaTypes": {
            "moduleType": {
                "other": {}
            },
            "path": "../../Generated/"
        },
        "operations": {
            "inSchemaModule": {}
        },
        "testMocks": {
            "none": {}
        }
    }
}

I already have the selectionSetInitializers property set to true for operations and named fragments. Is there another property I'm missing?

If you see in the example I posted in the original comment, the inits with DataDict are the only inits available to me except for the ones that have an generated initializer:

type name init
ButtonsRowFragment.Button DataDict
ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent generated init
ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent.Link DataDict
ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent.Link.AsAppPresentation_InternalOrExternalLink DataDict
InternalOrExternalLinkFragment DataDict
InternalOrExternalLinkFragment.AsAppPresentation_InternalLink generated init
InternalOrExternalLinkFragment.AsAppPresentation_InternalLink.Text generated init
InternalOrExternalLinkFragment.AsAppPresentation_InternalLink.Route generated init

I should mention that some of these types are Union types.
Is there a way to generate initializers for the types that don't have any?

@calvincestari
Copy link
Member

calvincestari commented Dec 5, 2024

I should mention that some of these types are Union types.
Is there a way to generate initializers for the types that don't have any?

Not at the moment no. I think you might be needing to use the asRootEntityType property for those types.

If you're able to share some of the Swift generated types (such as ButtonsRowFragment.Button, ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent) to my email address then I can take a closer look.

@bsrz
Copy link
Author

bsrz commented Dec 5, 2024

Not at the moment no. I think you might be needing to use the asRootEntityType property for those types.

yeah, so I've tried that too, these are all the tests I've written where the assertions fails because the property is continuously nil

    func test0() {
        let internalLink = InternalOrExternalLinkFragment.AsAppPresentation_InternalLink(
            text: InternalOrExternalLinkFragment.AsAppPresentation_InternalLink.Text(text: "AppAccountSupport"),
            route: InternalOrExternalLinkFragment.AsAppPresentation_InternalLink.Route(
                page: "AppAccountSupport",
                params: .dictionary([:])
            )
        )

        let intOrExt = InternalOrExternalLinkFragment(
            _dataDict: internalLink.__data
        )
        let link0 = ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent.Link.AsAppPresentation_InternalOrExternalLink(
            _dataDict: intOrExt.__data
        )
        let link = ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent.Link(
            _dataDict: link0.__data
        )
        let data = ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent(
            icon: "time",
            link: link
        )
        let finalButton = ButtonsRowFragment.Button(
            _dataDict: data.__data
        )

        XCTAssertNotNil(finalButton.asAppPresentation_PrimaryButtonComponent?.link?.asAppPresentation_InternalOrExternalLink?.fragments.internalOrExternalLinkFragment)
    }

    func test1() {
        let internalLink = InternalOrExternalLinkFragment.AsAppPresentation_InternalLink(
            text: InternalOrExternalLinkFragment.AsAppPresentation_InternalLink.Text(text: "AppAccountSupport"),
            route: InternalOrExternalLinkFragment.AsAppPresentation_InternalLink.Route(
                page: "AppAccountSupport",
                params: .dictionary([:])
            )
        ).asRootEntityType

        let link = ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent.Link.AsAppPresentation_InternalOrExternalLink(
            _dataDict: internalLink.__data
        ).asRootEntityType

        let finalButton = ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent(
            icon: "time",
            link: link
        ).asRootEntityType

        XCTAssertNotNil(finalButton.asAppPresentation_PrimaryButtonComponent?.link?.asAppPresentation_InternalOrExternalLink?.fragments.internalOrExternalLinkFragment)
    }

    func test2() {
        let otherLink = InternalLinkFragment(
            text: InternalLinkFragment.Text(text: "AppAccountSupport"),
            route: InternalLinkFragment.Route(
                page: "AppAccountSupport",
                params: .dictionary([:])
            )
        )

        let link = ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent.Link.AsAppPresentation_InternalOrExternalLink(
            _dataDict: otherLink.__data
        ).asRootEntityType

        let finalButton = ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent(
            icon: "time",
            link: link
        ).asRootEntityType

        XCTAssertNotNil(finalButton.asAppPresentation_PrimaryButtonComponent?.link?.asAppPresentation_InternalOrExternalLink?.fragments.internalOrExternalLinkFragment)
    }

    func test3() {
        let data = DataDict(
            data: [
                "__typename": "AppPresentation_InternalOrExternalLink",
                "text": "AppAccountSupport",
            ],
            fulfilledFragments: [
                ObjectIdentifier(ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent.Link.self),
                ObjectIdentifier(ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent.Link.AsAppPresentation_InternalOrExternalLink.self),
            ]
        )

        let link = ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent.Link.AsAppPresentation_InternalOrExternalLink(
            _dataDict: data
        ).asRootEntityType

        let finalButton = ButtonsRowFragment.Button.AsAppPresentation_PrimaryButtonComponent(
            icon: "time",
            link: link
        ).asRootEntityType

        XCTAssertNotNil(finalButton.asAppPresentation_PrimaryButtonComponent?.link?.asAppPresentation_InternalOrExternalLink?.fragments.internalOrExternalLinkFragment)
    }

I've emailed you the full generated code for this type. Let me know if you find anything obvious.

@calvincestari
Copy link
Member

@bsrz - I've created #3486 and #3487 to track the issues we discovered while looking through the code you sent.

We can track progress in those issues and I'll reach out to you in email for a workaround until the fixes are in place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Issues that have a question which should be addressed
Projects
None yet
Development

No branches or pull requests

2 participants