diff --git a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/exception/GraphqlException.java b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/exception/GraphqlException.java new file mode 100644 index 0000000..d0f9e27 --- /dev/null +++ b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/exception/GraphqlException.java @@ -0,0 +1,18 @@ +package ru.dragonestia.picker.api.impl.exception; + +import java.util.Collections; +import java.util.Map; + +public class GraphqlException extends RuntimeException { + + private final Map details; + + public GraphqlException(Map details) { + super(details.get("message")); + this.details = details; + } + + public Map getDetails() { + return Collections.unmodifiableMap(details); + } +} diff --git a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/GraphqlQuery.java b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/GraphqlQuery.java new file mode 100644 index 0000000..af9c59d --- /dev/null +++ b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/GraphqlQuery.java @@ -0,0 +1,21 @@ +package ru.dragonestia.picker.api.impl.util; + +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public record GraphqlQuery(String query, Class responseClass, ParamProvider paramProvider) { + + public interface ParamConsumer extends BiConsumer { + + default void put(String key, String value) { + accept(key, value); + } + } + + public interface ParamProvider extends Consumer {} + + public record Request(String query, Map variables) {} + + public record Response(T data) {} +} diff --git a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/RestTemplate.java b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/RestTemplate.java index e920fd0..3852ed3 100644 --- a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/RestTemplate.java +++ b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/RestTemplate.java @@ -3,10 +3,13 @@ package ru.dragonestia.picker.api.impl.util; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeCreator; import okhttp3.*; import org.jetbrains.annotations.ApiStatus.Internal; import ru.dragonestia.picker.api.impl.exception.ExceptionService; +import ru.dragonestia.picker.api.impl.exception.GraphqlException; import ru.dragonestia.picker.api.impl.exception.NotEnoughPermissions; import ru.dragonestia.picker.api.impl.exception.AuthException; import ru.dragonestia.picker.api.impl.RoomPickerClient; @@ -18,6 +21,7 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.function.Consumer; +import java.util.stream.Stream; @Internal public class RestTemplate { @@ -85,6 +89,46 @@ public class RestTemplate { .build()); } + public T executeGraphQL(GraphqlQuery query) { + var map = new HashMap(); + query.paramProvider().accept(map::put); + + String queryBody; + try { + queryBody = json.writeValueAsString(new GraphqlQuery.Request(query.query(), map)); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + + var request = client.prepareRequestBuilder("/graphql") + .post(RequestBody.create(queryBody, MediaType.get("application/json"))) + .build(); + + var response = executeGraphql(request); + JsonNode node; + try { + node = json.readTree(response); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + + if (node.has("errors")) { + var details = new HashMap(); + for (Iterator> it = node.fields(); it.hasNext(); ) { + var entry = it.next(); + + details.put(entry.getKey(), entry.getValue().textValue()); + } + throw new GraphqlException(details); + } + + try { + return json.readValue(response, new TypeReference>(){}).data(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + private String queryEncode(ParamsConsumer paramsConsumer) { var params = new HashMap(); paramsConsumer.accept(params); @@ -120,6 +164,18 @@ public class RestTemplate { } } + private String executeGraphql(Request request) { + try (var response = httpClient.newCall(request).execute()) { + checkResponseForErrors(response); + + return new String(Objects.requireNonNull(response.body()).bytes(), StandardCharsets.UTF_8); + } catch (JsonProcessingException ex) { + throw new RuntimeException("Json processing error", ex); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + private void checkResponseForErrors(Response response) throws IOException { var code = response.code(); var statusCode = code / 100;