From f1a1cf74bc0486efc528811017f22ee70809c9a8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 15 Sep 2023 12:07:25 +0200 Subject: [PATCH] Validate and return csv --- .../rowProcessor/bbReferenceProcessor.ts | 33 ++++++++------ .../src/utilities/rowProcessor/errors.ts | 7 +++ .../tests/bbReferenceProcessor.spec.ts | 45 +++++++++++++++---- 3 files changed, 64 insertions(+), 21 deletions(-) create mode 100644 packages/server/src/utilities/rowProcessor/errors.ts diff --git a/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts b/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts index 2d296cea3f..7e78eb9693 100644 --- a/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts +++ b/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts @@ -1,27 +1,34 @@ import { cache } from "@budibase/backend-core" import { utils } from "@budibase/shared-core" -import { FieldSubtype } from "@budibase/types" +import { Document, FieldSubtype } from "@budibase/types" +import { InvalidBBRefError } from "./errors" export async function processInputBBReferences( - value: string, + value: string | string[] | { _id: string } | { _id: string }[], subtype: FieldSubtype -) { - const result = [] - const ids = value.split(",").map((id: string) => id.trim()) +): Promise { + const result: string[] = [] switch (subtype) { case FieldSubtype.USER: - for (const id of ids) { - result.push(await cache.user.getUser(id)) + if (Array.isArray(value)) { + result.push(...value.map(x => (typeof x === "string" ? x : x._id))) + } else if (typeof value !== "string") { + result.push(value._id) + } else { + result.push(...value.split(",").map((id: string) => id.trim())) + } + + for (const id of result) { + const user = await cache.user.getUser(id) + if (!user) { + throw new InvalidBBRefError(id, FieldSubtype.USER) + } } break default: - utils.unreachable(subtype) + throw utils.unreachable(subtype) } - if (result.length > 1) { - return result - } - - return result[0] + return result.join(",") } diff --git a/packages/server/src/utilities/rowProcessor/errors.ts b/packages/server/src/utilities/rowProcessor/errors.ts new file mode 100644 index 0000000000..279a528b5f --- /dev/null +++ b/packages/server/src/utilities/rowProcessor/errors.ts @@ -0,0 +1,7 @@ +import { FieldSubtype } from "@budibase/types" + +export class InvalidBBRefError extends Error { + constructor(id: string, subtype: FieldSubtype) { + super(`Id "${id}" is not valid for the subtype "${subtype}"`) + } +} diff --git a/packages/server/src/utilities/rowProcessor/tests/bbReferenceProcessor.spec.ts b/packages/server/src/utilities/rowProcessor/tests/bbReferenceProcessor.spec.ts index 95716b0467..7e4312b398 100644 --- a/packages/server/src/utilities/rowProcessor/tests/bbReferenceProcessor.spec.ts +++ b/packages/server/src/utilities/rowProcessor/tests/bbReferenceProcessor.spec.ts @@ -2,6 +2,7 @@ import * as backendCore from "@budibase/backend-core" import { FieldSubtype, User } from "@budibase/types" import { processInputBBReferences } from "../bbReferenceProcessor" import { generator, structures } from "@budibase/backend-core/tests" +import { InvalidBBRefError } from "../errors" jest.mock("@budibase/backend-core", (): typeof backendCore => { const actual = jest.requireActual("@budibase/backend-core") @@ -26,7 +27,7 @@ describe("bbReferenceProcessor", () => { describe("processInputBBReferences", () => { describe("subtype user", () => { - it("fetches by user id", async () => { + it("validate valid string id", async () => { const input = generator.guid() const userFromCache = structures.users.user() @@ -34,29 +35,57 @@ describe("bbReferenceProcessor", () => { const result = await processInputBBReferences(input, FieldSubtype.USER) - expect(result).toEqual(userFromCache) + expect(result).toEqual(input) expect(mockedCacheGetUser).toBeCalledTimes(1) expect(mockedCacheGetUser).toBeCalledWith(input) }) - it("fetches by user id when send as csv", async () => { - const users: Record = {} + it("throws an error given an invalid id", async () => { + const input = generator.guid() + + await expect( + processInputBBReferences(input, FieldSubtype.USER) + ).rejects.toThrowError(new InvalidBBRefError(input, FieldSubtype.USER)) + }) + + it("validates valid user ids as csv", async () => { + const userIds: string[] = [] for (let i = 0; i < 5; i++) { const userId = generator.guid() const user = structures.users.user({ _id: userId, userId }) mockedCacheGetUser.mockResolvedValueOnce(user) - users[userId] = user + userIds.push(userId) } - const input = Object.keys(users).join(" , ") + const input = userIds.join(" , ") const result = await processInputBBReferences(input, FieldSubtype.USER) - expect(result).toEqual(Object.values(users)) + expect(result).toEqual(userIds.join(",")) expect(mockedCacheGetUser).toBeCalledTimes(5) - Object.keys(users).forEach(userId => { + userIds.forEach(userId => { expect(mockedCacheGetUser).toBeCalledWith(userId) }) }) + + it("throws an error given an invalid id in a csv", async () => { + const userId1 = generator.guid() + const userId2 = generator.guid() + const userId3 = generator.guid() + mockedCacheGetUser.mockResolvedValueOnce( + structures.users.user({ _id: userId1 }) + ) + mockedCacheGetUser.mockResolvedValueOnce( + structures.users.user({ _id: userId2 }) + ) + + const input = [userId1, userId2, userId3].join(" , ") + + await expect( + processInputBBReferences(input, FieldSubtype.USER) + ).rejects.toThrowError( + new InvalidBBRefError(userId3, FieldSubtype.USER) + ) + }) }) }) })