001package io.konik.sdk.http; 002 003import com.fasterxml.jackson.databind.ObjectMapper; 004import com.google.api.client.http.*; 005import com.google.api.client.http.javanet.NetHttpTransport; 006import com.google.api.client.json.JsonObjectParser; 007import com.google.api.client.json.jackson2.JacksonFactory; 008import com.google.common.io.ByteStreams; 009import io.konik.sdk.ZinvoiceApiConfig; 010import org.apache.tika.detect.DefaultDetector; 011import org.apache.tika.detect.Detector; 012import org.apache.tika.metadata.Metadata; 013import org.apache.tika.mime.MediaType; 014 015import java.io.ByteArrayInputStream; 016import java.io.IOException; 017import java.io.InputStream; 018import java.util.Map; 019 020/** 021 * HTTP client for Zinvoice services. 022 * 023 * It allows to call Zinvoice endpoints with no worries about internal connection details. 024 */ 025public class ZinvoiceHttpClient { 026 027 public static final HttpRequestFactory DEFAULT_HTTP_REQUEST_FACTORY = new NetHttpTransport().createRequestFactory(new HttpRequestInitializer() { 028 @Override 029 public void initialize(HttpRequest httpRequest) throws IOException { 030 httpRequest.setParser(new JsonObjectParser(new JacksonFactory())); 031 } 032 }); 033 034 private final ZinvoiceApiConfig apiConfig; 035 036 private final HttpRequestFactory httpRequestFactory; 037 038 private final ObjectMapper objectMapper; 039 040 public ZinvoiceHttpClient(ZinvoiceApiConfig apiConfig, HttpRequestFactory httpRequestFactory, ObjectMapper objectMapper) { 041 this.apiConfig = apiConfig; 042 this.httpRequestFactory = httpRequestFactory; 043 this.objectMapper = objectMapper; 044 } 045 046 public ZinvoiceHttpClient(ZinvoiceApiConfig apiConfig, ObjectMapper objectMapper) { 047 this.apiConfig = apiConfig; 048 this.objectMapper = objectMapper; 049 this.httpRequestFactory = DEFAULT_HTTP_REQUEST_FACTORY; 050 } 051 052 public ZinvoiceHttpClient(ZinvoiceApiConfig apiConfig) { 053 this.apiConfig = apiConfig; 054 this.objectMapper = apiConfig.getDefaulObjectMapper(); 055 this.httpRequestFactory = DEFAULT_HTTP_REQUEST_FACTORY; 056 } 057 058 /** 059 * Runs GET request. 060 * 061 * @param endpoint 062 * @param responseTypeClass 063 * @return 064 */ 065 public <T> T get(String endpoint, Class<T> responseTypeClass) { 066 T result; 067 068 try { 069 HttpRequest request = httpRequestFactory.buildGetRequest(createEndpoint(endpoint)); 070 request.setHeaders(new HttpHeaders().set("API-KEY", apiConfig.getApiKey())); 071 072 HttpResponse response = request.execute(); 073 result = objectMapper.readValue(response.parseAsString(), responseTypeClass); 074 } catch (IOException e) { 075 throw new RuntimeException(e); 076 } 077 078 return result; 079 } 080 081 /** 082 * Downloads content as {@see InputStream}. 083 * 084 * @param endpoint 085 * @return 086 */ 087 public InputStream download(String endpoint) { 088 try { 089 HttpRequest request = httpRequestFactory.buildGetRequest(createEndpoint(endpoint)); 090 request.setHeaders(new HttpHeaders() 091 .set("API-KEY", apiConfig.getApiKey()) 092 .setAccept("application/json")); 093 return request.execute().getContent(); 094 } catch (IOException e) { 095 throw new RuntimeException(e); 096 } 097 } 098 099 /** 100 * Uploads {@see InputStream} to given endpoint. 101 * 102 * @param endpoint 103 * @param files map where key is field name and value is a content of uploading file 104 * @param responseTypeClass 105 * @return 106 */ 107 public <T> T upload(String endpoint, Map<String, InputStream> files, Class<T> responseTypeClass) { 108 try { 109 MultipartContent content = new MultipartContent(); 110 content.setMediaType(new HttpMediaType("multipart/form-data") 111 .setParameter("boundary", "__END_OF_PART__")); 112 113 Detector detector = new DefaultDetector(); 114 115 for (String key : files.keySet()) { 116 InputStream file = new ByteArrayInputStream(ByteStreams.toByteArray(files.get(key))); 117 MediaType mediaType = detector.detect(file, new Metadata()); 118 MultipartContent.Part part = new MultipartContent.Part(new InputStreamContent(mediaType.toString(), file)); 119 part.setHeaders(new HttpHeaders().set("Content-Disposition", String.format("form-data; name=\"%s\"; filename=\"%s\"", key, key))); 120 content.addPart(part); 121 } 122 123 HttpRequest request = httpRequestFactory.buildPostRequest(createEndpoint(endpoint), content); 124 request.setHeaders(new HttpHeaders() 125 .set("API-KEY", apiConfig.getApiKey()) 126 .setAccept("application/json")); 127 HttpResponse response = request.execute(); 128 129 return objectMapper.readValue(response.parseAsString(), responseTypeClass); 130 131 } catch (HttpResponseException e) { 132 if (e.getStatusCode() == 400) { 133 throw new BadRequestException(getErrorResponse(e)); 134 } 135 throw new RuntimeException(e); 136 } catch (IOException e) { 137 throw new RuntimeException(e); 138 } 139 } 140 141 /** 142 * Runs POST request. 143 * 144 * @param endpoint 145 * @param body 146 * @param contentType 147 * @param responseTypeClass 148 * @return 149 */ 150 public <T> T post(String endpoint, byte[] body, String contentType, Class<T> responseTypeClass) { 151 T result; 152 153 try { 154 HttpContent content = new InputStreamContent(contentType, new ByteArrayInputStream(body)); 155 HttpRequest request = httpRequestFactory.buildPostRequest(createEndpoint(endpoint), content); 156 request.setHeaders(new HttpHeaders().set("API-KEY", apiConfig.getApiKey())); 157 158 HttpResponse response = request.execute(); 159 result = objectMapper.readValue(response.parseAsString(), responseTypeClass); 160 } catch (HttpResponseException e) { 161 if (e.getStatusCode() == 400) { 162 throw new BadRequestException(getErrorResponse(e)); 163 } 164 throw new RuntimeException(e); 165 } catch (IOException e) { 166 throw new RuntimeException(e); 167 } 168 169 return result; 170 } 171 172 /** 173 * Runs PUT request. 174 * 175 * @param endpoint 176 * @param body 177 * @param contentType 178 * @param responseTypeClass 179 * @return 180 */ 181 public <T> T put(String endpoint, byte[] body, String contentType, Class<T> responseTypeClass) { 182 T result; 183 184 try { 185 HttpContent content = new InputStreamContent(contentType, new ByteArrayInputStream(body)); 186 HttpRequest request = httpRequestFactory.buildPutRequest(createEndpoint(endpoint), content); 187 request.setHeaders(new HttpHeaders().set("API-KEY", apiConfig.getApiKey())); 188 189 HttpResponse response = request.execute(); 190 result = objectMapper.readValue(response.parseAsString(), responseTypeClass); 191 } catch (HttpResponseException e) { 192 if (e.getStatusCode() == 400) { 193 throw new BadRequestException(getErrorResponse(e)); 194 } 195 throw new RuntimeException(e); 196 } catch (IOException e) { 197 throw new RuntimeException(e); 198 } 199 200 return result; 201 } 202 203 /** 204 * Runs DELETE request. 205 * 206 * @param endpoint 207 * @return 208 */ 209 public void delete(String endpoint) { 210 try { 211 HttpRequest request = httpRequestFactory.buildDeleteRequest(createEndpoint(endpoint)); 212 request.setHeaders(new HttpHeaders().set("API-KEY", apiConfig.getApiKey())); 213 request.execute(); 214 } catch (IOException e) { 215 throw new RuntimeException(e); 216 } 217 } 218 219 private ErrorResponse getErrorResponse(HttpResponseException e) { 220 ErrorResponse errorResponse; 221 try { 222 errorResponse = objectMapper.readValue(e.getContent(), ErrorResponse.class); 223 } catch (IOException e1) { 224 throw new RuntimeException(e1); 225 } 226 return errorResponse; 227 } 228 229 private GenericUrl createEndpoint(String endpoint) { 230 return new GenericUrl(apiConfig.getDestinationUrl() + endpoint); 231 } 232}