From ad6e068e69f834f3bc44e91cade915475fd6bf19 Mon Sep 17 00:00:00 2001 From: Nathan Fischer Date: Fri, 17 Jun 2016 22:43:17 -0700 Subject: [PATCH] Addressed #16 Changed optional fields in HalLink to use java Optional. Added HalLinkSerializer to fix odd issue where Optional is not serialized correctly despite jackson module. Made indentation uniform --- README.md | 2 +- pom.xml | 2 +- src/main/java/black/door/hate/HalLink.java | 67 +- .../black/door/hate/HalRepresentation.java | 25 +- .../java/black/door/hate/HalLinkTest.java | 25 +- .../door/hate/HalRepresentationTest.java | 621 +++++++++--------- .../java/black/door/hate/example/Order.java | 2 +- 7 files changed, 401 insertions(+), 343 deletions(-) diff --git a/README.md b/README.md index d5ae3c5..01309a7 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ More info in the [wiki](https://github.com/blackdoor/hate/wiki). black.door hate - v1r3t2 + v1r4t0 ``` diff --git a/pom.xml b/pom.xml index 64e575a..95cec0d 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ black.door hate - v1r3t2 + v1r4t0 diff --git a/src/main/java/black/door/hate/HalLink.java b/src/main/java/black/door/hate/HalLink.java index 0ab72b0..4ef318d 100644 --- a/src/main/java/black/door/hate/HalLink.java +++ b/src/main/java/black/door/hate/HalLink.java @@ -4,9 +4,14 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; import lombok.*; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -18,31 +23,31 @@ */ @Getter @EqualsAndHashCode -@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonSerialize(using = HalLink.HalLinkSerializer.class) public class HalLink implements LinkOrResource{ private @NonNull final String href; @Getter(AccessLevel.NONE) @JsonProperty private final Boolean templated; - - private final String type; - private final URL deprecation; - private final String name; - private final URI profile; - private final String title; - private final String hreflang; + private final Optional type; + private final Optional deprecation; + private final Optional name; + private final Optional profile; + private final Optional title; + private final Optional hreflang; @java.beans.ConstructorProperties({"href", "templated", "type", "deprecation", "name", "profile", "title", "hreflang"}) HalLink(String href, Boolean templated, String type, URL deprecation, String name, URI profile, String title, String hreflang) { this.href = href; this.templated = templated; - this.type = type; - this.deprecation = deprecation; - this.name = name; - this.profile = profile; - this.title = title; - this.hreflang = hreflang; + this.type = Optional.ofNullable(type); + this.deprecation = Optional.ofNullable(deprecation); + this.name = Optional.ofNullable(name); + this.profile = Optional.ofNullable(profile); + this.title = Optional.ofNullable(title); + this.hreflang = Optional.ofNullable(hreflang); } public static HalLinkBuilder builder() { @@ -143,4 +148,38 @@ public HalLink build() { } } + + public static class HalLinkSerializer extends StdSerializer { + + protected HalLinkSerializer() { + this(HalLink.class); + } + + protected HalLinkSerializer(Class clazz) { + super(clazz); + } + + @Override + public void serialize(HalLink value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeStartObject(); + + provider.defaultSerializeField("href", value.href, gen); + if(value.templated != null) + provider.defaultSerializeField("templated", value.templated, gen); + if(value.type.isPresent()) + provider.defaultSerializeField("type", value.type, gen); + if(value.deprecation.isPresent()) + provider.defaultSerializeField("deprecation", value.deprecation, gen); + if(value.name.isPresent()) + provider.defaultSerializeField("name", value.name, gen); + if(value.profile.isPresent()) + provider.defaultSerializeField("profile", value.profile, gen); + if(value.title.isPresent()) + provider.defaultSerializeField("title", value.title, gen); + if(value.hreflang.isPresent()) + provider.defaultSerializeField("hreflang", value.hreflang, gen); + + gen.writeEndObject(); + } + } } diff --git a/src/main/java/black/door/hate/HalRepresentation.java b/src/main/java/black/door/hate/HalRepresentation.java index c8e504c..b2914c3 100644 --- a/src/main/java/black/door/hate/HalRepresentation.java +++ b/src/main/java/black/door/hate/HalRepresentation.java @@ -23,6 +23,7 @@ import static black.door.hate.Constants.*; import static black.door.util.Misc.require; import static java.util.Map.Entry; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; @@ -55,17 +56,12 @@ public class HalRepresentation implements java.io.Serializable { Map embedded, Map> multiEmbedded, Map properties) { - require(null != links); - require(null != multiLinks); - require(null != embedded); - require(null != multiEmbedded); - require(null != properties); - - this.links = links; - this.multiLinks = multiLinks; - this.embedded = embedded; - this.multiEmbedded = multiEmbedded; - this.properties = properties; + + this.links = requireNonNull(links); + this.multiLinks = requireNonNull(multiLinks); + this.embedded = requireNonNull(embedded); + this.multiEmbedded = requireNonNull(multiEmbedded); + this.properties = requireNonNull(properties); } static ObjectMapper getMapper(){ @@ -130,8 +126,8 @@ protected HalRepresentationSerializer(Class t) { @Override public void serialize(HalRepresentation halRepresentation, - JsonGenerator jsonGenerator, - SerializerProvider serializerProvider) + JsonGenerator jsonGenerator, + SerializerProvider serializerProvider) throws IOException{ jsonGenerator.writeStartObject(); @@ -171,6 +167,7 @@ public void serialize(HalRepresentation halRepresentation, .collect(toList()) )); + //put all embedded resources and collections of embedded resources into one object Map embedded = new HashMap<>(); embedded.putAll(embeddz); @@ -266,7 +263,7 @@ public HalRepresentationBuilder addProperties(JsonNode jax){ * @param multiRs */ private void add(String name, T res, Map rs, - Map> multiRs){ + Map> multiRs){ if(res == null && ignoreNullResources) return; diff --git a/src/test/java/black/door/hate/HalLinkTest.java b/src/test/java/black/door/hate/HalLinkTest.java index db96d99..7adab11 100644 --- a/src/test/java/black/door/hate/HalLinkTest.java +++ b/src/test/java/black/door/hate/HalLinkTest.java @@ -1,11 +1,15 @@ package black.door.hate; import com.damnhandy.uri.template.UriTemplate; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.val; import org.junit.Test; +import java.net.MalformedURLException; import java.net.URI; +import java.net.URL; import static org.junit.Assert.*; @@ -14,13 +18,30 @@ */ public class HalLinkTest { + private ObjectMapper mapper = new ObjectMapper() + .findAndRegisterModules(); + @Test - public void testSerialization(){ + public void testSerialization() throws Exception { val link = HalLink.builder() .href(UriTemplate.fromTemplate("/~{username}")) .build(); - assertTrue(new ObjectMapper().valueToTree(link).get("templated").asBoolean()); + JsonNode node = mapper.valueToTree(link); + assertTrue(node.get("templated").asBoolean()); + assertFalse(node.has("name")); + System.out.println(mapper.writeValueAsString(link)); + + val link2 = HalLink.builder() + .href(URI.create("/path")) + .deprecation(new URL("https://google.com")) + .hreflang("eng") + .name("link") + .profile(URI.create("thing.black")) + .title("title") + .build(); + + System.out.println(mapper.writeValueAsString(link2)); } @Test diff --git a/src/test/java/black/door/hate/HalRepresentationTest.java b/src/test/java/black/door/hate/HalRepresentationTest.java index 58db0e5..2cdbd81 100644 --- a/src/test/java/black/door/hate/HalRepresentationTest.java +++ b/src/test/java/black/door/hate/HalRepresentationTest.java @@ -13,6 +13,7 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import lombok.Data; import lombok.SneakyThrows; @@ -38,316 +39,316 @@ */ public class HalRepresentationTest { - private static final String RFC = "{\n" + - " \"_links\": {\n" + - " \"self\": { \"href\": \"/orders\" },\n" + - " \"next\": { \"href\": \"/orders?page=2\" }\n" + - " },\n" + - " \"_embedded\": {\n" + - " \"orders\": [{\n" + - " \"_links\": {\n" + - " \"self\": { \"href\": \"/orders/123\" },\n" + - " \"basket\": { \"href\": \"/baskets/98712\" },\n" + - " \"customer\": { \"href\": \"/customers/7809\" }\n" + - " },\n" + - " \"total\": 30.00,\n" + - " \"currency\": \"USD\",\n" + - " \"status\": \"shipped\"\n" + - " },{\n" + - " \"_links\": {\n" + - " \"self\": { \"href\": \"/orders/124\" },\n" + - " \"basket\": { \"href\": \"/baskets/97213\" },\n" + - " \"customer\": { \"href\": \"/customers/12369\" }\n" + - " },\n" + - " \"total\": 20.00,\n" + - " \"currency\": \"USD\",\n" + - " \"status\": \"processing\"\n" + - " }]\n" + - " },\n" + - " \"currentlyProcessing\": 14,\n" + - " \"shippedToday\": 20\n" + - " }"; - - @Test - public void testPagination() throws Exception{ - List orders = new LinkedList<>(); - - IntStream.range(0, 100) - .forEach(i -> - orders.add(new Order(i, i, "USD", "status", - new Basket(i), new Customer(i))) - ); - - val rep = HalRepresentation.paginated("orders", "/orders", orders.stream() - , 2, 20) - .build(); - - assertEquals(20, rep.getMultiEmbedded().get("orders").size()); - assertEquals("/orders/20", - rep.getMultiEmbedded().get("orders").get(0).asEmbedded().getLinks().get("self").asLink() - .getHref()); - } - - @Test - public void testLinkToString() throws JsonProcessingException { - Order o = new Order(1, 1, "USD", "status", new Basket(2), new Customer(3)); - - HalLink link = o.asLink(); - System.out.println(link.toString()); - assertTrue(link.toString().length() > 0); - assertTrue(o.asEmbedded().serialize().contains(link.toString())); - } - - @Test - public void testExpand() throws JsonProcessingException, URISyntaxException { - Order o = new Order(1, 1, "USD", "status", new Basket(2), new Customer(3)); - - val builder = o.representationBuilder(); - builder.addLink("z", new Basket(1)) - .addLink("z", new Basket(5)); - assertTrue(builder.build().getLinks().containsKey("basket")); - builder.expand("basket"); - assertFalse(builder.build().getLinks().containsKey("basket")); - assertTrue(o.asEmbedded("basket").getEmbedded().containsKey("basket")); - System.out.println(o.asEmbedded("basket").serialize()); - - builder.expand("basket"); - - try{ - builder.expand("shoe"); - fail(); - }catch (NoSuchElementException e){} - - builder.addLink("cars", new URI("/cars")); - - try{ - builder.expand("cars"); - fail(); - }catch (CannotEmbedLinkException e){} - - builder.expand("z"); - - assertFalse(builder.build().getMultiLinks().containsKey("z")); - assertTrue(builder.build().getMultiEmbedded().containsKey("z")); - } - - @Test - public void testNulls() throws Exception{ - val basket1 = new Basket(98712); - val cust2 = new Customer(12369); - - val order1 = new Order(123, 30.0, null, "shipped", basket1, cust2); - val order2 = new Order(124, 20, "USD", "processing", basket1, cust2); - - List n = null; - HalResource n2 = null; - - val orderz = HalRepresentation.paginated( - "orders", "/orders", list(order1, order2).stream(), 0, 2) - .addProperty("currentlyProcessing", 14) - .addProperty("shippedToday", 20) - .addEmbedded("n", n) - .build(); - - try { - HalRepresentation.builder().addEmbedded("n", n2).build().serialize(); - fail(); - }catch (IllegalArgumentException e){ - - } - - HalRepresentation.builder().ignoreNullResources(true).addEmbedded("n", n2).build().serialize(); - - System.out.println(orderz.serialize()); - } - - @Test - public void testIgnoreNullProp(){ - HalRepresentation rep = HalRepresentation.builder() - .ignoreNullProperties(true) - .addProperty("thing", "value") - .addProperty("nullthing", null) - .build(); - assertFalse(rep.getProperties().containsKey("nullthing")); - assertTrue(rep.getProperties().containsKey("thing")); - - HalRepresentation rep2 = HalRepresentation.builder() - .addProperty("thing", "value") - .addProperty("nullthing", null) - .build(); - assertTrue(rep2.getProperties().containsKey("nullthing")); - assertTrue(rep2.getProperties().containsKey("thing")); - } - - - @Test - public void testSerialize() throws Exception { - val basket1 = new Basket(98712); - val basket2 = new Basket(97213); - val cust1 = new Customer(7809); - val cust2 = new Customer(12369); - - val order1 = new Order(123, 30.0, "USD", "shipped", basket1, cust1); - val order2 = new Order(124, 20, "USD", "processing", basket2, cust2); - - val mapper = new ObjectMapper(); - - val orderz = HalRepresentation.paginated( - "orders", "/orders", list(order1, order2).stream(), 0, 2) - .addProperty("currentlyProcessing", 14) - .addProperty("shippedToday", 20) - .build(); - - val mine = (ObjectNode) mapper.readTree(orderz.serialize()); - val theirs = (ObjectNode) mapper.readTree(RFC); - - assertEquals(theirs, mine); - } - - @Data - private static class TimeBox implements JacksonHalResource{ - LocalDate start = LocalDate.MIN; - - LocalDateTime stuff = null; - - @JsonIgnore - LocalDate end = LocalDate.now(); + private static final String RFC = "{\n" + + " \"_links\": {\n" + + " \"self\": { \"href\": \"/orders\" },\n" + + " \"next\": { \"href\": \"/orders?page=2\" }\n" + + " },\n" + + " \"_embedded\": {\n" + + " \"orders\": [{\n" + + " \"_links\": {\n" + + " \"self\": { \"href\": \"/orders/123\" },\n" + + " \"basket\": { \"href\": \"/baskets/98712\" },\n" + + " \"customer\": { \"href\": \"/customers/7809\" }\n" + + " },\n" + + " \"total\": 30.00,\n" + + " \"currency\": \"USD\",\n" + + " \"status\": \"shipped\"\n" + + " },{\n" + + " \"_links\": {\n" + + " \"self\": { \"href\": \"/orders/124\" },\n" + + " \"basket\": { \"href\": \"/baskets/97213\" },\n" + + " \"customer\": { \"href\": \"/customers/12369\" }\n" + + " },\n" + + " \"total\": 20.00,\n" + + " \"currency\": \"USD\",\n" + + " \"status\": \"processing\"\n" + + " }]\n" + + " },\n" + + " \"currentlyProcessing\": 14,\n" + + " \"shippedToday\": 20\n" + + " }"; + + @Test + public void testPagination() throws Exception{ + List orders = new LinkedList<>(); + + IntStream.range(0, 100) + .forEach(i -> + orders.add(new Order(i, i, "USD", "status", + new Basket(i), new Customer(i))) + ); + + val rep = HalRepresentation.paginated("orders", "/orders", orders.stream() + , 2, 20) + .build(); + + assertEquals(20, rep.getMultiEmbedded().get("orders").size()); + assertEquals("/orders/20", + rep.getMultiEmbedded().get("orders").get(0).asEmbedded().getLinks().get("self").asLink() + .getHref()); + } + + @Test + public void testLinkToString() throws JsonProcessingException { + Order o = new Order(1, 1, "USD", "status", new Basket(2), new Customer(3)); + + HalLink link = o.asLink(); + System.out.println(link.toString()); + assertTrue(link.toString().length() > 0); + assertTrue(o.asEmbedded().serialize().contains(link.toString())); + } + + @Test + public void testExpand() throws JsonProcessingException, URISyntaxException { + Order o = new Order(1, 1, "USD", "status", new Basket(2), new Customer(3)); + + val builder = o.representationBuilder(); + builder.addLink("z", new Basket(1)) + .addLink("z", new Basket(5)); + assertTrue(builder.build().getLinks().containsKey("basket")); + builder.expand("basket"); + assertFalse(builder.build().getLinks().containsKey("basket")); + assertTrue(o.asEmbedded("basket").getEmbedded().containsKey("basket")); + System.out.println(o.asEmbedded("basket").serialize()); + + builder.expand("basket"); + + try{ + builder.expand("shoe"); + fail(); + }catch (NoSuchElementException e){} + + builder.addLink("cars", new URI("/cars")); + + try{ + builder.expand("cars"); + fail(); + }catch (CannotEmbedLinkException e){} + + builder.expand("z"); + + assertFalse(builder.build().getMultiLinks().containsKey("z")); + assertTrue(builder.build().getMultiEmbedded().containsKey("z")); + } + + @Test + public void testNulls() throws Exception{ + val basket1 = new Basket(98712); + val cust2 = new Customer(12369); + + val order1 = new Order(123, 30.0, null, "shipped", basket1, cust2); + val order2 = new Order(124, 20, "USD", "processing", basket1, cust2); + + List n = null; + HalResource n2 = null; + + val orderz = HalRepresentation.paginated( + "orders", "/orders", list(order1, order2).stream(), 0, 2) + .addProperty("currentlyProcessing", 14) + .addProperty("shippedToday", 20) + .addEmbedded("n", n) + .build(); + + try { + HalRepresentation.builder().addEmbedded("n", n2).build().serialize(); + fail(); + }catch (IllegalArgumentException e){ + + } + + HalRepresentation.builder().ignoreNullResources(true).addEmbedded("n", n2).build().serialize(); + + System.out.println(orderz.serialize()); + } + + @Test + public void testIgnoreNullProp(){ + HalRepresentation rep = HalRepresentation.builder() + .ignoreNullProperties(true) + .addProperty("thing", "value") + .addProperty("nullthing", null) + .build(); + assertFalse(rep.getProperties().containsKey("nullthing")); + assertTrue(rep.getProperties().containsKey("thing")); + + HalRepresentation rep2 = HalRepresentation.builder() + .addProperty("thing", "value") + .addProperty("nullthing", null) + .build(); + assertTrue(rep2.getProperties().containsKey("nullthing")); + assertTrue(rep2.getProperties().containsKey("thing")); + } + + + @Test + public void testSerialize() throws Exception { + val basket1 = new Basket(98712); + val basket2 = new Basket(97213); + val cust1 = new Customer(7809); + val cust2 = new Customer(12369); + + val order1 = new Order(123, 30.0, "USD", "shipped", basket1, cust1); + val order2 = new Order(124, 20, "USD", "processing", basket2, cust2); + + val mapper = new ObjectMapper(); + + val orderz = HalRepresentation.paginated( + "orders", "/orders", list(order1, order2).stream(), 0, 2) + .addProperty("currentlyProcessing", 14) + .addProperty("shippedToday", 20) + .build(); + + val mine = (ObjectNode) mapper.readTree(orderz.serialize()); + val theirs = (ObjectNode) mapper.readTree(RFC); + + assertEquals(theirs, mine); + } + + @Data + private static class TimeBox implements JacksonHalResource{ + LocalDate start = LocalDate.MIN; + + LocalDateTime stuff = null; + + @JsonIgnore + LocalDate end = LocalDate.now(); - public static class OtherThing{} - - OtherThing other = new OtherThing(); - - @Override - @SneakyThrows - public URI location() { - return new URI("/hi"); - } - } - - private static class HelloSerializer extends StdSerializer { - - public static final String HELLO = "Hello World."; - - protected HelloSerializer(Class t) { - super(t); - } - - @Override - public void serialize(TimeBox.OtherThing o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { - jsonGenerator.writeString(HELLO); - } - } - - @Test - public void testSerializer() throws IOException { - ObjectMapper mapper = new ObjectMapper() - .registerModule(new JavaTimeModule()); - - TimeBox box = new TimeBox(); - - val mod = new SimpleModule("mod", new Version(1,1,1,null)); - mod.addSerializer(new HelloSerializer(TimeBox.OtherThing.class)); - mapper.registerModule(mod); - - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - - System.out.println(mapper.writeValueAsString(box)); - final JsonNode node = mapper.valueToTree(box.asEmbedded(mapper)); - System.out.println(node); - System.out.println(box.asEmbedded(mapper).serialize()); - - assertTrue(node.get("other").asText().equals(HelloSerializer.HELLO)); - assertFalse(node.has("end")); - assertFalse(node.has("stuff")); - assertFalse(node.get("start").isArray()); - assertTrue(node.get("start").isTextual()); - assertEquals(LocalDate.MIN.toString(), node.get("start").asText()); - - HalRepresentation.useMapper(mapper); - final JsonNode node2 = new ObjectMapper().readTree(box.asEmbedded().serialize()); - - assertTrue(node2.get("other").asText().equals(HelloSerializer.HELLO)); - assertFalse(node2.has("end")); - assertFalse(node2.has("stuff")); - assertFalse(node2.get("start").isArray()); - assertTrue(node2.get("start").isTextual()); - assertEquals(LocalDate.MIN.toString(), node2.get("start").asText()); - } - - @Test - public void testNoLinks() throws IOException { - val basket1 = new Basket(98712); - val cust1 = new Customer(7809); - - val order1 = new Order(123, 30.0, "USD", "shipped", basket1, cust1); - - HalRepresentation rep = HalRepresentation.builder() - .addEmbedded("order1", order1) - .addEmbedded("order1", asList(order1)) - .addProperty("prop", 5) - .build(); - - assertFalse(new ObjectMapper().readTree(rep.serialize()).has("_links")); - } - - @Test - public void testNoEmbed() throws Exception { - val basket1 = new Basket(98712); - val cust1 = new Customer(7809); - - val order1 = new Order(123, 30.0, "USD", "shipped", basket1, cust1); - - HalRepresentation rep = HalRepresentation.builder() - .addLink("order1", order1) - .addLink("order1", asList(order1)) - .addProperty("prop", 5) - .build(); - - assertFalse(new ObjectMapper().readTree(rep.toString()).has("_embedded")); - } - - @Test - public void testAddMulti(){ - Collection orders = new LinkedList<>(); - Order straggler = new Order(999, 999, "adf", "asdf", new Basket(999), new Customer(999)); - Order straggler2 = new Order(998, 998, "adf", "asdf", new Basket(998), new Customer(998)); - - IntStream.range(0, 100) - .forEach(i -> - orders.add(new Order(i, i, "USD", "status", - new Basket(i), new Customer(i))) - ); - - val rep = HalRepresentation.builder() - .addEmbedded("orders", straggler) - .addEmbedded("orders", orders) - .addEmbedded("orders", straggler2) - .addLink("orders", straggler) - .addLink("orders", orders) - .addLink("orders", straggler2) - .build(); - - assertEquals(orders.size() +2, rep.getMultiEmbedded().get("orders").size()); - assertEquals(orders.size() + 2, rep.getMultiLinks().get("orders").size()); - } - - @Test - public void testNoProperties() throws Exception{ - val basket1 = new Basket(98712); - val cust1 = new Customer(7809); - - val order1 = new Order(123, 30.0, "USD", "shipped", basket1, cust1); - - HalRepresentation rep = HalRepresentation.builder() - .addLink("order1", order1) - .addLink("order1", asList(order1)) - .addEmbedded("order2", order1) - .build(); - val node = new JSONObject(rep.serialize()); - assertEquals(2, node.keySet().size()); - assertTrue(node.has("_embedded")); - assertTrue(node.has("_links")); - } + public static class OtherThing{} + + OtherThing other = new OtherThing(); + + @Override + @SneakyThrows + public URI location() { + return new URI("/hi"); + } + } + + private static class HelloSerializer extends StdSerializer { + + public static final String HELLO = "Hello World."; + + protected HelloSerializer(Class t) { + super(t); + } + + @Override + public void serialize(TimeBox.OtherThing o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { + jsonGenerator.writeString(HELLO); + } + } + + @Test + public void testSerializer() throws IOException { + ObjectMapper mapper = new ObjectMapper() + .registerModule(new JavaTimeModule()); + + TimeBox box = new TimeBox(); + + val mod = new SimpleModule("mod"); + mod.addSerializer(new HelloSerializer(TimeBox.OtherThing.class)); + mapper.registerModule(mod); + + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + System.out.println(mapper.writeValueAsString(box)); + final JsonNode node = mapper.valueToTree(box.asEmbedded(mapper)); + System.out.println(node); + System.out.println(box.asEmbedded(mapper).serialize()); + + assertTrue(node.get("other").asText().equals(HelloSerializer.HELLO)); + assertFalse(node.has("end")); + assertFalse(node.has("stuff")); + assertFalse(node.get("start").isArray()); + assertTrue(node.get("start").isTextual()); + assertEquals(LocalDate.MIN.toString(), node.get("start").asText()); + + HalRepresentation.useMapper(mapper); + final JsonNode node2 = new ObjectMapper().readTree(box.asEmbedded().serialize()); + + assertTrue(node2.get("other").asText().equals(HelloSerializer.HELLO)); + assertFalse(node2.has("end")); + assertFalse(node2.has("stuff")); + assertFalse(node2.get("start").isArray()); + assertTrue(node2.get("start").isTextual()); + assertEquals(LocalDate.MIN.toString(), node2.get("start").asText()); + } + + @Test + public void testNoLinks() throws IOException { + val basket1 = new Basket(98712); + val cust1 = new Customer(7809); + + val order1 = new Order(123, 30.0, "USD", "shipped", basket1, cust1); + + HalRepresentation rep = HalRepresentation.builder() + .addEmbedded("order1", order1) + .addEmbedded("order1", asList(order1)) + .addProperty("prop", 5) + .build(); + + assertFalse(new ObjectMapper().readTree(rep.serialize()).has("_links")); + } + + @Test + public void testNoEmbed() throws Exception { + val basket1 = new Basket(98712); + val cust1 = new Customer(7809); + + val order1 = new Order(123, 30.0, "USD", "shipped", basket1, cust1); + + HalRepresentation rep = HalRepresentation.builder() + .addLink("order1", order1) + .addLink("order1", asList(order1)) + .addProperty("prop", 5) + .build(); + + assertFalse(new ObjectMapper().readTree(rep.toString()).has("_embedded")); + } + + @Test + public void testAddMulti(){ + Collection orders = new LinkedList<>(); + Order straggler = new Order(999, 999, "adf", "asdf", new Basket(999), new Customer(999)); + Order straggler2 = new Order(998, 998, "adf", "asdf", new Basket(998), new Customer(998)); + + IntStream.range(0, 100) + .forEach(i -> + orders.add(new Order(i, i, "USD", "status", + new Basket(i), new Customer(i))) + ); + + val rep = HalRepresentation.builder() + .addEmbedded("orders", straggler) + .addEmbedded("orders", orders) + .addEmbedded("orders", straggler2) + .addLink("orders", straggler) + .addLink("orders", orders) + .addLink("orders", straggler2) + .build(); + + assertEquals(orders.size() +2, rep.getMultiEmbedded().get("orders").size()); + assertEquals(orders.size() + 2, rep.getMultiLinks().get("orders").size()); + } + + @Test + public void testNoProperties() throws Exception{ + val basket1 = new Basket(98712); + val cust1 = new Customer(7809); + + val order1 = new Order(123, 30.0, "USD", "shipped", basket1, cust1); + + HalRepresentation rep = HalRepresentation.builder() + .addLink("order1", order1) + .addLink("order1", asList(order1)) + .addEmbedded("order2", order1) + .build(); + val node = new JSONObject(rep.serialize()); + assertEquals(2, node.keySet().size()); + assertTrue(node.has("_embedded")); + assertTrue(node.has("_links")); + } } \ No newline at end of file diff --git a/src/test/java/black/door/hate/example/Order.java b/src/test/java/black/door/hate/example/Order.java index f4e56de..3289d3e 100644 --- a/src/test/java/black/door/hate/example/Order.java +++ b/src/test/java/black/door/hate/example/Order.java @@ -19,7 +19,7 @@ public class Order extends Thing implements JacksonHalResource{ private Customer customer; public Order(long id, double total, String currency, String status, - Basket basket, Customer customer) { + Basket basket, Customer customer) { super(id); this.total = total; this.currency = currency;