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
113 changes: 91 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ Where `${mindee.sdk.version}` is the version shown here:
## Loading a File and Parsing It
The `MindeeClient` class is the entry point for most of the helper library features.

## Synchronously Parsing a File
This is the easiest and fastest way to integrate into the Mindee API.

However, not all products are available in synchronous mode.

### Global Documents
These classes are available in the `com.mindee.product` package:
These classes are available in the `com.mindee.product` package:

```java
import com.mindee.MindeeClient;
Expand Down Expand Up @@ -55,19 +60,24 @@ public class SimpleMindeeClient {
System.out.println(response.getDocument().toString());
}
}

```

### Region-Specific Documents
**Note for Region-Specific Documents:**

Each region will have its own package within the general `com.mindee.product` package.

For example USA-specific classes will be in the `com.mindee.product.us` package:
```java
import com.mindee.product.us.bankcheck.BankCheckV1;
```

### Custom Documents (API Builder)
```java
import com.mindee.MindeeClient;
import com.mindee.input.LocalInputSource;
import com.mindee.parsing.common.PredictResponse;
import com.mindee.product.us.bankcheck.BankCheckV1;
import com.mindee.product.custom.CustomV1;
import com.mindee.http.Endpoint;
import java.io.File;
import java.io.IOException;

Expand All @@ -76,54 +86,113 @@ public class SimpleMindeeClient {

// Init a new client
MindeeClient mindeeClient = new MindeeClient("my-api-key");

// Init the endpoint for the custom document
Endpoint endpoint = new Endpoint("my-endpoint", "my-account");

// Load a file from disk
LocalInputSource localInputSource = new LocalInputSource(
"/path/to/the/file.ext"
"src/main/resources/invoices/invoice1.pdf"
);
// Parse the file
Document<BankCheckV1> response = mindeeClient.parse(
BankCheckV1.class,
localInputSource
Document<CustomV1> customDocument = mindeeClient.parse(
localInputSource,
endpoint
);
// Print a summary of the parsed data
System.out.println(response.getDocument().toString());
}
}
```

### Custom Documents (API Builder)
## Synchronously Parsing a File
This allows for easier handling of bursts of documents sent.

Some products are only available asynchronously, check the example code
directly on the Mindee platform.

### Enqueue and Parse a File
The client library will take care of handling the polling requests for you.

This is the easiest way to get started.

```java
import com.mindee.MindeeClient;
import com.mindee.input.LocalInputSource;
import com.mindee.parsing.common.PredictResponse;
import com.mindee.product.custom.CustomV1;
import com.mindee.http.Endpoint;
import com.mindee.parsing.common.AsyncPredictResponse;
import com.mindee.product.internationalid.InternationalIdV2;
import java.io.File;
import java.io.IOException;

public class SimpleMindeeClient {

public static void main(String[] args) throws IOException, InterruptedException {
String apiKey = "my-api-key";
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(new File(filePath));

// Parse the file asynchronously
AsyncPredictResponse<InternationalIdV2> response = mindeeClient.enqueueAndParse(
InternationalIdV2.class,
inputSource
);

// Print a summary of the response
System.out.println(response.toString());
}
}
```

### Enqueue and Parse a Webhook Response
This is an optional way of handling asynchronous APIs.

```java
import com.mindee.MindeeClient;
import com.mindee.input.LocalInputSource;
import com.mindee.input.LocalResponse;
import com.mindee.input.WebhookSource;
import com.mindee.product.internationalid.InternationalIdV2;

import java.io.IOException;

public class SimpleMindeeClient {
public static void main(String[] args) throws IOException {

// Init a new client
MindeeClient mindeeClient = new MindeeClient("my-api-key");

// Init the endpoint for the custom document
Endpoint endpoint = new Endpoint("my-endpoint", "my-account");

// Load a file from disk
LocalInputSource localInputSource = new LocalInputSource(
"src/main/resources/invoices/invoice1.pdf"
"/path/to/the/file.ext"
);
// Parse the file
Document<CustomV1> customDocument = mindeeClient.parse(
localInputSource,
endpoint
// Enqueue the file
String jobId = client.enqueue(InternationalIdV2.class, localInputSource)
.getJob().getId();

// Load the JSON string sent by the Mindee webhook callback.
//
// Reading the callback data will vary greatly depending on which
// HTTP server you are using, and is beyond the scope of this example.
LocalResponse localResponse = new LocalResponse("{'json': 'data'}");

// You can also use a File object as the input.
//LocalResponse localResponse = new LocalResponse(new File("/path/to/file.json"));

AsyncPredictResponse<InternationalIdV2> response = mindeeClient.loadPrediction(
InternationalIdV2.class,
new LocalResponse(webhookStream)
);

// Print a summary of the parsed data
System.out.println(response.getDocument().toString());
}
}
```


## Further Reading
Complete details on the working of the library are available in the following guides:

Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/mindee/MindeeClient.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.mindee;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mindee.http.Endpoint;
import com.mindee.http.MindeeApi;
import com.mindee.http.MindeeHttpApi;
import com.mindee.http.RequestParameters;
import com.mindee.input.InputSourceUtils;
import com.mindee.input.LocalInputSource;
import com.mindee.input.LocalResponse;
import com.mindee.input.PageOptions;
import com.mindee.parsing.common.AsyncPredictResponse;
import com.mindee.parsing.common.Inference;
Expand Down Expand Up @@ -487,6 +490,24 @@ private PredictResponse<CustomV1> parse(
.build());
}

/**
* Load a local prediction.
* Typically used when wanting to load from a webhook callback.
* However, any kind of Mindee response may be loaded.
*/
public <T extends Inference> AsyncPredictResponse<T> loadPrediction(
Class<T> type,
LocalResponse localResponse
) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
JavaType parametricType = objectMapper.getTypeFactory().constructParametricType(
AsyncPredictResponse.class,
type
);
return objectMapper.readValue(localResponse.getFile(), parametricType);
}

private byte[] getSplitFile(
LocalInputSource localInputSource,
PageOptions pageOptions
Expand Down
22 changes: 11 additions & 11 deletions src/main/java/com/mindee/http/MindeeHttpApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public <DocT extends Inference> AsyncPredictResponse<DocT> documentQueueGet(

// required to register jackson date module format to deserialize
mapper.findAndRegisterModules();
JavaType type = mapper.getTypeFactory().constructParametricType(
JavaType parametricType = mapper.getTypeFactory().constructParametricType(
AsyncPredictResponse.class,
documentClass
);
Expand All @@ -142,10 +142,10 @@ public <DocT extends Inference> AsyncPredictResponse<DocT> documentQueueGet(
HttpEntity responseEntity = response.getEntity();
int statusCode = response.getStatusLine().getStatusCode();
if (!is2xxStatusCode(statusCode)) {
throw getHttpError(type, response);
throw getHttpError(parametricType, response);
}
String rawResponse = readRawResponse(responseEntity);
AsyncPredictResponse<DocT> mappedResponse = mapper.readValue(rawResponse, type);
AsyncPredictResponse<DocT> mappedResponse = mapper.readValue(rawResponse, parametricType);
mappedResponse.setRawResponse(rawResponse);
if (
mappedResponse.getJob() != null
Expand Down Expand Up @@ -179,7 +179,7 @@ public <DocT extends Inference> PredictResponse<DocT> predictPost(

// required to register jackson date module format to deserialize
mapper.findAndRegisterModules();
JavaType type = mapper.getTypeFactory().constructParametricType(
JavaType parametricType = mapper.getTypeFactory().constructParametricType(
PredictResponse.class,
documentClass
);
Expand All @@ -190,13 +190,13 @@ public <DocT extends Inference> PredictResponse<DocT> predictPost(
HttpEntity responseEntity = response.getEntity();
int statusCode = response.getStatusLine().getStatusCode();
if (!is2xxStatusCode(statusCode)) {
throw getHttpError(type, response);
throw getHttpError(parametricType, response);
}
if (responseEntity.getContentLength() == 0) {
throw new MindeeException("Empty response from server.");
}
String rawResponse = readRawResponse(responseEntity);
PredictResponse<DocT> mappedResponse = mapper.readValue(rawResponse, type);
PredictResponse<DocT> mappedResponse = mapper.readValue(rawResponse, parametricType);
mappedResponse.setRawResponse(rawResponse);
return mappedResponse;
} catch (IOException err) {
Expand All @@ -218,7 +218,7 @@ public <DocT extends Inference> AsyncPredictResponse<DocT> predictAsyncPost(

// required to register jackson date module format to deserialize
mapper.findAndRegisterModules();
JavaType type = mapper.getTypeFactory().constructParametricType(
JavaType parametricType = mapper.getTypeFactory().constructParametricType(
AsyncPredictResponse.class,
documentClass
);
Expand All @@ -229,13 +229,13 @@ public <DocT extends Inference> AsyncPredictResponse<DocT> predictAsyncPost(
HttpEntity responseEntity = response.getEntity();
int statusCode = response.getStatusLine().getStatusCode();
if (!is2xxStatusCode(statusCode)) {
throw getHttpError(type, response);
throw getHttpError(parametricType, response);
}
if (responseEntity.getContentLength() == 0) {
throw new MindeeException("Empty response from server.");
}
String rawResponse = readRawResponse(responseEntity);
AsyncPredictResponse<DocT> mappedResponse = mapper.readValue(rawResponse, type);
AsyncPredictResponse<DocT> mappedResponse = mapper.readValue(rawResponse, parametricType);
mappedResponse.setRawResponse(rawResponse);
return mappedResponse;
} catch (IOException err) {
Expand All @@ -244,7 +244,7 @@ public <DocT extends Inference> AsyncPredictResponse<DocT> predictAsyncPost(
}

private <ResponseT extends ApiResponse> MindeeHttpException getHttpError(
JavaType javaType,
JavaType parametricType,
CloseableHttpResponse response
) {
int statusCode = response.getStatusLine().getStatusCode();
Expand All @@ -261,7 +261,7 @@ private <ResponseT extends ApiResponse> MindeeHttpException getHttpError(
return new MindeeHttpException(statusCode, message, details, errorCode);
}
try {
ResponseT predictResponse = mapper.readValue(rawResponse, javaType);
ResponseT predictResponse = mapper.readValue(rawResponse, parametricType);
message = predictResponse.getApiRequest().getError().getMessage();
ErrorDetails errorDetails = predictResponse.getApiRequest().getError().getDetails();
if (errorDetails != null) {
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/com/mindee/input/LocalResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.mindee.input;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import lombok.Getter;
import org.apache.pdfbox.io.IOUtils;

/**
* A Mindee response saved locally.
*/
@Getter
public class LocalResponse {
private final byte[] file;

/**
* Load from an {@link InputStream}.
*/
public LocalResponse(InputStream input) throws IOException {
this.file = IOUtils.toByteArray(input);
}

/**
* Load from a UTF-8 {@link String}.
*/
public LocalResponse(String input) {
this.file = input.getBytes(StandardCharsets.UTF_8);
}

/**
* Load from a {@link File}.
*/
public LocalResponse(File input) throws IOException {
this.file = Files.readAllBytes(input.toPath());
}
}
28 changes: 28 additions & 0 deletions src/test/java/com/mindee/MindeeClientTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.mindee;

import com.mindee.http.Endpoint;
import com.mindee.input.LocalResponse;
import com.mindee.input.LocalInputSource;
import com.mindee.http.MindeeApi;
import com.mindee.http.RequestParameters;
Expand All @@ -10,8 +11,10 @@
import com.mindee.parsing.common.Document;
import com.mindee.parsing.common.Job;
import com.mindee.parsing.common.PredictResponse;
import com.mindee.product.ProductTestHelper;
import com.mindee.product.custom.CustomV1;
import com.mindee.product.invoice.InvoiceV4;
import com.mindee.product.internationalid.InternationalIdV2;
import com.mindee.pdf.PdfOperation;
import com.mindee.pdf.SplitPdf;
import java.io.File;
Expand Down Expand Up @@ -391,4 +394,29 @@ void givenAnAsyncUrl_whenEnqueued_shouldInvokeApiCorrectly() throws IOException
Assertions.assertEquals(new URL("https://fake.pdf"), requestParameters.getFileUrl());
Assertions.assertEquals("someid", jobId);
}

@Test
void givenJsonInput_whenSync_shouldDeserializeCorrectly() throws IOException {
File file = new File("src/test/resources/products/invoices/response_v4/complete.json");
LocalResponse localResponse = new LocalResponse(file);
AsyncPredictResponse<InvoiceV4> predictResponse = client.loadPrediction(InvoiceV4.class, localResponse);
ProductTestHelper.assertStringEqualsFile(
predictResponse.getDocumentObj().toString(),
"src/test/resources/products/invoices/response_v4/summary_full.rst"
);
}

@Test
void givenJsonInput_whenAsync_shouldDeserializeCorrectly() throws IOException {
File file = new File("src/test/resources/products/international_id/response_v2/complete.json");
LocalResponse localResponse = new LocalResponse(file);
AsyncPredictResponse<InternationalIdV2> predictResponse = client.loadPrediction(
InternationalIdV2.class,
localResponse
);
ProductTestHelper.assertStringEqualsFile(
predictResponse.getDocumentObj().toString(),
"src/test/resources/products/international_id/response_v2/summary_full.rst"
);
}
}