Validation error of type FieldUndefined: Field 

2019-06-07 06:56发布

问题:

I am new to GrapQL. I am trying to use it with spring boot. I can make query successfully, it is returning the data that I need, but i want now to use mutation. I need to add a use to database when he registers.

This is my schema.graphqls file:

type Token {
    token: String
}
type Register {
    message: String
}
type User {
    username: String!
    firstName: String!
    lastName: String!
    password: String!
    role: String!
}

type Query {
    login(username: String, password: String): Token
}

type Mutation {
    register(input: RegisterUserInput!): Register
}

input RegisterUserInput {
    username: String!
    firstName: String!
    lastName: String!
    password: String!
    role: String!
}

schema {
    query: Query
    mutation: Mutation
}

So as you can see register is in Mutation type, which is added in schema as is Query. But for some reason it looks like it is not going into Mutation, it is only trying to find the types in Query.

This is my controller:

@Autowired
    private UserService userService;

    /**
     * Login the user and return generated token
     * @param query
     * @return String token
     */
    @PostMapping("/login")
    public ResponseEntity<Object> login(@RequestBody String query){
        ExecutionResult executionResult = userService.getGraphQL().execute(query);

        // Check if there are errors
        if(!executionResult.getErrors().isEmpty()){
            return new ResponseEntity<>(executionResult.getErrors().get(0).getMessage(), HttpStatus.UNAUTHORIZED);
        }

        return new ResponseEntity<>(executionResult, HttpStatus.OK);
    }

    /**
 * Create new user and save him to database
 * @param mutation
 * @return String message
 */
@PostMapping("/register")
public ResponseEntity<Object> register(@RequestBody String mutation){
    ExecutionResult executionResult = userService.getGraphQL().execute(mutation);

    // Check if there are errors
    if(!executionResult.getErrors().isEmpty()){
        return new ResponseEntity<>(executionResult.getErrors().get(0).getMessage(), HttpStatus.UNAUTHORIZED);
    }

    return new ResponseEntity<>(executionResult, HttpStatus.OK);
}

As I said, login is working fine, but register is return the error I mentioned in the title.

My service class:

@Value("classpath:graphql-schema/schema.graphqls")
    Resource resource;

    private GraphQL graphQL;

    @Autowired
    private LoginDataFetcher loginDataFetcher;
    @Autowired
    private RegisterDataFetcher registerDataFetcher;

    @PostConstruct
    public void  loadSchema() throws IOException{
    // Get the schema
    File schemaFile = resource.getFile();

    // Parse schema
    TypeDefinitionRegistry typeDefinitionRegistry = new SchemaParser().parse(schemaFile);
    RuntimeWiring runtimeWiring = buildRuntimeWiring();
    GraphQLSchema graphQLSchema = new SchemaGenerator().makeExecutableSchema(typeDefinitionRegistry, runtimeWiring);
    graphQL = GraphQL.newGraphQL(graphQLSchema).build();
}

private RuntimeWiring buildRuntimeWiring() {
    return RuntimeWiring.newRuntimeWiring()
            .type("Query", typeWiring ->
                typeWiring
                    .dataFetcher("login", loginDataFetcher))
            .type("Mutation", typeWiring ->
                typeWiring
                    .dataFetcher("register", registerDataFetcher))
            .build();
}

public GraphQL getGraphQL() {
    return graphQL;
}

My LoginDataFetcher:

@Autowired
    private AppUserRepository appUserRepository;

    private JwtGenerator jwtGenerator;

    public LoginDataFetcher(JwtGenerator jwtGenerator) {
        this.jwtGenerator = jwtGenerator;
    }

    @Override
    public TokenDAO get(DataFetchingEnvironment dataFetchingEnvironment) {
        String username = dataFetchingEnvironment.getArgument("username");
        String password = dataFetchingEnvironment.getArgument("password");

        AppUser appUser = appUserRepository.findByUsername(username);

        // If user is not foung
        if(appUser == null){
            throw new RuntimeException("Username does not exist");
        }

        // If the user is fount check passwords
        if(!appUser.getPassword().equals(password)){
            throw new RuntimeException("Incorrect password");
        }

        // Generate the token
        String token = jwtGenerator.generate(appUser);

        return new TokenDAO(token);
    }

The RegisterDataFetcher:

@Autowired
    private AppUserRepository appUserRepository;

    @Override
    public RegisterDAO get(DataFetchingEnvironment dataFetchingEnvironment) {
        String username = dataFetchingEnvironment.getArgument("username");
        String firstName = dataFetchingEnvironment.getArgument("firstName");
        String lastName = dataFetchingEnvironment.getArgument("lastName");
        String password = dataFetchingEnvironment.getArgument("password");
        String role = dataFetchingEnvironment.getArgument("role");

        AppUser appUser = appUserRepository.findByUsername(username);

        // Check if username exists
        if(appUser != null){
            throw new RuntimeException("Username already taken");
        }

        AppUser newAppUser = new AppUser(username, password, role, firstName, lastName);

        // Save new user
        appUserRepository.save(newAppUser);

        return new RegisterDAO("You have successfully registered");
    }

The error that I am getting in the console:

graphql.GraphQL                          : Query failed to validate : '{
    register(username: "user", firstName: "Bla", lastName: "Blabla", password: "password", role: "DEVELOPER") {
        message
    }
}'

Thank you for your help.

UPDATE

I changed my schema file like this, based on the answer I got:

query UserQuery{
    login(username: String, password: String){
        token
    }
}

mutation UserMutation{
    register(input: RegisterUserInput) {
        message
    }
}

input RegisterUserInput {
    username: String!
    firstName: String!
    lastName: String!
    password: String!
    role: String!
}

schema {
    query: UserQuery
    mutation: UserMutation
}

But now I am getting this error:

The operation type 'UserQuery' is not present when resolving type 'query' The operation type 'UserMutation' is not present when resolving type 'mutation'

So what is now the problem? How can I make this work?

回答1:

You are telling GraphQL that you are requesting a Query, while in fact register is a Mutation. When composing a GraphQL request, the syntax typically follows this format for Queries:

query someOperationName {
  login {
    # other fields
  }
}

When writing a mutation, you simply specify that instead:

mutation someOperationName {
  register {
    # other fields
  }
}

You can leave the operation name out, although it's good practice to include it. You may see examples in this format:

{
  someQuery {
    # other fields
  }
}

In this case both the operation name and the operation type (query vs mutation) were left off. This is still a valid request, since GraphQL simply assumes you meant query when you leave off the operation type. From the spec:

If a document contains only one operation, that operation may be unnamed or represented in the shorthand form, which omits both the query keyword and operation name.

So in your request, GraphQL is assuming register is a query, while in fact it's a mutation, and it's returning an error as a result.

Again, when writing requests, it's good practice to always include both the operation name and query/mutation keyword.