From 65ec46a0674337035b72cd304d95417b9ee59be5 Mon Sep 17 00:00:00 2001 From: Max Inno <40697586+innomaxx@users.noreply.github.com> Date: Tue, 5 Sep 2023 09:29:06 +0300 Subject: [PATCH] fix: empty array instead of object handling (#185) --- .../json/EmptyArrayToNullDeserializer.java | 53 +++++++++++ .../projectsgroups/model/ProjectSettings.java | 5 +- .../projectsgroups/ProjectsGroupsApiTest.java | 17 ++-- .../api/projectsgroups/projectSettings.json | 32 ++++--- .../projectSettings_tmPenaltiesArray.json | 88 +++++++++++++++++++ 5 files changed, 172 insertions(+), 23 deletions(-) create mode 100644 src/main/java/com/crowdin/client/core/http/impl/json/EmptyArrayToNullDeserializer.java create mode 100644 src/test/resources/api/projectsgroups/projectSettings_tmPenaltiesArray.json diff --git a/src/main/java/com/crowdin/client/core/http/impl/json/EmptyArrayToNullDeserializer.java b/src/main/java/com/crowdin/client/core/http/impl/json/EmptyArrayToNullDeserializer.java new file mode 100644 index 000000000..6efa5e6eb --- /dev/null +++ b/src/main/java/com/crowdin/client/core/http/impl/json/EmptyArrayToNullDeserializer.java @@ -0,0 +1,53 @@ +package com.crowdin.client.core.http.impl.json; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; +import java.util.Collection; + +public class EmptyArrayToNullDeserializer extends StdDeserializer implements ContextualDeserializer { + private JavaType type; + + public EmptyArrayToNullDeserializer() { + super(Object.class); + } + + public EmptyArrayToNullDeserializer(JavaType type) { + super(type.getRawClass()); + this.type = type; + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + if (p.getCurrentToken() == JsonToken.VALUE_NULL) { + return null; + } + + Class clazz = this.type != null ? this.type.getRawClass() : Object.class; + + if (p.getCurrentToken() == JsonToken.START_ARRAY) { + if (!isCollectionType(clazz)) { + p.nextToken(); + return null; + } else { + return ctxt.readValue(p, clazz); + } + } + + return ctxt.readValue(p, clazz); + } + + private static boolean isCollectionType(Class type) { + return type.isArray() && Collection.class.isAssignableFrom(type); + } + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { + return new EmptyArrayToNullDeserializer(property.getType()); + } +} diff --git a/src/main/java/com/crowdin/client/projectsgroups/model/ProjectSettings.java b/src/main/java/com/crowdin/client/projectsgroups/model/ProjectSettings.java index f8c79da78..8acb32741 100644 --- a/src/main/java/com/crowdin/client/projectsgroups/model/ProjectSettings.java +++ b/src/main/java/com/crowdin/client/projectsgroups/model/ProjectSettings.java @@ -1,6 +1,8 @@ package com.crowdin.client.projectsgroups.model; +import com.crowdin.client.core.http.impl.json.EmptyArrayToNullDeserializer; import com.crowdin.client.languages.model.Language; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -39,6 +41,7 @@ public class ProjectSettings extends Project { private Map> languageMapping; private Boolean delayedWorkflowStart; private NotificationSettings notificationSettings; - private TmPenalties[] tmPenalties; + @JsonDeserialize(using = EmptyArrayToNullDeserializer.class) + private TmPenalties tmPenalties; } diff --git a/src/test/java/com/crowdin/client/projectsgroups/ProjectsGroupsApiTest.java b/src/test/java/com/crowdin/client/projectsgroups/ProjectsGroupsApiTest.java index 38b87f50e..6e8a51be6 100644 --- a/src/test/java/com/crowdin/client/projectsgroups/ProjectsGroupsApiTest.java +++ b/src/test/java/com/crowdin/client/projectsgroups/ProjectsGroupsApiTest.java @@ -27,10 +27,7 @@ import java.util.Map; import static java.util.Collections.singletonList; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; public class ProjectsGroupsApiTest extends TestClient { @@ -39,6 +36,7 @@ public class ProjectsGroupsApiTest extends TestClient { private final String groupName = "KB materials"; private final Long projectId = 8L; private final Long projectSettingsId = 9L; + private final Long projectSettingsId2 = 10L; private final String projectName = "Knowledge Base"; private final String sourceLanguageId = "en"; private final String targetLanguageId = "uk"; @@ -56,6 +54,7 @@ public List getMocks() { RequestMock.build(this.url + "/projects", HttpPost.METHOD_NAME, "api/projectsgroups/addProjectRequest.json", "api/projectsgroups/project.json"), RequestMock.build(this.url + "/projects/" + projectId, HttpGet.METHOD_NAME, "api/projectsgroups/project.json"), RequestMock.build(this.url + "/projects/" + projectSettingsId, HttpGet.METHOD_NAME, "api/projectsgroups/projectSettings.json"), + RequestMock.build(this.url + "/projects/" + projectSettingsId2, HttpGet.METHOD_NAME, "api/projectsgroups/projectSettings_tmPenaltiesArray.json"), RequestMock.build(this.url + "/projects/" + projectId, HttpDelete.METHOD_NAME), RequestMock.build(this.url + "/projects/" + projectId, HttpPatch.METHOD_NAME, "api/projectsgroups/editProject.json", "api/projectsgroups/project.json") ); @@ -174,7 +173,7 @@ public void getProjectSettingsTest() { assertFalse(qaChecksIgnorableCategories.getDuplicate()); assertFalse(qaChecksIgnorableCategories.getFtl()); - TmPenalties tmPenalties = projectSettings.getTmPenalties()[0]; + TmPenalties tmPenalties = projectSettings.getTmPenalties(); assertNotNull(tmPenalties); assertEquals(1, tmPenalties.getAutoSubstitution()); assertEquals(1, tmPenalties.getMultipleTranslations()); @@ -195,6 +194,14 @@ public void getProjectSettingsTest() { assertEquals(1, lastModified.getPenalty()); } + @Test + public void getProjectSettingsTest_tmPenaltiesEmptyArray() { + ResponseObject response = this.getProjectsGroupsApi().getProject(projectSettingsId2); + + ProjectSettings settings = (ProjectSettings) response.getData(); + assertNull(settings.getTmPenalties()); + } + @Test public void deleteProjectTest() { this.getProjectsGroupsApi().deleteProject(projectId); diff --git a/src/test/resources/api/projectsgroups/projectSettings.json b/src/test/resources/api/projectsgroups/projectSettings.json index 5e5450f59..b0c32febd 100644 --- a/src/test/resources/api/projectsgroups/projectSettings.json +++ b/src/test/resources/api/projectsgroups/projectSettings.json @@ -83,23 +83,21 @@ "managerNewStrings": false, "managerLanguageCompleted": false }, - "tmPenalties": [ - { - "autoSubstitution": 1, - "tmPriority": { - "priority": 2, - "penalty": 1 - }, - "multipleTranslations": 1, - "timeSinceLastUsage": { - "months": 2, - "penalty": 1 - }, - "timeSinceLastModified": { - "months": 2, - "penalty": 1 - } + "tmPenalties": { + "autoSubstitution": 1, + "tmPriority": { + "priority": 2, + "penalty": 1 + }, + "multipleTranslations": 1, + "timeSinceLastUsage": { + "months": 2, + "penalty": 1 + }, + "timeSinceLastModified": { + "months": 2, + "penalty": 1 } - ] + } } } diff --git a/src/test/resources/api/projectsgroups/projectSettings_tmPenaltiesArray.json b/src/test/resources/api/projectsgroups/projectSettings_tmPenaltiesArray.json new file mode 100644 index 000000000..aad20d3a9 --- /dev/null +++ b/src/test/resources/api/projectsgroups/projectSettings_tmPenaltiesArray.json @@ -0,0 +1,88 @@ +{ + "data": { + "id": 9, + "groupId": 4, + "userId": 6, + "sourceLanguageId": "es", + "targetLanguageIds": [ + "uk" + ], + "name": "Knowledge Base", + "identifier": "1f198a4e907688bc65834a6d5a6000c3", + "description": "Vault of all terms and their explanation", + "logo": "data:image/png;", + "background": "data:image/png;", + "isExternal": false, + "externalType": "proofread", + "workflowId": 3, + "hasCrowdsourcing": false, + "createdAt": "2019-09-20T11:34:40+00:00", + "updatedAt": "2019-09-20T11:34:40+00:00", + "translateDuplicates": 1, + "glossaryAccess": false, + "isMtAllowed": true, + "hiddenStringsProofreadersAccess": true, + "autoSubstitution": true, + "exportTranslatedOnly": false, + "skipUntranslatedFiles": false, + "exportApprovedOnly": false, + "autoTranslateDialects": true, + "publicDownloads": true, + "useGlobalTm": false, + "inContext": true, + "inContextPseudoLanguageId": "uk", + "isSuspended": false, + "qaCheckIsActive": true, + "qaCheckCategories": { + "empty": true, + "size": true, + "tags": true, + "spaces": true, + "variables": true, + "punctuation": true, + "symbolRegister": true, + "specialSymbols": true, + "wrongTranslation": true, + "spellcheck": true, + "icu": true + }, + "qaChecksIgnorableCategories": { + "empty": false, + "size": true, + "tags": true, + "spaces": true, + "variables": true, + "punctuation": true, + "symbolRegister": true, + "specialSymbols": true, + "wrongTranslation": true, + "spellcheck": true, + "icu": false, + "terms": true, + "duplicate": false, + "ftl": false, + "android": true + }, + "customQaCheckIds": [ + 1 + ], + "languageMapping": { + "uk": { + "name": "Ukranian", + "two_letters_code": "ua", + "three_letters_code": "ukr", + "locale": "uk-UA", + "locale_with_underscore": "uk_UA", + "android_code": "uk-rUA", + "osx_code": "ua.lproj", + "osx_locale": "ua" + } + }, + "notificationSettings": { + "translatorNewStrings": true, + "managerNewStrings": false, + "managerLanguageCompleted": false + }, + "tmPenalties": [] + } +}