From 6781a173975e0de69b3f86d5f404d76b67a3365c Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <jean-pierre.portier@mailgun.com> Date: Tue, 26 Nov 2024 18:03:17 +0100 Subject: [PATCH 1/8] feat (Java): Support 'AdditionalProperty' interface for form parameters serialization --- .../adapters/DeliveryTimeFormSerializer.java | 5 +- .../sdk/core/databind/FormSerializer.java | 4 +- .../core/databind/multipart/ObjectMapper.java | 67 +++++++--- .../sdk/core/models/AdditionalProperties.java | 27 +++++ .../utils/databind/RFC822FormSerializer.java | 5 +- .../databind/multipart/ObjectMapperTest.java | 114 ++++++++++++++++-- .../v1/emails/request/SendEmailRequest.java | 13 +- .../emails/request/SendMimeEmailRequest.java | 13 +- .../internal/VerificationStartSmsOptions.java | 13 +- 9 files changed, 200 insertions(+), 61 deletions(-) create mode 100644 core/src/main/com/sinch/sdk/core/models/AdditionalProperties.java diff --git a/client/src/main/com/sinch/sdk/domains/mailgun/api/v1/adapters/DeliveryTimeFormSerializer.java b/client/src/main/com/sinch/sdk/domains/mailgun/api/v1/adapters/DeliveryTimeFormSerializer.java index c68d4d28..65287349 100644 --- a/client/src/main/com/sinch/sdk/domains/mailgun/api/v1/adapters/DeliveryTimeFormSerializer.java +++ b/client/src/main/com/sinch/sdk/domains/mailgun/api/v1/adapters/DeliveryTimeFormSerializer.java @@ -1,11 +1,12 @@ package com.sinch.sdk.domains.mailgun.api.v1.adapters; import com.sinch.sdk.core.databind.FormSerializer; +import java.util.Map; public class DeliveryTimeFormSerializer extends FormSerializer<Integer> { @Override - public String serialize(Integer in) { - return String.format("%dh", in); + public void serialize(Integer in, String fieldName, Map<String, Object> out) { + out.put(fieldName, String.format("%dh", in)); } } diff --git a/core/src/main/com/sinch/sdk/core/databind/FormSerializer.java b/core/src/main/com/sinch/sdk/core/databind/FormSerializer.java index 397eba1d..a61ad007 100644 --- a/core/src/main/com/sinch/sdk/core/databind/FormSerializer.java +++ b/core/src/main/com/sinch/sdk/core/databind/FormSerializer.java @@ -1,6 +1,8 @@ package com.sinch.sdk.core.databind; +import java.util.Map; + public abstract class FormSerializer<T> { - public abstract Object serialize(T in); + public abstract void serialize(T in, String fieldName, Map<String, Object> out); } diff --git a/core/src/main/com/sinch/sdk/core/databind/multipart/ObjectMapper.java b/core/src/main/com/sinch/sdk/core/databind/multipart/ObjectMapper.java index b199fbd9..048927f6 100644 --- a/core/src/main/com/sinch/sdk/core/databind/multipart/ObjectMapper.java +++ b/core/src/main/com/sinch/sdk/core/databind/multipart/ObjectMapper.java @@ -5,6 +5,7 @@ import com.sinch.sdk.core.databind.annotation.PropertiesOrder; import com.sinch.sdk.core.databind.annotation.Property; import com.sinch.sdk.core.exceptions.SerializationException; +import com.sinch.sdk.core.models.AdditionalProperties; import com.sinch.sdk.core.models.OptionalValue; import com.sinch.sdk.core.utils.EnumDynamic; import com.sinch.sdk.core.utils.Pair; @@ -16,6 +17,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -23,6 +25,8 @@ public class ObjectMapper { + static final String ADDITIONAL_PROPERTIES_IDENTIFIER = "additionalProperties"; + public Map<String, Object> serialize(Object value) throws IntrospectionException, InvocationTargetException, @@ -33,7 +37,13 @@ public Map<String, Object> serialize(Object value) BeanInfo beanInfo = Introspector.getBeanInfo(value.getClass(), Object.class); List<Pair<String, Method>> serializableProperties = collectSerializableProperties(beanInfo); - return serializeProperties(serializableProperties, value); + Map<String, Object> output = serializeProperties(serializableProperties, value); + if (value instanceof AdditionalProperties) { + serializeAdditionalProperties( + getAdditionalPropertiesGetter(beanInfo).getMethod(), value, output); + } + + return output; } private List<Pair<String, Method>> collectSerializableProperties(BeanInfo beanInfo) { @@ -77,49 +87,78 @@ private Optional<Pair<String, Method>> getPropertyGetter(Method method) { return Optional.of(new Pair<>(property.value(), method)); } + private MethodDescriptor getAdditionalPropertiesGetter(BeanInfo beanInfo) { + + return Arrays.stream(beanInfo.getMethodDescriptors()) + .filter(f -> f.getMethod().getName().equals(ADDITIONAL_PROPERTIES_IDENTIFIER)) + .findFirst() + .orElseThrow( + () -> + new SerializationException( + String.format("Missing '%s' getter", ADDITIONAL_PROPERTIES_IDENTIFIER))); + } + private Map<String, Object> serializeProperties( List<Pair<String, Method>> serializableProperties, Object object) throws InvocationTargetException, IllegalAccessException { - Map<String, Object> properties = new LinkedHashMap<>(); + Map<String, Object> out = new LinkedHashMap<>(); for (Pair<String, Method> property : serializableProperties) { + serializeProperty(object, property.getRight(), property.getLeft(), out); + } + return out; + } - serializeProperty(object, property.getRight()) - .ifPresent(v -> properties.put(property.getLeft(), v)); + private void serializeAdditionalProperties( + Method method, Object object, Map<String, Object> output) + throws InvocationTargetException, IllegalAccessException { + + @SuppressWarnings("unchecked") + Map<String, Object> propertyValue = (Map<String, Object>) method.invoke(object); + if (null == propertyValue) { + return; } - return properties; + + FormSerialize formSerialize = method.getDeclaredAnnotation(FormSerialize.class); + if (null != formSerialize) { + handleOverriddenSerialization(formSerialize, propertyValue, "", output); + return; + } + output.putAll(propertyValue); } - private OptionalValue<?> serializeProperty(Object object, Method method) + private void serializeProperty( + Object object, Method method, String fieldName, Map<String, Object> out) throws InvocationTargetException, IllegalAccessException { OptionalValue<?> propertyValue = (OptionalValue<?>) method.invoke(object); - if (!propertyValue.isPresent() || null == propertyValue.get()) { - return propertyValue; + return; } Object value = propertyValue.get(); FormSerialize formSerialize = method.getDeclaredAnnotation(FormSerialize.class); if (null != formSerialize) { - value = handleOverriddenSerialization(formSerialize, value); + handleOverriddenSerialization(formSerialize, value, fieldName, out); + return; } if (value instanceof EnumDynamic) { EnumDynamic<?, ?> enumDynamic = (EnumDynamic<?, ?>) value; - return OptionalValue.of((enumDynamic.value().toString())); + out.put(fieldName, enumDynamic.value().toString()); + return; } - return OptionalValue.of(value); + out.put(fieldName, value); } - private Object handleOverriddenSerialization(FormSerialize formSerialize, Object value) { + private void handleOverriddenSerialization( + FormSerialize formSerialize, Object in, String fieldName, Map<String, Object> out) { try { Class<?> clazz = Class.forName(formSerialize.using().getName()); @SuppressWarnings("unchecked") Constructor<FormSerializer<Object>> ctor = (Constructor<FormSerializer<Object>>) clazz.getConstructor(); - FormSerializer<Object> serializer = ctor.newInstance(); - return serializer.serialize(value); + ctor.newInstance().serialize(in, fieldName, out); } catch (Exception e) { throw new SerializationException(e); } diff --git a/core/src/main/com/sinch/sdk/core/models/AdditionalProperties.java b/core/src/main/com/sinch/sdk/core/models/AdditionalProperties.java new file mode 100644 index 00000000..d708d11b --- /dev/null +++ b/core/src/main/com/sinch/sdk/core/models/AdditionalProperties.java @@ -0,0 +1,27 @@ +package com.sinch.sdk.core.models; + +import com.sinch.sdk.domains.mailgun.models.v1.emails.request.SendEmailRequest.Builder; + +/** Interface definition for schemas supporting additional properties */ +public interface AdditionalProperties { + + /** + * Return the additional property with the specified name. + * + * @param key the name of the property + * @return the additional property with the specified name + * @since __TO_BE_DEFINED__ + */ + Object get(String key); + + interface Builder { + + /** + * see getter + * + * @return Current builder + * @see #get + */ + Builder put(String key, Object value); + } +} diff --git a/core/src/main/com/sinch/sdk/core/utils/databind/RFC822FormSerializer.java b/core/src/main/com/sinch/sdk/core/utils/databind/RFC822FormSerializer.java index d6ba5385..4d22a8fc 100644 --- a/core/src/main/com/sinch/sdk/core/utils/databind/RFC822FormSerializer.java +++ b/core/src/main/com/sinch/sdk/core/utils/databind/RFC822FormSerializer.java @@ -4,11 +4,12 @@ import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; +import java.util.Map; public class RFC822FormSerializer extends FormSerializer<Instant> { @Override - public String serialize(Instant in) { - return DateTimeFormatter.RFC_1123_DATE_TIME.format(in.atZone(ZoneId.of("UTC"))); + public void serialize(Instant in, String fieldName, Map<String, Object> out) { + out.put(fieldName, DateTimeFormatter.RFC_1123_DATE_TIME.format(in.atZone(ZoneId.of("UTC")))); } } diff --git a/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java b/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java index 27c80882..091bdb66 100644 --- a/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java +++ b/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java @@ -6,6 +6,7 @@ import com.sinch.sdk.core.databind.annotation.PropertiesOrder; import com.sinch.sdk.core.databind.annotation.Property; import com.sinch.sdk.core.databind.multipart.ObjectMapperTest.SerializableObject.AnEnum; +import com.sinch.sdk.core.models.AdditionalProperties; import com.sinch.sdk.core.models.OptionalValue; import com.sinch.sdk.core.utils.EnumDynamic; import com.sinch.sdk.core.utils.EnumSupportDynamic; @@ -18,7 +19,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; @@ -29,7 +32,7 @@ class ObjectMapperTest { static File fileAttachment1; static File fileAttachment2; - static Map<String, Object> serialized; + static Map<String, Object> serialized, defaultAdditionalPropertiesSerialized; static { ClassLoader classLoader = SendEmailRequestTest.class.getClassLoader(); @@ -40,6 +43,9 @@ class ObjectMapperTest { new File( classLoader.getResource("domains/mailgun/v1/emails/request/attachment2.txt").getFile()); + Map<String, Object> additionalProperties = + fillMap("header:toto", "toto-value", "variable:foo", "foo-value", "raw", "raw-value"); + SerializableObject object = new SerializableObject( OptionalValue.of("text value"), @@ -51,10 +57,16 @@ class ObjectMapperTest { OptionalValue.of(Arrays.asList(fileAttachment1, fileAttachment2)), OptionalValue.of( Arrays.asList( - Instant.parse("2024-11-25T10:06:54Z"), Instant.parse("2024-11-25T09:06:54Z")))); + Instant.parse("2024-11-25T10:06:54Z"), Instant.parse("2024-11-25T09:06:54Z"))), + additionalProperties); + + DefaultAdditionalPropertiesSerializableObject defaultAdditionalPropertiesSerializableObject = + new DefaultAdditionalPropertiesSerializableObject(additionalProperties); try { serialized = new ObjectMapper().serialize(object); + defaultAdditionalPropertiesSerialized = + new ObjectMapper().serialize(defaultAdditionalPropertiesSerializableObject); } catch (Exception e) { throw new RuntimeException(e); } @@ -62,7 +74,7 @@ class ObjectMapperTest { @Test void countValue() { - Assertions.assertEquals(8, serialized.size()); + Assertions.assertEquals(11, serialized.size()); } @Test @@ -76,7 +88,10 @@ void order() { SerializableObject.PROPERTY_FILE, SerializableObject.PROPERTY_ENUM, SerializableObject.PROPERTY_ENUM_COLLECTION, - SerializableObject.PROPERTY_TEXT_COLLECTION + SerializableObject.PROPERTY_TEXT_COLLECTION, + "h:toto", + "v:foo", + "raw" }; String[] keys = serialized.keySet().toArray(new String[0]); TestHelpers.recursiveEquals(expectedOrder, keys); @@ -128,6 +143,20 @@ void rfc822CollectionValue() { serialized.get("aRfc822Collection")); } + @Test + void additionalPropertiesValues() { + Assertions.assertEquals("toto-value", serialized.get("h:toto")); + Assertions.assertEquals("foo-value", serialized.get("v:foo")); + Assertions.assertEquals("raw-value", serialized.get("raw")); + } + + @Test + void defaultAdditionalPropertiesValues() { + TestHelpers.recursiveEquals( + fillMap("header:toto", "toto-value", "variable:foo", "foo-value", "raw", "raw-value"), + defaultAdditionalPropertiesSerialized); + } + @PropertiesOrder({ SerializableObject.PROPERTY_RFC822_COLLECTION, SerializableObject.PROPERTY_TEXT, @@ -139,7 +168,7 @@ void rfc822CollectionValue() { SerializableObject.PROPERTY_RFC822_COLLECTION, SerializableObject.PROPERTY_TEXT_COLLECTION }) - static class SerializableObject { + static class SerializableObject implements AdditionalProperties { public static final String PROPERTY_TEXT = "aText"; public static final String PROPERTY_ENUM = "anEnum"; public static final String PROPERTY_FILE = "aFile"; @@ -159,6 +188,7 @@ static class SerializableObject { private final OptionalValue<Collection<AnEnum>> enumCollection; private final OptionalValue<Collection<File>> fileCollection; private final OptionalValue<Collection<Instant>> instantCollection; + private final Map<String, Object> additionalProperties; public SerializableObject( OptionalValue<String> text, @@ -168,7 +198,8 @@ public SerializableObject( OptionalValue<Collection<String>> textCollection, OptionalValue<Collection<AnEnum>> enumCollection, OptionalValue<Collection<File>> fileCollection, - OptionalValue<Collection<Instant>> instantCollection) { + OptionalValue<Collection<Instant>> instantCollection, + Map<String, Object> additionalProperties) { this.text = text; this._enum = _enum; this.file = file; @@ -177,6 +208,7 @@ public SerializableObject( this.textCollection = textCollection; this.enumCollection = enumCollection; this.fileCollection = fileCollection; + this.additionalProperties = additionalProperties; } @Property(PROPERTY_TEXT) @@ -221,6 +253,19 @@ public OptionalValue<Collection<Instant>> instantCollection() { return instantCollection; } + @FormSerialize(using = AdditionalPropertiesFormSerializer.class) + public Map<String, Object> additionalProperties() { + return additionalProperties; + } + + public Object get(String key) { + return null; + } + + public Set<String> keys() { + return additionalProperties.keySet(); + } + public static class AnEnum extends EnumDynamic<String, AnEnum> { public static final AnEnum YES = new AnEnum("yes"); public static final AnEnum NO = new AnEnum("no"); @@ -246,15 +291,60 @@ public static String valueOf(AnEnum e) { } } + static class DefaultAdditionalPropertiesSerializableObject implements AdditionalProperties { + private final Map<String, Object> additionalProperties; + + public DefaultAdditionalPropertiesSerializableObject(Map<String, Object> additionalProperties) { + this.additionalProperties = additionalProperties; + } + + public Map<String, Object> additionalProperties() { + return additionalProperties; + } + + public Object get(String key) { + return null; + } + + public Set<String> keys() { + return additionalProperties.keySet(); + } + } + public static class RFC822ListFormSerializer extends FormSerializer<Collection<Instant>> { @Override - public Collection<String> serialize(Collection<Instant> in) { - return in.stream() - .map( - instant -> - DateTimeFormatter.RFC_1123_DATE_TIME.format(instant.atZone(ZoneId.of("UTC")))) - .collect(Collectors.toList()); + public void serialize(Collection<Instant> in, String fieldName, Map<String, Object> out) { + out.put( + fieldName, + in.stream() + .map( + instant -> + DateTimeFormatter.RFC_1123_DATE_TIME.format(instant.atZone(ZoneId.of("UTC")))) + .collect(Collectors.toList())); + } + } + + public static class AdditionalPropertiesFormSerializer + extends FormSerializer<Map<Object, Object>> { + + @Override + public void serialize(Map<Object, Object> in, String fieldName, Map<String, Object> out) { + + in.forEach( + (_key, value) -> { + String key = + ((String) _key).replaceAll("^header:", "h:").replaceAll("^variable:", "v:"); + out.put(key, value); + }); + } + } + + private static Map<String, Object> fillMap(Object... pairs) { + LinkedHashMap<String, Object> map = new LinkedHashMap<>(); + for (int i = 0; i < pairs.length; ) { + map.put((String) pairs[i++], pairs[i++]); } + return map; } } diff --git a/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendEmailRequest.java b/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendEmailRequest.java index bf845820..45afb119 100644 --- a/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendEmailRequest.java +++ b/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendEmailRequest.java @@ -11,6 +11,7 @@ package com.sinch.sdk.domains.mailgun.models.v1.emails.request; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.sinch.sdk.core.models.AdditionalProperties; import com.sinch.sdk.core.utils.EnumDynamic; import com.sinch.sdk.core.utils.EnumSupportDynamic; import java.io.File; @@ -21,7 +22,7 @@ /** SendEmailRequest */ @JsonDeserialize(builder = SendEmailRequestImpl.Builder.class) -public interface SendEmailRequest { +public interface SendEmailRequest extends AdditionalProperties { /** * Email address for <code>From</code> header @@ -540,14 +541,6 @@ public static String valueOf(SkipVerificationEnum e) { */ String getRecipientVariables(); - /** - * Return the additional property with the specified name. - * - * @param key the name of the property - * @return the additional property with the specified name - */ - Object get(String key); - /** * Getting builder * @@ -558,7 +551,7 @@ static Builder builder() { } /** Dedicated Builder */ - interface Builder { + interface Builder extends AdditionalProperties.Builder { /** * see getter diff --git a/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequest.java b/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequest.java index cadb8ac4..6b58b9ed 100644 --- a/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequest.java +++ b/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequest.java @@ -11,6 +11,7 @@ package com.sinch.sdk.domains.mailgun.models.v1.emails.request; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.sinch.sdk.core.models.AdditionalProperties; import com.sinch.sdk.core.utils.EnumDynamic; import com.sinch.sdk.core.utils.EnumSupportDynamic; import java.io.File; @@ -21,7 +22,7 @@ /** SendMimeEmailRequest */ @JsonDeserialize(builder = SendMimeEmailRequestImpl.Builder.class) -public interface SendMimeEmailRequest { +public interface SendMimeEmailRequest extends AdditionalProperties { /** * Email address of the recipient(s). Example: <code>\"Bob <bob@host.com>\"</code> @@ -478,14 +479,6 @@ public static String valueOf(SkipVerificationEnum e) { */ String getRecipientVariables(); - /** - * Return the additional property with the specified name. - * - * @param key the name of the property - * @return the additional property with the specified name - */ - Object get(String key); - /** * Getting builder * @@ -496,7 +489,7 @@ static Builder builder() { } /** Dedicated Builder */ - interface Builder { + interface Builder extends AdditionalProperties.Builder { /** * see getter diff --git a/openapi-contracts/src/main/com/sinch/sdk/domains/verification/models/v1/start/request/internal/VerificationStartSmsOptions.java b/openapi-contracts/src/main/com/sinch/sdk/domains/verification/models/v1/start/request/internal/VerificationStartSmsOptions.java index b9ae5e31..f5b8eb1b 100644 --- a/openapi-contracts/src/main/com/sinch/sdk/domains/verification/models/v1/start/request/internal/VerificationStartSmsOptions.java +++ b/openapi-contracts/src/main/com/sinch/sdk/domains/verification/models/v1/start/request/internal/VerificationStartSmsOptions.java @@ -11,6 +11,7 @@ package com.sinch.sdk.domains.verification.models.v1.start.request.internal; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.sinch.sdk.core.models.AdditionalProperties; import com.sinch.sdk.core.utils.EnumDynamic; import com.sinch.sdk.core.utils.EnumSupportDynamic; import java.util.Arrays; @@ -21,7 +22,7 @@ * not provided. */ @JsonDeserialize(builder = VerificationStartSmsOptionsImpl.Builder.class) -public interface VerificationStartSmsOptions { +public interface VerificationStartSmsOptions extends AdditionalProperties { /** * The expiration time for a verification process is represented in the format <code>HH:MM:SS @@ -82,14 +83,6 @@ public static String valueOf(CodeTypeEnum e) { */ String getAcceptLanguage(); - /** - * Return the additional property with the specified name. - * - * @param key the name of the property - * @return the additional property with the specified name - */ - Object get(String key); - /** * Getting builder * @@ -100,7 +93,7 @@ static Builder builder() { } /** Dedicated Builder */ - interface Builder { + interface Builder extends AdditionalProperties.Builder { /** * see getter From 3cafb58f59f7c784a887b7a447f4fb3d458e8c00 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <jean-pierre.portier@mailgun.com> Date: Wed, 27 Nov 2024 07:07:05 +0100 Subject: [PATCH 2/8] test (Mailgun): Unit tests --- .../api/v1/adapters/MailgunService.java | 3 +- .../api/v1/adapters/EmailsServiceTest.java | 103 ++++++++++++++++++ .../api/v1/adapters/MailgunServiceTest.java | 62 +++++++++++ .../databind/multipart/ObjectMapperTest.java | 4 +- .../emails/request/SendMimeEmailRequest.java | 18 +-- .../request/SendMimeEmailRequestImpl.java | 28 ++--- .../emails/request/SendEmailRequestTest.java | 14 +-- .../request/SendMimeEmailRequestTest.java | 95 ++++++++++++++++ 8 files changed, 289 insertions(+), 38 deletions(-) create mode 100644 client/src/test/java/com/sinch/sdk/domains/mailgun/api/v1/adapters/EmailsServiceTest.java create mode 100644 client/src/test/java/com/sinch/sdk/domains/mailgun/api/v1/adapters/MailgunServiceTest.java create mode 100644 openapi-contracts/src/test/java/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequestTest.java diff --git a/client/src/main/com/sinch/sdk/domains/mailgun/api/v1/adapters/MailgunService.java b/client/src/main/com/sinch/sdk/domains/mailgun/api/v1/adapters/MailgunService.java index 16e45858..3dc66f4b 100644 --- a/client/src/main/com/sinch/sdk/domains/mailgun/api/v1/adapters/MailgunService.java +++ b/client/src/main/com/sinch/sdk/domains/mailgun/api/v1/adapters/MailgunService.java @@ -34,8 +34,7 @@ public MailgunService( credentials.getApiUser(), "Mailgun service requires 'apiUser' to be defined"); StringUtil.requireNonEmpty( credentials.getApiKey(), "Mailgun service requires 'apiKey' to be defined"); - StringUtil.requireNonEmpty( - context.getUrl(), "'Mailgun service requires mailgunUrl' to be defined"); + StringUtil.requireNonEmpty(context.getUrl(), "'Mailgun service requires 'url' to be defined"); LOGGER.fine("Activate Mailgun API with server='" + context.getServer().getUrl() + "'"); diff --git a/client/src/test/java/com/sinch/sdk/domains/mailgun/api/v1/adapters/EmailsServiceTest.java b/client/src/test/java/com/sinch/sdk/domains/mailgun/api/v1/adapters/EmailsServiceTest.java new file mode 100644 index 00000000..4b8185ef --- /dev/null +++ b/client/src/test/java/com/sinch/sdk/domains/mailgun/api/v1/adapters/EmailsServiceTest.java @@ -0,0 +1,103 @@ +package com.sinch.sdk.domains.mailgun.api.v1.adapters; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.adelean.inject.resources.junit.jupiter.TestWithResources; +import com.sinch.sdk.BaseTest; +import com.sinch.sdk.core.TestHelpers; +import com.sinch.sdk.core.http.AuthManager; +import com.sinch.sdk.core.http.HttpClient; +import com.sinch.sdk.domains.mailgun.api.v1.internal.EmailsApi; +import com.sinch.sdk.domains.mailgun.models.v1.emails.request.SendEmailRequestTest; +import com.sinch.sdk.domains.mailgun.models.v1.emails.request.SendMimeEmailRequestTest; +import com.sinch.sdk.domains.mailgun.models.v1.emails.response.GetStoredEmailResponse; +import com.sinch.sdk.domains.mailgun.models.v1.emails.response.GetStoredEmailResponseTest; +import com.sinch.sdk.domains.mailgun.models.v1.emails.response.SendEmailResponse; +import com.sinch.sdk.domains.mailgun.models.v1.emails.response.SendEmailResponseTest; +import com.sinch.sdk.domains.mailgun.models.v1.emails.response.SendingQueuesStatusResponse; +import com.sinch.sdk.domains.mailgun.models.v1.emails.response.SendingQueuesStatusResponseTest; +import com.sinch.sdk.models.MailgunContext; +import java.util.Map; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; + +@TestWithResources +class EmailsServiceTest extends BaseTest { + + @Mock MailgunContext context; + @Mock EmailsApi api; + @Mock HttpClient httpClient; + @Mock Map<String, AuthManager> authManagers; + @Captor ArgumentCaptor<String> domainNameCaptor; + + EmailsService service; + + String domainName = "fooDomain"; + String storageKey = "fooStorageKey"; + + @BeforeEach + public void initMocks() { + service = spy(new EmailsService(context, httpClient, authManagers)); + doReturn(api).when(service).getApi(); + } + + @Test + void send() { + when(api.sendEmail(eq(domainName), eq(SendEmailRequestTest.sendEmailRequest))) + .thenReturn(SendEmailResponseTest.expectedSendEmailResponse); + + SendEmailResponse response = service.send(domainName, SendEmailRequestTest.sendEmailRequest); + + TestHelpers.recursiveEquals(response, SendEmailResponseTest.expectedSendEmailResponse); + } + + @Test + void sendMime() { + when(api.sendMimeEmail(eq(domainName), eq(SendMimeEmailRequestTest.sendMimEmailRequest))) + .thenReturn(SendEmailResponseTest.expectedSendEmailResponse); + + SendEmailResponse response = + service.sendMime(domainName, SendMimeEmailRequestTest.sendMimEmailRequest); + + TestHelpers.recursiveEquals(response, SendEmailResponseTest.expectedSendEmailResponse); + } + + @Test + void get() { + when(api.getStoredEmail(eq(domainName), eq(storageKey))) + .thenReturn(GetStoredEmailResponseTest.expectedGetEmailResponse); + + GetStoredEmailResponse response = service.get(domainName, storageKey); + + TestHelpers.recursiveEquals(response, GetStoredEmailResponseTest.expectedGetEmailResponse); + } + + @Test + void getSendingQueuesStatus() { + when(api.getSendingQueuesStatus(eq(domainName))) + .thenReturn(SendingQueuesStatusResponseTest.expectedSendingQueuesStatusResponse); + + SendingQueuesStatusResponse response = service.getSendingQueuesStatus(domainName); + + TestHelpers.recursiveEquals( + response, SendingQueuesStatusResponseTest.expectedSendingQueuesStatusResponse); + } + + @Test + void purgeDomainQueues() { + + service.purgeDomainQueues(domainName); + + verify(api).purgeDomainQueues(domainNameCaptor.capture()); + + Assertions.assertThat(domainNameCaptor.getValue()).isEqualTo(domainName); + } +} diff --git a/client/src/test/java/com/sinch/sdk/domains/mailgun/api/v1/adapters/MailgunServiceTest.java b/client/src/test/java/com/sinch/sdk/domains/mailgun/api/v1/adapters/MailgunServiceTest.java new file mode 100644 index 00000000..e44be529 --- /dev/null +++ b/client/src/test/java/com/sinch/sdk/domains/mailgun/api/v1/adapters/MailgunServiceTest.java @@ -0,0 +1,62 @@ +package com.sinch.sdk.domains.mailgun.api.v1.adapters; + +import static org.junit.jupiter.api.Assertions.*; + +import com.sinch.sdk.core.http.HttpClient; +import com.sinch.sdk.models.MailgunContext; +import com.sinch.sdk.models.MailgunCredentials; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +class MailgunServiceTest { + @Mock HttpClient httpClient; + + @Test + void doNotAcceptNullKey() { + MailgunCredentials credentials = MailgunCredentials.builder().setApiKey(null).build(); + MailgunContext context = MailgunContext.builder().build(); + Exception exception = + assertThrows( + IllegalArgumentException.class, + () -> new MailgunService(credentials, context, httpClient)); + assertTrue(exception.getMessage().contains("apiKey")); + } + + @Test + void doNotAcceptNullContext() { + MailgunCredentials credentials = MailgunCredentials.builder().setApiKey("foo").build(); + Exception exception = + assertThrows( + NullPointerException.class, () -> new MailgunService(credentials, null, httpClient)); + assertTrue(exception.getMessage().contains("Mailgun service requires context to be defined")); + } + + @Test + void doNotAcceptEmptyURL() { + MailgunCredentials credentials = MailgunCredentials.builder().setApiKey("foo").build(); + MailgunContext context = MailgunContext.builder().setUrl("").build(); + Exception exception = + assertThrows( + IllegalArgumentException.class, + () -> new MailgunService(credentials, context, httpClient)); + assertTrue(exception.getMessage().contains("Mailgun service requires 'url' to be defined")); + } + + @Test + void doNotAcceptNullURL() { + MailgunCredentials credentials = MailgunCredentials.builder().setApiKey("foo").build(); + MailgunContext context = MailgunContext.builder().build(); + Exception exception = + assertThrows( + IllegalArgumentException.class, + () -> new MailgunService(credentials, context, httpClient)); + assertTrue(exception.getMessage().contains("Mailgun service requires 'url' to be defined")); + } + + @Test + void passInit() { + MailgunCredentials credentials = MailgunCredentials.builder().setApiKey("foo").build(); + MailgunContext context = MailgunContext.builder().setUrl("foo").build(); + assertDoesNotThrow(() -> new MailgunService(credentials, context, httpClient), "Init passed"); + } +} diff --git a/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java b/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java index 091bdb66..da8b9ed5 100644 --- a/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java +++ b/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java @@ -27,7 +27,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -class ObjectMapperTest { +public class ObjectMapperTest { static File fileAttachment1; static File fileAttachment2; @@ -340,7 +340,7 @@ public void serialize(Map<Object, Object> in, String fieldName, Map<String, Obje } } - private static Map<String, Object> fillMap(Object... pairs) { + public static Map<String, Object> fillMap(Object... pairs) { LinkedHashMap<String, Object> map = new LinkedHashMap<>(); for (int i = 0; i < pairs.length; ) { map.put((String) pairs[i++], pairs[i++]); diff --git a/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequest.java b/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequest.java index 6b58b9ed..9ab62142 100644 --- a/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequest.java +++ b/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequest.java @@ -224,9 +224,9 @@ public static String valueOf(TestModeEnum e) { * href="https://documentation.mailgun.com/docs/mailgun/user-manual/sending-messages/#sending-in-test-mode">Sending * in Test Mode</a> * - * @return isTestMode + * @return testMode */ - TestModeEnum getIsTestMode(); + TestModeEnum getTestMode(); /** * Toggles both click and open tracking on a per-message basis, see <a @@ -320,10 +320,10 @@ public static String valueOf(TrackingClicksEnum e) { * Opens</a>. Has higher priority than domain-level setting. */ public class TrackingOpensEnum extends EnumDynamic<String, TrackingOpensEnum> { - public static final TrackingOpensEnum YES = new TrackingOpensEnum("test:yes"); - public static final TrackingOpensEnum NO = new TrackingOpensEnum("test:no"); - public static final TrackingOpensEnum TRUE = new TrackingOpensEnum("test:true"); - public static final TrackingOpensEnum FALSE = new TrackingOpensEnum("test:false"); + public static final TrackingOpensEnum YES = new TrackingOpensEnum("yes"); + public static final TrackingOpensEnum NO = new TrackingOpensEnum("no"); + public static final TrackingOpensEnum TRUE = new TrackingOpensEnum("true"); + public static final TrackingOpensEnum FALSE = new TrackingOpensEnum("false"); private static final EnumSupportDynamic<String, TrackingOpensEnum> ENUM_SUPPORT = new EnumSupportDynamic<>( @@ -611,11 +611,11 @@ interface Builder extends AdditionalProperties.Builder { /** * see getter * - * @param isTestMode see getter + * @param testMode see getter * @return Current builder - * @see #getIsTestMode + * @see #getTestMode */ - Builder setIsTestMode(TestModeEnum isTestMode); + Builder setTestMode(TestModeEnum testMode); /** * see getter diff --git a/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequestImpl.java b/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequestImpl.java index 7270ef18..94ebe4e5 100644 --- a/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequestImpl.java +++ b/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequestImpl.java @@ -94,7 +94,7 @@ public class SendMimeEmailRequestImpl implements SendMimeEmailRequest { public static final String PROPERTY_O_COLON_TESTMODE = "o:testmode"; - private OptionalValue<TestModeEnum> isTestMode; + private OptionalValue<TestModeEnum> testMode; public static final String PROPERTY_O_COLON_TRACKING = "o:tracking"; @@ -155,7 +155,7 @@ protected SendMimeEmailRequestImpl( OptionalValue<Instant> deliveryTime, OptionalValue<Integer> deliveryTimeOptimizePeriod, OptionalValue<String> timezoneLocalize, - OptionalValue<TestModeEnum> isTestMode, + OptionalValue<TestModeEnum> testMode, OptionalValue<TrackingEnum> tracking, OptionalValue<TrackingClicksEnum> trackingClicks, OptionalValue<TrackingOpensEnum> trackingOpens, @@ -179,7 +179,7 @@ protected SendMimeEmailRequestImpl( this.deliveryTime = deliveryTime; this.deliveryTimeOptimizePeriod = deliveryTimeOptimizePeriod; this.timezoneLocalize = timezoneLocalize; - this.isTestMode = isTestMode; + this.testMode = testMode; this.tracking = tracking; this.trackingClicks = trackingClicks; this.trackingOpens = trackingOpens; @@ -312,13 +312,13 @@ public OptionalValue<String> timezoneLocalize() { return timezoneLocalize; } - public TestModeEnum getIsTestMode() { - return isTestMode.orElse(null); + public TestModeEnum getTestMode() { + return testMode.orElse(null); } @Property(PROPERTY_O_COLON_TESTMODE) - public OptionalValue<TestModeEnum> isTestMode() { - return isTestMode; + public OptionalValue<TestModeEnum> testMode() { + return testMode; } public TrackingEnum getTracking() { @@ -465,7 +465,7 @@ public boolean equals(Object o) { this.timezoneLocalize, poSTV3DomainNameMessagesMimeMultipartFormDataRequestBody.timezoneLocalize) && Objects.equals( - this.isTestMode, poSTV3DomainNameMessagesMimeMultipartFormDataRequestBody.isTestMode) + this.testMode, poSTV3DomainNameMessagesMimeMultipartFormDataRequestBody.testMode) && Objects.equals( this.tracking, poSTV3DomainNameMessagesMimeMultipartFormDataRequestBody.tracking) && Objects.equals( @@ -512,7 +512,7 @@ public int hashCode() { deliveryTime, deliveryTimeOptimizePeriod, timezoneLocalize, - isTestMode, + testMode, tracking, trackingClicks, trackingOpens, @@ -550,7 +550,7 @@ public String toString() { .append(toIndentedString(deliveryTimeOptimizePeriod)) .append("\n"); sb.append(" timezoneLocalize: ").append(toIndentedString(timezoneLocalize)).append("\n"); - sb.append(" isTestMode: ").append(toIndentedString(isTestMode)).append("\n"); + sb.append(" testMode: ").append(toIndentedString(testMode)).append("\n"); sb.append(" tracking: ").append(toIndentedString(tracking)).append("\n"); sb.append(" trackingClicks: ").append(toIndentedString(trackingClicks)).append("\n"); sb.append(" trackingOpens: ").append(toIndentedString(trackingOpens)).append("\n"); @@ -593,7 +593,7 @@ static class Builder implements SendMimeEmailRequest.Builder { OptionalValue<Instant> deliveryTime = OptionalValue.empty(); OptionalValue<Integer> deliveryTimeOptimizePeriod = OptionalValue.empty(); OptionalValue<String> timezoneLocalize = OptionalValue.empty(); - OptionalValue<TestModeEnum> isTestMode = OptionalValue.empty(); + OptionalValue<TestModeEnum> testMode = OptionalValue.empty(); OptionalValue<TrackingEnum> tracking = OptionalValue.empty(); OptionalValue<TrackingClicksEnum> trackingClicks = OptionalValue.empty(); OptionalValue<TrackingOpensEnum> trackingOpens = OptionalValue.empty(); @@ -684,8 +684,8 @@ public Builder setTimezoneLocalize(String timezoneLocalize) { } @Property(value = PROPERTY_O_COLON_TESTMODE) - public Builder setIsTestMode(TestModeEnum isTestMode) { - this.isTestMode = OptionalValue.of(isTestMode); + public Builder setTestMode(TestModeEnum testMode) { + this.testMode = OptionalValue.of(testMode); return this; } @@ -766,7 +766,7 @@ public SendMimeEmailRequest build() { deliveryTime, deliveryTimeOptimizePeriod, timezoneLocalize, - isTestMode, + testMode, tracking, trackingClicks, trackingOpens, diff --git a/openapi-contracts/src/test/java/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendEmailRequestTest.java b/openapi-contracts/src/test/java/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendEmailRequestTest.java index f5d52dd2..69e8c267 100644 --- a/openapi-contracts/src/test/java/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendEmailRequestTest.java +++ b/openapi-contracts/src/test/java/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendEmailRequestTest.java @@ -3,6 +3,7 @@ import com.adelean.inject.resources.junit.jupiter.TestWithResources; import com.sinch.sdk.BaseTest; import com.sinch.sdk.core.TestHelpers; +import com.sinch.sdk.core.databind.multipart.ObjectMapperTest; import com.sinch.sdk.core.http.HttpMapper; import com.sinch.sdk.domains.mailgun.models.v1.emails.request.SendEmailRequest.DkimSignatureEnum; import com.sinch.sdk.domains.mailgun.models.v1.emails.request.SendEmailRequest.RequireTlsEnum; @@ -15,14 +16,13 @@ import java.io.File; import java.time.Instant; import java.util.Arrays; -import java.util.LinkedHashMap; import java.util.Map; import org.junit.jupiter.api.Test; @TestWithResources public class SendEmailRequestTest extends BaseTest { - static Map<Object, Object> expected; + static Map<String, Object> expected; static File fileAttachment1; static File fileAttachment2; @@ -36,7 +36,7 @@ public class SendEmailRequestTest extends BaseTest { classLoader.getResource("domains/mailgun/v1/emails/request/attachment2.txt").getFile()); expected = - fillMap( + ObjectMapperTest.fillMap( // spotless:off "from", "User <JavaSdkUser@sinch.com>", "to", Arrays.asList("aRecipient@mailgun-by-sinch.com"), @@ -116,12 +116,4 @@ void serialize() { TestHelpers.recursiveEquals(expected, serialized); } - - private static Map<Object, Object> fillMap(Object... pairs) { - LinkedHashMap<Object, Object> map = new LinkedHashMap<>(); - for (int i = 0; i < pairs.length; ) { - map.put(pairs[i++], pairs[i++]); - } - return map; - } } diff --git a/openapi-contracts/src/test/java/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequestTest.java b/openapi-contracts/src/test/java/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequestTest.java new file mode 100644 index 00000000..c37ca6c7 --- /dev/null +++ b/openapi-contracts/src/test/java/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequestTest.java @@ -0,0 +1,95 @@ +package com.sinch.sdk.domains.mailgun.models.v1.emails.request; + +import com.adelean.inject.resources.junit.jupiter.TestWithResources; +import com.sinch.sdk.BaseTest; +import com.sinch.sdk.core.TestHelpers; +import com.sinch.sdk.core.databind.multipart.ObjectMapperTest; +import com.sinch.sdk.core.http.HttpMapper; +import java.io.File; +import java.time.Instant; +import java.util.Arrays; +import java.util.Map; +import org.junit.jupiter.api.Test; + +@TestWithResources +public class SendMimeEmailRequestTest extends BaseTest { + + static Map<String, Object> expected; + static File fileAttachment1; + static File fileAttachment2; + + static { + ClassLoader classLoader = SendMimeEmailRequestTest.class.getClassLoader(); + fileAttachment1 = + new File( + classLoader.getResource("domains/mailgun/v1/emails/request/attachment1.txt").getFile()); + fileAttachment2 = + new File( + classLoader.getResource("domains/mailgun/v1/emails/request/attachment2.txt").getFile()); + + expected = + ObjectMapperTest.fillMap( + // spotless:off + "to", Arrays.asList("aRecipient@mailgun-by-sinch.com"), + "message", fileAttachment1, + "template","template value", + "t:version","2", + "t:text","yes", + "t:variables","{\"key\": \"value\"}", + "o:tag", Arrays.asList("tag1", "tag2"), + "o:dkim","true", + "o:secondary-dkim","example.com/s1", + "o:secondary-dkim-public","public.example.com/s1", + "o:deliverytime", "Sat, 22 Jan 2000 11:23:45 GMT", + "o:deliverytime-optimize-period", "29h", + "o:time-zone-localize","02:04PM", + "o:testmode", "no", + "o:tracking","htmlonly", + "o:tracking-clicks","true", + "o:tracking-opens","false", + "o:require-tls","true", + "o:skip-verification","no", + "o:sending-ip","192.168.0.10", + "o:sending-ip-pool","sending pool ID", + "o:tracking-pixel-location-top","foo", + "recipient-variables","{\"cc-dest@sinch.com\": {\"variable1\": \"value1\"}}" + // spotless:on + ); + } + + public static SendMimeEmailRequest sendMimEmailRequest = + SendMimeEmailRequest.builder() + .setMessage(fileAttachment1) + .setTag(Arrays.asList("tag1", "tag2")) + .setDeliveryTime(Instant.parse("2000-01-22T11:23:45Z")) + .setTestMode(SendMimeEmailRequest.TestModeEnum.from("no")) + .setTo(Arrays.asList("aRecipient@mailgun-by-sinch.com")) + .setTemplateText(SendMimeEmailRequest.TemplateTextEnum.YES) + .setTemplateVersion("2") + .setTemplateVariables("{\"key\": \"value\"}") + .setEnableDkimSignature(SendMimeEmailRequest.DkimSignatureEnum.TRUE) + .setSecondaryDkim("example.com/s1") + .setSecondaryDkimPublic("public.example.com/s1") + .setDeliveryTimeOptimizePeriod(29) + .setTimezoneLocalize("02:04PM") + .setTracking(SendMimeEmailRequest.TrackingEnum.HTMLONLY) + .setTrackingClicks(SendMimeEmailRequest.TrackingClicksEnum.TRUE) + .setTrackingOpens(SendMimeEmailRequest.TrackingOpensEnum.FALSE) + .setRequireTls(SendMimeEmailRequest.RequireTlsEnum.TRUE) + .setSkipVerification(SendMimeEmailRequest.SkipVerificationEnum.NO) + .setSendingIp("192.168.0.10") + .setSendingIpPool("sending pool ID") + .setTrackingPixelLocationTop("foo") + .setRecipientVariables("{\"cc-dest@sinch.com\": {\"variable1\": \"value1\"}}") + .setTemplate("template value") + .build(); + + @Test + void serialize() { + Object serialized = + new HttpMapper() + .serializeFormParameters(Arrays.asList("multipart/form-data"), sendMimEmailRequest); + + TestHelpers.recursiveEquals(expected, serialized); + } +} From 6db38a147c32eb127595a4e7c29210858e25c998 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <jean-pierre.portier@mailgun.com> Date: Wed, 27 Nov 2024 12:15:27 +0100 Subject: [PATCH 3/8] test (Mailgun/Emails): Adding Mailgun emails e2e tests --- .../test/java/com/sinch/sdk/e2e/Config.java | 10 + .../e2e/domains/mailgun/v1/EmailsSteps.java | 183 ++++++++++++++++++ .../sdk/e2e/domains/mailgun/v1/MailgunIT.java | 16 ++ pom.xml | 1 + 4 files changed, 210 insertions(+) create mode 100644 client/src/test/java/com/sinch/sdk/e2e/domains/mailgun/v1/EmailsSteps.java create mode 100644 client/src/test/java/com/sinch/sdk/e2e/domains/mailgun/v1/MailgunIT.java diff --git a/client/src/test/java/com/sinch/sdk/e2e/Config.java b/client/src/test/java/com/sinch/sdk/e2e/Config.java index 4c3d940f..e0eec03a 100644 --- a/client/src/test/java/com/sinch/sdk/e2e/Config.java +++ b/client/src/test/java/com/sinch/sdk/e2e/Config.java @@ -4,7 +4,9 @@ import com.sinch.sdk.models.Configuration; import com.sinch.sdk.models.ConversationContext; import com.sinch.sdk.models.ConversationRegion; +import com.sinch.sdk.models.MailgunContext; import com.sinch.sdk.models.VoiceContext; +import java.util.Arrays; public class Config { @@ -21,6 +23,10 @@ public class Config { public static final String VOICE_HOST_NAME = "http://localhost:3019"; public static final String VOICE_MANAGEMENT_HOST_NAME = "http://localhost:3020"; + public static final String MAILGUN_HOST_NAME = "http://localhost:3021"; + public static final String MAILGUN_API_KEY = "apiKey"; + public static final String MAILGUN_STORAGE = "http://localhost:3021"; + private final SinchClient client; private Config() { @@ -44,6 +50,10 @@ private Config() { .setVoiceApplicationMngmtUrl(VOICE_MANAGEMENT_HOST_NAME) .setVoiceUrl(VOICE_HOST_NAME) .build()) + .setMailgunContext( + MailgunContext.builder().setStorageUrls(Arrays.asList(MAILGUN_STORAGE)).build()) + .setMailgunApiKey(MAILGUN_API_KEY) + .setMailgunUrl(MAILGUN_HOST_NAME) .build(); client = new SinchClient(configuration); diff --git a/client/src/test/java/com/sinch/sdk/e2e/domains/mailgun/v1/EmailsSteps.java b/client/src/test/java/com/sinch/sdk/e2e/domains/mailgun/v1/EmailsSteps.java new file mode 100644 index 00000000..9af0035d --- /dev/null +++ b/client/src/test/java/com/sinch/sdk/e2e/domains/mailgun/v1/EmailsSteps.java @@ -0,0 +1,183 @@ +package com.sinch.sdk.e2e.domains.mailgun.v1; + +import com.sinch.sdk.core.TestHelpers; +import com.sinch.sdk.domains.mailgun.api.v1.EmailsService; +import com.sinch.sdk.domains.mailgun.models.v1.emails.request.SendEmailRequest; +import com.sinch.sdk.domains.mailgun.models.v1.emails.request.SendMimeEmailRequest; +import com.sinch.sdk.domains.mailgun.models.v1.emails.response.ExceededQueueQuotaRegular; +import com.sinch.sdk.domains.mailgun.models.v1.emails.response.ExceededQueueQuotaScheduled; +import com.sinch.sdk.domains.mailgun.models.v1.emails.response.GetStoredEmailResponse; +import com.sinch.sdk.domains.mailgun.models.v1.emails.response.QueueStatusDisabledDetails; +import com.sinch.sdk.domains.mailgun.models.v1.emails.response.SendEmailResponse; +import com.sinch.sdk.domains.mailgun.models.v1.emails.response.SendingQueuesStatusResponse; +import com.sinch.sdk.e2e.Config; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import org.junit.jupiter.api.Assertions; + +public class EmailsSteps { + + EmailsService service; + + SendEmailResponse sendEmailResponse; + SendEmailResponse sendMimeEmailResponse; + GetStoredEmailResponse getStoredEmailResponse; + SendingQueuesStatusResponse sendingQueuesStatusResponse; + Boolean purgeDomainQueuesPassed; + + static final String domainName = "domainName"; + static final String storageKey = "storageKey"; + + @Given("^the Mailgun service \"Emails\" is available$") + public void serviceAvailable() { + service = Config.getSinchClient().mailgun().v1().emails(); + } + + @When("^I send a request to send a text email$") + public void send() { + + SendEmailRequest request = + SendEmailRequest.builder() + .setText("Hello, this is a text message for E2E testing.") + .setTo(Collections.singletonList("destination@e2e.tst")) + .setFrom("Excited E2E user \uD83D\uDCE7 <sender@e2e.tst>") + .setSubject("E2E test text email") + .build(); + + sendEmailResponse = service.send(domainName, request); + } + + @When("^I send a request to send a MIME email$") + public void sendMime() { + + File tempFile = new File("MimeMessage"); + tempFile.deleteOnExit(); + try (BufferedWriter out = new BufferedWriter(new FileWriter(tempFile))) { + out.write("\uD83D\uDCE7 Sample content \uD83D\uDCE7"); + } catch (IOException e) { + throw new RuntimeException(e); + } + + SendMimeEmailRequest request = + SendMimeEmailRequest.builder() + .setTo(Collections.singletonList("destination@e2e.tst")) + .setMessage(tempFile) + .build(); + + sendMimeEmailResponse = service.sendMime(domainName, request); + } + + @When("^I send a request to retrieve a stored email$") + public void getStoredEmail() { + + getStoredEmailResponse = service.get(domainName, storageKey); + } + + @When("^I send a request to get the sending queue status$") + public void getSendingQueuesStatus() { + + sendingQueuesStatusResponse = service.getSendingQueuesStatus(domainName); + } + + @When("^I send a request to purge the domain queues$") + public void purgeDomainQueues() { + + service.purgeDomainQueues(domainName); + purgeDomainQueuesPassed = true; + } + + @Then("the sendEmail response contains information about the text email") + public void sendResult() { + + SendEmailResponse expected = + SendEmailResponse.builder() + .setId("<20240606154318.027ac0b5fc80da62@sandbox123.mailgun.org>") + .setMessage("Queued. Thank you.") + .build(); + + TestHelpers.recursiveEquals(expected, sendEmailResponse); + } + + @Then("the sendMimeEmail response contains information about the email") + public void sendMimeResult() { + + SendEmailResponse expected = + SendEmailResponse.builder() + .setId("<20240606154852.a3fafd8a5230e166@sandbox123.mailgun.org>") + .setMessage("Queued. Thank you.") + .build(); + + TestHelpers.recursiveEquals(expected, sendMimeEmailResponse); + } + + @Then("the getEmail response contains the email details") + public void getStoredEmailResult() { + + GetStoredEmailResponse expected = + GetStoredEmailResponse.builder() + .setFrom("sender@e2e.tst") + .setSubject("Hello from mailgun") + .setTo("%recipient%") + .setSender("postmaster@sandbox123.mailgun.org") + .setRecipients("recipient@e2e.tst") + .setBodyHtml( + "<h1>Hello %recipient.name%</h1><span style=\"color:blue\">This is an HTML" + + " email</span>") + .setStrippedHtml( + "<h1>Hello %recipient.name%</h1><span style=\"color:blue\">This is an HTML" + + " email</span>") + .setBodyPlain("Message text only") + .setStrippedText("Message text only") + .setStrippedSignature("") + .setMessageHeaders( + Arrays.asList( + Arrays.asList("Mime-Version", "1.0"), + Arrays.asList( + "Content-Type", + "multipart/alternative;" + + " boundary=\"44eea75a00c7df3bdd541c89727faec0ce8d5b09663245a35789d6b264c6\""), + Arrays.asList("Subject", "Hello from mailgun"), + Arrays.asList("From", "sender@e2e.tst"), + Arrays.asList("To", "%recipient%"), + Arrays.asList("X-Mailgun-Deliver-By", "Thu, 06 Jun 2024 07:40:00 +0000"), + Arrays.asList( + "Message-Id", "<20240606162145.5f329edde3b4ed71@sandbox123.mailgun.org>"))) + .build(); + + TestHelpers.recursiveEquals(expected, getStoredEmailResponse); + } + + @Then("the response contains the sending queues status") + public void getSendingQueuesStatusResult() { + + SendingQueuesStatusResponse expected = + SendingQueuesStatusResponse.builder() + .setRegular( + ExceededQueueQuotaRegular.builder() + .setIsDisabled(false) + .setDisabled( + QueueStatusDisabledDetails.builder().setUntil("").setReason("").build()) + .build()) + .setScheduled( + ExceededQueueQuotaScheduled.builder() + .setIsDisabled(false) + .setDisabled( + QueueStatusDisabledDetails.builder().setUntil("").setReason("").build()) + .build()) + .build(); + + TestHelpers.recursiveEquals(expected, sendingQueuesStatusResponse); + } + + @Then("the response indicates the purge has been done") + public void purgeDomainQueuesValue() { + Assertions.assertTrue(purgeDomainQueuesPassed); + } +} diff --git a/client/src/test/java/com/sinch/sdk/e2e/domains/mailgun/v1/MailgunIT.java b/client/src/test/java/com/sinch/sdk/e2e/domains/mailgun/v1/MailgunIT.java new file mode 100644 index 00000000..68b48dfd --- /dev/null +++ b/client/src/test/java/com/sinch/sdk/e2e/domains/mailgun/v1/MailgunIT.java @@ -0,0 +1,16 @@ +package com.sinch.sdk.e2e.domains.mailgun.v1; + +import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; + +import org.junit.platform.suite.api.ConfigurationParameter; +import org.junit.platform.suite.api.IncludeEngines; +import org.junit.platform.suite.api.SelectClasspathResource; +import org.junit.platform.suite.api.Suite; +import org.junit.platform.suite.api.SuiteDisplayName; + +@Suite +@SuiteDisplayName("Mailgun V1") +@IncludeEngines("cucumber") +@SelectClasspathResource("features/mailgun") +@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "com.sinch.sdk.e2e.domains.mailgun.v1") +public class MailgunIT {} diff --git a/pom.xml b/pom.xml index 7132a23a..73ec1815 100644 --- a/pom.xml +++ b/pom.xml @@ -321,6 +321,7 @@ </goals> <configuration> <includes> + <include>com.sinch.sdk.e2e.domains.mailgun.v1.MailgunIT</include> <include>com.sinch.sdk.e2e.domains.conversation.ConversationIT</include> <include>com.sinch.sdk.e2e.domains.voice.v0.VoiceIT</include> <include>com.sinch.sdk.e2e.domains.voice.v1.VoiceIT</include> From d45ce941c4e56f52515c035439f129c7236277ce Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <jean-pierre.portier@mailgun.com> Date: Wed, 27 Nov 2024 14:28:05 +0100 Subject: [PATCH 4/8] refactor (FormSerializer/Tests): Remove duplicated propery key from ordered list --- .../com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java b/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java index da8b9ed5..15fc0cc0 100644 --- a/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java +++ b/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java @@ -165,7 +165,6 @@ void defaultAdditionalPropertiesValues() { SerializableObject.PROPERTY_FILE, SerializableObject.PROPERTY_ENUM, SerializableObject.PROPERTY_ENUM_COLLECTION, - SerializableObject.PROPERTY_RFC822_COLLECTION, SerializableObject.PROPERTY_TEXT_COLLECTION }) static class SerializableObject implements AdditionalProperties { From 02bfb0e6d81c76b97780d0da906da682269552d4 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <jean-pierre.portier@mailgun.com> Date: Wed, 27 Nov 2024 16:01:34 +0100 Subject: [PATCH 5/8] feat (HttpCLient/Multipart): Set a default charset to 'UTF-8' is not specified --- .../src/main/com/sinch/sdk/http/HttpClientApache.java | 11 ++++++++--- .../sinch/sdk/e2e/domains/mailgun/v1/EmailsSteps.java | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/client/src/main/com/sinch/sdk/http/HttpClientApache.java b/client/src/main/com/sinch/sdk/http/HttpClientApache.java index ef9b6de2..d6fc2b70 100644 --- a/client/src/main/com/sinch/sdk/http/HttpClientApache.java +++ b/client/src/main/com/sinch/sdk/http/HttpClientApache.java @@ -134,7 +134,7 @@ public HttpResponse invokeAPI( addBody(requestBuilder, body); } - addFormParams(requestBuilder, formParams); + addFormParams(requestBuilder, contentType, formParams); addAuth(requestBuilder, authManagersByOasSecuritySchemes, authNames, body); @@ -225,14 +225,19 @@ private void addBody(ClassicRequestBuilder requestBuilder, String body) { requestBuilder.setEntity(new StringEntity(body, charset)); } - private void addFormParams(ClassicRequestBuilder requestBuilder, Map<String, Object> formParams) { + private void addFormParams( + ClassicRequestBuilder requestBuilder, + Collection<String> contentType, + Map<String, Object> formParams) { if (null == formParams) { return; } MultipartEntityBuilder multiPartBuilder = MultipartEntityBuilder.create(); - + if (contentType.stream().noneMatch(cType -> cType.toLowerCase().contains("charset="))) { + multiPartBuilder.setCharset(StandardCharsets.UTF_8); + } formParams.forEach((key, value) -> addMultiPart(requestBuilder, multiPartBuilder, key, value)); requestBuilder.setEntity(multiPartBuilder.build()); diff --git a/client/src/test/java/com/sinch/sdk/e2e/domains/mailgun/v1/EmailsSteps.java b/client/src/test/java/com/sinch/sdk/e2e/domains/mailgun/v1/EmailsSteps.java index 9af0035d..8776e7d4 100644 --- a/client/src/test/java/com/sinch/sdk/e2e/domains/mailgun/v1/EmailsSteps.java +++ b/client/src/test/java/com/sinch/sdk/e2e/domains/mailgun/v1/EmailsSteps.java @@ -47,7 +47,7 @@ public void send() { SendEmailRequest.builder() .setText("Hello, this is a text message for E2E testing.") .setTo(Collections.singletonList("destination@e2e.tst")) - .setFrom("Excited E2E user \uD83D\uDCE7 <sender@e2e.tst>") + .setFrom("Excited E2E user ✉️ <sender@e2e.tst>") .setSubject("E2E test text email") .build(); From b2d7acb077439ed9a113463befb0b5de4e880a75 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <jean-pierre.portier@mailgun.com> Date: Wed, 27 Nov 2024 17:42:14 +0100 Subject: [PATCH 6/8] refactor (FormSerializer): Sorting properties optimization --- .../core/databind/multipart/ObjectMapper.java | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/core/src/main/com/sinch/sdk/core/databind/multipart/ObjectMapper.java b/core/src/main/com/sinch/sdk/core/databind/multipart/ObjectMapper.java index 048927f6..3f97b718 100644 --- a/core/src/main/com/sinch/sdk/core/databind/multipart/ObjectMapper.java +++ b/core/src/main/com/sinch/sdk/core/databind/multipart/ObjectMapper.java @@ -18,6 +18,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -54,28 +55,24 @@ private List<Pair<String, Method>> collectSerializableProperties(BeanInfo beanIn for (MethodDescriptor methodDescriptor : methodDescriptors) { getPropertyGetter(methodDescriptor.getMethod()).ifPresent(result::add); } - return sortProperties(beanInfo, result); + + sortProperties(beanInfo, result); + return result; } - private List<Pair<String, Method>> sortProperties( - BeanInfo beanInfo, List<Pair<String, Method>> properties) { + private void sortProperties(BeanInfo beanInfo, List<Pair<String, Method>> properties) { - PropertiesOrder propertyOrder = + PropertiesOrder propertiesOrder = beanInfo.getBeanDescriptor().getBeanClass().getAnnotation(PropertiesOrder.class); - if (null == propertyOrder) { - return properties; + if (null == propertiesOrder + || null == propertiesOrder.value() + || 0 == propertiesOrder.value().length) { + return; } - ArrayList<Pair<String, Method>> sorted = new ArrayList<>(properties.size()); - - for (String property : propertyOrder.value()) { - properties.stream() - .filter(p -> p.getLeft().equals(property)) - .findFirst() - .ifPresent(sorted::add); - } - return sorted; + List<String> order = java.util.Arrays.asList(propertiesOrder.value()); + properties.sort(Comparator.comparingInt(l -> order.indexOf(l.getLeft()))); } private Optional<Pair<String, Method>> getPropertyGetter(Method method) { From cc4789e2ef78047742ae1e95b350c64c45dcc894 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <jean-pierre.portier@mailgun.com> Date: Thu, 28 Nov 2024 09:59:10 +0100 Subject: [PATCH 7/8] feat (FormSerializer): Remove 'PropertiesOrder' feature --- .../databind/annotation/PropertiesOrder.java | 14 -------- .../core/databind/multipart/ObjectMapper.java | 18 ---------- .../databind/multipart/ObjectMapperTest.java | 31 ----------------- .../emails/request/SendEmailRequestImpl.java | 34 ------------------- .../request/SendMimeEmailRequestImpl.java | 26 -------------- 5 files changed, 123 deletions(-) delete mode 100644 core/src/main/com/sinch/sdk/core/databind/annotation/PropertiesOrder.java diff --git a/core/src/main/com/sinch/sdk/core/databind/annotation/PropertiesOrder.java b/core/src/main/com/sinch/sdk/core/databind/annotation/PropertiesOrder.java deleted file mode 100644 index 9fedef10..00000000 --- a/core/src/main/com/sinch/sdk/core/databind/annotation/PropertiesOrder.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.sinch.sdk.core.databind.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@SinchAnnotation -public @interface PropertiesOrder { - - String[] value() default {}; -} diff --git a/core/src/main/com/sinch/sdk/core/databind/multipart/ObjectMapper.java b/core/src/main/com/sinch/sdk/core/databind/multipart/ObjectMapper.java index 3f97b718..2401658d 100644 --- a/core/src/main/com/sinch/sdk/core/databind/multipart/ObjectMapper.java +++ b/core/src/main/com/sinch/sdk/core/databind/multipart/ObjectMapper.java @@ -2,7 +2,6 @@ import com.sinch.sdk.core.databind.FormSerializer; import com.sinch.sdk.core.databind.annotation.FormSerialize; -import com.sinch.sdk.core.databind.annotation.PropertiesOrder; import com.sinch.sdk.core.databind.annotation.Property; import com.sinch.sdk.core.exceptions.SerializationException; import com.sinch.sdk.core.models.AdditionalProperties; @@ -18,7 +17,6 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; -import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -56,25 +54,9 @@ private List<Pair<String, Method>> collectSerializableProperties(BeanInfo beanIn getPropertyGetter(methodDescriptor.getMethod()).ifPresent(result::add); } - sortProperties(beanInfo, result); return result; } - private void sortProperties(BeanInfo beanInfo, List<Pair<String, Method>> properties) { - - PropertiesOrder propertiesOrder = - beanInfo.getBeanDescriptor().getBeanClass().getAnnotation(PropertiesOrder.class); - - if (null == propertiesOrder - || null == propertiesOrder.value() - || 0 == propertiesOrder.value().length) { - return; - } - - List<String> order = java.util.Arrays.asList(propertiesOrder.value()); - properties.sort(Comparator.comparingInt(l -> order.indexOf(l.getLeft()))); - } - private Optional<Pair<String, Method>> getPropertyGetter(Method method) { Property property = method.getDeclaredAnnotation(Property.class); diff --git a/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java b/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java index 15fc0cc0..db12a092 100644 --- a/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java +++ b/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java @@ -3,7 +3,6 @@ import com.sinch.sdk.core.TestHelpers; import com.sinch.sdk.core.databind.FormSerializer; import com.sinch.sdk.core.databind.annotation.FormSerialize; -import com.sinch.sdk.core.databind.annotation.PropertiesOrder; import com.sinch.sdk.core.databind.annotation.Property; import com.sinch.sdk.core.databind.multipart.ObjectMapperTest.SerializableObject.AnEnum; import com.sinch.sdk.core.models.AdditionalProperties; @@ -77,26 +76,6 @@ void countValue() { Assertions.assertEquals(11, serialized.size()); } - @Test - void order() { - - String[] expectedOrder = { - SerializableObject.PROPERTY_RFC822_COLLECTION, - SerializableObject.PROPERTY_TEXT, - SerializableObject.PROPERTY_FILE_COLLECTION, - SerializableObject.PROPERTY_RFC822, - SerializableObject.PROPERTY_FILE, - SerializableObject.PROPERTY_ENUM, - SerializableObject.PROPERTY_ENUM_COLLECTION, - SerializableObject.PROPERTY_TEXT_COLLECTION, - "h:toto", - "v:foo", - "raw" - }; - String[] keys = serialized.keySet().toArray(new String[0]); - TestHelpers.recursiveEquals(expectedOrder, keys); - } - @Test void textValue() { TestHelpers.recursiveEquals("text value", serialized.get("aText")); @@ -157,16 +136,6 @@ void defaultAdditionalPropertiesValues() { defaultAdditionalPropertiesSerialized); } - @PropertiesOrder({ - SerializableObject.PROPERTY_RFC822_COLLECTION, - SerializableObject.PROPERTY_TEXT, - SerializableObject.PROPERTY_FILE_COLLECTION, - SerializableObject.PROPERTY_RFC822, - SerializableObject.PROPERTY_FILE, - SerializableObject.PROPERTY_ENUM, - SerializableObject.PROPERTY_ENUM_COLLECTION, - SerializableObject.PROPERTY_TEXT_COLLECTION - }) static class SerializableObject implements AdditionalProperties { public static final String PROPERTY_TEXT = "aText"; public static final String PROPERTY_ENUM = "anEnum"; diff --git a/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendEmailRequestImpl.java b/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendEmailRequestImpl.java index 9befa99a..6233c9e1 100644 --- a/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendEmailRequestImpl.java +++ b/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendEmailRequestImpl.java @@ -1,7 +1,6 @@ package com.sinch.sdk.domains.mailgun.models.v1.emails.request; import com.sinch.sdk.core.databind.annotation.FormSerialize; -import com.sinch.sdk.core.databind.annotation.PropertiesOrder; import com.sinch.sdk.core.databind.annotation.Property; import com.sinch.sdk.core.models.OptionalValue; import java.io.File; @@ -11,39 +10,6 @@ import java.util.Map; import java.util.Objects; -@PropertiesOrder({ - SendEmailRequestImpl.PROPERTY_FROM, - SendEmailRequestImpl.PROPERTY_TO, - SendEmailRequestImpl.PROPERTY_CC, - SendEmailRequestImpl.PROPERTY_BCC, - SendEmailRequestImpl.PROPERTY_SUBJECT, - SendEmailRequestImpl.PROPERTY_TEXT, - SendEmailRequestImpl.PROPERTY_HTML, - SendEmailRequestImpl.PROPERTY_AMP_HTML, - SendEmailRequestImpl.PROPERTY_ATTACHMENT, - SendEmailRequestImpl.PROPERTY_INLINE, - SendEmailRequestImpl.PROPERTY_TEMPLATE, - SendEmailRequestImpl.PROPERTY_T_COLON_VERSION, - SendEmailRequestImpl.PROPERTY_T_COLON_TEXT, - SendEmailRequestImpl.PROPERTY_T_COLON_VARIABLES, - SendEmailRequestImpl.PROPERTY_O_COLON_TAG, - SendEmailRequestImpl.PROPERTY_O_COLON_DKIM, - SendEmailRequestImpl.PROPERTY_O_COLON_SECONDARY_DKIM, - SendEmailRequestImpl.PROPERTY_O_COLON_SECONDARY_DKIM_PUBLIC, - SendEmailRequestImpl.PROPERTY_O_COLON_DELIVERYTIME, - SendEmailRequestImpl.PROPERTY_O_COLON_DELIVERYTIME_OPTIMIZE_PERIOD, - SendEmailRequestImpl.PROPERTY_O_COLON_TIME_ZONE_LOCALIZE, - SendEmailRequestImpl.PROPERTY_O_COLON_TESTMODE, - SendEmailRequestImpl.PROPERTY_O_COLON_TRACKING, - SendEmailRequestImpl.PROPERTY_O_COLON_TRACKING_CLICKS, - SendEmailRequestImpl.PROPERTY_O_COLON_TRACKING_OPENS, - SendEmailRequestImpl.PROPERTY_O_COLON_REQUIRE_TLS, - SendEmailRequestImpl.PROPERTY_O_COLON_SKIP_VERIFICATION, - SendEmailRequestImpl.PROPERTY_O_COLON_SENDING_IP, - SendEmailRequestImpl.PROPERTY_O_COLON_SENDING_IP_POOL, - SendEmailRequestImpl.PROPERTY_O_COLON_TRACKING_PIXEL_LOCATION_TOP, - SendEmailRequestImpl.PROPERTY_RECIPIENT_VARIABLES -}) public class SendEmailRequestImpl implements SendEmailRequest { private static final long serialVersionUID = 1L; diff --git a/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequestImpl.java b/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequestImpl.java index 94ebe4e5..05cd1024 100644 --- a/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequestImpl.java +++ b/openapi-contracts/src/main/com/sinch/sdk/domains/mailgun/models/v1/emails/request/SendMimeEmailRequestImpl.java @@ -1,7 +1,6 @@ package com.sinch.sdk.domains.mailgun.models.v1.emails.request; import com.sinch.sdk.core.databind.annotation.FormSerialize; -import com.sinch.sdk.core.databind.annotation.PropertiesOrder; import com.sinch.sdk.core.databind.annotation.Property; import com.sinch.sdk.core.models.OptionalValue; import java.io.File; @@ -11,31 +10,6 @@ import java.util.Map; import java.util.Objects; -@PropertiesOrder({ - SendMimeEmailRequestImpl.PROPERTY_TO, - SendMimeEmailRequestImpl.PROPERTY_MESSAGE, - SendMimeEmailRequestImpl.PROPERTY_TEMPLATE, - SendMimeEmailRequestImpl.PROPERTY_T_COLON_VERSION, - SendMimeEmailRequestImpl.PROPERTY_T_COLON_TEXT, - SendMimeEmailRequestImpl.PROPERTY_T_COLON_VARIABLES, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_TAG, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_DKIM, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_SECONDARY_DKIM, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_SECONDARY_DKIM_PUBLIC, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_DELIVERYTIME, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_DELIVERYTIME_OPTIMIZE_PERIOD, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_TIME_ZONE_LOCALIZE, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_TESTMODE, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_TRACKING, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_TRACKING_CLICKS, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_TRACKING_OPENS, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_REQUIRE_TLS, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_SKIP_VERIFICATION, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_SENDING_IP, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_SENDING_IP_POOL, - SendMimeEmailRequestImpl.PROPERTY_O_COLON_TRACKING_PIXEL_LOCATION_TOP, - SendMimeEmailRequestImpl.PROPERTY_RECIPIENT_VARIABLES -}) public class SendMimeEmailRequestImpl implements SendMimeEmailRequest { private static final long serialVersionUID = 1L; From d075b64e8d1a33e950e1d054b98bc3dbe50b3994 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <jean-pierre.portier@mailgun.com> Date: Thu, 28 Nov 2024 16:55:50 +0100 Subject: [PATCH 8/8] refactor (Core/RFC822 formatter): Use Core RFC822 formatter from ObjectMapper test --- .../core/utils/databind/RFC822FormSerializer.java | 6 +++++- .../core/databind/multipart/ObjectMapperTest.java | 14 +++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/main/com/sinch/sdk/core/utils/databind/RFC822FormSerializer.java b/core/src/main/com/sinch/sdk/core/utils/databind/RFC822FormSerializer.java index 4d22a8fc..51c84e3a 100644 --- a/core/src/main/com/sinch/sdk/core/utils/databind/RFC822FormSerializer.java +++ b/core/src/main/com/sinch/sdk/core/utils/databind/RFC822FormSerializer.java @@ -10,6 +10,10 @@ public class RFC822FormSerializer extends FormSerializer<Instant> { @Override public void serialize(Instant in, String fieldName, Map<String, Object> out) { - out.put(fieldName, DateTimeFormatter.RFC_1123_DATE_TIME.format(in.atZone(ZoneId.of("UTC")))); + out.put(fieldName, format(in)); + } + + public static String format(Instant instant) { + return DateTimeFormatter.RFC_1123_DATE_TIME.format(instant.atZone(ZoneId.of("UTC"))); } } diff --git a/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java b/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java index db12a092..c1f1668b 100644 --- a/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java +++ b/core/src/test/java/com/sinch/sdk/core/databind/multipart/ObjectMapperTest.java @@ -13,8 +13,6 @@ import com.sinch.sdk.domains.mailgun.models.v1.emails.request.SendEmailRequestTest; import java.io.File; import java.time.Instant; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -283,13 +281,11 @@ public static class RFC822ListFormSerializer extends FormSerializer<Collection<I @Override public void serialize(Collection<Instant> in, String fieldName, Map<String, Object> out) { - out.put( - fieldName, - in.stream() - .map( - instant -> - DateTimeFormatter.RFC_1123_DATE_TIME.format(instant.atZone(ZoneId.of("UTC")))) - .collect(Collectors.toList())); + + Collection<String> formatted = + in.stream().map(RFC822FormSerializer::format).collect(Collectors.toList()); + + out.put(fieldName, formatted); } }