From bd71a54587190fcd127d022ffedc6c0e18e60ead Mon Sep 17 00:00:00 2001 From: Fanis Tharropoulos Date: Wed, 5 Feb 2025 16:38:07 +0200 Subject: [PATCH 1/2] fix(documents): throw on empty lists of documents --- src/Typesense/Documents.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Typesense/Documents.ts b/src/Typesense/Documents.ts index cd1b6460..6cad53f4 100644 --- a/src/Typesense/Documents.ts +++ b/src/Typesense/Documents.ts @@ -1,7 +1,7 @@ import type { ReadStream } from "fs"; import ApiCall from "./ApiCall"; import Configuration from "./Configuration"; -import { ImportError } from "./Errors"; +import { ImportError, RequestMalformed } from "./Errors"; import { SearchOnlyDocuments } from "./SearchOnlyDocuments"; // Todo: use generic to extract filter_by values @@ -388,6 +388,9 @@ export default class Documents ): Promise { let documentsInJSONLFormat; if (Array.isArray(documents)) { + if (documents.length === 0) { + throw new RequestMalformed("No documents provided"); + } try { documentsInJSONLFormat = documents .map((document) => JSON.stringify(document)) @@ -410,6 +413,9 @@ export default class Documents } } else { documentsInJSONLFormat = documents; + if (isEmptyString(documentsInJSONLFormat)) { + throw new RequestMalformed("No documents provided"); + } } const resultsInJSONLFormat = await this.apiCall.performRequest( @@ -514,3 +520,7 @@ export default class Documents }); } } + +function isEmptyString(str: string | null | undefined): boolean { + return str == null || str === "" || str.length === 0; +} From 3a55318a456a6c6097ab92b1b4f6aa6d4192da78 Mon Sep 17 00:00:00 2001 From: Fanis Tharropoulos Date: Thu, 6 Feb 2025 11:10:51 +0200 Subject: [PATCH 2/2] test(documents): add tests for empty document validation - add test for empty array input - add test for empty string input - add test for null input - add test for undefined input --- test/Typesense/Documents.spec.js | 59 ++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/test/Typesense/Documents.spec.js b/test/Typesense/Documents.spec.js index 6b9c0032..c09a324f 100644 --- a/test/Typesense/Documents.spec.js +++ b/test/Typesense/Documents.spec.js @@ -436,6 +436,65 @@ describe("Documents", function () { }); describe(".import", function () { + context("when an empty array of documents is passed", function () { + it("throws RequestMalformed error", function (done) { + documents + .import([]) + .then(() => { + done(new Error("Expected import to throw RequestMalformed error")); + }) + .catch((error) => { + expect(error.constructor.name).to.eq("RequestMalformed"); + expect(error.message).to.eq("No documents provided"); + done(); + }); + }); + }); + + context("when an empty string is passed", function () { + it("throws RequestMalformed error", function (done) { + documents + .import("") + .then(() => { + done(new Error("Expected import to throw RequestMalformed error")); + }) + .catch((error) => { + expect(error.constructor.name).to.eq("RequestMalformed"); + expect(error.message).to.eq("No documents provided"); + done(); + }); + }); + }); + + context("when null is passed as JSONL string", function () { + it("throws RequestMalformed error", function (done) { + documents + .import(null) + .then(() => { + done(new Error("Expected import to throw RequestMalformed error")); + }) + .catch((error) => { + expect(error.constructor.name).to.eq("RequestMalformed"); + expect(error.message).to.eq("No documents provided"); + done(); + }); + }); + }); + + context("when undefined is passed as JSONL string", function () { + it("throws RequestMalformed error", function (done) { + documents + .import(undefined) + .then(() => { + done(new Error("Expected import to throw RequestMalformed error")); + }) + .catch((error) => { + expect(error.constructor.name).to.eq("RequestMalformed"); + expect(error.message).to.eq("No documents provided"); + done(); + }); + }); + }); context("when a query paramater is passed", function () { it("passes the query parameter to the API", function (done) { mockAxios