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

Some types don't resolve anymore after updating to 1.2.1 #86

Closed
cshadek opened this issue Oct 7, 2022 · 8 comments
Closed

Some types don't resolve anymore after updating to 1.2.1 #86

cshadek opened this issue Oct 7, 2022 · 8 comments
Labels

Comments

@cshadek
Copy link
Contributor

cshadek commented Oct 7, 2022

After removing the Types construct as recommended in #84, some types don't resolve anymore.

So far I've been able to narrow it down to:

  1. Union types
  2. Connection types
  3. Types in an array
  4. Also types of fields on the type in an array no longer resolve and need to be included in the Types construct

If I keep the Types construct (which is deprecated) to include the types above and still remove all instances of TypeReference in the schema, it resolves.

@NeedleInAJayStack
Copy link
Member

NeedleInAJayStack commented Oct 7, 2022

Oh dang! Thanks for reporting @cshadek!

From a quick initial investigation, I'm seeing that the Graphiti tests for resolution are passing on union types, array types, and type fields within arrays. We don't seem to have any tests on ConnectionTypes though.

Do you have a schema I could use to recreate the issues you are seeing? It would help me diagnose the issue and I'd love to use it to write testing coverage if we're missing it.

Thanks again!

@cshadek
Copy link
Contributor Author

cshadek commented Oct 7, 2022

Thanks for getting back so quickly @NeedleInAJayStack!

So I'm not entirely sure why this worked, but I was able to get the Union types and Array types to resolve by changing how the schema was declared.

I was using the old way:

static func schema() throws -> Schema<MainAPI, UserContext> {

and am now doing it as recommended:

let schema = try! Schema<RootResolver, UserContext>(coders: coders) {

For some reason this one change caused the Union and Array types to resolve. I don't really understand why.

Still the connection types are not resolving and then also it seems that nested types inside array types are not resolving either.

Unfortunately, I can't share the exact schema as it's proprietary.

Here's an example:

Type(ParentObject.self, interfaces: [InterfaceA.self, InterfaceB.self]) {
                Field("objects", at: ParentObject.objects, as: [ObjectA].self)
 }

Type(ObjectA.self, interfaces: [InterfaceA.self]) {
                Field("objectB", at: ObjectA.objectB, as: ObjectB?.self)
                Field("objectC", at: ObjectA.objectC, as: ObjectC?.self)
 }
            
Type(ObjectB.self) {
...
}
            
Type(ObjectC.self) {
....
 }

ParentObject, ObjectA, ObjectB, and ObjectC are all Codable types.

I haven't confirmed yet if the schema I just created above resolves or not. I'll check on that, but this is a rough recreation of the schema in question.

In my schema when I comment out the line with the array, all the types resolve. So, I figure it must have to do with the array part.

The actual schema is much more extensive, but this is what seems to be causing a problem. For some reason, ObjectB and ObjectC don't get resolved unless I include them in the Types construct. The server returns an error on introspection.

The only other cause I could think of is if the depth of the schema graph matters. Both ObjectB and ObjectC in my case are nested several layers down the graph.

@NeedleInAJayStack
Copy link
Member

Darn, I'm still having trouble recreating this. I built the test file below based on your example and everything still worked. What is the specific error that you are seeing? And is it on the Schema creation or the query resolution step?

import Graphiti
import NIO
import XCTest

private protocol InterfaceA: Codable {
    var interfaceAField: String? { get }
}
private protocol InterfaceB: Codable {
    var interfaceBField: String? { get }
}
private struct ParentObject: Codable, InterfaceA, InterfaceB {
    let interfaceAField: String?
    let interfaceBField: String?
    let objects: [ObjectA]
}
private struct ObjectA: Codable, InterfaceA {
    let interfaceAField: String?
    let objectB: ObjectB?
    let objectC: ObjectC?
}
private struct ObjectB: Codable {
    let scalar: String
}
private struct ObjectC: Codable {
    let scalar: String
}
private struct TestResolver {
    func parentObject(context _: NoContext, arguments _: NoArguments) -> ParentObject {
        return ParentObject(
            interfaceAField: "ParentObject is A",
            interfaceBField: "ParentObject is B",
            objects: [
                ObjectA(
                    interfaceAField: "ObjectA1 is A",
                    objectB: ObjectB(scalar: "I'm A1's B"),
                    objectC: ObjectC(scalar: "I'm A1's C")
                ),
                ObjectA(
                    interfaceAField: "ObjectA2 is A",
                    objectB: ObjectB(scalar: "I'm A2's B"),
                    objectC: ObjectC(scalar: "I'm A2's C")
                )
            ]
        )
    }
}
private class TestAPI<Resolver, ContextType>: API {
    public let resolver: Resolver
    public let schema: Schema<Resolver, ContextType>

    init(resolver: Resolver, schema: Schema<Resolver, ContextType>) {
        self.resolver = resolver
        self.schema = schema
    }
}

class ReportedTests: XCTestCase {
    func testReportedError() throws {
        let testSchema = try Schema<TestResolver, NoContext> {
            Interface(InterfaceA.self) {
                Field("interfaceAField", at: \.interfaceAField)
            }
            Interface(InterfaceB.self) {
                Field("interfaceBField", at: \.interfaceBField)
            }
            
            Type(ParentObject.self, interfaces: [InterfaceA.self, InterfaceB.self]) {
                Field("interfaceAField", at: \.interfaceAField)
                Field("interfaceBField", at: \.interfaceBField)
                Field("objects", at: \.objects, as: [ObjectA].self)
            }
            Type(ObjectA.self, interfaces: [InterfaceA.self]) {
                Field("interfaceAField", at: \.interfaceAField)
                Field("objectB", at: \.objectB, as: ObjectB?.self)
                Field("objectC", at: \.objectC, as: ObjectC?.self)
            }
            Type(ObjectB.self) {
                Field("scalar", at: \.scalar)
            }
            Type(ObjectC.self) {
                Field("scalar", at: \.scalar)
            }
            
            Query {
                Field("parentObject", at: TestResolver.parentObject)
            }
        }
        let api = TestAPI<TestResolver, NoContext>(
            resolver: TestResolver(),
            schema: testSchema
        )

        let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
        defer { try? group.syncShutdownGracefully() }

        print(
            try api.execute(
                request: """
                query {
                  parentObject {
                    interfaceAField
                    interfaceBField
                    objects {
                        interfaceAField
                        objectB {
                            scalar
                        }
                        objectC {
                            scalar
                        }
                    }
                  }
                }
                """,
                context: NoContext(),
                on: group
            ).wait()
        )
//        Prints:
//        {
//          "data": {
//            "parentObject": {
//              "interfaceAField": "ParentObject is A",
//              "interfaceBField": "ParentObject is B",
//              "objects": [
//                {
//                  "interfaceAField": "ObjectA1 is A",
//                  "objectB": {
//                    "scalar": "I'm A1's B"
//                  },
//                  "objectC": {
//                    "scalar": "I'm A1's C"
//                  }
//                },
//                {
//                  "interfaceAField": "ObjectA2 is A",
//                  "objectB": {
//                    "scalar": "I'm A2's B"
//                  },
//                  "objectC": {
//                    "scalar": "I'm A2's C"
//                  }
//                }
//              ]
//            }
//          }
//        }
    }
}

@d-exclaimation
Copy link
Member

Sorry for just jumping to the conversation here.

I am also interested in which types wouldn't resolve and whether that's an issue during the schema building or the resolving process.

In my case, Both Union and Array types are working fine for me, even with using

func schema() throws -> Schema<Resolver, Context> {

Resolving union

Screenshot 2022-11-01 at 7 24 49 PM

Resolving other types within an Array

Screenshot 2022-11-01 at 7 25 41 PM

I have the working example here

https://github.com/d-exclaimation/pioneer-example/tree/v1

Not sure whether there are other requirements needed to recreate the issue or whether my example is even relevant to the issue to begin with.

@NeedleInAJayStack
Copy link
Member

Hey @cshadek, have you been able to get a reproducer of this issue? If not, I'm planning to close it with the can't reproduce status. Thanks!

@cshadek
Copy link
Contributor Author

cshadek commented Jan 30, 2023

I was able to fix the issue by reordering some of the declarations, and I'm still not sure why the issue was happening.

It's still a problem with ConnectionTypes right? I haven't checked in a while - I still have a Types block with all my ConnectionTypes.

@NeedleInAJayStack
Copy link
Member

@cshadek Yeah, there used to be some weird ordering requirements but they should have been resolved by #84 and #90

As for ConnectionTypes, I'm not sure. I wasn't able to recreate the issue. If you can get me a reproducer, I'd be happy to debug.

@cshadek
Copy link
Contributor Author

cshadek commented Jan 30, 2023

@NeedleInAJayStack, so I just checked and ConnectionTypes seem to work fine now without the Types block, so I'll close this issue.

@cshadek cshadek closed this as completed Jan 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants