Skip to content

Commit 1f61fc0

Browse files
committed
private and public API
1 parent 6c9382b commit 1f61fc0

4 files changed

Lines changed: 307 additions & 207 deletions

File tree

Lines changed: 14 additions & 206 deletions
Original file line numberDiff line numberDiff line change
@@ -1,235 +1,43 @@
11
package com.openelements.cardless;
22

3-
import com.google.gson.JsonElement;
4-
import com.google.gson.JsonObject;
5-
import com.google.gson.JsonParser;
6-
import com.openelements.cardless.data.AccessAndRefreshToken;
7-
import com.openelements.cardless.data.AccessToken;
83
import com.openelements.cardless.data.Account;
94
import com.openelements.cardless.data.Balance;
10-
import com.openelements.cardless.data.ErrorMessage;
115
import com.openelements.cardless.data.Institution;
12-
import com.openelements.cardless.data.JsonBasedFactory;
136
import com.openelements.cardless.data.Requisition;
147
import com.openelements.cardless.data.RequisitionsPage;
158
import com.openelements.cardless.data.Transactions;
9+
import com.openelements.cardless.internal.CardlessClientImpl;
1610
import java.io.IOException;
17-
import java.net.URI;
18-
import java.net.http.HttpClient;
19-
import java.net.http.HttpRequest;
20-
import java.net.http.HttpResponse;
21-
import java.net.http.HttpResponse.BodyHandlers;
2211
import java.util.List;
23-
import java.util.Objects;
24-
import java.util.concurrent.atomic.AtomicReference;
2512
import org.jspecify.annotations.NonNull;
26-
import org.slf4j.Logger;
27-
import org.slf4j.LoggerFactory;
2813

29-
public class CardlessClient {
14+
public interface CardlessClient {
3015

31-
private final static Logger log = LoggerFactory.getLogger(CardlessClient.class);
32-
33-
private final HttpClient httpClient;
34-
35-
private AtomicReference<AccessAndRefreshToken> accessAndRefreshTokenRef = new AtomicReference<>();
36-
37-
public CardlessClient(@NonNull final String secretId, @NonNull final String secretKey)
38-
throws IOException, InterruptedException {
39-
Objects.requireNonNull(secretId, "secretId must not be null");
40-
Objects.requireNonNull(secretKey, "secretKey must not be null");
41-
httpClient = HttpClient.newBuilder().build();
42-
final JsonObject body = JsonBasedFactory.createReceiveAccessToken(secretId, secretKey);
43-
final JsonElement response = handlePostRequest("https://bankaccountdata.gocardless.com/api/v2/token/new/",
44-
body);
45-
final AccessAndRefreshToken token = JsonBasedFactory.createAccessAndRefreshToken(response);
46-
accessAndRefreshTokenRef.set(token);
16+
static CardlessClient of(@NonNull final String secretId, @NonNull final String secretKey)
17+
throws CardlessException {
18+
return new CardlessClientImpl(secretId, secretKey);
4719
}
4820

4921
@NonNull
50-
private static ErrorMessage createFromJson(@NonNull final String json) {
51-
Objects.requireNonNull(json, "json must not be null");
52-
final JsonElement jsonElement = JsonParser.parseString(json);
53-
if (jsonElement.isJsonObject()) {
54-
final JsonObject jsonObject = jsonElement.getAsJsonObject();
55-
final String summary = jsonObject.get("summary").getAsString();
56-
final String detail = jsonObject.get("detail").getAsString();
57-
final int statusCode = jsonObject.get("status_code").getAsInt();
58-
return new ErrorMessage(summary, detail, statusCode);
59-
}
60-
throw new IllegalArgumentException("Invalid JSON format: " + json);
61-
}
22+
RequisitionsPage getRequisitions(final int limit, final int offset)
23+
throws CardlessException;
6224

6325
@NonNull
64-
private HttpRequest createGetRequest(@NonNull final String url) throws IOException, InterruptedException {
65-
return createGetRequest(url, true);
66-
}
26+
List<Institution> getInstitutions(@NonNull final String country) throws CardlessException;
6727

6828
@NonNull
69-
private HttpRequest createGetRequest(@NonNull final String url, final boolean checkAccessToken)
70-
throws IOException, InterruptedException {
71-
Objects.requireNonNull(url, "url must not be null");
72-
if (checkAccessToken) {
73-
checkAccessToken();
74-
}
75-
final AccessAndRefreshToken accessAndRefreshToken = accessAndRefreshTokenRef.get();
76-
final HttpRequest.Builder builder = HttpRequest.newBuilder()
77-
.uri(URI.create(url))
78-
.header("Content-Type", "application/json")
79-
.header("accept", "application/json");
80-
if (accessAndRefreshToken != null) {
81-
builder.header("Authorization", "Bearer " + accessAndRefreshTokenRef.get().access());
82-
}
83-
return builder.GET()
84-
.build();
85-
}
29+
Requisition createRequisition(@NonNull final Institution institution)
30+
throws CardlessException;
8631

8732
@NonNull
88-
private HttpRequest createPostRequest(@NonNull final String url, @NonNull JsonElement body,
89-
boolean checkAccessToken) throws IOException, InterruptedException {
90-
Objects.requireNonNull(url, "url must not be null");
91-
Objects.requireNonNull(body, "body must not be null");
92-
if (checkAccessToken) {
93-
checkAccessToken();
94-
}
95-
final AccessAndRefreshToken accessAndRefreshToken = accessAndRefreshTokenRef.get();
96-
final HttpRequest.Builder builder = HttpRequest.newBuilder()
97-
.uri(URI.create(url))
98-
.header("Content-Type", "application/json")
99-
.header("accept", "application/json");
100-
if (accessAndRefreshToken != null) {
101-
builder.header("Authorization", "Bearer " + accessAndRefreshTokenRef.get().access());
102-
}
103-
return builder.POST(HttpRequest.BodyPublishers.ofString(body.toString()))
104-
.build();
105-
}
33+
Requisition createRequisition(@NonNull final String institutionId) throws CardlessException;
10634

10735
@NonNull
108-
private JsonElement handleGetRequest(@NonNull final String url) throws IOException, InterruptedException {
109-
final HttpRequest request = createGetRequest(url);
110-
final HttpResponse<String> response = httpClient.send(request, BodyHandlers.ofString());
111-
if (response.statusCode() != 200) {
112-
try {
113-
final ErrorMessage errorMessage = createFromJson(response.body());
114-
throw new ApiCallException(errorMessage);
115-
} catch (Exception e) {
116-
throw new IOException("Error in HTTP call", e);
117-
}
118-
}
119-
final String json = response.body();
120-
try {
121-
return JsonParser.parseString(json);
122-
} catch (Exception e) {
123-
throw new IOException("Error parsing JSON response: " + json, e);
124-
}
125-
}
36+
Transactions getTransactions(@NonNull final String account) throws CardlessException;
12637

12738
@NonNull
128-
private JsonElement handlePostRequest(@NonNull final String url, @NonNull final JsonElement body)
129-
throws IOException, InterruptedException {
130-
final HttpRequest request = createPostRequest(url, body, false);
131-
final HttpResponse<String> response = httpClient.send(request, BodyHandlers.ofString());
132-
if (response.statusCode() != 200 && response.statusCode() != 201) {
133-
try {
134-
final ErrorMessage errorMessage = createFromJson(response.body());
135-
throw new ApiCallException(errorMessage);
136-
} catch (Exception e) {
137-
throw new IOException("Error in HTTP call", e);
138-
}
139-
}
140-
final String json = response.body();
141-
try {
142-
return JsonParser.parseString(json);
143-
} catch (Exception e) {
144-
throw new IOException("Error parsing JSON response: " + json, e);
145-
}
146-
}
147-
148-
@NonNull
149-
private AccessToken updateAccessToken() throws IOException, InterruptedException {
150-
final JsonObject body = JsonBasedFactory.createUpdateAccessTokenBody(accessAndRefreshTokenRef.get().refresh());
151-
final JsonElement jsonElement = handlePostRequest(
152-
"https://bankaccountdata.gocardless.com/api/v2/token/refresh/",
153-
body);
154-
return JsonBasedFactory.createAccessToken(jsonElement);
155-
}
156-
157-
private synchronized void checkAccessToken() throws IOException, InterruptedException {
158-
final AccessAndRefreshToken currentAccessToken = accessAndRefreshTokenRef.get();
159-
if (currentAccessToken.willExpireShortly()) {
160-
AccessToken newAccessToken = updateAccessToken();
161-
accessAndRefreshTokenRef.set(
162-
new AccessAndRefreshToken(newAccessToken.access(), newAccessToken.access_expires(),
163-
currentAccessToken.refresh(), currentAccessToken.refreshExpires()));
164-
}
165-
}
166-
167-
@NonNull
168-
public RequisitionsPage getRequisitions(final int limit, final int offset)
169-
throws IOException, InterruptedException {
170-
log.debug("Fetching requisitions with limit: {}, offset: {}", limit, offset);
171-
final JsonElement jsonElement = handleGetRequest(
172-
"https://bankaccountdata.gocardless.com/api/v2/requisitions/?limit=" + limit + "&offset=" + offset);
173-
log.debug("Received JSON: {}", jsonElement);
174-
return JsonBasedFactory.createRequisitionsPage(jsonElement);
175-
}
39+
Account getAccount(@NonNull final String id) throws CardlessException;
17640

17741
@NonNull
178-
public List<Institution> getInstitutions(@NonNull final String country) throws IOException, InterruptedException {
179-
Objects.requireNonNull(country, "country must not be null");
180-
log.debug("Fetching institutions for country: {}", country);
181-
final JsonElement jsonElement = handleGetRequest(
182-
"https://bankaccountdata.gocardless.com/api/v2/institutions/?country=" + country);
183-
log.debug("Received JSON: {}", jsonElement);
184-
return jsonElement.getAsJsonArray().asList().stream()
185-
.map(JsonBasedFactory::createInstitution)
186-
.toList();
187-
}
188-
189-
@NonNull
190-
public Requisition createRequisition(@NonNull final Institution institution)
191-
throws IOException, InterruptedException {
192-
return createRequisition(institution.id());
193-
}
194-
195-
@NonNull
196-
public Requisition createRequisition(@NonNull final String institutionId) throws IOException, InterruptedException {
197-
Objects.requireNonNull(institutionId, "institutionId must not be null");
198-
log.debug("Creating requisition for institutionId: {}", institutionId);
199-
final JsonObject body = JsonBasedFactory.createRequisitionRequestBody(institutionId);
200-
final JsonElement jsonElement = handlePostRequest("https://bankaccountdata.gocardless.com/api/v2/requisitions/",
201-
body);
202-
log.debug("Received JSON: {}", jsonElement);
203-
return JsonBasedFactory.createRequisition(jsonElement);
204-
}
205-
206-
@NonNull
207-
public Transactions getTransactions(@NonNull final String account) throws IOException, InterruptedException {
208-
Objects.requireNonNull(account, "account must not be null");
209-
log.debug("Fetching transactions for account: {}", account);
210-
final JsonElement jsonElement = handleGetRequest(
211-
"https://bankaccountdata.gocardless.com/api/v2/accounts/" + account + "/transactions/");
212-
log.debug("Received JSON: {}", jsonElement);
213-
return JsonBasedFactory.createTransactions(jsonElement);
214-
}
215-
216-
@NonNull
217-
public Account getAccount(@NonNull final String id) throws IOException, InterruptedException {
218-
Objects.requireNonNull(id, "id must not be null");
219-
log.debug("Fetching account with id: {}", id);
220-
final JsonElement jsonElement = handleGetRequest(
221-
"https://bankaccountdata.gocardless.com/api/v2/accounts/" + id + "/");
222-
log.debug("Received JSON: {}", jsonElement);
223-
return JsonBasedFactory.createAccount(jsonElement);
224-
}
225-
226-
@NonNull
227-
public List<Balance> getBalances(@NonNull final String accountId) throws IOException, InterruptedException {
228-
Objects.requireNonNull(accountId, "accountId must not be null");
229-
log.debug("Fetching balances for accountId: {}", accountId);
230-
final JsonElement jsonElement = handleGetRequest(
231-
"https://bankaccountdata.gocardless.com/api/v2/accounts/" + accountId + "/balances/");
232-
log.debug("Received JSON: {}", jsonElement);
233-
return JsonBasedFactory.createBalances(jsonElement);
234-
}
42+
List<Balance> getBalances(@NonNull final String accountId) throws CardlessException;
23543
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.openelements.cardless;
2+
3+
import org.jspecify.annotations.NonNull;
4+
5+
public class CardlessException extends Exception {
6+
public CardlessException(@NonNull final String message) {
7+
super(message);
8+
}
9+
10+
public CardlessException(@NonNull final String message, final Throwable cause) {
11+
super(message, cause);
12+
}
13+
}

0 commit comments

Comments
 (0)