diff --git a/.github/workflows/_test-code-samples.yml b/.github/workflows/_test-code-samples.yml index 896227d18..bf5dcac2c 100644 --- a/.github/workflows/_test-code-samples.yml +++ b/.github/workflows/_test-code-samples.yml @@ -12,6 +12,7 @@ env: MINDEE_V2_SE_TESTS_CLASSIFICATION_MODEL_ID: ${{ secrets.MINDEE_V2_SE_TESTS_CLASSIFICATION_MODEL_ID }} MINDEE_V2_SE_TESTS_CROP_MODEL_ID: ${{ secrets.MINDEE_V2_SE_TESTS_CROP_MODEL_ID }} MINDEE_V2_SE_TESTS_SPLIT_MODEL_ID: ${{ secrets.MINDEE_V2_SE_TESTS_SPLIT_MODEL_ID }} + MINDEE_V2_SE_TESTS_OCR_MODEL_ID: ${{ secrets.MINDEE_V2_SE_TESTS_OCR_MODEL_ID }} jobs: test_sample_code: diff --git a/.github/workflows/_test-integrations.yml b/.github/workflows/_test-integrations.yml index bca2f4546..1d191603a 100644 --- a/.github/workflows/_test-integrations.yml +++ b/.github/workflows/_test-integrations.yml @@ -4,6 +4,17 @@ on: workflow_call: workflow_dispatch: +env: + MINDEE_API_KEY: ${{ secrets.MINDEE_API_KEY_SE_TESTS }} + WORKFLOW_ID: ${{ secrets.WORKFLOW_ID_SE_TESTS }} + MINDEE_V2_API_KEY: ${{ secrets.MINDEE_V2_SE_TESTS_API_KEY }} + MINDEE_V2_SE_TESTS_BLANK_PDF_URL: ${{ secrets.MINDEE_V2_SE_TESTS_BLANK_PDF_URL }} + MINDEE_V2_SE_TESTS_FINDOC_MODEL_ID: ${{ secrets.MINDEE_V2_SE_TESTS_FINDOC_MODEL_ID }} + MINDEE_V2_SE_TESTS_CLASSIFICATION_MODEL_ID: ${{ secrets.MINDEE_V2_SE_TESTS_CLASSIFICATION_MODEL_ID }} + MINDEE_V2_SE_TESTS_CROP_MODEL_ID: ${{ secrets.MINDEE_V2_SE_TESTS_CROP_MODEL_ID }} + MINDEE_V2_SE_TESTS_SPLIT_MODEL_ID: ${{ secrets.MINDEE_V2_SE_TESTS_SPLIT_MODEL_ID }} + MINDEE_V2_SE_TESTS_OCR_MODEL_ID: ${{ secrets.MINDEE_V2_SE_TESTS_OCR_MODEL_ID }} + jobs: integration_tests: name: Run Integration Tests @@ -31,11 +42,5 @@ jobs: cache: "maven" - name: Verify with Maven - env: - MINDEE_API_KEY: ${{ secrets.MINDEE_API_KEY_SE_TESTS }} - WORKFLOW_ID: ${{ secrets.WORKFLOW_ID_SE_TESTS }} - MINDEE_V2_API_KEY: ${{ secrets.MINDEE_V2_SE_TESTS_API_KEY }} - MINDEE_V2_FINDOC_MODEL_ID: ${{ secrets.MINDEE_V2_SE_TESTS_FINDOC_MODEL_ID }} - MINDEE_V2_SE_TESTS_BLANK_PDF_URL: ${{ secrets.MINDEE_V2_SE_TESTS_BLANK_PDF_URL }} run: | mvn clean test-compile failsafe:integration-test failsafe:verify diff --git a/.gitignore b/.gitignore index cbad0e621..d7fac2370 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ src-lomboked # test jshell file _test.jsh -SimpleMindeeClient.java +SimpleMindeeClientV* # Compiled class file *.class diff --git a/docs/code_samples/v2_classification.txt b/docs/code_samples/v2_classification.txt new file mode 100644 index 000000000..6ddfb4967 --- /dev/null +++ b/docs/code_samples/v2_classification.txt @@ -0,0 +1,46 @@ +import com.mindee.MindeeClientV2; +import com.mindee.input.LocalInputSource; +import com.mindee.v2.product.classification.ClassificationClassifier; +import com.mindee.v2.product.classification.ClassificationResponse; +import com.mindee.v2.product.classification.ClassificationResult; +import com.mindee.v2.product.classification.params.ClassificationParameters; +import java.io.File; +import java.io.IOException; + +public class SimpleMindeeClientV2 { + + public static void main(String[] args) + throws IOException, InterruptedException + { + String apiKey = "MY_API_KEY"; + String filePath = "/path/to/the/file.ext"; + String modelId = "MY_MODEL_ID"; + + // Init a new client + MindeeClientV2 mindeeClient = new MindeeClientV2(apiKey); + + // Set inference parameters + // Note: modelId is mandatory. + ClassificationParameters classificationParams = ClassificationParameters + // ID of the model, required. + .builder(modelId) + .build(); + + // Load a file from disk + LocalInputSource inputSource = new LocalInputSource(filePath); + + // Send for processing + ClassificationResponse response = mindeeClient.enqueueAndGetResult( + ClassificationResponse.class, + inputSource, + classificationParams + ); + + // Print a summary of the response + System.out.println(response.getInference().toString()); + + // Access the classification result + ClassificationResult result = response.getInference().getResult(); + ClassificationClassifier classification = result.getClassification(); + } +} diff --git a/docs/code_samples/v2_crop.txt b/docs/code_samples/v2_crop.txt new file mode 100644 index 000000000..97242b00b --- /dev/null +++ b/docs/code_samples/v2_crop.txt @@ -0,0 +1,44 @@ +import com.mindee.MindeeClientV2; +import com.mindee.input.LocalInputSource; +import com.mindee.v2.product.crop.CropResponse; +import com.mindee.v2.product.crop.CropResult; +import com.mindee.v2.product.crop.params.CropParameters; +import java.io.File; +import java.io.IOException; + +public class SimpleMindeeClientV2 { + + public static void main(String[] args) + throws IOException, InterruptedException + { + String apiKey = "MY_API_KEY"; + String filePath = "/path/to/the/file.ext"; + String modelId = "MY_MODEL_ID"; + + // Init a new client + MindeeClientV2 mindeeClient = new MindeeClientV2(apiKey); + + // Set inference parameters + // Note: modelId is mandatory. + CropParameters cropParams = CropParameters + // ID of the model, required. + .builder(modelId) + .build(); + + // Load a file from disk + LocalInputSource inputSource = new LocalInputSource(filePath); + + // Send for processing + CropResponse response = mindeeClient.enqueueAndGetResult( + CropResponse.class, + inputSource, + cropParams + ); + + // Print a summary of the response + System.out.println(response.getInference().toString()); + + // Access the crop results + CropResult result = response.getInference().getResult(); + } +} diff --git a/docs/code_samples/v2_extraction.txt b/docs/code_samples/v2_extraction.txt index c35f0e11a..4f6dce58d 100644 --- a/docs/code_samples/v2_extraction.txt +++ b/docs/code_samples/v2_extraction.txt @@ -2,7 +2,7 @@ import com.mindee.MindeeClientV2; import com.mindee.InferenceParameters; import com.mindee.input.LocalInputSource; import com.mindee.parsing.v2.InferenceResponse; -import java.io.File; +import com.mindee.parsing.v2.InferenceResult; import java.io.IOException; public class SimpleMindeeClientV2 { @@ -19,7 +19,7 @@ public class SimpleMindeeClientV2 { // Set inference parameters // Note: modelId is mandatory. - InferenceParameters inferenceParams = InferenceParameters + InferenceParameters extractionParams = InferenceParameters // ID of the model, required. .builder(modelId) @@ -38,17 +38,19 @@ public class SimpleMindeeClientV2 { .build(); // Load a file from disk - LocalInputSource inputSource = new LocalInputSource( - new File(filePath) - ); + LocalInputSource inputSource = new LocalInputSource(filePath); // Send for processing - InferenceResponse response = mindeeClient.enqueueAndGetInference( + InferenceResponse response = mindeeClient.enqueueAndGetResult( + InferenceResponse.class, inputSource, - inferenceParams + extractionParams ); // Print a summary of the response System.out.println(response.getInference().toString()); + + // Access the result fields + InferenceResult result = response.getInference().getResult(); } } diff --git a/docs/code_samples/v2_ocr.txt b/docs/code_samples/v2_ocr.txt new file mode 100644 index 000000000..a6cdcd771 --- /dev/null +++ b/docs/code_samples/v2_ocr.txt @@ -0,0 +1,44 @@ +import com.mindee.MindeeClientV2; +import com.mindee.input.LocalInputSource; +import com.mindee.v2.product.ocr.OcrResponse; +import com.mindee.v2.product.ocr.OcrResult; +import com.mindee.v2.product.ocr.params.OcrParameters; +import java.io.File; +import java.io.IOException; + +public class SimpleMindeeClientV2 { + + public static void main(String[] args) + throws IOException, InterruptedException + { + String apiKey = "MY_API_KEY"; + String filePath = "/path/to/the/file.ext"; + String modelId = "MY_MODEL_ID"; + + // Init a new client + MindeeClientV2 mindeeClient = new MindeeClientV2(apiKey); + + // Set inference parameters + // Note: modelId is mandatory. + OcrParameters ocrParams = OcrParameters + // ID of the model, required. + .builder(modelId) + .build(); + + // Load a file from disk + LocalInputSource inputSource = new LocalInputSource(filePath); + + // Send for processing + OcrResponse response = mindeeClient.enqueueAndGetResult( + OcrResponse.class, + inputSource, + ocrParams + ); + + // Print a summary of the response + System.out.println(response.getInference().toString()); + + // Access the result OCR pages + OcrResult result = response.getInference().getResult(); + } +} diff --git a/docs/code_samples/v2_split.txt b/docs/code_samples/v2_split.txt new file mode 100644 index 000000000..cd9b56b8f --- /dev/null +++ b/docs/code_samples/v2_split.txt @@ -0,0 +1,44 @@ +import com.mindee.MindeeClientV2; +import com.mindee.input.LocalInputSource; +import com.mindee.v2.product.split.SplitResponse; +import com.mindee.v2.product.split.SplitResult; +import com.mindee.v2.product.split.params.SplitParameters; +import java.io.File; +import java.io.IOException; + +public class SimpleMindeeClientV2 { + + public static void main(String[] args) + throws IOException, InterruptedException + { + String apiKey = "MY_API_KEY"; + String filePath = "/path/to/the/file.ext"; + String modelId = "MY_MODEL_ID"; + + // Init a new client + MindeeClientV2 mindeeClient = new MindeeClientV2(apiKey); + + // Set inference parameters + // Note: modelId is mandatory. + SplitParameters splitParams = SplitParameters + // ID of the model, required. + .builder(modelId) + .build(); + + // Load a file from disk + LocalInputSource inputSource = new LocalInputSource(filePath); + + // Send for processing + SplitResponse response = mindeeClient.enqueueAndGetResult( + SplitResponse.class, + inputSource, + splitParams + ); + + // Print a summary of the response + System.out.println(response.getInference().toString()); + + // Access the split result + SplitResult result = response.getInference().getResult(); + } +} diff --git a/src/main/java/com/mindee/InferenceParameters.java b/src/main/java/com/mindee/InferenceParameters.java index c9b40ccee..285bda60e 100644 --- a/src/main/java/com/mindee/InferenceParameters.java +++ b/src/main/java/com/mindee/InferenceParameters.java @@ -1,7 +1,7 @@ package com.mindee; import com.mindee.v2.clientOptions.BaseParameters; -import java.util.Objects; +import com.mindee.v2.http.ProductInfo; import lombok.EqualsAndHashCode; import lombok.Getter; import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; @@ -11,6 +11,7 @@ */ @Getter @EqualsAndHashCode(callSuper = true) +@ProductInfo(slug = "extraction") public final class InferenceParameters extends BaseParameters { /** * Enhance extraction accuracy with Retrieval-Augmented Generation. @@ -96,21 +97,16 @@ public static Builder builder(String modelId) { /** * Fluent builder for {@link InferenceParameters}. */ - public static final class Builder { - - private final String modelId; + public static final class Builder extends BaseParameters.BaseBuilder { private Boolean rag = null; private Boolean rawText = null; private Boolean polygon = null; private Boolean confidence = null; - private String alias; - private String[] webhookIds = new String[] {}; private String textContext; private String dataSchema; - private AsyncPollingOptions pollingOptions = AsyncPollingOptions.builder().build(); - private Builder(String modelId) { - this.modelId = Objects.requireNonNull(modelId, "modelId must not be null"); + Builder(String modelId) { + super(modelId); } /** Enhance extraction accuracy with Retrieval-Augmented Generation. */ @@ -140,18 +136,6 @@ public Builder confidence(Boolean confidence) { return this; } - /** Set an alias for the uploaded document. */ - public Builder alias(String alias) { - this.alias = alias; - return this; - } - - /** Provide IDs of webhooks to forward the API response to. */ - public Builder webhookIds(String[] webhookIds) { - this.webhookIds = webhookIds; - return this; - } - /** Provide additional text context used by the model during inference. */ public Builder textContext(String textContext) { this.textContext = textContext; @@ -164,12 +148,6 @@ public Builder dataSchema(String dataSchema) { return this; } - /** Set polling options. */ - public Builder pollingOptions(AsyncPollingOptions pollingOptions) { - this.pollingOptions = pollingOptions; - return this; - } - /** Build an immutable {@link InferenceParameters} instance. */ public InferenceParameters build() { return new InferenceParameters( diff --git a/src/main/java/com/mindee/MindeeClientV2.java b/src/main/java/com/mindee/MindeeClientV2.java index 1af7030c5..9dc2f23f3 100644 --- a/src/main/java/com/mindee/MindeeClientV2.java +++ b/src/main/java/com/mindee/MindeeClientV2.java @@ -5,6 +5,7 @@ import com.mindee.http.MindeeHttpExceptionV2; import com.mindee.input.LocalInputSource; import com.mindee.input.URLInputSource; +import com.mindee.parsing.v2.CommonResponse; import com.mindee.parsing.v2.ErrorResponse; import com.mindee.parsing.v2.InferenceResponse; import com.mindee.parsing.v2.JobResponse; @@ -33,23 +34,46 @@ public MindeeClientV2(MindeeApiV2 mindeeApi) { } /** - * Enqueue a document in the asynchronous queue. + * @deprecated use `enqueue` instead. */ public JobResponse enqueueInference( LocalInputSource inputSource, - BaseParameters params + InferenceParameters params ) throws IOException { - return mindeeApi.reqPostInferenceEnqueue(inputSource, params); + return enqueue(inputSource, params); } /** - * Enqueue a document in the asynchronous queue. + * @deprecated use `enqueue` instead. */ public JobResponse enqueueInference( URLInputSource inputSource, + InferenceParameters params + ) throws IOException { + return enqueue(inputSource, params); + } + + /** + * Enqueue a document in the asynchronous queue. + * + * @param inputSource The local input source to send. + * @param params The parameters to send along with the file. + */ + public JobResponse enqueue( + LocalInputSource inputSource, BaseParameters params ) throws IOException { - return mindeeApi.reqPostInferenceEnqueue(inputSource, params); + return mindeeApi.reqPostEnqueue(inputSource, params); + } + + /** + * Enqueue a document in the asynchronous queue. + * + * @param inputSource The URL input source to send. + * @param params The parameters to send along with the file. + */ + public JobResponse enqueue(URLInputSource inputSource, BaseParameters params) throws IOException { + return mindeeApi.reqPostEnqueue(inputSource, params); } /** @@ -63,51 +87,83 @@ public JobResponse getJob(String jobId) { return mindeeApi.reqGetJob(jobId); } + /** + * @deprecated use `getResult` instead. + */ + public InferenceResponse getInference(String inferenceId) { + return getResult(InferenceResponse.class, inferenceId); + } + /** * Get the result of an inference that was previously enqueued. * The inference will only be available after it has finished processing. */ - public InferenceResponse getInference(String inferenceId) { + public TResponse getResult( + Class responseClass, + String inferenceId + ) { if (inferenceId == null || inferenceId.trim().isEmpty()) { throw new IllegalArgumentException("inferenceId must not be null or blank."); } - return mindeeApi.reqGetInference(inferenceId); + return mindeeApi.reqGetResult(responseClass, inferenceId); + } + + /** + * @deprecated use `enqueueAndGetResult` instead. + */ + public InferenceResponse enqueueAndGetInference( + LocalInputSource inputSource, + InferenceParameters options + ) throws IOException, InterruptedException { + return enqueueAndGetResult(InferenceResponse.class, inputSource, options); + } + + /** + * @deprecated use `enqueueAndGetResult` instead. + */ + public InferenceResponse enqueueAndGetInference( + URLInputSource inputSource, + InferenceParameters options + ) throws IOException, InterruptedException { + return enqueueAndGetResult(InferenceResponse.class, inputSource, options); } /** * Send a local file to an async queue, poll, and parse when complete. * - * @param inputSource The input source to send. - * @param options The options to send along with the file. + * @param inputSource The local input source to send. + * @param params The parameters to send along with the file. * @return an instance of {@link InferenceResponse}. * @throws IOException Throws if the file can't be accessed. * @throws InterruptedException Throws if the thread is interrupted. */ - public InferenceResponse enqueueAndGetInference( + public TResponse enqueueAndGetResult( + Class responseClass, LocalInputSource inputSource, - BaseParameters options + BaseParameters params ) throws IOException, InterruptedException { - validatePollingOptions(options.getPollingOptions()); - JobResponse job = enqueueInference(inputSource, options); - return pollAndFetch(job, options); + params.validatePollingOptions(); + JobResponse job = enqueue(inputSource, params); + return pollAndFetch(responseClass, job, params); } /** - * Send a local file to an async queue, poll, and parse when complete. + * Send a remote file to an async queue, poll, and parse when complete. * - * @param inputSource The input source to send. - * @param options The options to send along with the file. + * @param inputSource The URL input source to send. + * @param params The parameters to send along with the file. * @return an instance of {@link InferenceResponse}. * @throws IOException Throws if the file can't be accessed. * @throws InterruptedException Throws if the thread is interrupted. */ - public InferenceResponse enqueueAndGetInference( + public TResponse enqueueAndGetResult( + Class responseClass, URLInputSource inputSource, - BaseParameters options + BaseParameters params ) throws IOException, InterruptedException { - validatePollingOptions(options.getPollingOptions()); - JobResponse job = enqueueInference(inputSource, options); - return pollAndFetch(job, options); + params.validatePollingOptions(); + JobResponse job = enqueue(inputSource, params); + return pollAndFetch(responseClass, job, params); } /** @@ -117,7 +173,8 @@ public InferenceResponse enqueueAndGetInference( * @return an instance of {@link InferenceResponse}. * @throws InterruptedException Throws if interrupted. */ - private InferenceResponse pollAndFetch( + private TResponse pollAndFetch( + Class responseClass, JobResponse initialJob, BaseParameters options ) throws InterruptedException { @@ -135,7 +192,7 @@ private InferenceResponse pollAndFetch( attempts = max; } if (resp.getJob().getStatus().equals("Processed")) { - return getInference(resp.getJob().getId()); + return getResult(responseClass, resp.getJob().getId()); } attempts++; } @@ -153,16 +210,4 @@ private static MindeeApiV2 createDefaultApiV2(String apiKey) { : new MindeeSettingsV2(apiKey); return MindeeHttpApiV2.builder().mindeeSettings(settings).build(); } - - private static void validatePollingOptions(AsyncPollingOptions p) { - if (p.getInitialDelaySec() < 1) { - throw new IllegalArgumentException("Initial delay must be ≥ 1 s"); - } - if (p.getIntervalSec() < 1) { - throw new IllegalArgumentException("Interval must be ≥ 1 s"); - } - if (p.getMaxRetries() < 2) { - throw new IllegalArgumentException("Max retries must be ≥ 2"); - } - } } diff --git a/src/main/java/com/mindee/geometry/Point.java b/src/main/java/com/mindee/geometry/Point.java index fb925d790..0efa24c3d 100644 --- a/src/main/java/com/mindee/geometry/Point.java +++ b/src/main/java/com/mindee/geometry/Point.java @@ -35,4 +35,9 @@ public boolean equals(Object object) { public int hashCode() { return x.hashCode() + y.hashCode(); } + + @Override + public String toString() { + return String.format("(%s,%s)", x, y); + } } diff --git a/src/main/java/com/mindee/geometry/Polygon.java b/src/main/java/com/mindee/geometry/Polygon.java index 8369204b0..e01985e78 100644 --- a/src/main/java/com/mindee/geometry/Polygon.java +++ b/src/main/java/com/mindee/geometry/Polygon.java @@ -57,6 +57,9 @@ public boolean isEmpty() { return coordinates.isEmpty(); } + /** + * Default string representation. + */ public String toString() { if (!isEmpty()) { return String.format("Polygon with %s points.", getCoordinates().size()); @@ -64,6 +67,21 @@ public String toString() { return ""; } + /** + * String representation with precise coordinates. + */ + public String toStringPrecise() { + StringBuilder builder = new StringBuilder("("); + for (int i = 0; i < coordinates.size(); i++) { + if (i > 0) { + builder.append(", "); + } + builder.append(coordinates.get(i)); + } + builder.append(")"); + return builder.toString(); + } + @Override public boolean equals(Object object) { if (this == object) { diff --git a/src/main/java/com/mindee/http/MindeeApiV2.java b/src/main/java/com/mindee/http/MindeeApiV2.java index 66ea62865..c93db9791 100644 --- a/src/main/java/com/mindee/http/MindeeApiV2.java +++ b/src/main/java/com/mindee/http/MindeeApiV2.java @@ -1,11 +1,13 @@ package com.mindee.http; +import com.mindee.MindeeException; import com.mindee.input.LocalInputSource; import com.mindee.input.URLInputSource; +import com.mindee.parsing.v2.CommonResponse; import com.mindee.parsing.v2.ErrorResponse; -import com.mindee.parsing.v2.InferenceResponse; import com.mindee.parsing.v2.JobResponse; import com.mindee.v2.clientOptions.BaseParameters; +import com.mindee.v2.http.ProductInfo; import java.io.IOException; /** @@ -18,7 +20,7 @@ public abstract class MindeeApiV2 extends MindeeApiCommon { * @param inputSource Local input source from URL. * @param options parameters. */ - public abstract JobResponse reqPostInferenceEnqueue( + public abstract JobResponse reqPostEnqueue( LocalInputSource inputSource, BaseParameters options ) throws IOException; @@ -29,7 +31,7 @@ public abstract JobResponse reqPostInferenceEnqueue( * @param inputSource Remote input source from URL. * @param options parameters. */ - public abstract JobResponse reqPostInferenceEnqueue( + public abstract JobResponse reqPostEnqueue( URLInputSource inputSource, BaseParameters options ) throws IOException; @@ -46,7 +48,10 @@ public abstract JobResponse reqPostInferenceEnqueue( * * @param inferenceId ID of the inference to poll. */ - abstract public InferenceResponse reqGetInference(String inferenceId); + abstract public TResponse reqGetResult( + Class responseClass, + String inferenceId + ); /** * Creates an "unknown error" response from an HTTP status code. @@ -60,4 +65,24 @@ protected ErrorResponse makeUnknownError(int statusCode) { null ); } + + protected ProductInfo getResponseProductInfo(Class responseClass) { + ProductInfo productInfo = responseClass.getAnnotation(ProductInfo.class); + if (productInfo == null) { + throw new MindeeException( + "The class " + responseClass.getSimpleName() + " is not annotated with @ProductInfo" + ); + } + return productInfo; + } + + protected ProductInfo getParamsProductInfo(Class responseClass) { + ProductInfo productInfo = responseClass.getAnnotation(ProductInfo.class); + if (productInfo == null) { + throw new MindeeException( + "The class " + responseClass.getSimpleName() + " is not annotated with @ProductInfo" + ); + } + return productInfo; + } } diff --git a/src/main/java/com/mindee/http/MindeeHttpApiV2.java b/src/main/java/com/mindee/http/MindeeHttpApiV2.java index 6fcb61601..735a7d63c 100644 --- a/src/main/java/com/mindee/http/MindeeHttpApiV2.java +++ b/src/main/java/com/mindee/http/MindeeHttpApiV2.java @@ -7,9 +7,9 @@ import com.mindee.input.URLInputSource; import com.mindee.parsing.v2.CommonResponse; import com.mindee.parsing.v2.ErrorResponse; -import com.mindee.parsing.v2.InferenceResponse; import com.mindee.parsing.v2.JobResponse; import com.mindee.v2.clientOptions.BaseParameters; +import com.mindee.v2.http.ProductInfo; import java.io.IOException; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; @@ -68,8 +68,10 @@ private MindeeHttpApiV2(MindeeSettingsV2 mindeeSettings, HttpClientBuilder httpC * @return A job response. */ @Override - public JobResponse reqPostInferenceEnqueue(LocalInputSource inputSource, BaseParameters options) { - String url = this.mindeeSettings.getBaseUrl() + "/products/extraction/enqueue"; + public JobResponse reqPostEnqueue(LocalInputSource inputSource, BaseParameters options) { + ProductInfo productInfo = getParamsProductInfo(options.getClass()); + String url = String + .format("%s/products/%s/enqueue", this.mindeeSettings.getBaseUrl(), productInfo.slug()); HttpPost post = buildHttpPost(url); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); @@ -93,8 +95,10 @@ public JobResponse reqPostInferenceEnqueue(LocalInputSource inputSource, BasePar * @return A job response. */ @Override - public JobResponse reqPostInferenceEnqueue(URLInputSource inputSource, BaseParameters options) { - String url = this.mindeeSettings.getBaseUrl() + "/products/extraction/enqueue"; + public JobResponse reqPostEnqueue(URLInputSource inputSource, BaseParameters options) { + ProductInfo productInfo = getParamsProductInfo(options.getClass()); + String url = String + .format("%s/products/%s/enqueue", this.mindeeSettings.getBaseUrl(), productInfo.slug()); HttpPost post = buildHttpPost(url); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); @@ -167,9 +171,18 @@ public JobResponse reqGetJob(String jobId) { } @Override - public InferenceResponse reqGetInference(String inferenceId) { - - String url = this.mindeeSettings.getBaseUrl() + "/products/extraction/results/" + inferenceId; + public TResponse reqGetResult( + Class responseClass, + String inferenceId + ) { + ProductInfo productInfo = getResponseProductInfo(responseClass); + String url = String + .format( + "%s/products/%s/results/%s", + this.mindeeSettings.getBaseUrl(), + productInfo.slug(), + inferenceId + ); HttpGet get = new HttpGet(url); if (this.mindeeSettings.getApiKey().isPresent()) { @@ -189,7 +202,7 @@ public InferenceResponse reqGetInference(String inferenceId) { throw getHttpError(response); } String raw = EntityUtils.toString(entity, StandardCharsets.UTF_8); - return deserializeOrThrow(raw, InferenceResponse.class, status); + return deserializeOrThrow(raw, responseClass, status); } finally { EntityUtils.consumeQuietly(entity); } diff --git a/src/main/java/com/mindee/parsing/v2/CommonResponse.java b/src/main/java/com/mindee/parsing/v2/CommonResponse.java index 5165eeda7..72f625f3f 100644 --- a/src/main/java/com/mindee/parsing/v2/CommonResponse.java +++ b/src/main/java/com/mindee/parsing/v2/CommonResponse.java @@ -16,8 +16,4 @@ public abstract class CommonResponse { * This is not formatted in any way by the library and may contain newline and tab characters. */ private String rawResponse; - - public void setRawResponse(String contents) { - rawResponse = contents; - } } diff --git a/src/main/java/com/mindee/parsing/v2/Inference.java b/src/main/java/com/mindee/parsing/v2/Inference.java index 9f4517d10..6fecd35d9 100644 --- a/src/main/java/com/mindee/parsing/v2/Inference.java +++ b/src/main/java/com/mindee/parsing/v2/Inference.java @@ -15,23 +15,17 @@ @JsonIgnoreProperties(ignoreUnknown = true) @AllArgsConstructor @NoArgsConstructor -public class Inference extends BaseInference { +public class Inference extends BaseInference { /** * Active options for the inference. */ @JsonProperty("active_options") private InferenceActiveOptions activeOptions; - /** - * Model result values. - */ - @JsonProperty("result") - private InferenceResult result; - @Override public String toString() { StringJoiner joiner = new StringJoiner("\n"); - joiner.add(activeOptions.toString()).add("").add(result != null ? result.toString() : ""); - return super.toString() + "\n" + joiner.toString().trim() + "\n"; + joiner.add(toStringBase()).add(activeOptions.toString()).add("").add(result.toString()); + return joiner.toString().trim() + "\n"; } } diff --git a/src/main/java/com/mindee/parsing/v2/InferenceResponse.java b/src/main/java/com/mindee/parsing/v2/InferenceResponse.java index 5280ae3ca..a68de9a4a 100644 --- a/src/main/java/com/mindee/parsing/v2/InferenceResponse.java +++ b/src/main/java/com/mindee/parsing/v2/InferenceResponse.java @@ -1,12 +1,16 @@ package com.mindee.parsing.v2; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.mindee.v2.http.ProductInfo; import lombok.Getter; /** * Response for an extraction inference. */ @Getter +@JsonIgnoreProperties(ignoreUnknown = true) +@ProductInfo(slug = "extraction") public class InferenceResponse extends CommonResponse { /** diff --git a/src/main/java/com/mindee/parsing/v2/field/FieldLocation.java b/src/main/java/com/mindee/parsing/v2/field/FieldLocation.java index 35a7ec2ce..f82c40888 100644 --- a/src/main/java/com/mindee/parsing/v2/field/FieldLocation.java +++ b/src/main/java/com/mindee/parsing/v2/field/FieldLocation.java @@ -32,4 +32,9 @@ public class FieldLocation { */ @JsonProperty("page") private int page; + + @Override + public String toString() { + return polygon.toStringPrecise() + " on page " + page; + } } diff --git a/src/main/java/com/mindee/v2/clientOptions/BaseParameters.java b/src/main/java/com/mindee/v2/clientOptions/BaseParameters.java index c0b7ac9e3..7d38c309f 100644 --- a/src/main/java/com/mindee/v2/clientOptions/BaseParameters.java +++ b/src/main/java/com/mindee/v2/clientOptions/BaseParameters.java @@ -1,6 +1,7 @@ package com.mindee.v2.clientOptions; import com.mindee.AsyncPollingOptions; +import java.util.Objects; import lombok.Data; import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; @@ -34,4 +35,51 @@ public MultipartEntityBuilder buildHttpBody(MultipartEntityBuilder builder) { } return builder; } + + public void validatePollingOptions() { + if (pollingOptions.getInitialDelaySec() < 1) { + throw new IllegalArgumentException("Initial delay must be ≥ 1 s"); + } + if (pollingOptions.getIntervalSec() < 1) { + throw new IllegalArgumentException("Interval must be ≥ 1 s"); + } + if (pollingOptions.getMaxRetries() < 2) { + throw new IllegalArgumentException("Max retries must be ≥ 2"); + } + } + + protected static abstract class BaseBuilder> { + protected final String modelId; + protected String alias; + protected String[] webhookIds = new String[] {}; + protected AsyncPollingOptions pollingOptions = AsyncPollingOptions.builder().build(); + + @SuppressWarnings("unchecked") + protected T self() { + return (T) this; + } + + protected BaseBuilder(String modelId) { + this.modelId = Objects.requireNonNull(modelId, "modelId must not be null"); + } + + /** Set an alias for the uploaded document. */ + public T alias(String alias) { + this.alias = alias; + return self(); + } + + /** Provide IDs of webhooks to forward the API response to. */ + public T webhookIds(String[] webhookIds) { + this.webhookIds = webhookIds; + return self(); + } + + /** Set polling options. */ + public T pollingOptions(AsyncPollingOptions pollingOptions) { + this.pollingOptions = pollingOptions; + return self(); + } + } + } diff --git a/src/main/java/com/mindee/v2/http/ProductInfo.java b/src/main/java/com/mindee/v2/http/ProductInfo.java new file mode 100644 index 000000000..05057a265 --- /dev/null +++ b/src/main/java/com/mindee/v2/http/ProductInfo.java @@ -0,0 +1,17 @@ +package com.mindee.v2.http; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Base info for all v2 products. + */ +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ProductInfo { + String slug(); +} diff --git a/src/main/java/com/mindee/v2/parsing/BaseInference.java b/src/main/java/com/mindee/v2/parsing/BaseInference.java index b1919acd8..26053d457 100644 --- a/src/main/java/com/mindee/v2/parsing/BaseInference.java +++ b/src/main/java/com/mindee/v2/parsing/BaseInference.java @@ -17,7 +17,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) @AllArgsConstructor @NoArgsConstructor -public abstract class BaseInference { +public abstract class BaseInference { /** * Inference ID. */ @@ -42,8 +42,13 @@ public abstract class BaseInference { @JsonProperty("file") protected InferenceFile file; - @Override - public String toString() { + /** + * Result of the inference. + */ + @JsonProperty("result") + protected TResult result; + + protected String toStringBase() { StringJoiner joiner = new StringJoiner("\n"); joiner .add("Inference") @@ -55,4 +60,11 @@ public String toString() { .add(file.toString()); return joiner.toString().trim() + "\n"; } + + @Override + public String toString() { + StringJoiner joiner = new StringJoiner("\n"); + joiner.add(toStringBase()).add(result.toString()); + return joiner.toString().trim() + "\n"; + } } diff --git a/src/main/java/com/mindee/v2/product/classification/ClassificationClassifier.java b/src/main/java/com/mindee/v2/product/classification/ClassificationClassifier.java new file mode 100644 index 000000000..ac9292210 --- /dev/null +++ b/src/main/java/com/mindee/v2/product/classification/ClassificationClassifier.java @@ -0,0 +1,23 @@ +package com.mindee.v2.product.classification; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +/** + * Classification of the document type from the source file. + */ +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public class ClassificationClassifier { + /** + * The document type, as identified on given classification values. + */ + @JsonProperty("document_type") + private String documentType; + + @Override + public String toString() { + return "Document Type: " + documentType; + } +} diff --git a/src/main/java/com/mindee/v2/product/classification/ClassificationInference.java b/src/main/java/com/mindee/v2/product/classification/ClassificationInference.java new file mode 100644 index 000000000..6db16acc3 --- /dev/null +++ b/src/main/java/com/mindee/v2/product/classification/ClassificationInference.java @@ -0,0 +1,9 @@ +package com.mindee.v2.product.classification; + +import com.mindee.v2.parsing.BaseInference; + +/** + * The inference result for a classification utility request. + */ +public class ClassificationInference extends BaseInference { +} diff --git a/src/main/java/com/mindee/v2/product/classification/ClassificationResponse.java b/src/main/java/com/mindee/v2/product/classification/ClassificationResponse.java new file mode 100644 index 000000000..e21c33ab5 --- /dev/null +++ b/src/main/java/com/mindee/v2/product/classification/ClassificationResponse.java @@ -0,0 +1,22 @@ +package com.mindee.v2.product.classification; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.mindee.parsing.v2.CommonResponse; +import com.mindee.v2.http.ProductInfo; +import lombok.Getter; + +/** + * Response for a classification utility inference. + */ +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +@ProductInfo(slug = "classification") +public class ClassificationResponse extends CommonResponse { + + /** + * The inference result for a classification utility request. + */ + @JsonProperty("inference") + private ClassificationInference inference; +} diff --git a/src/main/java/com/mindee/v2/product/classification/ClassificationResult.java b/src/main/java/com/mindee/v2/product/classification/ClassificationResult.java new file mode 100644 index 000000000..77ccd1d82 --- /dev/null +++ b/src/main/java/com/mindee/v2/product/classification/ClassificationResult.java @@ -0,0 +1,34 @@ +package com.mindee.v2.product.classification; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.StringJoiner; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * Result of the document classifier inference. + */ +@Getter +@EqualsAndHashCode +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +@NoArgsConstructor +public final class ClassificationResult { + /** + * Classification of the document type from the source file. + */ + @JsonProperty("classification") + private ClassificationClassifier classification; + + @Override + public String toString() { + StringJoiner joiner = new StringJoiner("\n"); + joiner.add("Classification\n=============="); + joiner.add(classification.toString()); + + return joiner.toString(); + } +} diff --git a/src/main/java/com/mindee/v2/product/classification/params/ClassificationParameters.java b/src/main/java/com/mindee/v2/product/classification/params/ClassificationParameters.java new file mode 100644 index 000000000..7fa37267e --- /dev/null +++ b/src/main/java/com/mindee/v2/product/classification/params/ClassificationParameters.java @@ -0,0 +1,39 @@ +package com.mindee.v2.product.classification.params; + +import com.mindee.AsyncPollingOptions; +import com.mindee.v2.clientOptions.BaseParameters; +import com.mindee.v2.http.ProductInfo; + +@ProductInfo(slug = "classification") +public class ClassificationParameters extends BaseParameters { + public ClassificationParameters( + String modelId, + String alias, + String[] webhookIds, + AsyncPollingOptions pollingOptions + ) { + super(modelId, alias, webhookIds, pollingOptions); + } + + /** + * Create a new builder. + * + * @param modelId the mandatory model identifier + * @return a fresh {@link ClassificationParameters.Builder} + */ + public static Builder builder(String modelId) { + return new Builder(modelId); + } + + public static final class Builder extends BaseParameters.BaseBuilder { + + Builder(String modelId) { + super(modelId); + } + + /** Build an immutable {@link ClassificationParameters} instance. */ + public ClassificationParameters build() { + return new ClassificationParameters(modelId, alias, webhookIds, pollingOptions); + } + } +} diff --git a/src/main/java/com/mindee/v2/product/crop/CropInference.java b/src/main/java/com/mindee/v2/product/crop/CropInference.java new file mode 100644 index 000000000..695b1facf --- /dev/null +++ b/src/main/java/com/mindee/v2/product/crop/CropInference.java @@ -0,0 +1,9 @@ +package com.mindee.v2.product.crop; + +import com.mindee.v2.parsing.BaseInference; + +/** + * The inference result for a classification utility request. + */ +public class CropInference extends BaseInference { +} diff --git a/src/main/java/com/mindee/v2/product/crop/CropItem.java b/src/main/java/com/mindee/v2/product/crop/CropItem.java new file mode 100644 index 000000000..1c8c1aa1c --- /dev/null +++ b/src/main/java/com/mindee/v2/product/crop/CropItem.java @@ -0,0 +1,36 @@ +package com.mindee.v2.product.crop; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.mindee.parsing.v2.field.FieldLocation; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * Result of a cropped document region. + */ +@Getter +@EqualsAndHashCode +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +@NoArgsConstructor +public class CropItem { + /** + * Type or classification of the detected object. + */ + @JsonProperty("object_type") + private String objectType; + /** + * Location which includes cropping coordinates for the detected object, within the source + * document. + */ + @JsonProperty("location") + private FieldLocation location; + + @Override + public String toString() { + return "* :Location: " + location + "\n :Object Type: " + objectType; + } +} diff --git a/src/main/java/com/mindee/v2/product/crop/CropResponse.java b/src/main/java/com/mindee/v2/product/crop/CropResponse.java new file mode 100644 index 000000000..7d39b2cb9 --- /dev/null +++ b/src/main/java/com/mindee/v2/product/crop/CropResponse.java @@ -0,0 +1,22 @@ +package com.mindee.v2.product.crop; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.mindee.parsing.v2.CommonResponse; +import com.mindee.v2.http.ProductInfo; +import lombok.Getter; + +/** + * Response for a crop utility inference. + */ +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +@ProductInfo(slug = "crop") +public class CropResponse extends CommonResponse { + + /** + * The inference result for a crop utility request. + */ + @JsonProperty("inference") + private CropInference inference; +} diff --git a/src/main/java/com/mindee/v2/product/crop/CropResult.java b/src/main/java/com/mindee/v2/product/crop/CropResult.java new file mode 100644 index 000000000..7beea3e74 --- /dev/null +++ b/src/main/java/com/mindee/v2/product/crop/CropResult.java @@ -0,0 +1,36 @@ +package com.mindee.v2.product.crop; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.StringJoiner; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * Result of a crop utility inference. + */ +@Getter +@EqualsAndHashCode +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +@NoArgsConstructor +public final class CropResult { + /** + * List of objects and their cropping coordinates identified in the source document. + */ + @JsonProperty("crops") + private ArrayList crops; + + @Override + public String toString() { + StringJoiner joiner = new StringJoiner("\n"); + joiner.add("Crops\n====="); + for (CropItem item : crops) { + joiner.add(item.toString()); + } + return joiner.toString(); + } +} diff --git a/src/main/java/com/mindee/v2/product/crop/params/CropParameters.java b/src/main/java/com/mindee/v2/product/crop/params/CropParameters.java new file mode 100644 index 000000000..0daa2c6fe --- /dev/null +++ b/src/main/java/com/mindee/v2/product/crop/params/CropParameters.java @@ -0,0 +1,40 @@ +package com.mindee.v2.product.crop.params; + +import com.mindee.AsyncPollingOptions; +import com.mindee.v2.clientOptions.BaseParameters; +import com.mindee.v2.http.ProductInfo; + +@ProductInfo(slug = "crop") +public class CropParameters extends BaseParameters { + + public CropParameters( + String modelId, + String alias, + String[] webhookIds, + AsyncPollingOptions pollingOptions + ) { + super(modelId, alias, webhookIds, pollingOptions); + } + + /** + * Create a new builder. + * + * @param modelId the mandatory model identifier + * @return a fresh {@link CropParameters.Builder} + */ + public static Builder builder(String modelId) { + return new Builder(modelId); + } + + public static final class Builder extends BaseParameters.BaseBuilder { + + Builder(String modelId) { + super(modelId); + } + + /** Build an immutable {@link CropParameters} instance. */ + public CropParameters build() { + return new CropParameters(modelId, alias, webhookIds, pollingOptions); + } + } +} diff --git a/src/main/java/com/mindee/v2/product/ocr/OcrInference.java b/src/main/java/com/mindee/v2/product/ocr/OcrInference.java new file mode 100644 index 000000000..0bf1ee74d --- /dev/null +++ b/src/main/java/com/mindee/v2/product/ocr/OcrInference.java @@ -0,0 +1,9 @@ +package com.mindee.v2.product.ocr; + +import com.mindee.v2.parsing.BaseInference; + +/** + * The inference result for an OCR utility request. + */ +public class OcrInference extends BaseInference { +} diff --git a/src/main/java/com/mindee/v2/product/ocr/OcrPage.java b/src/main/java/com/mindee/v2/product/ocr/OcrPage.java new file mode 100644 index 000000000..19334108c --- /dev/null +++ b/src/main/java/com/mindee/v2/product/ocr/OcrPage.java @@ -0,0 +1,31 @@ +package com.mindee.v2.product.ocr; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * OCR result for a single page. + */ +@Getter +@EqualsAndHashCode +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +@NoArgsConstructor +public class OcrPage { + /** + * Full text content extracted from the document page. + */ + @JsonProperty("content") + private String content; + + /** + * List of words extracted from the document page. + */ + @JsonProperty("words") + private ArrayList words; +} diff --git a/src/main/java/com/mindee/v2/product/ocr/OcrResponse.java b/src/main/java/com/mindee/v2/product/ocr/OcrResponse.java new file mode 100644 index 000000000..36dc333a6 --- /dev/null +++ b/src/main/java/com/mindee/v2/product/ocr/OcrResponse.java @@ -0,0 +1,22 @@ +package com.mindee.v2.product.ocr; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.mindee.parsing.v2.CommonResponse; +import com.mindee.v2.http.ProductInfo; +import lombok.Getter; + +/** + * Response for an OCR utility inference. + */ +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +@ProductInfo(slug = "ocr") +public class OcrResponse extends CommonResponse { + + /** + * The inference result for an OCR utility request. + */ + @JsonProperty("inference") + private OcrInference inference; +} diff --git a/src/main/java/com/mindee/v2/product/ocr/OcrResult.java b/src/main/java/com/mindee/v2/product/ocr/OcrResult.java new file mode 100644 index 000000000..7a5cfc286 --- /dev/null +++ b/src/main/java/com/mindee/v2/product/ocr/OcrResult.java @@ -0,0 +1,36 @@ +package com.mindee.v2.product.ocr; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.StringJoiner; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * Result of the OCR utility inference. + */ +@Getter +@EqualsAndHashCode +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +@NoArgsConstructor +public final class OcrResult { + /** + * List of OCR results for each page in the document. + */ + @JsonProperty("pages") + private ArrayList pages; + + @Override + public String toString() { + StringJoiner joiner = new StringJoiner("\n"); + joiner.add("Pages\n======"); + for (OcrPage item : pages) { + joiner.add(item.toString()); + } + return joiner.toString(); + } +} diff --git a/src/main/java/com/mindee/v2/product/ocr/OcrWord.java b/src/main/java/com/mindee/v2/product/ocr/OcrWord.java new file mode 100644 index 000000000..18097da13 --- /dev/null +++ b/src/main/java/com/mindee/v2/product/ocr/OcrWord.java @@ -0,0 +1,31 @@ +package com.mindee.v2.product.ocr; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.mindee.geometry.Polygon; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * OCR result for a single word extracted from the document page. + */ +@Getter +@EqualsAndHashCode +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +@NoArgsConstructor +public class OcrWord { + /** + * Text content of the word. + */ + @JsonProperty("content") + private String content; + + /** + * Position information as a list of points in clockwise order. + */ + @JsonProperty("polygon") + private Polygon polygon; +} diff --git a/src/main/java/com/mindee/v2/product/ocr/params/OcrParameters.java b/src/main/java/com/mindee/v2/product/ocr/params/OcrParameters.java new file mode 100644 index 000000000..0ab4a11a5 --- /dev/null +++ b/src/main/java/com/mindee/v2/product/ocr/params/OcrParameters.java @@ -0,0 +1,40 @@ +package com.mindee.v2.product.ocr.params; + +import com.mindee.AsyncPollingOptions; +import com.mindee.v2.clientOptions.BaseParameters; +import com.mindee.v2.http.ProductInfo; + +@ProductInfo(slug = "ocr") +public class OcrParameters extends BaseParameters { + + public OcrParameters( + String modelId, + String alias, + String[] webhookIds, + AsyncPollingOptions pollingOptions + ) { + super(modelId, alias, webhookIds, pollingOptions); + } + + /** + * Create a new builder. + * + * @param modelId the mandatory model identifier + * @return a fresh {@link OcrParameters.Builder} + */ + public static Builder builder(String modelId) { + return new Builder(modelId); + } + + public static final class Builder extends BaseParameters.BaseBuilder { + + Builder(String modelId) { + super(modelId); + } + + /** Build an immutable {@link OcrParameters} instance. */ + public OcrParameters build() { + return new OcrParameters(modelId, alias, webhookIds, pollingOptions); + } + } +} diff --git a/src/main/java/com/mindee/v2/product/split/SplitInference.java b/src/main/java/com/mindee/v2/product/split/SplitInference.java new file mode 100644 index 000000000..4d07f0a8c --- /dev/null +++ b/src/main/java/com/mindee/v2/product/split/SplitInference.java @@ -0,0 +1,9 @@ +package com.mindee.v2.product.split; + +import com.mindee.v2.parsing.BaseInference; + +/** + * The inference result for a split utility request. + */ +public class SplitInference extends BaseInference { +} diff --git a/src/main/java/com/mindee/v2/product/split/SplitRange.java b/src/main/java/com/mindee/v2/product/split/SplitRange.java new file mode 100644 index 000000000..254805204 --- /dev/null +++ b/src/main/java/com/mindee/v2/product/split/SplitRange.java @@ -0,0 +1,32 @@ +package com.mindee.v2.product.split; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * A single document as identified when splitting a multi-document source file. + */ +@Getter +@EqualsAndHashCode +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +@NoArgsConstructor +public class SplitRange { + /** + * 0-based page indexes, where the first integer indicates the start page and the second integer + * indicates the end page. + */ + @JsonProperty("page_range") + public ArrayList pageRange; + + /** + * The document type, as identified on given classification values. + */ + @JsonProperty("document_type") + public String documentType; +} diff --git a/src/main/java/com/mindee/v2/product/split/SplitResponse.java b/src/main/java/com/mindee/v2/product/split/SplitResponse.java new file mode 100644 index 000000000..248612323 --- /dev/null +++ b/src/main/java/com/mindee/v2/product/split/SplitResponse.java @@ -0,0 +1,22 @@ +package com.mindee.v2.product.split; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.mindee.parsing.v2.CommonResponse; +import com.mindee.v2.http.ProductInfo; +import lombok.Getter; + +/** + * Response for a crop utility inference. + */ +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +@ProductInfo(slug = "split") +public class SplitResponse extends CommonResponse { + + /** + * The inference result for a split utility request. + */ + @JsonProperty("inference") + private SplitInference inference; +} diff --git a/src/main/java/com/mindee/v2/product/split/SplitResult.java b/src/main/java/com/mindee/v2/product/split/SplitResult.java new file mode 100644 index 000000000..41deb8ecc --- /dev/null +++ b/src/main/java/com/mindee/v2/product/split/SplitResult.java @@ -0,0 +1,36 @@ +package com.mindee.v2.product.split; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.StringJoiner; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * Result of the document splitter inference. + */ +@Getter +@EqualsAndHashCode +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +@NoArgsConstructor +public final class SplitResult { + /** + * List of documents identified within a multi-document source file. + */ + @JsonProperty("splits") + private ArrayList splits; + + @Override + public String toString() { + StringJoiner joiner = new StringJoiner("\n"); + joiner.add("Splits\n======"); + for (SplitRange item : splits) { + joiner.add(item.toString()); + } + return joiner.toString(); + } +} diff --git a/src/main/java/com/mindee/v2/product/split/params/SplitParameters.java b/src/main/java/com/mindee/v2/product/split/params/SplitParameters.java new file mode 100644 index 000000000..4624b3177 --- /dev/null +++ b/src/main/java/com/mindee/v2/product/split/params/SplitParameters.java @@ -0,0 +1,40 @@ +package com.mindee.v2.product.split.params; + +import com.mindee.AsyncPollingOptions; +import com.mindee.v2.clientOptions.BaseParameters; +import com.mindee.v2.http.ProductInfo; + +@ProductInfo(slug = "split") +public class SplitParameters extends BaseParameters { + + public SplitParameters( + String modelId, + String alias, + String[] webhookIds, + AsyncPollingOptions pollingOptions + ) { + super(modelId, alias, webhookIds, pollingOptions); + } + + /** + * Create a new builder. + * + * @param modelId the mandatory model identifier + * @return a fresh {@link SplitParameters.Builder} + */ + public static Builder builder(String modelId) { + return new Builder(modelId); + } + + public static final class Builder extends BaseParameters.BaseBuilder { + + Builder(String modelId) { + super(modelId); + } + + /** Build an immutable {@link SplitParameters} instance. */ + public SplitParameters build() { + return new SplitParameters(modelId, alias, webhookIds, pollingOptions); + } + } +} diff --git a/src/test/java/com/mindee/MindeeClientV2IT.java b/src/test/java/com/mindee/MindeeClientV2IT.java index 106582b16..c96225be5 100644 --- a/src/test/java/com/mindee/MindeeClientV2IT.java +++ b/src/test/java/com/mindee/MindeeClientV2IT.java @@ -30,7 +30,7 @@ class MindeeClientV2IT { @BeforeAll void setUp() { String apiKey = System.getenv("MINDEE_V2_API_KEY"); - modelId = System.getenv("MINDEE_V2_FINDOC_MODEL_ID"); + modelId = System.getenv("MINDEE_V2_SE_TESTS_FINDOC_MODEL_ID"); mindeeClient = new MindeeClientV2(apiKey); } @@ -53,7 +53,8 @@ void parseFile_emptyMultiPage_mustSucceed() throws IOException, InterruptedExcep ) .build(); - InferenceResponse response = mindeeClient.enqueueAndGetInference(source, params); + InferenceResponse response = mindeeClient + .enqueueAndGetResult(InferenceResponse.class, source, params); assertNotNull(response); Inference inference = response.getInference(); assertNotNull(inference); @@ -147,7 +148,8 @@ void parseFile_dataSchemaReplace_mustSucceed() throws IOException, InterruptedEx ) .build(); - InferenceResponse response = mindeeClient.enqueueAndGetInference(source, params); + InferenceResponse response = mindeeClient + .enqueueAndGetResult(InferenceResponse.class, source, params); assertNotNull(response); Inference inference = response.getInference(); assertNotNull(inference); @@ -210,7 +212,7 @@ void invalidJob_mustThrowError() { } @Test - @DisplayName("URL input source - A url param should not raise errors.") + @DisplayName("URL input source - A URL param should not raise errors.") void urlInputSource_mustNotRaiseErrors() throws IOException, InterruptedException { URLInputSource urlSource = URLInputSource .builder(System.getenv("MINDEE_V2_SE_TESTS_BLANK_PDF_URL")) diff --git a/src/test/java/com/mindee/MindeeClientV2Test.java b/src/test/java/com/mindee/MindeeClientV2Test.java index d3e41f968..90ddb2b4a 100644 --- a/src/test/java/com/mindee/MindeeClientV2Test.java +++ b/src/test/java/com/mindee/MindeeClientV2Test.java @@ -3,8 +3,7 @@ import static com.mindee.TestingUtilities.getResourcePath; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.atMostOnce; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -39,10 +38,8 @@ class Enqueue { @DisplayName("sends exactly one HTTP call and yields a non-null response") void enqueue_post_async() throws IOException { MindeeApiV2 predictable = Mockito.mock(MindeeApiV2.class); - when( - predictable - .reqPostInferenceEnqueue(any(LocalInputSource.class), any(InferenceParameters.class)) - ).thenReturn(new JobResponse()); + when(predictable.reqPostEnqueue(any(LocalInputSource.class), any(InferenceParameters.class))) + .thenReturn(new JobResponse()); MindeeClientV2 mindeeClient = makeClientWithMockedApi(predictable); @@ -55,7 +52,7 @@ void enqueue_post_async() throws IOException { assertNotNull(response, "enqueue() must return a response"); verify(predictable, atMostOnce()) - .reqPostInferenceEnqueue(any(LocalInputSource.class), any(InferenceParameters.class)); + .reqPostEnqueue(any(LocalInputSource.class), any(InferenceParameters.class)); } } @@ -100,7 +97,8 @@ void document_getInference_async() throws IOException { InferenceResponse processing = mapper.readValue(json, InferenceResponse.class); - when(predictable.reqGetInference(anyString())).thenReturn(processing); + when(predictable.reqGetResult(eq(InferenceResponse.class), anyString())) + .thenReturn(processing); MindeeClientV2 mindeeClient = makeClientWithMockedApi(predictable); @@ -123,7 +121,7 @@ void document_getInference_async() throws IOException { .getValue(), "Result must deserialize fields properly." ); - verify(predictable, atMostOnce()).reqGetInference(anyString()); + verify(predictable, atMostOnce()).reqGetResult(eq(InferenceResponse.class), anyString()); } } diff --git a/src/test/java/com/mindee/TestingUtilities.java b/src/test/java/com/mindee/TestingUtilities.java index 560a5f627..c2e17ecfe 100644 --- a/src/test/java/com/mindee/TestingUtilities.java +++ b/src/test/java/com/mindee/TestingUtilities.java @@ -39,6 +39,10 @@ public static void assertStringEqualsFile(String expected, String filePath) thro Assertions.assertEquals(expectedSummary, actualSummary); } + public static void assertStringEqualsFile(String expected, Path filePath) throws IOException { + assertStringEqualsFile(expected, filePath.toString()); + } + /** * Retrieves the version from an RST prediction output. * diff --git a/src/test/java/com/mindee/v2/product/ClassificationTest.java b/src/test/java/com/mindee/v2/product/ClassificationTest.java new file mode 100644 index 000000000..d73f029bb --- /dev/null +++ b/src/test/java/com/mindee/v2/product/ClassificationTest.java @@ -0,0 +1,37 @@ +package com.mindee.v2.product; + +import static com.mindee.TestingUtilities.getV2ResourcePath; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.mindee.input.LocalResponse; +import com.mindee.v2.product.classification.ClassificationResponse; +import java.io.IOException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@DisplayName("MindeeV2 - Classification Model Tests") +public class ClassificationTest { + private ClassificationResponse loadResponse(String filePath) throws IOException { + LocalResponse localResponse = new LocalResponse(getV2ResourcePath(filePath)); + return localResponse.deserializeResponse(ClassificationResponse.class); + } + + @Nested + @DisplayName("Result with single value") + class SinglePredictionTest { + @Test + @DisplayName("all properties must be valid") + void mustHaveValidProperties() throws IOException { + ClassificationResponse response = loadResponse( + "products/classification/classification_single.json" + ); + assertNotNull(response.getInference()); + assertEquals( + "invoice", + response.getInference().getResult().getClassification().getDocumentType() + ); + } + } +} diff --git a/src/test/java/com/mindee/v2/product/CropIT.java b/src/test/java/com/mindee/v2/product/CropIT.java new file mode 100644 index 000000000..f4d445967 --- /dev/null +++ b/src/test/java/com/mindee/v2/product/CropIT.java @@ -0,0 +1,68 @@ +package com.mindee.v2.product; + +import static com.mindee.TestingUtilities.getResourcePath; +import static org.junit.jupiter.api.Assertions.*; + +import com.mindee.AsyncPollingOptions; +import com.mindee.MindeeClientV2; +import com.mindee.input.LocalInputSource; +import com.mindee.parsing.v2.InferenceFile; +import com.mindee.v2.product.crop.CropInference; +import com.mindee.v2.product.crop.CropResponse; +import com.mindee.v2.product.crop.CropResult; +import com.mindee.v2.product.crop.params.CropParameters; +import java.io.IOException; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Tag("integration") +@DisplayName("MindeeV2 –Integration Tests - Crop") +class CropIT { + + private MindeeClientV2 mindeeClient; + private String modelId; + + @BeforeAll + void setUp() { + String apiKey = System.getenv("MINDEE_V2_API_KEY"); + modelId = System.getenv("MINDEE_V2_SE_TESTS_CROP_MODEL_ID"); + mindeeClient = new MindeeClientV2(apiKey); + } + + @Test + @DisplayName("Empty, multi-page PDF – enqueue & parse must succeed") + void parseFile_emptyMultiPage_mustSucceed() throws IOException, InterruptedException { + LocalInputSource source = new LocalInputSource( + getResourcePath("file_types/pdf/multipage_cut-2.pdf") + ); + CropParameters params = CropParameters + .builder(modelId) + .alias("java_integration-test_crop_multipage") + .pollingOptions( + AsyncPollingOptions.builder().initialDelaySec(3.0).intervalSec(1.5).maxRetries(80).build() + ) + .build(); + + CropResponse response = mindeeClient.enqueueAndGetResult(CropResponse.class, source, params); + assertNotNull(response); + CropInference inference = response.getInference(); + assertNotNull(inference); + + InferenceFile file = inference.getFile(); + assertNotNull(file); + assertEquals("multipage_cut-2.pdf", file.getName()); + assertEquals(2, file.getPageCount()); + + assertNotNull(inference.getModel()); + assertEquals(modelId, inference.getModel().getId()); + + CropResult result = inference.getResult(); + assertNotNull(result); + assertEquals(1, result.getCrops().size()); + assertEquals("other", result.getCrops().get(0).getObjectType()); + } +} diff --git a/src/test/java/com/mindee/v2/product/CropTest.java b/src/test/java/com/mindee/v2/product/CropTest.java new file mode 100644 index 000000000..f0df46732 --- /dev/null +++ b/src/test/java/com/mindee/v2/product/CropTest.java @@ -0,0 +1,86 @@ +package com.mindee.v2.product; + +import static com.mindee.TestingUtilities.assertStringEqualsFile; +import static com.mindee.TestingUtilities.getV2ResourcePath; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.mindee.input.LocalResponse; +import com.mindee.v2.product.crop.CropItem; +import com.mindee.v2.product.crop.CropResponse; +import java.io.IOException; +import java.util.ArrayList; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@DisplayName("MindeeV2 - Crop Model Tests") +public class CropTest { + private CropResponse loadResponse(String filePath) throws IOException { + LocalResponse localResponse = new LocalResponse(getV2ResourcePath(filePath)); + return localResponse.deserializeResponse(CropResponse.class); + } + + @Nested + @DisplayName("Result with single value") + class SinglePredictionTest { + @Test + @DisplayName("all properties must be valid") + void mustHaveValidProperties() throws IOException { + CropResponse response = loadResponse("products/crop/crop_single.json"); + assertNotNull(response.getInference()); + + ArrayList crops = response.getInference().getResult().getCrops(); + assertEquals(1, crops.size()); + + CropItem crop1 = crops.get(0); + assertEquals("invoice", crop1.getObjectType()); + assertNotNull(crop1.getLocation().getPolygon()); + assertEquals(0, crop1.getLocation().getPage()); + } + + @Test + @DisplayName("RST output must be valid") + void mustHaveValidDisplay() throws IOException { + CropResponse response = loadResponse("products/crop/crop_single.json"); + assertStringEqualsFile( + response.getInference().toString(), + getV2ResourcePath("products/crop/crop_single.rst") + ); + } + } + + @Nested + @DisplayName("Result with multiple values") + class MultiPredictionTest { + @Test + @DisplayName("all properties must be valid") + void mustHaveValidProperties() throws IOException { + CropResponse response = loadResponse("products/crop/crop_multiple.json"); + assertNotNull(response.getInference()); + + ArrayList crops = response.getInference().getResult().getCrops(); + assertEquals(2, crops.size()); + + CropItem crop1 = crops.get(0); + assertEquals("invoice", crop1.getObjectType()); + assertNotNull(crop1.getLocation().getPolygon()); + assertEquals(0, crop1.getLocation().getPage()); + + CropItem crop2 = crops.get(1); + assertEquals("receipt", crop2.getObjectType()); + assertNotNull(crop2.getLocation().getPolygon()); + assertEquals(0, crop2.getLocation().getPage()); + } + + @Test + @DisplayName("RST output must be valid") + void mustHaveValidDisplay() throws IOException { + CropResponse response = loadResponse("products/crop/crop_multiple.json"); + assertStringEqualsFile( + response.getInference().toString(), + getV2ResourcePath("products/crop/crop_multiple.rst") + ); + } + } +} diff --git a/src/test/java/com/mindee/v2/product/OcrTest.java b/src/test/java/com/mindee/v2/product/OcrTest.java new file mode 100644 index 000000000..c39268be0 --- /dev/null +++ b/src/test/java/com/mindee/v2/product/OcrTest.java @@ -0,0 +1,62 @@ +package com.mindee.v2.product; + +import static com.mindee.TestingUtilities.getV2ResourcePath; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.mindee.input.LocalResponse; +import com.mindee.v2.product.ocr.OcrPage; +import com.mindee.v2.product.ocr.OcrResponse; +import java.io.IOException; +import java.util.ArrayList; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@DisplayName("MindeeV2 - OCR Model Tests") +public class OcrTest { + private OcrResponse loadResponse(String filePath) throws IOException { + LocalResponse localResponse = new LocalResponse(getV2ResourcePath(filePath)); + return localResponse.deserializeResponse(OcrResponse.class); + } + + @Nested + @DisplayName("Result with single value") + class SinglePredictionTest { + @Test + @DisplayName("all properties must be valid") + void mustHaveValidProperties() throws IOException { + OcrResponse response = loadResponse("products/ocr/ocr_single.json"); + assertNotNull(response.getInference()); + + ArrayList pages = response.getInference().getResult().getPages(); + assertEquals(1, pages.size()); + assertEquals(305, pages.get(0).getWords().size()); + assertEquals("Shipper:", pages.get(0).getWords().get(0).getContent()); + } + } + + @Nested + @DisplayName("Result with multiple values") + class MultiPredictionTest { + @Test + @DisplayName("all properties must be valid") + void mustHaveValidProperties() throws IOException { + OcrResponse response = loadResponse("products/ocr/ocr_multiple.json"); + assertNotNull(response.getInference()); + + ArrayList pages = response.getInference().getResult().getPages(); + assertEquals(3, pages.size()); + + OcrPage page1 = pages.get(0); + assertNotNull(page1.getContent()); + assertEquals(295, page1.getWords().size()); + assertEquals("FICTIOCORP", page1.getWords().get(0).getContent()); + + OcrPage page2 = pages.get(1); + assertNotNull(page2.getContent()); + assertEquals(450, page2.getWords().size()); + assertEquals("KEOLIO", page2.getWords().get(0).getContent()); + } + } +} diff --git a/src/test/java/com/mindee/v2/product/SplitIT.java b/src/test/java/com/mindee/v2/product/SplitIT.java new file mode 100644 index 000000000..ce897416a --- /dev/null +++ b/src/test/java/com/mindee/v2/product/SplitIT.java @@ -0,0 +1,69 @@ +package com.mindee.v2.product; + +import static com.mindee.TestingUtilities.getResourcePath; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.mindee.AsyncPollingOptions; +import com.mindee.MindeeClientV2; +import com.mindee.input.LocalInputSource; +import com.mindee.parsing.v2.InferenceFile; +import com.mindee.v2.product.split.SplitInference; +import com.mindee.v2.product.split.SplitResponse; +import com.mindee.v2.product.split.SplitResult; +import com.mindee.v2.product.split.params.SplitParameters; +import java.io.IOException; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Tag("integration") +@DisplayName("MindeeV2 –Integration Tests - Split") +class SplitIT { + + private MindeeClientV2 mindeeClient; + private String modelId; + + @BeforeAll + void setUp() { + String apiKey = System.getenv("MINDEE_V2_API_KEY"); + modelId = System.getenv("MINDEE_V2_SE_TESTS_SPLIT_MODEL_ID"); + mindeeClient = new MindeeClientV2(apiKey); + } + + @Test + @DisplayName("Empty, multi-page PDF – enqueue & parse must succeed") + void parseFile_emptyMultiPage_mustSucceed() throws IOException, InterruptedException { + LocalInputSource source = new LocalInputSource( + getResourcePath("file_types/pdf/multipage_cut-2.pdf") + ); + SplitParameters params = SplitParameters + .builder(modelId) + .alias("java_integration-test_split_multipage") + .pollingOptions( + AsyncPollingOptions.builder().initialDelaySec(3.0).intervalSec(1.5).maxRetries(80).build() + ) + .build(); + + SplitResponse response = mindeeClient.enqueueAndGetResult(SplitResponse.class, source, params); + assertNotNull(response); + SplitInference inference = response.getInference(); + assertNotNull(inference); + + InferenceFile file = inference.getFile(); + assertNotNull(file); + assertEquals("multipage_cut-2.pdf", file.getName()); + assertEquals(2, file.getPageCount()); + + assertNotNull(inference.getModel()); + assertEquals(modelId, inference.getModel().getId()); + + SplitResult result = inference.getResult(); + assertNotNull(result); + assertTrue(result.getSplits().isEmpty()); + } +} diff --git a/src/test/java/com/mindee/v2/product/SplitTest.java b/src/test/java/com/mindee/v2/product/SplitTest.java new file mode 100644 index 000000000..08a21bef8 --- /dev/null +++ b/src/test/java/com/mindee/v2/product/SplitTest.java @@ -0,0 +1,64 @@ +package com.mindee.v2.product; + +import static com.mindee.TestingUtilities.getV2ResourcePath; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.mindee.input.LocalResponse; +import com.mindee.v2.product.split.SplitRange; +import com.mindee.v2.product.split.SplitResponse; +import java.io.IOException; +import java.util.ArrayList; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@DisplayName("MindeeV2 - Split Model Tests") +public class SplitTest { + private SplitResponse loadResponse(String filePath) throws IOException { + LocalResponse localResponse = new LocalResponse(getV2ResourcePath(filePath)); + return localResponse.deserializeResponse(SplitResponse.class); + } + + @Nested + @DisplayName("Result with single value") + class SinglePredictionTest { + @Test + @DisplayName("all properties must be valid") + void mustHaveValidProperties() throws IOException { + SplitResponse response = loadResponse("products/split/split_single.json"); + assertNotNull(response.getInference()); + + ArrayList splits = response.getInference().getResult().getSplits(); + assertEquals(1, splits.size()); + assertEquals("receipt", splits.get(0).getDocumentType()); + assertEquals(0, splits.get(0).getPageRange().get(0)); + } + } + + @Nested + @DisplayName("Result with multiple values") + class MultiPredictionTest { + @Test + @DisplayName("all properties must be valid") + void mustHaveValidProperties() throws IOException { + SplitResponse response = loadResponse("products/split/split_multiple.json"); + assertNotNull(response.getInference()); + + ArrayList splits = response.getInference().getResult().getSplits(); + assertEquals(3, splits.size()); + + SplitRange split1 = splits.get(0); + assertEquals("passport", split1.getDocumentType()); + assertEquals(0, split1.getPageRange().get(0)); + + SplitRange split2 = splits.get(1); + assertEquals("invoice", split2.getDocumentType()); + assertEquals(1, split2.getPageRange().get(0)); + + SplitRange split3 = splits.get(2); + assertEquals("receipt", split3.getDocumentType()); + assertEquals(4, split3.getPageRange().get(0)); + } + } +} diff --git a/src/test/resources b/src/test/resources index c2e36f5b6..53f0efbc0 160000 --- a/src/test/resources +++ b/src/test/resources @@ -1 +1 @@ -Subproject commit c2e36f5b635386cb9bb922b517c4e02039b0a122 +Subproject commit 53f0efbc08c77c2c085aadd27de9d2d6c359276e diff --git a/tests/test_v2_code_samples.sh b/tests/test_v2_code_samples.sh index 5c030cc23..f847d75e4 100755 --- a/tests/test_v2_code_samples.sh +++ b/tests/test_v2_code_samples.sh @@ -18,33 +18,33 @@ do sed "s/MY_API_KEY/${MINDEE_V2_API_KEY}/" "${f}" > $OUTPUT_FILE sed -i "s/\/path\/to\/the\/file.ext/src\/test\/resources\/file_types\/pdf\/blank_1.pdf/" $OUTPUT_FILE - if echo "${f}" | grep -q "v2_classification.txt" + if echo "${f}" | grep -q "v2_classification" then sed -i "s/MY_MODEL_ID/${MINDEE_V2_SE_TESTS_CLASSIFICATION_MODEL_ID}/" $OUTPUT_FILE fi - if echo "${f}" | grep -q "v2_crop.txt" + if echo "${f}" | grep -q "v2_crop" then sed -i "s/MY_MODEL_ID/${MINDEE_V2_SE_TESTS_CROP_MODEL_ID}/" $OUTPUT_FILE fi - if echo "${f}" | grep -q "v2_extraction.txt" + if echo "${f}" | grep -q "v2_extraction" then sed -i "s/MY_MODEL_ID/${MINDEE_V2_SE_TESTS_FINDOC_MODEL_ID}/" $OUTPUT_FILE fi - if echo "${f}" | grep -q "v2_ocr.txt" + if echo "${f}" | grep -q "v2_ocr" then sed -i "s/MY_MODEL_ID/${MINDEE_V2_SE_TESTS_OCR_MODEL_ID}/" $OUTPUT_FILE fi - if echo "${f}" | grep -q "v2_split.txt" + if echo "${f}" | grep -q "v2_split" then sed -i "s/MY_MODEL_ID/${MINDEE_V2_SE_TESTS_SPLIT_MODEL_ID}/" $OUTPUT_FILE fi sleep 0.5 # avoid too many request errors - javac -cp ./target/dependency/*:./target/* "${OUTPUT_FILE}" + javac -Xlint:-options -cp ./target/dependency/*:./target/* "${OUTPUT_FILE}" java -cp .:./target/dependency/*:./target/* SimpleMindeeClientV2 done