Skip to content

Commit c7a61f2

Browse files
committed
The getContext method has been fixed using a third-party API. The library now uses the ZenRows API and requires an API key to function.
- Added the apikey.properties file - Refactored the getContext method - New messages have been added to the message.properties file - README.md file has been expanded - Added new tests
1 parent e9c0ae1 commit c7a61f2

File tree

10 files changed

+212
-32
lines changed

10 files changed

+212
-32
lines changed

README.md

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,39 @@ services used by Reverso is not available, though some are known.
1010
They work and behave differently, so our main goal was to create
1111
a unified and simple API, the responses of which would have a clear
1212
structure and behavior.
13+
1314
## Navigation
15+
- [How to use this library](#How-to-use)
1416
- [Main components](#main-components)
1517
- [Response](#response)
1618
- [Language](#language)
1719
- [Usage](#usage)
1820
- [Standart workfolw](#standard-workflow)
1921
- [getVoiceStream](#getvoicestream)
22+
- [getContext problems](#getcontext-problems)
2023
- [Response status](#response-status)
2124
- [Success](#success)
2225
- [Failure](#failure)
2326
- [Credits](#credits)
2427

28+
## How to use
29+
**Recently, the [Context page](https://context.reverso.net/) started using Cloudflare
30+
protection. The solution to this was to use a third-party scraping API that
31+
utilizes a headless browser to bypass the protection (by executing JavaScript
32+
code on the page).**
33+
34+
Since ZenRows was chosen as the third-party API, follow these simple steps to use it:
35+
36+
Go to the [website](https://www.zenrows.com/) and complete a simple registration.
37+
- Answer a few questions during the initial registration (this part can be skipped).
38+
- Obtain an API key as part of the free trial period.
39+
- Insert it into the already created apikey.properties file in the format:
40+
`apiKey=yourKey`
41+
- Everything is ready to use!
42+
43+
P.S It is also worth mentioning that this key can be inserted into the constructor
44+
when creating a [Reverso object](#usage). However, the first option is preferable.
45+
2546
## Main components
2647
### Response
2748
Abstract class Response declares the common structure of all responses
@@ -58,6 +79,7 @@ public enum Language {
5879
ARABIC("ar", true, null),
5980
GERMAN("de", true, null),
6081
ENGLISH("en", true, "eng"),
82+
ROMANIAN("ro", false, null)
6183
}
6284
```
6385
Each object in this enumeration has fields that help determine whether
@@ -74,15 +96,18 @@ does not support the respective functionality.
7496
For example:
7597
```java
7698
JAPANESE("ja", true, null);
99+
ROMANIAN("ro", false, null);
77100
```
78101

79-
The third field is null, this means that **spellCheck** are not available
80-
for *japanese* language.
102+
<i> Japanese </i> – Synonym search and verb conjugation are available, but spell check is not. <br>
103+
<i>Romanian </i> – Only synonyms are available. Verb conjugation and spell check are not available.
104+
And so on.
81105

82106
## Usage
83107
The fundamental class, **Reverso**, manages all interactions with the API.
84-
First, we need to create an instance of this class.
85-
## Standard workflow
108+
First, we need to create an instance of this class. As mentioned above, the API key can also
109+
be added to the class constructor, although this is not mandatory.
110+
### Standard workflow
86111
To retrieve the necessary data, we must call one of the methods
87112
of this class.
88113

@@ -126,7 +151,7 @@ Here's the output :
126151
Most methods are very similar, some require two languages in their
127152
arguments, such as the translation method, which is logical.
128153

129-
## getVoiceStream
154+
### getVoiceStream
130155
However, there is a method that differs slightly from the others:
131156

132157
`VoiceResponse getVoiceStream(Voice voice, String sourceText)`
@@ -157,6 +182,13 @@ method, the *byte[] mp3Data* field is ignored. However, in this class, we
157182
can also get this field as a Base64 string by invoking
158183
the `getAudioAsBase64()` method.
159184

185+
### getContext problems
186+
187+
Unfortunately, due to the use of a third-party API, the method may be unstable. The response
188+
time can take a while, which can sometimes be critical. This is because the API needs to process
189+
the request in a headless browser mode and execute the necessary JavaScripts, which has significantly
190+
slowed it down. Also, we cannot predict the state of the third-party API servers at the time of use.
191+
160192
## Response status
161193
Each response object has a `boolean isOK` field, along with a
162194
corresponding getter method, which indicates the success of our

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>anton3413</groupId>
88
<artifactId>reverso_java_API</artifactId>
9-
<version>1.0.4</version>
9+
<version>1.1.0</version>
1010
<packaging>jar</packaging>
1111

1212
<name>Reverso API</name>

src/main/java/reverso/Reverso.java

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@
1111
import reverso.language.Language;
1212
import reverso.language.Voice;
1313
import java.io.IOException;
14-
import java.util.*;
14+
import java.util.Base64;
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.Properties;
1518

1619
public class Reverso {
20+
21+
private String apiKey;
1722
private HtmlParser parser;
1823
private Properties properties;
1924
private static final String SYNONYM_URL = "https://synonyms.reverso.net/synonym/";
@@ -22,9 +27,13 @@ public class Reverso {
2227
private static final String CONJUGATION_URL = "https://conjugator.reverso.net/conjugation";
2328
private static final String SPELLCHECK_URL = "https://orthographe.reverso.net/api/v1/Spelling";
2429

25-
{
26-
initializeProperties();
27-
initializeParser(properties);
30+
public Reverso(String apiKey){
31+
initialize();
32+
this.apiKey = apiKey;
33+
}
34+
public Reverso(){
35+
initialize();
36+
initializeAPIKey();
2837
}
2938

3039
public SynonymResponse getSynonyms(Language language, String word) {
@@ -66,18 +75,31 @@ public ContextResponse getContext(Language sourceLanguage, Language targetLangua
6675
contextResponse.setErrorMessage(properties.getProperty("message.error.context.sameLanguage"));
6776
return contextResponse;
6877
}
69-
String URL = CONTEXT_URL + sourceLanguage + "-" + targetLanguage + "/" + word;
78+
String contextURL = CONTEXT_URL + sourceLanguage + "-" + targetLanguage + "/" + word;
7079

80+
String APIURL = "https://api.zenrows.com/v1/?apikey=" + apiKey + "&url=" + contextURL + "&js_render=true" +
81+
"&original_status=true";
7182
Document document;
7283
Map<String, String> contextMap;
7384
String[] translations;
7485
Connection.Response response;
7586
try {
76-
response = Jsoup.connect(URL)
77-
.header("User-Agent", RandomUserAgent.getRandomUserAgent())
87+
response = Jsoup.connect(APIURL)
7888
.ignoreHttpErrors(true)
89+
.ignoreContentType(true)
90+
.followRedirects(false)
91+
.header("User-Agent",RandomUserAgent.getRandomUserAgent())
92+
.timeout(20000)
7993
.execute();
80-
document = response.parse();
94+
if (response.statusCode() == 404) {
95+
contextResponse.setErrorMessage(properties.getProperty("message.error.context.noResults"));
96+
return contextResponse;
97+
}
98+
if(response.statusCode()== 401){
99+
contextResponse.setErrorMessage(properties.getProperty("message.error.apikey.invalid"));
100+
}
101+
document = response.parse();
102+
81103
} catch (IOException e) {
82104
contextResponse.setErrorMessage(properties.getProperty("message.error.connection"));
83105
return contextResponse;
@@ -87,17 +109,13 @@ public ContextResponse getContext(Language sourceLanguage, Language targetLangua
87109
return contextResponse;
88110
}
89111
translations = parser.parseContextPageGetTranslations(document);
90-
if (translations.length == 0) {
91-
contextResponse.setErrorMessage(properties.getProperty("message.error.context.noResults"));
92-
return contextResponse;
93-
}
94112
contextMap = parser.parseContextPage(document);
95113

96114
contextResponse.setContextResults(contextMap);
97115
contextResponse.setOK(true);
98116
contextResponse.setTranslations(translations);
99117
return contextResponse;
100-
}
118+
}
101119

102120
public VoiceResponse getVoiceStream(Voice voice, String text) {
103121

@@ -204,6 +222,11 @@ public SpellCheckResponse getSpellCheck(Language language, String text) {
204222
return spellCheckResponse;
205223
}
206224

225+
private void initialize(){
226+
initializeProperties();
227+
initializeParser(properties);
228+
}
229+
207230
private void initializeProperties() {
208231
try {
209232
properties = new Properties();
@@ -216,6 +239,23 @@ private void initializeProperties() {
216239
private void initializeParser(Properties properties){
217240
parser = new HtmlParser(properties);
218241
}
242+
243+
private void initializeAPIKey() {
244+
Properties properties = new Properties();
245+
try {
246+
properties.load(Reverso.class.getResourceAsStream("/apikey.properties"));
247+
apiKey = properties.getProperty("apiKey");
248+
validateAPIKey(apiKey);
249+
} catch (IOException e) {
250+
throw new RuntimeException(properties.getProperty("message.error.apikey.init"), e);
251+
252+
}
253+
}
254+
private void validateAPIKey(String apiKey) {
255+
if (apiKey == null || apiKey.isBlank()) {
256+
throw new RuntimeException(properties.getProperty("message.error.apikey.blank"));
257+
}
258+
}
219259
}
220260

221261

src/main/java/reverso/data/response/entity/Stats.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,8 @@ public void setLongestSentence(int longestSentence) {
5353
public String toJson(){
5454
return new Gson().toJson(this);
5555
}
56+
57+
public String toString(){
58+
return toJson();
59+
}
5660
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
apiKey=

src/main/resources/messages.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ message.error.spellCheck.unsupportedLanguage=This language does not support this
2020
The list of supported languages includes English, French, Spanish, and Italian.
2121
message.error.spellCheck.noErrorsOrMismatchedLanguage=The input text matches the original. \
2222
Either there are no errors, or the specified language does not match the language of the text.
23+
message.error.apikey.blank=API key is missing in apikey.properties
24+
message.error.apikey.init=Problem during apikey.properties file initialization. Possibly the path is incorrect
25+
message.error.apikey.invalid=ZenRows API key is invalid or expired

src/test/java/reverso/GetContextTest.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.logging.Level;
1010
import java.util.logging.Logger;
1111
import static org.junit.jupiter.api.Assertions.*;
12+
import org.junit.jupiter.api.Test;
1213

1314
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
1415
public class GetContextTest {
@@ -29,6 +30,30 @@ void initializeReversoAndProperties() {
2930
}
3031
}
3132

33+
@Test
34+
void successGermanSpanishContextRequest(){
35+
contextResponse = reverso.getContext(Language.GERMAN,Language.SPANISH,"Wirtschaft");
36+
37+
assertTrue(contextResponse.isOK());
38+
assertNull(contextResponse.getErrorMessage());
39+
assertEquals(Language.GERMAN.toString(),contextResponse.getSourceLanguage());
40+
assertNotNull(contextResponse.getContextResults());
41+
assertTrue(contextResponse.getContextResults().size()>15);
42+
assertTrue(contextResponse.getTranslations().length>5);
43+
}
44+
45+
@Test
46+
void successUkrainianFrenchContextRequest(){
47+
contextResponse = reverso.getContext(Language.UKRAINIAN,Language.FRENCH,"небокрай");
48+
49+
assertTrue(contextResponse.isOK());
50+
assertNull(contextResponse.getErrorMessage());
51+
assertEquals(Language.UKRAINIAN.toString(),contextResponse.getSourceLanguage());
52+
assertNotNull(contextResponse.getContextResults());
53+
assertTrue(contextResponse.getContextResults().size()>15);
54+
assertTrue(contextResponse.getTranslations().length>0);
55+
}
56+
3257
@Test
3358
void successRussianEnglishContextRequest(){
3459
contextResponse = reverso.getContext(Language.ENGLISH,Language.RUSSIAN,"красивый");
@@ -54,7 +79,20 @@ void successArabicItalianContextRequest(){
5479
}
5580

5681
@Test
57-
void FailureUnsupportedKoreanUkrainianContextRequest(){
82+
void failureUnsupportedKoreanArabicContextRequest(){
83+
contextResponse = reverso.getContext(Language.KOREAN,Language.ARABIC,"ありがとう");
84+
85+
assertFalse(contextResponse.isOK());
86+
assertNotNull(contextResponse.getErrorMessage());
87+
assertEquals(Language.ARABIC.toString(),contextResponse.getTargetLanguage());
88+
assertNull(contextResponse.getContextResults());
89+
assertEquals(properties.getProperty("message.error.context.UnsupportedLanguages"),
90+
contextResponse.getErrorMessage());
91+
}
92+
93+
94+
@Test
95+
void failureUnsupportedKoreanUkrainianContextRequest(){
5896
contextResponse = reverso.getContext(Language.KOREAN,Language.UKRAINIAN,"красивий");
5997

6098
assertFalse(contextResponse.isOK());
@@ -90,6 +128,19 @@ void FailureIncorrectWordContextRequest(){
90128
assertEquals(properties.getProperty("message.error.context.noResults"),
91129
contextResponse.getErrorMessage());
92130
}
131+
@Test
132+
void FailureIncorrectWordContextRequest2(){
133+
contextResponse = reverso.getContext(Language.ITALIAN, Language.RUSSIAN,"someincorrectstringexample");
134+
135+
assertFalse(contextResponse.isOK());
136+
assertNotNull(contextResponse.getErrorMessage());
137+
assertEquals(Language.RUSSIAN.toString(),contextResponse.getTargetLanguage());
138+
assertNull(contextResponse.getContextResults());
139+
assertNull(contextResponse.getTranslations());
140+
assertEquals(properties.getProperty("message.error.context.noResults"),
141+
contextResponse.getErrorMessage());
142+
}
143+
93144
@Test
94145
void FailureSameLanguagesContextRequest(){
95146
contextResponse = reverso.getContext(Language.POLISH,Language.POLISH,"ja pierdole bobr");

0 commit comments

Comments
 (0)