From da44a4c40c727f7fcb79e4ff49865f66cfdc6b6f Mon Sep 17 00:00:00 2001 From: Kateryna Oblakevych Date: Mon, 26 Oct 2026 19:25:28 -0700 Subject: [PATCH 1/3] feat(glossaries,tm): update glossaries and translation memory API to the latest docs Glossaries: - concordance search: add the required `expressions`, deprecate `expression` - add DRAFT term status, Enterprise `fields` on terms and concepts, `isShared` on glossaries - add updateConcept overload with a dedicated UpdateConceptRequest and deprecate the overload that sends the Concept response model - export: send the documented upper-case filter values (statuses/types/genders/partsOfSpeech via new term* fields; the deprecated setters convert term enums), add text, caseSensitive, searchStrict, searchFullMatch, dateFrom and dateTo filters - add the missing export status attributes and the `filter` query param for listGlossaries - deprecate ListTermsParams.translationOfTermId Translation memory: - concordance search: add the required `expressions`, deprecate `expression` - add languageId, isShared and webUrl to TranslationMemory and isShared to AddTranslationMemoryRequest - add listTms(ListTmsParams) with filter and Long userId, and listTmSegments(tmId, ListTmSegmentsParams) with croql Co-Authored-By: Claude Opus 5.5 (1M context) --- .../client/glossaries/GlossariesApi.java | 29 ++ .../glossaries/model/AddGlossaryRequest.java | 1 + .../glossaries/model/AddTermRequest.java | 6 + .../client/glossaries/model/Concept.java | 5 + .../model/ExportGlossaryRequest.java | 279 +++++++++++++++++- .../glossaries/model/ExportPartOfSpeech.java | 20 ++ .../glossaries/model/ExportTermGender.java | 20 ++ .../glossaries/model/ExportTermStatus.java | 20 ++ .../glossaries/model/ExportTermType.java | 20 ++ .../client/glossaries/model/Glossary.java | 1 + .../model/GlossaryExportStatus.java | 23 ++ .../model/ListGlossariesParams.java | 4 + .../glossaries/model/ListTermsParams.java | 3 +- .../model/SearchConcordanceRequest.java | 10 + .../SearchOrganizationConcordanceRequest.java | 3 + .../client/glossaries/model/Status.java | 2 +- .../crowdin/client/glossaries/model/Term.java | 5 + .../model/UpdateConceptRequest.java | 30 ++ .../TranslationMemoryApi.java | 56 ++++ .../model/AddTranslationMemoryRequest.java | 1 + .../model/ListTmSegmentsParams.java | 20 ++ .../model/ListTmsParams.java | 28 ++ .../model/SearchConcordanceRequest.java | 9 +- .../SearchOrganizationConcordanceRequest.java | 3 + .../model/TranslationMemory.java | 3 + .../client/glossaries/GlossariesApiTest.java | 148 ++++++++++ .../TranslationMemoryApiTest.java | 55 ++++ .../TranslationMemorySegmentsApiTest.java | 29 ++ .../api/glossaries/addGlossaryRequest.json | 3 +- .../api/glossaries/addTermRequest.json | 8 +- .../resources/api/glossaries/concept.json | 9 +- .../concordanceSearchExpressionsRequest.json | 8 + .../api/glossaries/exportGlossary.json | 12 +- .../api/glossaries/exportGlossaryFilters.json | 37 +++ .../api/glossaries/exportGlossaryStatus.json | 18 +- .../resources/api/glossaries/glossary.json | 5 +- src/test/resources/api/glossaries/term.json | 7 +- .../api/glossaries/updateConceptRequest.json | 19 ++ .../api/translationmemory/addTmRequest.json | 4 +- .../concordanceSearchExpressionsRequest.json | 10 + .../resources/api/translationmemory/tm.json | 5 +- 41 files changed, 957 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/crowdin/client/glossaries/model/ExportPartOfSpeech.java create mode 100644 src/main/java/com/crowdin/client/glossaries/model/ExportTermGender.java create mode 100644 src/main/java/com/crowdin/client/glossaries/model/ExportTermStatus.java create mode 100644 src/main/java/com/crowdin/client/glossaries/model/ExportTermType.java create mode 100644 src/main/java/com/crowdin/client/glossaries/model/UpdateConceptRequest.java create mode 100644 src/main/java/com/crowdin/client/translationmemory/model/ListTmSegmentsParams.java create mode 100644 src/main/java/com/crowdin/client/translationmemory/model/ListTmsParams.java create mode 100644 src/test/resources/api/glossaries/concordanceSearchExpressionsRequest.json create mode 100644 src/test/resources/api/glossaries/exportGlossaryFilters.json create mode 100644 src/test/resources/api/glossaries/updateConceptRequest.json create mode 100644 src/test/resources/api/translationmemory/concordanceSearchExpressionsRequest.json diff --git a/src/main/java/com/crowdin/client/glossaries/GlossariesApi.java b/src/main/java/com/crowdin/client/glossaries/GlossariesApi.java index e2df82341..91d9956f8 100644 --- a/src/main/java/com/crowdin/client/glossaries/GlossariesApi.java +++ b/src/main/java/com/crowdin/client/glossaries/GlossariesApi.java @@ -37,6 +37,8 @@ public ResponseList searchConcordance(Long projectId, SearchC } /** + * Note: {@code userId} in the request is supported by Crowdin only (not by Crowdin Enterprise). + * * @param request request object * @return list of search results * @see + * @deprecated sends the response model {@link Concept} (including read-only properties such as id, userId and + * dates) as the request body; use {@link #updateConcept(Long, Long, UpdateConceptRequest)} instead */ + @Deprecated public ResponseObject updateConcept(Long glossaryId, Long conceptId, Concept request) throws HttpException, HttpBadRequestException { ConceptResponseObject conceptResponseObject = this.httpClient.put(this.url + "/glossaries/" + glossaryId + "/concepts/" + conceptId, request, new HttpRequestConfig(), ConceptResponseObject.class); return ResponseObject.of(conceptResponseObject.getData()); } + /** + * @param glossaryId glossary identifier + * @param conceptId concept identifier + * @param request request object + * @return updated concept + * @see + */ + public ResponseObject updateConcept(Long glossaryId, Long conceptId, UpdateConceptRequest request) throws HttpException, HttpBadRequestException { + ConceptResponseObject conceptResponseObject = this.httpClient.put(this.url + "/glossaries/" + glossaryId + "/concepts/" + conceptId, request, new HttpRequestConfig(), ConceptResponseObject.class); + return ResponseObject.of(conceptResponseObject.getData()); + } + /** * @param glossaryId glossary identifier * @param conceptId concept identifier @@ -181,6 +201,14 @@ public ResponseList listGlossaries(Long groupId, Integer limit, Intege return listGlossaries(params); } + /** + * @param params query params (groupId is Enterprise only, userId is Crowdin only) + * @return list of glossaries + * @see + */ public ResponseList listGlossaries(ListGlossariesParams params) throws HttpException, HttpBadRequestException { ListGlossariesParams query = Optional.ofNullable(params).orElse(new ListGlossariesParams()); @@ -191,6 +219,7 @@ public ResponseList listGlossaries(ListGlossariesParams params) throws Map> queryParams = HttpRequestConfig.buildUrlParams( "groupId", Optional.ofNullable(query.getGroupId()), "userId", Optional.ofNullable(query.getUserId()), + "filter", Optional.ofNullable(query.getFilter()), "limit", Optional.ofNullable(query.getLimit()), "offset", Optional.ofNullable(query.getOffset()), "orderBy", Optional.ofNullable(orderBy) diff --git a/src/main/java/com/crowdin/client/glossaries/model/AddGlossaryRequest.java b/src/main/java/com/crowdin/client/glossaries/model/AddGlossaryRequest.java index 86b0256a2..4bdf883a6 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/AddGlossaryRequest.java +++ b/src/main/java/com/crowdin/client/glossaries/model/AddGlossaryRequest.java @@ -8,4 +8,5 @@ public class AddGlossaryRequest { private String name; private String languageId; private Long groupId; + private Boolean isShared; } diff --git a/src/main/java/com/crowdin/client/glossaries/model/AddTermRequest.java b/src/main/java/com/crowdin/client/glossaries/model/AddTermRequest.java index 28ca28558..7ce0564e8 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/AddTermRequest.java +++ b/src/main/java/com/crowdin/client/glossaries/model/AddTermRequest.java @@ -2,6 +2,8 @@ import lombok.Data; +import java.util.Map; + @Data public class AddTermRequest { @@ -17,4 +19,8 @@ public class AddTermRequest { private Long conceptId; @Deprecated private Long translationOfTermId; + /** + * Enterprise only. Keys get via List Fields + */ + private Map fields; } diff --git a/src/main/java/com/crowdin/client/glossaries/model/Concept.java b/src/main/java/com/crowdin/client/glossaries/model/Concept.java index 79a38f1ee..2b32b6274 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/Concept.java +++ b/src/main/java/com/crowdin/client/glossaries/model/Concept.java @@ -4,6 +4,7 @@ import java.util.Date; import java.util.List; +import java.util.Map; @Data public class Concept { @@ -18,6 +19,10 @@ public class Concept { private String url; private String figure; private List languagesDetails; + /** + * Enterprise only + */ + private Map fields; private Date createdAt; private Date updatedAt; diff --git a/src/main/java/com/crowdin/client/glossaries/model/ExportGlossaryRequest.java b/src/main/java/com/crowdin/client/glossaries/model/ExportGlossaryRequest.java index d902a7b3a..8178b72be 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/ExportGlossaryRequest.java +++ b/src/main/java/com/crowdin/client/glossaries/model/ExportGlossaryRequest.java @@ -1,19 +1,294 @@ package com.crowdin.client.glossaries.model; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; +import java.util.ArrayList; +import java.util.Date; import java.util.List; @Data public class ExportGlossaryRequest { private GlossariesFormat format; + /** + * Fields to export (CSV and XLSX formats only), e.g. {@code term}, {@code description}, {@code partOfSpeech}. + *

+ * Enterprise only: a custom field can be selected by its slug — {@code field_{fieldSlug}} for a term field + * (exported as one column per language) and {@code conceptField_{fieldSlug}} for a concept field + * (exported as a single column). + */ private List exportFields; private ExportType exportType; + /** + * Note: must be used together with {@link #text} + */ + private String text; + /** + * Note: must be used together with {@link #text} + */ + private Boolean caseSensitive; + /** + * Note: must be used together with {@link #text} + */ + private Boolean searchStrict; + /** + * Note: must be used together with {@link #text} + */ + private Boolean searchFullMatch; + @JsonProperty("statuses") + private List termStatuses; + @JsonProperty("partsOfSpeech") + private List termPartsOfSpeech; + @JsonProperty("types") + private List termTypes; + @JsonProperty("genders") + private List termGenders; + private List authorIds; + private List languageIds; + private Date dateFrom; + private Date dateTo; + + /** + * @deprecated the export endpoint expects upper-case values ({@code NOT_RECOMMENDED}), not the term values + * ({@code not recommended}); use {@link #termStatuses} instead. Kept for compatibility, values set through + * {@link #setStatuses(List)} are converted into {@link #termStatuses}. + */ + @Deprecated + @JsonIgnore private List statuses; + /** + * @deprecated the export endpoint expects part of speech tags ({@code ADJ}, {@code PROPN}), not the term values + * ({@code adjective}, {@code proper noun}); use {@link #termPartsOfSpeech} instead. Kept for compatibility, values + * set through {@link #setPartsOfSpeech(List)} are converted into {@link #termPartsOfSpeech}. + */ + @Deprecated + @JsonIgnore private List partsOfSpeech; + /** + * @deprecated the export endpoint expects upper-case values ({@code FULL_FORM}), not the term values + * ({@code full form}); use {@link #termTypes} instead. Kept for compatibility, values set through + * {@link #setTypes(List)} are converted into {@link #termTypes}. + */ + @Deprecated + @JsonIgnore private List types; + /** + * @deprecated the export endpoint expects upper-case values ({@code MASCULINE}), not the term values + * ({@code masculine}); use {@link #termGenders} instead. Kept for compatibility, values set through + * {@link #setGenders(List)} are converted into {@link #termGenders}. + */ + @Deprecated + @JsonIgnore private List genders; - private List authorIds; - private List languageIds; + + /** + * @param statuses term statuses, converted to the matching {@link ExportTermStatus} values + * @deprecated use {@link #termStatuses} + */ + @Deprecated + public void setStatuses(List statuses) { + if (statuses == null) { + this.termStatuses = null; + return; + } + List converted = new ArrayList<>(); + for (Status status : statuses) { + if (status != null) { + converted.add(ExportTermStatus.valueOf(status.name())); + } + } + this.termStatuses = converted; + } + + /** + * @return {@link #termStatuses} converted to term statuses + * @deprecated use {@link #termStatuses} + */ + @Deprecated + public List getStatuses() { + if (this.termStatuses == null) { + return null; + } + List converted = new ArrayList<>(); + for (ExportTermStatus status : this.termStatuses) { + if (status != null) { + converted.add(Status.valueOf(status.name())); + } + } + return converted; + } + + /** + * Converts term parts of speech to export tags: adjective → ADJ, adposition → ADP, adverb → ADV, + * auxiliary → AUX, coordinating conjunction → CCONJ, determiner → DET, interjection → INTJ, + * noun → NOUN, numeral → NUM, particle → PRT, pronoun → PRON, proper noun → PROPN, + * subordinating conjunction → SCONJ, verb → VERB, other → X. + * + * @param partsOfSpeech term parts of speech + * @deprecated use {@link #termPartsOfSpeech} + */ + @Deprecated + public void setPartsOfSpeech(List partsOfSpeech) { + if (partsOfSpeech == null) { + this.termPartsOfSpeech = null; + return; + } + List converted = new ArrayList<>(); + for (PartOfSpeech partOfSpeech : partsOfSpeech) { + ExportPartOfSpeech value = toExportPartOfSpeech(partOfSpeech); + if (value != null) { + converted.add(value); + } + } + this.termPartsOfSpeech = converted; + } + + /** + * @return {@link #termPartsOfSpeech} converted to term parts of speech ({@link ExportPartOfSpeech#SYM} has no + * term counterpart and is omitted) + * @deprecated use {@link #termPartsOfSpeech} + */ + @Deprecated + public List getPartsOfSpeech() { + if (this.termPartsOfSpeech == null) { + return null; + } + List converted = new ArrayList<>(); + for (ExportPartOfSpeech partOfSpeech : this.termPartsOfSpeech) { + PartOfSpeech value = toPartOfSpeech(partOfSpeech); + if (value != null) { + converted.add(value); + } + } + return converted; + } + + /** + * @param types term types, converted to the matching {@link ExportTermType} values + * @deprecated use {@link #termTypes} + */ + @Deprecated + public void setTypes(List types) { + if (types == null) { + this.termTypes = null; + return; + } + List converted = new ArrayList<>(); + for (Type type : types) { + if (type != null) { + converted.add(ExportTermType.valueOf(type.name())); + } + } + this.termTypes = converted; + } + + /** + * @return {@link #termTypes} converted to term types + * @deprecated use {@link #termTypes} + */ + @Deprecated + public List getTypes() { + if (this.termTypes == null) { + return null; + } + List converted = new ArrayList<>(); + for (ExportTermType type : this.termTypes) { + if (type != null) { + converted.add(Type.valueOf(type.name())); + } + } + return converted; + } + + /** + * @param genders term genders, converted to the matching {@link ExportTermGender} values + * @deprecated use {@link #termGenders} + */ + @Deprecated + public void setGenders(List genders) { + if (genders == null) { + this.termGenders = null; + return; + } + List converted = new ArrayList<>(); + for (Gender gender : genders) { + if (gender != null) { + converted.add(ExportTermGender.valueOf(gender.name())); + } + } + this.termGenders = converted; + } + + /** + * @return {@link #termGenders} converted to term genders ({@link ExportTermGender#COMMON} has no term counterpart + * and is omitted) + * @deprecated use {@link #termGenders} + */ + @Deprecated + public List getGenders() { + if (this.termGenders == null) { + return null; + } + List converted = new ArrayList<>(); + for (ExportTermGender gender : this.termGenders) { + if (gender != null && gender != ExportTermGender.COMMON) { + converted.add(Gender.valueOf(gender.name())); + } + } + return converted; + } + + private static ExportPartOfSpeech toExportPartOfSpeech(PartOfSpeech partOfSpeech) { + if (partOfSpeech == null) { + return null; + } + switch (partOfSpeech) { + case ADJECTIVE: + return ExportPartOfSpeech.ADJ; + case ADPOSITION: + return ExportPartOfSpeech.ADP; + case ADVERB: + return ExportPartOfSpeech.ADV; + case AUXILIARY: + return ExportPartOfSpeech.AUX; + case COORDINATING_CONJUNCTION: + return ExportPartOfSpeech.CCONJ; + case DETERMINER: + return ExportPartOfSpeech.DET; + case INTERJECTION: + return ExportPartOfSpeech.INTJ; + case NOUN: + return ExportPartOfSpeech.NOUN; + case NUMERAL: + return ExportPartOfSpeech.NUM; + case PARTICLE: + return ExportPartOfSpeech.PRT; + case PRONOUN: + return ExportPartOfSpeech.PRON; + case PROPER_NOUN: + return ExportPartOfSpeech.PROPN; + case SUBORDINATING_CONJUNCTION: + return ExportPartOfSpeech.SCONJ; + case VERB: + return ExportPartOfSpeech.VERB; + case OTHER: + return ExportPartOfSpeech.X; + default: + return null; + } + } + + private static PartOfSpeech toPartOfSpeech(ExportPartOfSpeech partOfSpeech) { + if (partOfSpeech == null) { + return null; + } + for (PartOfSpeech value : PartOfSpeech.values()) { + if (toExportPartOfSpeech(value) == partOfSpeech) { + return value; + } + } + return null; + } } diff --git a/src/main/java/com/crowdin/client/glossaries/model/ExportPartOfSpeech.java b/src/main/java/com/crowdin/client/glossaries/model/ExportPartOfSpeech.java new file mode 100644 index 000000000..062b4454a --- /dev/null +++ b/src/main/java/com/crowdin/client/glossaries/model/ExportPartOfSpeech.java @@ -0,0 +1,20 @@ +package com.crowdin.client.glossaries.model; + +import com.crowdin.client.core.model.EnumConverter; + +/** + * Part of speech filter values accepted by the glossary export endpoint + * (Universal Dependencies style tags, e.g. {@code ADJ}, {@code PROPN}, {@code CCONJ}). + */ +public enum ExportPartOfSpeech implements EnumConverter { + NOUN, VERB, ADJ, PRON, PROPN, DET, ADV, ADP, CCONJ, SCONJ, NUM, INTJ, AUX, PRT, SYM, X; + + public static ExportPartOfSpeech from(String value) { + return ExportPartOfSpeech.valueOf(value.toUpperCase()); + } + + @Override + public String to(ExportPartOfSpeech v) { + return v.name(); + } +} diff --git a/src/main/java/com/crowdin/client/glossaries/model/ExportTermGender.java b/src/main/java/com/crowdin/client/glossaries/model/ExportTermGender.java new file mode 100644 index 000000000..1b3416e5c --- /dev/null +++ b/src/main/java/com/crowdin/client/glossaries/model/ExportTermGender.java @@ -0,0 +1,20 @@ +package com.crowdin.client.glossaries.model; + +import com.crowdin.client.core.model.EnumConverter; + +/** + * Term gender filter values accepted by the glossary export endpoint + * (sent as upper-case values, e.g. {@code MASCULINE}). + */ +public enum ExportTermGender implements EnumConverter { + MASCULINE, FEMININE, NEUTER, COMMON, OTHER; + + public static ExportTermGender from(String value) { + return ExportTermGender.valueOf(value.toUpperCase()); + } + + @Override + public String to(ExportTermGender v) { + return v.name(); + } +} diff --git a/src/main/java/com/crowdin/client/glossaries/model/ExportTermStatus.java b/src/main/java/com/crowdin/client/glossaries/model/ExportTermStatus.java new file mode 100644 index 000000000..3932008ca --- /dev/null +++ b/src/main/java/com/crowdin/client/glossaries/model/ExportTermStatus.java @@ -0,0 +1,20 @@ +package com.crowdin.client.glossaries.model; + +import com.crowdin.client.core.model.EnumConverter; + +/** + * Term status filter values accepted by the glossary export endpoint + * (sent as upper-case values, e.g. {@code NOT_RECOMMENDED}). + */ +public enum ExportTermStatus implements EnumConverter { + PREFERRED, ADMITTED, NOT_RECOMMENDED, OBSOLETE, DRAFT; + + public static ExportTermStatus from(String value) { + return ExportTermStatus.valueOf(value.toUpperCase().replaceAll(" ", "_")); + } + + @Override + public String to(ExportTermStatus v) { + return v.name(); + } +} diff --git a/src/main/java/com/crowdin/client/glossaries/model/ExportTermType.java b/src/main/java/com/crowdin/client/glossaries/model/ExportTermType.java new file mode 100644 index 000000000..752fc01a7 --- /dev/null +++ b/src/main/java/com/crowdin/client/glossaries/model/ExportTermType.java @@ -0,0 +1,20 @@ +package com.crowdin.client.glossaries.model; + +import com.crowdin.client.core.model.EnumConverter; + +/** + * Term type filter values accepted by the glossary export endpoint + * (sent as upper-case values, e.g. {@code FULL_FORM}). + */ +public enum ExportTermType implements EnumConverter { + FULL_FORM, ACRONYM, ABBREVIATION, SHORT_FORM, PHRASE, VARIANT; + + public static ExportTermType from(String value) { + return ExportTermType.valueOf(value.toUpperCase().replaceAll(" ", "_")); + } + + @Override + public String to(ExportTermType v) { + return v.name(); + } +} diff --git a/src/main/java/com/crowdin/client/glossaries/model/Glossary.java b/src/main/java/com/crowdin/client/glossaries/model/Glossary.java index 48ca991df..3bebef18e 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/Glossary.java +++ b/src/main/java/com/crowdin/client/glossaries/model/Glossary.java @@ -17,6 +17,7 @@ public class Glossary { private List languageIds; private List defaultProjectIds; private List projectIds; + private Boolean isShared; private String webUrl; private Date createdAt; } diff --git a/src/main/java/com/crowdin/client/glossaries/model/GlossaryExportStatus.java b/src/main/java/com/crowdin/client/glossaries/model/GlossaryExportStatus.java index fcffcbb72..1b8d19e0a 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/GlossaryExportStatus.java +++ b/src/main/java/com/crowdin/client/glossaries/model/GlossaryExportStatus.java @@ -22,5 +22,28 @@ public static class Attributes { private GlossariesFormat format; private List exportFields; + private String text; + private Boolean caseSensitive; + private Boolean searchFullMatch; + private Boolean searchStrict; + /** + * Export filter value, e.g. {@code DRAFT} + */ + private String status; + /** + * Export filter value, e.g. {@code VERB} + */ + private String partOfSpeech; + /** + * Export filter value, e.g. {@code ACRONYM} + */ + private String type; + /** + * Export filter value, e.g. {@code OTHER} + */ + private String gender; + private Long authorId; + private Date dateFrom; + private Date dateTo; } } diff --git a/src/main/java/com/crowdin/client/glossaries/model/ListGlossariesParams.java b/src/main/java/com/crowdin/client/glossaries/model/ListGlossariesParams.java index ca7f7aa8f..1963bb840 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/ListGlossariesParams.java +++ b/src/main/java/com/crowdin/client/glossaries/model/ListGlossariesParams.java @@ -12,5 +12,9 @@ public class ListGlossariesParams extends Pagination { private String orderBy; private Long userId; private Long groupId; + /** + * Filter glossaries by {@code name} + */ + private String filter; private List orderByList; } diff --git a/src/main/java/com/crowdin/client/glossaries/model/ListTermsParams.java b/src/main/java/com/crowdin/client/glossaries/model/ListTermsParams.java index 946947613..3b65cf727 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/ListTermsParams.java +++ b/src/main/java/com/crowdin/client/glossaries/model/ListTermsParams.java @@ -14,8 +14,9 @@ public class ListTermsParams extends Pagination { private String languageId; private Long conceptId; /** - * @deprecated + * @deprecated use {@link #conceptId} instead */ + @Deprecated private Long translationOfTermId; private String croql; private List orderByList; diff --git a/src/main/java/com/crowdin/client/glossaries/model/SearchConcordanceRequest.java b/src/main/java/com/crowdin/client/glossaries/model/SearchConcordanceRequest.java index 854ea426c..4d24ab9e7 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/SearchConcordanceRequest.java +++ b/src/main/java/com/crowdin/client/glossaries/model/SearchConcordanceRequest.java @@ -2,9 +2,19 @@ import lombok.Data; +import java.util.List; + @Data public class SearchConcordanceRequest { private String sourceLanguageId; private String targetLanguageId; + /** + * Expressions to search. Can't be used with {@link #expression} in the same request + */ + private List expressions; + /** + * @deprecated use {@link #expressions} instead. Can't be used with {@link #expressions} in the same request + */ + @Deprecated private String expression; } diff --git a/src/main/java/com/crowdin/client/glossaries/model/SearchOrganizationConcordanceRequest.java b/src/main/java/com/crowdin/client/glossaries/model/SearchOrganizationConcordanceRequest.java index 6298074d1..073c9ccf2 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/SearchOrganizationConcordanceRequest.java +++ b/src/main/java/com/crowdin/client/glossaries/model/SearchOrganizationConcordanceRequest.java @@ -9,5 +9,8 @@ public class SearchOrganizationConcordanceRequest { private String sourceLanguageId; private String targetLanguageId; private List expressions; + /** + * Crowdin only. Owner (user) whose glossaries to search, defaults to your own account + */ private Long userId; } diff --git a/src/main/java/com/crowdin/client/glossaries/model/Status.java b/src/main/java/com/crowdin/client/glossaries/model/Status.java index 0146fddc9..42e983213 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/Status.java +++ b/src/main/java/com/crowdin/client/glossaries/model/Status.java @@ -3,7 +3,7 @@ import com.crowdin.client.core.model.EnumConverter; public enum Status implements EnumConverter { - PREFERRED, ADMITTED, NOT_RECOMMENDED, OBSOLETE; + PREFERRED, ADMITTED, NOT_RECOMMENDED, OBSOLETE, DRAFT; public static Status from(String value) { return Status.valueOf(value.toUpperCase().replaceAll(" ", "_")); diff --git a/src/main/java/com/crowdin/client/glossaries/model/Term.java b/src/main/java/com/crowdin/client/glossaries/model/Term.java index 77bcc8ee0..77f6241e4 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/Term.java +++ b/src/main/java/com/crowdin/client/glossaries/model/Term.java @@ -3,6 +3,7 @@ import lombok.Data; import java.util.Date; +import java.util.Map; @Data public class Term { @@ -23,4 +24,8 @@ public class Term { private String lemma; private Date createdAt; private Date updatedAt; + /** + * Enterprise only + */ + private Map fields; } diff --git a/src/main/java/com/crowdin/client/glossaries/model/UpdateConceptRequest.java b/src/main/java/com/crowdin/client/glossaries/model/UpdateConceptRequest.java new file mode 100644 index 000000000..aacb55dcf --- /dev/null +++ b/src/main/java/com/crowdin/client/glossaries/model/UpdateConceptRequest.java @@ -0,0 +1,30 @@ +package com.crowdin.client.glossaries.model; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class UpdateConceptRequest { + + private String subject; + private String definition; + private Boolean translatable; + private String note; + private String url; + private String figure; + private List languagesDetails; + /** + * Enterprise only. Writes the whole set of custom field values: a field the request omits, or whose value is + * empty, is cleared. + */ + private Map fields; + + @Data + public static class LanguagesDetails { + private String languageId; + private String definition; + private String note; + } +} diff --git a/src/main/java/com/crowdin/client/translationmemory/TranslationMemoryApi.java b/src/main/java/com/crowdin/client/translationmemory/TranslationMemoryApi.java index b2be8b264..08dc3b639 100644 --- a/src/main/java/com/crowdin/client/translationmemory/TranslationMemoryApi.java +++ b/src/main/java/com/crowdin/client/translationmemory/TranslationMemoryApi.java @@ -7,6 +7,8 @@ import com.crowdin.client.core.model.*; import com.crowdin.client.translationmemory.model.AddTranslationMemoryRequest; import com.crowdin.client.translationmemory.model.CreateTmSegmentRequest; +import com.crowdin.client.translationmemory.model.ListTmSegmentsParams; +import com.crowdin.client.translationmemory.model.ListTmsParams; import com.crowdin.client.translationmemory.model.SearchConcordance; import com.crowdin.client.translationmemory.model.SearchConcordanceRequest; import com.crowdin.client.translationmemory.model.SearchConcordanceResponse; @@ -113,6 +115,33 @@ public ResponseList listTms(Long groupId, Integer limit, Inte return TranslationMemoryResponseList.to(translationMemoryResponseList); } + /** + * @param params query params ({@code groupId} is Enterprise only, {@code userId} is Crowdin only) + * @return list of translation memories + * @see

+ */ + public ResponseList listTms(ListTmsParams params) throws HttpException, HttpBadRequestException { + ListTmsParams query = Optional.ofNullable(params).orElse(new ListTmsParams()); + + String orderBy = query.getOrderByList() != null + ? OrderByField.generateSortParam(query.getOrderByList()) + : query.getOrderBy(); + + Map> queryParams = HttpRequestConfig.buildUrlParams( + "groupId", Optional.ofNullable(query.getGroupId()), + "userId", Optional.ofNullable(query.getUserId()), + "filter", Optional.ofNullable(query.getFilter()), + "limit", Optional.ofNullable(query.getLimit()), + "offset", Optional.ofNullable(query.getOffset()), + "orderBy", Optional.ofNullable(orderBy) + ); + TranslationMemoryResponseList translationMemoryResponseList = this.httpClient.get(this.url + "/tms", new HttpRequestConfig(queryParams), TranslationMemoryResponseList.class); + return TranslationMemoryResponseList.to(translationMemoryResponseList); + } + /** * @param request request object * @return newly created translation memory @@ -290,6 +319,33 @@ public ResponseList listTmSegments(Long tmId, Integer limit, Integer return TmSegmentResponseList.to(responseList); } + /** + * @param tmId translation memory identifier + * @param params query params + * @return list of translation memory segments + * @see + */ + public ResponseList listTmSegments(Long tmId, ListTmSegmentsParams params) throws HttpException, HttpBadRequestException { + ListTmSegmentsParams query = Optional.ofNullable(params).orElse(new ListTmSegmentsParams()); + + String orderBy = query.getOrderByList() != null + ? OrderByField.generateSortParam(query.getOrderByList()) + : query.getOrderBy(); + + String url = formUrl_tmSegments(tmId); + Map> queryParams = HttpRequestConfig.buildUrlParams( + "limit", Optional.ofNullable(query.getLimit()), + "offset", Optional.ofNullable(query.getOffset()), + "orderBy", Optional.ofNullable(orderBy), + "croql", Optional.ofNullable(query.getCroql()) + ); + TmSegmentResponseList responseList = this.httpClient.get(url, new HttpRequestConfig(queryParams), TmSegmentResponseList.class); + return TmSegmentResponseList.to(responseList); + } + /** * @param tmId translation memory identifier * @param request request object diff --git a/src/main/java/com/crowdin/client/translationmemory/model/AddTranslationMemoryRequest.java b/src/main/java/com/crowdin/client/translationmemory/model/AddTranslationMemoryRequest.java index 2cb426628..46d4856a2 100644 --- a/src/main/java/com/crowdin/client/translationmemory/model/AddTranslationMemoryRequest.java +++ b/src/main/java/com/crowdin/client/translationmemory/model/AddTranslationMemoryRequest.java @@ -8,4 +8,5 @@ public class AddTranslationMemoryRequest { private String name; private String languageId; private Long groupId; + private Boolean isShared; } diff --git a/src/main/java/com/crowdin/client/translationmemory/model/ListTmSegmentsParams.java b/src/main/java/com/crowdin/client/translationmemory/model/ListTmSegmentsParams.java new file mode 100644 index 000000000..902ea48fc --- /dev/null +++ b/src/main/java/com/crowdin/client/translationmemory/model/ListTmSegmentsParams.java @@ -0,0 +1,20 @@ +package com.crowdin.client.translationmemory.model; + +import com.crowdin.client.core.model.OrderByField; +import com.crowdin.client.core.model.Pagination; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +public class ListTmSegmentsParams extends Pagination { + + private String orderBy; + private List orderByList; + /** + * Filter segments by CroQL + */ + private String croql; +} diff --git a/src/main/java/com/crowdin/client/translationmemory/model/ListTmsParams.java b/src/main/java/com/crowdin/client/translationmemory/model/ListTmsParams.java new file mode 100644 index 000000000..385975811 --- /dev/null +++ b/src/main/java/com/crowdin/client/translationmemory/model/ListTmsParams.java @@ -0,0 +1,28 @@ +package com.crowdin.client.translationmemory.model; + +import com.crowdin.client.core.model.OrderByField; +import com.crowdin.client.core.model.Pagination; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +public class ListTmsParams extends Pagination { + + private String orderBy; + private List orderByList; + /** + * Filter TMs by {@code name} + */ + private String filter; + /** + * Crowdin only. Project member identifier + */ + private Long userId; + /** + * Enterprise only. Group identifier (set 0 to see TMs of the root group) + */ + private Long groupId; +} diff --git a/src/main/java/com/crowdin/client/translationmemory/model/SearchConcordanceRequest.java b/src/main/java/com/crowdin/client/translationmemory/model/SearchConcordanceRequest.java index abc54f0f9..7d2efc575 100644 --- a/src/main/java/com/crowdin/client/translationmemory/model/SearchConcordanceRequest.java +++ b/src/main/java/com/crowdin/client/translationmemory/model/SearchConcordanceRequest.java @@ -2,6 +2,8 @@ import lombok.Data; +import java.util.List; + @Data public class SearchConcordanceRequest { private String sourceLanguageId; @@ -9,7 +11,12 @@ public class SearchConcordanceRequest { private Boolean autoSubstitution; private Integer minRelevant; /** - * @deprecated + * Expressions to search. Can't be used with {@link #expression} in the same request + */ + private List expressions; + /** + * @deprecated use {@link #expressions} instead. Can't be used with {@link #expressions} in the same request */ + @Deprecated private String expression; } diff --git a/src/main/java/com/crowdin/client/translationmemory/model/SearchOrganizationConcordanceRequest.java b/src/main/java/com/crowdin/client/translationmemory/model/SearchOrganizationConcordanceRequest.java index 2054d83be..9b49cd75a 100644 --- a/src/main/java/com/crowdin/client/translationmemory/model/SearchOrganizationConcordanceRequest.java +++ b/src/main/java/com/crowdin/client/translationmemory/model/SearchOrganizationConcordanceRequest.java @@ -11,5 +11,8 @@ public class SearchOrganizationConcordanceRequest { private Boolean autoSubstitution; private Integer minRelevant; private List expressions; + /** + * Crowdin only. Owner (user) whose translation memories to search, defaults to your own account + */ private Long userId; } diff --git a/src/main/java/com/crowdin/client/translationmemory/model/TranslationMemory.java b/src/main/java/com/crowdin/client/translationmemory/model/TranslationMemory.java index 128a398c3..f1f014f56 100644 --- a/src/main/java/com/crowdin/client/translationmemory/model/TranslationMemory.java +++ b/src/main/java/com/crowdin/client/translationmemory/model/TranslationMemory.java @@ -12,9 +12,12 @@ public class TranslationMemory { private Long groupId; private Long userId; private String name; + private String languageId; private List languageIds; private Integer segmentsCount; private List defaultProjectIds; private List projectIds; + private Boolean isShared; + private String webUrl; private Date createdAt; } diff --git a/src/test/java/com/crowdin/client/glossaries/GlossariesApiTest.java b/src/test/java/com/crowdin/client/glossaries/GlossariesApiTest.java index e04681c2b..7ed0705a6 100644 --- a/src/test/java/com/crowdin/client/glossaries/GlossariesApiTest.java +++ b/src/test/java/com/crowdin/client/glossaries/GlossariesApiTest.java @@ -1,5 +1,6 @@ package com.crowdin.client.glossaries; +import com.crowdin.client.core.http.impl.json.JacksonJsonTransformer; import com.crowdin.client.core.model.*; import com.crowdin.client.framework.RequestMock; import com.crowdin.client.framework.TestClient; @@ -16,6 +17,7 @@ import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class GlossariesApiTest extends TestClient { @@ -49,13 +51,20 @@ public List getMocks() { }}), RequestMock.build(this.url + "/glossaries/" + glossaryId + "/concepts/" + conceptId, HttpGet.METHOD_NAME, "api/glossaries/concept.json"), RequestMock.build(this.url + "/glossaries/" + glossaryId + "/concepts/" + conceptId, HttpPut.METHOD_NAME, "api/glossaries/updateConcept.json", "api/glossaries/concept.json"), + RequestMock.build(this.url + "/glossaries/" + glossaryId + "/concepts/" + conceptId, HttpPut.METHOD_NAME, "api/glossaries/updateConceptRequest.json", "api/glossaries/concept.json"), RequestMock.build(this.url + "/glossaries/" + glossaryId + "/concepts/" + conceptId, HttpDelete.METHOD_NAME), RequestMock.build(this.url + "/glossaries", HttpGet.METHOD_NAME, "api/glossaries/listGlossaries.json"), + RequestMock.build(this.url + "/glossaries", HttpGet.METHOD_NAME, "api/glossaries/listGlossaries.json", new HashMap() {{ + put("groupId", groupId); + put("filter", "Eyes"); + put("limit", 10); + }}), RequestMock.build(this.url + "/glossaries", HttpPost.METHOD_NAME, "api/glossaries/addGlossaryRequest.json", "api/glossaries/glossary.json"), RequestMock.build(this.url + "/glossaries/" + glossaryId, HttpGet.METHOD_NAME, "api/glossaries/glossary.json"), RequestMock.build(this.url + "/glossaries/" + glossaryId, HttpDelete.METHOD_NAME), RequestMock.build(this.url + "/glossaries/" + glossaryId, HttpPatch.METHOD_NAME, "api/glossaries/editGlossary.json", "api/glossaries/glossary.json"), RequestMock.build(this.url + "/glossaries/" + glossaryId + "/exports", HttpPost.METHOD_NAME, "api/glossaries/exportGlossary.json", "api/glossaries/exportGlossaryStatus.json"), + RequestMock.build(this.url + "/glossaries/" + glossaryId + "/exports", HttpPost.METHOD_NAME, "api/glossaries/exportGlossaryFilters.json", "api/glossaries/exportGlossaryStatus.json"), RequestMock.build(this.url + "/glossaries/" + glossaryId + "/exports/" + exportId, HttpGet.METHOD_NAME, "api/glossaries/exportGlossaryStatus.json"), RequestMock.build(this.url + "/glossaries/" + glossaryId + "/exports/" + exportId + "/download", HttpGet.METHOD_NAME, "api/glossaries/downloadLink.json"), RequestMock.build(this.url + "/glossaries/" + glossaryId + "/imports", HttpPost.METHOD_NAME, "api/glossaries/importGlossary.json", "api/glossaries/importGlossaryStatus.json"), @@ -73,6 +82,7 @@ public List getMocks() { RequestMock.build(this.url + "/glossaries/" + glossaryId + "/terms/" + termId, HttpDelete.METHOD_NAME), RequestMock.build(this.url + "/glossaries/" + glossaryId + "/terms/" + termId, HttpPatch.METHOD_NAME, "api/glossaries/editTerm.json", "api/glossaries/term.json"), RequestMock.build(this.url + "/projects/" + projectId + "/glossaries/concordance", HttpPost.METHOD_NAME, "api/glossaries/concordanceSearchRequest.json", "api/glossaries/concordanceSearchResponse.json"), + RequestMock.build(this.url + "/projects/" + projectId + "/glossaries/concordance", HttpPost.METHOD_NAME, "api/glossaries/concordanceSearchExpressionsRequest.json", "api/glossaries/concordanceSearchResponse.json"), RequestMock.build(this.url + "/glossaries/concordance", HttpPost.METHOD_NAME, "api/glossaries/organizationConcordanceSearchRequest.json", "api/glossaries/concordanceSearchResponse.json") ); } @@ -91,6 +101,17 @@ public void concordanceSearchTest() { assertEquals(searchConcordanceResponseList.getData().get(0).getData().getTargetTerms().get(0).getId(), termId); } + @Test + public void concordanceSearchExpressionsTest() { + SearchConcordanceRequest searchConcordanceRequest = new SearchConcordanceRequest(); + searchConcordanceRequest.setSourceLanguageId("en"); + searchConcordanceRequest.setTargetLanguageId("de"); + searchConcordanceRequest.setExpressions(Arrays.asList("Welcome!", "View")); + ResponseList searchConcordanceResponseList = this.getGlossariesApi().searchConcordance(projectId, searchConcordanceRequest); + assertEquals(1, searchConcordanceResponseList.getData().size()); + assertEquals(glossaryId, searchConcordanceResponseList.getData().get(0).getData().getGlossary().getId()); + } + @Test public void searchOrganizationConcordanceTest() { SearchOrganizationConcordanceRequest request = new SearchOrganizationConcordanceRequest(); @@ -161,6 +182,9 @@ public void getConceptTest() { assertEquals(conceptResponseObject.getData().getId(), glossaryId); assertEquals(conceptResponseObject.getData().getSubject(), subject); assertEquals(new Date(119,Calendar.SEPTEMBER,23,7,19,47), conceptResponseObject.getData().getCreatedAt()); + assertEquals(true, conceptResponseObject.getData().getTranslatable()); + assertEquals("some value 1", conceptResponseObject.getData().getFields().get("some-field-1")); + assertEquals(true, conceptResponseObject.getData().getFields().get("some-field-3")); } @Test @@ -182,6 +206,31 @@ public void updateConceptTest() { assertEquals(conceptResponseObject.getData().getSubject(), subject); } + @Test + public void updateConceptWithRequestTest() { + UpdateConceptRequest request = new UpdateConceptRequest(); + request.setSubject(subject); + request.setDefinition("This is a sample definition."); + request.setTranslatable(true); + request.setNote("Any concept-level note information"); + request.setUrl("www.example.com"); + request.setFigure("www.example.com/image.png"); + UpdateConceptRequest.LanguagesDetails languagesDetails = new UpdateConceptRequest.LanguagesDetails(); + languagesDetails.setLanguageId("en"); + languagesDetails.setDefinition("This is a sample definition."); + languagesDetails.setNote("Some note"); + request.setLanguagesDetails(singletonList(languagesDetails)); + Map fields = new LinkedHashMap<>(); + fields.put("some-field-1", "some value 1"); + fields.put("some-field-3", true); + request.setFields(fields); + + ResponseObject conceptResponseObject = this.getGlossariesApi().updateConcept(glossaryId, conceptId, request); + assertEquals(glossaryId, conceptResponseObject.getData().getId()); + assertEquals(subject, conceptResponseObject.getData().getSubject()); + assertEquals("some value 1", conceptResponseObject.getData().getFields().get("some-field-1")); + } + @Test public void deleteConceptTest() { this.getGlossariesApi().deleteConcept(glossaryId, conceptId); @@ -201,9 +250,23 @@ public void addGlossaryTest() { request.setName(name); request.setLanguageId(languageId); request.setGroupId(groupId); + request.setIsShared(false); ResponseObject glossaryResponseObject = this.getGlossariesApi().addGlossary(request); assertEquals(glossaryResponseObject.getData().getId(), glossaryId); assertEquals(glossaryResponseObject.getData().getName(), name); + assertEquals(false, glossaryResponseObject.getData().getIsShared()); + assertEquals("https://example.crowdin.com/u/glossaries/2", glossaryResponseObject.getData().getWebUrl()); + } + + @Test + public void listGlossariesWithParamsTest() { + ListGlossariesParams params = new ListGlossariesParams(); + params.setGroupId(groupId); + params.setFilter("Eyes"); + params.setLimit(10); + ResponseList glossaryResponseList = this.getGlossariesApi().listGlossaries(params); + assertEquals(1, glossaryResponseList.getData().size()); + assertEquals(glossaryId, glossaryResponseList.getData().get(0).getData().getId()); } @Test @@ -243,12 +306,90 @@ public void exportGlossaryTest() { request.setLanguageIds(Arrays.asList("en", "uk")); ResponseObject glossaryExportStatusResponseObject = this.getGlossariesApi().exportGlossary(glossaryId, request); assertEquals(glossaryExportStatusResponseObject.getData().getIdentifier(), exportId); + assertEquals(Arrays.asList(ExportTermStatus.PREFERRED, ExportTermStatus.ADMITTED), request.getTermStatuses()); + assertEquals(Arrays.asList(Status.PREFERRED, Status.ADMITTED), request.getStatuses()); + assertEquals(Arrays.asList(PartOfSpeech.NOUN, PartOfSpeech.VERB), request.getPartsOfSpeech()); + assertEquals(singletonList(Type.FULL_FORM), request.getTypes()); + assertEquals(singletonList(Gender.MASCULINE), request.getGenders()); + } + + @Test + public void exportGlossaryWithFiltersTest() { + ExportGlossaryRequest request = new ExportGlossaryRequest(); + request.setFormat(GlossariesFormat.XLSX); + request.setExportFields(Arrays.asList("term", "field_term-field", "conceptField_concept-field")); + request.setExportType(ExportType.CONCEPTS); + request.setText("value"); + request.setCaseSensitive(true); + request.setSearchStrict(false); + request.setSearchFullMatch(true); + request.setTermStatuses(Arrays.asList(ExportTermStatus.DRAFT, ExportTermStatus.NOT_RECOMMENDED)); + request.setTermPartsOfSpeech(Arrays.asList(ExportPartOfSpeech.ADJ, ExportPartOfSpeech.PROPN, ExportPartOfSpeech.SYM)); + request.setTermTypes(singletonList(ExportTermType.SHORT_FORM)); + request.setTermGenders(Arrays.asList(ExportTermGender.COMMON, ExportTermGender.NEUTER)); + request.setAuthorIds(singletonList(12L)); + request.setLanguageIds(singletonList("uk")); + request.setDateFrom(new Date(1705993214000L)); + request.setDateTo(new Date(1727420414000L)); + ResponseObject glossaryExportStatusResponseObject = this.getGlossariesApi().exportGlossary(glossaryId, request); + assertEquals(exportId, glossaryExportStatusResponseObject.getData().getIdentifier()); + + assertEquals(Arrays.asList(Status.DRAFT, Status.NOT_RECOMMENDED), request.getStatuses()); + assertEquals(Arrays.asList(PartOfSpeech.ADJECTIVE, PartOfSpeech.PROPER_NOUN), request.getPartsOfSpeech()); + assertEquals(singletonList(Type.SHORT_FORM), request.getTypes()); + assertEquals(singletonList(Gender.NEUTER), request.getGenders()); + } + + @Test + public void exportGlossaryRequestDeprecatedSettersConversionTest() { + ExportGlossaryRequest request = new ExportGlossaryRequest(); + request.setStatuses(Arrays.asList(Status.values())); + request.setTypes(Arrays.asList(Type.values())); + request.setGenders(Arrays.asList(Gender.values())); + request.setPartsOfSpeech(Arrays.asList(PartOfSpeech.values())); + + assertEquals(Arrays.asList(ExportTermStatus.PREFERRED, ExportTermStatus.ADMITTED, ExportTermStatus.NOT_RECOMMENDED, + ExportTermStatus.OBSOLETE, ExportTermStatus.DRAFT), request.getTermStatuses()); + assertEquals(Arrays.asList(ExportTermType.FULL_FORM, ExportTermType.ACRONYM, ExportTermType.ABBREVIATION, + ExportTermType.SHORT_FORM, ExportTermType.PHRASE, ExportTermType.VARIANT), request.getTermTypes()); + assertEquals(Arrays.asList(ExportTermGender.MASCULINE, ExportTermGender.FEMININE, ExportTermGender.NEUTER, + ExportTermGender.OTHER), request.getTermGenders()); + assertEquals(Arrays.asList(ExportPartOfSpeech.ADJ, ExportPartOfSpeech.ADP, ExportPartOfSpeech.ADV, + ExportPartOfSpeech.AUX, ExportPartOfSpeech.CCONJ, ExportPartOfSpeech.DET, ExportPartOfSpeech.INTJ, + ExportPartOfSpeech.NOUN, ExportPartOfSpeech.NUM, ExportPartOfSpeech.PRT, ExportPartOfSpeech.PRON, + ExportPartOfSpeech.PROPN, ExportPartOfSpeech.SCONJ, ExportPartOfSpeech.VERB, ExportPartOfSpeech.X), + request.getTermPartsOfSpeech()); + assertEquals(Arrays.asList(PartOfSpeech.values()), request.getPartsOfSpeech()); + + String json = new JacksonJsonTransformer().convert(request); + assertEquals("{\"statuses\":[\"PREFERRED\",\"ADMITTED\",\"NOT_RECOMMENDED\",\"OBSOLETE\",\"DRAFT\"]," + + "\"partsOfSpeech\":[\"ADJ\",\"ADP\",\"ADV\",\"AUX\",\"CCONJ\",\"DET\",\"INTJ\",\"NOUN\",\"NUM\",\"PRT\",\"PRON\",\"PROPN\",\"SCONJ\",\"VERB\",\"X\"]," + + "\"types\":[\"FULL_FORM\",\"ACRONYM\",\"ABBREVIATION\",\"SHORT_FORM\",\"PHRASE\",\"VARIANT\"]," + + "\"genders\":[\"MASCULINE\",\"FEMININE\",\"NEUTER\",\"OTHER\"]}", json); + + request.setStatuses(null); + assertNull(request.getTermStatuses()); + assertNull(request.getStatuses()); } @Test public void checkGlossaryExportStatusTest() { ResponseObject glossaryExportStatusResponseObject = this.getGlossariesApi().checkGlossaryExportStatus(glossaryId, exportId); assertEquals(glossaryExportStatusResponseObject.getData().getIdentifier(), exportId); + GlossaryExportStatus.Attributes attributes = glossaryExportStatusResponseObject.getData().getAttributes(); + assertEquals(GlossariesFormat.CSV, attributes.getFormat()); + assertEquals(Arrays.asList("term", "description", "partOfSpeech"), attributes.getExportFields()); + assertEquals("value", attributes.getText()); + assertEquals(true, attributes.getCaseSensitive()); + assertEquals(false, attributes.getSearchFullMatch()); + assertEquals(true, attributes.getSearchStrict()); + assertEquals("DRAFT", attributes.getStatus()); + assertEquals("VERB", attributes.getPartOfSpeech()); + assertEquals("ACRONYM", attributes.getType()); + assertEquals("COMMON", attributes.getGender()); + assertEquals(12L, attributes.getAuthorId()); + assertEquals(new Date(1727090814000L), attributes.getDateFrom()); + assertEquals(new Date(1734953214000L), attributes.getDateTo()); } @Test @@ -345,8 +486,15 @@ public void addTermTest() { request.setUrl("string"); request.setConceptId(0L); request.setTranslationOfTermId(0L); + Map fields = new LinkedHashMap<>(); + fields.put("some-field-1", "some value 1"); + fields.put("some-field-2", 12); + request.setFields(fields); ResponseObject termResponseObject = this.getGlossariesApi().addTerm(glossaryId, request); assertEquals(termResponseObject.getData().getId(), termId); + assertEquals(Status.DRAFT, termResponseObject.getData().getStatus()); + assertEquals("some value 1", termResponseObject.getData().getFields().get("some-field-1")); + assertEquals(12, termResponseObject.getData().getFields().get("some-field-2")); } @Test diff --git a/src/test/java/com/crowdin/client/translationmemory/TranslationMemoryApiTest.java b/src/test/java/com/crowdin/client/translationmemory/TranslationMemoryApiTest.java index 8dcd72094..a3b18324f 100644 --- a/src/test/java/com/crowdin/client/translationmemory/TranslationMemoryApiTest.java +++ b/src/test/java/com/crowdin/client/translationmemory/TranslationMemoryApiTest.java @@ -1,6 +1,8 @@ package com.crowdin.client.translationmemory; import com.crowdin.client.core.model.DownloadLink; +import com.crowdin.client.core.model.OrderByField; +import com.crowdin.client.core.model.SortOrder; import com.crowdin.client.core.model.PatchOperation; import com.crowdin.client.core.model.PatchRequest; import com.crowdin.client.core.model.ResponseList; @@ -8,6 +10,7 @@ import com.crowdin.client.framework.RequestMock; import com.crowdin.client.framework.TestClient; import com.crowdin.client.translationmemory.model.AddTranslationMemoryRequest; +import com.crowdin.client.translationmemory.model.ListTmsParams; import com.crowdin.client.translationmemory.model.SearchConcordance; import com.crowdin.client.translationmemory.model.SearchConcordanceRequest; import com.crowdin.client.translationmemory.model.SearchOrganizationConcordanceRequest; @@ -24,6 +27,7 @@ import org.junit.jupiter.api.Test; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import static java.util.Collections.singletonList; @@ -45,6 +49,14 @@ public class TranslationMemoryApiTest extends TestClient { public List getMocks() { return Arrays.asList( RequestMock.build(this.url + "/tms", HttpGet.METHOD_NAME, "api/translationmemory/listTms.json"), + RequestMock.build(this.url + "/tms", HttpGet.METHOD_NAME, "api/translationmemory/listTms.json", new HashMap() {{ + put("groupId", 2L); + put("userId", 12L); + put("filter", "Knowledge"); + put("limit", 10); + put("offset", 5); + put("orderBy", "id%20desc"); + }}), RequestMock.build(this.url + "/tms", HttpPost.METHOD_NAME, "api/translationmemory/addTmRequest.json", "api/translationmemory/tm.json"), RequestMock.build(this.url + "/tms/" + tmId, HttpGet.METHOD_NAME, "api/translationmemory/tm.json"), RequestMock.build(this.url + "/tms/" + tmId, HttpDelete.METHOD_NAME), @@ -56,6 +68,7 @@ public List getMocks() { RequestMock.build(this.url + "/tms/" + tmId + "/imports/" + importId, HttpGet.METHOD_NAME, "api/translationmemory/tmImportStatus.json"), RequestMock.build(String.format("%s/tms/%d/segments", this.url, tmId), HttpDelete.METHOD_NAME), RequestMock.build(String.format("%s/projects/%d/tms/concordance", this.url, projectId), HttpPost.METHOD_NAME, "api/translationmemory/concordanceSearchRequest.json", "api/translationmemory/concordanceSearchResponse.json"), + RequestMock.build(String.format("%s/projects/%d/tms/concordance", this.url, projectId), HttpPost.METHOD_NAME, "api/translationmemory/concordanceSearchExpressionsRequest.json", "api/translationmemory/concordanceSearchResponse.json"), RequestMock.build(this.url + "/tms/concordance", HttpPost.METHOD_NAME, "api/translationmemory/organizationConcordanceSearchRequest.json", "api/translationmemory/concordanceSearchResponse.json") ); } @@ -77,6 +90,19 @@ public void searchConcordanceTest() { assertEquals(searchConcordanceResponseList.getData().get(0).getData().getSubstituted(), "62→100"); } + @Test + public void searchConcordanceExpressionsTest() { + SearchConcordanceRequest request = new SearchConcordanceRequest(); + request.setSourceLanguageId("en"); + request.setTargetLanguageId("de"); + request.setAutoSubstitution(true); + request.setMinRelevant(60); + request.setExpressions(Arrays.asList("Welcome!", "View")); + ResponseList responseList = this.getTranslationMemoryApi().searchConcordance(projectId, request); + assertEquals(1, responseList.getData().size()); + assertEquals(tmId, responseList.getData().get(0).getData().getTm().getId()); + } + @Test public void searchOrganizationConcordanceTest() { SearchOrganizationConcordanceRequest request = new SearchOrganizationConcordanceRequest(); @@ -107,14 +133,43 @@ public void listTmsTest_orderByNull() { assertEquals(name, translationMemoryResponseList.getData().get(0).getData().getName()); } + @Test + public void listTmsWithParamsTest() { + OrderByField orderById = new OrderByField(); + orderById.setFieldName("id"); + orderById.setOrderBy(SortOrder.DESC); + + ListTmsParams params = new ListTmsParams(); + params.setGroupId(2L); + params.setUserId(12L); + params.setFilter("Knowledge"); + params.setLimit(10); + params.setOffset(5); + params.setOrderByList(singletonList(orderById)); + ResponseList translationMemoryResponseList = this.getTranslationMemoryApi().listTms(params); + assertEquals(1, translationMemoryResponseList.getData().size()); + assertEquals(tmId, translationMemoryResponseList.getData().get(0).getData().getId()); + } + + @Test + public void listTmsWithNullParamsTest() { + ResponseList translationMemoryResponseList = this.getTranslationMemoryApi().listTms((ListTmsParams) null); + assertEquals(1, translationMemoryResponseList.getData().size()); + } + @Test public void addTmTest() { AddTranslationMemoryRequest request = new AddTranslationMemoryRequest(); request.setName(name); request.setLanguageId(languageId); + request.setGroupId(2L); + request.setIsShared(true); ResponseObject translationMemoryResponseObject = this.getTranslationMemoryApi().addTm(request); assertEquals(translationMemoryResponseObject.getData().getId(), tmId); assertEquals(translationMemoryResponseObject.getData().getName(), name); + assertEquals("el", translationMemoryResponseObject.getData().getLanguageId()); + assertEquals(false, translationMemoryResponseObject.getData().getIsShared()); + assertEquals("https://example.crowdin.com/u/tms/4", translationMemoryResponseObject.getData().getWebUrl()); } @Test diff --git a/src/test/java/com/crowdin/client/translationmemory/TranslationMemorySegmentsApiTest.java b/src/test/java/com/crowdin/client/translationmemory/TranslationMemorySegmentsApiTest.java index bdeb068f8..e30ff3632 100644 --- a/src/test/java/com/crowdin/client/translationmemory/TranslationMemorySegmentsApiTest.java +++ b/src/test/java/com/crowdin/client/translationmemory/TranslationMemorySegmentsApiTest.java @@ -65,6 +65,19 @@ public List getMocks() { }} ), + // LIST with params + RequestMock.build( + formUrl_tmSegments(tmId), + HttpGet.METHOD_NAME, + "api/translationmemory/segments/listTmSegmentsResponse.json", + new HashMap() {{ + put("limit", 20); + put("offset", 10); + put("orderBy", "id%20asc"); + put("croql", "count of records > 1"); + }} + ), + // CREATE RequestMock.build( formUrl_tmSegments(tmId), @@ -123,6 +136,22 @@ public void listTmSegments() { assertTmSegment(response.getData().get(0).getData()); } + @Test + public void listTmSegments_withParams() { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + OrderByField orderById = new OrderByField(); + orderById.setFieldName("id"); + orderById.setOrderBy(SortOrder.ASC); + + ListTmSegmentsParams params = new ListTmSegmentsParams(); + params.setLimit(20); + params.setOffset(10); + params.setOrderByList(singletonList(orderById)); + params.setCroql("count of records > 1"); + ResponseList response = this.getTranslationMemoryApi().listTmSegments(tmId, params); + assertTmSegment(response.getData().get(0).getData()); + } + @Test public void listTmSegments_orderByNull() { TimeZone.setDefault(TimeZone.getTimeZone("GMT")); diff --git a/src/test/resources/api/glossaries/addGlossaryRequest.json b/src/test/resources/api/glossaries/addGlossaryRequest.json index b206f7e31..86ea45f4d 100644 --- a/src/test/resources/api/glossaries/addGlossaryRequest.json +++ b/src/test/resources/api/glossaries/addGlossaryRequest.json @@ -1,5 +1,6 @@ { "name": "Be My Eyes iOS's Glossary", "languageId": "ro", - "groupId": 2 + "groupId": 2, + "isShared": false } diff --git a/src/test/resources/api/glossaries/addTermRequest.json b/src/test/resources/api/glossaries/addTermRequest.json index 18c175fa8..5f032d802 100644 --- a/src/test/resources/api/glossaries/addTermRequest.json +++ b/src/test/resources/api/glossaries/addTermRequest.json @@ -9,5 +9,9 @@ "note": "string", "url": "string", "conceptId": 0, - "translationOfTermId": 0 -} \ No newline at end of file + "translationOfTermId": 0, + "fields": { + "some-field-1": "some value 1", + "some-field-2": 12 + } +} diff --git a/src/test/resources/api/glossaries/concept.json b/src/test/resources/api/glossaries/concept.json index dcb0699f9..30f6bc7a1 100644 --- a/src/test/resources/api/glossaries/concept.json +++ b/src/test/resources/api/glossaries/concept.json @@ -19,6 +19,11 @@ } ], "createdAt": "2019-09-23T07:19:47+00:00", - "updatedAt": "2019-09-23T07:19:47+00:00" + "updatedAt": "2019-09-23T07:19:47+00:00", + "translatable": true, + "fields": { + "some-field-1": "some value 1", + "some-field-3": true + } } -} \ No newline at end of file +} diff --git a/src/test/resources/api/glossaries/concordanceSearchExpressionsRequest.json b/src/test/resources/api/glossaries/concordanceSearchExpressionsRequest.json new file mode 100644 index 000000000..07bd11021 --- /dev/null +++ b/src/test/resources/api/glossaries/concordanceSearchExpressionsRequest.json @@ -0,0 +1,8 @@ +{ + "sourceLanguageId": "en", + "targetLanguageId": "de", + "expressions": [ + "Welcome!", + "View" + ] +} diff --git a/src/test/resources/api/glossaries/exportGlossary.json b/src/test/resources/api/glossaries/exportGlossary.json index 96189f082..cc71269bd 100644 --- a/src/test/resources/api/glossaries/exportGlossary.json +++ b/src/test/resources/api/glossaries/exportGlossary.json @@ -7,18 +7,18 @@ ], "exportType": "terms", "statuses": [ - "preferred", - "admitted" + "PREFERRED", + "ADMITTED" ], "partsOfSpeech": [ - "noun", - "verb" + "NOUN", + "VERB" ], "types": [ - "full form" + "FULL_FORM" ], "genders": [ - "masculine" + "MASCULINE" ], "authorIds": [ 1, diff --git a/src/test/resources/api/glossaries/exportGlossaryFilters.json b/src/test/resources/api/glossaries/exportGlossaryFilters.json new file mode 100644 index 000000000..b7a093717 --- /dev/null +++ b/src/test/resources/api/glossaries/exportGlossaryFilters.json @@ -0,0 +1,37 @@ +{ + "format": "xlsx", + "exportFields": [ + "term", + "field_term-field", + "conceptField_concept-field" + ], + "exportType": "concepts", + "text": "value", + "caseSensitive": true, + "searchStrict": false, + "searchFullMatch": true, + "statuses": [ + "DRAFT", + "NOT_RECOMMENDED" + ], + "partsOfSpeech": [ + "ADJ", + "PROPN", + "SYM" + ], + "types": [ + "SHORT_FORM" + ], + "genders": [ + "COMMON", + "NEUTER" + ], + "authorIds": [ + 12 + ], + "languageIds": [ + "uk" + ], + "dateFrom": "2024-01-23T07:00:14.000+00:00", + "dateTo": "2024-09-27T07:00:14.000+00:00" +} diff --git a/src/test/resources/api/glossaries/exportGlossaryStatus.json b/src/test/resources/api/glossaries/exportGlossaryStatus.json index 1d4e238b4..663fadae3 100644 --- a/src/test/resources/api/glossaries/exportGlossaryStatus.json +++ b/src/test/resources/api/glossaries/exportGlossaryStatus.json @@ -4,7 +4,23 @@ "status": "created", "progress": 0, "attributes": { - "format": "csv" + "format": "csv", + "exportFields": [ + "term", + "description", + "partOfSpeech" + ], + "text": "value", + "caseSensitive": true, + "searchFullMatch": false, + "searchStrict": true, + "status": "DRAFT", + "partOfSpeech": "VERB", + "type": "ACRONYM", + "gender": "COMMON", + "authorId": 12, + "dateFrom": "2024-09-23T11:26:54+00:00", + "dateTo": "2024-12-23T11:26:54+00:00" }, "createdAt": "2019-09-23T07:06:43+00:00", "updatedAt": "2019-09-23T07:06:43+00:00", diff --git a/src/test/resources/api/glossaries/glossary.json b/src/test/resources/api/glossaries/glossary.json index 4a806ff29..a385d00a2 100644 --- a/src/test/resources/api/glossaries/glossary.json +++ b/src/test/resources/api/glossaries/glossary.json @@ -11,6 +11,9 @@ "projectIds": [ 6 ], - "createdAt": "2019-09-16T13:42:04+00:00" + "createdAt": "2019-09-16T13:42:04+00:00", + "languageId": "ro", + "isShared": false, + "webUrl": "https://example.crowdin.com/u/glossaries/2" } } diff --git a/src/test/resources/api/glossaries/term.json b/src/test/resources/api/glossaries/term.json index 44d5e377e..62ad72d33 100644 --- a/src/test/resources/api/glossaries/term.json +++ b/src/test/resources/api/glossaries/term.json @@ -9,6 +9,11 @@ "partOfSpeech": "verb", "lemma": "voir", "createdAt": "2019-09-23T07:19:47+00:00", - "updatedAt": "2019-09-23T07:19:47+00:00" + "updatedAt": "2019-09-23T07:19:47+00:00", + "status": "draft", + "fields": { + "some-field-1": "some value 1", + "some-field-2": 12 + } } } diff --git a/src/test/resources/api/glossaries/updateConceptRequest.json b/src/test/resources/api/glossaries/updateConceptRequest.json new file mode 100644 index 000000000..0b48fd170 --- /dev/null +++ b/src/test/resources/api/glossaries/updateConceptRequest.json @@ -0,0 +1,19 @@ +{ + "subject": "general", + "definition": "This is a sample definition.", + "translatable": true, + "note": "Any concept-level note information", + "url": "www.example.com", + "figure": "www.example.com/image.png", + "languagesDetails": [ + { + "languageId": "en", + "definition": "This is a sample definition.", + "note": "Some note" + } + ], + "fields": { + "some-field-1": "some value 1", + "some-field-3": true + } +} diff --git a/src/test/resources/api/translationmemory/addTmRequest.json b/src/test/resources/api/translationmemory/addTmRequest.json index 31fbd21ad..88d1c737d 100644 --- a/src/test/resources/api/translationmemory/addTmRequest.json +++ b/src/test/resources/api/translationmemory/addTmRequest.json @@ -1,4 +1,6 @@ { "name": "Knowledge Base's TM", - "languageId": "ro" + "languageId": "ro", + "groupId": 2, + "isShared": true } diff --git a/src/test/resources/api/translationmemory/concordanceSearchExpressionsRequest.json b/src/test/resources/api/translationmemory/concordanceSearchExpressionsRequest.json new file mode 100644 index 000000000..240374552 --- /dev/null +++ b/src/test/resources/api/translationmemory/concordanceSearchExpressionsRequest.json @@ -0,0 +1,10 @@ +{ + "sourceLanguageId": "en", + "targetLanguageId": "de", + "autoSubstitution": true, + "minRelevant": 60, + "expressions": [ + "Welcome!", + "View" + ] +} diff --git a/src/test/resources/api/translationmemory/tm.json b/src/test/resources/api/translationmemory/tm.json index 1142dace5..9eb7bcc4c 100644 --- a/src/test/resources/api/translationmemory/tm.json +++ b/src/test/resources/api/translationmemory/tm.json @@ -14,6 +14,9 @@ "projectIds": [ 2 ], - "createdAt": "2019-09-16T13:42:04+00:00" + "createdAt": "2019-09-16T13:42:04+00:00", + "languageId": "el", + "isShared": false, + "webUrl": "https://example.crowdin.com/u/tms/4" } } From 40acaf7acbd646170020513da3ea6207ced3217e Mon Sep 17 00:00:00 2001 From: Kateryna Oblakevych Date: Mon, 26 Oct 2026 20:42:30 -0700 Subject: [PATCH 2/3] test(glossaries): cover the OTHER to X part-of-speech export mapping Add a dedicated test that the deprecated ExportGlossaryRequest.setPartsOfSpeech sends PartOfSpeech.OTHER as "X" and maps it back, and document that the mapping follows the published API values (term `other`, export `X`) and Universal Dependencies semantics but is not confirmed by a live Crowdin API test. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../glossaries/model/ExportGlossaryRequest.java | 5 +++++ .../client/glossaries/model/ExportPartOfSpeech.java | 3 +++ .../client/glossaries/GlossariesApiTest.java | 13 +++++++++++++ 3 files changed, 21 insertions(+) diff --git a/src/main/java/com/crowdin/client/glossaries/model/ExportGlossaryRequest.java b/src/main/java/com/crowdin/client/glossaries/model/ExportGlossaryRequest.java index 8178b72be..1629be5e6 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/ExportGlossaryRequest.java +++ b/src/main/java/com/crowdin/client/glossaries/model/ExportGlossaryRequest.java @@ -125,6 +125,10 @@ public List getStatuses() { * auxiliary → AUX, coordinating conjunction → CCONJ, determiner → DET, interjection → INTJ, * noun → NOUN, numeral → NUM, particle → PRT, pronoun → PRON, proper noun → PROPN, * subordinating conjunction → SCONJ, verb → VERB, other → X. + *

+ * The mapping follows the published API values (glossary terms use {@code other}, glossary export uses {@code X}) + * and the Universal Dependencies semantics, where {@code X} means "other". It has not been confirmed by a live + * Crowdin API test. * * @param partsOfSpeech term parts of speech * @deprecated use {@link #termPartsOfSpeech} @@ -274,6 +278,7 @@ private static ExportPartOfSpeech toExportPartOfSpeech(PartOfSpeech partOfSpeech case VERB: return ExportPartOfSpeech.VERB; case OTHER: + // Universal Dependencies "X" = other; not confirmed by a live Crowdin API test return ExportPartOfSpeech.X; default: return null; diff --git a/src/main/java/com/crowdin/client/glossaries/model/ExportPartOfSpeech.java b/src/main/java/com/crowdin/client/glossaries/model/ExportPartOfSpeech.java index 062b4454a..81386d851 100644 --- a/src/main/java/com/crowdin/client/glossaries/model/ExportPartOfSpeech.java +++ b/src/main/java/com/crowdin/client/glossaries/model/ExportPartOfSpeech.java @@ -5,6 +5,9 @@ /** * Part of speech filter values accepted by the glossary export endpoint * (Universal Dependencies style tags, e.g. {@code ADJ}, {@code PROPN}, {@code CCONJ}). + *

+ * {@link #X} means "other" in Universal Dependencies; the deprecated + * {@link ExportGlossaryRequest#setPartsOfSpeech(java.util.List)} maps {@link PartOfSpeech#OTHER} to it. */ public enum ExportPartOfSpeech implements EnumConverter { NOUN, VERB, ADJ, PRON, PROPN, DET, ADV, ADP, CCONJ, SCONJ, NUM, INTJ, AUX, PRT, SYM, X; diff --git a/src/test/java/com/crowdin/client/glossaries/GlossariesApiTest.java b/src/test/java/com/crowdin/client/glossaries/GlossariesApiTest.java index 7ed0705a6..567f181e1 100644 --- a/src/test/java/com/crowdin/client/glossaries/GlossariesApiTest.java +++ b/src/test/java/com/crowdin/client/glossaries/GlossariesApiTest.java @@ -340,6 +340,19 @@ public void exportGlossaryWithFiltersTest() { assertEquals(singletonList(Gender.NEUTER), request.getGenders()); } + @Test + public void exportGlossaryRequestDeprecatedSetterMapsOtherPartOfSpeechToXTest() { + ExportGlossaryRequest request = new ExportGlossaryRequest(); + request.setPartsOfSpeech(singletonList(PartOfSpeech.OTHER)); + + assertEquals(singletonList(ExportPartOfSpeech.X), request.getTermPartsOfSpeech()); + assertEquals("{\"partsOfSpeech\":[\"X\"]}", new JacksonJsonTransformer().convert(request)); + assertEquals(singletonList(PartOfSpeech.OTHER), request.getPartsOfSpeech()); + + request.setPartsOfSpeech(Arrays.asList(PartOfSpeech.NOUN, PartOfSpeech.OTHER)); + assertEquals("{\"partsOfSpeech\":[\"NOUN\",\"X\"]}", new JacksonJsonTransformer().convert(request)); + } + @Test public void exportGlossaryRequestDeprecatedSettersConversionTest() { ExportGlossaryRequest request = new ExportGlossaryRequest(); From 0529bdadc3895a8857d986adabd621e034d9f9f9 Mon Sep 17 00:00:00 2001 From: Kateryna Oblakevych Date: Mon, 26 Oct 2026 22:03:37 -0700 Subject: [PATCH 3/3] fix(glossaries): rename UpdateConceptRequest overload to updateConceptWithRequest The new updateConcept(Long, Long, UpdateConceptRequest) overload made existing calls such as updateConcept(id, id, null) ambiguous at compile time. It is now named updateConceptWithRequest; the original updateConcept(Long, Long, Concept) keeps its signature, deprecation and runtime behaviour. Add a compatibility test that calls updateConcept(id, id, null). Co-Authored-By: Claude Opus 5.5 (1M context) --- .../crowdin/client/glossaries/GlossariesApi.java | 6 ++++-- .../client/glossaries/GlossariesApiTest.java | 14 +++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/crowdin/client/glossaries/GlossariesApi.java b/src/main/java/com/crowdin/client/glossaries/GlossariesApi.java index 91d9956f8..5d730a77a 100644 --- a/src/main/java/com/crowdin/client/glossaries/GlossariesApi.java +++ b/src/main/java/com/crowdin/client/glossaries/GlossariesApi.java @@ -128,7 +128,7 @@ public ResponseObject getConcept(Long glossaryId, Long conceptId) throw *

  • Enterprise API Documentation
  • * * @deprecated sends the response model {@link Concept} (including read-only properties such as id, userId and - * dates) as the request body; use {@link #updateConcept(Long, Long, UpdateConceptRequest)} instead + * dates) as the request body; use {@link #updateConceptWithRequest(Long, Long, UpdateConceptRequest)} instead */ @Deprecated public ResponseObject updateConcept(Long glossaryId, Long conceptId, Concept request) throws HttpException, HttpBadRequestException { @@ -137,6 +137,8 @@ public ResponseObject updateConcept(Long glossaryId, Long conceptId, Co } /** + * Updates a concept using a request model that contains only the properties accepted by the API. + * * @param glossaryId glossary identifier * @param conceptId concept identifier * @param request request object @@ -146,7 +148,7 @@ public ResponseObject updateConcept(Long glossaryId, Long conceptId, Co *
  • Enterprise API Documentation
  • * */ - public ResponseObject updateConcept(Long glossaryId, Long conceptId, UpdateConceptRequest request) throws HttpException, HttpBadRequestException { + public ResponseObject updateConceptWithRequest(Long glossaryId, Long conceptId, UpdateConceptRequest request) throws HttpException, HttpBadRequestException { ConceptResponseObject conceptResponseObject = this.httpClient.put(this.url + "/glossaries/" + glossaryId + "/concepts/" + conceptId, request, new HttpRequestConfig(), ConceptResponseObject.class); return ResponseObject.of(conceptResponseObject.getData()); } diff --git a/src/test/java/com/crowdin/client/glossaries/GlossariesApiTest.java b/src/test/java/com/crowdin/client/glossaries/GlossariesApiTest.java index 567f181e1..c0c35190e 100644 --- a/src/test/java/com/crowdin/client/glossaries/GlossariesApiTest.java +++ b/src/test/java/com/crowdin/client/glossaries/GlossariesApiTest.java @@ -225,12 +225,24 @@ public void updateConceptWithRequestTest() { fields.put("some-field-3", true); request.setFields(fields); - ResponseObject conceptResponseObject = this.getGlossariesApi().updateConcept(glossaryId, conceptId, request); + ResponseObject conceptResponseObject = this.getGlossariesApi().updateConceptWithRequest(glossaryId, conceptId, request); assertEquals(glossaryId, conceptResponseObject.getData().getId()); assertEquals(subject, conceptResponseObject.getData().getSubject()); assertEquals("some value 1", conceptResponseObject.getData().getFields().get("some-field-1")); } + /** + * Source compatibility: {@code updateConcept(id, id, null)} must keep compiling (no ambiguous overload) and + * still call the original {@code PUT /glossaries/{glossaryId}/concepts/{conceptId}} endpoint. + */ + @Test + @SuppressWarnings("deprecation") + public void updateConceptWithNullRequestCompatibilityTest() { + ResponseObject conceptResponseObject = this.getGlossariesApi().updateConcept(glossaryId, conceptId, null); + assertEquals(glossaryId, conceptResponseObject.getData().getId()); + assertEquals(subject, conceptResponseObject.getData().getSubject()); + } + @Test public void deleteConceptTest() { this.getGlossariesApi().deleteConcept(glossaryId, conceptId);