From 562eae824e8164a7dfdcc4299d89815f6fc31fcb Mon Sep 17 00:00:00 2001 From: Kleberson Canuto Date: Mon, 12 May 2025 08:57:49 -0300 Subject: [PATCH 1/2] Add new routes to SDK --- README.md | 6 + src/main/java/com/nuveo/ultraocr/Client.java | 91 ++++++++++ .../java/com/nuveo/ultraocr/Constants.java | 3 + .../ultraocr/responses/BatchInfoResponse.java | 121 +++++++++++++ .../ultraocr/responses/BatchResultJob.java | 104 ++++++++++++ .../responses/BatchResultStorageResponse.java | 26 +++ .../ultraocr/responses/JobInfoResponse.java | 159 ++++++++++++++++++ .../ultraocr/responses/JobResultResponse.java | 35 ---- .../com/nuveo/ultraocr/responses/Result.java | 38 +++++ .../java/com/nuveo/ultraocr/ClientTest.java | 98 +++++++++++ 10 files changed, 646 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/nuveo/ultraocr/responses/BatchInfoResponse.java create mode 100644 src/main/java/com/nuveo/ultraocr/responses/BatchResultJob.java create mode 100644 src/main/java/com/nuveo/ultraocr/responses/BatchResultStorageResponse.java create mode 100644 src/main/java/com/nuveo/ultraocr/responses/JobInfoResponse.java create mode 100644 src/main/java/com/nuveo/ultraocr/responses/Result.java diff --git a/README.md b/README.md index 102186d..e48f0d5 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,12 @@ With the job or batch id, you can get the job result or batch status with: BatchStatusResponse response = client.getBatchStatus("BATCH_ID"); // Batches JobResultResponse jobResponse = client.getJobResult("JOB_ID", "JOB_ID"); // Simple jobs JobResultResponse jobResponse2 = client.getJobResult("BATCH_ID", "JOB_ID"); // Jobs belonging to batches +List batchResult = client.getBatchResult("BATCH_ID"); // Get batch jobs result as array +BatchResultStorageResponse storage = client.getBatchResultStorage("BATCH_ID", params); // Get batch jobs result in a file + +// More details about job and batch +BatchInfoResponse batchInfo = client.getBatchInfo("BATCH_ID"); // Batches info (without jobs info) +JobInfoResponse jobInfo = client.getJobInfo("JOB_ID"); // Jobs info (single jobs only) ``` Alternatively, you can use a utily `waitForJobDone` or `waitForBatchDone`: diff --git a/src/main/java/com/nuveo/ultraocr/Client.java b/src/main/java/com/nuveo/ultraocr/Client.java index 0ea33e1..370cd88 100644 --- a/src/main/java/com/nuveo/ultraocr/Client.java +++ b/src/main/java/com/nuveo/ultraocr/Client.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; /** * Client to help on UltraOCR usage. For more details about all arguments and @@ -261,6 +262,15 @@ private HttpResponse get(String url, Map params) return this.httpClient.send(request, BodyHandlers.ofString()); } + private String getBatchResult(String batchKsuid, Map params) + throws IOException, InterruptedException, InvalidStatusCodeException { + String url = String.format("%s/ocr/batch/result/%s", this.baseUrl, batchKsuid); + HttpResponse response = this.get(url, params); + validateStatus(Constants.STATUS_OK, response.statusCode()); + + return response.body(); + } + /** * Generate signed url to send the document. * @@ -816,4 +826,85 @@ public List getJobs(String start, String end) return jobs; } + + /** + * Get the job info with more details. + * + * @param jobKsuid the id of the job, given on job creation. + * @return the job with infos. + * @see JobInfoResponse + * @throws InvalidStatusCodeException if status code is not 200. + * @throws InterruptedException if http request fail. + * @throws IOException if http request fail. + */ + public JobInfoResponse getJobInfo(String jobKsuid) + throws IOException, InterruptedException, InvalidStatusCodeException { + String url = String.format("%s/ocr/job/info/%s", this.baseUrl, jobKsuid); + HttpResponse response = this.get(url, new HashMap<>()); + validateStatus(Constants.STATUS_OK, response.statusCode()); + + Gson gson = new Gson(); + return gson.fromJson(response.body(), JobInfoResponse.class); + } + + /** + * Get the infos of the batch with more details. + * + * @param batchKsuid the id of the batch, given on batch creation. + * @return the batch with infos. + * @see BatchInfoResponse + * @throws InvalidStatusCodeException if status code is not 200. + * @throws InterruptedException if http request fail. + * @throws IOException if http request fail. + */ + public BatchInfoResponse getBatchInfo(String batchKsuid) + throws IOException, InterruptedException, InvalidStatusCodeException { + String url = String.format("%s/ocr/batch/info/%s", this.baseUrl, batchKsuid); + HttpResponse response = this.get(url, new HashMap<>()); + validateStatus(Constants.STATUS_OK, response.statusCode()); + + Gson gson = new Gson(); + return gson.fromJson(response.body(), BatchInfoResponse.class); + } + + /** + * Get the batch jobs results as array. + * + * @param batchKsuid the id of the batch, given on batch creation. + * @return the batch jobs results. + * @see List + * @throws InvalidStatusCodeException if status code is not 200. + * @throws InterruptedException if http request fail. + * @throws IOException if http request fail. + */ + public List getBatchResult(String batchKsuid) + throws IOException, InterruptedException, InvalidStatusCodeException { + Map params = new HashMap<>(); + params.put(Constants.RETURN_ATTRIBUTE, Constants.RETURN_REQUEST); + String body = this.getBatchResult(batchKsuid, params); + + Gson gson = new Gson(); + return gson.fromJson(body, new TypeToken>() { + }.getType()); + } + + /** + * Generate url to download a file containing the batch jobs results. + * + * @param batchKsuid the id of the batch, given on batch creation. + * @param params the query parameters based on UltraOCR Docs. + * @return the url to download result file. + * @see BatchResultStorageResponse + * @throws InvalidStatusCodeException if status code is not 200. + * @throws InterruptedException if http request fail. + * @throws IOException if http request fail. + */ + public BatchResultStorageResponse getBatchResultStorage(String batchKsuid, Map params) + throws IOException, InterruptedException, InvalidStatusCodeException { + params.put(Constants.RETURN_ATTRIBUTE, Constants.RETURN_STORAGE); + String body = this.getBatchResult(batchKsuid, params); + + Gson gson = new Gson(); + return gson.fromJson(body, BatchResultStorageResponse.class); + } } \ No newline at end of file diff --git a/src/main/java/com/nuveo/ultraocr/Constants.java b/src/main/java/com/nuveo/ultraocr/Constants.java index 649018a..ecda432 100644 --- a/src/main/java/com/nuveo/ultraocr/Constants.java +++ b/src/main/java/com/nuveo/ultraocr/Constants.java @@ -21,6 +21,9 @@ public class Constants { public static final String KEY_EXTRA_URL = "extra_document"; public static final String FLAG_TRUE = "true"; public static final String BASE64_ATTRIBUTE = "base64"; + public static final String RETURN_ATTRIBUTE = "return"; + public static final String RETURN_REQUEST = "request"; + public static final String RETURN_STORAGE = "storage"; private Constants() { diff --git a/src/main/java/com/nuveo/ultraocr/responses/BatchInfoResponse.java b/src/main/java/com/nuveo/ultraocr/responses/BatchInfoResponse.java new file mode 100644 index 0000000..7e8ca0b --- /dev/null +++ b/src/main/java/com/nuveo/ultraocr/responses/BatchInfoResponse.java @@ -0,0 +1,121 @@ +package com.nuveo.ultraocr.responses; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +public class BatchInfoResponse { + @SerializedName("batch_id") + private String batchId; + + @SerializedName("client_id") + private String clientId; + + @SerializedName("company_id") + private String companyId; + + @SerializedName("validation_id") + private String validationId; + + @SerializedName("created_at") + private String createdAt; + + @SerializedName("total_jobs") + private int totalJobs; + + @SerializedName("total_processed") + private int totalProcessed; + + private String service; + private String status; + private String error; + private String source; + + public String getBatchId() { + return batchId; + } + + public void setBatchId(String batchId) { + this.batchId = batchId; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getCompanyId() { + return companyId; + } + + public void setCompanyId(String companyId) { + this.companyId = companyId; + } + + public String getValidationId() { + return validationId; + } + + public void setValidationId(String validationId) { + this.validationId = validationId; + } + + public String getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + + public int getTotalJobs() { + return totalJobs; + } + + public void setTotalJobs(int totalJobs) { + this.totalJobs = totalJobs; + } + + public int getTotalProcessed() { + return totalProcessed; + } + + public void setTotalProcessed(int totalProcessed) { + this.totalProcessed = totalProcessed; + } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } +} \ No newline at end of file diff --git a/src/main/java/com/nuveo/ultraocr/responses/BatchResultJob.java b/src/main/java/com/nuveo/ultraocr/responses/BatchResultJob.java new file mode 100644 index 0000000..f800ebd --- /dev/null +++ b/src/main/java/com/nuveo/ultraocr/responses/BatchResultJob.java @@ -0,0 +1,104 @@ +package com.nuveo.ultraocr.responses; + +import com.google.gson.annotations.SerializedName; + +public class BatchResultJob { + @SerializedName("job_ksuid") + private String jobKsuid; + + @SerializedName("created_at") + private String createdAt; + + @SerializedName("validation_status") + private String validationStatus; + + @SerializedName("client_data") + private Object clientData; + + private String service; + private String status; + private String error; + private String filename; + private Object validation; + private Result result; + + public String getJobKsuid() { + return jobKsuid; + } + + public void setJobKsuid(String jobKsuid) { + this.jobKsuid = jobKsuid; + } + + public String getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + + public String getValidationStatus() { + return validationStatus; + } + + public void setValidationStatus(String validationStatus) { + this.validationStatus = validationStatus; + } + + public Object getClientData() { + return clientData; + } + + public void setClientData(Object clientData) { + this.clientData = clientData; + } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public Object getValidation() { + return validation; + } + + public void setValidation(Object validation) { + this.validation = validation; + } + + public Result getResult() { + return result; + } + + public void setResult(Result result) { + this.result = result; + } +} \ No newline at end of file diff --git a/src/main/java/com/nuveo/ultraocr/responses/BatchResultStorageResponse.java b/src/main/java/com/nuveo/ultraocr/responses/BatchResultStorageResponse.java new file mode 100644 index 0000000..97f785b --- /dev/null +++ b/src/main/java/com/nuveo/ultraocr/responses/BatchResultStorageResponse.java @@ -0,0 +1,26 @@ +package com.nuveo.ultraocr.responses; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +public class BatchResultStorageResponse { + private String exp; + private String url; + + public String getExp() { + return exp; + } + + public void setExp(String exp) { + this.exp = exp; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} \ No newline at end of file diff --git a/src/main/java/com/nuveo/ultraocr/responses/JobInfoResponse.java b/src/main/java/com/nuveo/ultraocr/responses/JobInfoResponse.java new file mode 100644 index 0000000..07374a6 --- /dev/null +++ b/src/main/java/com/nuveo/ultraocr/responses/JobInfoResponse.java @@ -0,0 +1,159 @@ +package com.nuveo.ultraocr.responses; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +public class JobInfoResponse { + @SerializedName("job_id") + private String jobId; + + @SerializedName("client_id") + private String clientId; + + @SerializedName("company_id") + private String companyId; + + @SerializedName("validation_id") + private String validationId; + + @SerializedName("created_at") + private String createdAt; + + @SerializedName("finished_at") + private String finishedAt; + + @SerializedName("client_data") + private Object clientData; + + @SerializedName("validation_status") + private String validationStatus; + + private String service; + private String status; + private String error; + private Object validation; + private Object metadata; + private Result result; + private String source; + + public String getJobId() { + return jobId; + } + + public void setJobId(String jobId) { + this.jobId = jobId; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getCompanyId() { + return companyId; + } + + public void setCompanyId(String companyId) { + this.companyId = companyId; + } + + public String getValidationId() { + return validationId; + } + + public void setValidationId(String validationId) { + this.validationId = validationId; + } + + public String getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + + public String getFinishedAt() { + return finishedAt; + } + + public void setFinishedAt(String finishedAt) { + this.finishedAt = finishedAt; + } + + public Object getClientData() { + return clientData; + } + + public void setClientData(Object clientData) { + this.clientData = clientData; + } + + public String getValidationStatus() { + return validationStatus; + } + + public void setValidationStatus(String validationStatus) { + this.validationStatus = validationStatus; + } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + public Object getValidation() { + return validation; + } + + public void setValidation(Object validation) { + this.validation = validation; + } + + public Object getMetadata() { + return metadata; + } + + public void setMetadata(Object metadata) { + this.metadata = metadata; + } + + public Result getResult() { + return result; + } + + public void setResult(Result result) { + this.result = result; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } +} \ No newline at end of file diff --git a/src/main/java/com/nuveo/ultraocr/responses/JobResultResponse.java b/src/main/java/com/nuveo/ultraocr/responses/JobResultResponse.java index 05334f8..9196882 100644 --- a/src/main/java/com/nuveo/ultraocr/responses/JobResultResponse.java +++ b/src/main/java/com/nuveo/ultraocr/responses/JobResultResponse.java @@ -112,39 +112,4 @@ public Result getResult() { public void setResult(Result result) { this.result = result; } -} - -class Result { - @SerializedName("Document") - private Object document; - - @SerializedName("Quantity") - private int quantity; - - @SerializedName("Time") - private String time; - - public int getQuantity() { - return quantity; - } - - public void setQuantity(int quantity) { - this.quantity = quantity; - } - - public String getTime() { - return time; - } - - public void setTime(String time) { - this.time = time; - } - - public Object getDocument() { - return document; - } - - public void setDocument(Object document) { - this.document = document; - } } \ No newline at end of file diff --git a/src/main/java/com/nuveo/ultraocr/responses/Result.java b/src/main/java/com/nuveo/ultraocr/responses/Result.java new file mode 100644 index 0000000..d9cfaea --- /dev/null +++ b/src/main/java/com/nuveo/ultraocr/responses/Result.java @@ -0,0 +1,38 @@ +package com.nuveo.ultraocr.responses; + +import com.google.gson.annotations.SerializedName; + +public class Result { + @SerializedName("Document") + private Object document; + + @SerializedName("Quantity") + private int quantity; + + @SerializedName("Time") + private String time; + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public Object getDocument() { + return document; + } + + public void setDocument(Object document) { + this.document = document; + } +} \ No newline at end of file diff --git a/src/test/java/com/nuveo/ultraocr/ClientTest.java b/src/test/java/com/nuveo/ultraocr/ClientTest.java index e42a983..d207eb9 100644 --- a/src/test/java/com/nuveo/ultraocr/ClientTest.java +++ b/src/test/java/com/nuveo/ultraocr/ClientTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.net.http.HttpClient; @@ -17,6 +18,7 @@ import com.nuveo.ultraocr.enums.Resource; import com.nuveo.ultraocr.exceptions.InvalidStatusCodeException; import com.nuveo.ultraocr.exceptions.TimeoutException; +import com.nuveo.ultraocr.responses.BatchResultJob; class ClientTest { @Test @@ -524,4 +526,100 @@ void shouldFailToGetJobs() throws IOException, InterruptedException { Client client = new Client(httpClient); assertThrows(InvalidStatusCodeException.class, () -> client.getJobs("123", "123")); } + + @Test + void shouldGetJobInfo() throws IOException, InterruptedException { + HttpResponse mockResponse = Mockito.mock(HttpResponse.class); + Mockito.when(mockResponse.statusCode()).thenReturn(200); + Mockito.when(mockResponse.body()).thenReturn("{}"); + HttpClient httpClient = Mockito.mock(HttpClient.class); + Mockito.when(httpClient.send(Mockito.any(), Mockito.any())).thenReturn(mockResponse); + Client client = new Client(httpClient); + assertDoesNotThrow(() -> client.getJobInfo("123")); + } + + @Test + void shouldFailToGetJobInfo() throws IOException, InterruptedException { + HttpResponse mockResponse = Mockito.mock(HttpResponse.class); + Mockito.when(mockResponse.statusCode()).thenReturn(400); + HttpClient httpClient = Mockito.mock(HttpClient.class); + Mockito.when(httpClient.send(Mockito.any(), Mockito.any())).thenReturn(mockResponse); + Client client = new Client(httpClient); + assertThrows(InvalidStatusCodeException.class, () -> client.getJobInfo("123")); + } + + @Test + void shouldGetBatchInfo() throws IOException, InterruptedException { + HttpResponse mockResponse = Mockito.mock(HttpResponse.class); + Mockito.when(mockResponse.statusCode()).thenReturn(200); + Mockito.when(mockResponse.body()).thenReturn("{}"); + HttpClient httpClient = Mockito.mock(HttpClient.class); + Mockito.when(httpClient.send(Mockito.any(), Mockito.any())).thenReturn(mockResponse); + Client client = new Client(httpClient); + assertDoesNotThrow(() -> client.getBatchInfo("123")); + } + + @Test + void shouldFailToGetBatchInfo() throws IOException, InterruptedException { + HttpResponse mockResponse = Mockito.mock(HttpResponse.class); + Mockito.when(mockResponse.statusCode()).thenReturn(400); + HttpClient httpClient = Mockito.mock(HttpClient.class); + Mockito.when(httpClient.send(Mockito.any(), Mockito.any())).thenReturn(mockResponse); + Client client = new Client(httpClient); + assertThrows(InvalidStatusCodeException.class, () -> client.getBatchInfo("123")); + } + + @Test + void shouldGetBatchResultStorage() throws IOException, InterruptedException { + HttpResponse mockResponse = Mockito.mock(HttpResponse.class); + Mockito.when(mockResponse.statusCode()).thenReturn(200); + Mockito.when(mockResponse.body()).thenReturn("{}"); + HttpClient httpClient = Mockito.mock(HttpClient.class); + Mockito.when(httpClient.send(Mockito.any(), Mockito.any())).thenReturn(mockResponse); + Client client = new Client(httpClient); + assertDoesNotThrow(() -> client.getBatchResultStorage("123", new HashMap<>())); + } + + @Test + void shouldFailToGetBatchResultStorage() throws IOException, InterruptedException { + HttpResponse mockResponse = Mockito.mock(HttpResponse.class); + Mockito.when(mockResponse.statusCode()).thenReturn(400); + HttpClient httpClient = Mockito.mock(HttpClient.class); + Mockito.when(httpClient.send(Mockito.any(), Mockito.any())).thenReturn(mockResponse); + Client client = new Client(httpClient); + assertThrows(InvalidStatusCodeException.class, () -> client.getBatchResultStorage("123", new HashMap<>())); + } + + @Test + void shouldGetBatchResult() throws IOException, InterruptedException { + HttpResponse mockResponse = Mockito.mock(HttpResponse.class); + Mockito.when(mockResponse.statusCode()).thenReturn(200); + Mockito.when(mockResponse.body()).thenReturn("[]"); + HttpClient httpClient = Mockito.mock(HttpClient.class); + Mockito.when(httpClient.send(Mockito.any(), Mockito.any())).thenReturn(mockResponse); + Client client = new Client(httpClient); + assertDoesNotThrow(() -> client.getBatchResult("123")); + } + + @Test + void shouldGetBatchResultWithJob() throws IOException, InterruptedException, InvalidStatusCodeException { + HttpResponse mockResponse = Mockito.mock(HttpResponse.class); + Mockito.when(mockResponse.statusCode()).thenReturn(200); + Mockito.when(mockResponse.body()).thenReturn("[{\"job_ksuid\":\"123\"}]"); + HttpClient httpClient = Mockito.mock(HttpClient.class); + Mockito.when(httpClient.send(Mockito.any(), Mockito.any())).thenReturn(mockResponse); + Client client = new Client(httpClient); + List jobs = client.getBatchResult("123"); + assertEquals(jobs.size(), 1); + } + + @Test + void shouldFailToGetBatchResult() throws IOException, InterruptedException { + HttpResponse mockResponse = Mockito.mock(HttpResponse.class); + Mockito.when(mockResponse.statusCode()).thenReturn(400); + HttpClient httpClient = Mockito.mock(HttpClient.class); + Mockito.when(httpClient.send(Mockito.any(), Mockito.any())).thenReturn(mockResponse); + Client client = new Client(httpClient); + assertThrows(InvalidStatusCodeException.class, () -> client.getBatchResult("123")); + } } From b02af49a299278f05d17bdaba65dae33e1de255f Mon Sep 17 00:00:00 2001 From: Kleberson Canuto Date: Mon, 12 May 2025 09:04:01 -0300 Subject: [PATCH 2/2] Fix unit tests --- .github/workflows/pull-request.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index b33c513..b2106ff 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -12,5 +12,4 @@ jobs: java-version: 17 - name: Run unit tests run: | - cd ultraocr mvn -B package --file pom.xml