Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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

Registered Deserializers

  • com.pureinsights.pdp.core.time.CoreDurationDeserializer as an extension of the default DurationDeserializer 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 - days
    • h - hours
    • m - minutes
    • s - seconds
    • ms - milliseconds
    • ns - 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());