diff --git a/src/main/java/com/sendgrid/ApiKeySendGrid.java b/src/main/java/com/sendgrid/ApiKeySendGrid.java index 76b6f39b..685ed2a8 100644 --- a/src/main/java/com/sendgrid/ApiKeySendGrid.java +++ b/src/main/java/com/sendgrid/ApiKeySendGrid.java @@ -1,5 +1,6 @@ package com.sendgrid; +import com.sendgrid.constant.EnumConstants; import com.sendgrid.constant.ErrorMessages; import com.sendgrid.exception.AuthenticationException; import com.sendgrid.http.ApiKeyRestClient; @@ -39,6 +40,9 @@ public static synchronized void setRegion(final String region) { if (region == null || region.isEmpty()) { throw new AuthenticationException(String.format(ErrorMessages.EMPTY_STRING, "REGION")); } + if (!EnumConstants.Region.getValues().contains(region)) { + throw new AuthenticationException(String.format(ErrorMessages.INVALID_STRING, "REGION")); + } if (!Objects.equals(region, ApiKeySendGrid.region)) { ApiKeySendGrid.invalidate(); } @@ -77,8 +81,10 @@ private static ApiKeyRestClient buildRestClient() { if (userAgentExtensions != null) { builder.userAgentExtensions(ApiKeySendGrid.userAgentExtensions); } - // TODO: Check if it mandatory to fetch region from customer. - builder.region(region); + if (region == null) + builder.region(EnumConstants.Region.GLOBAL.getValue()); + else + builder.region(region); return builder.build(); } diff --git a/src/main/java/com/sendgrid/constant/Domains.java b/src/main/java/com/sendgrid/constant/Domains.java new file mode 100644 index 00000000..70cb16c6 --- /dev/null +++ b/src/main/java/com/sendgrid/constant/Domains.java @@ -0,0 +1,14 @@ +package com.sendgrid.constant; + +public enum Domains { + API("api"); + private final String value; + + private Domains(final String value) { + this.value = value; + } + + public String toString() { + return value; + } +} diff --git a/src/main/java/com/sendgrid/constant/EnumConstants.java b/src/main/java/com/sendgrid/constant/EnumConstants.java index 54c80698..60f5f1a8 100644 --- a/src/main/java/com/sendgrid/constant/EnumConstants.java +++ b/src/main/java/com/sendgrid/constant/EnumConstants.java @@ -3,6 +3,10 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + public class EnumConstants { @Getter @RequiredArgsConstructor @@ -12,4 +16,19 @@ public enum ContentType { private final String value; } + + @Getter + @RequiredArgsConstructor + public enum Region { + GLOBAL("global"), + EU("eu"); + + private final String value; + + public static List getValues() { + return Arrays.stream(Region.values()) + .map(Region::getValue) + .collect(Collectors.toList()); + } + } } diff --git a/src/main/java/com/sendgrid/constant/ErrorMessages.java b/src/main/java/com/sendgrid/constant/ErrorMessages.java index 2000a750..9cbdd172 100644 --- a/src/main/java/com/sendgrid/constant/ErrorMessages.java +++ b/src/main/java/com/sendgrid/constant/ErrorMessages.java @@ -6,5 +6,7 @@ @UtilityClass public class ErrorMessages { public static final String EMPTY_STRING = "'%s' can not be null or empty"; + + public static final String INVALID_STRING = "'%s' is invalid"; public static final String DEFAULT_REST_CLIENT = "Sending API request using default '%s' RestClient"; } diff --git a/src/main/java/com/sendgrid/exception/ApiErrorResponse.java b/src/main/java/com/sendgrid/exception/ApiErrorResponse.java index 6e887b32..8e07163a 100644 --- a/src/main/java/com/sendgrid/exception/ApiErrorResponse.java +++ b/src/main/java/com/sendgrid/exception/ApiErrorResponse.java @@ -1,10 +1,9 @@ package com.sendgrid.exception; -import com.sun.net.httpserver.Headers; + import lombok.Getter; import org.apache.http.Header; -import java.util.Map; public class ApiErrorResponse extends RuntimeException { @Getter @@ -17,7 +16,7 @@ public class ApiErrorResponse extends RuntimeException { private Header[] headers; public ApiErrorResponse(Integer statusCode, String statusMessage, Object error, Header[] headers) { - super(statusMessage); + super("An error has occurred"); this.statusCode = statusCode; this.statusMessage = statusMessage; this.error = error; diff --git a/src/main/java/com/sendgrid/exception/GenericApiError.java b/src/main/java/com/sendgrid/exception/GenericApiError.java new file mode 100644 index 00000000..d4fafd38 --- /dev/null +++ b/src/main/java/com/sendgrid/exception/GenericApiError.java @@ -0,0 +1,65 @@ +package com.sendgrid.exception; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.util.List; +import java.util.StringJoiner; + +@ToString +public class GenericApiError { + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonProperty("errors") + @Getter + @Setter + private List errors; + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonProperty("id") + @Getter + @Setter + private String id; + + public GenericApiError() { + } + + private GenericApiError(GenericApiError.Builder builder) { + this.errors = builder.errors; + this.id = builder.id; + } + + // Builder class for constructing object + public static class Builder { + private List errors; + private String id; + + public Builder() { + } + + public GenericApiError.Builder errors(List errors) { + this.errors = errors; + return this; + } + + public GenericApiError.Builder id(String id) { + this.id = id; + return this; + } + + public GenericApiError build() { + return new GenericApiError(this); + } + + } + + @Override + public String toString() { + StringJoiner joiner = new StringJoiner(", ", GenericApiError.class.getSimpleName() + "(", ")"); + if (errors != null) joiner.add("errors=" + errors); + if (id != null) joiner.add("id=" + id); + return joiner.toString(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/sendgrid/exception/GenericError.java b/src/main/java/com/sendgrid/exception/GenericError.java new file mode 100644 index 00000000..bc2f3a9d --- /dev/null +++ b/src/main/java/com/sendgrid/exception/GenericError.java @@ -0,0 +1,78 @@ +package com.sendgrid.exception; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.util.StringJoiner; + +@ToString +public class GenericError { + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonProperty("message") + @Getter + @Setter + private String message; + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonProperty("field") + @Getter + @Setter + private String field; + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonProperty("help") + @Getter + @Setter + private Object help; + + public GenericError() { + } + + private GenericError(GenericError.Builder builder) { + this.message = builder.message; + this.field = builder.field; + this.help = builder.help; + } + + // Builder class for constructing object + public static class Builder { + private String message; + private String field; + private Object help; + + public Builder() { + } + + public GenericError.Builder message(String message) { + this.message = message; + return this; + } + + public GenericError.Builder field(String field) { + this.field = field; + return this; + } + + public GenericError.Builder help(Object help) { + this.help = help; + return this; + } + + public GenericError build() { + return new GenericError(this); + } + + } + + @Override + public String toString() { + StringJoiner joiner = new StringJoiner(", ", GenericError.class.getSimpleName() + "(", ")"); + if (message != null) joiner.add("message=" + message); + if (field != null) joiner.add("field=" + field); + if (help != null) joiner.add("help=" + help); + return joiner.toString(); + } + +} + \ No newline at end of file diff --git a/src/main/java/com/sendgrid/http/ApiKeyRestClient.java b/src/main/java/com/sendgrid/http/ApiKeyRestClient.java index 388ef11c..e5f90543 100644 --- a/src/main/java/com/sendgrid/http/ApiKeyRestClient.java +++ b/src/main/java/com/sendgrid/http/ApiKeyRestClient.java @@ -14,6 +14,7 @@ public class ApiKeyRestClient { private final String apiKey; + @Getter private final String region; @Getter private final ObjectMapper objectMapper; @@ -42,7 +43,18 @@ public Response request(final Request request) { } logRequest(request); - return null; + Response response = httpClient.makeRequest(request); + + if (logger.isDebugEnabled()) { + logger.debug("status code: {}", response.getStatusCode()); + org.apache.http.Header[] responseHeaders = response.getHeaders(); + logger.debug("response headers:"); + for (int i = 0; i < responseHeaders.length; i++) { + logger.debug("responseHeader: {}", responseHeaders[i]); + } + } + + return response; } diff --git a/src/main/java/com/sendgrid/http/Request.java b/src/main/java/com/sendgrid/http/Request.java index 780a7aa2..681d4682 100644 --- a/src/main/java/com/sendgrid/http/Request.java +++ b/src/main/java/com/sendgrid/http/Request.java @@ -16,6 +16,8 @@ public class Request { @Getter private final String url; @Getter + private final String domain; + @Getter private String body; @Getter private Map headers; @@ -33,26 +35,31 @@ public void addHeader(String key, String value) { private Request(Builder builder) { this.method = builder.method; + this.domain = builder.domain; + this.region = builder.region; this.body = builder.body; this.headers = builder.headers; - String baseUrl = builder.url; + String baseUrl = Utility.buildBaseUrl(domain, region, builder.endPoint); baseUrl = Utility.buildWithPathParams(baseUrl, builder.pathParams); baseUrl = Utility.buildWithQueryParams(baseUrl, builder.queryParams); this.url = baseUrl; } public static class Builder { - private String url; + private String endPoint; private HttpMethod method; + private String domain; + private String region; private Map headers = new HashMap<>(); - private Map> queryParams = new HashMap<>(); + private Map queryParams = new HashMap<>(); private Map pathParams = new HashMap<>(); private String body; - public Builder(HttpMethod method, String url) { + public Builder(HttpMethod method, String endPoint, String domain) { this.method = method; - this.url = url; + this.endPoint = endPoint; + this.domain = domain; } public Builder addPathParam(String key, String value) { @@ -70,13 +77,10 @@ public Builder addHeaderParam(String key, String value) { * limit: If limit occurs as query param in open api spec * offset: If offset occurs as query param in open api spec * query: If there is query parameter apart from limit and offset. - * It will be the responsibility of the client to build a compound query, encode it and pass it as a query parameter in the query field. + * It will be the responsibility of the client to build a compound query and pass it as a query parameter in the query field. */ public Builder addQueryParam(String key, String value) { - if (!queryParams.containsKey(key)) { - queryParams.put(key, new ArrayList()); - } - queryParams.get(key).add(value); + queryParams.put(key, value); return this; } @@ -84,9 +88,16 @@ public Builder addBody(String body) { this.body = body; return this; } + + public Builder region(String region) { + this.region = region; + return this; + } public Request build() { return new Request(this); } } + + } diff --git a/src/main/java/com/sendgrid/util/Utility.java b/src/main/java/com/sendgrid/util/Utility.java index 61ec6b50..cf1587c8 100644 --- a/src/main/java/com/sendgrid/util/Utility.java +++ b/src/main/java/com/sendgrid/util/Utility.java @@ -1,20 +1,26 @@ package com.sendgrid.util; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.sendgrid.exception.ApiConnectionException; -import com.sendgrid.exception.ApiException; +import com.sendgrid.constant.EnumConstants; +import org.apache.http.client.utils.URIBuilder; -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.StringJoiner; public class Utility { - public static String buildWithPathParams(String path, Map params) { + + public static String buildBaseUrl(final String domain, final String region, final String endPoint) { + List availableRegions = EnumConstants.Region.getValues(); + StringBuilder defaultDomain = new StringBuilder(domain); + if (availableRegions.contains(region)) { + if (!EnumConstants.Region.GLOBAL.getValue().equals(region)) { + defaultDomain.append("."); + defaultDomain.append(region); + } + } + return "https://" + defaultDomain.toString() + ".sendgrid.com" + endPoint; + } + public static String buildWithPathParams(String path, final Map params) { for (Map.Entry entry : params.entrySet()) { String placeholder = "\\{" + entry.getKey() + "\\}"; path = path.replaceAll(placeholder, entry.getValue()); @@ -22,17 +28,24 @@ public static String buildWithPathParams(String path, Map params return path; } - public static String buildWithQueryParams(String path, Map> queryParams) { - if (queryParams.isEmpty()) { - return path; + public static String buildWithQueryParams(String path, final Map queryParams) { + URIBuilder builder = new URIBuilder(); + if (queryParams != null) { + String multiValueDelimiter = "&"; + for (Map.Entry entry : queryParams.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + + if (value.contains(multiValueDelimiter)) { + List values = Arrays.asList(value.split(multiValueDelimiter)); + for (String val : values) { + builder.addParameter(key, val); + } + } else { + builder.setParameter(key, value); + } + } } - StringJoiner joiner = new StringJoiner("&"); - queryParams.forEach((key, values) -> { - values.forEach(value -> { - joiner.add(key + "=" + value); // In case all query parameter needs to be URL Encoded, encode value here. - }); - }); - path = path + "?" + joiner.toString(); - return path; + return path + builder.toString(); } }