...
public class MyTokenPageCollectionResponse extends AbstractTokenCollectionResponse<MyRequest, String, MyTokenPageResponse> {
public MyTokenPageCollectionResponse(Function<String, MyTokenPageResponse> pageFunction) {
super(pageFunction);
}
}
With this definition, the endpoint can be exposed in the client:
public class MyClient extends HttpRoundRobinClient {
...
public MyTokenPageCollectionResponse paginatedByToken(MyRequest clientRequest) {
return new MyTokenPageCollectionResponse(
token -> {
var urlBuilder = serverHosts.next()
.newBuilder()
.addPathSegment("list")
.addPathSegment(request.getId())
.addQueryParameter("limit", String.valueOf(scroll.getSize()));
if (token != null) {
urlBuilder.addQueryParameter("token", token);
}
var requestDetails = new HttpClientRequestDetails<>(
new Request.Builder().url(urlBuilder.build()).get().build(),
clientRequest);
return new MyTokenPageResponse(
objectMapper,
requestDetails,
executeWithBackoff(requestDetails),
token
);
}
);
}
...
}
Note that in case of a null
token, the function must return the first page.
Each element in the response can then be retrieved by using the Iterable<>
interface. The pagination will be handled behind the scenes.
Offset Page
Expected when the response is a consecutive list of pages obtained with a page number.
Offset Page Response
The response corresponds to each page from the pagination requests. It is assumed that the payload of the response is a JSON array (if that's not the case, the handleSuccess(Response httpResponse)
method must be overridden).
It is also assumed that the AbstractOffsetPageResponse#getNextPageReference()
is always 1 after the current page. This method can be overridden for custom behaviors.
public class MyOffsetPageResponse extends AbstractOffsetPageResponse<MyRequest, String> {
public MyOffsetPageResponse(
ObjectMapper objectMapper,
HttpClientRequestDetails<MyRequest> requestDetails,
Response httpResponse,
Long currentOffset
) {
super(objectMapper, requestDetails, httpResponse, currentOffset);
}
@Override
protected String parseElement(JsonNode jsonNode) {
return jsonNode.asText();
}
}
The predicate to determine if a response was successful can be provided in the constructor, the same way as in the previous sections.
Pageable Page Response
The response is a specialization of the Offset Page Response, and it is based on a default page structure:
{
"totalPages": 0,
"totalElements": 0,
"size": 0,
"content": [
{}
],
"number": 0,
"first": true,
"last": true,
"sort": {
"sorted": true,
"unsorted": true,
"empty": true
},
"numberOfElements": 0,
"pageable": {
"page": 0,
"size": 0,
"sort": [
"string"
]
},
"empty": true
}
Offset Page Collection
The response corresponds to a collection of all the page responses that iterates through the data.
public class MyOffsetPageCollectionResponse extends AbstractOffsetCollectionResponse<MyRequest, String, MyOffsetPageResponse> {
public MyOffsetPageCollectionResponse(Function<String, MyOffsetPageResponse> pageFunction) {
super(pageFunction);
}
}
With this definition, the endpoint can be exposed in the client:
public class MyClient extends HttpRoundRobinClient {
...
public MyOffsetPageCollectionResponse paginatedByOffset(MyRequest clientRequest) {
return new MyOffsetPageCollectionResponse(
offset -> {
var urlBuilder = serverHosts.next()
.newBuilder()
.addPathSegment("list")
.addPathSegment(request.getId())
.addQueryParameter("limit", String.valueOf(scroll.getSize()));
if (token != null) {
urlBuilder.addQueryParameter("page", String.valueOf(offset));
}
var requestDetails = new HttpClientRequestDetails<>(
new Request.Builder().url(urlBuilder.build()).get().build(),
clientRequest);
return new MyOffsetPageResponse(
objectMapper,
requestDetails,
executeWithBackoff(requestDetails),
offset
);
}
);
}
...
}
Note that in case of a null
token, the function must return the first page.
Each element in the response can then be retrieved by using the Iterable<>
interface. The pagination will be handled behind the scenes.
JSON
JSON handling is done with Jackson Project, and the default configuration for the mapper can be instantiated with com.pureinsights.pdp.core.config.CoreSerializer.newInstance()
.
Default Object Mapper
Registered Modules
JavaTimeModule
to handle the JSR-310: Date and Time API
Registered Deserializers
com.pureinsights.pdp.core.time.CoreDurationDeserializer
as an extension of the defaultDurationDeserializer
to allow values in human-readable format (i.e. "1s", "12m", "5h"...). If no suffix is provided, milliseconds are assumed. The supported suffixes are:d
- daysh
- hoursm
- minutess
- secondsms
- millisecondsns
- nanoseconds
Enabled Features
MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS
MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES
DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY
Disabled Features
SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE
Logging Handler
The logging handler simplifies the process of log analysis by using a JSON output with support for custom POJOs.
Logback Configuration
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<provider class="com.pureinsights.pdp.core.logging.CoreArgumentsProvider"/>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
Custom POJO
@Getter
public class MyPOJO {
private final UUID id = UUID.randomUUID();
}
public class MyPOJOLoggingHandler implements PojoLoggingHandler<MyPOJO> {
private static final String PARENT_LABEL = "myPOJO";
private static final String ID_LABEL = "id";
@Override
public Class<MyPOJO> getSupportedType() {
return MyPOJO.class;
}
@Override
public void write(JsonGenerator generator, MyPOJO pojo) throws IOException {
generator.writeFieldName(PARENT_LABEL);
generator.writeStartObject();
generator.writeObjectField(ID_LABEL, pojo.getId());
generator.writeEndObject();
}
}
In order to invoke the handler, the class type can call the logging framework:
log.info("Message", new MyPOJO());
log.info("Message {}", new MyPOJO());
log.info("Message {}", "with placeholder", new MyPOJO());