Merge pull request #12953 from Budibase/mongo-tests
Basic Postgres and Mongo query testcases.
This commit is contained in:
commit
df5279b96f
14 changed files with 729 additions and 88 deletions
|
@ -93,7 +93,13 @@
|
||||||
|
|
||||||
notifications.success("Query executed successfully")
|
notifications.success("Query executed successfully")
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error(`Query Error: ${error.message}`)
|
if (typeof error.message === "string") {
|
||||||
|
notifications.error(`Query Error: ${error.message}`)
|
||||||
|
} else if (typeof error.message?.code === "string") {
|
||||||
|
notifications.error(`Query Error: ${error.message.code}`)
|
||||||
|
} else {
|
||||||
|
notifications.error(`Query Error: ${JSON.stringify(error.message)}`)
|
||||||
|
}
|
||||||
|
|
||||||
if (!suppressErrors) {
|
if (!suppressErrors) {
|
||||||
throw error
|
throw error
|
||||||
|
|
|
@ -15,6 +15,9 @@ import {
|
||||||
SessionCookie,
|
SessionCookie,
|
||||||
QuerySchema,
|
QuerySchema,
|
||||||
FieldType,
|
FieldType,
|
||||||
|
type ExecuteQueryRequest,
|
||||||
|
type ExecuteQueryResponse,
|
||||||
|
type Row,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { ValidQueryNameRegex } from "@budibase/shared-core"
|
import { ValidQueryNameRegex } from "@budibase/shared-core"
|
||||||
|
|
||||||
|
@ -223,7 +226,7 @@ export async function preview(ctx: UserCtx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function execute(
|
async function execute(
|
||||||
ctx: UserCtx,
|
ctx: UserCtx<ExecuteQueryRequest, ExecuteQueryResponse | Row[]>,
|
||||||
opts: any = { rowsOnly: false, isAutomation: false }
|
opts: any = { rowsOnly: false, isAutomation: false }
|
||||||
) {
|
) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
|
|
390
packages/server/src/api/routes/tests/queries/mongodb.spec.ts
Normal file
390
packages/server/src/api/routes/tests/queries/mongodb.spec.ts
Normal file
|
@ -0,0 +1,390 @@
|
||||||
|
import { Datasource, Query } from "@budibase/types"
|
||||||
|
import * as setup from "../utilities"
|
||||||
|
import { databaseTestProviders } from "../../../../integrations/tests/utils"
|
||||||
|
import { MongoClient, type Collection } from "mongodb"
|
||||||
|
|
||||||
|
jest.unmock("mongodb")
|
||||||
|
|
||||||
|
const collection = "test_collection"
|
||||||
|
|
||||||
|
describe("/queries", () => {
|
||||||
|
let config = setup.getConfig()
|
||||||
|
let datasource: Datasource
|
||||||
|
|
||||||
|
async function createQuery(query: Partial<Query>): Promise<Query> {
|
||||||
|
const defaultQuery: Query = {
|
||||||
|
datasourceId: datasource._id!,
|
||||||
|
name: "New Query",
|
||||||
|
parameters: [],
|
||||||
|
fields: {},
|
||||||
|
schema: {},
|
||||||
|
queryVerb: "read",
|
||||||
|
transformer: "return data",
|
||||||
|
readable: true,
|
||||||
|
}
|
||||||
|
const combinedQuery = { ...defaultQuery, ...query }
|
||||||
|
if (
|
||||||
|
combinedQuery.fields &&
|
||||||
|
combinedQuery.fields.extra &&
|
||||||
|
!combinedQuery.fields.extra.collection
|
||||||
|
) {
|
||||||
|
combinedQuery.fields.extra.collection = collection
|
||||||
|
}
|
||||||
|
return await config.api.query.create(combinedQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function withClient(
|
||||||
|
callback: (client: MongoClient) => Promise<void>
|
||||||
|
): Promise<void> {
|
||||||
|
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(
|
||||||
|
callback: (collection: Collection) => Promise<void>
|
||||||
|
): Promise<void> {
|
||||||
|
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()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await config.init()
|
||||||
|
datasource = await config.api.datasource.create(
|
||||||
|
await databaseTestProviders.mongodb.datasource()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await withCollection(async collection => {
|
||||||
|
await collection.insertMany([
|
||||||
|
{ name: "one" },
|
||||||
|
{ name: "two" },
|
||||||
|
{ name: "three" },
|
||||||
|
{ name: "four" },
|
||||||
|
{ name: "five" },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await withCollection(async collection => {
|
||||||
|
await collection.drop()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should execute a count query", async () => {
|
||||||
|
const query = await createQuery({
|
||||||
|
fields: {
|
||||||
|
json: {},
|
||||||
|
extra: {
|
||||||
|
actionType: "count",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await config.api.query.execute(query._id!)
|
||||||
|
|
||||||
|
expect(result.data).toEqual([{ value: 5 }])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should execute a count query with a transformer", async () => {
|
||||||
|
const query = await createQuery({
|
||||||
|
fields: {
|
||||||
|
json: {},
|
||||||
|
extra: {
|
||||||
|
actionType: "count",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transformer: "return data + 1",
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await config.api.query.execute(query._id!)
|
||||||
|
|
||||||
|
expect(result.data).toEqual([{ value: 6 }])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should execute a find query", async () => {
|
||||||
|
const query = await createQuery({
|
||||||
|
fields: {
|
||||||
|
json: {},
|
||||||
|
extra: {
|
||||||
|
actionType: "find",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await config.api.query.execute(query._id!)
|
||||||
|
|
||||||
|
expect(result.data).toEqual([
|
||||||
|
{ _id: expect.anything(), name: "one" },
|
||||||
|
{ _id: expect.anything(), name: "two" },
|
||||||
|
{ _id: expect.anything(), name: "three" },
|
||||||
|
{ _id: expect.anything(), name: "four" },
|
||||||
|
{ _id: expect.anything(), name: "five" },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should execute a findOne query", async () => {
|
||||||
|
const query = await createQuery({
|
||||||
|
fields: {
|
||||||
|
json: {},
|
||||||
|
extra: {
|
||||||
|
actionType: "findOne",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await config.api.query.execute(query._id!)
|
||||||
|
|
||||||
|
expect(result.data).toEqual([{ _id: expect.anything(), name: "one" }])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should execute a findOneAndUpdate query", async () => {
|
||||||
|
const query = await createQuery({
|
||||||
|
fields: {
|
||||||
|
json: {
|
||||||
|
filter: { name: { $eq: "one" } },
|
||||||
|
update: { $set: { name: "newName" } },
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
actionType: "findOneAndUpdate",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await config.api.query.execute(query._id!)
|
||||||
|
|
||||||
|
expect(result.data).toEqual([
|
||||||
|
{
|
||||||
|
lastErrorObject: { n: 1, updatedExisting: true },
|
||||||
|
ok: 1,
|
||||||
|
value: { _id: expect.anything(), name: "one" },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
await withCollection(async collection => {
|
||||||
|
expect(await collection.countDocuments()).toBe(5)
|
||||||
|
|
||||||
|
const doc = await collection.findOne({ name: { $eq: "newName" } })
|
||||||
|
expect(doc).toEqual({
|
||||||
|
_id: expect.anything(),
|
||||||
|
name: "newName",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should execute a distinct query", async () => {
|
||||||
|
const query = await createQuery({
|
||||||
|
fields: {
|
||||||
|
json: "name",
|
||||||
|
extra: {
|
||||||
|
actionType: "distinct",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await config.api.query.execute(query._id!)
|
||||||
|
const values = result.data.map(o => o.value).sort()
|
||||||
|
expect(values).toEqual(["five", "four", "one", "three", "two"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should execute a create query with parameters", async () => {
|
||||||
|
const query = await createQuery({
|
||||||
|
fields: {
|
||||||
|
json: { foo: "{{ foo }}" },
|
||||||
|
extra: {
|
||||||
|
actionType: "insertOne",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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(async collection => {
|
||||||
|
const doc = await collection.findOne({ foo: { $eq: "bar" } })
|
||||||
|
expect(doc).toEqual({
|
||||||
|
_id: expect.anything(),
|
||||||
|
foo: "bar",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should execute a delete query with parameters", async () => {
|
||||||
|
const query = await createQuery({
|
||||||
|
fields: {
|
||||||
|
json: { name: { $eq: "{{ name }}" } },
|
||||||
|
extra: {
|
||||||
|
actionType: "deleteOne",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
queryVerb: "delete",
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await config.api.query.execute(query._id!, {
|
||||||
|
parameters: { name: "one" },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.data).toEqual([
|
||||||
|
{
|
||||||
|
acknowledged: true,
|
||||||
|
deletedCount: 1,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
await withCollection(async collection => {
|
||||||
|
const doc = await collection.findOne({ name: { $eq: "one" } })
|
||||||
|
expect(doc).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should execute an update query with parameters", async () => {
|
||||||
|
const query = await createQuery({
|
||||||
|
fields: {
|
||||||
|
json: {
|
||||||
|
filter: { name: { $eq: "{{ name }}" } },
|
||||||
|
update: { $set: { name: "{{ newName }}" } },
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
actionType: "updateOne",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
queryVerb: "update",
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "newName",
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await config.api.query.execute(query._id!, {
|
||||||
|
parameters: { name: "one", newName: "newOne" },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.data).toEqual([
|
||||||
|
{
|
||||||
|
acknowledged: true,
|
||||||
|
matchedCount: 1,
|
||||||
|
modifiedCount: 1,
|
||||||
|
upsertedCount: 0,
|
||||||
|
upsertedId: null,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
await withCollection(async collection => {
|
||||||
|
const doc = await collection.findOne({ name: { $eq: "newOne" } })
|
||||||
|
expect(doc).toEqual({
|
||||||
|
_id: expect.anything(),
|
||||||
|
name: "newOne",
|
||||||
|
})
|
||||||
|
|
||||||
|
const oldDoc = await collection.findOne({ name: { $eq: "one" } })
|
||||||
|
expect(oldDoc).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to delete all records", async () => {
|
||||||
|
const query = await createQuery({
|
||||||
|
fields: {
|
||||||
|
json: {},
|
||||||
|
extra: {
|
||||||
|
actionType: "deleteMany",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
queryVerb: "delete",
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await config.api.query.execute(query._id!)
|
||||||
|
|
||||||
|
expect(result.data).toEqual([
|
||||||
|
{
|
||||||
|
acknowledged: true,
|
||||||
|
deletedCount: 5,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
await withCollection(async collection => {
|
||||||
|
const docs = await collection.find().toArray()
|
||||||
|
expect(docs).toHaveLength(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to update all documents", async () => {
|
||||||
|
const query = await createQuery({
|
||||||
|
fields: {
|
||||||
|
json: {
|
||||||
|
filter: {},
|
||||||
|
update: { $set: { name: "newName" } },
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
actionType: "updateMany",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
queryVerb: "update",
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await config.api.query.execute(query._id!)
|
||||||
|
|
||||||
|
expect(result.data).toEqual([
|
||||||
|
{
|
||||||
|
acknowledged: true,
|
||||||
|
matchedCount: 5,
|
||||||
|
modifiedCount: 5,
|
||||||
|
upsertedCount: 0,
|
||||||
|
upsertedId: null,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
await withCollection(async collection => {
|
||||||
|
const docs = await collection.find().toArray()
|
||||||
|
expect(docs).toHaveLength(5)
|
||||||
|
for (const doc of docs) {
|
||||||
|
expect(doc).toEqual({
|
||||||
|
_id: expect.anything(),
|
||||||
|
name: "newName",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
170
packages/server/src/api/routes/tests/queries/postgres.spec.ts
Normal file
170
packages/server/src/api/routes/tests/queries/postgres.spec.ts
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
import { Datasource, Query } from "@budibase/types"
|
||||||
|
import * as setup from "../utilities"
|
||||||
|
import { databaseTestProviders } from "../../../../integrations/tests/utils"
|
||||||
|
import { Client } from "pg"
|
||||||
|
|
||||||
|
jest.unmock("pg")
|
||||||
|
|
||||||
|
const createTableSQL = `
|
||||||
|
CREATE TABLE test_table (
|
||||||
|
id serial PRIMARY KEY,
|
||||||
|
name VARCHAR ( 50 ) NOT NULL
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
const insertSQL = `
|
||||||
|
INSERT INTO test_table (name) VALUES ('one');
|
||||||
|
INSERT INTO test_table (name) VALUES ('two');
|
||||||
|
INSERT INTO test_table (name) VALUES ('three');
|
||||||
|
INSERT INTO test_table (name) VALUES ('four');
|
||||||
|
INSERT INTO test_table (name) VALUES ('five');
|
||||||
|
`
|
||||||
|
|
||||||
|
const dropTableSQL = `
|
||||||
|
DROP TABLE test_table;
|
||||||
|
`
|
||||||
|
|
||||||
|
describe("/queries", () => {
|
||||||
|
let config = setup.getConfig()
|
||||||
|
let datasource: Datasource
|
||||||
|
|
||||||
|
async function createQuery(query: Partial<Query>): Promise<Query> {
|
||||||
|
const defaultQuery: Query = {
|
||||||
|
datasourceId: datasource._id!,
|
||||||
|
name: "New Query",
|
||||||
|
parameters: [],
|
||||||
|
fields: {},
|
||||||
|
schema: {},
|
||||||
|
queryVerb: "read",
|
||||||
|
transformer: "return data",
|
||||||
|
readable: true,
|
||||||
|
}
|
||||||
|
return await config.api.query.create({ ...defaultQuery, ...query })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function withClient(
|
||||||
|
callback: (client: Client) => Promise<void>
|
||||||
|
): Promise<void> {
|
||||||
|
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()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await config.init()
|
||||||
|
datasource = await config.api.datasource.create(
|
||||||
|
await databaseTestProviders.postgres.datasource()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await withClient(async client => {
|
||||||
|
await client.query(createTableSQL)
|
||||||
|
await client.query(insertSQL)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await withClient(async client => {
|
||||||
|
await client.query(dropTableSQL)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should execute a query", async () => {
|
||||||
|
const query = await createQuery({
|
||||||
|
fields: {
|
||||||
|
sql: "SELECT * FROM test_table ORDER BY id",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await config.api.query.execute(query._id!)
|
||||||
|
|
||||||
|
expect(result.data).toEqual([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "one",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "two",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "three",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "four",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: "five",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to transform a query", async () => {
|
||||||
|
const query = await createQuery({
|
||||||
|
fields: {
|
||||||
|
sql: "SELECT * FROM test_table WHERE id = 1",
|
||||||
|
},
|
||||||
|
transformer: `
|
||||||
|
data[0].id = data[0].id + 1;
|
||||||
|
return data;
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await config.api.query.execute(query._id!)
|
||||||
|
|
||||||
|
expect(result.data).toEqual([
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "one",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -16,9 +16,9 @@ jest.mock("@budibase/backend-core", () => {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
import * as setup from "./utilities"
|
import * as setup from "../utilities"
|
||||||
import { checkBuilderEndpoint } from "./utilities/TestFunctions"
|
import { checkBuilderEndpoint } from "../utilities/TestFunctions"
|
||||||
import { checkCacheForDynamicVariable } from "../../../threads/utils"
|
import { checkCacheForDynamicVariable } from "../../../../threads/utils"
|
||||||
|
|
||||||
const { basicQuery, basicDatasource } = setup.structures
|
const { basicQuery, basicDatasource } = setup.structures
|
||||||
import { events, db as dbCore } from "@budibase/backend-core"
|
import { events, db as dbCore } from "@budibase/backend-core"
|
|
@ -12,7 +12,6 @@ import {
|
||||||
FieldTypeSubtypes,
|
FieldTypeSubtypes,
|
||||||
FormulaType,
|
FormulaType,
|
||||||
INTERNAL_TABLE_SOURCE_ID,
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
MonthlyQuotaName,
|
|
||||||
PermissionLevel,
|
PermissionLevel,
|
||||||
QuotaUsageType,
|
QuotaUsageType,
|
||||||
RelationshipType,
|
RelationshipType,
|
||||||
|
@ -53,7 +52,7 @@ describe.each([
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
if (dsProvider) {
|
if (dsProvider) {
|
||||||
await dsProvider.stopContainer()
|
await dsProvider.stop()
|
||||||
}
|
}
|
||||||
setup.afterAll()
|
setup.afterAll()
|
||||||
})
|
})
|
||||||
|
@ -63,7 +62,7 @@ describe.each([
|
||||||
|
|
||||||
if (dsProvider) {
|
if (dsProvider) {
|
||||||
await config.createDatasource({
|
await config.createDatasource({
|
||||||
datasource: await dsProvider.getDsConfig(),
|
datasource: await dsProvider.datasource(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -117,16 +116,6 @@ describe.each([
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
const getQueryUsage = async () => {
|
|
||||||
const { total } = await config.doInContext(null, () =>
|
|
||||||
quotas.getCurrentUsageValues(
|
|
||||||
QuotaUsageType.MONTHLY,
|
|
||||||
MonthlyQuotaName.QUERIES
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return total
|
|
||||||
}
|
|
||||||
|
|
||||||
const assertRowUsage = async (expected: number) => {
|
const assertRowUsage = async (expected: number) => {
|
||||||
const usage = await getRowUsage()
|
const usage = await getRowUsage()
|
||||||
expect(usage).toBe(expected)
|
expect(usage).toBe(expected)
|
||||||
|
@ -162,7 +151,6 @@ describe.each([
|
||||||
describe("save, load, update", () => {
|
describe("save, load, update", () => {
|
||||||
it("returns a success message when the row is created", async () => {
|
it("returns a success message when the row is created", async () => {
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const res = await request
|
const res = await request
|
||||||
.post(`/api/${tableId}/rows`)
|
.post(`/api/${tableId}/rows`)
|
||||||
|
@ -180,7 +168,6 @@ describe.each([
|
||||||
|
|
||||||
it("Increment row autoId per create row request", async () => {
|
it("Increment row autoId per create row request", async () => {
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const tableConfig = generateTableConfig()
|
const tableConfig = generateTableConfig()
|
||||||
const newTable = await createTable(
|
const newTable = await createTable(
|
||||||
|
@ -231,7 +218,6 @@ describe.each([
|
||||||
it("updates a row successfully", async () => {
|
it("updates a row successfully", async () => {
|
||||||
const existing = await config.createRow()
|
const existing = await config.createRow()
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const res = await config.api.row.save(tableId, {
|
const res = await config.api.row.save(tableId, {
|
||||||
_id: existing._id,
|
_id: existing._id,
|
||||||
|
@ -246,7 +232,6 @@ describe.each([
|
||||||
|
|
||||||
it("should load a row", async () => {
|
it("should load a row", async () => {
|
||||||
const existing = await config.createRow()
|
const existing = await config.createRow()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const res = await config.api.row.get(tableId, existing._id!)
|
const res = await config.api.row.get(tableId, existing._id!)
|
||||||
|
|
||||||
|
@ -268,7 +253,6 @@ describe.each([
|
||||||
}
|
}
|
||||||
const firstRow = await config.createRow({ tableId })
|
const firstRow = await config.createRow({ tableId })
|
||||||
await config.createRow(newRow)
|
await config.createRow(newRow)
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const res = await config.api.row.fetch(tableId)
|
const res = await config.api.row.fetch(tableId)
|
||||||
|
|
||||||
|
@ -279,7 +263,6 @@ describe.each([
|
||||||
|
|
||||||
it("load should return 404 when row does not exist", async () => {
|
it("load should return 404 when row does not exist", async () => {
|
||||||
await config.createRow()
|
await config.createRow()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
await config.api.row.get(tableId, "1234567", {
|
await config.api.row.get(tableId, "1234567", {
|
||||||
expectStatus: 404,
|
expectStatus: 404,
|
||||||
|
@ -530,7 +513,6 @@ describe.each([
|
||||||
const existing = await config.createRow()
|
const existing = await config.createRow()
|
||||||
|
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const row = await config.api.row.patch(table._id!, {
|
const row = await config.api.row.patch(table._id!, {
|
||||||
_id: existing._id!,
|
_id: existing._id!,
|
||||||
|
@ -552,7 +534,6 @@ describe.each([
|
||||||
it("should throw an error when given improper types", async () => {
|
it("should throw an error when given improper types", async () => {
|
||||||
const existing = await config.createRow()
|
const existing = await config.createRow()
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
await config.api.row.patch(
|
await config.api.row.patch(
|
||||||
table._id!,
|
table._id!,
|
||||||
|
@ -650,7 +631,6 @@ describe.each([
|
||||||
it("should be able to delete a row", async () => {
|
it("should be able to delete a row", async () => {
|
||||||
const createdRow = await config.createRow()
|
const createdRow = await config.createRow()
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const res = await config.api.row.delete(table._id!, [createdRow])
|
const res = await config.api.row.delete(table._id!, [createdRow])
|
||||||
expect(res.body[0]._id).toEqual(createdRow._id)
|
expect(res.body[0]._id).toEqual(createdRow._id)
|
||||||
|
@ -666,7 +646,6 @@ describe.each([
|
||||||
|
|
||||||
it("should return no errors on valid row", async () => {
|
it("should return no errors on valid row", async () => {
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const res = await config.api.row.validate(table._id!, { name: "ivan" })
|
const res = await config.api.row.validate(table._id!, { name: "ivan" })
|
||||||
|
|
||||||
|
@ -677,7 +656,6 @@ describe.each([
|
||||||
|
|
||||||
it("should errors on invalid row", async () => {
|
it("should errors on invalid row", async () => {
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const res = await config.api.row.validate(table._id!, { name: 1 })
|
const res = await config.api.row.validate(table._id!, { name: 1 })
|
||||||
|
|
||||||
|
@ -703,7 +681,6 @@ describe.each([
|
||||||
const row1 = await config.createRow()
|
const row1 = await config.createRow()
|
||||||
const row2 = await config.createRow()
|
const row2 = await config.createRow()
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const res = await config.api.row.delete(table._id!, [row1, row2])
|
const res = await config.api.row.delete(table._id!, [row1, row2])
|
||||||
|
|
||||||
|
@ -719,7 +696,6 @@ describe.each([
|
||||||
config.createRow(),
|
config.createRow(),
|
||||||
])
|
])
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const res = await config.api.row.delete(table._id!, [
|
const res = await config.api.row.delete(table._id!, [
|
||||||
row1,
|
row1,
|
||||||
|
@ -735,7 +711,6 @@ describe.each([
|
||||||
it("should accept a valid row object and delete the row", async () => {
|
it("should accept a valid row object and delete the row", async () => {
|
||||||
const row1 = await config.createRow()
|
const row1 = await config.createRow()
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const res = await config.api.row.delete(table._id!, row1)
|
const res = await config.api.row.delete(table._id!, row1)
|
||||||
|
|
||||||
|
@ -746,7 +721,6 @@ describe.each([
|
||||||
|
|
||||||
it("Should ignore malformed/invalid delete requests", async () => {
|
it("Should ignore malformed/invalid delete requests", async () => {
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const res = await config.api.row.delete(
|
const res = await config.api.row.delete(
|
||||||
table._id!,
|
table._id!,
|
||||||
|
@ -782,7 +756,6 @@ describe.each([
|
||||||
it("should be able to fetch tables contents via 'view'", async () => {
|
it("should be able to fetch tables contents via 'view'", async () => {
|
||||||
const row = await config.createRow()
|
const row = await config.createRow()
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const res = await config.api.legacyView.get(table._id!)
|
const res = await config.api.legacyView.get(table._id!)
|
||||||
expect(res.body.length).toEqual(1)
|
expect(res.body.length).toEqual(1)
|
||||||
|
@ -792,7 +765,6 @@ describe.each([
|
||||||
|
|
||||||
it("should throw an error if view doesn't exist", async () => {
|
it("should throw an error if view doesn't exist", async () => {
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
await config.api.legacyView.get("derp", { expectStatus: 404 })
|
await config.api.legacyView.get("derp", { expectStatus: 404 })
|
||||||
|
|
||||||
|
@ -808,7 +780,6 @@ describe.each([
|
||||||
})
|
})
|
||||||
const row = await config.createRow()
|
const row = await config.createRow()
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
const res = await config.api.legacyView.get(view.name)
|
const res = await config.api.legacyView.get(view.name)
|
||||||
expect(res.body.length).toEqual(1)
|
expect(res.body.length).toEqual(1)
|
||||||
|
@ -864,7 +835,6 @@ describe.each([
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
// test basic enrichment
|
// test basic enrichment
|
||||||
const resBasic = await config.api.row.get(
|
const resBasic = await config.api.row.get(
|
||||||
|
@ -1100,7 +1070,6 @@ describe.each([
|
||||||
|
|
||||||
const createdRow = await config.createRow()
|
const createdRow = await config.createRow()
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
await config.api.row.delete(view.id, [createdRow])
|
await config.api.row.delete(view.id, [createdRow])
|
||||||
|
|
||||||
|
@ -1127,7 +1096,6 @@ describe.each([
|
||||||
config.createRow(),
|
config.createRow(),
|
||||||
])
|
])
|
||||||
const rowUsage = await getRowUsage()
|
const rowUsage = await getRowUsage()
|
||||||
const queryUsage = await getQueryUsage()
|
|
||||||
|
|
||||||
await config.api.row.delete(view.id, [rows[0], rows[2]])
|
await config.api.row.delete(view.id, [rows[0], rows[2]])
|
||||||
|
|
||||||
|
|
|
@ -41,12 +41,12 @@ describe("postgres integrations", () => {
|
||||||
makeRequest = generateMakeRequest(apiKey, true)
|
makeRequest = generateMakeRequest(apiKey, true)
|
||||||
|
|
||||||
postgresDatasource = await config.api.datasource.create(
|
postgresDatasource = await config.api.datasource.create(
|
||||||
await databaseTestProviders.postgres.getDsConfig()
|
await databaseTestProviders.postgres.datasource()
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
await databaseTestProviders.postgres.stopContainer()
|
await databaseTestProviders.postgres.stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
@ -1041,14 +1041,14 @@ describe("postgres integrations", () => {
|
||||||
describe("POST /api/datasources/verify", () => {
|
describe("POST /api/datasources/verify", () => {
|
||||||
it("should be able to verify the connection", async () => {
|
it("should be able to verify the connection", async () => {
|
||||||
const response = await config.api.datasource.verify({
|
const response = await config.api.datasource.verify({
|
||||||
datasource: await databaseTestProviders.postgres.getDsConfig(),
|
datasource: await databaseTestProviders.postgres.datasource(),
|
||||||
})
|
})
|
||||||
expect(response.status).toBe(200)
|
expect(response.status).toBe(200)
|
||||||
expect(response.body.connected).toBe(true)
|
expect(response.body.connected).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should state an invalid datasource cannot connect", async () => {
|
it("should state an invalid datasource cannot connect", async () => {
|
||||||
const dbConfig = await databaseTestProviders.postgres.getDsConfig()
|
const dbConfig = await databaseTestProviders.postgres.datasource()
|
||||||
const response = await config.api.datasource.verify({
|
const response = await config.api.datasource.verify({
|
||||||
datasource: {
|
datasource: {
|
||||||
...dbConfig,
|
...dbConfig,
|
||||||
|
@ -1082,7 +1082,7 @@ describe("postgres integrations", () => {
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
client = new Client(
|
client = new Client(
|
||||||
(await databaseTestProviders.postgres.getDsConfig()).config!
|
(await databaseTestProviders.postgres.datasource()).config!
|
||||||
)
|
)
|
||||||
await client.connect()
|
await client.connect()
|
||||||
})
|
})
|
||||||
|
@ -1125,7 +1125,7 @@ describe("postgres integrations", () => {
|
||||||
schema2 = "test-2"
|
schema2 = "test-2"
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const dsConfig = await databaseTestProviders.postgres.getDsConfig()
|
const dsConfig = await databaseTestProviders.postgres.datasource()
|
||||||
const dbConfig = dsConfig.config!
|
const dbConfig = dsConfig.config!
|
||||||
|
|
||||||
client = new Client(dbConfig)
|
client = new Client(dbConfig)
|
||||||
|
|
|
@ -202,8 +202,13 @@ class PostgresIntegration extends Sql implements DatasourcePlus {
|
||||||
await this.openConnection()
|
await this.openConnection()
|
||||||
response.connected = true
|
response.connected = true
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.log(e)
|
if (typeof e.message === "string" && e.message !== "") {
|
||||||
response.error = e.message as string
|
response.error = e.message as string
|
||||||
|
} else if (typeof e.code === "string" && e.code !== "") {
|
||||||
|
response.error = e.code
|
||||||
|
} else {
|
||||||
|
response.error = "Unknown error"
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await this.closeConnection()
|
await this.closeConnection()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
jest.unmock("pg")
|
jest.unmock("pg")
|
||||||
|
|
||||||
import { Datasource } from "@budibase/types"
|
import { Datasource } from "@budibase/types"
|
||||||
import * as pg from "./postgres"
|
import * as postgres from "./postgres"
|
||||||
|
import * as mongodb from "./mongodb"
|
||||||
|
import { StartedTestContainer } from "testcontainers"
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
export interface DatabasePlusTestProvider {
|
export interface DatabaseProvider {
|
||||||
getDsConfig(): Promise<Datasource>
|
start(): Promise<StartedTestContainer>
|
||||||
|
stop(): Promise<void>
|
||||||
|
datasource(): Promise<Datasource>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const databaseTestProviders = {
|
export const databaseTestProviders = { postgres, mongodb }
|
||||||
postgres: pg,
|
|
||||||
}
|
|
||||||
|
|
41
packages/server/src/integrations/tests/utils/mongodb.ts
Normal file
41
packages/server/src/integrations/tests/utils/mongodb.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { Datasource, SourceName } from "@budibase/types"
|
||||||
|
import { GenericContainer, Wait, StartedTestContainer } from "testcontainers"
|
||||||
|
|
||||||
|
let container: StartedTestContainer | undefined
|
||||||
|
|
||||||
|
export async function start(): Promise<StartedTestContainer> {
|
||||||
|
return await new GenericContainer("mongo:7.0-jammy")
|
||||||
|
.withExposedPorts(27017)
|
||||||
|
.withEnvironment({
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: "mongo",
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: "password",
|
||||||
|
})
|
||||||
|
.withWaitStrategy(
|
||||||
|
Wait.forSuccessfulCommand(`mongosh --eval "db.version()"`)
|
||||||
|
)
|
||||||
|
.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function datasource(): Promise<Datasource> {
|
||||||
|
if (!container) {
|
||||||
|
container = await start()
|
||||||
|
}
|
||||||
|
const host = container.getHost()
|
||||||
|
const port = container.getMappedPort(27017)
|
||||||
|
return {
|
||||||
|
type: "datasource",
|
||||||
|
source: SourceName.MONGODB,
|
||||||
|
plus: false,
|
||||||
|
config: {
|
||||||
|
connectionString: `mongodb://mongo:password@${host}:${port}`,
|
||||||
|
db: "mongo",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function stop() {
|
||||||
|
if (container) {
|
||||||
|
await container.stop()
|
||||||
|
container = undefined
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,45 +3,44 @@ import { GenericContainer, Wait, StartedTestContainer } from "testcontainers"
|
||||||
|
|
||||||
let container: StartedTestContainer | undefined
|
let container: StartedTestContainer | undefined
|
||||||
|
|
||||||
export async function getDsConfig(): Promise<Datasource> {
|
export async function start(): Promise<StartedTestContainer> {
|
||||||
try {
|
return await new GenericContainer("postgres:16.1-bullseye")
|
||||||
if (!container) {
|
.withExposedPorts(5432)
|
||||||
container = await new GenericContainer("postgres:16.1-bullseye")
|
.withEnvironment({ POSTGRES_PASSWORD: "password" })
|
||||||
.withExposedPorts(5432)
|
.withWaitStrategy(
|
||||||
.withEnvironment({ POSTGRES_PASSWORD: "password" })
|
Wait.forSuccessfulCommand(
|
||||||
.withWaitStrategy(
|
"pg_isready -h localhost -p 5432"
|
||||||
Wait.forLogMessage(
|
).withStartupTimeout(10000)
|
||||||
"database system is ready to accept connections",
|
)
|
||||||
2
|
.start()
|
||||||
)
|
}
|
||||||
)
|
|
||||||
.start()
|
|
||||||
}
|
|
||||||
const host = container.getHost()
|
|
||||||
const port = container.getMappedPort(5432)
|
|
||||||
|
|
||||||
return {
|
export async function datasource(): Promise<Datasource> {
|
||||||
type: "datasource_plus",
|
if (!container) {
|
||||||
source: SourceName.POSTGRES,
|
container = await start()
|
||||||
plus: true,
|
}
|
||||||
config: {
|
const host = container.getHost()
|
||||||
host,
|
const port = container.getMappedPort(5432)
|
||||||
port,
|
|
||||||
database: "postgres",
|
return {
|
||||||
user: "postgres",
|
type: "datasource_plus",
|
||||||
password: "password",
|
source: SourceName.POSTGRES,
|
||||||
schema: "public",
|
plus: true,
|
||||||
ssl: false,
|
config: {
|
||||||
rejectUnauthorized: false,
|
host,
|
||||||
ca: false,
|
port,
|
||||||
},
|
database: "postgres",
|
||||||
}
|
user: "postgres",
|
||||||
} catch (err) {
|
password: "password",
|
||||||
throw new Error("**UNABLE TO CREATE TO POSTGRES CONTAINER**")
|
schema: "public",
|
||||||
|
ssl: false,
|
||||||
|
rejectUnauthorized: false,
|
||||||
|
ca: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stopContainer() {
|
export async function stop() {
|
||||||
if (container) {
|
if (container) {
|
||||||
await container.stop()
|
await container.stop()
|
||||||
container = undefined
|
container = undefined
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { ApplicationAPI } from "./application"
|
||||||
import { BackupAPI } from "./backup"
|
import { BackupAPI } from "./backup"
|
||||||
import { AttachmentAPI } from "./attachment"
|
import { AttachmentAPI } from "./attachment"
|
||||||
import { UserAPI } from "./user"
|
import { UserAPI } from "./user"
|
||||||
|
import { QueryAPI } from "./query"
|
||||||
|
|
||||||
export default class API {
|
export default class API {
|
||||||
table: TableAPI
|
table: TableAPI
|
||||||
|
@ -23,6 +24,7 @@ export default class API {
|
||||||
backup: BackupAPI
|
backup: BackupAPI
|
||||||
attachment: AttachmentAPI
|
attachment: AttachmentAPI
|
||||||
user: UserAPI
|
user: UserAPI
|
||||||
|
query: QueryAPI
|
||||||
|
|
||||||
constructor(config: TestConfiguration) {
|
constructor(config: TestConfiguration) {
|
||||||
this.table = new TableAPI(config)
|
this.table = new TableAPI(config)
|
||||||
|
@ -36,5 +38,6 @@ export default class API {
|
||||||
this.backup = new BackupAPI(config)
|
this.backup = new BackupAPI(config)
|
||||||
this.attachment = new AttachmentAPI(config)
|
this.attachment = new AttachmentAPI(config)
|
||||||
this.user = new UserAPI(config)
|
this.user = new UserAPI(config)
|
||||||
|
this.query = new QueryAPI(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
44
packages/server/src/tests/utilities/api/query.ts
Normal file
44
packages/server/src/tests/utilities/api/query.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import TestConfiguration from "../TestConfiguration"
|
||||||
|
import {
|
||||||
|
Query,
|
||||||
|
type ExecuteQueryRequest,
|
||||||
|
type ExecuteQueryResponse,
|
||||||
|
} from "@budibase/types"
|
||||||
|
import { TestAPI } from "./base"
|
||||||
|
|
||||||
|
export class QueryAPI extends TestAPI {
|
||||||
|
constructor(config: TestConfiguration) {
|
||||||
|
super(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
create = async (body: Query): Promise<Query> => {
|
||||||
|
const res = await this.request
|
||||||
|
.post(`/api/queries`)
|
||||||
|
.set(this.config.defaultHeaders())
|
||||||
|
.send(body)
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw new Error(JSON.stringify(res.body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.body as Query
|
||||||
|
}
|
||||||
|
|
||||||
|
execute = async (
|
||||||
|
queryId: string,
|
||||||
|
body?: ExecuteQueryRequest
|
||||||
|
): Promise<ExecuteQueryResponse> => {
|
||||||
|
const res = await this.request
|
||||||
|
.post(`/api/v2/queries/${queryId}`)
|
||||||
|
.set(this.config.defaultHeaders())
|
||||||
|
.send(body)
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw new Error(JSON.stringify(res.body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.body
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import { Document } from "../document"
|
import { Document } from "../document"
|
||||||
|
import type { Row } from "./row"
|
||||||
|
|
||||||
export interface QuerySchema {
|
export interface QuerySchema {
|
||||||
name?: string
|
name?: string
|
||||||
|
@ -54,3 +55,12 @@ export interface PreviewQueryRequest extends Omit<Query, "parameters"> {
|
||||||
urlName?: boolean
|
urlName?: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ExecuteQueryRequest {
|
||||||
|
parameters?: { [key: string]: string }
|
||||||
|
pagination?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExecuteQueryResponse {
|
||||||
|
data: Row[]
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue