| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,10 @@ | |||
| 1 | 1 | # CHANGELOG | |
| 2 | 2 | ||
| 3 | + ## v2.7.2 | ||
| 4 | + | ||
| 5 | + ### Jul 06, 2026 | ||
| 6 | + - Snyk fixes | ||
| 7 | + | ||
| 3 | 8 | ## v2.7.1 | |
| 4 | 9 | ||
| 5 | 10 | ### Jun 29, 2026 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,7 +5,7 @@ | |||
| 5 | 5 | <modelVersion>4.0.0</modelVersion> | |
| 6 | 6 | <groupId>com.contentstack.sdk</groupId> | |
| 7 | 7 | <artifactId>java</artifactId> | |
| 8 | - <version>2.7.1</version> | ||
| 8 | + <version>2.7.2</version> | ||
| 9 | 9 | <packaging>jar</packaging> | |
| 10 | 10 | <name>contentstack-java</name> | |
| 11 | 11 | <description>Java SDK for Contentstack Content Delivery API</description> | |
@@ -24,7 +24,7 @@ | |||
| 24 | 24 | <retrofit-source.version>3.0.0</retrofit-source.version> | |
| 25 | 25 | <loggin.version>5.3.2</loggin.version> | |
| 26 | 26 | <jococo-plugin.version>0.8.5</jococo-plugin.version> | |
| 27 | - <lombok-source.version>1.18.42</lombok-source.version> | ||
| 27 | + <lombok-source.version>1.18.44</lombok-source.version> | ||
| 28 | 28 | <junit-jupiter.version>5.11.4</junit-jupiter.version> | |
| 29 | 29 | <junit-jupiter-engine.version>5.8.0-M1</junit-jupiter-engine.version> | |
| 30 | 30 | <gson.version>2.8.8</gson.version> | |
@@ -33,7 +33,7 @@ | |||
| 33 | 33 | <maven-gpg-plugin.version>1.5</maven-gpg-plugin.version> | |
| 34 | 34 | <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version> | |
| 35 | 35 | <nexus-staging-maven-plugin.version>1.6.13</nexus-staging-maven-plugin.version> | |
| 36 | - <json-version>20251224</json-version> | ||
| 36 | + <json-version>20260522</json-version> | ||
| 37 | 37 | <jacoco-maven-plugin-version>0.8.11</jacoco-maven-plugin-version> | |
| 38 | 38 | <maven-release-plugin-version>2.5.3</maven-release-plugin-version> | |
| 39 | 39 | <contentstack-utils-version>1.5.1</contentstack-utils-version> | |
@@ -178,11 +178,6 @@ | |||
| 178 | 178 | </exclusions> | |
| 179 | 179 | </dependency> | |
| 180 | 180 | ||
| 181 | - <dependency> | ||
| 182 | - <groupId>com.fasterxml.jackson.core</groupId> | ||
| 183 | - <artifactId>jackson-databind</artifactId> | ||
| 184 | - <version>2.21.4</version> | ||
| 185 | - </dependency> | ||
| 186 | 181 | <dependency> | |
| 187 | 182 | <groupId>com.slack.api</groupId> | |
| 188 | 183 | <artifactId>bolt</artifactId> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,13 +1,12 @@ | |||
| 1 | 1 | package com.contentstack.sdk; | |
| 2 | 2 | ||
| 3 | - import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 4 | - import com.fasterxml.jackson.databind.json.JsonMapper; | ||
| 5 | - import com.fasterxml.jackson.databind.type.MapType; | ||
| 6 | 3 | import java.io.IOException; | |
| 7 | 4 | import java.io.UnsupportedEncodingException; | |
| 8 | 5 | import java.net.SocketTimeoutException; | |
| 9 | 6 | import java.net.URLEncoder; | |
| 7 | + import java.math.BigDecimal; | ||
| 10 | 8 | import java.nio.charset.StandardCharsets; | |
| 9 | + import java.util.ArrayList; | ||
| 11 | 10 | import java.util.HashMap; | |
| 12 | 11 | import java.util.Iterator; | |
| 13 | 12 | import java.util.LinkedHashMap; | |
@@ -201,6 +200,41 @@ private JSONObject createOrderedJSONObject(Map<String, Object> map) { | |||
| 201 | 200 | return json; | |
| 202 | 201 | } | |
| 203 | 202 | ||
| 203 | + /** | ||
| 204 | + * Recursively converts a parsed {@link JSONObject} into plain Java collections that | ||
| 205 | + * mirror what the response models expect: JSON objects become {@link LinkedHashMap} | ||
| 206 | + * (preserving key order) and JSON arrays become {@link ArrayList}. | ||
| 207 | + */ | ||
| 208 | + private static Map<String, Object> jsonToOrderedMap(JSONObject object) { | ||
| 209 | + LinkedHashMap<String, Object> map = new LinkedHashMap<>(); | ||
| 210 | + for (String key : object.keySet()) { | ||
| 211 | + map.put(key, convertJsonValue(object.get(key))); | ||
| 212 | + } | ||
| 213 | + return map; | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + private static Object convertJsonValue(Object value) { | ||
| 217 | + if (value == null || value == JSONObject.NULL) { | ||
| 218 | + return null; | ||
| 219 | + } | ||
| 220 | + if (value instanceof JSONObject) { | ||
| 221 | + return jsonToOrderedMap((JSONObject) value); | ||
| 222 | + } | ||
| 223 | + if (value instanceof JSONArray) { | ||
| 224 | + JSONArray array = (JSONArray) value; | ||
| 225 | + ArrayList<Object> list = new ArrayList<>(array.length()); | ||
| 226 | + for (int i = 0; i < array.length(); i++) { | ||
| 227 | + list.add(convertJsonValue(array.get(i))); | ||
| 228 | + } | ||
| 229 | + return list; | ||
| 230 | + } | ||
| 231 | + // Normalize floating-point numbers to Double to match the previous parser's output. | ||
| 232 | + if (value instanceof BigDecimal) { | ||
| 233 | + return ((BigDecimal) value).doubleValue(); | ||
| 234 | + } | ||
| 235 | + return value; | ||
| 236 | + } | ||
| 237 | + | ||
| 204 | 238 | private void getService(String requestUrl) throws IOException { | |
| 205 | 239 | ||
| 206 | 240 | this.headers.put(X_USER_AGENT_KEY, "contentstack-delivery-java/" + SDK_VERSION); | |
@@ -226,12 +260,11 @@ private void getService(String requestUrl) throws IOException { | |||
| 226 | 260 | response = pluginResponseImp(request, response); | |
| 227 | 261 | } | |
| 228 | 262 | try { | |
| 229 | - // Use Jackson to parse the JSON while preserving order | ||
| 230 | - ObjectMapper mapper = JsonMapper.builder().build(); | ||
| 231 | - MapType type = mapper.getTypeFactory().constructMapType(LinkedHashMap.class, String.class, | ||
| 232 | - Object.class); | ||
| 233 | - Map<String, Object> responseMap = mapper.readValue(response.body().string(), type); | ||
| 234 | - | ||
| 263 | + // Parse the JSON into ordered maps/lists using org.json. Nested objects | ||
| 264 | + // become LinkedHashMap and arrays become ArrayList, matching the shape | ||
| 265 | + // the response models expect. | ||
| 266 | + Map<String, Object> responseMap = jsonToOrderedMap(new JSONObject(response.body().string())); | ||
| 267 | + | ||
| 235 | 268 | // Use the custom method to create an ordered JSONObject | |
| 236 | 269 | responseJSON = createOrderedJSONObject(responseMap); | |
| 237 | 270 | if (this.config.livePreviewEntry != null && !this.config.livePreviewEntry.isEmpty()) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -597,6 +597,130 @@ void testOnRequestFailedWithNullCallback() throws Exception { | |||
| 597 | 597 | assertDoesNotThrow(() -> request.onRequestFailed(errorResponse, 500, null)); | |
| 598 | 598 | } | |
| 599 | 599 | ||
| 600 | + @Test | ||
| 601 | + void testOnRequestFailedWithEmptyError() throws Exception { | ||
| 602 | + // Empty error object exercises the "false" side of the has(error_message/ | ||
| 603 | + // error_code/errors) checks in onRequestFailed. | ||
| 604 | + JSONObject errorResponse = new JSONObject(); | ||
| 605 | + | ||
| 606 | + AtomicBoolean callbackCalled = new AtomicBoolean(false); | ||
| 607 | + ResultCallBack callback = new ResultCallBack() { | ||
| 608 | + @Override | ||
| 609 | + public void onRequestFail(ResponseType responseType, Error error) { | ||
| 610 | + callbackCalled.set(true); | ||
| 611 | + } | ||
| 612 | + }; | ||
| 613 | + | ||
| 614 | + CSConnectionRequest request = new CSConnectionRequest(stack); | ||
| 615 | + Field callbackField = CSConnectionRequest.class.getDeclaredField("resultCallBack"); | ||
| 616 | + callbackField.setAccessible(true); | ||
| 617 | + callbackField.set(request, callback); | ||
| 618 | + | ||
| 619 | + assertDoesNotThrow(() -> request.onRequestFailed(errorResponse, 0, callback)); | ||
| 620 | + assertTrue(callbackCalled.get()); | ||
| 621 | + } | ||
| 622 | + | ||
| 623 | + @Test | ||
| 624 | + void testOnRequestFinishedFetchEntryWithNullCallback() throws Exception { | ||
| 625 | + CSHttpConnection mockConnection = createMockConnection(); | ||
| 626 | + | ||
| 627 | + Field controllerField = CSHttpConnection.class.getDeclaredField("controller"); | ||
| 628 | + controllerField.setAccessible(true); | ||
| 629 | + controllerField.set(mockConnection, Constants.FETCHENTRY); | ||
| 630 | + | ||
| 631 | + LinkedHashMap<String, Object> entryMap = new LinkedHashMap<>(); | ||
| 632 | + entryMap.put("uid", "test_entry_uid"); | ||
| 633 | + entryMap.put("title", "Test Entry"); | ||
| 634 | + | ||
| 635 | + JSONObject response = new JSONObject(); | ||
| 636 | + Field mapField = JSONObject.class.getDeclaredField("map"); | ||
| 637 | + mapField.setAccessible(true); | ||
| 638 | + @SuppressWarnings("unchecked") | ||
| 639 | + Map<String, Object> internalMap = (Map<String, Object>) mapField.get(response); | ||
| 640 | + internalMap.put("entry", entryMap); | ||
| 641 | + | ||
| 642 | + Field responseField = CSHttpConnection.class.getDeclaredField("responseJSON"); | ||
| 643 | + responseField.setAccessible(true); | ||
| 644 | + responseField.set(mockConnection, response); | ||
| 645 | + | ||
| 646 | + // No callback set -> exercises the "false" side of the callback null-check. | ||
| 647 | + CSConnectionRequest request = new CSConnectionRequest(entry); | ||
| 648 | + assertDoesNotThrow(() -> request.onRequestFinished(mockConnection)); | ||
| 649 | + assertEquals("test_entry_uid", entry.uid); | ||
| 650 | + } | ||
| 651 | + | ||
| 652 | + @Test | ||
| 653 | + void testOnRequestFinishedFetchSyncWithNullCallback() throws Exception { | ||
| 654 | + CSHttpConnection mockConnection = createMockConnection(); | ||
| 655 | + | ||
| 656 | + Field controllerField = CSHttpConnection.class.getDeclaredField("controller"); | ||
| 657 | + controllerField.setAccessible(true); | ||
| 658 | + controllerField.set(mockConnection, Constants.FETCHSYNC); | ||
| 659 | + | ||
| 660 | + JSONObject response = new JSONObject(); | ||
| 661 | + response.put("sync_token", "test_sync_token"); | ||
| 662 | + response.put("items", new JSONArray()); | ||
| 663 | + | ||
| 664 | + Field responseField = CSHttpConnection.class.getDeclaredField("responseJSON"); | ||
| 665 | + responseField.setAccessible(true); | ||
| 666 | + responseField.set(mockConnection, response); | ||
| 667 | + | ||
| 668 | + CSConnectionRequest request = new CSConnectionRequest(stack); | ||
| 669 | + assertDoesNotThrow(() -> request.onRequestFinished(mockConnection)); | ||
| 670 | + } | ||
| 671 | + | ||
| 672 | + @Test | ||
| 673 | + void testOnRequestFinishedFetchContentTypesWithNullCallback() throws Exception { | ||
| 674 | + CSHttpConnection mockConnection = createMockConnection(); | ||
| 675 | + | ||
| 676 | + Field controllerField = CSHttpConnection.class.getDeclaredField("controller"); | ||
| 677 | + controllerField.setAccessible(true); | ||
| 678 | + controllerField.set(mockConnection, Constants.FETCHCONTENTTYPES); | ||
| 679 | + | ||
| 680 | + LinkedHashMap<String, Object> contentTypeMap = new LinkedHashMap<>(); | ||
| 681 | + contentTypeMap.put("uid", "blog_post"); | ||
| 682 | + | ||
| 683 | + JSONObject response = new JSONObject(); | ||
| 684 | + Field mapField = JSONObject.class.getDeclaredField("map"); | ||
| 685 | + mapField.setAccessible(true); | ||
| 686 | + @SuppressWarnings("unchecked") | ||
| 687 | + Map<String, Object> internalMap = (Map<String, Object>) mapField.get(response); | ||
| 688 | + internalMap.put("content_type", contentTypeMap); | ||
| 689 | + | ||
| 690 | + Field responseField = CSHttpConnection.class.getDeclaredField("responseJSON"); | ||
| 691 | + responseField.setAccessible(true); | ||
| 692 | + responseField.set(mockConnection, response); | ||
| 693 | + | ||
| 694 | + CSConnectionRequest request = new CSConnectionRequest(contentType); | ||
| 695 | + assertDoesNotThrow(() -> request.onRequestFinished(mockConnection)); | ||
| 696 | + } | ||
| 697 | + | ||
| 698 | + @Test | ||
| 699 | + void testOnRequestFinishedFetchGlobalFieldsWithNullCallback() throws Exception { | ||
| 700 | + CSHttpConnection mockConnection = createMockConnection(); | ||
| 701 | + | ||
| 702 | + Field controllerField = CSHttpConnection.class.getDeclaredField("controller"); | ||
| 703 | + controllerField.setAccessible(true); | ||
| 704 | + controllerField.set(mockConnection, Constants.FETCHGLOBALFIELDS); | ||
| 705 | + | ||
| 706 | + LinkedHashMap<String, Object> globalFieldMap = new LinkedHashMap<>(); | ||
| 707 | + globalFieldMap.put("uid", "test_global_field"); | ||
| 708 | + | ||
| 709 | + JSONObject response = new JSONObject(); | ||
| 710 | + Field mapField = JSONObject.class.getDeclaredField("map"); | ||
| 711 | + mapField.setAccessible(true); | ||
| 712 | + @SuppressWarnings("unchecked") | ||
| 713 | + Map<String, Object> internalMap = (Map<String, Object>) mapField.get(response); | ||
| 714 | + internalMap.put("global_field", globalFieldMap); | ||
| 715 | + | ||
| 716 | + Field responseField = CSHttpConnection.class.getDeclaredField("responseJSON"); | ||
| 717 | + responseField.setAccessible(true); | ||
| 718 | + responseField.set(mockConnection, response); | ||
| 719 | + | ||
| 720 | + CSConnectionRequest request = new CSConnectionRequest(globalField); | ||
| 721 | + assertDoesNotThrow(() -> request.onRequestFinished(mockConnection)); | ||
| 722 | + } | ||
| 723 | + | ||
| 600 | 724 | // ========== HELPER METHODS ========== | |
| 601 | 725 | ||
| 602 | 726 | private CSHttpConnection createMockConnection() throws Exception { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments