io.camunda.identity.sdk.impl.rest.RestClient Maven / Gradle / Ivy
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. Licensed under a proprietary license. See the
* License.txt file for more information. You may not use this file except in compliance with the
* proprietary license.
*/
package io.camunda.identity.sdk.impl.rest;
import static java.util.stream.Collectors.joining;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.camunda.identity.sdk.impl.rest.exception.RestException;
import io.camunda.identity.sdk.impl.rest.request.Request;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.stream.Collectors;
public class RestClient {
private final ObjectMapper mapper =
new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
private final HttpClient client;
public RestClient() {
this(HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NEVER).build());
}
RestClient(final HttpClient client) {
this.client = client;
}
/**
* Performs a request to Camunda Account REST API and returns the response body mapped to provided
* object type.
*
* @param the return type parameter
* @param request the request to be performed
* @return the response of the provided type
* @throws RestException Rest request exception in case the request fails
*/
public T request(final Request request) {
final TypeReference typeReference = request.getTypeReference();
final HttpRequest.Builder requestBuilder = HttpRequest.newBuilder();
if (request.getParams().isEmpty()) {
requestBuilder.uri(URI.create(request.getUrl()));
} else {
final String params = generateParamString(request.getParams());
requestBuilder.uri(
URI.create(
request.getUrl().concat("?").concat(params)
)
);
}
if (request.getAuthentication() != null) {
requestBuilder.header("Authorization", "Bearer " + request.getAuthentication());
}
if (request.getContentType() != null) {
requestBuilder.header("Content-Type", request.getContentType().toString());
}
if (request.getBody() != null) {
final String body;
switch (request.getContentType()) {
case X_WWW_URL_ENCODED:
body = urlEncodedBody(request.getBody());
break;
case JSON:
default:
try {
body = jsonBody(request.getBody());
} catch (final JsonProcessingException e) {
throw new RestException("body serialization failed", e);
}
break;
}
final HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.ofString(body);
if (
request.getHttpMethod() != null
&& request.getHttpMethod().equals(Request.HttpMethod.PUT)
) {
requestBuilder.PUT(bodyPublisher);
} else {
requestBuilder.POST(bodyPublisher);
}
}
final HttpRequest httpRequest = requestBuilder.build();
final HttpResponse> response = send(httpRequest);
try {
if (typeReference.getType() == Void.class) {
return null;
}
return mapper.readValue(response.body().toString(), typeReference);
} catch (final IOException e) {
throw new RestException("response can not be mapped to provided type", e);
}
}
private HttpResponse> send(final HttpRequest request) {
try {
final HttpResponse> response = client.send(request, HttpResponse.BodyHandlers.ofString());
final int statusCode = response.statusCode();
if (statusCode < 200 || statusCode > 299) {
throw new RestException(String
.format("request failed with status code '%s' and body '%s'",
statusCode, response.body().toString()));
}
return response;
} catch (final IOException | InterruptedException e) {
throw new RestException("request failed", e);
}
}
private String jsonBody(final Object body) throws JsonProcessingException {
return mapper.writeValueAsString(body);
}
private String urlEncodedBody(final Object body) {
final Map params = mapper.convertValue(body, Map.class);
return params.keySet().stream()
.map(key -> key + "=" + urlEncode(params.get(key)))
.collect(joining("&"));
}
private String generateParamString(final Map params) {
return params.entrySet()
.stream()
.map(entry -> urlEncode(entry.getKey())
.concat("=")
.concat(urlEncode(entry.getValue())))
.collect(Collectors.joining("&"));
}
private String urlEncode(final String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8);
}
}