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
2 changes: 1 addition & 1 deletion .github/workflows/_test-code-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ jobs:

- name: Tests sample code
run: |
./tests/test_code_samples.sh ${{ secrets.MINDEE_ACCOUNT_SE_TESTS }} ${{ secrets.MINDEE_ENDPOINT_SE_TESTS }} ${{ secrets.MINDEE_API_KEY_SE_TESTS }}
./tests/test_code_samples.sh ${{ secrets.MINDEE_ACCOUNT_SE_TESTS }} ${{ secrets.MINDEE_ENDPOINT_SE_TESTS }} ${{ secrets.MINDEE_API_KEY_SE_TESTS }} ${{ secrets.MINDEE_V2_SE_TESTS_API_KEY }} ${{ secrets.MINDEE_V2_SE_TESTS_FINDOC_MODEL_ID }}
2 changes: 2 additions & 0 deletions .github/workflows/_test-integrations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,7 @@ jobs:
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 }}
run: |
mvn clean test-compile failsafe:integration-test failsafe:verify
12 changes: 0 additions & 12 deletions src/main/java/com/mindee/MindeeClientV2.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,6 @@ else if (resp.getJob().getStatus().equals("Processed")) {
throw new RuntimeException("Max retries exceeded (" + max + ").");
}

/**
* Deserialize a webhook payload (or any saved response) into an
* {@link InferenceResponse}.
*/
public InferenceResponse loadInference(LocalResponse localResponse) throws IOException {
ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
InferenceResponse model =
mapper.readValue(localResponse.getFile(), InferenceResponse.class);
model.setRawResponse(localResponse.toString());
return model;
}

private static MindeeApiV2 createDefaultApiV2(String apiKey) {
MindeeSettingsV2 settings = apiKey == null || apiKey.trim().isEmpty()
? new MindeeSettingsV2()
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/mindee/http/MindeeHttpApiV2.java
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ private HttpPost buildHttpPost(
private <R extends CommonResponse> R deserializeOrThrow(
String body, Class<R> clazz, int httpStatus) throws MindeeHttpExceptionV2 {

if (httpStatus >= 200 && httpStatus < 300) {
if (httpStatus >= 200 && httpStatus < 400) {
try {
R model = mapper.readerFor(clazz).readValue(body);
model.setRawResponse(body);
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/com/mindee/input/LocalResponse.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.mindee.input;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mindee.MindeeException;
import com.mindee.parsing.v2.CommonResponse;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -91,4 +94,25 @@ public String getHmacSignature(String secretKey) {
public boolean isValidHmacSignature(String secretKey, String signature) {
return signature.equals(getHmacSignature(secretKey));
}


/**
* Deserialize this local JSON payload into a specific {@link CommonResponse}
* subtype: {@code InferenceResponse}, {@code JobResponse}.
*
* @param responseClass the concrete class to instantiate
* @param <T> generic {@link CommonResponse}
* @return Either a {@code InferenceResponse} or {@code JobResponse} instance.
* @throws MindeeException if the payload cannot be deserialized into the requested type
*/
public <T extends CommonResponse> T deserializeResponse(Class<T> responseClass) {
ObjectMapper mapper = new ObjectMapper();
try {
T response = mapper.readValue(this.file, responseClass);
response.setRawResponse(new String(this.file, StandardCharsets.UTF_8));
return response;
} catch (Exception ex) {
throw new MindeeException("Invalid class specified for deserialization.", ex);
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/mindee/parsing/v2/field/BaseField.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public abstract class BaseField {
* Field's location.
*/
@JsonProperty("locations")
private List<FieldLocation> page;
private List<FieldLocation> locations;

/**
* Confidence associated with the field.
Expand Down
12 changes: 1 addition & 11 deletions src/test/java/com/mindee/MindeeClientV2IT.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,6 @@ class MindeeClientV2IT {
void setUp() {
String apiKey = System.getenv("MINDEE_V2_API_KEY");
modelId = System.getenv("MINDEE_V2_FINDOC_MODEL_ID");

assumeTrue(
apiKey != null && !apiKey.trim().isEmpty(),
"MINDEE_V2_API_KEY env var is missing – integration tests skipped"
);
assumeTrue(
modelId != null && !modelId.trim().isEmpty(),
"MINDEE_V2_FINDOC_MODEL_ID env var is missing – integration tests skipped"
);

mindeeClient = new MindeeClientV2(apiKey);
}

Expand Down Expand Up @@ -116,7 +106,7 @@ void invalidJob_mustThrowError() {
MindeeHttpExceptionV2.class,
() -> mindeeClient.getInference("not-a-valid-job-ID")
);
assertEquals(404, ex.getStatus());
assertEquals(422, ex.getStatus());
assertNotNull(ex);
}
}
7 changes: 3 additions & 4 deletions src/test/java/com/mindee/MindeeClientV2Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,18 +114,17 @@ void document_getInference_async() throws IOException {
}

@Nested
@DisplayName("loadInference()")
class LoadInference {
@DisplayName("deserializeResponse()")
class DeserializeResponse {

@Test
@DisplayName("parses local JSON and exposes correct field values")
void inference_loadsLocally() throws IOException {
MindeeClientV2 mindeeClient = new MindeeClientV2("dummy");
File jsonFile =
new File("src/test/resources/v2/products/financial_document/complete.json");
LocalResponse localResponse = new LocalResponse(jsonFile);

InferenceResponse loaded = mindeeClient.loadInference(localResponse);
InferenceResponse loaded = localResponse.deserializeResponse(InferenceResponse.class);

assertNotNull(loaded, "Loaded InferenceResponse must not be null");
assertEquals(
Expand Down
89 changes: 32 additions & 57 deletions src/test/java/com/mindee/parsing/v2/InferenceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
class InferenceTest {

private InferenceResponse loadFromResource(String resourcePath) throws IOException {
MindeeClientV2 dummyClient = new MindeeClientV2("dummy");
return dummyClient.loadInference(new LocalResponse(InferenceTest.class.getClassLoader().getResourceAsStream(resourcePath)));
LocalResponse localResponse = new LocalResponse(InferenceTest.class.getClassLoader().getResourceAsStream(resourcePath));
return localResponse.deserializeResponse(InferenceResponse.class);
}

private String readFileAsString(String path)
Expand Down Expand Up @@ -90,41 +90,55 @@ void asyncPredict_whenEmpty_mustHaveValidProperties() throws IOException {
class CompletePrediction {

@Test
@DisplayName("all properties must be valid")
void asyncPredict_whenComplete_mustHaveValidProperties() throws IOException {
@DisplayName("every exposed property must be valid and consistent")
void asyncPredict_whenComplete_mustExposeAllProperties() throws IOException {
InferenceResponse response = loadFromResource("v2/products/financial_document/complete.json");
InferenceFields fields = response.getInference().getResult().getFields();
Inference inf = response.getInference();
assertNotNull(inf, "Inference must not be null");
assertEquals("12345678-1234-1234-1234-123456789abc", inf.getId(), "Inference ID mismatch");

assertEquals(21, fields.size(), "Expected 21 fields");
InferenceResultModel model = inf.getModel();
assertNotNull(model, "Model must not be null");
assertEquals("12345678-1234-1234-1234-123456789abc", model.getId(), "Model ID mismatch");

InferenceResultFile file = inf.getFile();
assertNotNull(file, "File must not be null");
assertEquals("complete.jpg", file.getName(), "File name mismatch");
assertNull(file.getAlias(), "File alias must be null for this payload");

InferenceFields fields = inf.getResult().getFields();
assertEquals(21, fields.size(), "Expected 21 fields in the payload");

SimpleField date = fields.get("date").getSimpleField();
assertEquals("2019-11-02", date.getValue(), "'date' value mismatch");

DynamicField taxes = fields.get("taxes");
assertNotNull(taxes, "'taxes' field must exist");
ListField taxesList = taxes.getListField();
assertNotNull(taxesList, "'taxes' must be a ListField");
assertEquals(1, taxesList.getItems().size(), "'taxes' list must contain exactly one item");
assertNotNull(taxes.toString(), "'taxes' toString() must not be null");

ObjectField taxItemObj = taxesList.getItems().get(0).getObjectField();
assertNotNull(taxItemObj, "First item of 'taxes' must be an ObjectField");
assertEquals(3, taxItemObj.getFields().size(), "Tax ObjectField must contain 3 sub-fields");
assertEquals(
31.5,
taxItemObj.getFields().get("base").getSimpleField().getValue(),
"'taxes.base' value mismatch"
);
SimpleField baseTax = taxItemObj.getFields().get("base").getSimpleField();
assertEquals(31.5, baseTax.getValue(), "'taxes.base' value mismatch");
assertNotNull(taxes.toString(), "'taxes'.toString() must not be null");

DynamicField supplierAddress = fields.get("supplier_address");
assertNotNull(supplierAddress, "'supplier_address' field must exist");

ObjectField supplierObj = supplierAddress.getObjectField();
assertNotNull(supplierObj, "'supplier_address' must be an ObjectField");

DynamicField country = supplierObj.getFields().get("country");
assertNotNull(country, "'supplier_address.country' must exist");
assertEquals("USA", country.getSimpleField().getValue());
assertEquals("USA", country.toString());

assertEquals("USA", country.getSimpleField().getValue(), "Country mismatch");
assertEquals("USA", country.toString(), "'country'.toString() mismatch");
assertNotNull(supplierAddress.toString(), "'supplier_address'.toString() must not be null");

ObjectField customerAddr = fields.get("customer_address").getObjectField();
SimpleField city = customerAddr.getFields().get("city").getSimpleField();
assertEquals("New York", city.getValue(), "City mismatch");

assertNull(inf.getResult().getOptions(), "Options must be null");
}
}

Expand Down Expand Up @@ -206,45 +220,6 @@ void rawTexts_mustBeAccessible() throws IOException {
}
}

@Nested
@DisplayName("complete.json – full inference response")
class FullInference {
@Test
@DisplayName("complete financial-document JSON must round-trip correctly")
void fullInferenceResponse_mustExposeEveryProperty() throws IOException {
InferenceResponse resp = loadFromResource("v2/products/financial_document/complete.json");

Inference inf = resp.getInference();
assertNotNull(inf);
assertEquals("12345678-1234-1234-1234-123456789abc", inf.getId());

InferenceFields f = inf.getResult().getFields();

SimpleField date = f.get("date").getSimpleField();
assertEquals("2019-11-02", date.getValue());

ListField taxes = f.get("taxes").getListField();
ObjectField firstTax = taxes.getItems().get(0).getObjectField();
SimpleField baseTax = firstTax.getFields().get("base").getSimpleField();
assertEquals(31.5, baseTax.getValue());

ObjectField customerAddr = f.get("customer_address").getObjectField();
SimpleField city = customerAddr.getFields().get("city").getSimpleField();
assertEquals("New York", city.getValue());

InferenceResultModel model = inf.getModel();
assertNotNull(model);
assertEquals("12345678-1234-1234-1234-123456789abc", model.getId());

InferenceResultFile file = inf.getFile();
assertNotNull(file);
assertEquals("complete.jpg", file.getName());
assertNull(file.getAlias());

assertNull(inf.getResult().getOptions());
}
}

@Nested
@DisplayName("rst display")
class RstDisplay {
Expand Down
Loading