Search code examples
javagraphqlgraphql-java

Graphql no resolver definied for interface/union - java


I have problem adding resolver using this approach in graphql:

@RestController
@RequestMapping("/api/dictionary/")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DictionaryController {
    @Value("classpath:items.graphqls")
    private Resource schemaResource;
    private GraphQL graphQL;
    private final DictionaryService dictionaryService;

    @PostConstruct
    public void loadSchema() throws IOException {
        File schemaFile = schemaResource.getFile();
        TypeDefinitionRegistry registry = new SchemaParser().parse(schemaFile);
        RuntimeWiring wiring = buildWiring();
        GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(registry, wiring);
        graphQL = GraphQL.newGraphQL(schema).build();
    }

private RuntimeWiring buildWiring() {
    DataFetcher<List<DictionaryItemWithParentDto>> fetcher6 = dataFetchingEnvironment -> dictionaryService.getClaimSubType();

        return RuntimeWiring.newRuntimeWiring()
                .type("Query", typeWriting ->
                   typeWriting
                    .dataFetcher("getClaimSubType", fetcher6)
                    )
                .build();
    }


public List<DictionaryItemWithParentDto> getClaimSubType() {
    return dictionaryService.getClaimSubType();
   }
  }

items.graphqls file content:

type Query {
    getClaimSubType: [DictionaryItemWithParentDto]
}

type DictionaryItemWithParentDto {
    code: String!
    name: String
    parents: [DictionaryItemDto]
}

type DictionaryItemDto {
    code: String!
    name: String
    description: String
}

In java I have Vehicle interface and two classes that implement it: Airplane and Car. When i add to schema this line:

union SearchResult = Airplane | Car

I get following error:

There is no type resolver defined for interface / union 'Vehicle' type, There is no type resolver defined for interface / union 'SearchResult' type]}

I am not sure how to handle it.

If instead i add:

interface Vehicle {
    maxSpeed: Int
}

type Airplane implements Vehicle {
    maxSpeed: Int
    wingspan: Int
}

type Car implements Vehicle {
    maxSpeed: Int
    licensePlate: String
}

I get following error:

errors=[There is no type resolver defined for interface / union 'Vehicle' type]

How can i handle these errors using my approach ? Is there another approach to handle it?

Edit

Adding these lines of code fix the issue partway i guess:

TypeResolver t =  new TypeResolver() {
    @Override
    public GraphQLObjectType getType(TypeResolutionEnvironment env) {
        Object javaObject = env.getObject();
        if (javaObject instanceof Car) {
            return env.getSchema().getObjectType("Car");
        } else if (javaObject instanceof Airplane) {
            return env.getSchema().getObjectType("Airplane");
        } else {
            return env.getSchema().getObjectType("Car");
        }
    }
};

And adding to RuntimeWiring builder this:

            .type("Vehicle", typeWriting ->
                    typeWriting
                            .typeResolver(t)
            )


    @PostMapping("getVehicle")
    public ResponseEntity<Object> getVehicleMaxSpeed(@RequestBody String query) 
   {
        ExecutionResult result = graphQL.execute(query);
        return new ResponseEntity<Object>(result, HttpStatus.OK);
    }

When asking for:

query {
    getVehicle(maxSpeed: 30) {
        maxSpeed

    }
}

I get the maxSpeed but when i add wingspan i get an error

Field 'wingspan' in type 'Vehicle' is undefined @ 'getVehicle/wingspan'",

I added

getVehicle(maxSpeed: Int): Vehicle

To the graphqls file. I thought that polymorphism would work here.


Solution

  • Adding these lines of code fix the issue:

    TypeResolver t =  new TypeResolver() {
        @Override
        public GraphQLObjectType getType(TypeResolutionEnvironment env) {
            Object javaObject = env.getObject();
            if (javaObject instanceof Car) {
                return env.getSchema().getObjectType("Car");
            } else if (javaObject instanceof Airplane) {
                return env.getSchema().getObjectType("Airplane");
            } else {
                return env.getSchema().getObjectType("Car");
            }
        }
    };
    

    And adding to RuntimeWiring builder this:

                .type("Vehicle", typeWriting ->
                        typeWriting
                                .typeResolver(t)
                )
    
    
        @PostMapping("getVehicle")
        public ResponseEntity<Object> getVehicleMaxSpeed(@RequestBody String query) 
       {
            ExecutionResult result = graphQL.execute(query);
            return new ResponseEntity<Object>(result, HttpStatus.OK);
        }
    

    When asking for:

    query {
        getVehicle(maxSpeed: 30) {
            maxSpeed
    
        }
    }
    

    I get the maxSpeed but when i add wingspan i get an error

    Field 'wingspan' in type 'Vehicle' is undefined @ 'getVehicle/wingspan'",
    

    I added

    getVehicle(maxSpeed: Int): Vehicle
    

    To the graphqls file. I thought that polymorphism would work here.

    If i want fields from subclasses i can ask for them like that:

    query {
        getVehicle(maxSpeed: 10) {
            maxSpeed
            ... on Airplane {
            wingspan
          }
            ... on Car {
            licensePlate
          }
        }
    }