From b9db17e1d506c3e570773a1d65852d7fd55931e9 Mon Sep 17 00:00:00 2001 From: saeron Date: Fri, 20 Nov 2020 13:19:18 +0530 Subject: [PATCH 1/3] Allowing relationship links to get serialized for null relationship object. --- .../jasminb/jsonapi/ResourceConverter.java | 48 ++++++++++--------- .../jsonapi/ResourceConverterTest.java | 41 ++++++++++++++++ .../jasminb/jsonapi/models/Article.java | 15 +++++- ...n-included-empty-to-many-relationship.json | 39 +++++++++++++++ src/test/resources/users.json | 6 +++ 5 files changed, 125 insertions(+), 24 deletions(-) create mode 100644 src/test/resources/articles-with-non-included-empty-to-many-relationship.json diff --git a/src/main/java/com/github/jasminb/jsonapi/ResourceConverter.java b/src/main/java/com/github/jasminb/jsonapi/ResourceConverter.java index 484cd3c..7d62a90 100644 --- a/src/main/java/com/github/jasminb/jsonapi/ResourceConverter.java +++ b/src/main/java/com/github/jasminb/jsonapi/ResourceConverter.java @@ -842,19 +842,36 @@ private ObjectNode getDataNode(Object object, Map includedCo removeField(attributesNode, relationshipField); - if (relationshipObject != null) { + Relationship relationship = configuration.getFieldRelationship(relationshipField); - Relationship relationship = configuration.getFieldRelationship(relationshipField); + // In case serialisation is disabled for a given relationship, skip it + if (!relationship.serialise()) { + continue; + } - // In case serialisation is disabled for a given relationship, skip it - if (!relationship.serialise()) { - continue; - } + // Serialize relationship object + ObjectNode relationshipDataNode = objectMapper.createObjectNode(); + + String relationshipName = relationship.value(); + + // Allow relationships node to have relationship data node even if relationship object is null + relationshipsNode.set(relationshipName, relationshipDataNode); + + // Serialize relationship links + JsonNode relationshipLinks = getRelationshipLinks(object, relationship, selfHref, settings); + + if (relationshipLinks != null) { + // Allow relationship data node to have links even if relationship object is null + relationshipDataNode.set(LINKS, relationshipLinks); + + // Remove link object from serialized JSON + Field refField = configuration + .getRelationshipLinksField(object.getClass(), relationshipName); - String relationshipName = relationship.value(); + removeField(attributesNode, refField); + } - ObjectNode relationshipDataNode = objectMapper.createObjectNode(); - relationshipsNode.set(relationshipName, relationshipDataNode); + if (relationshipObject != null) { // Serialize relationship meta JsonNode relationshipMeta = getRelationshipMeta(object, relationshipName, settings); @@ -868,19 +885,6 @@ private ObjectNode getDataNode(Object object, Map includedCo removeField(attributesNode, refField); } - // Serialize relationship links - JsonNode relationshipLinks = getRelationshipLinks(object, relationship, selfHref, settings); - - if (relationshipLinks != null) { - relationshipDataNode.set(LINKS, relationshipLinks); - - // Remove link object from serialized JSON - Field refField = configuration - .getRelationshipLinksField(object.getClass(), relationshipName); - - removeField(attributesNode, refField); - } - boolean shouldSerializeData = configuration.getFieldRelationship(relationshipField).serialiseData(); if (shouldSerializeData) { if (relationshipObject instanceof Collection) { diff --git a/src/test/java/com/github/jasminb/jsonapi/ResourceConverterTest.java b/src/test/java/com/github/jasminb/jsonapi/ResourceConverterTest.java index 44467c9..60a5c07 100644 --- a/src/test/java/com/github/jasminb/jsonapi/ResourceConverterTest.java +++ b/src/test/java/com/github/jasminb/jsonapi/ResourceConverterTest.java @@ -148,6 +148,47 @@ public void testWriteCollection() throws IOException, IllegalAccessException { } @Test + public void testLinksForNonIncludedEmptyToManyRelationship() throws IOException, IllegalAccessException { + InputStream apiResponse = IOUtils.getResource("articles-with-non-included-empty-to-many-relationship.json"); + + ObjectMapper articlesMapper = new ObjectMapper(); + articlesMapper.setPropertyNamingStrategy(PropertyNamingStrategy.KEBAB_CASE); + + ResourceConverter articlesConverter = new ResourceConverter(articlesMapper, Article.class, Author.class, + Comment.class); + + JSONAPIDocument> articlesDocument = articlesConverter.readDocumentCollection(apiResponse, Article.class); + List
articles = articlesDocument.get(); + + assertNotNull(articles); + assertEquals(1, articles.size()); + + Article article = articles.get(0); + + assertNull(article.getComments()); + + assertNull(article.getCommentRelationshipLinks()); + + byte[] convertedData = converter.writeObjectCollection(articles); + assertNotNull(convertedData); + assertNotEquals(0, convertedData.length); + + JSONAPIDocument> convertedDocument = converter.readDocumentCollection(new ByteArrayInputStream(convertedData), Article.class); + List
convertedArticles = convertedDocument.get(); + assertNotNull(convertedArticles); + + Article convertedArticle = convertedArticles.get(0); + + assertNull(convertedArticle.getComments()); + + // Make sure Relationship links are getting serialized even if relationship object i.e. comments is null + assertNotNull(convertedArticle.getCommentRelationshipLinks()); + assertEquals("https://api.example.com/articles/1/relationships/comments", convertedArticle.getCommentRelationshipLinks().getSelf().toString()); + assertEquals("https://api.example.com/articles/1/comments", convertedArticle.getCommentRelationshipLinks().getRelated().toString()); + + } + + @Test public void testReadWithMetaAndLinksSection() throws IOException { InputStream apiResponse = IOUtils.getResource("user-with-meta.json"); diff --git a/src/test/java/com/github/jasminb/jsonapi/models/Article.java b/src/test/java/com/github/jasminb/jsonapi/models/Article.java index c0aa071..13d5030 100644 --- a/src/test/java/com/github/jasminb/jsonapi/models/Article.java +++ b/src/test/java/com/github/jasminb/jsonapi/models/Article.java @@ -13,7 +13,7 @@ import java.util.Collections; import java.util.List; -@Type("articles") +@Type(value = "articles", path = "/articles/{id}") @JsonIdentityInfo(generator = ObjectIdGenerators.StringIdGenerator.class, property = "id") public class Article { @Id @@ -24,9 +24,12 @@ public class Article { @Relationship(value = "author", resolve = true, relType = RelType.RELATED) private Author author; - @Relationship(value = "comments", resolve = true) + @Relationship(value = "comments", resolve = true, path = "relationships/comments", relatedPath = "comments") private List comments; + @RelationshipLinks(value = "comments") + private Links commentRelationshipLinks; + @Relationship(value = "users", serialiseData = false) private List users; @@ -84,4 +87,12 @@ public Links getUserRelationshipLinks() { public void setUserRelationshipLinks(Links userRelationshipLinks) { this.userRelationshipLinks = userRelationshipLinks; } + + public Links getCommentRelationshipLinks() { + return commentRelationshipLinks; + } + + public void setCommentRelationshipLinks(Links commentRelationshipLinks) { + this.commentRelationshipLinks = commentRelationshipLinks; + } } diff --git a/src/test/resources/articles-with-non-included-empty-to-many-relationship.json b/src/test/resources/articles-with-non-included-empty-to-many-relationship.json new file mode 100644 index 0000000..ee1dbe1 --- /dev/null +++ b/src/test/resources/articles-with-non-included-empty-to-many-relationship.json @@ -0,0 +1,39 @@ +{ + "data": [{ + "type": "articles", + "id": "1", + "attributes": { + "title": "JSON API paints my bikeshed!" + }, + "links": { + "self": "http://example.com/articles/1" + }, + "relationships": { + "author": { + "links": { + "self": "http://example.com/articles/1/relationships/author", + "related": "http://example.com/articles/1/author" + }, + "data": { "type": "people", "id": "9" } + }, + "users": { + "links": { + "self": "http://example.com/articles/1/relationships/users", + "related": "http://example.com/articles/1/users" + } + } + } + }], + "included": [{ + "type": "people", + "id": "9", + "attributes": { + "first-name": "Dan", + "last-name": "Gebhardt", + "twitter": "dgeb" + }, + "links": { + "self": "http://example.com/people/9" + } + }] +} diff --git a/src/test/resources/users.json b/src/test/resources/users.json index 48b73bd..bf187c6 100644 --- a/src/test/resources/users.json +++ b/src/test/resources/users.json @@ -5,6 +5,9 @@ "id": "1", "attributes": { "name": "liz" + }, + "relationships": { + "statuses": {} } }, { @@ -12,6 +15,9 @@ "id": "2", "attributes": { "name": "john" + }, + "relationships": { + "statuses": {} } } ] From cd98cbdc736a49db37ed532f9368a183936109a3 Mon Sep 17 00:00:00 2001 From: saeron Date: Fri, 20 Nov 2020 22:35:09 +0530 Subject: [PATCH 2/3] Updated testWriteCollection to evaluate relationship node serialization --- .../jsonapi/ResourceConverterTest.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/test/java/com/github/jasminb/jsonapi/ResourceConverterTest.java b/src/test/java/com/github/jasminb/jsonapi/ResourceConverterTest.java index 60a5c07..928c5dd 100644 --- a/src/test/java/com/github/jasminb/jsonapi/ResourceConverterTest.java +++ b/src/test/java/com/github/jasminb/jsonapi/ResourceConverterTest.java @@ -127,6 +127,13 @@ public void testWriteCollection() throws IOException, IllegalAccessException { JSONAPIDocument> usersDocument = converter.readDocumentCollection(usersRequest, User.class); List users = usersDocument.get(); + + assertNotNull(users); + assertEquals(2, users.size()); + + // Make sure that relationship object i.e. statuses is null + assertNull(users.get(0).getStatuses()); + byte[] convertedData = converter.writeObjectCollection(users); assertNotNull(convertedData); @@ -140,7 +147,21 @@ public void testWriteCollection() throws IOException, IllegalAccessException { ObjectMapper mapper = new ObjectMapper(); try { JsonNode node1 = mapper.readTree(IOUtils.getResource("users.json")); + + // Make sure relationship node always get serialized even if relationship object i.e. statuses is null + JsonNode user1Relationships = node1.get("data").get(0).get("relationships"); + assertNotNull(user1Relationships.get("statuses")); + + JsonNode user2Relationships = node1.get("data").get(1).get("relationships"); + assertNotNull(user2Relationships.get("statuses")); + JsonNode node2 = mapper.readTree(convertedData); + user1Relationships = node2.get("data").get(0).get("relationships"); + assertNotNull(user1Relationships.get("statuses")); + + user2Relationships = node2.get("data").get(1).get("relationships"); + assertNotNull(user2Relationships.get("statuses")); + assertEquals(node1, node2); } catch (IOException e) { throw new RuntimeException("Unable to read json, make sure is correct", e); From b0a724757017ede618bfe26c6cce5f0b4c8cbdec Mon Sep 17 00:00:00 2001 From: saeron Date: Mon, 11 Jan 2021 15:25:34 +0530 Subject: [PATCH 3/3] Corrected testWriteCollection to must have atleast one out of meta, links and data in relationship node. --- .../github/jasminb/jsonapi/ResourceConverterTest.java | 10 +++++++--- .../com/github/jasminb/jsonapi/SerializationTest.java | 1 + .../java/com/github/jasminb/jsonapi/models/User.java | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/github/jasminb/jsonapi/ResourceConverterTest.java b/src/test/java/com/github/jasminb/jsonapi/ResourceConverterTest.java index 928c5dd..f47382c 100644 --- a/src/test/java/com/github/jasminb/jsonapi/ResourceConverterTest.java +++ b/src/test/java/com/github/jasminb/jsonapi/ResourceConverterTest.java @@ -122,7 +122,7 @@ public void testReadWithIncludedSection() throws IOException { } @Test - public void testWriteCollection() throws IOException, IllegalAccessException { + public void testWriteCollection() throws DocumentSerializationException, IOException { InputStream usersRequest = IOUtils.getResource("users.json"); JSONAPIDocument> usersDocument = converter.readDocumentCollection(usersRequest, User.class); @@ -134,7 +134,7 @@ public void testWriteCollection() throws IOException, IllegalAccessException { // Make sure that relationship object i.e. statuses is null assertNull(users.get(0).getStatuses()); - byte[] convertedData = converter.writeObjectCollection(users); + byte[] convertedData = converter.writeDocumentCollection(usersDocument); assertNotNull(convertedData); assertFalse(convertedData.length == 0); @@ -156,13 +156,17 @@ public void testWriteCollection() throws IOException, IllegalAccessException { assertNotNull(user2Relationships.get("statuses")); JsonNode node2 = mapper.readTree(convertedData); + + // Make sure relationship node must always contains one of either meta, link or data node user1Relationships = node2.get("data").get(0).get("relationships"); assertNotNull(user1Relationships.get("statuses")); + assertNotNull(user1Relationships.get("statuses").get("links")); user2Relationships = node2.get("data").get(1).get("relationships"); assertNotNull(user2Relationships.get("statuses")); + assertNotNull(user2Relationships.get("statuses").get("links")); - assertEquals(node1, node2); + assertNotEquals(node1, node2); } catch (IOException e) { throw new RuntimeException("Unable to read json, make sure is correct", e); } diff --git a/src/test/java/com/github/jasminb/jsonapi/SerializationTest.java b/src/test/java/com/github/jasminb/jsonapi/SerializationTest.java index 33d28a4..8fbed5e 100644 --- a/src/test/java/com/github/jasminb/jsonapi/SerializationTest.java +++ b/src/test/java/com/github/jasminb/jsonapi/SerializationTest.java @@ -187,6 +187,7 @@ public void testSerializeWithoutId() throws DocumentSerializationException { User user = new User(); user.setName("Name"); + converter.disableSerializationOption(SerializationFeature.INCLUDE_LINKS); byte [] data = converter.writeDocument(new JSONAPIDocument<>(user)); Assert.assertTrue(new String(data).contains(user.getName())); diff --git a/src/test/java/com/github/jasminb/jsonapi/models/User.java b/src/test/java/com/github/jasminb/jsonapi/models/User.java index 8aafb6e..efc9847 100644 --- a/src/test/java/com/github/jasminb/jsonapi/models/User.java +++ b/src/test/java/com/github/jasminb/jsonapi/models/User.java @@ -10,7 +10,7 @@ import java.util.List; -@Type("users") +@Type(value = "users", path = "/users/{id}") @JsonIdentityInfo(generator = ObjectIdGenerators.StringIdGenerator.class, property = "id") public class User { @@ -26,7 +26,7 @@ public String getToken() { public String id; public String name; - @Relationship("statuses") + @Relationship(value = "statuses", path = "/relationship/statuses") private List statuses; @Meta