Updating rest of test cases.
This commit is contained in:
parent
c7a0489b82
commit
66ade79ff9
|
@ -164,9 +164,12 @@ describe("/datasources", () => {
|
|||
})
|
||||
})
|
||||
|
||||
datasourceDescribe(
|
||||
{ name: "%s", exclude: [DatabaseName.MONGODB, DatabaseName.SQS] },
|
||||
({ config, dsProvider }) => {
|
||||
const descriptions = datasourceDescribe({
|
||||
exclude: [DatabaseName.MONGODB, DatabaseName.SQS],
|
||||
})
|
||||
|
||||
if (descriptions.length) {
|
||||
describe.each(descriptions)("$dbName", ({ config, dsProvider }) => {
|
||||
let datasource: Datasource
|
||||
let rawDatasource: Datasource
|
||||
let client: Knex
|
||||
|
@ -492,5 +495,5 @@ datasourceDescribe(
|
|||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -977,63 +977,69 @@ describe("/rowsActions", () => {
|
|||
})
|
||||
})
|
||||
|
||||
datasourceDescribe(
|
||||
{ name: "row actions (%s)", only: [DatabaseName.SQS, DatabaseName.POSTGRES] },
|
||||
({ config, dsProvider, isInternal }) => {
|
||||
let datasource: Datasource | undefined
|
||||
const descriptions = datasourceDescribe({
|
||||
exclude: [DatabaseName.SQS, DatabaseName.POSTGRES],
|
||||
})
|
||||
|
||||
beforeAll(async () => {
|
||||
const ds = await dsProvider()
|
||||
datasource = ds.datasource
|
||||
})
|
||||
if (descriptions.length) {
|
||||
describe.each(descriptions)(
|
||||
"row actions ($dbName)",
|
||||
({ config, dsProvider, isInternal }) => {
|
||||
let datasource: Datasource | undefined
|
||||
|
||||
async function getTable(): Promise<Table> {
|
||||
if (isInternal) {
|
||||
await config.api.application.addSampleData(config.getAppId())
|
||||
const tables = await config.api.table.fetch()
|
||||
return tables.find(t => t.sourceId === DEFAULT_BB_DATASOURCE_ID)!
|
||||
} else {
|
||||
const table = await config.api.table.save(
|
||||
setup.structures.tableForDatasource(datasource!)
|
||||
)
|
||||
return table
|
||||
}
|
||||
}
|
||||
beforeAll(async () => {
|
||||
const ds = await dsProvider()
|
||||
datasource = ds.datasource
|
||||
})
|
||||
|
||||
it("should delete all the row actions (and automations) for its tables when a datasource is deleted", async () => {
|
||||
async function getRowActionsFromDb(tableId: string) {
|
||||
return await context.doInAppContext(config.getAppId(), async () => {
|
||||
const db = context.getAppDB()
|
||||
const tableDoc = await db.tryGet<TableRowActions>(
|
||||
generateRowActionsID(tableId)
|
||||
async function getTable(): Promise<Table> {
|
||||
if (isInternal) {
|
||||
await config.api.application.addSampleData(config.getAppId())
|
||||
const tables = await config.api.table.fetch()
|
||||
return tables.find(t => t.sourceId === DEFAULT_BB_DATASOURCE_ID)!
|
||||
} else {
|
||||
const table = await config.api.table.save(
|
||||
setup.structures.tableForDatasource(datasource!)
|
||||
)
|
||||
return tableDoc
|
||||
})
|
||||
return table
|
||||
}
|
||||
}
|
||||
|
||||
const table = await getTable()
|
||||
const tableId = table._id!
|
||||
it("should delete all the row actions (and automations) for its tables when a datasource is deleted", async () => {
|
||||
async function getRowActionsFromDb(tableId: string) {
|
||||
return await context.doInAppContext(config.getAppId(), async () => {
|
||||
const db = context.getAppDB()
|
||||
const tableDoc = await db.tryGet<TableRowActions>(
|
||||
generateRowActionsID(tableId)
|
||||
)
|
||||
return tableDoc
|
||||
})
|
||||
}
|
||||
|
||||
await config.api.rowAction.save(tableId, {
|
||||
name: generator.guid(),
|
||||
const table = await getTable()
|
||||
const tableId = table._id!
|
||||
|
||||
await config.api.rowAction.save(tableId, {
|
||||
name: generator.guid(),
|
||||
})
|
||||
await config.api.rowAction.save(tableId, {
|
||||
name: generator.guid(),
|
||||
})
|
||||
|
||||
const { actions } = (await getRowActionsFromDb(tableId))!
|
||||
expect(Object.entries(actions)).toHaveLength(2)
|
||||
|
||||
const { automations } = await config.api.automation.fetch()
|
||||
expect(automations).toHaveLength(2)
|
||||
|
||||
const datasource = await config.api.datasource.get(table.sourceId)
|
||||
await config.api.datasource.delete(datasource)
|
||||
|
||||
const automationsResp = await config.api.automation.fetch()
|
||||
expect(automationsResp.automations).toHaveLength(0)
|
||||
|
||||
expect(await getRowActionsFromDb(tableId)).toBeUndefined()
|
||||
})
|
||||
await config.api.rowAction.save(tableId, {
|
||||
name: generator.guid(),
|
||||
})
|
||||
|
||||
const { actions } = (await getRowActionsFromDb(tableId))!
|
||||
expect(Object.entries(actions)).toHaveLength(2)
|
||||
|
||||
const { automations } = await config.api.automation.fetch()
|
||||
expect(automations).toHaveLength(2)
|
||||
|
||||
const datasource = await config.api.datasource.get(table.sourceId)
|
||||
await config.api.datasource.delete(datasource)
|
||||
|
||||
const automationsResp = await config.api.automation.fetch()
|
||||
expect(automationsResp.automations).toHaveLength(0)
|
||||
|
||||
expect(await getRowActionsFromDb(tableId)).toBeUndefined()
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -7,71 +7,74 @@ import {
|
|||
import { Knex } from "knex"
|
||||
import { generator } from "@budibase/backend-core/tests"
|
||||
|
||||
datasourceDescribe(
|
||||
{
|
||||
name: "execute query action",
|
||||
exclude: [DatabaseName.MONGODB, DatabaseName.SQS],
|
||||
},
|
||||
({ config, dsProvider }) => {
|
||||
let tableName: string
|
||||
let client: Knex
|
||||
let datasource: Datasource
|
||||
let query: Query
|
||||
const descriptions = datasourceDescribe({
|
||||
exclude: [DatabaseName.MONGODB, DatabaseName.SQS],
|
||||
})
|
||||
|
||||
beforeAll(async () => {
|
||||
const ds = await dsProvider()
|
||||
datasource = ds.datasource!
|
||||
client = ds.client!
|
||||
})
|
||||
if (descriptions.length) {
|
||||
describe.each(descriptions)(
|
||||
"execute query action ($dbName)",
|
||||
({ config, dsProvider }) => {
|
||||
let tableName: string
|
||||
let client: Knex
|
||||
let datasource: Datasource
|
||||
let query: Query
|
||||
|
||||
beforeEach(async () => {
|
||||
tableName = generator.guid()
|
||||
await client.schema.createTable(tableName, table => {
|
||||
table.string("a")
|
||||
table.integer("b")
|
||||
beforeAll(async () => {
|
||||
const ds = await dsProvider()
|
||||
datasource = ds.datasource!
|
||||
client = ds.client!
|
||||
})
|
||||
await client(tableName).insert({ a: "string", b: 1 })
|
||||
query = await setup.saveTestQuery(config, client, tableName, datasource)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await client.schema.dropTable(tableName)
|
||||
})
|
||||
beforeEach(async () => {
|
||||
tableName = generator.guid()
|
||||
await client.schema.createTable(tableName, table => {
|
||||
table.string("a")
|
||||
table.integer("b")
|
||||
})
|
||||
await client(tableName).insert({ a: "string", b: 1 })
|
||||
query = await setup.saveTestQuery(config, client, tableName, datasource)
|
||||
})
|
||||
|
||||
it("should be able to execute a query", async () => {
|
||||
let res = await setup.runStep(
|
||||
config,
|
||||
setup.actions.EXECUTE_QUERY.stepId,
|
||||
{
|
||||
query: { queryId: query._id },
|
||||
}
|
||||
)
|
||||
expect(res.response).toEqual([{ a: "string", b: 1 }])
|
||||
expect(res.success).toEqual(true)
|
||||
})
|
||||
afterEach(async () => {
|
||||
await client.schema.dropTable(tableName)
|
||||
})
|
||||
|
||||
it("should handle a null query value", async () => {
|
||||
let res = await setup.runStep(
|
||||
config,
|
||||
setup.actions.EXECUTE_QUERY.stepId,
|
||||
{
|
||||
query: null,
|
||||
}
|
||||
)
|
||||
expect(res.response.message).toEqual("Invalid inputs")
|
||||
expect(res.success).toEqual(false)
|
||||
})
|
||||
it("should be able to execute a query", async () => {
|
||||
let res = await setup.runStep(
|
||||
config,
|
||||
setup.actions.EXECUTE_QUERY.stepId,
|
||||
{
|
||||
query: { queryId: query._id },
|
||||
}
|
||||
)
|
||||
expect(res.response).toEqual([{ a: "string", b: 1 }])
|
||||
expect(res.success).toEqual(true)
|
||||
})
|
||||
|
||||
it("should handle an error executing a query", async () => {
|
||||
let res = await setup.runStep(
|
||||
config,
|
||||
setup.actions.EXECUTE_QUERY.stepId,
|
||||
{
|
||||
query: { queryId: "wrong_id" },
|
||||
}
|
||||
)
|
||||
expect(res.response).toBeDefined()
|
||||
expect(res.success).toEqual(false)
|
||||
})
|
||||
}
|
||||
)
|
||||
it("should handle a null query value", async () => {
|
||||
let res = await setup.runStep(
|
||||
config,
|
||||
setup.actions.EXECUTE_QUERY.stepId,
|
||||
{
|
||||
query: null,
|
||||
}
|
||||
)
|
||||
expect(res.response.message).toEqual("Invalid inputs")
|
||||
expect(res.success).toEqual(false)
|
||||
})
|
||||
|
||||
it("should handle an error executing a query", async () => {
|
||||
let res = await setup.runStep(
|
||||
config,
|
||||
setup.actions.EXECUTE_QUERY.stepId,
|
||||
{
|
||||
query: { queryId: "wrong_id" },
|
||||
}
|
||||
)
|
||||
expect(res.response).toBeDefined()
|
||||
expect(res.success).toEqual(false)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -433,9 +433,10 @@ describe("Automation Scenarios", () => {
|
|||
})
|
||||
})
|
||||
|
||||
datasourceDescribe(
|
||||
{ name: "", only: [DatabaseName.MYSQL] },
|
||||
({ config, dsProvider }) => {
|
||||
const descriptions = datasourceDescribe({ only: [DatabaseName.MYSQL] })
|
||||
|
||||
if (descriptions.length) {
|
||||
describe.each(descriptions)("/rows ($dbName)", ({ config, dsProvider }) => {
|
||||
let datasource: Datasource
|
||||
let client: Knex
|
||||
|
||||
|
@ -531,5 +532,5 @@ datasourceDescribe(
|
|||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -10,119 +10,123 @@ function uniqueTableName(length?: number): string {
|
|||
.substring(0, length || 10)
|
||||
}
|
||||
|
||||
datasourceDescribe(
|
||||
{
|
||||
name: "Integration compatibility with mysql search_path",
|
||||
only: [DatabaseName.MYSQL],
|
||||
},
|
||||
({ config, dsProvider }) => {
|
||||
let rawDatasource: Datasource
|
||||
let datasource: Datasource
|
||||
let client: Knex
|
||||
const mainDescriptions = datasourceDescribe({ only: [DatabaseName.MYSQL] })
|
||||
|
||||
const database = generator.guid()
|
||||
const database2 = generator.guid()
|
||||
if (mainDescriptions.length) {
|
||||
describe.each(mainDescriptions)(
|
||||
"/Integration compatibility with mysql search_path ($dbName)",
|
||||
({ config, dsProvider }) => {
|
||||
let rawDatasource: Datasource
|
||||
let datasource: Datasource
|
||||
let client: Knex
|
||||
|
||||
beforeAll(async () => {
|
||||
const ds = await dsProvider()
|
||||
rawDatasource = ds.rawDatasource!
|
||||
datasource = ds.datasource!
|
||||
client = ds.client!
|
||||
const database = generator.guid()
|
||||
const database2 = generator.guid()
|
||||
|
||||
await client.raw(`CREATE DATABASE \`${database}\`;`)
|
||||
await client.raw(`CREATE DATABASE \`${database2}\`;`)
|
||||
beforeAll(async () => {
|
||||
const ds = await dsProvider()
|
||||
rawDatasource = ds.rawDatasource!
|
||||
datasource = ds.datasource!
|
||||
client = ds.client!
|
||||
|
||||
rawDatasource.config!.database = database
|
||||
datasource = await config.api.datasource.create(rawDatasource)
|
||||
})
|
||||
await client.raw(`CREATE DATABASE \`${database}\`;`)
|
||||
await client.raw(`CREATE DATABASE \`${database2}\`;`)
|
||||
|
||||
afterAll(async () => {
|
||||
await client.raw(`DROP DATABASE \`${database}\`;`)
|
||||
await client.raw(`DROP DATABASE \`${database2}\`;`)
|
||||
})
|
||||
|
||||
it("discovers tables from any schema in search path", async () => {
|
||||
await client.schema.createTable(`${database}.table1`, table => {
|
||||
table.increments("id1").primary()
|
||||
rawDatasource.config!.database = database
|
||||
datasource = await config.api.datasource.create(rawDatasource)
|
||||
})
|
||||
const res = await config.api.datasource.info(datasource)
|
||||
expect(res.tableNames).toBeDefined()
|
||||
expect(res.tableNames).toEqual(expect.arrayContaining(["table1"]))
|
||||
})
|
||||
|
||||
it("does not mix columns from different tables", async () => {
|
||||
const repeated_table_name = "table_same_name"
|
||||
await client.schema.createTable(
|
||||
`${database}.${repeated_table_name}`,
|
||||
table => {
|
||||
table.increments("id").primary()
|
||||
table.string("val1")
|
||||
}
|
||||
)
|
||||
await client.schema.createTable(
|
||||
`${database2}.${repeated_table_name}`,
|
||||
table => {
|
||||
table.increments("id2").primary()
|
||||
table.string("val2")
|
||||
}
|
||||
)
|
||||
|
||||
const res = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
tablesFilter: [repeated_table_name],
|
||||
afterAll(async () => {
|
||||
await client.raw(`DROP DATABASE \`${database}\`;`)
|
||||
await client.raw(`DROP DATABASE \`${database2}\`;`)
|
||||
})
|
||||
expect(res.datasource.entities![repeated_table_name].schema).toBeDefined()
|
||||
const schema = res.datasource.entities![repeated_table_name].schema
|
||||
expect(Object.keys(schema).sort()).toEqual(["id", "val1"])
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
datasourceDescribe(
|
||||
{
|
||||
name: "POST /api/datasources/:datasourceId/schema",
|
||||
only: [DatabaseName.MYSQL],
|
||||
},
|
||||
({ config, dsProvider }) => {
|
||||
let datasource: Datasource
|
||||
let client: Knex
|
||||
it("discovers tables from any schema in search path", async () => {
|
||||
await client.schema.createTable(`${database}.table1`, table => {
|
||||
table.increments("id1").primary()
|
||||
})
|
||||
const res = await config.api.datasource.info(datasource)
|
||||
expect(res.tableNames).toBeDefined()
|
||||
expect(res.tableNames).toEqual(expect.arrayContaining(["table1"]))
|
||||
})
|
||||
|
||||
beforeAll(async () => {
|
||||
const ds = await dsProvider()
|
||||
datasource = ds.datasource!
|
||||
client = ds.client!
|
||||
})
|
||||
|
||||
let tableName: string
|
||||
beforeEach(async () => {
|
||||
tableName = uniqueTableName()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await client.schema.dropTableIfExists(tableName)
|
||||
})
|
||||
|
||||
it("recognises enum columns as options", async () => {
|
||||
const enumColumnName = "status"
|
||||
|
||||
await client.schema.createTable(tableName, table => {
|
||||
table.increments("order_id").primary()
|
||||
table.string("customer_name", 100).notNullable()
|
||||
table.enum(
|
||||
enumColumnName,
|
||||
["pending", "processing", "shipped", "delivered", "cancelled"],
|
||||
{ useNative: true, enumName: `${tableName}_${enumColumnName}` }
|
||||
it("does not mix columns from different tables", async () => {
|
||||
const repeated_table_name = "table_same_name"
|
||||
await client.schema.createTable(
|
||||
`${database}.${repeated_table_name}`,
|
||||
table => {
|
||||
table.increments("id").primary()
|
||||
table.string("val1")
|
||||
}
|
||||
)
|
||||
await client.schema.createTable(
|
||||
`${database2}.${repeated_table_name}`,
|
||||
table => {
|
||||
table.increments("id2").primary()
|
||||
table.string("val2")
|
||||
}
|
||||
)
|
||||
|
||||
const res = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
tablesFilter: [repeated_table_name],
|
||||
})
|
||||
expect(
|
||||
res.datasource.entities![repeated_table_name].schema
|
||||
).toBeDefined()
|
||||
const schema = res.datasource.entities![repeated_table_name].schema
|
||||
expect(Object.keys(schema).sort()).toEqual(["id", "val1"])
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
const res = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
})
|
||||
const descriptions = datasourceDescribe({ only: [DatabaseName.MYSQL] })
|
||||
|
||||
const table = res.datasource.entities![tableName]
|
||||
if (descriptions.length) {
|
||||
describe.each(descriptions)(
|
||||
"POST /api/datasources/:datasourceId/schema ($dbName)",
|
||||
({ config, dsProvider }) => {
|
||||
let datasource: Datasource
|
||||
let client: Knex
|
||||
|
||||
expect(table).toBeDefined()
|
||||
expect(table.schema[enumColumnName].type).toEqual(FieldType.OPTIONS)
|
||||
})
|
||||
beforeAll(async () => {
|
||||
const ds = await dsProvider()
|
||||
datasource = ds.datasource!
|
||||
client = ds.client!
|
||||
})
|
||||
|
||||
let tableName: string
|
||||
beforeEach(async () => {
|
||||
tableName = uniqueTableName()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await client.schema.dropTableIfExists(tableName)
|
||||
})
|
||||
|
||||
it("recognises enum columns as options", async () => {
|
||||
const enumColumnName = "status"
|
||||
|
||||
await client.schema.createTable(tableName, table => {
|
||||
table.increments("order_id").primary()
|
||||
table.string("customer_name", 100).notNullable()
|
||||
table.enum(
|
||||
enumColumnName,
|
||||
["pending", "processing", "shipped", "delivered", "cancelled"],
|
||||
{ useNative: true, enumName: `${tableName}_${enumColumnName}` }
|
||||
)
|
||||
})
|
||||
|
||||
const res = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
})
|
||||
|
||||
const table = res.datasource.entities![tableName]
|
||||
|
||||
expect(table).toBeDefined()
|
||||
expect(table.schema[enumColumnName].type).toEqual(FieldType.OPTIONS)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,283 +8,292 @@ import {
|
|||
} from "../integrations/tests/utils"
|
||||
import { Knex } from "knex"
|
||||
|
||||
datasourceDescribe(
|
||||
{ name: "postgres integrations", only: [DatabaseName.POSTGRES] },
|
||||
({ config, dsProvider }) => {
|
||||
let datasource: Datasource
|
||||
let client: Knex
|
||||
const mainDescriptions = datasourceDescribe({ only: [DatabaseName.POSTGRES] })
|
||||
|
||||
beforeAll(async () => {
|
||||
const ds = await dsProvider()
|
||||
datasource = ds.datasource!
|
||||
client = ds.client!
|
||||
})
|
||||
if (mainDescriptions.length) {
|
||||
describe.each(mainDescriptions)(
|
||||
"/postgres integrations",
|
||||
({ config, dsProvider }) => {
|
||||
let datasource: Datasource
|
||||
let client: Knex
|
||||
|
||||
afterAll(config.end)
|
||||
|
||||
describe("POST /api/datasources/:datasourceId/schema", () => {
|
||||
let tableName: string
|
||||
|
||||
beforeEach(async () => {
|
||||
tableName = generator.guid().replaceAll("-", "").substring(0, 10)
|
||||
beforeAll(async () => {
|
||||
const ds = await dsProvider()
|
||||
datasource = ds.datasource!
|
||||
client = ds.client!
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await client.schema.dropTableIfExists(tableName)
|
||||
})
|
||||
afterAll(config.end)
|
||||
|
||||
it("recognises when a table has no primary key", async () => {
|
||||
await client.schema.createTable(tableName, table => {
|
||||
table.increments("id", { primaryKey: false })
|
||||
describe("POST /api/datasources/:datasourceId/schema", () => {
|
||||
let tableName: string
|
||||
|
||||
beforeEach(async () => {
|
||||
tableName = generator.guid().replaceAll("-", "").substring(0, 10)
|
||||
})
|
||||
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
afterEach(async () => {
|
||||
await client.schema.dropTableIfExists(tableName)
|
||||
})
|
||||
|
||||
expect(response.errors).toEqual({
|
||||
[tableName]: "Table must have a primary key.",
|
||||
})
|
||||
})
|
||||
it("recognises when a table has no primary key", async () => {
|
||||
await client.schema.createTable(tableName, table => {
|
||||
table.increments("id", { primaryKey: false })
|
||||
})
|
||||
|
||||
it("recognises when a table is using a reserved column name", async () => {
|
||||
await client.schema.createTable(tableName, table => {
|
||||
table.increments("_id").primary()
|
||||
})
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
})
|
||||
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
})
|
||||
|
||||
expect(response.errors).toEqual({
|
||||
[tableName]: "Table contains invalid columns.",
|
||||
})
|
||||
})
|
||||
|
||||
it("recognises enum columns as options", async () => {
|
||||
const tableName = `orders_${generator
|
||||
.guid()
|
||||
.replaceAll("-", "")
|
||||
.substring(0, 6)}`
|
||||
|
||||
await client.schema.createTable(tableName, table => {
|
||||
table.increments("order_id").primary()
|
||||
table.string("customer_name").notNullable()
|
||||
table.enum("status", ["pending", "processing", "shipped"], {
|
||||
useNative: true,
|
||||
enumName: `${tableName}_status`,
|
||||
expect(response.errors).toEqual({
|
||||
[tableName]: "Table must have a primary key.",
|
||||
})
|
||||
})
|
||||
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
it("recognises when a table is using a reserved column name", async () => {
|
||||
await client.schema.createTable(tableName, table => {
|
||||
table.increments("_id").primary()
|
||||
})
|
||||
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
})
|
||||
|
||||
expect(response.errors).toEqual({
|
||||
[tableName]: "Table contains invalid columns.",
|
||||
})
|
||||
})
|
||||
|
||||
const table = response.datasource.entities?.[tableName]
|
||||
it("recognises enum columns as options", async () => {
|
||||
const tableName = `orders_${generator
|
||||
.guid()
|
||||
.replaceAll("-", "")
|
||||
.substring(0, 6)}`
|
||||
|
||||
expect(table).toBeDefined()
|
||||
expect(table?.schema["status"].type).toEqual(FieldType.OPTIONS)
|
||||
})
|
||||
})
|
||||
await client.schema.createTable(tableName, table => {
|
||||
table.increments("order_id").primary()
|
||||
table.string("customer_name").notNullable()
|
||||
table.enum("status", ["pending", "processing", "shipped"], {
|
||||
useNative: true,
|
||||
enumName: `${tableName}_status`,
|
||||
})
|
||||
})
|
||||
|
||||
describe("check custom column types", () => {
|
||||
beforeAll(async () => {
|
||||
await client.schema.createTable("binaryTable", table => {
|
||||
table.binary("id").primary()
|
||||
table.string("column1")
|
||||
table.integer("column2")
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
})
|
||||
|
||||
const table = response.datasource.entities?.[tableName]
|
||||
|
||||
expect(table).toBeDefined()
|
||||
expect(table?.schema["status"].type).toEqual(FieldType.OPTIONS)
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle binary columns", async () => {
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
describe("check custom column types", () => {
|
||||
beforeAll(async () => {
|
||||
await client.schema.createTable("binaryTable", table => {
|
||||
table.binary("id").primary()
|
||||
table.string("column1")
|
||||
table.integer("column2")
|
||||
})
|
||||
})
|
||||
expect(response.datasource.entities).toBeDefined()
|
||||
const table = response.datasource.entities?.["binaryTable"]
|
||||
expect(table).toBeDefined()
|
||||
expect(table?.schema.id.externalType).toBe("bytea")
|
||||
const row = await config.api.row.save(table?._id!, {
|
||||
id: "1111",
|
||||
column1: "hello",
|
||||
column2: 222,
|
||||
})
|
||||
expect(row._id).toBeDefined()
|
||||
const decoded = decodeURIComponent(row._id!).replace(/'/g, '"')
|
||||
expect(JSON.parse(decoded)[0]).toBe("1111")
|
||||
})
|
||||
})
|
||||
|
||||
describe("check fetching null/not null table", () => {
|
||||
beforeAll(async () => {
|
||||
await client.schema.createTable("nullableTable", table => {
|
||||
table.increments("order_id").primary()
|
||||
table.integer("order_number").notNullable()
|
||||
it("should handle binary columns", async () => {
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
})
|
||||
expect(response.datasource.entities).toBeDefined()
|
||||
const table = response.datasource.entities?.["binaryTable"]
|
||||
expect(table).toBeDefined()
|
||||
expect(table?.schema.id.externalType).toBe("bytea")
|
||||
const row = await config.api.row.save(table?._id!, {
|
||||
id: "1111",
|
||||
column1: "hello",
|
||||
column2: 222,
|
||||
})
|
||||
expect(row._id).toBeDefined()
|
||||
const decoded = decodeURIComponent(row._id!).replace(/'/g, '"')
|
||||
expect(JSON.parse(decoded)[0]).toBe("1111")
|
||||
})
|
||||
})
|
||||
|
||||
it("should be able to change the table to allow nullable and refetch this", async () => {
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
})
|
||||
const entities = response.datasource.entities
|
||||
expect(entities).toBeDefined()
|
||||
const nullableTable = entities?.["nullableTable"]
|
||||
expect(nullableTable).toBeDefined()
|
||||
expect(
|
||||
nullableTable?.schema["order_number"].constraints?.presence
|
||||
).toEqual(true)
|
||||
|
||||
// need to perform these calls raw to the DB so that the external state of the DB differs to what Budibase
|
||||
// is aware of - therefore we can try to fetch and make sure BB updates correctly
|
||||
await client.schema.alterTable("nullableTable", table => {
|
||||
table.setNullable("order_number")
|
||||
describe("check fetching null/not null table", () => {
|
||||
beforeAll(async () => {
|
||||
await client.schema.createTable("nullableTable", table => {
|
||||
table.increments("order_id").primary()
|
||||
table.integer("order_number").notNullable()
|
||||
})
|
||||
})
|
||||
|
||||
const responseAfter = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
it("should be able to change the table to allow nullable and refetch this", async () => {
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
})
|
||||
const entities = response.datasource.entities
|
||||
expect(entities).toBeDefined()
|
||||
const nullableTable = entities?.["nullableTable"]
|
||||
expect(nullableTable).toBeDefined()
|
||||
expect(
|
||||
nullableTable?.schema["order_number"].constraints?.presence
|
||||
).toEqual(true)
|
||||
|
||||
// need to perform these calls raw to the DB so that the external state of the DB differs to what Budibase
|
||||
// is aware of - therefore we can try to fetch and make sure BB updates correctly
|
||||
await client.schema.alterTable("nullableTable", table => {
|
||||
table.setNullable("order_number")
|
||||
})
|
||||
|
||||
const responseAfter = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
})
|
||||
const entitiesAfter = responseAfter.datasource.entities
|
||||
expect(entitiesAfter).toBeDefined()
|
||||
const nullableTableAfter = entitiesAfter?.["nullableTable"]
|
||||
expect(nullableTableAfter).toBeDefined()
|
||||
expect(
|
||||
nullableTableAfter?.schema["order_number"].constraints?.presence
|
||||
).toBeUndefined()
|
||||
})
|
||||
const entitiesAfter = responseAfter.datasource.entities
|
||||
expect(entitiesAfter).toBeDefined()
|
||||
const nullableTableAfter = entitiesAfter?.["nullableTable"]
|
||||
expect(nullableTableAfter).toBeDefined()
|
||||
expect(
|
||||
nullableTableAfter?.schema["order_number"].constraints?.presence
|
||||
).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("money field 💰", () => {
|
||||
const tableName = "moneytable"
|
||||
let table: Table
|
||||
describe("money field 💰", () => {
|
||||
const tableName = "moneytable"
|
||||
let table: Table
|
||||
|
||||
beforeAll(async () => {
|
||||
await client.raw(`
|
||||
beforeAll(async () => {
|
||||
await client.raw(`
|
||||
CREATE TABLE ${tableName} (
|
||||
id serial PRIMARY KEY,
|
||||
price money
|
||||
)
|
||||
`)
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
})
|
||||
table = response.datasource.entities![tableName]
|
||||
})
|
||||
|
||||
it("should be able to import a money field", async () => {
|
||||
expect(table).toBeDefined()
|
||||
expect(table?.schema.price.type).toBe(FieldType.NUMBER)
|
||||
})
|
||||
|
||||
it("should be able to search a money field", async () => {
|
||||
await config.api.row.bulkImport(table._id!, {
|
||||
rows: [{ price: 200 }, { price: 300 }],
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
})
|
||||
table = response.datasource.entities![tableName]
|
||||
})
|
||||
|
||||
const { rows } = await config.api.row.search(table._id!, {
|
||||
query: {
|
||||
equal: {
|
||||
price: 200,
|
||||
it("should be able to import a money field", async () => {
|
||||
expect(table).toBeDefined()
|
||||
expect(table?.schema.price.type).toBe(FieldType.NUMBER)
|
||||
})
|
||||
|
||||
it("should be able to search a money field", async () => {
|
||||
await config.api.row.bulkImport(table._id!, {
|
||||
rows: [{ price: 200 }, { price: 300 }],
|
||||
})
|
||||
|
||||
const { rows } = await config.api.row.search(table._id!, {
|
||||
query: {
|
||||
equal: {
|
||||
price: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(rows).toHaveLength(1)
|
||||
expect(rows[0].price).toBe("200.00")
|
||||
})
|
||||
|
||||
it("should be able to update a money field", async () => {
|
||||
let row = await config.api.row.save(table._id!, { price: 200 })
|
||||
expect(row.price).toBe("200.00")
|
||||
|
||||
row = await config.api.row.save(table._id!, { ...row, price: 300 })
|
||||
expect(row.price).toBe("300.00")
|
||||
|
||||
row = await config.api.row.save(table._id!, {
|
||||
...row,
|
||||
price: "400.00",
|
||||
})
|
||||
expect(row.price).toBe("400.00")
|
||||
})
|
||||
expect(rows).toHaveLength(1)
|
||||
expect(rows[0].price).toBe("200.00")
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
it("should be able to update a money field", async () => {
|
||||
let row = await config.api.row.save(table._id!, { price: 200 })
|
||||
expect(row.price).toBe("200.00")
|
||||
const descriptions = datasourceDescribe({ only: [DatabaseName.POSTGRES] })
|
||||
|
||||
row = await config.api.row.save(table._id!, { ...row, price: 300 })
|
||||
expect(row.price).toBe("300.00")
|
||||
if (descriptions.length) {
|
||||
describe.each(descriptions)(
|
||||
"Integration compatibility with postgres search_path",
|
||||
({ config, dsProvider }) => {
|
||||
let datasource: Datasource
|
||||
let client: Knex
|
||||
let schema1: string
|
||||
let schema2: string
|
||||
|
||||
row = await config.api.row.save(table._id!, { ...row, price: "400.00" })
|
||||
expect(row.price).toBe("400.00")
|
||||
})
|
||||
})
|
||||
beforeEach(async () => {
|
||||
const ds = await dsProvider()
|
||||
datasource = ds.datasource!
|
||||
const rawDatasource = ds.rawDatasource!
|
||||
|
||||
schema1 = generator.guid().replaceAll("-", "")
|
||||
schema2 = generator.guid().replaceAll("-", "")
|
||||
|
||||
client = await knexClient(rawDatasource)
|
||||
|
||||
await client.schema.createSchema(schema1)
|
||||
await client.schema.createSchema(schema2)
|
||||
|
||||
rawDatasource.config!.schema = `${schema1}, ${schema2}`
|
||||
|
||||
client = await knexClient(rawDatasource)
|
||||
datasource = await config.api.datasource.create(rawDatasource)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await client.schema.dropSchema(schema1, true)
|
||||
await client.schema.dropSchema(schema2, true)
|
||||
})
|
||||
|
||||
it("discovers tables from any schema in search path", async () => {
|
||||
await client.schema.createTable(`${schema1}.table1`, table => {
|
||||
table.increments("id1").primary()
|
||||
})
|
||||
|
||||
await client.schema.createTable(`${schema2}.table2`, table => {
|
||||
table.increments("id2").primary()
|
||||
})
|
||||
|
||||
const response = await config.api.datasource.info(datasource)
|
||||
expect(response.tableNames).toBeDefined()
|
||||
expect(response.tableNames).toEqual(
|
||||
expect.arrayContaining(["table1", "table2"])
|
||||
)
|
||||
})
|
||||
|
||||
it("does not mix columns from different tables", async () => {
|
||||
const repeated_table_name = "table_same_name"
|
||||
|
||||
await client.schema.createTable(
|
||||
`${schema1}.${repeated_table_name}`,
|
||||
table => {
|
||||
table.increments("id").primary()
|
||||
table.string("val1")
|
||||
}
|
||||
)
|
||||
|
||||
await client.schema.createTable(
|
||||
`${schema2}.${repeated_table_name}`,
|
||||
table => {
|
||||
table.increments("id2").primary()
|
||||
table.string("val2")
|
||||
}
|
||||
)
|
||||
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
tablesFilter: [repeated_table_name],
|
||||
})
|
||||
expect(
|
||||
response.datasource.entities?.[repeated_table_name].schema
|
||||
).toBeDefined()
|
||||
const schema =
|
||||
response.datasource.entities?.[repeated_table_name].schema
|
||||
expect(Object.keys(schema || {}).sort()).toEqual(["id", "val1"])
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
datasourceDescribe(
|
||||
{
|
||||
name: "Integration compatibility with postgres search_path",
|
||||
only: [DatabaseName.POSTGRES],
|
||||
},
|
||||
({ config, dsProvider }) => {
|
||||
let datasource: Datasource
|
||||
let client: Knex
|
||||
let schema1: string
|
||||
let schema2: string
|
||||
|
||||
beforeEach(async () => {
|
||||
const ds = await dsProvider()
|
||||
datasource = ds.datasource!
|
||||
const rawDatasource = ds.rawDatasource!
|
||||
|
||||
schema1 = generator.guid().replaceAll("-", "")
|
||||
schema2 = generator.guid().replaceAll("-", "")
|
||||
|
||||
client = await knexClient(rawDatasource)
|
||||
|
||||
await client.schema.createSchema(schema1)
|
||||
await client.schema.createSchema(schema2)
|
||||
|
||||
rawDatasource.config!.schema = `${schema1}, ${schema2}`
|
||||
|
||||
client = await knexClient(rawDatasource)
|
||||
datasource = await config.api.datasource.create(rawDatasource)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await client.schema.dropSchema(schema1, true)
|
||||
await client.schema.dropSchema(schema2, true)
|
||||
})
|
||||
|
||||
it("discovers tables from any schema in search path", async () => {
|
||||
await client.schema.createTable(`${schema1}.table1`, table => {
|
||||
table.increments("id1").primary()
|
||||
})
|
||||
|
||||
await client.schema.createTable(`${schema2}.table2`, table => {
|
||||
table.increments("id2").primary()
|
||||
})
|
||||
|
||||
const response = await config.api.datasource.info(datasource)
|
||||
expect(response.tableNames).toBeDefined()
|
||||
expect(response.tableNames).toEqual(
|
||||
expect.arrayContaining(["table1", "table2"])
|
||||
)
|
||||
})
|
||||
|
||||
it("does not mix columns from different tables", async () => {
|
||||
const repeated_table_name = "table_same_name"
|
||||
|
||||
await client.schema.createTable(
|
||||
`${schema1}.${repeated_table_name}`,
|
||||
table => {
|
||||
table.increments("id").primary()
|
||||
table.string("val1")
|
||||
}
|
||||
)
|
||||
|
||||
await client.schema.createTable(
|
||||
`${schema2}.${repeated_table_name}`,
|
||||
table => {
|
||||
table.increments("id2").primary()
|
||||
table.string("val2")
|
||||
}
|
||||
)
|
||||
|
||||
const response = await config.api.datasource.fetchSchema({
|
||||
datasourceId: datasource._id!,
|
||||
tablesFilter: [repeated_table_name],
|
||||
})
|
||||
expect(
|
||||
response.datasource.entities?.[repeated_table_name].schema
|
||||
).toBeDefined()
|
||||
const schema = response.datasource.entities?.[repeated_table_name].schema
|
||||
expect(Object.keys(schema || {}).sort()).toEqual(["id", "val1"])
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -19,202 +19,206 @@ import { tableForDatasource } from "../../../../../tests/utilities/structures"
|
|||
// These test cases are only for things that cannot be tested through the API
|
||||
// (e.g. limiting searches to returning specific fields). If it's possible to
|
||||
// test through the API, it should be done there instead.
|
||||
datasourceDescribe(
|
||||
{ name: "search sdk (%s)", exclude: [DatabaseName.MONGODB] },
|
||||
({ config, dsProvider, isInternal }) => {
|
||||
let datasource: Datasource | undefined
|
||||
let table: Table
|
||||
const descriptions = datasourceDescribe({ exclude: [DatabaseName.MONGODB] })
|
||||
|
||||
beforeAll(async () => {
|
||||
const ds = await dsProvider()
|
||||
datasource = ds.datasource
|
||||
})
|
||||
if (descriptions.length) {
|
||||
describe.each(descriptions)(
|
||||
"search sdk ($dbName)",
|
||||
({ config, dsProvider, isInternal }) => {
|
||||
let datasource: Datasource | undefined
|
||||
let table: Table
|
||||
|
||||
beforeEach(async () => {
|
||||
const idFieldSchema: NumberFieldMetadata | AutoColumnFieldMetadata =
|
||||
isInternal
|
||||
? {
|
||||
name: "id",
|
||||
type: FieldType.AUTO,
|
||||
subtype: AutoFieldSubType.AUTO_ID,
|
||||
autocolumn: true,
|
||||
}
|
||||
: {
|
||||
name: "id",
|
||||
type: FieldType.NUMBER,
|
||||
autocolumn: true,
|
||||
}
|
||||
|
||||
table = await config.api.table.save(
|
||||
tableForDatasource(datasource, {
|
||||
primary: ["id"],
|
||||
schema: {
|
||||
id: idFieldSchema,
|
||||
name: {
|
||||
name: "name",
|
||||
type: FieldType.STRING,
|
||||
},
|
||||
surname: {
|
||||
name: "surname",
|
||||
type: FieldType.STRING,
|
||||
},
|
||||
age: {
|
||||
name: "age",
|
||||
type: FieldType.NUMBER,
|
||||
},
|
||||
address: {
|
||||
name: "address",
|
||||
type: FieldType.STRING,
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await config.api.row.save(table._id!, {
|
||||
name: generator.first(),
|
||||
surname: generator.last(),
|
||||
age: generator.age(),
|
||||
address: generator.address(),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
config.end()
|
||||
})
|
||||
|
||||
it("querying by fields will always return data attribute columns", async () => {
|
||||
await config.doInContext(config.appId, async () => {
|
||||
const { rows } = await search({
|
||||
tableId: table._id!,
|
||||
query: {},
|
||||
fields: ["name", "age"],
|
||||
})
|
||||
|
||||
expect(rows).toHaveLength(10)
|
||||
for (const row of rows) {
|
||||
const keys = Object.keys(row)
|
||||
expect(keys).toContain("name")
|
||||
expect(keys).toContain("age")
|
||||
expect(keys).not.toContain("surname")
|
||||
expect(keys).not.toContain("address")
|
||||
}
|
||||
beforeAll(async () => {
|
||||
const ds = await dsProvider()
|
||||
datasource = ds.datasource
|
||||
})
|
||||
})
|
||||
|
||||
!isInternal &&
|
||||
it("will decode _id in oneOf query", async () => {
|
||||
await config.doInContext(config.appId, async () => {
|
||||
const result = await search({
|
||||
tableId: table._id!,
|
||||
query: {
|
||||
oneOf: {
|
||||
_id: ["%5B1%5D", "%5B4%5D", "%5B8%5D"],
|
||||
beforeEach(async () => {
|
||||
const idFieldSchema: NumberFieldMetadata | AutoColumnFieldMetadata =
|
||||
isInternal
|
||||
? {
|
||||
name: "id",
|
||||
type: FieldType.AUTO,
|
||||
subtype: AutoFieldSubType.AUTO_ID,
|
||||
autocolumn: true,
|
||||
}
|
||||
: {
|
||||
name: "id",
|
||||
type: FieldType.NUMBER,
|
||||
autocolumn: true,
|
||||
}
|
||||
|
||||
table = await config.api.table.save(
|
||||
tableForDatasource(datasource, {
|
||||
primary: ["id"],
|
||||
schema: {
|
||||
id: idFieldSchema,
|
||||
name: {
|
||||
name: "name",
|
||||
type: FieldType.STRING,
|
||||
},
|
||||
surname: {
|
||||
name: "surname",
|
||||
type: FieldType.STRING,
|
||||
},
|
||||
age: {
|
||||
name: "age",
|
||||
type: FieldType.NUMBER,
|
||||
},
|
||||
address: {
|
||||
name: "address",
|
||||
type: FieldType.STRING,
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
expect(result.rows).toHaveLength(3)
|
||||
expect(result.rows.map(row => row.id)).toEqual(
|
||||
expect.arrayContaining([1, 4, 8])
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it("does not allow accessing hidden fields", async () => {
|
||||
await config.doInContext(config.appId, async () => {
|
||||
await config.api.table.save({
|
||||
...table,
|
||||
schema: {
|
||||
...table.schema,
|
||||
name: {
|
||||
...table.schema.name,
|
||||
visible: true,
|
||||
},
|
||||
age: {
|
||||
...table.schema.age,
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
const result = await search({
|
||||
tableId: table._id!,
|
||||
query: {},
|
||||
})
|
||||
expect(result.rows).toHaveLength(10)
|
||||
for (const row of result.rows) {
|
||||
const keys = Object.keys(row)
|
||||
expect(keys).toContain("name")
|
||||
expect(keys).toContain("surname")
|
||||
expect(keys).toContain("address")
|
||||
expect(keys).not.toContain("age")
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await config.api.row.save(table._id!, {
|
||||
name: generator.first(),
|
||||
surname: generator.last(),
|
||||
age: generator.age(),
|
||||
address: generator.address(),
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it("does not allow accessing hidden fields even if requested", async () => {
|
||||
await config.doInContext(config.appId, async () => {
|
||||
await config.api.table.save({
|
||||
...table,
|
||||
schema: {
|
||||
...table.schema,
|
||||
name: {
|
||||
...table.schema.name,
|
||||
visible: true,
|
||||
},
|
||||
age: {
|
||||
...table.schema.age,
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
const result = await search({
|
||||
tableId: table._id!,
|
||||
query: {},
|
||||
fields: ["name", "age"],
|
||||
})
|
||||
expect(result.rows).toHaveLength(10)
|
||||
for (const row of result.rows) {
|
||||
const keys = Object.keys(row)
|
||||
expect(keys).toContain("name")
|
||||
expect(keys).not.toContain("age")
|
||||
expect(keys).not.toContain("surname")
|
||||
expect(keys).not.toContain("address")
|
||||
}
|
||||
afterAll(async () => {
|
||||
config.end()
|
||||
})
|
||||
})
|
||||
|
||||
it.each([
|
||||
[["id", "name", "age"], 3],
|
||||
[["name", "age"], 10],
|
||||
])(
|
||||
"cannot query by non search fields (fields: %s)",
|
||||
async (queryFields, expectedRows) => {
|
||||
it("querying by fields will always return data attribute columns", async () => {
|
||||
await config.doInContext(config.appId, async () => {
|
||||
const { rows } = await search({
|
||||
tableId: table._id!,
|
||||
query: {
|
||||
$or: {
|
||||
conditions: [
|
||||
{
|
||||
$and: {
|
||||
conditions: [
|
||||
{ range: { id: { low: 2, high: 4 } } },
|
||||
{ range: { id: { low: 3, high: 5 } } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{ equal: { id: 7 } },
|
||||
],
|
||||
},
|
||||
},
|
||||
fields: queryFields,
|
||||
query: {},
|
||||
fields: ["name", "age"],
|
||||
})
|
||||
|
||||
expect(rows).toHaveLength(expectedRows)
|
||||
expect(rows).toHaveLength(10)
|
||||
for (const row of rows) {
|
||||
const keys = Object.keys(row)
|
||||
expect(keys).toContain("name")
|
||||
expect(keys).toContain("age")
|
||||
expect(keys).not.toContain("surname")
|
||||
expect(keys).not.toContain("address")
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
!isInternal &&
|
||||
it("will decode _id in oneOf query", async () => {
|
||||
await config.doInContext(config.appId, async () => {
|
||||
const result = await search({
|
||||
tableId: table._id!,
|
||||
query: {
|
||||
oneOf: {
|
||||
_id: ["%5B1%5D", "%5B4%5D", "%5B8%5D"],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(result.rows).toHaveLength(3)
|
||||
expect(result.rows.map(row => row.id)).toEqual(
|
||||
expect.arrayContaining([1, 4, 8])
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it("does not allow accessing hidden fields", async () => {
|
||||
await config.doInContext(config.appId, async () => {
|
||||
await config.api.table.save({
|
||||
...table,
|
||||
schema: {
|
||||
...table.schema,
|
||||
name: {
|
||||
...table.schema.name,
|
||||
visible: true,
|
||||
},
|
||||
age: {
|
||||
...table.schema.age,
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
const result = await search({
|
||||
tableId: table._id!,
|
||||
query: {},
|
||||
})
|
||||
expect(result.rows).toHaveLength(10)
|
||||
for (const row of result.rows) {
|
||||
const keys = Object.keys(row)
|
||||
expect(keys).toContain("name")
|
||||
expect(keys).toContain("surname")
|
||||
expect(keys).toContain("address")
|
||||
expect(keys).not.toContain("age")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it("does not allow accessing hidden fields even if requested", async () => {
|
||||
await config.doInContext(config.appId, async () => {
|
||||
await config.api.table.save({
|
||||
...table,
|
||||
schema: {
|
||||
...table.schema,
|
||||
name: {
|
||||
...table.schema.name,
|
||||
visible: true,
|
||||
},
|
||||
age: {
|
||||
...table.schema.age,
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
const result = await search({
|
||||
tableId: table._id!,
|
||||
query: {},
|
||||
fields: ["name", "age"],
|
||||
})
|
||||
expect(result.rows).toHaveLength(10)
|
||||
for (const row of result.rows) {
|
||||
const keys = Object.keys(row)
|
||||
expect(keys).toContain("name")
|
||||
expect(keys).not.toContain("age")
|
||||
expect(keys).not.toContain("surname")
|
||||
expect(keys).not.toContain("address")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it.each([
|
||||
[["id", "name", "age"], 3],
|
||||
[["name", "age"], 10],
|
||||
])(
|
||||
"cannot query by non search fields (fields: %s)",
|
||||
async (queryFields, expectedRows) => {
|
||||
await config.doInContext(config.appId, async () => {
|
||||
const { rows } = await search({
|
||||
tableId: table._id!,
|
||||
query: {
|
||||
$or: {
|
||||
conditions: [
|
||||
{
|
||||
$and: {
|
||||
conditions: [
|
||||
{ range: { id: { low: 2, high: 4 } } },
|
||||
{ range: { id: { low: 3, high: 5 } } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{ equal: { id: 7 } },
|
||||
],
|
||||
},
|
||||
},
|
||||
fields: queryFields,
|
||||
})
|
||||
|
||||
expect(rows).toHaveLength(expectedRows)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue