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

Allowing ToScalar to pass in a method to use to deserialize. #643

Merged
merged 2 commits into from
Feb 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,16 @@ public AnnotationValue getAnnotationValue(DotName annotation) {
return this.annotationsMap.get(annotation).value();
}

/**
* Get a specific annotation
*
* @param annotation the annotation you want
* @return the annotation value or null
*/
public AnnotationValue getAnnotationValue(DotName annotation, String name) {
return this.annotationsMap.get(annotation).value(name);
}

/**
* Check if there is an annotation and it has a valid value
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Queue;
import java.util.Set;
import java.util.SortedSet;
Expand Down Expand Up @@ -63,7 +66,10 @@ public static boolean isParameterized(Type type) {
* @return true if it is
*/
public static boolean isOptional(Type type) {
return isParameterized(type) && type.name().equals(OPTIONAL);
return isParameterized(type) && type.name().equals(OPTIONAL) // Normal Optional
|| type.name().equals(LONG_OPTIONAL)
|| type.name().equals(DOUBLE_OPTIONAL)
|| type.name().equals(INTEGER_OPTIONAL);
}

/**
Expand Down Expand Up @@ -98,7 +104,8 @@ public static boolean isEnum(ClassInfo classInfo) {
*/
public static boolean isNumberLikeTypeOrContainedIn(Type type) {
return isTypeOrContainedIn(type, BYTE, BYTE_PRIMATIVE, SHORT, SHORT_PRIMATIVE, INTEGER, INTEGER_PRIMATIVE,
BIG_INTEGER, DOUBLE, DOUBLE_PRIMATIVE, BIG_DECIMAL, LONG, LONG_PRIMATIVE, FLOAT, FLOAT_PRIMATIVE);
BIG_INTEGER, DOUBLE, DOUBLE_PRIMATIVE, BIG_DECIMAL, LONG, LONG_PRIMATIVE, FLOAT, FLOAT_PRIMATIVE,
INTEGER_OPTIONAL, DOUBLE_OPTIONAL, LONG_OPTIONAL);
}

/**
Expand Down Expand Up @@ -256,14 +263,17 @@ public static boolean isUnwrappedType(Type type) {

private static final DotName INTEGER = DotName.createSimple(Integer.class.getName());
private static final DotName INTEGER_PRIMATIVE = DotName.createSimple(int.class.getName());
private static final DotName INTEGER_OPTIONAL = DotName.createSimple(OptionalInt.class.getName());
private static final DotName BIG_INTEGER = DotName.createSimple(BigInteger.class.getName());

private static final DotName DOUBLE = DotName.createSimple(Double.class.getName());
private static final DotName DOUBLE_PRIMATIVE = DotName.createSimple(double.class.getName());
private static final DotName DOUBLE_OPTIONAL = DotName.createSimple(OptionalDouble.class.getName());
private static final DotName BIG_DECIMAL = DotName.createSimple(BigDecimal.class.getName());

private static final DotName LONG = DotName.createSimple(Long.class.getName());
private static final DotName LONG_PRIMATIVE = DotName.createSimple(long.class.getName());
private static final DotName LONG_OPTIONAL = DotName.createSimple(OptionalLong.class.getName());

private static final DotName FLOAT = DotName.createSimple(Float.class.getName());
private static final DotName FLOAT_PRIMATIVE = DotName.createSimple(float.class.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,7 @@ private Reference getReference(Direction direction, Type fieldType, Type methodT
return createReference(direction, classInfo, shouldCreateType, parentObjectReference,
parametrizedTypeArgumentsReferences, false);
} else {
LOG.warn("Class [" + fieldType.name()
+ "] in not indexed in Jandex. Can not scan Object Type, defaulting to String Scalar");
return Scalars.getScalar(String.class.getName()); // default
return getNonIndexedReference(direction, fieldType);
}
} else if (fieldType.kind().equals(Type.Kind.PARAMETERIZED_TYPE)) {
// Type.Kind.PARAMETERIZED_TYPE handles generics PoJos here, collections and unwrapped types are catched
Expand All @@ -316,9 +314,7 @@ private Reference getReference(Direction direction, Type fieldType, Type methodT
return createReference(direction, classInfo, shouldCreateType, parentObjectReference,
parametrizedTypeArgumentsReferences, true);
} else {
LOG.warn("Class [" + fieldType.name()
+ "] in not indexed in Jandex. Can not scan Object Type, defaulting to String Scalar");
return Scalars.getScalar(String.class.getName()); // default
return getNonIndexedReference(direction, fieldType);
}
} else if (fieldType.kind().equals(Type.Kind.TYPE_VARIABLE)) {
if (parentObjectReference == null || parentObjectReference.getParametrizedTypeArguments() == null) {
Expand Down Expand Up @@ -441,4 +437,26 @@ private static ReferenceType getCorrectReferenceType(Direction direction) {
return ReferenceType.TYPE;
}
}

private Reference getNonIndexedReference(Direction direction, Type fieldType) {

LOG.warn("Class [" + fieldType.name()
+ "] is not indexed in Jandex. Can not scan Object Type, might not be mapped correctly");

Reference r = new Reference();
r.setClassName(fieldType.name().toString());
r.setGraphQlClassName(fieldType.name().toString());
r.setName(fieldType.name().local());

boolean isNumber = Classes.isNumberLikeTypeOrContainedIn(fieldType);
boolean isDate = Classes.isDateLikeTypeOrContainedIn(fieldType);
if (isNumber || isDate) {
r.setType(ReferenceType.SCALAR);
} else if (direction.equals(Direction.IN)) {
r.setType(ReferenceType.INPUT);
} else {
r.setType(ReferenceType.TYPE);
}
return r;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public static boolean shouldCreateTypeInSchema(Annotations annotations) {
/**
* Get the mapping for a certain field.
*
* @param field
* @param annotations the annotations
* @return Potentially a MappingInfo model
*/
Expand All @@ -43,6 +44,7 @@ public static Optional<Mapping> getMapping(Field field, Annotations annotations)
/**
* Get the mapping for a certain reference.
*
* @param r
* @param annotations the annotations
* @return Potentially a MappingInfo model
*/
Expand All @@ -52,35 +54,55 @@ public static Optional<Mapping> getMapping(Reference r, Annotations annotations)
String scalarName = getScalarName(type);
Reference reference = Scalars.getScalar(scalarName);
Mapping mappingInfo = new Mapping(reference);
// Check the way to create this.
String className = r.getClassName();
if (!r.getType().equals(ReferenceType.SCALAR)) { // mapping to scalar stays on default NONE
ClassInfo classInfo = ScanningContext.getIndex().getClassByName(DotName.createSimple(className));
if (classInfo != null) {
// Get Parameter type
Type parameter = Type.create(DotName.createSimple(reference.getClassName()), Type.Kind.CLASS);

// Check if we can use a constructor
MethodInfo constructor = classInfo.method(CONTRUCTOR_METHOD_NAME, parameter);
if (constructor != null) {
mappingInfo.setCreate(Mapping.Create.CONSTRUCTOR);
} else {
// Check if we can use setValue
MethodInfo setValueMethod = classInfo.method("setValue", parameter);
if (setValueMethod != null) {
mappingInfo.setCreate(Mapping.Create.SET_VALUE);
// Check the way to create this (deserializeMethod)
// First check if the user supplied a way
String deserializeMethod = getDeserializeMethod(annotations);
if (deserializeMethod != null) {
mappingInfo.setDeserializeMethod(deserializeMethod);
} else {
// Auto detect this.
String className = r.getClassName();
if (!r.getType().equals(ReferenceType.SCALAR)) { // mapping to scalar stays on default NONE
ClassInfo classInfo = ScanningContext.getIndex().getClassByName(DotName.createSimple(className));
if (classInfo != null) {
// Get Parameter type
Type parameter = Type.create(DotName.createSimple(reference.getClassName()), Type.Kind.CLASS);

// Check if we can use a constructor
MethodInfo constructor = classInfo.method(CONTRUCTOR_METHOD_NAME, parameter);
if (constructor != null) {
mappingInfo.setDeserializeMethod(CONTRUCTOR_METHOD_NAME); // Create new instance with a contructor
} else {
// Check if we can use static fromXXXXX
String staticFromMethodName = "from" + scalarName;
MethodInfo staticFromMethod = classInfo.method(staticFromMethodName, parameter);
if (staticFromMethod != null) {
mappingInfo.setCreate(Mapping.Create.STATIC_FROM);
// Check if we can use setValue
MethodInfo setValueMethod = classInfo.method(SET_VALUE_METHOD_NAME, parameter);
if (setValueMethod != null) {
mappingInfo.setDeserializeMethod(SET_VALUE_METHOD_NAME);
} else {
// Check if we can use static fromXXXXX
String staticFromMethodName = FROM + scalarName;
MethodInfo staticFromMethod = classInfo.method(staticFromMethodName, parameter);
if (staticFromMethod != null) {
mappingInfo.setDeserializeMethod(staticFromMethodName);
} else {
// Check if we can use static getInstance
MethodInfo staticGetInstance = classInfo.method(GET_INSTANCE_METHOD_NAME, parameter);
if (staticGetInstance != null) {
mappingInfo.setDeserializeMethod(GET_INSTANCE_METHOD_NAME);
}
}
}
}
}

}
}
}

// Get serializeMethod (default to toString)
String serializeMethod = getSerializeMethod(annotations);
if (serializeMethod != null) {
mappingInfo.setSerializeMethod(serializeMethod);
}

return Optional.of(mappingInfo);
} else {
// TODO: Support other than Scalar mapping
Expand Down Expand Up @@ -111,5 +133,31 @@ private static Type getMapTo(Annotations annotations) {
return null;
}

private static String getSerializeMethod(Annotations annotations) {
return getAnnotationParameterAsString(annotations, SERIALIZE_METHOD);
}

private static String getDeserializeMethod(Annotations annotations) {
return getAnnotationParameterAsString(annotations, DESERIALIZE_METHOD);
}

private static String getAnnotationParameterAsString(Annotations annotations, String param) {
if (annotations != null && annotations.containsOneOfTheseAnnotations(Annotations.TO_SCALAR)) {
AnnotationValue annotationValue = annotations.getAnnotationValue(Annotations.TO_SCALAR, param);
if (annotationValue != null) {
String value = annotationValue.asString();
if (value != null && !value.isEmpty()) {
return value;
}
}
}
return null;
}

private static final String CONTRUCTOR_METHOD_NAME = "<init>";
private static final String SET_VALUE_METHOD_NAME = "setValue";
private static final String GET_INSTANCE_METHOD_NAME = "getInstance";
private static final String FROM = "from";
private static final String SERIALIZE_METHOD = "serializeMethod";
private static final String DESERIALIZE_METHOD = "deserializeMethod";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.smallrye.graphql.index.app;

import java.time.LocalDate;
import java.util.Optional;
import java.util.OptionalDouble;

import org.eclipse.microprofile.graphql.DateFormat;

import io.smallrye.graphql.api.Scalar;
import io.smallrye.graphql.api.ToScalar;

public class ClassWithOptionalField {

@ToScalar(Scalar.Int.class)
public Optional<Long> id;

public Optional<String> maybe;

@DateFormat("dd MMM yyyy")
public Optional<LocalDate> maybeOneDay;

@ToScalar(Scalar.String.class)
public Optional<Mapped> mapped;

public OptionalDouble amount;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.smallrye.graphql.index.app;

import java.util.Currency;

import javax.json.Json;
import javax.json.JsonObject;
import javax.json.bind.adapter.JsonbAdapter;

/**
* Map a Currency to and from json
*
* @author Phillip Kruger ([email protected])
*/
public class CurrencyAdapter implements JsonbAdapter<Currency, JsonObject> {

@Override
public JsonObject adaptToJson(Currency currency) {
return Json.createObjectBuilder()
.add("currency", currency.getCurrencyCode())
.build();
}

@Override
public Currency adaptFromJson(JsonObject json) {
return Currency.getInstance(json.getString("currencyCode"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.smallrye.graphql.index.app;

public class Mapped {
private String value;

public Mapped() {
}

public Mapped(String value) {
this.value = value;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

@Override
public String toString() {
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.smallrye.graphql.index.app;

import java.util.Currency;

import javax.json.bind.annotation.JsonbTypeAdapter;

import org.eclipse.microprofile.graphql.Description;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Mutation;
import org.eclipse.microprofile.graphql.Query;

import io.smallrye.graphql.api.Scalar;
import io.smallrye.graphql.api.ToScalar;

@GraphQLApi
public class MappingResource {

@Mutation
@Description("Add new Data")
public Data createData(Data data) {
data.id = (long) (Math.random() * 10000);
return data;
}

@Query
public Data getData() {
Data d = new Data();
d.id = 1L;
d.name = "Foo";
d.currency = Currency.getInstance("EUR");
return d;
}

public static class Data {
public Long id;

public String name;

@ToScalar(Scalar.String.class)
@JsonbTypeAdapter(CurrencyAdapter.class)
public Currency currency;

@ToScalar(Scalar.String.class)
public Email email;

}

}
Loading