Skip to content

Commit

Permalink
feat(EWT-535): Support for merchant account transactions pagination (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
dili91 committed Apr 15, 2024
1 parent 5233eec commit 34ab929
Show file tree
Hide file tree
Showing 18 changed files with 230 additions and 29 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,15 @@ URI hppLink = client.hpp().getHostedPaymentPageLink("your-createPaymentResponse-
Any version of the JDK >= 8 supported by Gradle can build and run this package.
Check the [Gradle compatibility matrix](https://docs.gradle.org/current/userguide/compatibility.html) for more info.

To ensure compatibility, Gradle is configured to use the Java 8 toolchain by default.
Note that on M1 Macs, Gradle is unable to download this toolchain automatically and returns the following error:
`Could not read 'https: //api.adoptopenidk.net/v3/binary/latest/8/ga/mac/aarch64/idk/hotspot/normal/adoptopenidk' as it does not exist.`.
Manually install an aarch64 version of the JDK 1.8 to solve it, like the "Azul Zulu 1.8" for example.
> [!IMPORTANT]
> To ensure compatibility, Gradle is configured to use the Java 8 toolchain by default.
> Note that on M1 Macs, Gradle is unable to download this toolchain automatically and returns the following error:
> `Could not read 'https: //api.adoptopenidk.net/v3/binary/latest/8/ga/mac/aarch64/idk/hotspot/normal/adoptopenidk' as it does not exist.`.
>
> Manually install an aarch64 version of the JDK 1.8 to solve it, like the "Azul Zulu 1.8" for example.
> [!TIP]
> We use [just](https://github.com/casey/just) to simplify commands management when building locally. Run `just -l` for a list of recipes.
## Building locally

Expand Down
10 changes: 5 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
plugins {
id 'java-library'
// to unleash the lombok magic
id "io.freefair.lombok" version "8.4"
id "io.freefair.lombok" version "8.6"
// to make our tests output more fancy
id 'com.adarshr.test-logger' version '4.0.0'
// to publish packages
id 'maven-publish'
// code linting
id "com.diffplug.spotless" version "6.23.3"
id "com.diffplug.spotless" version "6.25.0"
// test coverage
id 'jacoco'
id 'com.github.kt3k.coveralls' version '2.12.2'
Expand Down Expand Up @@ -110,11 +110,11 @@ dependencies {
implementation group: 'org.tinylog', name: 'tinylog-impl', version: tinyLogVersion

// JUnit test framework.
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.10.1'
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.10.2'

// Mocking libraries
testImplementation group: 'org.mockito', name: 'mockito-core', version: '5.8.0'
testImplementation group: 'org.wiremock', name: 'wiremock', version: '3.3.1'
testImplementation group: 'org.mockito', name: 'mockito-core', version: '5.11.0'
testImplementation group: 'org.wiremock', name: 'wiremock', version: '3.5.2'

// Wait test utility
testImplementation group: 'org.awaitility', name: 'awaitility', version: '4.2.0'
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Main properties
group=com.truelayer
archivesBaseName=truelayer-java
version=11.0.1
version=11.1.0

# Artifacts properties
sonatype_repository_url=https://s01.oss.sonatype.org/service/local/
Expand Down
24 changes: 24 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
alias b := build
alias l := lint
alias ut := unit-test
alias it := integration-test
alias at := acceptance-test
alias t := test

build:
./gradlew build -x test

lint:
./gradlew spotlessApply

unit-test:
./gradlew unit-tests

integration-test:
./gradlew integration-tests

acceptance-test:
echo "Staring acceptance-tests with client_id=[$TL_CLIENT_ID]..."
./gradlew acceptance-tests

test: unit-test integration-test acceptance-test
1 change: 1 addition & 0 deletions src/main/java/com/truelayer/java/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ public static final class HeaderNames {
public static final String X_DEVICE_USER_AGENT = "X-Device-User-Agent";
public static final String COOKIE = "Cookie";
public static final String TL_CORRELATION_ID = "X-Tl-Correlation-Id";
public static final String TL_ENABLE_PAGINATION = "tl-enable-pagination";
}
}
12 changes: 12 additions & 0 deletions src/main/java/com/truelayer/java/http/entities/Headers.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,16 @@ public class Headers {
private String xForwardedFor;

private String xDeviceUserAgent;

private boolean enablePagination;

/**
* Custom builder for the Headers class that prevents setting the enablePagination field to false.
*/
public static class HeadersBuilder {
public HeadersBuilder enablePagination() {
this.enablePagination = true;
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ public static Map<String, String> toMap(Headers customHeaders) {
headersMap.put(Constants.HeaderNames.X_DEVICE_USER_AGENT, xDeviceUserAgent);
}

if (customHeaders.isEnablePagination()) {
headersMap.put(Constants.HeaderNames.TL_ENABLE_PAGINATION, "true");
}

return Collections.unmodifiableMap(headersMap);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ CompletableFuture<ApiResponse<MerchantAccount>> getMerchantAccountById(
/**
* Get the transactions of a single merchant account.
* @param scopes the scopes to be used by the underlying Oauth token
* @param headers map representing custom HTTP headers to be sent. In this case used for old clients to opt-in to paginated results
* @param merchantAccountId the id of the merchant account
* @param from Timestamp as a string for the start of the range you are querying. Mandatory
* @param to Timestamp as a string for the end of the range you are querying. Mandatory
Expand All @@ -52,10 +53,12 @@ CompletableFuture<ApiResponse<MerchantAccount>> getMerchantAccountById(
@GET("/merchant-accounts/{merchantAccountId}/transactions")
CompletableFuture<ApiResponse<ListTransactionsResponse>> listTransactions(
@Tag RequestScopes scopes,
@HeaderMap Map<String, String> headers,
@Path("merchantAccountId") String merchantAccountId,
@Query("from") String from,
@Query("to") String to,
@Query("type") TransactionTypeQuery type);
@Query("type") TransactionTypeQuery type,
@Query("cursor") String cursor);

/**
* Set the automatic sweeping settings for a merchant account. At regular intervals, any available balance in excess
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ public interface IMerchantAccountsHandler {
CompletableFuture<ApiResponse<ListTransactionsResponse>> listTransactions(
String merchantAccountId, ListTransactionsQuery query);

/**
* Overload meant to give clients created before December 2023 the ability to opt-in for paginated results at a <i>request</i> level via the
* <code>tl-enable-pagination</code> HTTP header.<br>
* This is useful for clients that want to ease the migration towards paginated responses, it is not required once
* the pagination opt-in is enabled at the client level.<br>
* See <a href="https://docs.truelayer.com/docs/enable-pagination-transactions-endpoint#enable-pagination">Enable pagination</a> for more details.<br><br>
* Clients created after November 2023 can use the {@link #listTransactions(String, ListTransactionsQuery) simpler overload}.
* @param headers map representing custom HTTP headers to be sent. In this case used to specify the {@link Headers#enablePagination enablePagination} flag
* @param merchantAccountId the id of the merchant account
* @param query the query to filter the transactions
* @return a list of transactions matching the specified filters
*/
CompletableFuture<ApiResponse<ListTransactionsResponse>> listTransactions(
Headers headers, String merchantAccountId, ListTransactionsQuery query);

CompletableFuture<ApiResponse<SweepingSettings>> updateSweeping(
String merchantAccountId, UpdateSweepingRequest updateSweepingRequest);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,17 @@ public CompletableFuture<ApiResponse<ListTransactionsResponse>> listTransactions
String merchantAccountId, ListTransactionsQuery query) {
String from = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(query.from());
String to = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(query.to());
return merchantAccountsApi.listTransactions(getRequestScopes(), merchantAccountId, from, to, query.type());
return merchantAccountsApi.listTransactions(
getRequestScopes(), emptyMap(), merchantAccountId, from, to, query.type(), query.cursor());
}

@Override
public CompletableFuture<ApiResponse<ListTransactionsResponse>> listTransactions(
Headers headers, String merchantAccountId, ListTransactionsQuery query) {
String from = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(query.from());
String to = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(query.to());
return merchantAccountsApi.listTransactions(
getRequestScopes(), toMap(headers), merchantAccountId, from, to, query.type(), query.cursor());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ public class ListTransactionsQuery {
private ZonedDateTime to;

private TransactionTypeQuery type;

private String cursor;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.truelayer.java.merchantaccounts.entities;

import com.truelayer.java.entities.PaginationMetadata;
import com.truelayer.java.merchantaccounts.entities.transactions.Transaction;
import java.util.List;
import lombok.Value;
Expand All @@ -8,4 +9,6 @@
public class ListTransactionsResponse {

List<Transaction> items;

PaginationMetadata pagination;
}
3 changes: 2 additions & 1 deletion src/test/java/com/truelayer/java/TestUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ public static Headers buildTestHeaders() {
.idempotencyKey("a-custom-key_" + randomString)
.signature("a-custom-signature_" + randomString)
.xForwardedFor("1.2.3.4_" + randomString)
.xDeviceUserAgent("ADummyUserAgen")
.xDeviceUserAgent("ADummyUserAgent")
.enablePagination()
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import com.truelayer.java.entities.CurrencyCode;
import com.truelayer.java.http.entities.ApiResponse;
import com.truelayer.java.http.entities.Headers;
import com.truelayer.java.merchantaccounts.entities.*;
import com.truelayer.java.merchantaccounts.entities.sweeping.Frequency;
import com.truelayer.java.merchantaccounts.entities.sweeping.SweepingSettings;
Expand Down Expand Up @@ -133,6 +134,9 @@ public void itShouldGetPaymentSources(ZonedDateTime from, ZonedDateTime to) {
private ApiResponse<ListTransactionsResponse> getTransactions(ZonedDateTime from, ZonedDateTime to) {
return tlClient.merchantAccounts()
.listTransactions(
Headers.builder()
.enablePagination()
.build(), // Required for clients before December 2023 to opt-in for paginated results
getMerchantAccount(CurrencyCode.GBP).getId(),
ListTransactionsQuery.builder().from(from).to(to).build())
.get();
Expand All @@ -141,11 +145,11 @@ private ApiResponse<ListTransactionsResponse> getTransactions(ZonedDateTime from
private static Stream<Arguments> provideFromAndToParameters() {
return Stream.of(
Arguments.of(
ZonedDateTime.of(LocalDate.of(2021, 3, 1), LocalTime.MIN, ZoneId.of("UTC")),
ZonedDateTime.of(LocalDate.of(2022, 3, 1), LocalTime.MIN, ZoneId.of("UTC"))),
ZonedDateTime.of(LocalDate.of(2024, 2, 1), LocalTime.MIN, ZoneId.of("UTC")),
ZonedDateTime.of(LocalDate.of(2024, 3, 1), LocalTime.MIN, ZoneId.of("UTC"))),
Arguments.of(
ZonedDateTime.of(LocalDate.of(2021, 3, 1), LocalTime.MIN, ZoneId.of("Europe/Paris")),
ZonedDateTime.of(LocalDate.of(2022, 3, 1), LocalTime.MIN, ZoneId.of("Europe/Paris"))),
Arguments.of(ZonedDateTime.parse("2021-03-01T00:00:00Z"), ZonedDateTime.parse("2022-03-01T00:00:00Z")));
ZonedDateTime.of(LocalDate.of(2024, 2, 1), LocalTime.MIN, ZoneId.of("Europe/Paris")),
ZonedDateTime.of(LocalDate.of(2024, 3, 1), LocalTime.MIN, ZoneId.of("Europe/Paris"))),
Arguments.of(ZonedDateTime.parse("2024-02-01T00:00:00Z"), ZonedDateTime.parse("2024-03-01T00:00:00Z")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,14 @@ public void shouldSendAProvidedCustomHeadersWithoutAutoGeneration() {
Headers.builder()
.signature(signature)
.idempotencyKey(idempotencyKey)
.enablePagination()
.build(),
paymentRequest)
.get();

verifyGeneratedToken(Collections.singletonList(Constants.Scopes.PAYMENTS));
verify(postRequestedFor(urlPathEqualTo("/payments"))
.withHeader(TL_ENABLE_PAGINATION, equalTo("true"))
.withHeader(TL_SIGNATURE, equalTo(signature))
.withHeader(IDEMPOTENCY_KEY, equalTo(idempotencyKey)));
}
Expand Down

0 comments on commit 34ab929

Please sign in to comment.