Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Java Client Library (beta)

* Latest released version 0.14.3
* Latest snapshot version 0.14.4-SNAPSHOT
* Latest released version 0.15.0
* Latest snapshot version 0.15.1-SNAPSHOT

## Introduction
Welcome my friends! This is the Poly API Java client GitHub page. If you are here, then it means you're familiar with what we do at Poly. If you aren't, you can always check [here](https://github.com/polyapi/poly-alpha).
Expand Down Expand Up @@ -55,7 +55,7 @@ Nice to have some customers looking around here! So, you'll need to run the foll
2. **Update the project.** Add the following to your project's `pom.xml`:
```xml
<properties>
<poly.version>0.14.3</poly.version>
<poly.version>0.15.0</poly.version>
</properties>
<dependencies>
<dependency>
Expand Down
2 changes: 1 addition & 1 deletion commons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>io.polyapi</groupId>
<artifactId>parent-pom</artifactId>
<version>0.14.4-SNAPSHOT</version>
<version>0.15.0-SNAPSHOT</version>
<relativePath>../parent-pom</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
*/
public class ParsingException extends PolyApiException {

/**
* @see PolyApiException#PolyApiException(String)
*/
public ParsingException(String message) {
super(message);
}

/**
* @see PolyApiException#PolyApiException(String, Throwable)
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.polyapi.commons.api.error.parse;

import java.lang.reflect.Type;

/**
* Exception thrown when the content type of the response is not supported or tye type expected by the caller doesn't match the type of the response.
*/
public class UnsupportedContentTypeException extends ParsingException {
public UnsupportedContentTypeException(String contentType, Type type) {
super("Unsupported content type '" + contentType + "' for response type " + type + ".");
}
}
174 changes: 101 additions & 73 deletions commons/src/main/java/io/polyapi/commons/api/service/PolyApiService.java
Original file line number Diff line number Diff line change
@@ -1,98 +1,126 @@
package io.polyapi.commons.api.service;

import io.polyapi.commons.api.error.http.UnexpectedHttpResponseException;
import io.polyapi.commons.api.error.http.UnexpectedInformationalResponseException;
import io.polyapi.commons.api.error.parse.ParsingException;
import io.polyapi.commons.api.error.parse.UnsupportedContentTypeException;
import io.polyapi.commons.api.http.HttpClient;
import io.polyapi.commons.api.http.HttpMethod;
import io.polyapi.commons.api.http.Request;
import io.polyapi.commons.api.http.Response;
import io.polyapi.commons.api.json.JsonParser;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static io.polyapi.commons.api.http.HttpMethod.*;
import static java.nio.charset.Charset.defaultCharset;
import static java.util.function.Predicate.not;

/**
* Parent implementation class for all services that connecto the Poly API service.
*/
@Slf4j
public class PolyApiService {
private final String host;
private final Integer port;
private final HttpClient client;
private final JsonParser jsonParser;

public PolyApiService(HttpClient client, JsonParser jsonParser, String host, Integer port) {
this.client = client;
this.jsonParser = jsonParser;
this.host = host;
this.port = port;
}

public <O> O get(String relativePath, Type expectedResponseType) {
return get(relativePath, new HashMap<>(), new HashMap<>(), expectedResponseType);
}

public <O> O get(String relativePath, Map<String, List<String>> headers, Map<String, List<String>> queryParams, Type expectedResponseType) {
return parsedCall(GET, relativePath, headers, queryParams, null, expectedResponseType);
}

public <I, O> O post(String relativePath, I body, Type expectedResponseType) {
return post(relativePath, new HashMap<>(), new HashMap<>(), body, expectedResponseType);
}

public <I, O> O post(String relativePath, Map<String, List<String>> headers, Map<String, List<String>> queryParams, I body, Type expectedResponseType) {
return parsedCall(POST, relativePath, headers, queryParams, body, expectedResponseType);
}

public <I> void patch(String relativePath, I body) {
parsedCall(PATCH, relativePath, new HashMap<>(), new HashMap<>(), body, Void.TYPE);
}

public <I> void patch(String relativePath, Map<String, List<String>> headers, Map<String, List<String>> queryParams, I body) {
parsedCall(PATCH, relativePath, headers, queryParams, body, Void.TYPE);
}

public void delete(String relativePath) {
delete(relativePath, new HashMap<>(), new HashMap<>(), null);
}


public <I> void delete(String relativePath, Map<String, List<String>> headers, Map<String, List<String>> queryParams, I body) {
parsedCall(DELETE, relativePath, headers, queryParams, body, Void.TYPE);
}

private <I, O> O parsedCall(HttpMethod method, String relativePath, Map<String, List<String>> headers, Map<String, List<String>> queryParams, I body, Type expectedResponseType) {
Map<String, List<String>> allHeaders = new HashMap<>();
allHeaders.put("Accept", List.of("application/json"));
allHeaders.put("Content-type", List.of("application/json"));
headers.forEach((key, value) -> allHeaders.put(key, value.stream().toList()));

Response response = callApi(method, relativePath, allHeaders, queryParams, jsonParser.toJsonInputStream(body));
log.debug("Response is successful. Status code is {}.", response.statusCode());
log.debug("Parsing response.");
O result = Optional.of(expectedResponseType)
.filter(not(Void.TYPE::equals))
.map(type -> jsonParser.<O>parseInputStream(response.body(), type))
.orElse(null);
log.debug("Response parsed successfully.");
return result;
}

private Response callApi(HttpMethod method, String relativePath, Map<String, List<String>> headers, Map<String, List<String>> queryParams, InputStream body) {
Request request = client.prepareAuthenticatedRequest(host, port, method, relativePath)
.withHeaders(headers)
.withQueryParams(queryParams)
.withBody(body)
.build();
log.debug("Executing authenticated {} request with target {}", method, request.getUrl());
return client.send(request);
}
private final String host;
private final Integer port;
private final HttpClient client;
private final JsonParser jsonParser;

public PolyApiService(HttpClient client, JsonParser jsonParser, String host, Integer port) {
this.client = client;
this.jsonParser = jsonParser;
this.host = host;
this.port = port;
}

public <O> O get(String relativePath, Type expectedResponseType) {
return get(relativePath, new HashMap<>(), new HashMap<>(), expectedResponseType);
}

public <O> O get(String relativePath, Map<String, List<String>> headers, Map<String, List<String>> queryParams, Type expectedResponseType) {
return parsedCall(GET, relativePath, headers, queryParams, null, expectedResponseType);
}

public <I, O> O post(String relativePath, I body, Type expectedResponseType) {
return post(relativePath, new HashMap<>(), new HashMap<>(), body, expectedResponseType);
}

public <I, O> O post(String relativePath, Map<String, List<String>> headers, Map<String, List<String>> queryParams, I body, Type expectedResponseType) {
return parsedCall(POST, relativePath, headers, queryParams, body, expectedResponseType);
}

public <I> void patch(String relativePath, I body) {
parsedCall(PATCH, relativePath, new HashMap<>(), new HashMap<>(), body, Void.TYPE);
}

public <I> void patch(String relativePath, Map<String, List<String>> headers, Map<String, List<String>> queryParams, I body) {
parsedCall(PATCH, relativePath, headers, queryParams, body, Void.TYPE);
}

public void delete(String relativePath) {
delete(relativePath, new HashMap<>(), new HashMap<>(), null);
}


public <I> void delete(String relativePath, Map<String, List<String>> headers, Map<String, List<String>> queryParams, I body) {
parsedCall(DELETE, relativePath, headers, queryParams, body, Void.TYPE);
}

private <I, O> O parsedCall(HttpMethod method, String relativePath, Map<String, List<String>> headers, Map<String, List<String>> queryParams, I body, Type expectedResponseType) {
Map<String, List<String>> allHeaders = new HashMap<>();
//allHeaders.put("Accept", List.of("application/json"));
allHeaders.put("Content-type", List.of("application/json"));
headers.forEach((key, value) -> allHeaders.put(key, value.stream().toList()));

Response response = callApi(method, relativePath, allHeaders, queryParams, jsonParser.toJsonInputStream(body));
log.debug("Response is successful. Status code is {}.", response.statusCode());
log.debug("Parsing response.");
O result = Optional.of(expectedResponseType)
.filter(not(Void.TYPE::equals))
.map(type ->{
String contentType = response.headers().get("Content-type").stream().findFirst().orElseThrow();
O parsedResult;
log.debug("Content type is {}.", contentType);
log.debug("Type class is {}.", type.getClass());
log.debug("Type is {}.", type);
if (contentType.startsWith("application/json")) {
parsedResult = jsonParser.parseInputStream(response.body(), TypeVariable.class.isAssignableFrom(type.getClass()) ? Object.class : type);
} else {
if ((type.getClass().isAssignableFrom(String.class) || TypeVariable.class.isAssignableFrom(type.getClass())) && contentType.startsWith("text/")) {
try {
parsedResult = (O)IOUtils.toString(response.body(), defaultCharset());
} catch (IOException e) {
throw new ParsingException("An error occurred while parsing the response.", e);
}
} else {
if ((InputStream.class.isAssignableFrom(type.getClass()) || type.getClass().isAssignableFrom(TypeVariable.class))) {
parsedResult = (O)response.body();
} else {
throw new UnsupportedContentTypeException(contentType, type);
}
}
}
return parsedResult;
})
.orElse(null);
log.debug("Response parsed successfully.");
return result;
}

private Response callApi(HttpMethod method, String relativePath, Map<String, List<String>> headers, Map<String, List<String>> queryParams, InputStream body) {
Request request = client.prepareAuthenticatedRequest(host, port, method, relativePath)
.withHeaders(headers)
.withQueryParams(queryParams)
.withBody(body)
.build();
log.debug("Executing authenticated {} request with target {}", method, request.getUrl());
return client.send(request);
}
}
2 changes: 1 addition & 1 deletion library/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>io.polyapi</groupId>
<artifactId>parent-pom</artifactId>
<version>0.14.4-SNAPSHOT</version>
<version>0.15.0-SNAPSHOT</version>
<relativePath>../parent-pom</relativePath>
</parent>
<artifactId>library</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion parent-pom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.polyapi</groupId>
<artifactId>parent-pom</artifactId>
<version>0.14.4-SNAPSHOT</version>
<version>0.15.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Poly API Java parent POM</name>
<url>https://polyapi.io</url>
Expand Down
2 changes: 1 addition & 1 deletion polyapi-maven-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>io.polyapi</groupId>
<artifactId>parent-pom</artifactId>
<version>0.14.4-SNAPSHOT</version>
<version>0.15.0-SNAPSHOT</version>
<relativePath>../parent-pom</relativePath>
</parent>
<artifactId>polyapi-maven-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ public ResolvedFunctionSpecification(String id, String name, String packageName,
}

public String getReturnType() {
return Optional.ofNullable(returnType)
String result = Optional.ofNullable(returnType)
.map(String::trim)
.filter(not(String::isBlank))
.filter(not(isEqual(Void.class.getName())))
.orElse("void");
return result.equalsIgnoreCase(Object.class.getName()) ? "<T> T" : result;
}

public String getParamNames() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public List<PolyFunction> deployFunctions(List<String> functionFilters, boolean
"java.lang.Short", "java.lang.Byte" -> "number";
case "java.lang.Boolean" -> "boolean";
case "java.lang.String", "java.lang.Character" -> "string";
case "java.util.Object" -> "any";
case "java.lang.Object" -> "any";
case "void" -> "void";
default -> "object";
});
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.polyapi</groupId>
<artifactId>polyapi-java</artifactId>
<version>0.14.4-SNAPSHOT</version>
<version>0.15.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>parent-pom</module>
Expand Down