Skip to content

Commit 1cde4d4

Browse files
hive-botclaude
authored andcommitted
fix: validate spoken terms like glossary terms
Terms are joined with newlines on the wire, so a term containing a newline was silently sent as several terms. createSpokenTerms(), updateSpokenTerms() and replaceSpokenTermsList() now check each term with GlossaryEntries.validateGlossaryTerm() before any request and throw the same IllegalArgumentException as the glossary methods. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent c9e9132 commit 1cde4d4

4 files changed

Lines changed: 34 additions & 3 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
`updateSpokenTerms()`, `getSpokenTermsEntries()`, `replaceSpokenTermsList()`,
1212
`deleteSpokenTerms()` and `deleteSpokenTermsList()`, with the new classes
1313
`SpokenTerms`, `SpokenTermsList` and `SpokenTermsEntries`. Terms are passed
14-
and returned as a list of strings.
14+
and returned as a list of strings and are checked like glossary terms, so a
15+
term containing a newline throws an `IllegalArgumentException`.
1516
- Added the API Pro fields of `GET /v2/usage` to `Usage`:
1617
`getApiKeyCharacter()` (per-key character usage), `getSpeechToTextMinutes()`,
1718
`getSpeechToSpeechMinutes()`, `getStartTime()` and `getEndTime()` (billing

‎README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,9 @@ collections can be stored with your account, each with a user-specified name
691691
and a uniquely-assigned ID.
692692

693693
Each method that takes a collection accepts either its ID or a `SpokenTerms`
694-
object. The terms are not validated client-side; the API rejects invalid terms
694+
object. Terms are checked like glossary terms before any request is sent: a
695+
term that is empty or contains a control character such as a newline throws an
696+
`IllegalArgumentException`. Other invalid term lists are rejected by the API
695697
with a `DeepLException`.
696698

697699
#### Creating a Spoken Terms collection

‎deepl-java/src/main/java/com/deepl/api/DeepLClient.java‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,7 @@ public void deleteMultilingualGlossaryDictionary(
782782
* @param name User-defined name of the collection.
783783
* @param termLists Term lists of the collection, one {@link SpokenTermsEntries} per language.
784784
* @return {@link SpokenTerms} object with details about the new collection.
785+
* @throws IllegalArgumentException If a term is invalid, as checked for glossary terms.
785786
* @throws InterruptedException If the thread is interrupted during execution of this function.
786787
* @throws DeepLException If any error occurs while communicating with the DeepL API, a {@link
787788
* DeepLException} or a derived class will be thrown.
@@ -856,6 +857,7 @@ public SpokenTerms getSpokenTerms(SpokenTerms spokenTerms)
856857
* current term lists.
857858
* @return {@link SpokenTerms} object with details about the updated collection.
858859
* @throws NotFoundException If no collection with the given ID is found.
860+
* @throws IllegalArgumentException If a term is invalid, as checked for glossary terms.
859861
* @throws InterruptedException If the thread is interrupted during execution of this function.
860862
* @throws DeepLException If any error occurs while communicating with the DeepL API.
861863
*/
@@ -929,6 +931,7 @@ public SpokenTermsEntries getSpokenTermsEntries(SpokenTerms spokenTerms, String
929931
* @param entries Language and terms of the new term list.
930932
* @return {@link SpokenTermsList} object with details about the new term list.
931933
* @throws NotFoundException If no collection with the given ID is found.
934+
* @throws IllegalArgumentException If a term is invalid, as checked for glossary terms.
932935
* @throws InterruptedException If the thread is interrupted during execution of this function.
933936
* @throws DeepLException If any error occurs while communicating with the DeepL API.
934937
*/
@@ -1716,8 +1719,14 @@ private static String langQueryString(String lang) {
17161719
return createQueryString(queryParams);
17171720
}
17181721

1719-
/** Builds the JSON object of one term list; the API expects the terms newline-separated. */
1722+
/**
1723+
* Builds the JSON object of one term list; the API expects the terms newline-separated, so each
1724+
* term is checked like a glossary term first: a term containing a newline would split.
1725+
*/
17201726
private static Map<String, Object> createSpokenTermsListData(SpokenTermsEntries entries) {
1727+
for (String term : entries.getEntries()) {
1728+
GlossaryEntries.validateGlossaryTerm(term);
1729+
}
17211730
Map<String, Object> data = new LinkedHashMap<>();
17221731
data.put("lang", entries.getLang());
17231732
data.put("entries", String.join("\n", entries.getEntries()));

‎deepl-java/src/offlineTest/java/com/deepl/api/SpokenTermsTest.java‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@
33
// license that can be found in the LICENSE file.
44
package com.deepl.api;
55

6+
import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor;
7+
import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
68
import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson;
79
import static com.github.tomakehurst.wiremock.client.WireMock.get;
810
import static com.github.tomakehurst.wiremock.client.WireMock.okJson;
911
import static com.github.tomakehurst.wiremock.client.WireMock.put;
1012
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
1113
import static org.junit.jupiter.api.Assertions.assertEquals;
14+
import static org.junit.jupiter.api.Assertions.assertThrows;
1215

1316
import java.util.Arrays;
17+
import java.util.Collections;
18+
import java.util.List;
1419
import org.junit.jupiter.api.Test;
1520
import org.junit.jupiter.api.extension.RegisterExtension;
1621

@@ -35,4 +40,18 @@ void joinsEntriesWithNewlinesWhenSendingAndSplitsThemWhenReading() throws Except
3540
assertEquals("en", entries.getLang());
3641
assertEquals(Arrays.asList("a", "b"), entries.getEntries());
3742
}
43+
44+
@Test
45+
void rejectsATermContainingANewlineBeforeSendingAnyRequest() {
46+
List<SpokenTermsEntries> termLists =
47+
Collections.singletonList(new SpokenTermsEntries("en", Arrays.asList("DeepL", "a\nb")));
48+
49+
assertThrows(IllegalArgumentException.class, () -> client.createSpokenTerms("n", termLists));
50+
assertThrows(
51+
IllegalArgumentException.class, () -> client.updateSpokenTerms("st1", null, termLists));
52+
assertThrows(
53+
IllegalArgumentException.class,
54+
() -> client.replaceSpokenTermsList("st1", termLists.get(0)));
55+
wm.verify(0, anyRequestedFor(anyUrl()));
56+
}
3857
}

0 commit comments

Comments
 (0)