Skip to content

Commit

Permalink
[DNTT-165] Adds support for related_products object, X-Device-User-Ag…
Browse files Browse the repository at this point in the history
…ent (#228)
  • Loading branch information
dili91 authored Oct 12, 2023
1 parent 593478a commit 5a0f91c
Show file tree
Hide file tree
Showing 15 changed files with 175 additions and 16 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# TrueLayer Java

[![Build](https://github.com/TrueLayer/truelayer-java/actions/workflows/build.yml/badge.svg)](https://github.com/TrueLayer/truelayer-java/actions/workflows/build.yml)
[![Release final version](https://github.com/TrueLayer/truelayer-java/actions/workflows/release-final.yml/badge.svg)](https://github.com/TrueLayer/truelayer-java/actions/workflows/release-final.yml)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.truelayer/truelayer-java/badge.svg?style=flat)](https://search.maven.org/artifact/com.truelayer/truelayer-java)
[![javadoc](https://javadoc.io/badge2/com.truelayer/truelayer-java/javadoc.svg)](https://javadoc.io/doc/com.truelayer/truelayer-java)
[![Coverage Status](https://coveralls.io/repos/github/TrueLayer/truelayer-java/badge.svg?t=gcGKQv)](https://coveralls.io/github/TrueLayer/truelayer-java)
Expand Down
4 changes: 2 additions & 2 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.3"
id "io.freefair.lombok" version "8.4"
// to make our tests output more fancy
id 'com.adarshr.test-logger' version '3.2.0'
// to publish packages
id 'maven-publish'
// code linting
id "com.diffplug.spotless" version "6.21.0"
id "com.diffplug.spotless" version "6.22.0"
// test coverage
id 'jacoco'
id 'com.github.kt3k.coveralls' version '2.12.2'
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=10.0.0
version=10.1.0

# Artifacts properties
sonatype_repository_url=https://s01.oss.sonatype.org/service/local/
Expand Down
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 @@ -32,6 +32,7 @@ public static final class HeaderNames {
public static final String AUTHORIZATION = "Authorization";
public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
public static final String X_FORWARDED_FOR = "X-Forwarded-For";
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";
}
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/truelayer/java/entities/RelatedProducts.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.truelayer.java.entities;

import java.util.Map;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;

@Builder
@Getter
@ToString
@EqualsAndHashCode
public class RelatedProducts {

private Map<String, String> signupPlus;
}
2 changes: 2 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 @@ -18,4 +18,6 @@ public class Headers {
private String signature;

private String xForwardedFor;

private String xDeviceUserAgent;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public static Map<String, String> toMap(Headers customHeaders) {
headersMap.put(Constants.HeaderNames.X_FORWARDED_FOR, xForwardedFor);
}

String xDeviceUserAgent = customHeaders.getXDeviceUserAgent();
if (isNotEmpty(xDeviceUserAgent)) {
headersMap.put(Constants.HeaderNames.X_DEVICE_USER_AGENT, xDeviceUserAgent);
}

return Collections.unmodifiableMap(headersMap);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.truelayer.java.mandates.entities;

import com.truelayer.java.entities.CurrencyCode;
import com.truelayer.java.entities.RelatedProducts;
import com.truelayer.java.entities.User;
import com.truelayer.java.mandates.entities.mandate.Mandate;
import java.util.Map;
Expand All @@ -24,4 +25,6 @@ public class CreateMandateRequest {
private Constraints constraints;

private Map<String, String> metadata;

private RelatedProducts relatedProducts;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.truelayer.java.payments.entities;

import com.truelayer.java.entities.CurrencyCode;
import com.truelayer.java.entities.RelatedProducts;
import com.truelayer.java.entities.User;
import com.truelayer.java.payments.entities.paymentmethod.PaymentMethod;
import java.util.Map;
Expand All @@ -23,4 +24,9 @@ public class CreatePaymentRequest {
private User user;

private Map<String, String> metadata;

/**
* Optional field. Not available when creating recurring payments.
*/
private RelatedProducts relatedProducts;
}
11 changes: 11 additions & 0 deletions src/test/java/com/truelayer/java/TestUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ public static class RequestStub {
private String bodyFile;
private Integer delayMilliseconds;
private String xForwardedForHeader;
private String xDeviceUserAgent;

private RequestStub() {}

Expand Down Expand Up @@ -154,6 +155,11 @@ public RequestStub withXForwardedForHeader(String ipAddress) {
return this;
}

public RequestStub withXDeviceUserAgent(String deviceUserAgent) {
this.xDeviceUserAgent = deviceUserAgent;
return this;
}

public RequestStub delayMs(int delayMilliseconds) {
this.delayMilliseconds = delayMilliseconds;
return this;
Expand All @@ -178,6 +184,10 @@ public StubMapping build() {
request.withHeader(X_FORWARDED_FOR, equalTo(xForwardedForHeader));
}

if (isNotEmpty(xDeviceUserAgent)) {
request.withHeader(X_DEVICE_USER_AGENT, equalTo(xDeviceUserAgent));
}

ResponseDefinitionBuilder response = aResponse()
.withHeader(TL_CORRELATION_ID, UUID.randomUUID().toString())
.withStatus(status);
Expand All @@ -204,6 +214,7 @@ public static Headers buildTestHeaders() {
.idempotencyKey("a-custom-key_" + randomString)
.signature("a-custom-signature_" + randomString)
.xForwardedFor("1.2.3.4_" + randomString)
.xDeviceUserAgent("ADummyUserAgen")
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import com.truelayer.java.entities.CurrencyCode;
import com.truelayer.java.entities.ProviderFilter;
import com.truelayer.java.entities.RelatedProducts;
import com.truelayer.java.entities.User;
import com.truelayer.java.entities.accountidentifier.AccountIdentifier;
import com.truelayer.java.http.entities.ApiResponse;
Expand Down Expand Up @@ -68,6 +69,22 @@ public void itShouldCreateASweepingMandateWithPreselectedProvider() {
assertNotError(startAuthorizationFlowResponse);
}

@Test
@DisplayName("It should create a VRP sweeping mandate to be used with Signup+")
@SneakyThrows
public void itShouldCreateASweepingMandateWithSignupPlusIntention() {
ApiResponse<CreateMandateResponse> createMandateResponse = tlClient.mandates()
.createMandate(createMandateRequest(
SWEEPING,
ProviderSelection.preselected().providerId(PROVIDER_ID).build(),
RelatedProducts.builder()
.signupPlus(Collections.emptyMap())
.build()))
.get();

assertNotError(createMandateResponse);
}

@Test
@Disabled("The provider ob-uki-mock-bank-sbox does not support commercial VRP yet")
@DisplayName("It should create a VRP commercial mandate with preselected provider")
Expand Down Expand Up @@ -292,6 +309,11 @@ public void itShouldCreateAPaymentOnMandate(Retry retry) {
}

private CreateMandateRequest createMandateRequest(Mandate.Type type, ProviderSelection providerSelection) {
return createMandateRequest(type, providerSelection, null);
}

private CreateMandateRequest createMandateRequest(
Mandate.Type type, ProviderSelection providerSelection, RelatedProducts relatedProducts) {
Mandate mandate = null;
if (type.equals(Mandate.Type.COMMERCIAL)) {
mandate = Mandate.vrpCommercialMandate()
Expand All @@ -317,7 +339,7 @@ private CreateMandateRequest createMandateRequest(Mandate.Type type, ProviderSel
.build();
}

return CreateMandateRequest.builder()
CreateMandateRequest.CreateMandateRequestBuilder builder = CreateMandateRequest.builder()
.mandate(mandate)
.currency(CurrencyCode.GBP)
.user(User.builder()
Expand All @@ -334,8 +356,13 @@ private CreateMandateRequest createMandateRequest(Mandate.Type type, ProviderSel
.build())
.maximumIndividualAmount(1000)
.build())
.metadata(Collections.singletonMap("a_custom_key", "a-custom-value"))
.build();
.metadata(Collections.singletonMap("a_custom_key", "a-custom-value"));

if (ObjectUtils.isNotEmpty(relatedProducts)) {
builder.relatedProducts(relatedProducts);
}

return builder.build();
}

@SneakyThrows
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,22 @@ public void shouldCreateAPaymentWithUserSelectionProvider(CurrencyCode currency,
assertEquals(getPaymentByIdResponse.getData().getPaymentMethod(), paymentRequest.getPaymentMethod());
}

@Test
@DisplayName("It should create a payment to be used with Signup+")
@SneakyThrows
public void itShouldCreateAPaymentWithSignupPlusIntention() {
ApiResponse<CreatePaymentResponse> createPaymentResponse = tlClient.payments()
.createPayment(buildPaymentRequestWithProviderSelection(
buildPreselectedProviderSelection(),
CurrencyCode.GBP,
RelatedProducts.builder()
.signupPlus(Collections.emptyMap())
.build()))
.get();

assertNotError(createPaymentResponse);
}

@Test
@DisplayName("It should create and get by id a payment with preselected provider")
@SneakyThrows
Expand Down Expand Up @@ -509,10 +525,14 @@ private Beneficiary buildBeneficiary(CurrencyCode currencyCode) {
}
}

@SneakyThrows
private CreatePaymentRequest buildPaymentRequestWithProviderSelection(
ProviderSelection providerSelection, CurrencyCode currencyCode) {
return CreatePaymentRequest.builder()
return buildPaymentRequestWithProviderSelection(providerSelection, currencyCode, null);
}

private CreatePaymentRequest buildPaymentRequestWithProviderSelection(
ProviderSelection providerSelection, CurrencyCode currencyCode, RelatedProducts relatedProducts) {
CreatePaymentRequest.CreatePaymentRequestBuilder builder = CreatePaymentRequest.builder()
.amountInMinor(RandomUtils.nextInt(50, 500))
.currency(currencyCode)
.paymentMethod(PaymentMethod.bankTransfer()
Expand All @@ -531,8 +551,13 @@ private CreatePaymentRequest buildPaymentRequestWithProviderSelection(
.countryCode("GB")
.build())
.build())
.metadata(Collections.singletonMap("a_custom_key", "a-custom-value"))
.build();
.metadata(Collections.singletonMap("a_custom_key", "a-custom-value"));

if (relatedProducts != null) {
builder.relatedProducts(relatedProducts);
}

return builder.build();
}

private CreatePaymentRequest buildPaymentRequest(CurrencyCode currencyCode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public void shouldConvertHeadersObjectToMap() {

assertThrows(UnsupportedOperationException.class, () -> headersMap.put("foo", "bar"));
assertEquals(headers.getXForwardedFor(), headersMap.get(X_FORWARDED_FOR));
assertEquals(headers.getXDeviceUserAgent(), headersMap.get(X_DEVICE_USER_AGENT));
assertEquals(headers.getSignature(), headersMap.get(TL_SIGNATURE));
assertEquals(headers.getIdempotencyKey(), headersMap.get(IDEMPOTENCY_KEY));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.truelayer.java.integration;

import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.truelayer.java.Constants.Scopes.RECURRING_PAYMENTS_SWEEPING;
import static com.truelayer.java.TestUtils.assertNotError;
import static com.truelayer.java.TestUtils.deserializeJsonFileTo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.truelayer.java.TestUtils.RequestStub;
import com.truelayer.java.entities.RelatedProducts;
import com.truelayer.java.http.entities.ApiResponse;
import com.truelayer.java.http.entities.ProblemDetails;
import com.truelayer.java.mandates.entities.*;
Expand Down Expand Up @@ -61,6 +61,30 @@ public void shouldCreateAMandate() {
assertEquals(expected, response.getData());
}

@SneakyThrows
@Test
@DisplayName("It should create a mandate with additional product intention")
public void shouldCreateAMandateWithAdditionalProductIntention() {
RequestStub.New()
.method("post")
.path(urlPathEqualTo("/connect/token"))
.status(200)
.bodyFile("auth/200.access_token.json")
.build();
CreateMandateRequest createMandateRequest = CreateMandateRequest.builder()
.relatedProducts(RelatedProducts.builder()
.signupPlus(Collections.singletonMap("foo", "bar"))
.build())
.build();

tlClient.mandates().createMandate(createMandateRequest).get();

verifyGeneratedToken(Collections.singletonList(RECURRING_PAYMENTS_SWEEPING));
verify(postRequestedFor(urlPathEqualTo("/mandates"))
.withRequestBody(
matchingJsonPath("$.related_products", equalToJson("{\"signup_plus\": {\"foo\": \"bar\"}}"))));
}

@Test
@DisplayName("It should get a list of mandates")
@SneakyThrows
Expand Down
Loading

0 comments on commit 5a0f91c

Please sign in to comment.