Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat (Java): Support 'AdditionalProperty' interface for form parameters serialization #170

Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -16,13 +17,16 @@
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;
import java.util.Optional;

public class ObjectMapper {

static final String ADDITIONAL_PROPERTIES_IDENTIFIER = "additionalProperties";

public Map<String, Object> serialize(Object value)
throws IntrospectionException,
InvocationTargetException,
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand Down
27 changes: 27 additions & 0 deletions core/src/main/com/sinch/sdk/core/models/AdditionalProperties.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"))));
}
}
Loading
Loading