diff --git a/.github/workflows/_test-integrations.yml b/.github/workflows/_test-integrations.yml index 6060941b7..81f28c250 100644 --- a/.github/workflows/_test-integrations.yml +++ b/.github/workflows/_test-integrations.yml @@ -47,4 +47,5 @@ jobs: 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 }} + MINDEE_V2_SE_TESTS_BLANK_PDF_URL: ${{ secrets.MINDEE_V2_SE_TESTS_BLANK_PDF_URL }} run: npm run test-integration diff --git a/package-lock.json b/package-lock.json index 0133b3414..4bfffbc04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -270,9 +270,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", - "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -983,17 +983,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1007,22 +1007,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -1038,14 +1038,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -1060,14 +1060,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1078,9 +1078,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -1095,15 +1095,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1120,9 +1120,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -1134,16 +1134,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1163,16 +1163,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1187,13 +1187,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3056,9 +3056,9 @@ } }, "node_modules/node-html-better-parser": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/node-html-better-parser/-/node-html-better-parser-1.5.1.tgz", - "integrity": "sha512-K3OUfP3UvIgoxlcoj6e9zeszeEk4MfhmiG7aiRRFEdoNqnfILCtL/AoLJ8UWFvDlRJOgKPRIECqWxbr25btnyQ==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/node-html-better-parser/-/node-html-better-parser-1.5.2.tgz", + "integrity": "sha512-ytjqwEgBQeNt//M19gukAzqvcSEn2EJPk+3svNs3f2lc+K50eZdokBSyAqlL9pMXK2Z3rMOLe18iZKrnEJKqtQ==", "license": "MIT", "dependencies": { "html-entities": "^2.3.2" diff --git a/src/clientV2.ts b/src/clientV2.ts index abfd4f638..4f24ce77e 100644 --- a/src/clientV2.ts +++ b/src/clientV2.ts @@ -1,6 +1,6 @@ import { - Base64Input, BufferInput, BytesInput, - LocalInputSource, PathInput, StreamInput, UrlInput, + Base64Input, BufferInput, BytesInput, InputSource, + PathInput, StreamInput, UrlInput, } from "./input"; import { errorHandler } from "./errors/handler"; import { LOG_LEVELS, logger } from "./logger"; @@ -124,7 +124,7 @@ export interface ClientOptions { * @category ClientV2 */ export class ClientV2 { - /** Key of the API. */ + /** Mindee API handler. */ protected mindeeApi: MindeeApiV2; /** @@ -148,13 +148,13 @@ export class ClientV2 { /** * Send the document to an asynchronous endpoint and return its ID in the queue. - * @param inputSource file to parse. + * @param inputSource file or URL to parse. * @param params parameters relating to prediction options. * @category Asynchronous * @returns a `Promise` containing the job (queue) corresponding to a document. */ async enqueueInference( - inputSource: LocalInputSource, + inputSource: InputSource, params: InferenceParameters ): Promise { if (inputSource === undefined) { @@ -240,7 +240,7 @@ export class ClientV2 { * @returns a `Promise` containing parsing results. */ async enqueueAndGetInference( - inputDoc: LocalInputSource, + inputDoc: InputSource, params: InferenceParameters ): Promise { const validatedAsyncParams = this.#setAsyncParams(params.pollingOptions); diff --git a/src/http/mindeeApiV2.ts b/src/http/mindeeApiV2.ts index 7aa76f433..cd13e2a49 100644 --- a/src/http/mindeeApiV2.ts +++ b/src/http/mindeeApiV2.ts @@ -4,7 +4,7 @@ import { InferenceResponse, JobResponse } from "../parsing/v2"; import FormData from "form-data"; import { RequestOptions } from "https"; import { BaseEndpoint, EndpointResponse } from "./baseEndpoint"; -import { LocalInputSource } from "../input"; +import { InputSource, LocalInputSource, UrlInput } from "../input"; import { MindeeApiV2Error, MindeeHttpErrorV2 } from "../errors/mindeeError"; import { logger } from "../logger"; @@ -17,18 +17,18 @@ export class MindeeApiV2 { /** * Sends a file to the inference queue. - * @param inputDoc Local file loaded as an input. + * @param inputSource Local file loaded as an input. * @param params {InferenceParameters} parameters relating to the enqueueing options. * @category V2 * @throws Error if the server's response contains one. * @returns a `Promise` containing a job response. */ - async reqPostInferenceEnqueue(inputDoc: LocalInputSource, params: InferenceParameters): Promise { - await inputDoc.init(); + async reqPostInferenceEnqueue(inputSource: InputSource, params: InferenceParameters): Promise { + await inputSource.init(); if (params.modelId === undefined || params.modelId === null || params.modelId === "") { throw new Error("Model ID must be provided"); } - const result: EndpointResponse = await this.#documentEnqueuePost(inputDoc, params); + const result: EndpointResponse = await this.#documentEnqueuePost(inputSource, params); if (result.data.error?.code !== undefined) { throw new MindeeHttpErrorV2( result.data.error.code, @@ -86,10 +86,10 @@ export class MindeeApiV2 { /** * Sends a document to the inference queue. * - * @param inputDoc Local file loaded as an input. + * @param inputSource Local or remote file as an input. * @param params {InferenceParameters} parameters relating to the enqueueing options. */ - #documentEnqueuePost(inputDoc: LocalInputSource, params: InferenceParameters): Promise { + #documentEnqueuePost(inputSource: InputSource, params: InferenceParameters): Promise { const form = new FormData(); form.append("model_id", params.modelId); @@ -99,9 +99,13 @@ export class MindeeApiV2 { if (params.webhookIds && params.webhookIds.length > 0) { form.append("webhook_ids", params.webhookIds.join(",")); } - form.append("file", inputDoc.fileObject, { - filename: inputDoc.filename, - }); + if (inputSource instanceof LocalInputSource) { + form.append("file", inputSource.fileObject, { + filename: inputSource.filename, + }); + } else { + form.append("url", (inputSource as UrlInput).url); + } const path = "/v2/inferences/enqueue"; const headers = { ...this.settings.baseHeaders, ...form.getHeaders() }; const options: RequestOptions = { diff --git a/src/input/sources/urlInput.ts b/src/input/sources/urlInput.ts index c431b82fa..c646eb193 100644 --- a/src/input/sources/urlInput.ts +++ b/src/input/sources/urlInput.ts @@ -8,7 +8,7 @@ import { IncomingMessage } from "http"; import { BytesInput } from "./bytesInput"; export class UrlInput extends InputSource { - private readonly url: string; + public readonly url: string; constructor({ url }: { url: string }) { super(); diff --git a/src/parsing/v2/field/baseField.ts b/src/parsing/v2/field/baseField.ts index cf1b55015..ec4d19e6e 100644 --- a/src/parsing/v2/field/baseField.ts +++ b/src/parsing/v2/field/baseField.ts @@ -1,19 +1,12 @@ import { FieldConfidence } from "./fieldConfidence"; -import { FieldLocation } from "./fieldLocation"; import { StringDict } from "../../common"; export abstract class BaseField { protected _indentLevel: number; - public locations: Array | undefined; public confidence: FieldConfidence | undefined; protected constructor(rawResponse: StringDict, indentLevel = 0) { this._indentLevel = indentLevel; - if (rawResponse["locations"]) { - this.locations = rawResponse["locations"].map((location: StringDict | undefined) => { - return location ? new FieldLocation(location) : ""; - }); - } if (rawResponse["confidence"] !== undefined) { this.confidence = rawResponse["confidence"] as FieldConfidence; } diff --git a/src/parsing/v2/field/dynamicField.ts b/src/parsing/v2/field/dynamicField.ts new file mode 100644 index 000000000..ce74119df --- /dev/null +++ b/src/parsing/v2/field/dynamicField.ts @@ -0,0 +1,17 @@ +import { BaseField } from "./baseField"; +import { FieldLocation } from "./fieldLocation"; +import { StringDict } from "../../common"; + + +export class DynamicField extends BaseField { + public locations: Array | undefined; + + constructor(rawResponse: StringDict, indentLevel = 0) { + super(rawResponse, indentLevel); + if (rawResponse["locations"]) { + this.locations = rawResponse["locations"].map((location: StringDict | undefined) => { + return location ? new FieldLocation(location) : ""; + }); + } + } +} diff --git a/src/parsing/v2/field/objectField.ts b/src/parsing/v2/field/objectField.ts index f6779cfb6..c82d7840b 100644 --- a/src/parsing/v2/field/objectField.ts +++ b/src/parsing/v2/field/objectField.ts @@ -1,8 +1,8 @@ -import { BaseField } from "./baseField"; import { InferenceFields } from "./inferenceFields"; import { StringDict } from "../../common"; +import { DynamicField } from "./dynamicField"; -export class ObjectField extends BaseField { +export class ObjectField extends DynamicField { readonly fields: InferenceFields; constructor(serverResponse: StringDict, indentLevel = 0) { diff --git a/src/parsing/v2/field/simpleField.ts b/src/parsing/v2/field/simpleField.ts index 597c46763..7ba4079c8 100644 --- a/src/parsing/v2/field/simpleField.ts +++ b/src/parsing/v2/field/simpleField.ts @@ -1,7 +1,7 @@ -import { BaseField } from "./baseField"; import { StringDict } from "../../common"; +import { DynamicField } from "./dynamicField"; -export class SimpleField extends BaseField { +export class SimpleField extends DynamicField { readonly value: string | number | boolean | null; constructor(serverResponse: StringDict, indentLevel = 0) { diff --git a/tests/v2/clientV2.integration.ts b/tests/v2/clientV2.integration.ts index 906013ae5..1eef80c82 100644 --- a/tests/v2/clientV2.integration.ts +++ b/tests/v2/clientV2.integration.ts @@ -2,7 +2,7 @@ import { expect } from "chai"; import path from "node:path"; import { ClientV2, InferenceParameters } from "../../src"; -import { PathInput } from "../../src/input"; +import { PathInput, UrlInput } from "../../src/input"; import { SimpleField } from "../../src/parsing/v2/field"; import { MindeeHttpErrorV2 } from "../../src/errors/mindeeError"; @@ -85,4 +85,16 @@ describe("MindeeClientV2 – integration tests (V2)", () => { expect((err as MindeeHttpErrorV2).status).to.equal(422); } }).timeout(60000); + + it("HTTPS URL – enqueue & parse must succeed", async () => { + const url = process.env.MINDEE_V2_SE_TESTS_BLANK_PDF_URL ?? "error-no-url-found"; + const source = new UrlInput({ url }); + const params: InferenceParameters = { modelId }; + + const response = await client.enqueueAndGetInference(source, params); + + expect(response).to.exist; + expect(response.inference).to.exist; + }).timeout(60000); + });