Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Commit

Permalink
Merge pull request #54 from mkalen/feature/jsr-310
Browse files Browse the repository at this point in the history
Support JSR-310 classes, pluggable ObjectMapper
  • Loading branch information
chemdrew authored Nov 2, 2018
2 parents 109d404 + 26d72b2 commit afd5fc4
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 12 deletions.
14 changes: 12 additions & 2 deletions nodes/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,26 @@
<artifactId>nodes</artifactId>
<name>Nodes</name>

<properties>
<jackson.annotations.version>2.9.4</jackson.annotations.version>
<jackson.databind.version>2.9.7</jackson.databind.version>
</properties>

<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.4</version>
<version>${jackson.annotations.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.7</version>
<version>${jackson.databind.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.databind.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
Expand Down
9 changes: 6 additions & 3 deletions nodes/src/main/java/io/aexp/nodes/graphql/Deserializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package io.aexp.nodes.graphql;

import io.aexp.nodes.graphql.annotations.GraphQLProperty;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
Expand All @@ -25,18 +26,20 @@

final class Deserializer<T> extends JsonDeserializer<Resource<T>> {

private Class<T> t;
private final Class<T> t;
private final ObjectMapperFactory objectMapperFactory;

Deserializer(Class<T> type) {
Deserializer(Class<T> type, ObjectMapperFactory objectMapperFactory) {
t = type;
this.objectMapperFactory = objectMapperFactory;
}

@Override
public Resource<T> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {

Resource<T> resourceModel = new Resource<T>();

ObjectMapper mapper = new ObjectMapper();
ObjectMapper mapper = objectMapperFactory.newDeserializerMapper();
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);

Expand Down
10 changes: 8 additions & 2 deletions nodes/src/main/java/io/aexp/nodes/graphql/Fetch.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package io.aexp.nodes.graphql;

import io.aexp.nodes.graphql.exceptions.GraphQLException;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
Expand All @@ -31,12 +32,17 @@

final class Fetch {

private final ObjectMapperFactory objectMapperFactory;
private ObjectMapper mapper;
private SimpleModule module;
private static final int STATUS_CODE_THRESHOLD = 400;

Fetch(ObjectMapperFactory objectMapperFactory) {
this.objectMapperFactory = objectMapperFactory;
}

<T> GraphQLResponseEntity<T> send(GraphQLRequestEntity requestEntity, Class<T> responseClass) throws GraphQLException {
mapper = new ObjectMapper();
mapper = objectMapperFactory.newSerializerMapper();
module = new SimpleModule();

Request request = new Request();
Expand Down Expand Up @@ -114,7 +120,7 @@ private HttpURLConnection createConnection(URL requestUrl, byte[] postData, Map<
}

private <T> Wrapper<T> deserializeResponse(BufferedReader bufferedReader, Class<T> responseClass) throws IOException {
Deserializer<T> deserializer = new Deserializer<T>(responseClass);
Deserializer<T> deserializer = new Deserializer<T>(responseClass, objectMapperFactory);
module.addDeserializer(Resource.class, deserializer);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(module);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ private boolean isProperty(Class clazz) {
if (scalar.equals(clazz)) return true;
}
}
return clazz.isPrimitive() || clazz.getPackage().getName().equalsIgnoreCase("java.lang");
return clazz.isPrimitive()
|| clazz.getPackage().getName().equalsIgnoreCase("java.lang")
|| clazz.getPackage().getName().equalsIgnoreCase("java.time");
}

/**
Expand Down
18 changes: 17 additions & 1 deletion nodes/src/main/java/io/aexp/nodes/graphql/GraphQLTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
package io.aexp.nodes.graphql;

import io.aexp.nodes.graphql.exceptions.GraphQLException;
import io.aexp.nodes.graphql.internal.DefaultObjectMapperFactory;

public class GraphQLTemplate {

private Fetch fetch = new Fetch();
private Fetch fetch;

public enum GraphQLMethod {
QUERY("query"),
Expand All @@ -32,6 +33,21 @@ public String getValue() {
}
}

/**
* Constructs a new GraphQL template instance using the default ObjectMapper factory.
*/
public GraphQLTemplate() {
this(new DefaultObjectMapperFactory());
}

/**
* Constructs a new GraphQL template instance using the specified ObjectMapper factory.
* @param objectMapperFactory factory class used for creating ObjectMapper instances
*/
public GraphQLTemplate(final ObjectMapperFactory objectMapperFactory) {
fetch = new Fetch(objectMapperFactory);
}

/**
* Execute a GraphQL query request.
* @param requestEntity request entity to be executed upon
Expand Down
31 changes: 31 additions & 0 deletions nodes/src/main/java/io/aexp/nodes/graphql/ObjectMapperFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018 American Express Travel Related Services Company, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

package io.aexp.nodes.graphql;

import com.fasterxml.jackson.databind.ObjectMapper;

public interface ObjectMapperFactory {

/**
* Returns a new ObjectMapper instance used for serialization.
* @return ObjectMapper instance used for serialization
*/
ObjectMapper newSerializerMapper();

/**
* Returns a new ObjectMapper instance used for deserialization.
* @return ObjectMapper instance used for deserialization
*/
ObjectMapper newDeserializerMapper();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2018 American Express Travel Related Services Company, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

package io.aexp.nodes.graphql.internal;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import io.aexp.nodes.graphql.ObjectMapperFactory;

public final class DefaultObjectMapperFactory implements ObjectMapperFactory {

public DefaultObjectMapperFactory() {
}

public ObjectMapper newSerializerMapper() {
final ObjectMapper mapper = new ObjectMapper();

// JDK8+ Time and Date handling / JSR-310
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.disable(SerializationFeature.WRITE_DATES_WITH_ZONE_ID);
mapper.disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS);

return mapper;
}

public ObjectMapper newDeserializerMapper() {
final ObjectMapper mapper = new ObjectMapper();

// JDK8+ Time and Date handling / JSR-310
mapper.registerModule(new JavaTimeModule());
mapper.disable(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS);

return mapper;
}
}
25 changes: 22 additions & 3 deletions nodes/src/test/java/io/aexp/nodes/graphql/DeserializerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@

package io.aexp.nodes.graphql;

import io.aexp.nodes.graphql.internal.DefaultObjectMapperFactory;
import io.aexp.nodes.graphql.models.TestModel;
import io.aexp.nodes.graphql.models.TestModelDateTime;
import io.aexp.nodes.graphql.models.TestModels;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
Expand All @@ -29,11 +31,13 @@

public class DeserializerTest {

private ObjectMapperFactory objectMapperFactory;
private ObjectMapper mapper;

@Before
public void setup() {
mapper = new ObjectMapper();
objectMapperFactory = new DefaultObjectMapperFactory();
mapper = objectMapperFactory.newDeserializerMapper();
}

@Test
Expand Down Expand Up @@ -68,7 +72,7 @@ public void deserializeSimple() throws IOException {
InputStream stream = new ByteArrayInputStream(json.getBytes());
JsonParser parser = mapper.getFactory().createParser(stream);
DeserializationContext ctxt = mapper.getDeserializationContext();
Deserializer deserializer = new Deserializer(TestModel.class);
Deserializer<TestModel> deserializer = new Deserializer<TestModel>(TestModel.class, objectMapperFactory);
Resource<TestModel> res = deserializer.deserialize(parser, ctxt);
assertEquals("Resource{resource=TestTO{testString='String', testByte=1, testShort=1, testInteger=1, testLong=1, testCharacter=a, testFloat=1.5, testDouble=1.5, testBoolean=true, nestedTest=NestedTest{anotherTestString='AnotherString', andAnothaOne='null'}, testArrayList=[val1, val2], testList=[NestedTest{anotherTestString='AnotherString', andAnothaOne='null'}], ignoredField='null'}}", res.toString());
}
Expand Down Expand Up @@ -128,8 +132,23 @@ public void deserializeMulti() throws IOException {
InputStream stream = new ByteArrayInputStream(json.getBytes());
JsonParser parser = mapper.getFactory().createParser(stream);
DeserializationContext ctxt = mapper.getDeserializationContext();
Deserializer deserializer = new Deserializer(TestModels.class);
Deserializer<TestModels> deserializer = new Deserializer<TestModels>(TestModels.class, objectMapperFactory);
Resource<TestModels> res = deserializer.deserialize(parser, ctxt);
assertEquals("Resource{resource=TestTOs{test1=TestTO{testString='String', testByte=1, testShort=1, testInteger=1, testLong=1, testCharacter=a, testFloat=1.5, testDouble=1.5, testBoolean=true, nestedTest=NestedTest{anotherTestString='AnotherString', andAnothaOne='null'}, testArrayList=[val1, val2], testList=[NestedTest{anotherTestString='AnotherString', andAnothaOne='null'}], ignoredField='null'}, test2=TestTO{testString='String', testByte=1, testShort=1, testInteger=1, testLong=1, testCharacter=a, testFloat=1.5, testDouble=1.5, testBoolean=true, nestedTest=NestedTest{anotherTestString='AnotherString', andAnothaOne='null'}, testArrayList=[val1, val2], testList=[NestedTest{anotherTestString='AnotherString', andAnothaOne='null'}], ignoredField='null'}}}", res.toString());
}

@Test
public void deserializeJsr310() throws IOException {
String json = (
"{\n" +
" \"dateTime\": \"2018-10-29T22:00:01+00:00\"\n" +
"}"
);
InputStream stream = new ByteArrayInputStream(json.getBytes());
JsonParser parser = mapper.getFactory().createParser(stream);
DeserializationContext ctxt = mapper.getDeserializationContext();
Deserializer<TestModelDateTime> deserializer = new Deserializer<TestModelDateTime>(TestModelDateTime.class, objectMapperFactory);
Resource<TestModelDateTime> res = deserializer.deserialize(parser, ctxt);
assertEquals("Resource{resource=TestModelDateTime{dateTime='2018-10-29T22:00:01Z'}}", res.toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2018 American Express Travel Related Services Company, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

package io.aexp.nodes.graphql.internal;

import static org.junit.Assert.assertNotNull;

import org.junit.Test;

public class DefaultObjectMapperFactoryTest {

@Test
public void defaultObjectMapperFactoryTest() {
assertNotNull(new DefaultObjectMapperFactory().newSerializerMapper());
assertNotNull(new DefaultObjectMapperFactory().newDeserializerMapper());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018 American Express Travel Related Services Company, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

package io.aexp.nodes.graphql.models;

import java.time.OffsetDateTime;

public class TestModelDateTime {
private OffsetDateTime dateTime;

public OffsetDateTime getDateTime() {
return dateTime;
}

public void setDateTime(OffsetDateTime dateTime) {
this.dateTime = dateTime;
}

@Override
public String toString() {
return "TestModelDateTime{" + "dateTime='" + dateTime + '\'' + '}';
}
}

0 comments on commit afd5fc4

Please sign in to comment.