diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 40f9a9837..5eb3fd97b 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -33,5 +33,6 @@ jobs: - name: Verify with Maven env: MINDEE_API_KEY: ${{ secrets.MINDEE_API_KEY_SE_TESTS }} + WORKFLOW_ID: ${{ secrets.WORKFLOW_ID_SE_TESTS }} run: | mvn clean test-compile failsafe:integration-test failsafe:verify diff --git a/docs/code_samples/workflow_execution.txt b/docs/code_samples/workflow_execution.txt new file mode 100644 index 000000000..316ce7488 --- /dev/null +++ b/docs/code_samples/workflow_execution.txt @@ -0,0 +1,41 @@ +import com.mindee.MindeeClient; +import com.mindee.input.LocalInputSource; +import com.mindee.parsing.common.WorkflowResponse; +import com.mindee.product.generated.GeneratedV1; +import com.mindee.http.MindeeHttpException; +import java.io.IOException; + +public class SimpleMindeeClient { + + public static void main(String[] args) throws IOException { + String apiKey = "my-api-key"; + String workflowId = "workflow-id"; + String filePath = "/path/to/the/file.ext"; + + // Init a new client + MindeeClient mindeeClient = new MindeeClient(apiKey); + + // Load a file from disk + LocalInputSource inputSource = new LocalInputSource(filePath); + + // Send the file to a workflow + WorkflowResponse response = mindeeClient.executeWorkflow( + workflowId, + inputSource + ); + + + // Alternatively: give an alias to the document + // WorkflowResponse response = mindeeClient.executeWorkflow( + // workflowId, + // inputSource, + // WorkflowOptions.builder().alias("my-alias").build() + // ); + + // Print the workflow ID. + System.out.println(response.getExecution().getId()); + + // Print the inference result. + // System.out.println(response.getExecution().getInference()); + } +} diff --git a/pom.xml b/pom.xml index cafd7a6e8..9e8c25138 100644 --- a/pom.xml +++ b/pom.xml @@ -382,7 +382,7 @@ scm:git:git://github.com/mindee/mindee-api-java.git scm:git:ssh://github.com:mindee/mindee-api-java.git - http://github.com/mindee/mindee-api-java/tree/main + https://github.com/mindee/mindee-api-java/tree/main diff --git a/src/main/java/com/mindee/MindeeClient.java b/src/main/java/com/mindee/MindeeClient.java index d982ac26f..227850586 100644 --- a/src/main/java/com/mindee/MindeeClient.java +++ b/src/main/java/com/mindee/MindeeClient.java @@ -13,6 +13,7 @@ import com.mindee.parsing.common.AsyncPredictResponse; import com.mindee.parsing.common.Inference; import com.mindee.parsing.common.PredictResponse; +import com.mindee.parsing.common.WorkflowResponse; import com.mindee.pdf.PdfBoxApi; import com.mindee.pdf.PdfOperation; import com.mindee.pdf.SplitQuery; @@ -323,6 +324,42 @@ private AsyncPredictResponse enqueueAndParse( throw new RuntimeException("Max retries exceeded. Failed to get the document."); } + /** + * Send a local file to a workflow execution. + */ + public WorkflowResponse executeWorkflow( + String workflowId, + LocalInputSource localInputSource, + WorkflowOptions workflowOptions + ) throws IOException { + return this.mindeeApi.executeWorkflowPost( + GeneratedV1.class, + workflowId, + RequestParameters.builder() + .file(localInputSource.getFile()) + .fileName(localInputSource.getFilename()) + .workflowOptions(workflowOptions) + .build() + ); + } + /** + * Send a local file to a workflow execution. + */ + public WorkflowResponse executeWorkflow( + String workflowId, + LocalInputSource localInputSource + ) throws IOException { + return this.mindeeApi.executeWorkflowPost( + GeneratedV1.class, + workflowId, + RequestParameters.builder() + .file(localInputSource.getFile()) + .fileName(localInputSource.getFilename()) + .workflowOptions(WorkflowOptions.builder().build()) + .build() + ); + } + /** * Send a local file to a Standard prediction API and parse the results. */ diff --git a/src/main/java/com/mindee/WorkflowOptions.java b/src/main/java/com/mindee/WorkflowOptions.java new file mode 100644 index 000000000..2ad7bbab9 --- /dev/null +++ b/src/main/java/com/mindee/WorkflowOptions.java @@ -0,0 +1,47 @@ +package com.mindee; + +import com.mindee.parsing.common.ExecutionPriority; +import lombok.Builder; +import lombok.Value; + +/** + * Workflow-specific options. + */ +@Value +public class WorkflowOptions { + /** + * Alias to give to the file. + */ + String alias; + + /** + * Priority to give to the execution. + */ + ExecutionPriority priority; + + /** + * Whether to include the full text data for async APIs. + * This performs a full OCR operation on the server and will increase response time and payload + * size. + */ + Boolean fullText; + + /** + * A unique, encrypted URL for accessing the document validation interface without requiring + * authentication. + */ + String publicUrl; + + @Builder + private WorkflowOptions( + String alias, + ExecutionPriority priority, + Boolean fullText, + String publicUrl + ) { + this.alias = alias; + this.priority = priority; + this.fullText = fullText == null ? Boolean.FALSE : fullText; + this.publicUrl = publicUrl; + } +} diff --git a/src/main/java/com/mindee/http/MindeeApi.java b/src/main/java/com/mindee/http/MindeeApi.java index 080197b0c..35e6545a6 100644 --- a/src/main/java/com/mindee/http/MindeeApi.java +++ b/src/main/java/com/mindee/http/MindeeApi.java @@ -3,6 +3,7 @@ import com.mindee.parsing.common.AsyncPredictResponse; import com.mindee.parsing.common.Inference; import com.mindee.parsing.common.PredictResponse; +import com.mindee.parsing.common.WorkflowResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import org.apache.http.HttpEntity; @@ -39,6 +40,12 @@ abstract public PredictResponse predictPost( RequestParameters requestParameters ) throws IOException; + abstract public WorkflowResponse executeWorkflowPost( + Class documentClass, + String workflowId, + RequestParameters requestParameters + ) throws IOException; + protected String getUserAgent() { String javaVersion = System.getProperty("java.version"); String sdkVersion = getClass().getPackage().getImplementationVersion(); diff --git a/src/main/java/com/mindee/http/MindeeHttpApi.java b/src/main/java/com/mindee/http/MindeeHttpApi.java index 6ff49378c..5e7515283 100644 --- a/src/main/java/com/mindee/http/MindeeHttpApi.java +++ b/src/main/java/com/mindee/http/MindeeHttpApi.java @@ -10,6 +10,7 @@ import com.mindee.parsing.common.ErrorDetails; import com.mindee.parsing.common.Inference; import com.mindee.parsing.common.PredictResponse; +import com.mindee.parsing.common.WorkflowResponse; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; @@ -41,6 +42,7 @@ public final class MindeeHttpApi extends MindeeApi { private static final ObjectMapper mapper = new ObjectMapper(); private final Function buildBaseUrl = this::buildUrl; + private final Function buildWorkflowBaseUrl = this::buildWorkflowUrl; /** * The MindeeSetting needed to make the api call. */ @@ -51,27 +53,35 @@ public final class MindeeHttpApi extends MindeeApi { */ private final HttpClientBuilder httpClientBuilder; /** - * The function used to generate the API endpoint URL. Only needs to be set if the api calls need - * to be directed through internal URLs. + * The function used to generate the API endpoint URL. + * Only needs to be set if the api calls need to be directed through internal URLs. */ private final Function urlFromEndpoint; + /** - * The function used to generate the API endpoint URL for Async calls. Only needs to be set if the - * api calls need to be directed through internal URLs. + * The function used to generate the API endpoint URL for workflow execution calls. + * Only needs to be set if the api calls need to be directed through internal URLs. */ private final Function asyncUrlFromEndpoint; /** - * The function used to generate the Job status URL for Async calls. Only needs to be set if the - * api calls need to be directed through internal URLs. + * The function used to generate the Job status URL for Async calls. + * Only needs to be set if the api calls need to be directed through internal URLs. */ private final Function documentUrlFromEndpoint; + /** + * The function used to generate the Job status URL for Async calls. + * Only needs to be set if the api calls need to be directed through internal URLs. + */ + private final Function workflowUrlFromId; + public MindeeHttpApi(MindeeSettings mindeeSettings) { this( mindeeSettings, null, null, null, + null, null ); } @@ -82,7 +92,8 @@ private MindeeHttpApi( HttpClientBuilder httpClientBuilder, Function urlFromEndpoint, Function asyncUrlFromEndpoint, - Function documentUrlFromEndpoint + Function documentUrlFromEndpoint, + Function workflowUrlFromEndpoint ) { this.mindeeSettings = mindeeSettings; @@ -110,6 +121,12 @@ private MindeeHttpApi( this.documentUrlFromEndpoint = this.buildBaseUrl.andThen( (url) -> url.concat("/documents/queue/")); } + + if (workflowUrlFromEndpoint != null) { + this.workflowUrlFromId = workflowUrlFromEndpoint; + } else { + this.workflowUrlFromId = this.buildWorkflowBaseUrl; + } } /** @@ -149,14 +166,14 @@ public AsyncPredictResponse documentQueueGet( mappedResponse.setRawResponse(rawResponse); if ( mappedResponse.getJob() != null - && mappedResponse.getJob().getError() != null - && mappedResponse.getJob().getError().getCode() != null + && mappedResponse.getJob().getError() != null + && mappedResponse.getJob().getError().getCode() != null ) { throw new MindeeHttpException( - 500, - mappedResponse.getJob().getError().getMessage(), - mappedResponse.getJob().getError().getDetails().toString(), - mappedResponse.getJob().getError().getCode() + 500, + mappedResponse.getJob().getError().getMessage(), + mappedResponse.getJob().getError().getDetails().toString(), + mappedResponse.getJob().getError().getCode() ); } return mappedResponse; @@ -243,6 +260,47 @@ public AsyncPredictResponse predictAsyncPost( } } + + /** + * POST a prediction request for a workflow response. + */ + public WorkflowResponse executeWorkflowPost( + Class documentClass, + String workflowId, + RequestParameters requestParameters + ) throws IOException { + + String url = workflowUrlFromId.apply(workflowId); + HttpPost post = buildHttpPost(url, requestParameters); + + // required to register jackson date module format to deserialize + mapper.findAndRegisterModules(); + JavaType parametricType = mapper.getTypeFactory().constructParametricType( + WorkflowResponse.class, + documentClass + ); + try ( + CloseableHttpClient httpClient = httpClientBuilder.build(); + CloseableHttpResponse response = httpClient.execute(post) + ) { + HttpEntity responseEntity = response.getEntity(); + int statusCode = response.getStatusLine().getStatusCode(); + if (!is2xxStatusCode(statusCode)) { + throw getHttpError(parametricType, response); + } + if (responseEntity.getContentLength() == 0) { + throw new MindeeException("Empty response from server."); + } + String rawResponse = readRawResponse(responseEntity); + WorkflowResponse mappedResponse = mapper.readValue(rawResponse, parametricType); + mappedResponse.setRawResponse(rawResponse); + return mappedResponse; + } catch (IOException err) { + throw new MindeeException(err.getMessage(), err); + } + } + + private MindeeHttpException getHttpError( JavaType parametricType, CloseableHttpResponse response @@ -266,8 +324,7 @@ private MindeeHttpException getHttpError( ErrorDetails errorDetails = predictResponse.getApiRequest().getError().getDetails(); if (errorDetails != null) { details = errorDetails.toString(); - } - else { + } else { details = ""; } errorCode = predictResponse.getApiRequest().getError().getCode(); @@ -289,6 +346,10 @@ private String buildUrl(Endpoint endpoint) { + endpoint.getVersion(); } + private String buildWorkflowUrl(String workflowId) { + return this.mindeeSettings.getBaseUrl() + "/workflows/" + workflowId + "/executions"; + } + private HttpPost buildHttpPost( String url, RequestParameters requestParameters @@ -341,12 +402,33 @@ private HttpEntity buildHttpBody( if (Boolean.TRUE.equals(requestParameters.getPredictOptions().getAllWords())) { builder.addTextBody("include_mvision", "true"); } + + if (requestParameters.getWorkflowOptions().getPriority() != null) { + builder.addTextBody( + "priority", + requestParameters.getWorkflowOptions().getPriority().getValue() + ); + } + if (requestParameters.getWorkflowOptions().getAlias() != null) { + builder.addTextBody( + "alias", + requestParameters.getWorkflowOptions().getAlias().toLowerCase() + ); + } + if (requestParameters.getWorkflowOptions().getPublicUrl() != null) { + builder.addTextBody( + "public_url", + requestParameters.getWorkflowOptions().getPublicUrl() + ); + } return builder.build(); } else if (requestParameters.getFileUrl() != null) { Map urlMap = new HashMap<>(); urlMap.put("document", requestParameters.getFileUrl()); - return new StringEntity(mapper.writeValueAsString(urlMap), - ContentType.APPLICATION_JSON); + return new StringEntity( + mapper.writeValueAsString(urlMap), + ContentType.APPLICATION_JSON + ); } else { throw new MindeeException("Either document bytes or a document URL are needed"); } diff --git a/src/main/java/com/mindee/http/RequestParameters.java b/src/main/java/com/mindee/http/RequestParameters.java index c4e81f392..50e913227 100644 --- a/src/main/java/com/mindee/http/RequestParameters.java +++ b/src/main/java/com/mindee/http/RequestParameters.java @@ -1,6 +1,7 @@ package com.mindee.http; import com.mindee.PredictOptions; +import com.mindee.WorkflowOptions; import java.net.URL; import lombok.Builder; import lombok.Value; @@ -15,12 +16,14 @@ public class RequestParameters { byte[] file; String fileName; PredictOptions predictOptions; + WorkflowOptions workflowOptions; @Builder private RequestParameters( URL urlInputSource, byte[] file, PredictOptions predictOptions, + WorkflowOptions workflowOptions, String fileName ) { if (file != null && urlInputSource != null) { @@ -31,6 +34,11 @@ private RequestParameters( } else { this.predictOptions = predictOptions; } + if (workflowOptions == null){ + this.workflowOptions = WorkflowOptions.builder().build(); + } else { + this.workflowOptions = workflowOptions; + } this.fileUrl = urlInputSource; this.file = file; this.fileName = fileName; diff --git a/src/main/java/com/mindee/parsing/common/Execution.java b/src/main/java/com/mindee/parsing/common/Execution.java new file mode 100644 index 000000000..dd75d8f3c --- /dev/null +++ b/src/main/java/com/mindee/parsing/common/Execution.java @@ -0,0 +1,120 @@ +package com.mindee.parsing.common; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.mindee.product.generated.GeneratedV1Document; +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * Representation of a workflow execution. + */ +@Getter +@EqualsAndHashCode +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +@NoArgsConstructor +public class Execution { + /** + * Identifier for the batch to which the execution belongs. + */ + @JsonProperty("batch_name") + private String batchName; + + /** + * The time at which the execution started. + */ + @JsonProperty("created_at") + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime createdAt; + + /** + * File representation within a workflow execution. + */ + @JsonProperty("file") + private ExecutionFile file; + + /** + * Identifier for the execution. + */ + @JsonProperty("id") + private String id; + + /** + * Deserialized inference object. + */ + @JsonProperty("inference") + private DocT inference; + + /** + * Priority of the execution. + */ + @JsonProperty("priority") + private String priority; + + /** + * The time at which the file was tagged as reviewed. + */ + @JsonProperty("reviewed_at") + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime reviewedAt; + + /** + * The time at which the file was uploaded to a workflow. + */ + @JsonProperty("available_at") + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime availableAt; + + /** + * Reviewed fields and values. + */ + @JsonProperty("reviewed_prediction") + private GeneratedV1Document reviewedPrediction; + + /** + * Execution Status. + */ + @JsonProperty("status") + private String status; + + /** + * Execution type. + */ + @JsonProperty("type") + private String type; + + /** + * The time at which the file was uploaded to a workflow. + */ + @JsonProperty("uploaded_at") + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime uploadedAt; + + /** + * Identifier for the workflow. + */ + @JsonProperty("workflow_id") + private String workflowId; + + @Override + public String toString() { + return ":batch_name: " + batchName + "\n" + + ":created_at: " + createdAt + "\n" + + ":file: " + file + "\n" + + ":id: " + id + "\n" + + ":inference: " + inference + "\n" + + ":priority: " + priority + "\n" + + ":reviewed_at: " + reviewedAt + "\n" + + ":available_at: " + availableAt + "\n" + + ":reviewed_prediction: " + reviewedPrediction + "\n" + + ":status: " + status + "\n" + + ":type: " + type + "\n" + + ":uploaded_at: " + uploadedAt + "\n" + + ":workflow_id: " + workflowId; + } +} diff --git a/src/main/java/com/mindee/parsing/common/ExecutionFile.java b/src/main/java/com/mindee/parsing/common/ExecutionFile.java new file mode 100644 index 000000000..5795a4050 --- /dev/null +++ b/src/main/java/com/mindee/parsing/common/ExecutionFile.java @@ -0,0 +1,36 @@ +package com.mindee.parsing.common; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * Representation of a workflow execution's file data. + */ +@Getter +@EqualsAndHashCode +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +@NoArgsConstructor +public class ExecutionFile { + /** + * File name. + */ + @JsonProperty("name") + private String name; + + /** + * Optional alias for the file. + */ + @JsonProperty("alias") + private String alias; + + + @Override + public String toString() { + return "\n :name: " + name + "\n" + " :alias: " + alias; + } +} diff --git a/src/main/java/com/mindee/parsing/common/ExecutionPriority.java b/src/main/java/com/mindee/parsing/common/ExecutionPriority.java new file mode 100644 index 000000000..0cde417cc --- /dev/null +++ b/src/main/java/com/mindee/parsing/common/ExecutionPriority.java @@ -0,0 +1,24 @@ +package com.mindee.parsing.common; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Execution priority for a workflow. + */ +public enum ExecutionPriority { + LOW("low"), + MEDIUM("medium"), + HIGH("high"); + + private final String value; + + ExecutionPriority(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } +} diff --git a/src/main/java/com/mindee/parsing/common/Job.java b/src/main/java/com/mindee/parsing/common/Job.java index 6e6a1213c..0bddaad71 100644 --- a/src/main/java/com/mindee/parsing/common/Job.java +++ b/src/main/java/com/mindee/parsing/common/Job.java @@ -2,17 +2,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.io.IOException; import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeFormatterBuilder; -import java.time.temporal.TemporalAccessor; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -26,30 +17,30 @@ @JsonIgnoreProperties(ignoreUnknown = true) @AllArgsConstructor @NoArgsConstructor -public class Job { +public class Job{ /** - * The time at which the job finished + * The time at which the job finished. */ @JsonProperty("available_at") @JsonDeserialize(using = LocalDateTimeDeserializer.class) private LocalDateTime availableAt; /** - * Identifier for the job + * Identifier for the job. */ @JsonProperty("id") private String id; /** - * The time at which the job started + * The time at which the job started. */ @JsonProperty("issued_at") @JsonDeserialize(using = LocalDateTimeDeserializer.class) private LocalDateTime issuedAt; /** - * Job Status + * Job Status. */ @JsonProperty("status") private String status; diff --git a/src/main/java/com/mindee/parsing/common/WorkflowResponse.java b/src/main/java/com/mindee/parsing/common/WorkflowResponse.java new file mode 100644 index 000000000..4c3d5e2b0 --- /dev/null +++ b/src/main/java/com/mindee/parsing/common/WorkflowResponse.java @@ -0,0 +1,27 @@ +package com.mindee.parsing.common; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.mindee.product.generated.GeneratedV1; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * Represents the server response after a document is sent to a workflow. + */ +@Data +@EqualsAndHashCode(callSuper = true) +@JsonIgnoreProperties(ignoreUnknown = true) +public class WorkflowResponse extends ApiResponse { + /** + * Set the prediction model used to parse the document. + * The response object will be instantiated based on this parameter. + */ + @JsonProperty("execution") + Execution execution; + + /** + * Default product is GeneratedV1. + */ + public static class Default extends WorkflowResponse {} +} diff --git a/src/test/java/com/mindee/parsing/common/WorkflowResponseTest.java b/src/test/java/com/mindee/parsing/common/WorkflowResponseTest.java new file mode 100644 index 000000000..6f5856f37 --- /dev/null +++ b/src/test/java/com/mindee/parsing/common/WorkflowResponseTest.java @@ -0,0 +1,21 @@ +package com.mindee.parsing.common; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mindee.product.invoicesplitter.InvoiceSplitterV1; +import java.io.File; +import java.io.IOException; + +public class WorkflowResponseTest { + private WorkflowResponse loadWorkflowResponse(String filePath) throws + IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.findAndRegisterModules(); + + JavaType type = objectMapper.getTypeFactory().constructParametricType( + WorkflowResponse.class, + InvoiceSplitterV1.class + ); + return objectMapper.readValue(new File(filePath), type); + } +} diff --git a/src/test/java/com/mindee/workflow/WorkflowIT.java b/src/test/java/com/mindee/workflow/WorkflowIT.java new file mode 100644 index 000000000..06c265820 --- /dev/null +++ b/src/test/java/com/mindee/workflow/WorkflowIT.java @@ -0,0 +1,53 @@ +package com.mindee.workflow; + +import com.mindee.MindeeClient; +import com.mindee.MindeeException; +import com.mindee.WorkflowOptions; +import com.mindee.input.LocalInputSource; +import com.mindee.parsing.common.Execution; +import com.mindee.parsing.common.ExecutionPriority; +import com.mindee.parsing.common.WorkflowResponse; +import com.mindee.product.generated.GeneratedV1; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class WorkflowIT { + private static MindeeClient client; + private static LocalInputSource financialDocumentInputSource; + private static String currentDateTime; + + @BeforeAll + static void clientSetUp() throws IOException { + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH:mm:ss"); + currentDateTime = now.format(formatter); + client = new MindeeClient(); + financialDocumentInputSource = new LocalInputSource( + "src/test/resources/products/financial_document/default_sample.jpg" + ); + } + + protected Execution getFinancialDocumentWorkflow(String workflowId) throws + IOException, MindeeException { + + WorkflowOptions options = WorkflowOptions.builder().alias("java-" + currentDateTime).priority( + ExecutionPriority.LOW).build(); + WorkflowResponse response = + client.executeWorkflow(workflowId, financialDocumentInputSource, options); + return response.getExecution(); + } + + + @Test + public void givenAWorkflowIDShouldReturnACorrectWorkflowObject() throws IOException { + Execution execution = getFinancialDocumentWorkflow(System.getenv("WORKFLOW_ID")); + + Assertions.assertEquals("low", execution.getPriority()); + Assertions.assertEquals("java-" + currentDateTime, execution.getFile().getAlias()); + + } +} diff --git a/src/test/java/com/mindee/workflow/WorkflowTest.java b/src/test/java/com/mindee/workflow/WorkflowTest.java new file mode 100644 index 000000000..331fa57e6 --- /dev/null +++ b/src/test/java/com/mindee/workflow/WorkflowTest.java @@ -0,0 +1,154 @@ +package com.mindee.workflow; + +import static org.mockito.Mockito.when; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mindee.MindeeClient; +import com.mindee.http.MindeeApi; +import com.mindee.input.LocalInputSource; +import com.mindee.parsing.common.Execution; +import com.mindee.parsing.common.WorkflowResponse; +import com.mindee.pdf.PdfOperation; +import com.mindee.product.generated.GeneratedV1; +import java.io.File; +import java.io.IOException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class WorkflowTest { + MindeeClient client; + @Mock + MindeeClient mockedClient; + MindeeApi mindeeApi; + PdfOperation pdfOperation; + + private ObjectMapper objectMapper; + + @BeforeEach + public void setUp() { + mindeeApi = Mockito.mock(MindeeApi.class); + pdfOperation = Mockito.mock(PdfOperation.class); + client = new MindeeClient(pdfOperation, mindeeApi); + + MockitoAnnotations.openMocks(this); + objectMapper = new ObjectMapper(); + } + + @Test + void givenAWorkflowMockFileShouldReturnAValidWorkflowObject() + throws IOException { + + File file = new File("src/test/resources/file_types/pdf/blank_1.pdf"); + + WorkflowResponse workflowResponse = new WorkflowResponse(); + workflowResponse.setExecution(new Execution()); + workflowResponse.setApiRequest(null); + when( + mindeeApi.executeWorkflowPost( + Mockito.any(), + Mockito.any(), + Mockito.any() + )) + .thenReturn(workflowResponse); + + WorkflowResponse execution = client.executeWorkflow( + "", + new LocalInputSource(file) + ); + + Assertions.assertNotNull(execution); + Mockito.verify(mindeeApi, Mockito.times(1)) + .executeWorkflowPost(Mockito.any(), Mockito.any(), Mockito.any()); + } + + @Test + void sendingADocumentToAnExecutionShouldDeserializeResponseCorrectly() throws IOException { + File jsonFile = new File("src/test/resources/workflows/success.json"); + WorkflowResponse.Default mockResponse = + objectMapper.readValue(jsonFile, WorkflowResponse.Default.class); + + when(mockedClient.executeWorkflow(Mockito.anyString(), + Mockito.any(LocalInputSource.class) + )) + .thenReturn(mockResponse); + + String workflowId = "07ebf237-ff27-4eee-b6a2-425df4a5cca6"; + String filePath = "src/test/resources/products/financial_document/default_sample.jpg"; + LocalInputSource inputSource = new LocalInputSource(filePath); + + WorkflowResponse response = + mockedClient.executeWorkflow(workflowId, inputSource); + + Assertions.assertNotNull(response); + Assertions.assertNotNull(response.getApiRequest()); + Assertions.assertNull(response.getExecution().getBatchName()); + Assertions.assertNull(response.getExecution().getCreatedAt()); + Assertions.assertNull(response.getExecution().getFile().getAlias()); + Assertions.assertEquals("default_sample.jpg", response.getExecution().getFile().getName()); + Assertions.assertEquals( + "8c75c035-e083-4e77-ba3b-7c3598bd1d8a", response.getExecution().getId()); + Assertions.assertNull(response.getExecution().getInference()); + Assertions.assertEquals("medium", response.getExecution().getPriority()); + Assertions.assertNull(response.getExecution().getReviewedAt()); + Assertions.assertNull(response.getExecution().getReviewedPrediction()); + Assertions.assertEquals("processing", response.getExecution().getStatus()); + Assertions.assertEquals("manual", response.getExecution().getType()); + Assertions.assertEquals( + "2024-11-13T13:02:31.699190", response.getExecution().getUploadedAt().toString()); + Assertions.assertEquals( + workflowId, response.getExecution().getWorkflowId()); + + Mockito.verify(mockedClient).executeWorkflow(workflowId, inputSource); + } + + + @Test + void sendingADocumentToAnExecutionWithPriorityAndAliasShouldDeserializeResponseCorrectly() + throws IOException { + File jsonFile = new File("src/test/resources/workflows/success_low_priority.json"); + WorkflowResponse.Default mockResponse = + objectMapper.readValue(jsonFile, WorkflowResponse.Default.class); + + when(mockedClient.executeWorkflow(Mockito.anyString(), + Mockito.any(LocalInputSource.class) + )) + .thenReturn(mockResponse); + + String workflowId = "07ebf237-ff27-4eee-b6a2-425df4a5cca6"; + String filePath = "src/test/resources/products/financial_document/default_sample.jpg"; + LocalInputSource inputSource = new LocalInputSource(filePath); + + WorkflowResponse response = + mockedClient.executeWorkflow(workflowId, inputSource); + + Assertions.assertNotNull(response); + Assertions.assertNotNull(response.getApiRequest()); + Assertions.assertNull(response.getExecution().getBatchName()); + Assertions.assertNull(response.getExecution().getCreatedAt()); + Assertions.assertEquals( + "low-priority-sample-test", response.getExecution().getFile().getAlias()); + Assertions.assertEquals("default_sample.jpg", response.getExecution().getFile().getName()); + Assertions.assertEquals( + "b743e123-e18c-4b62-8a07-811a4f72afd3", response.getExecution().getId()); + Assertions.assertNull(response.getExecution().getInference()); + Assertions.assertEquals("low", response.getExecution().getPriority()); + Assertions.assertNull(response.getExecution().getReviewedAt()); + Assertions.assertNull(response.getExecution().getReviewedPrediction()); + Assertions.assertEquals("processing", response.getExecution().getStatus()); + Assertions.assertEquals("manual", response.getExecution().getType()); + Assertions.assertEquals( + "2024-11-13T13:17:01.315179", response.getExecution().getUploadedAt().toString()); + Assertions.assertEquals( + workflowId, response.getExecution().getWorkflowId()); + + Mockito.verify(mockedClient).executeWorkflow(workflowId, inputSource); + } + +} diff --git a/tests/test_code_samples.sh b/tests/test_code_samples.sh index c59b48b57..43e00b2bf 100755 --- a/tests/test_code_samples.sh +++ b/tests/test_code_samples.sh @@ -12,7 +12,7 @@ if [ -z "${ENDPOINT}" ]; then echo "ENDPOINT is required"; exit 1; fi # We need the dependencies otherwise we get class not found exceptions mvn dependency:copy-dependencies -for f in $(find docs/code_samples -maxdepth 1 -name "*.txt" | sort -h) +for f in $(find docs/code_samples -maxdepth 1 -name "*.txt" -not -name "workflow_execution.txt" | sort -h) do echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" echo "${f}"