Как передать параметры / переменные в запрос или мутацию?

Я пытаюсь прочитать некоторые параметры, передаваемые через переменные на моем бэкэнде, давайте посмотрим: (этот метод находится внутри AuthenticationService, введенный в моем контроллере graphql, см. ниже)

@GraphQLMutation(name = "getSessionToken")
public AuthReturn getSessionToken(@GraphQLArgument(name = "user") String u, @GraphQLArgument(name = "password") String p) {...}

И вот мой запрос graphQL:

mutation ($user: String!, $password: String!) {
  getSessionToken(user: $user, password: $password) {
    status
    payload
  }
}

и мои переменные:

{ "user": "myuser", "password": "mypass"}

но когда я пытаюсь запустить этот пример кода, появляется следующая ошибка:

{"timestamp":"2019-07-29T17:18:32.753+0000","status":400,"error":"Bad Request","message":"JSON parse error: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token\n at [Source: (PushbackInputStream); line: 1, column: 162] (through reference chain: java.util.LinkedHashMap[\"variables\"])","path":"/graphql"}

[Редактировать] И вот мой контроллер:

@RestController
public class GraphQLController {

    private final GraphQL graphQL;

    public GraphQLController(AgendamentoService agendamentos, ConfiguracaoService config, ProcessoService processos, ParametroService parametros, AuthenticationService autenticacao) {
        GraphQLSchema schema = new GraphQLSchemaGenerator()
                .withResolverBuilders(
                        //Resolve by annotations
                        new AnnotatedResolverBuilder())
                .withOperationsFromSingletons(agendamentos, config, processos, parametros, autenticacao)
                .withValueMapperFactory(new JacksonValueMapperFactory())
                .generate();
        graphQL = GraphQL.newGraphQL(schema).build();
    }

    @CrossOrigin
    @PostMapping(value = "/graphql", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    public Map<String, Object> graphql(@RequestBody Map<String, String> request, HttpServletRequest raw) {
        // em context estamos passando o Request, usamos para fazer as verificacoes de autenticacao com GraphQl 
        ExecutionResult executionResult = graphQL.execute(ExecutionInput.newExecutionInput()
                .query(request.get("query"))
                .operationName(request.get("operationName"))
                .context(raw)
                .build());
        return executionResult.toSpecification();
    }
}

но если я запускаю эту мутацию без передачи параметров как variables по запросу все работает правильно. Что я могу сделать, чтобы передать переменные в мои запросы GraphQl? Заранее спасибо.

2 ответа

Решение

Вы на самом деле не передаете переменную в graphql-java. Это должно быть сделано через ExecutionInput, Я бы предложил создать такой класс, как:

@JsonIgnoreProperties(ignoreUnknown = true)
public class GraphQLRequest {

    private final String query;
    private final String operationName;
    private final Map<String, Object> variables;

    @JsonCreator
    public GraphQLRequest(@JsonProperty("query") String query,
                          @JsonProperty("operationName") String operationName,
                          @JsonProperty("variables") Map<String, Object> variables) {
        this.query = query;
        this.operationName = operationName;
        this.variables = variables != null ? variables : Collections.emptyMap();
    }

    public String getQuery() {
        return query;
    }

    public String getOperationName() {
        return operationName;
    }

    public Map<String, Object> getVariables() {
        return variables;
    }
}

и используйте это как параметр в методе контроллера:

@CrossOrigin
@PostMapping(value = "/graphql", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public Map<String, Object> graphql(@RequestBody GraphQLRequest graphQLRequest, HttpServletRequest httpRequest) {
    // em context estamos passando o Request, usamos para fazer as verificacoes de autenticacao com GraphQl 
    ExecutionInput.Builder inputBuilder = ExecutionInput.newExecutionInput()
                .query(graphQLRequest.getQuery())
                .operationName(graphQLRequest.getOperationName())
                .variables(graphQLRequest.getVariables()) //this is the line you were missing
                .context(httpRequest);
    return executionResult.toSpecification();
}

Отсутствующие переменные в ExecutionInput все же не объясняйте ошибку десериализации, которую вы получили. В нем говорится, что в JSON был найден объект, в котором ожидалась строка. Не уверен, откуда это происходит, но я подозреваю, что веб-часть больше, чем реальный код.

В любом случае, поместите точку останова в коде контроллера и посмотрите, правильно ли десериализован запрос и ударился ли вообще механизм GraphQL.

Я также предлагаю вам упростить настройку:

public GraphQLController(AgendamentoService agendamentos, ConfiguracaoService config, ProcessoService processos, ParametroService parametros, AuthenticationService autenticacao) {
    GraphQLSchema schema = new GraphQLSchemaGenerator()
            .withResolverBuilders(
                    //Resolve by annotations
                    new AnnotatedResolverBuilder())
            .withOperationsFromSingletons(agendamentos, config, processos, parametros, autenticacao)
            .withValueMapperFactory(new JacksonValueMapperFactory())
            .generate();
    graphQL = GraphQL.newGraphQL(schema).build();
}

в

public GraphQLController(AgendamentoService agendamentos, ConfiguracaoService config, ProcessoService processos, ParametroService parametros, AuthenticationService autenticacao) {
    GraphQLSchema schema = new GraphQLSchemaGenerator()
            .withOperationsFromSingletons(agendamentos, config, processos, parametros, autenticacao)
            .generate();
    graphQL = GraphQL.newGraphQL(schema).build();
}

так как остальные строки избыточны. Они только устанавливают то, что уже является поведением по умолчанию.

вы можете использовать VTL для этого варианта использования

      Map<String, Object> requestBody = new HashMap<>();

        String query = "query MyQuery {"
                + "    getTemplate("
                + "        id: \"$id\""
                + "    ){"
                + "        id"
                + "        listOfPlaceholders"
                + "        messageTemplate"
                + "        type"
                + "    }"
                + "}";
        VelocityContext queryContext = new VelocityContext();
        queryContext.put("id", data.get("id"));

        StringWriter queryWriter = new StringWriter();
        Velocity.evaluate(context, queryWriter, "TemplateName", query);
        System.out.println(queryWriter.toString());

        requestBody.put("query", queryWriter.toString());
Другие вопросы по тегам