Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
41 changes: 41 additions & 0 deletions docs/code_samples/workflow_execution.txt
Original file line number Diff line number Diff line change
@@ -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());
}
}
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@
<scm>
<connection>scm:git:git://github.com/mindee/mindee-api-java.git</connection>
<developerConnection>scm:git:ssh://github.com:mindee/mindee-api-java.git</developerConnection>
<url>http://github.com/mindee/mindee-api-java/tree/main</url>
<url>https://github.com/mindee/mindee-api-java/tree/main</url>
</scm>

<properties>
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/com/mindee/MindeeClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -323,6 +324,42 @@ private <T extends Inference> AsyncPredictResponse<T> enqueueAndParse(
throw new RuntimeException("Max retries exceeded. Failed to get the document.");
}

/**
* Send a local file to a workflow execution.
*/
public WorkflowResponse<GeneratedV1> 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<GeneratedV1> 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.
*/
Expand Down
47 changes: 47 additions & 0 deletions src/main/java/com/mindee/WorkflowOptions.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
7 changes: 7 additions & 0 deletions src/main/java/com/mindee/http/MindeeApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -39,6 +40,12 @@ abstract public <DocT extends Inference> PredictResponse<DocT> predictPost(
RequestParameters requestParameters
) throws IOException;

abstract public <DocT extends Inference> WorkflowResponse<DocT> executeWorkflowPost(
Class<DocT> documentClass,
String workflowId,
RequestParameters requestParameters
) throws IOException;

protected String getUserAgent() {
String javaVersion = System.getProperty("java.version");
String sdkVersion = getClass().getPackage().getImplementationVersion();
Expand Down
116 changes: 99 additions & 17 deletions src/main/java/com/mindee/http/MindeeHttpApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -41,6 +42,7 @@ public final class MindeeHttpApi extends MindeeApi {

private static final ObjectMapper mapper = new ObjectMapper();
private final Function<Endpoint, String> buildBaseUrl = this::buildUrl;
private final Function<String, String> buildWorkflowBaseUrl = this::buildWorkflowUrl;
/**
* The MindeeSetting needed to make the api call.
*/
Expand All @@ -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<Endpoint, String> 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<Endpoint, String> 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<Endpoint, String> 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<String, String> workflowUrlFromId;

public MindeeHttpApi(MindeeSettings mindeeSettings) {
this(
mindeeSettings,
null,
null,
null,
null,
null
);
}
Expand All @@ -82,7 +92,8 @@ private MindeeHttpApi(
HttpClientBuilder httpClientBuilder,
Function<Endpoint, String> urlFromEndpoint,
Function<Endpoint, String> asyncUrlFromEndpoint,
Function<Endpoint, String> documentUrlFromEndpoint
Function<Endpoint, String> documentUrlFromEndpoint,
Function<String, String> workflowUrlFromEndpoint
) {
this.mindeeSettings = mindeeSettings;

Expand Down Expand Up @@ -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;
}
}

/**
Expand Down Expand Up @@ -149,14 +166,14 @@ public <DocT extends Inference> AsyncPredictResponse<DocT> 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;
Expand Down Expand Up @@ -243,6 +260,47 @@ public <DocT extends Inference> AsyncPredictResponse<DocT> predictAsyncPost(
}
}


/**
* POST a prediction request for a workflow response.
*/
public <DocT extends Inference> WorkflowResponse<DocT> executeWorkflowPost(
Class<DocT> 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<DocT> mappedResponse = mapper.readValue(rawResponse, parametricType);
mappedResponse.setRawResponse(rawResponse);
return mappedResponse;
} catch (IOException err) {
throw new MindeeException(err.getMessage(), err);
}
}


private <ResponseT extends ApiResponse> MindeeHttpException getHttpError(
JavaType parametricType,
CloseableHttpResponse response
Expand All @@ -266,8 +324,7 @@ private <ResponseT extends ApiResponse> MindeeHttpException getHttpError(
ErrorDetails errorDetails = predictResponse.getApiRequest().getError().getDetails();
if (errorDetails != null) {
details = errorDetails.toString();
}
else {
} else {
details = "";
}
errorCode = predictResponse.getApiRequest().getError().getCode();
Expand All @@ -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
Expand Down Expand Up @@ -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<String, URL> 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");
}
Expand Down
Loading
Loading