From 7a012f1f4b2ab1fc46b482339ab20c3832430b59 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 5 Feb 2024 16:49:21 +0000 Subject: [PATCH] Add tests for create queries. --- .../server/src/api/controllers/query/index.ts | 5 +- .../api/routes/tests/queries/mongodb.spec.ts | 97 +++++++++++++++---- .../api/routes/tests/queries/postgres.spec.ts | 65 ++++++++++--- .../server/src/tests/utilities/api/query.ts | 12 ++- packages/types/src/documents/app/query.ts | 10 ++ 5 files changed, 154 insertions(+), 35 deletions(-) diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts index d38df00443..1be836b169 100644 --- a/packages/server/src/api/controllers/query/index.ts +++ b/packages/server/src/api/controllers/query/index.ts @@ -15,6 +15,9 @@ import { SessionCookie, QuerySchema, FieldType, + type ExecuteQueryRequest, + type ExecuteQueryResponse, + type Row, } from "@budibase/types" import { ValidQueryNameRegex } from "@budibase/shared-core" @@ -223,7 +226,7 @@ export async function preview(ctx: UserCtx) { } async function execute( - ctx: UserCtx, + ctx: UserCtx, opts: any = { rowsOnly: false, isAutomation: false } ) { const db = context.getAppDB() diff --git a/packages/server/src/api/routes/tests/queries/mongodb.spec.ts b/packages/server/src/api/routes/tests/queries/mongodb.spec.ts index 0c2ba67322..b736d19750 100644 --- a/packages/server/src/api/routes/tests/queries/mongodb.spec.ts +++ b/packages/server/src/api/routes/tests/queries/mongodb.spec.ts @@ -1,7 +1,7 @@ import { Datasource, Query } from "@budibase/types" import * as setup from "../utilities" import { databaseTestProviders } from "../../../../integrations/tests/utils" -import { MongoClient } from "mongodb" +import { MongoClient, type Collection } from "mongodb" jest.unmock("mongodb") @@ -23,6 +23,31 @@ describe("/queries", () => { return await config.api.query.create({ ...defaultQuery, ...query }) } + async function withClient( + callback: (client: MongoClient) => Promise + ): Promise { + const ds = await databaseTestProviders.mongodb.datasource() + const client = new MongoClient(ds.config!.connectionString) + await client.connect() + try { + await callback(client) + } finally { + await client.close() + } + } + + async function withCollection( + collection: string, + callback: (collection: Collection) => Promise + ): Promise { + await withClient(async client => { + const db = client.db( + (await databaseTestProviders.mongodb.datasource()).config!.db + ) + await callback(db.collection(collection)) + }) + } + afterAll(async () => { await databaseTestProviders.mongodb.stop() setup.afterAll() @@ -36,29 +61,21 @@ describe("/queries", () => { }) beforeEach(async () => { - const ds = await databaseTestProviders.mongodb.datasource() - const client = new MongoClient(ds.config!.connectionString) - await client.connect() - - const db = client.db(ds.config!.db) - const collection = db.collection("test_table") - await collection.insertMany([ - { name: "one" }, - { name: "two" }, - { name: "three" }, - { name: "four" }, - { name: "five" }, - ]) - await client.close() + await withCollection("test_table", async collection => { + await collection.insertMany([ + { name: "one" }, + { name: "two" }, + { name: "three" }, + { name: "four" }, + { name: "five" }, + ]) + }) }) afterEach(async () => { - const ds = await databaseTestProviders.mongodb.datasource() - const client = new MongoClient(ds.config!.connectionString) - await client.connect() - const db = client.db(ds.config!.db) - await db.collection("test_table").drop() - await client.close() + await withCollection("test_table", async collection => { + await collection.drop() + }) }) it("should execute a query", async () => { @@ -93,4 +110,42 @@ describe("/queries", () => { expect(result.data).toEqual([{ value: 6 }]) }) + + it("should execute a create query with parameters", async () => { + const query = await createQuery({ + fields: { + json: '{"foo": "{{ foo }}"}', + extra: { + actionType: "insertOne", + collection: "test_table", + }, + }, + queryVerb: "create", + parameters: [ + { + name: "foo", + default: "default", + }, + ], + }) + + const result = await config.api.query.execute(query._id!, { + parameters: { foo: "bar" }, + }) + + expect(result.data).toEqual([ + { + acknowledged: true, + insertedId: expect.anything(), + }, + ]) + + await withCollection("test_table", async collection => { + const doc = await collection.findOne({ foo: { $eq: "bar" } }) + expect(doc).toEqual({ + _id: expect.anything(), + foo: "bar", + }) + }) + }) }) diff --git a/packages/server/src/api/routes/tests/queries/postgres.spec.ts b/packages/server/src/api/routes/tests/queries/postgres.spec.ts index d1302b04f8..487644e787 100644 --- a/packages/server/src/api/routes/tests/queries/postgres.spec.ts +++ b/packages/server/src/api/routes/tests/queries/postgres.spec.ts @@ -42,6 +42,19 @@ describe("/queries", () => { return await config.api.query.create({ ...defaultQuery, ...query }) } + async function withClient( + callback: (client: Client) => Promise + ): Promise { + const ds = await databaseTestProviders.postgres.datasource() + const client = new Client(ds.config!) + await client.connect() + try { + await callback(client) + } finally { + await client.end() + } + } + afterAll(async () => { await databaseTestProviders.postgres.stop() setup.afterAll() @@ -55,20 +68,16 @@ describe("/queries", () => { }) beforeEach(async () => { - const ds = await databaseTestProviders.postgres.datasource() - const client = new Client(ds.config!) - await client.connect() - await client.query(createTableSQL) - await client.query(insertSQL) - await client.end() + await withClient(async client => { + await client.query(createTableSQL) + await client.query(insertSQL) + }) }) afterEach(async () => { - const ds = await databaseTestProviders.postgres.datasource() - const client = new Client(ds.config!) - await client.connect() - await client.query(dropTableSQL) - await client.end() + await withClient(async client => { + await client.query(dropTableSQL) + }) }) it("should execute a query", async () => { @@ -124,4 +133,38 @@ describe("/queries", () => { }, ]) }) + + it("should be able to insert with bindings", async () => { + const query = await createQuery({ + fields: { + sql: "INSERT INTO test_table (name) VALUES ({{ foo }})", + }, + parameters: [ + { + name: "foo", + default: "bar", + }, + ], + queryVerb: "create", + }) + + const result = await config.api.query.execute(query._id!, { + parameters: { + foo: "baz", + }, + }) + + expect(result.data).toEqual([ + { + created: true, + }, + ]) + + await withClient(async client => { + const { rows } = await client.query( + "SELECT * FROM test_table WHERE name = 'baz'" + ) + expect(rows).toHaveLength(1) + }) + }) }) diff --git a/packages/server/src/tests/utilities/api/query.ts b/packages/server/src/tests/utilities/api/query.ts index 98ea91c60f..350fe03c74 100644 --- a/packages/server/src/tests/utilities/api/query.ts +++ b/packages/server/src/tests/utilities/api/query.ts @@ -1,5 +1,9 @@ import TestConfiguration from "../TestConfiguration" -import { Query } from "@budibase/types" +import { + Query, + type ExecuteQueryRequest, + type ExecuteQueryResponse, +} from "@budibase/types" import { TestAPI } from "./base" export class QueryAPI extends TestAPI { @@ -21,10 +25,14 @@ export class QueryAPI extends TestAPI { return res.body as Query } - execute = async (queryId: string): Promise<{ data: any }> => { + execute = async ( + queryId: string, + body?: ExecuteQueryRequest + ): Promise => { const res = await this.request .post(`/api/v2/queries/${queryId}`) .set(this.config.defaultHeaders()) + .send(body) .expect("Content-Type", /json/) if (res.status !== 200) { diff --git a/packages/types/src/documents/app/query.ts b/packages/types/src/documents/app/query.ts index 473449bffb..790c297813 100644 --- a/packages/types/src/documents/app/query.ts +++ b/packages/types/src/documents/app/query.ts @@ -1,4 +1,5 @@ import { Document } from "../document" +import type { Row } from "./row" export interface QuerySchema { name?: string @@ -54,3 +55,12 @@ export interface PreviewQueryRequest extends Omit { urlName?: boolean } } + +export interface ExecuteQueryRequest { + parameters?: { [key: string]: string } + pagination?: any +} + +export interface ExecuteQueryResponse { + data: Row[] +}