From 7ff87395cc87d4fe1eaa06949486512dfa6ce7c4 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:59:02 +0100 Subject: [PATCH 1/6] :sparkles: add better errors --- src/Error/ErrorItem.php | 28 +++++++++++++++++++++++++ src/Error/MindeeV2HttpException.php | 24 +++++++++++++++------ src/Error/MindeeV2HttpUnknownError.php | 28 +++++++++++++++++++++++++ src/Http/MindeeApiV2.php | 14 ++++++------- src/Parsing/V2/ErrorResponse.php | 29 ++++++++++++++++++++++++-- 5 files changed, 107 insertions(+), 16 deletions(-) create mode 100644 src/Error/ErrorItem.php create mode 100644 src/Error/MindeeV2HttpUnknownError.php diff --git a/src/Error/ErrorItem.php b/src/Error/ErrorItem.php new file mode 100644 index 00000000..e23cbda5 --- /dev/null +++ b/src/Error/ErrorItem.php @@ -0,0 +1,28 @@ +detail = $detail; + $this->pointer = $pointer; + } +} diff --git a/src/Error/MindeeV2HttpException.php b/src/Error/MindeeV2HttpException.php index 898435c2..0b0feee2 100644 --- a/src/Error/MindeeV2HttpException.php +++ b/src/Error/MindeeV2HttpException.php @@ -2,6 +2,8 @@ namespace Mindee\Error; +use Mindee\Parsing\V2\ErrorResponse; + /** * Exceptions relating to HTTP errors for the V2 API. */ @@ -15,15 +17,25 @@ class MindeeV2HttpException extends MindeeException * @var string|null Details on the exception. */ public ?string $detail; + /** + * @var string|null Title of the error. + */ + public ?string $title; + /** + * @var string|null Error code. + * Note: PHP's `RuntimeException` class uses `$code` for the error code. + */ + public ?string $errorCode; /** - * @param integer $status HTTP status code, defaults to -1 if not set. - * @param string|null $detail Optional details on the exception. + * @param ErrorResponse $response Server Error response. */ - public function __construct(int $status, string $detail = null) + public function __construct(ErrorResponse $response) { - parent::__construct("HTTP Error $status - $detail"); - $this->status = $status; - $this->detail = $detail; + parent::__construct("HTTP $response->status - $response->title :: $response->code - $response->detail"); + $this->status = $response->status; + $this->detail = $response->detail; + $this->errorCode = $response->code; + $this->title = $response->title; } } diff --git a/src/Error/MindeeV2HttpUnknownError.php b/src/Error/MindeeV2HttpUnknownError.php new file mode 100644 index 00000000..e2ec4a85 --- /dev/null +++ b/src/Error/MindeeV2HttpUnknownError.php @@ -0,0 +1,28 @@ + -1, + "detail" => "Couldn't deserialize server error. Found: $response", + "title" => "Unknown error", + "code" => "000-000" + ] + ) + ); + } +} diff --git a/src/Http/MindeeApiV2.php b/src/Http/MindeeApiV2.php index d0a77cd1..c2e60dbc 100644 --- a/src/Http/MindeeApiV2.php +++ b/src/Http/MindeeApiV2.php @@ -15,10 +15,12 @@ // phpcs:enable use Mindee\Error\MindeeV2HttpException; +use Mindee\Error\MindeeV2HttpUnknownError; use Mindee\Input\InferenceParameters; use Mindee\Input\InputSource; use Mindee\Input\LocalInputSource; use Mindee\Input\URLInputSource; +use Mindee\Parsing\V2\ErrorResponse; use Mindee\Parsing\V2\InferenceResponse; use Mindee\Parsing\V2\JobResponse; @@ -164,8 +166,9 @@ public function reqPostInferenceEnqueue(InputSource $inputDoc, InferenceParamete * @return JobResponse|InferenceResponse The processed response object. * @throws MindeeException Throws if HTTP status indicates an error or deserialization fails. * @throws MindeeV2HttpException Throws if the HTTP status indicates an error. + * @throws MindeeV2HttpUnknownError Throws if the server sends an unexpected reply. */ - private function processResponse(array $result, string $responseType) + private function processResponse(array $result, string $responseType): InferenceResponse|JobResponse { $statusCode = $result['code'] ?? -1; @@ -173,14 +176,9 @@ private function processResponse(array $result, string $responseType) $responseData = json_decode($result['data'], true); if ($responseData && isset($responseData['status'])) { - throw new MindeeV2HttpException( - $responseData['status'], - $responseData['detail'] ?? 'Unknown error.' - ); + throw new MindeeV2HttpException(new ErrorResponse($responseData)); } - - $detail = $responseData && $responseData['detail'] ? $responseData['detail'] : 'Unknown Error'; - throw new MindeeException($result['code'] ?? -1, $detail); + throw new MindeeV2HttpUnknownError(json_encode($result, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); } try { diff --git a/src/Parsing/V2/ErrorResponse.php b/src/Parsing/V2/ErrorResponse.php index 5362c680..7190621e 100644 --- a/src/Parsing/V2/ErrorResponse.php +++ b/src/Parsing/V2/ErrorResponse.php @@ -2,21 +2,37 @@ namespace Mindee\Parsing\V2; +use Mindee\Error\ErrorItem; + /** * Error response class. */ class ErrorResponse { /** - * @var integer HTTP Status code. + * @var integer The HTTP status code returned by the server. */ public int $status; /** - * @var string The detail on the error. + * @var string A human-readable explanation specific to the occurrence of the problem. */ public string $detail; + /** + * @var string|null A short, human-readable summary of the problem. + */ + public ?string $title; + /** + * @var string|null A machine-readable code specific to the occurrence of the problem. + * Note: PHP's `RuntimeException` class uses `$code` for the error code. + */ + public ?string $code; + /** + * @var array|mixed|null A list of explicit error details. + */ + public ?array $errors; + /** * @param array $serverResponse Raw server response array. */ @@ -24,5 +40,14 @@ public function __construct(array $serverResponse) { $this->status = $serverResponse['status']; $this->detail = $serverResponse['detail']; + $this->title = $serverResponse['title'] ?? null; + $this->code = $serverResponse['code'] ?? null; + if (isset($serverResponse['errors']) && is_array($serverResponse['errors'])) { + $this->errors = array_map(static function ($error) { + return new ErrorItem($error); + }, $serverResponse['errors']); + } else { + $this->errors = []; + } } } From 765ce1908b5dea687951b3d73c56af24bd705e57 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 13 Nov 2025 19:05:55 +0100 Subject: [PATCH 2/6] :sparkles: add rag metadata --- src/Error/ErrorItem.php | 9 ++-- src/Error/MindeeV2HttpException.php | 5 ++ src/Input/LocalResponse.php | 2 +- src/Parsing/V2/InferenceResult.php | 8 +++ src/Parsing/V2/RagMetadata.php | 22 ++++++++ tests/{ => V2}/ClientV2Test.php | 13 ++--- tests/{ => V2}/ClientV2TestFunctional.php | 31 ++++++++---- .../InferenceResponseTest.php} | 50 +++++++++++++++---- tests/V2/Parsing/JobResponseTest.php | 1 + 9 files changed, 109 insertions(+), 32 deletions(-) create mode 100644 src/Parsing/V2/RagMetadata.php rename tests/{ => V2}/ClientV2Test.php (89%) rename tests/{ => V2}/ClientV2TestFunctional.php (81%) rename tests/V2/{InferenceTest.php => Parsing/InferenceResponseTest.php} (88%) create mode 100644 tests/V2/Parsing/JobResponseTest.php diff --git a/src/Error/ErrorItem.php b/src/Error/ErrorItem.php index e23cbda5..fc2e96d8 100644 --- a/src/Error/ErrorItem.php +++ b/src/Error/ErrorItem.php @@ -17,12 +17,11 @@ class ErrorItem public string $detail; /** - * @param string $detail Explicit information on the issue. - * @param string|null $pointer A JSON Pointer to the location of the body property. + * @param array $rawResponse Raw error response from the API. */ - public function __construct(string $detail, ?string $pointer = null) + public function __construct(array $rawResponse) { - $this->detail = $detail; - $this->pointer = $pointer; + $this->detail = $rawResponse['detail']; + $this->pointer = $rawResponse['pointer']; } } diff --git a/src/Error/MindeeV2HttpException.php b/src/Error/MindeeV2HttpException.php index 0b0feee2..d8146fba 100644 --- a/src/Error/MindeeV2HttpException.php +++ b/src/Error/MindeeV2HttpException.php @@ -26,6 +26,10 @@ class MindeeV2HttpException extends MindeeException * Note: PHP's `RuntimeException` class uses `$code` for the error code. */ public ?string $errorCode; + /** + * @var array List of associated errors. + */ + public array $errors; /** * @param ErrorResponse $response Server Error response. @@ -37,5 +41,6 @@ public function __construct(ErrorResponse $response) $this->detail = $response->detail; $this->errorCode = $response->code; $this->title = $response->title; + $this->errors = $response->errors; } } diff --git a/src/Input/LocalResponse.php b/src/Input/LocalResponse.php index 9df33843..81e037f8 100644 --- a/src/Input/LocalResponse.php +++ b/src/Input/LocalResponse.php @@ -125,7 +125,7 @@ public function isValidHMACSignature(string $secretKey, string $signature): bool * @return mixed An instance of responseClass populated with the file content. * @throws MindeeException If the provided class cannot be instantiated. */ - public function deserializeResponse(string $responseClass) + public function deserializeResponse(string $responseClass): mixed { try { $data = $this->toArray(); diff --git a/src/Parsing/V2/InferenceResult.php b/src/Parsing/V2/InferenceResult.php index 8d879c43..78ccaabd 100644 --- a/src/Parsing/V2/InferenceResult.php +++ b/src/Parsing/V2/InferenceResult.php @@ -19,6 +19,11 @@ class InferenceResult */ public ?RawText $rawText; + /** + * @var RagMetadata|null RAG metadata. + */ + public ?RagMetadata $rag; + /** * @param array $serverResponse Raw server response array. */ @@ -28,6 +33,9 @@ public function __construct(array $serverResponse) $this->rawText = isset($serverResponse['raw_text']) ? new RawText($serverResponse['raw_text']) : null; + $this->rag = isset( + $serverResponse['rag'] + ) ? new RagMetadata($serverResponse['rag']) : null; } /** diff --git a/src/Parsing/V2/RagMetadata.php b/src/Parsing/V2/RagMetadata.php new file mode 100644 index 00000000..a2dec186 --- /dev/null +++ b/src/Parsing/V2/RagMetadata.php @@ -0,0 +1,22 @@ +retrievedDocumentId = $rawResponse['retrieved_document_id'] ?? null; + } +} diff --git a/tests/ClientV2Test.php b/tests/V2/ClientV2Test.php similarity index 89% rename from tests/ClientV2Test.php rename to tests/V2/ClientV2Test.php index 428d2777..af7f6fe5 100644 --- a/tests/ClientV2Test.php +++ b/tests/V2/ClientV2Test.php @@ -1,5 +1,7 @@ createMock(MindeeApiV2::class); - $syntheticResponse = file_get_contents(__DIR__ . '/resources/v2/job/ok_processing.json'); + $syntheticResponse = file_get_contents(\TestingUtilities::getV2DataDir() . '/job/ok_processing.json'); $predictable->expects($this->once()) ->method('reqPostInferenceEnqueue') ->with( @@ -38,7 +39,7 @@ public function testEnqueuePostAsync(): void $mindeeClient = self::makeClientWithMockedApi($predictable); - $input = new PathInput(__DIR__ . '/resources/file_types/pdf/blank_1.pdf'); + $input = new PathInput(\TestingUtilities::getFileTypesDir() . '/pdf/blank_1.pdf'); $params = new InferenceParameters('dummy-model-id'); $response = $mindeeClient->enqueueInference($input, $params); @@ -52,7 +53,7 @@ public function testDocumentGetJobAsync(): void /** @var MindeeApiV2&MockObject $predictable */ $predictable = $this->createMock(MindeeApiV2::class); - $syntheticResponse = file_get_contents(__DIR__ . '/resources/v2/job/ok_processing.json'); + $syntheticResponse = file_get_contents(\TestingUtilities::getV2DataDir() . '/job/ok_processing.json'); $processing = new JobResponse(json_decode($syntheticResponse, true)); $predictable->expects($this->once()) @@ -73,7 +74,7 @@ public function testDocumentGetInferenceAsync(): void /** @var MindeeApiV2&MockObject $predictable */ $predictable = $this->createMock(MindeeApiV2::class); - $jsonFile = __DIR__ . '/resources/v2/products/financial_document/complete.json'; + $jsonFile = \TestingUtilities::getV2DataDir() . '/products/financial_document/complete.json'; $this->assertFileExists($jsonFile, 'Test resource file must exist'); $json = json_decode(file_get_contents($jsonFile), true); @@ -108,7 +109,7 @@ public function testDocumentGetInferenceAsync(): void public function testInferenceLoadsLocally(): void { - $jsonFile = __DIR__ . '/resources/v2/products/financial_document/complete.json'; + $jsonFile = \TestingUtilities::getV2DataDir() . '/products/financial_document/complete.json'; $this->assertFileExists($jsonFile, 'Test resource file must exist'); $localResponse = new LocalResponse($jsonFile); diff --git a/tests/ClientV2TestFunctional.php b/tests/V2/ClientV2TestFunctional.php similarity index 81% rename from tests/ClientV2TestFunctional.php rename to tests/V2/ClientV2TestFunctional.php index e5cc8a91..411a27f2 100644 --- a/tests/ClientV2TestFunctional.php +++ b/tests/V2/ClientV2TestFunctional.php @@ -1,6 +1,7 @@ - modelId, rag: false, rawText: true); $response = $this->mindeeClient->enqueueAndGetInference($source, $inferenceParams); @@ -92,22 +93,30 @@ public function testParseFileFilledSinglePageMustSucceed(): void public function testInvalidModelMustThrowError(): void { - $source = new PathInput(__DIR__ . '/resources/file_types/pdf/multipage_cut-2.pdf'); + $source = new PathInput(\TestingUtilities::getFileTypesDir() . '/pdf/multipage_cut-2.pdf'); $inferenceParams = new InferenceParameters('INVALID MODEL ID'); - $this->expectException(MindeeV2HttpException::class); - $this->expectExceptionMessage('422'); - - $this->mindeeClient->enqueueInference($source, $inferenceParams); + try { + $this->mindeeClient->enqueueInference($source, $inferenceParams); + } catch (MindeeV2HttpException $e) { + $this->assertEquals(404, $e->getCode()); + $this->assertStringStartsWith('404-', $e->errorCode); + $this->assertNotEmpty($e->title); + $this->assertIsArray($e->errors); + } } public function testInvalidJobMustThrowError(): void { - $this->expectException(MindeeV2HttpException::class); - $this->expectExceptionMessage('422'); - - $this->mindeeClient->getInference('not-a-valid-job-ID'); + try { + $this->mindeeClient->getInference('not-a-valid-job-ID'); + } catch (MindeeV2HttpException $e) { + $this->assertEquals(422, $e->getCode()); + $this->assertStringStartsWith('422-', $e->errorCode); + $this->assertNotEmpty($e->title); + $this->assertIsArray($e->errors); + } } public function testUrlInputSourceMustNotRaiseErrors(): void diff --git a/tests/V2/InferenceTest.php b/tests/V2/Parsing/InferenceResponseTest.php similarity index 88% rename from tests/V2/InferenceTest.php rename to tests/V2/Parsing/InferenceResponseTest.php index bf04c37f..90d6ea81 100644 --- a/tests/V2/InferenceTest.php +++ b/tests/V2/Parsing/InferenceResponseTest.php @@ -1,23 +1,26 @@ assertFileExists($fullPath, "Resource file must exist: $path"); + $this->assertFileExists($path, "Resource file must exist: $path"); - return file_get_contents($fullPath); + return file_get_contents($path); } /** @@ -292,7 +294,7 @@ public function testRawTextsMustBeAccessible(): void $this->assertEquals('This is the raw text of the first page...', $first->content); foreach ($rawText->pages as $page) { - $this->assertEquals('string', gettype($page->content)); + $this->assertIsString($page->content); } } @@ -301,8 +303,10 @@ public function testRawTextsMustBeAccessible(): void */ public function testRstDisplayMustBeAccessible(): void { - $response = $this->loadFromResource('/v2/inference/standard_field_types.json'); - $expectedRst = $this->readFileAsString('/v2/inference/standard_field_types.rst'); + $response = $this->loadFromResource('v2/inference/standard_field_types.json'); + $expectedRst = $this->readFileAsString( + \TestingUtilities::getV2DataDir() . '/inference/standard_field_types.rst' + ); $inference = $response->inference; $this->assertNotNull($inference); $this->assertEquals($expectedRst, strval($response->inference)); @@ -358,4 +362,32 @@ public function testCoordinatesAndLocationDataMustBeAccessible(): void $this->assertTrue(FieldConfidence::Low->lt($dateField->confidence)); $this->assertEquals('Medium', $dateField->confidence->value); } + + public function testRagMetadataWhenMatched() + { + $response = $this->loadFromResource('v2/inference/rag_matched.json'); + $inference = $response->inference; + $this->assertNotNull($inference); + $this->assertEquals('12345abc-1234-1234-1234-123456789abc', $inference->result->rag->retrievedDocumentId); + } + + public function testRagMetadataWhenNotMatched() + { + $response = $this->loadFromResource('v2/inference/rag_not_matched.json'); + $inference = $response->inference; + $this->assertNotNull($inference); + $this->assertNull($inference->result->rag->retrievedDocumentId); + } + + public function testShouldLoadWith422Error() + { + $jsonResponse = json_decode(file_get_contents(\TestingUtilities::getV2DataDir() . '/job/fail_422.json'), true); + $response = new JobResponse($jsonResponse); + $this->assertNotNull($response->job); + $this->assertInstanceOf(ErrorResponse::class, $response->job->error); + $this->assertEquals(422, $response->job->error->status); + $this->assertStringStartsWith("422-", $response->job->error->code); + $this->assertEquals(1, count($response->job->error->errors)); + $this->assertInstanceOf(ErrorItem::class, $response->job->error->errors[0]); + } } diff --git a/tests/V2/Parsing/JobResponseTest.php b/tests/V2/Parsing/JobResponseTest.php new file mode 100644 index 00000000..b3d9bbc7 --- /dev/null +++ b/tests/V2/Parsing/JobResponseTest.php @@ -0,0 +1 @@ + Date: Fri, 14 Nov 2025 14:04:07 +0100 Subject: [PATCH 3/6] fix tests --- tests/V2/ClientV2TestFunctional.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/V2/ClientV2TestFunctional.php b/tests/V2/ClientV2TestFunctional.php index 411a27f2..1fd6019a 100644 --- a/tests/V2/ClientV2TestFunctional.php +++ b/tests/V2/ClientV2TestFunctional.php @@ -100,7 +100,6 @@ public function testInvalidModelMustThrowError(): void try { $this->mindeeClient->enqueueInference($source, $inferenceParams); } catch (MindeeV2HttpException $e) { - $this->assertEquals(404, $e->getCode()); $this->assertStringStartsWith('404-', $e->errorCode); $this->assertNotEmpty($e->title); $this->assertIsArray($e->errors); @@ -112,7 +111,6 @@ public function testInvalidJobMustThrowError(): void try { $this->mindeeClient->getInference('not-a-valid-job-ID'); } catch (MindeeV2HttpException $e) { - $this->assertEquals(422, $e->getCode()); $this->assertStringStartsWith('422-', $e->errorCode); $this->assertNotEmpty($e->title); $this->assertIsArray($e->errors); From b77dcfeb79d3f8a64ccc6c31a1d4fe31c341c998 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Fri, 14 Nov 2025 14:09:28 +0100 Subject: [PATCH 4/6] fix warning --- src/Input/LocalInputSource.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Input/LocalInputSource.php b/src/Input/LocalInputSource.php index a2dce14c..a1c26633 100644 --- a/src/Input/LocalInputSource.php +++ b/src/Input/LocalInputSource.php @@ -277,8 +277,8 @@ public function fixPDF(): void */ public function compress( int $quality = 85, - int $maxWidth = null, - int $maxHeight = null, + ?int $maxWidth = null, + ?int $maxHeight = null, bool $forceSourceTextCompression = false, bool $disableSourceText = true ): void { From 6bbadb1155caa94f68384a45757dbeba0ec2ae19 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:27:31 +0100 Subject: [PATCH 5/6] fix missing alias & webhook IDs --- src/Http/MindeeApiV2.php | 15 +++++++++ tests/V2/ClientV2TestFunctional.php | 48 ++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/Http/MindeeApiV2.php b/src/Http/MindeeApiV2.php index c2e60dbc..2304d3c8 100644 --- a/src/Http/MindeeApiV2.php +++ b/src/Http/MindeeApiV2.php @@ -12,6 +12,7 @@ // phpcs:disable include_once(dirname(__DIR__) . '/version.php'); + // phpcs:enable use Mindee\Error\MindeeV2HttpException; @@ -70,6 +71,7 @@ private function getUserAgent(): string } return 'mindee-api-php@v' . VERSION . ' php-v' . PHP_VERSION . ' ' . $os; } + /** * @var string|null API key. */ @@ -319,6 +321,19 @@ private function documentEnqueuePost( if (isset($params->rag)) { $postFields['rag'] = $params->rag ? 'true' : 'false'; } + if (isset($params->webhooksIds)) { + if (PHP_VERSION_ID < 80200 && count($params->webhooksIds) > 1) { + # NOTE: see https://bugs.php.net/bug.php?id=51634 + error_log("PHP version is too low to support webbook array destructuring. + \nOnly the first webhook ID will be sent to the server."); + $params->webhooksIds = $params->webhooksIds[0]; + } else { + $postFields['webhook_ids'] = $params->webhooksIds; + } + } + if (isset($params->alias)) { + $postFields['alias'] = $params->alias; + } $url = $this->baseUrl . '/inferences/enqueue'; curl_setopt($ch, CURLOPT_URL, $url); diff --git a/tests/V2/ClientV2TestFunctional.php b/tests/V2/ClientV2TestFunctional.php index 1fd6019a..bf6f91f3 100644 --- a/tests/V2/ClientV2TestFunctional.php +++ b/tests/V2/ClientV2TestFunctional.php @@ -60,7 +60,7 @@ public function testParseFileEmptyMultiPageMustSucceed(): void public function testParseFileFilledSinglePageMustSucceed(): void { $source = new PathInput( - TestingUtilities::getV1DataDir() . '/products/financial_document/default_sample.jpg' + \TestingUtilities::getV1DataDir() . '/products/financial_document/default_sample.jpg' ); $inferenceParams = new InferenceParameters($this->modelId, rag: false); @@ -91,12 +91,28 @@ public function testParseFileFilledSinglePageMustSucceed(): void ); } - public function testInvalidModelMustThrowError(): void + public function testInvalidUUIDMustThrowError(): void { - $source = new PathInput(\TestingUtilities::getFileTypesDir() . '/pdf/multipage_cut-2.pdf'); + + $source = new PathInput(\TestingUtilities::getFileTypesDir() . '/pdf/blank_1.pdf'); $inferenceParams = new InferenceParameters('INVALID MODEL ID'); + try { + $this->mindeeClient->enqueueInference($source, $inferenceParams); + } catch (MindeeV2HttpException $e) { + $this->assertStringStartsWith('422-', $e->errorCode); + $this->assertNotEmpty($e->title); + $this->assertIsArray($e->errors); + } + } + + public function testUnknownModelMustThrowError(): void + { + $source = new PathInput(\TestingUtilities::getFileTypesDir() . '/pdf/multipage_cut-2.pdf'); + + $inferenceParams = new InferenceParameters('fc405e37-4ba4-4d03-aeba-533a8d1f0f21'); + try { $this->mindeeClient->enqueueInference($source, $inferenceParams); } catch (MindeeV2HttpException $e) { @@ -106,10 +122,34 @@ public function testInvalidModelMustThrowError(): void } } + public function testInvalidJobMustThrowError(): void { try { - $this->mindeeClient->getInference('not-a-valid-job-ID'); + $this->mindeeClient->getInference('fc405e37-4ba4-4d03-aeba-533a8d1f0f21'); + } catch (MindeeV2HttpException $e) { + $this->assertStringStartsWith('404-', $e->errorCode); + $this->assertNotEmpty($e->title); + $this->assertIsArray($e->errors); + } + } + + public function testInvalidWebhookIDsMustThrowError() + { + $source = new PathInput(\TestingUtilities::getFileTypesDir() . '/pdf/multipage_cut-2.pdf'); + + $inferenceParams = new InferenceParameters( + $this->modelId, + null, + null, + null, + null, + null, + ['fc405e37-4ba4-4d03-aeba-533a8d1f0f21', 'fc405e37-4ba4-4d03-aeba-533a8d1f0f21'] + ); + + try { + $this->mindeeClient->enqueueInference($source, $inferenceParams); } catch (MindeeV2HttpException $e) { $this->assertStringStartsWith('422-', $e->errorCode); $this->assertNotEmpty($e->title); From 62eee5a72f2136df099c7730e55116c9876a350c Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:34:14 +0100 Subject: [PATCH 6/6] fix typo --- src/Http/MindeeApiV2.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Http/MindeeApiV2.php b/src/Http/MindeeApiV2.php index 2304d3c8..cce82496 100644 --- a/src/Http/MindeeApiV2.php +++ b/src/Http/MindeeApiV2.php @@ -321,12 +321,12 @@ private function documentEnqueuePost( if (isset($params->rag)) { $postFields['rag'] = $params->rag ? 'true' : 'false'; } - if (isset($params->webhooksIds)) { + if (isset($params->webhooksIds) && count($params->webhooksIds) > 0) { if (PHP_VERSION_ID < 80200 && count($params->webhooksIds) > 1) { # NOTE: see https://bugs.php.net/bug.php?id=51634 error_log("PHP version is too low to support webbook array destructuring. \nOnly the first webhook ID will be sent to the server."); - $params->webhooksIds = $params->webhooksIds[0]; + $postFields['webhook_ids'] = $params->webhooksIds[0]; } else { $postFields['webhook_ids'] = $params->webhooksIds; }