Skip to content

Commit 2c7d5f0

Browse files
committed
Implemented Attachments API
1 parent a5a41ff commit 2c7d5f0

File tree

12 files changed

+330
-25
lines changed

12 files changed

+330
-25
lines changed

pom.xml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,21 @@
101101
<version>5.12.0</version>
102102
<scope>test</scope>
103103
</dependency>
104-
105-
106104
</dependencies>
107105

108106
<build>
109107
<plugins>
108+
<plugin>
109+
<groupId>org.apache.maven.plugins</groupId>
110+
<artifactId>maven-compiler-plugin</artifactId>
111+
<version>3.13.0</version>
112+
<configuration>
113+
<compilerArgs>
114+
<arg>-parameters</arg>
115+
</compilerArgs>
116+
</configuration>
117+
</plugin>
118+
110119
<plugin>
111120
<groupId>org.apache.maven.plugins</groupId>
112121
<artifactId>maven-surefire-plugin</artifactId>

src/main/java/io/mailtrap/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ private Constants() {
1010
public static final String EMAIL_TESTING_SEND_HOST = "https://sandbox.api.mailtrap.io";
1111
public static final String EMAIL_SENDING_SEND_HOST = "https://send.api.mailtrap.io";
1212
public static final String BULK_SENDING_HOST = "https://bulk.api.mailtrap.io";
13+
public static final String DEFAULT_HOST = "https://mailtrap.io";
1314

1415
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package io.mailtrap.api;
2+
3+
import io.mailtrap.Constants;
4+
import io.mailtrap.api.abstractions.Attachments;
5+
import io.mailtrap.api.abstractions.classes.ApiResource;
6+
import io.mailtrap.config.MailtrapConfig;
7+
import io.mailtrap.http.RequestData;
8+
import io.mailtrap.model.response.AttachmentResponse;
9+
10+
import java.util.List;
11+
import java.util.Optional;
12+
13+
import static io.mailtrap.http.RequestData.entry;
14+
15+
public class AttachmentsImpl extends ApiResource implements Attachments {
16+
17+
public AttachmentsImpl(MailtrapConfig config) {
18+
super(config);
19+
this.apiHost = Constants.DEFAULT_HOST;
20+
}
21+
22+
@Override
23+
public AttachmentResponse getSingleAttachment(long accountId, int inboxId, long messageId, long attachmentId) {
24+
return httpClient.get(
25+
String.format(apiHost + "/api/accounts/%s/inboxes/%s/messages/%s/attachments/%s", accountId, inboxId, messageId, attachmentId),
26+
new RequestData(),
27+
AttachmentResponse.class);
28+
}
29+
30+
@Override
31+
public List<AttachmentResponse> getAttachments(long accountId, int inboxId, long messageId, String attachmentType) {
32+
var queryParams = RequestData.buildQueryParams(
33+
entry("attachment_type", Optional.ofNullable(attachmentType)));
34+
35+
return httpClient.getList(
36+
String.format(apiHost + "/api/accounts/%s/inboxes/%s/messages/%s/attachments", accountId, inboxId, messageId),
37+
new RequestData(queryParams),
38+
AttachmentResponse.class);
39+
}
40+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.mailtrap.api.abstractions;
2+
3+
import io.mailtrap.model.response.AttachmentResponse;
4+
5+
import java.util.List;
6+
7+
public interface Attachments {
8+
9+
AttachmentResponse getSingleAttachment(long accountId, int inboxId, long messageId, long attachmentId);
10+
11+
List<AttachmentResponse> getAttachments(long accountId, int inboxId, long messageId, String attachmentType);
12+
13+
}

src/main/java/io/mailtrap/client/layers/MailtrapEmailTestingApi.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.mailtrap.client.layers;
22

3+
import io.mailtrap.api.abstractions.Attachments;
34
import io.mailtrap.api.abstractions.TestingEmails;
45
import lombok.Getter;
56
import lombok.RequiredArgsConstructor;
@@ -13,4 +14,5 @@
1314
@RequiredArgsConstructor
1415
public class MailtrapEmailTestingApi {
1516
private final TestingEmails emails;
17+
private final Attachments attachments;
1618
}

src/main/java/io/mailtrap/factory/MailtrapClientFactory.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.mailtrap.factory;
22

33
import io.mailtrap.CustomValidator;
4+
import io.mailtrap.api.AttachmentsImpl;
45
import io.mailtrap.api.BulkEmailsImpl;
56
import io.mailtrap.api.SendingEmailsImpl;
67
import io.mailtrap.api.TestingEmailsImpl;
@@ -56,8 +57,9 @@ private static MailtrapEmailSendingApi createSendingApi(MailtrapConfig config, C
5657

5758
private static MailtrapEmailTestingApi createTestingApi(MailtrapConfig config, CustomValidator customValidator) {
5859
var emails = new TestingEmailsImpl(config, customValidator);
60+
var attachments = new AttachmentsImpl(config);
5961

60-
return new MailtrapEmailTestingApi(emails);
62+
return new MailtrapEmailTestingApi(emails, attachments);
6163
}
6264

6365
private static MailtrapBulkSendingApi createBulkSendingApi(MailtrapConfig config, CustomValidator customValidator) {

src/main/java/io/mailtrap/http/CustomHttpClient.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.mailtrap.model.AbstractModel;
99

1010
import java.net.http.HttpClient;
11+
import java.util.List;
1112
import java.util.Map;
1213
import java.util.Optional;
1314
import java.util.stream.Collectors;
@@ -30,6 +31,16 @@ public interface CustomHttpClient {
3031
*/
3132
<T> T get(String url, RequestData requestData, Class<T> responseType) throws HttpException;
3233

34+
/**
35+
* @param url Request url
36+
* @param requestData Additional request data - headers and query parameters
37+
* @param responseType Return class type
38+
* @param <T> Return type
39+
* @return Response type
40+
* @throws HttpException in case any error. Might throw specific {@link HttpClientException} for HTTP response codes 4xx or {@link HttpServerException} for HTTP response codes 5xx
41+
*/
42+
<T> List<T> getList(String url, RequestData requestData, Class<T> responseType) throws HttpException;
43+
3344
/**
3445
* @param url Request url
3546
* @param requestData Additional request data - headers and query parameters
@@ -96,7 +107,10 @@ public interface CustomHttpClient {
96107
* @return The URL with appended query parameters. If no parameters are provided, returns the original URL unchanged.
97108
*/
98109
default String appendUrlParams(String url, Map<String, ? extends Optional<?>> urlParams) {
99-
if (urlParams.isEmpty()) {
110+
if (urlParams.isEmpty()
111+
|| urlParams.entrySet()
112+
.stream()
113+
.noneMatch(e -> e.getValue().isPresent())) {
100114
return url;
101115
}
102116

src/main/java/io/mailtrap/http/RequestData.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import lombok.Data;
55
import lombok.NoArgsConstructor;
66

7+
import java.util.AbstractMap;
78
import java.util.HashMap;
89
import java.util.Map;
910
import java.util.Optional;
@@ -28,6 +29,10 @@ public class RequestData {
2829
*/
2930
private Map<String, ?> headers = new HashMap<>();
3031

32+
public RequestData(Map<String, ? extends Optional<?>> queryParams) {
33+
this.queryParams = queryParams;
34+
}
35+
3136
/**
3237
* Builds a map of query parameters from the specified key-value pairs.
3338
*
@@ -42,4 +47,8 @@ public static <T extends Optional<?>> Map<String, T> buildQueryParams(Map.Entry<
4247
}
4348
return params;
4449
}
50+
51+
public static <T extends Optional<?>> Map.Entry<String, T> entry(String key, T value) {
52+
return new AbstractMap.SimpleEntry<>(key, value);
53+
}
4554
}

src/main/java/io/mailtrap/http/impl/DefaultMailtrapHttpClient.java

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.mailtrap.http.impl;
22

3+
import com.fasterxml.jackson.databind.JavaType;
4+
import com.fasterxml.jackson.databind.type.TypeFactory;
35
import io.mailtrap.Mapper;
46
import io.mailtrap.config.MailtrapConfig;
57
import io.mailtrap.exception.BaseMailtrapException;
@@ -13,12 +15,12 @@
1315
import io.mailtrap.model.response.ErrorResponse;
1416

1517
import java.io.IOException;
16-
import java.io.InputStream;
1718
import java.net.URI;
1819
import java.net.http.HttpClient;
1920
import java.net.http.HttpRequest;
2021
import java.net.http.HttpResponse;
2122
import java.util.HashMap;
23+
import java.util.List;
2224
import java.util.Map;
2325

2426
public class DefaultMailtrapHttpClient implements CustomHttpClient {
@@ -37,72 +39,106 @@ public DefaultMailtrapHttpClient(MailtrapConfig config) {
3739

3840
@Override
3941
public <T> T get(String url, RequestData requestData, Class<T> responseType) throws HttpException {
40-
HttpRequest httpRequest = prebuildRequest(url, requestData)
42+
HttpRequest httpRequest = prepareRequest(url, requestData)
4143
.GET()
4244
.build();
4345
return this.request(httpRequest, responseType);
4446
}
4547

48+
@Override
49+
public <T> List<T> getList(String url, RequestData requestData, Class<T> responseClass) throws HttpException {
50+
HttpRequest httpRequest = prepareRequest(url, requestData)
51+
.GET()
52+
.build();
53+
54+
JavaType responseType = TypeFactory.defaultInstance().constructCollectionType(List.class, responseClass);
55+
56+
return this.request(httpRequest, responseType);
57+
}
58+
4659
@Override
4760
public <T> T delete(String url, RequestData requestData, Class<T> responseType) throws HttpException {
48-
HttpRequest httpRequest = prebuildRequest(url, requestData)
61+
HttpRequest httpRequest = prepareRequest(url, requestData)
4962
.DELETE()
5063
.build();
5164
return this.request(httpRequest, responseType);
5265
}
5366

5467
@Override
5568
public <T> T head(String url, RequestData requestData, Class<T> responseType) throws HttpException {
56-
HttpRequest httpRequest = prebuildRequest(url, requestData)
69+
HttpRequest httpRequest = prepareRequest(url, requestData)
5770
.method("HEAD", HttpRequest.BodyPublishers.noBody())
5871
.build();
5972
return this.request(httpRequest, responseType);
6073
}
6174

6275
@Override
6376
public <T, V extends AbstractModel> T post(String url, V data, RequestData requestData, Class<T> responseType) throws HttpException {
64-
HttpRequest httpRequest = prebuildRequest(url, requestData)
77+
HttpRequest httpRequest = prepareRequest(url, requestData)
6578
.POST(HttpRequest.BodyPublishers.ofString(data.toJson()))
6679
.build();
6780
return this.request(httpRequest, responseType);
6881
}
6982

7083
@Override
7184
public <T, V extends AbstractModel> T put(String url, V data, RequestData requestData, Class<T> responseType) throws HttpException {
72-
HttpRequest httpRequest = prebuildRequest(url, requestData)
85+
HttpRequest httpRequest = prepareRequest(url, requestData)
7386
.PUT(HttpRequest.BodyPublishers.ofString(data.toJson()))
7487
.build();
7588
return this.request(httpRequest, responseType);
7689
}
7790

7891
@Override
7992
public <T, V extends AbstractModel> T patch(String url, V data, RequestData requestData, Class<T> responseType) throws HttpException {
80-
HttpRequest httpRequest = prebuildRequest(url, requestData)
93+
HttpRequest httpRequest = prepareRequest(url, requestData)
8194
.method("PATCH", HttpRequest.BodyPublishers.ofString(data.toJson()))
8295
.build();
8396
return this.request(httpRequest, responseType);
8497
}
8598

8699
private <T> T request(HttpRequest request, Class<T> responseType) throws HttpException {
87100
try {
88-
var send = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
101+
var send = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
102+
return handleResponse(send, responseType);
103+
} catch (InterruptedException | IOException e) {
104+
throw new BaseMailtrapException("An error has occurred while sending request", e);
105+
}
106+
}
107+
108+
private <T> T request(HttpRequest request, JavaType responseType) throws HttpException {
109+
try {
110+
var send = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
89111
return handleResponse(send, responseType);
90112
} catch (InterruptedException | IOException e) {
91113
throw new BaseMailtrapException("An error has occurred while sending request", e);
92114
}
93115
}
94116

95-
private <T> T handleResponse(HttpResponse<InputStream> response, Class<T> responseType) {
96-
try (InputStream body = response.body()) {
97-
if (body == null) {
117+
private <T> T handleResponse(HttpResponse<String> response, Class<T> responseType) throws HttpException {
118+
return handleResponseInternal(response, responseType, null);
119+
}
120+
121+
private <T> T handleResponse(HttpResponse<String> response, JavaType responseType) throws HttpException {
122+
return handleResponseInternal(response, null, responseType);
123+
}
124+
125+
private <T> T handleResponseInternal(HttpResponse<String> response, Class<T> responseClassType, JavaType responseJavaType) throws HttpException {
126+
try {
127+
if (response.body() == null) {
98128
throw new BaseMailtrapException("Response body is null");
99129
}
100130

101131
int statusCode = response.statusCode();
102132
if (statusCode == 200) {
103-
return Mapper.get().readValue(body, responseType);
133+
if (responseClassType != null) {
134+
return Mapper.get().readValue(response.body(), responseClassType);
135+
} else if (responseJavaType != null) {
136+
return Mapper.get().readValue(response.body(), responseJavaType);
137+
} else {
138+
throw new IllegalArgumentException("Both responseType and typeReference are null");
139+
}
104140
} else if (statusCode >= 400 && statusCode < 500) {
105-
ErrorResponse errorResponse = Mapper.get().readValue(body, ErrorResponse.class);
141+
ErrorResponse errorResponse = Mapper.get().readValue(response.body(), ErrorResponse.class);
106142
throw new HttpClientException(String.join(", ", errorResponse.getErrors()), statusCode);
107143
} else if (statusCode > 500) {
108144
throw new HttpServerException(String.format("Internal Server Error. HTTP response code (%d) received from the API server. Retry later or contact support.", statusCode), statusCode);
@@ -113,7 +149,7 @@ private <T> T handleResponse(HttpResponse<InputStream> response, Class<T> respon
113149
}
114150
}
115151

116-
private HttpRequest.Builder prebuildRequest(String url, RequestData requestData) {
152+
private HttpRequest.Builder prepareRequest(String url, RequestData requestData) {
117153

118154
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
119155
.uri(URI.create(this.appendUrlParams(url, requestData.getQueryParams())))
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.mailtrap.model.response;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.Data;
5+
6+
@Data
7+
public class AttachmentResponse {
8+
private long id;
9+
10+
@JsonProperty("message_id")
11+
private long messageId;
12+
13+
private String filename;
14+
15+
@JsonProperty("attachment_type")
16+
private String attachmentType;
17+
18+
@JsonProperty("content_type")
19+
private String contentType;
20+
21+
@JsonProperty("content_id")
22+
private String contentId;
23+
24+
@JsonProperty("transfer_encoding")
25+
private String transferEncoding;
26+
27+
@JsonProperty("attachment_size")
28+
private int attachmentSize;
29+
30+
@JsonProperty("created_at")
31+
private String createdAt;
32+
33+
@JsonProperty("updated_at")
34+
private String updatedAt;
35+
36+
@JsonProperty("attachment_human_size")
37+
private String attachmentHumanSize;
38+
39+
@JsonProperty("download_path")
40+
private String downloadPath;
41+
42+
}

0 commit comments

Comments
 (0)