From 020e1a5c64314931e43151638fc0fe91316d4eb0 Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Mon, 7 Apr 2025 14:08:21 +0530 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8feat:=20Add=20POJO=20support=20for?= =?UTF-8?q?=20Entry=20and=20EntryListResponse,=20including=20fetch=20metho?= =?UTF-8?q?ds=20in=20EntryService?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/contentstack/cms/stack/Entry.java | 10 ++++ .../cms/stack/EntryListResponse.java | 46 ++++++++++++++++ .../com/contentstack/cms/stack/EntryPojo.java | 52 +++++++++++++++++++ .../contentstack/cms/stack/EntryResponse.java | 44 ++++++++++++++++ .../contentstack/cms/stack/EntryService.java | 21 ++++++++ 5 files changed, 173 insertions(+) create mode 100644 src/main/java/com/contentstack/cms/stack/EntryListResponse.java create mode 100644 src/main/java/com/contentstack/cms/stack/EntryPojo.java create mode 100644 src/main/java/com/contentstack/cms/stack/EntryResponse.java diff --git a/src/main/java/com/contentstack/cms/stack/Entry.java b/src/main/java/com/contentstack/cms/stack/Entry.java index 8fb6d88e..818229a9 100644 --- a/src/main/java/com/contentstack/cms/stack/Entry.java +++ b/src/main/java/com/contentstack/cms/stack/Entry.java @@ -189,6 +189,11 @@ public Call find() { return this.service.fetch(this.headers, this.contentTypeUid, this.params); } + public Call findAsPojo() { + validateCT(); + return this.service.fetchPojo(this.headers, this.contentTypeUid, this.params); + } + /** * The Get a single entry request fetches a particular entry of a content * type. @@ -214,6 +219,11 @@ public Call fetch() { validateEntry(); return this.service.single(headers, this.contentTypeUid, this.entryUid, this.params); } + public Call fetchAsPojo() { + validateCT(); + validateEntry(); + return this.service.singlePojo(headers, this.contentTypeUid, this.entryUid, this.params); + } /** * The Create an entry call creates a new entry for the selected content type. diff --git a/src/main/java/com/contentstack/cms/stack/EntryListResponse.java b/src/main/java/com/contentstack/cms/stack/EntryListResponse.java new file mode 100644 index 00000000..d41c5e36 --- /dev/null +++ b/src/main/java/com/contentstack/cms/stack/EntryListResponse.java @@ -0,0 +1,46 @@ +package com.contentstack.cms.stack; +import com.google.gson.annotations.SerializedName; +import com.google.gson.JsonObject; +import com.google.gson.JsonElement; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.util.*; + +public class EntryListResponse { + + @SerializedName("entries") + private List rawEntries; + + private transient List entries; + + public List getEntries() { + if (entries == null && rawEntries != null) { + Gson gson = new Gson(); + entries = new ArrayList<>(); + + for (JsonObject entryJson : rawEntries) { + EntryPojo entryPojo = gson.fromJson(entryJson, EntryPojo.class); + // Map dynamic fields + Map fields = new LinkedHashMap<>(); + for (Map.Entry field : entryJson.entrySet()) { + String key = field.getKey(); + if (!EntryPojo.SYSTEM_FIELDS.contains(key)) { + fields.put(key, gson.fromJson(field.getValue(), Object.class)); + } + } + entryPojo.setFields(fields); + entries.add(entryPojo); + } + } + return entries; + } + + public List getRawJson() { + return rawEntries; + } + + @Override + public String toString() { + return new GsonBuilder().setPrettyPrinting().create().toJson(this); + } +} diff --git a/src/main/java/com/contentstack/cms/stack/EntryPojo.java b/src/main/java/com/contentstack/cms/stack/EntryPojo.java new file mode 100644 index 00000000..9e97a295 --- /dev/null +++ b/src/main/java/com/contentstack/cms/stack/EntryPojo.java @@ -0,0 +1,52 @@ +package com.contentstack.cms.stack; +import java.util.Map; +import java.util.Set; +import java.util.HashSet; +import java.util.Arrays; +import com.google.gson.annotations.SerializedName; + +public class EntryPojo { + + @SerializedName("title") + public String title; + + @SerializedName("uid") + public String uid; + + @SerializedName("_version") + public int version; + + @SerializedName("locale") + public String locale; + + // Additional fields captured dynamically from content type schema + protected Map fields; + + public Map getFields() { + return fields; + } + + public void setFields(Map fields) { + this.fields = fields; + } + + public String getStringField(String key) { + Object value = fields.get(key); + return value != null ? value.toString() : null; + } + + public Map getJsonObjectField(String key) { + Object value = fields.get(key); + if (value instanceof Map) { + return (Map) value; + } + return null; + } + // Define system fields to exclude from dynamic mapping + public static final Set SYSTEM_FIELDS = new HashSet<>(Arrays.asList( + "title","uid", "_version", "locale", "tags", "ACL", "created_by", "updated_by", + "created_at", "updated_at", "_in_progress" + )); +} + + diff --git a/src/main/java/com/contentstack/cms/stack/EntryResponse.java b/src/main/java/com/contentstack/cms/stack/EntryResponse.java new file mode 100644 index 00000000..c9acc5c8 --- /dev/null +++ b/src/main/java/com/contentstack/cms/stack/EntryResponse.java @@ -0,0 +1,44 @@ +package com.contentstack.cms.stack; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class EntryResponse { + + @SerializedName("entry") + private JsonObject entryJson; + + private EntryPojo entryPojo; + + public EntryPojo getEntryPojo() { + if (entryPojo == null && entryJson != null) { + Gson gson = new Gson(); + // Deserialize system fields automatically + entryPojo = gson.fromJson(entryJson, EntryPojo.class); + // Manually extract and map only non-system (dynamic) fields + Map fields = new LinkedHashMap<>(); + for (Map.Entry entry : entryJson.entrySet()) { + String key = entry.getKey(); + if (!EntryPojo.SYSTEM_FIELDS.contains(key)) { + fields.put(key, gson.fromJson(entry.getValue(), Object.class)); + } + } + entryPojo.setFields(fields); + } + return entryPojo; + } + + public JsonObject getRawJson() { + return entryJson; + } + + @Override + public String toString() { + return new GsonBuilder().setPrettyPrinting().create().toJson(this); + } +} diff --git a/src/main/java/com/contentstack/cms/stack/EntryService.java b/src/main/java/com/contentstack/cms/stack/EntryService.java index 83f54d8a..959fbc61 100644 --- a/src/main/java/com/contentstack/cms/stack/EntryService.java +++ b/src/main/java/com/contentstack/cms/stack/EntryService.java @@ -16,6 +16,12 @@ Call fetch( @Path("content_type_uid") String contentTypeUid, @QueryMap(encoded = true) Map queryParameter); + @GET("content_types/{content_type_uid}/entries") + Call fetchPojo( + @HeaderMap Map headers, + @Path("content_type_uid") String contentTypeUid, + @QueryMap(encoded = true) Map queryParameter); + @Headers("Content-Type: application/json") @GET("content_types/{content_type_uid}/entries/{entry_uid}") Call single( @@ -24,6 +30,14 @@ Call single( @Path("entry_uid") String entryUid, @QueryMap(encoded = true) Map queryParameter); + @Headers("Content-Type: application/json") + @GET("content_types/{content_type_uid}/entries/{entry_uid}") + Call singlePojo( + @HeaderMap Map headers, + @Path("content_type_uid") String contentTypeUid, + @Path("entry_uid") String entryUid, + @QueryMap(encoded = true) Map queryParameter); + @Headers("Content-Type: application/json") @POST("content_types/{content_type_uid}/entries") Call create( @@ -115,6 +129,13 @@ Call export( @Path("entry_uid") String entryUid, @QueryMap(encoded = true) Map queryOptions); + @GET("content_types/{content_type_uid}/entries/{entry_uid}/export") + Call exportPojo( + @HeaderMap Map headers, + @Path("content_type_uid") String contentTypeUid, + @Path("entry_uid") String entryUid, + @QueryMap(encoded = true) Map queryOptions); + @POST("content_types/{content_type_uid}/entries/import") Call imports( @HeaderMap Map headers, From 0ece770b2f0d6c2b7d22fa864487399aa35cb941 Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Mon, 7 Apr 2025 16:25:11 +0530 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=94=A7refactor:=20Remove=20unused=20e?= =?UTF-8?q?xportPojo=20method=20from=20EntryService=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/contentstack/cms/stack/EntryService.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/com/contentstack/cms/stack/EntryService.java b/src/main/java/com/contentstack/cms/stack/EntryService.java index 959fbc61..8c5c82fb 100644 --- a/src/main/java/com/contentstack/cms/stack/EntryService.java +++ b/src/main/java/com/contentstack/cms/stack/EntryService.java @@ -129,13 +129,6 @@ Call export( @Path("entry_uid") String entryUid, @QueryMap(encoded = true) Map queryOptions); - @GET("content_types/{content_type_uid}/entries/{entry_uid}/export") - Call exportPojo( - @HeaderMap Map headers, - @Path("content_type_uid") String contentTypeUid, - @Path("entry_uid") String entryUid, - @QueryMap(encoded = true) Map queryOptions); - @POST("content_types/{content_type_uid}/entries/import") Call imports( @HeaderMap Map headers, From 5b3141e5cff65708bdf43a6a2ebf4c5c5a99094d Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Mon, 7 Apr 2025 16:54:08 +0530 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=A8test:=20Add=20POJO=20tests=20for?= =?UTF-8?q?=20Entry=20and=20EntryListResponse=20fetching=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cms/stack/EntryFieldsAPITest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/test/java/com/contentstack/cms/stack/EntryFieldsAPITest.java b/src/test/java/com/contentstack/cms/stack/EntryFieldsAPITest.java index 3879ba78..c8bbedb1 100644 --- a/src/test/java/com/contentstack/cms/stack/EntryFieldsAPITest.java +++ b/src/test/java/com/contentstack/cms/stack/EntryFieldsAPITest.java @@ -13,6 +13,7 @@ import retrofit2.Response; import java.io.IOException; +import java.util.List; import org.json.simple.JSONArray; import org.json.simple.parser.ParseException; @@ -551,4 +552,42 @@ void testPublishRequest_RejectRequest() throws ParseException { Assertions.assertTrue(request.headers().names().contains("authorization")); } + @Test + void testEntryPojo() throws IOException { + Request request = contentType.entry(API_KEY).fetchAsPojo().request(); + Assertions.assertEquals(3, request.headers().names().size()); + Assertions.assertEquals("GET", request.method()); + Assertions.assertTrue(request.url().isHttps()); + Assertions.assertEquals("api.contentstack.io", request.url().host()); + Assertions.assertEquals(5, request.url().pathSegments().size()); + Assertions.assertEquals("v3", request.url().pathSegments().get(0)); + Assertions.assertEquals("content_types", request.url().pathSegments().get(1)); + Assertions.assertEquals("test", request.url().pathSegments().get(2)); + Assertions.assertEquals("entries", request.url().pathSegments().get(3)); + Assertions.assertNull(request.url().encodedQuery()); + Assertions.assertEquals( + String.format("https://api.contentstack.io/v3/content_types/test/entries/"+ TestClient.API_KEY), + request.url().toString()); + } + @Test + void testEntryPojoList() throws IOException { + entry.addParam("include_count", true); + entry.addParam("limit", 10); + Request request = entry.findAsPojo().request(); + Assertions.assertEquals(_COUNT, request.headers().names().size()); + Assertions.assertEquals("GET", request.method()); + Assertions.assertTrue(request.url().isHttps()); + Assertions.assertEquals("api.contentstack.io", request.url().host()); + Assertions.assertEquals(4, request.url().pathSegments().size()); + Assertions.assertEquals("v3", request.url().pathSegments().get(0)); + Assertions.assertEquals("content_types", request.url().pathSegments().get(1)); + Assertions.assertEquals("test", request.url().pathSegments().get(2)); + Assertions.assertEquals("entries", request.url().pathSegments().get(3)); + Assertions.assertNotNull(request.url().encodedQuery()); + Assertions.assertEquals("limit=10&include_count=true", request.url().query().toString()); + Assertions.assertEquals( + String.format("https://api.contentstack.io/v3/content_types/test/entries?limit=10&include_count=true"), + request.url().toString()); + } + }