package io.konik.sdk.http;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.api.client.http.*;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.common.io.ByteStreams;
import io.konik.sdk.ZinvoiceApiConfig;
import org.apache.tika.detect.DefaultDetector;
import org.apache.tika.detect.Detector;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.mime.MediaType;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
* HTTP client for Zinvoice services.
*
* It allows to call Zinvoice endpoints with no worries about internal connection details.
*/
public class ZinvoiceHttpClient {
public static final HttpRequestFactory DEFAULT_HTTP_REQUEST_FACTORY = new NetHttpTransport().createRequestFactory(new HttpRequestInitializer() {
@Override
public void initialize(HttpRequest httpRequest) throws IOException {
httpRequest.setParser(new JsonObjectParser(new JacksonFactory()));
}
});
private final ZinvoiceApiConfig apiConfig;
private final HttpRequestFactory httpRequestFactory;
private final ObjectMapper objectMapper;
public ZinvoiceHttpClient(ZinvoiceApiConfig apiConfig, HttpRequestFactory httpRequestFactory, ObjectMapper objectMapper) {
this.apiConfig = apiConfig;
this.httpRequestFactory = httpRequestFactory;
this.objectMapper = objectMapper;
}
public ZinvoiceHttpClient(ZinvoiceApiConfig apiConfig, ObjectMapper objectMapper) {
this.apiConfig = apiConfig;
this.objectMapper = objectMapper;
this.httpRequestFactory = DEFAULT_HTTP_REQUEST_FACTORY;
}
public ZinvoiceHttpClient(ZinvoiceApiConfig apiConfig) {
this.apiConfig = apiConfig;
this.objectMapper = apiConfig.getDefaulObjectMapper();
this.httpRequestFactory = DEFAULT_HTTP_REQUEST_FACTORY;
}
/**
* Runs GET request.
*
* @param endpoint
* @param responseTypeClass
* @return
*/
public <T> T get(String endpoint, Class<T> responseTypeClass) {
T result;
try {
HttpRequest request = httpRequestFactory.buildGetRequest(createEndpoint(endpoint));
request.setHeaders(new HttpHeaders().set("API-KEY", apiConfig.getApiKey()));
HttpResponse response = request.execute();
result = objectMapper.readValue(response.parseAsString(), responseTypeClass);
} catch (IOException e) {
throw new RuntimeException(e);
}
return result;
}
/**
* Downloads content as {@see InputStream}.
*
* @param endpoint
* @return
*/
public InputStream download(String endpoint) {
try {
HttpRequest request = httpRequestFactory.buildGetRequest(createEndpoint(endpoint));
request.setHeaders(new HttpHeaders()
.set("API-KEY", apiConfig.getApiKey())
.setAccept("application/json"));
return request.execute().getContent();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Uploads {@see InputStream} to given endpoint.
*
* @param endpoint
* @param files map where key is field name and value is a content of uploading file
* @param responseTypeClass
* @return
*/
public <T> T upload(String endpoint, Map<String, InputStream> files, Class<T> responseTypeClass) {
try {
MultipartContent content = new MultipartContent();
content.setMediaType(new HttpMediaType("multipart/form-data")
.setParameter("boundary", "__END_OF_PART__"));
Detector detector = new DefaultDetector();
for (String key : files.keySet()) {
InputStream file = new ByteArrayInputStream(ByteStreams.toByteArray(files.get(key)));
MediaType mediaType = detector.detect(file, new Metadata());
MultipartContent.Part part = new MultipartContent.Part(new InputStreamContent(mediaType.toString(), file));
part.setHeaders(new HttpHeaders().set("Content-Disposition", String.format("form-data; name=\"%s\"; filename=\"%s\"", key, key)));
content.addPart(part);
}
HttpRequest request = httpRequestFactory.buildPostRequest(createEndpoint(endpoint), content);
request.setHeaders(new HttpHeaders()
.set("API-KEY", apiConfig.getApiKey())
.setAccept("application/json"));
HttpResponse response = request.execute();
return objectMapper.readValue(response.parseAsString(), responseTypeClass);
} catch (HttpResponseException e) {
if (e.getStatusCode() == 400) {
throw new BadRequestException(getErrorResponse(e));
}
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Runs POST request.
*
* @param endpoint
* @param body
* @param contentType
* @param responseTypeClass
* @return
*/
public <T> T post(String endpoint, byte[] body, String contentType, Class<T> responseTypeClass) {
T result;
try {
HttpContent content = new InputStreamContent(contentType, new ByteArrayInputStream(body));
HttpRequest request = httpRequestFactory.buildPostRequest(createEndpoint(endpoint), content);
request.setHeaders(new HttpHeaders().set("API-KEY", apiConfig.getApiKey()));
HttpResponse response = request.execute();
result = objectMapper.readValue(response.parseAsString(), responseTypeClass);
} catch (HttpResponseException e) {
if (e.getStatusCode() == 400) {
throw new BadRequestException(getErrorResponse(e));
}
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
return result;
}
/**
* Runs PUT request.
*
* @param endpoint
* @param body
* @param contentType
* @param responseTypeClass
* @return
*/
public <T> T put(String endpoint, byte[] body, String contentType, Class<T> responseTypeClass) {
T result;
try {
HttpContent content = new InputStreamContent(contentType, new ByteArrayInputStream(body));
HttpRequest request = httpRequestFactory.buildPutRequest(createEndpoint(endpoint), content);
request.setHeaders(new HttpHeaders().set("API-KEY", apiConfig.getApiKey()));
HttpResponse response = request.execute();
result = objectMapper.readValue(response.parseAsString(), responseTypeClass);
} catch (HttpResponseException e) {
if (e.getStatusCode() == 400) {
throw new BadRequestException(getErrorResponse(e));
}
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
return result;
}
/**
* Runs DELETE request.
*
* @param endpoint
* @return
*/
public void delete(String endpoint) {
try {
HttpRequest request = httpRequestFactory.buildDeleteRequest(createEndpoint(endpoint));
request.setHeaders(new HttpHeaders().set("API-KEY", apiConfig.getApiKey()));
request.execute();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private ErrorResponse getErrorResponse(HttpResponseException e) {
ErrorResponse errorResponse;
try {
errorResponse = objectMapper.readValue(e.getContent(), ErrorResponse.class);
} catch (IOException e1) {
throw new RuntimeException(e1);
}
return errorResponse;
}
private GenericUrl createEndpoint(String endpoint) {
return new GenericUrl(apiConfig.getDestinationUrl() + endpoint);
}
}