Adding test case.
This commit is contained in:
parent
676058bbbd
commit
680c68a35b
|
@ -42,6 +42,7 @@ import { Knex } from "knex"
|
||||||
import { structures } from "@budibase/backend-core/tests"
|
import { structures } from "@budibase/backend-core/tests"
|
||||||
import { DEFAULT_EMPLOYEE_TABLE_SCHEMA } from "../../../db/defaultData/datasource_bb_default"
|
import { DEFAULT_EMPLOYEE_TABLE_SCHEMA } from "../../../db/defaultData/datasource_bb_default"
|
||||||
import { generateRowIdField } from "../../../integrations/utils"
|
import { generateRowIdField } from "../../../integrations/utils"
|
||||||
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
|
||||||
describe.each([
|
describe.each([
|
||||||
["in-memory", undefined],
|
["in-memory", undefined],
|
||||||
|
@ -66,6 +67,35 @@ describe.each([
|
||||||
let table: Table
|
let table: Table
|
||||||
let rows: Row[]
|
let rows: Row[]
|
||||||
|
|
||||||
|
async function basicRelationshipTables(type: RelationshipType) {
|
||||||
|
const relatedTable = await createTable(
|
||||||
|
{
|
||||||
|
name: { name: "name", type: FieldType.STRING },
|
||||||
|
},
|
||||||
|
"productCategory"
|
||||||
|
)
|
||||||
|
table = await createTable(
|
||||||
|
{
|
||||||
|
name: { name: "name", type: FieldType.STRING },
|
||||||
|
productCat: {
|
||||||
|
type: FieldType.LINK,
|
||||||
|
relationshipType: type,
|
||||||
|
name: "productCat",
|
||||||
|
fieldName: "product",
|
||||||
|
tableId: relatedTable._id!,
|
||||||
|
constraints: {
|
||||||
|
type: "array",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"product"
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
relatedTable: await config.api.table.get(relatedTable._id!),
|
||||||
|
table,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await withCoreEnv({ TENANT_FEATURE_FLAGS: "*:SQS" }, () => config.init())
|
await withCoreEnv({ TENANT_FEATURE_FLAGS: "*:SQS" }, () => config.init())
|
||||||
if (isLucene) {
|
if (isLucene) {
|
||||||
|
@ -201,6 +231,7 @@ describe.each([
|
||||||
// rows returned by the query will also cause the assertion to fail.
|
// rows returned by the query will also cause the assertion to fail.
|
||||||
async toMatchExactly(expectedRows: any[]) {
|
async toMatchExactly(expectedRows: any[]) {
|
||||||
const response = await this.performSearch()
|
const response = await this.performSearch()
|
||||||
|
const cloned = cloneDeep(response)
|
||||||
const foundRows = response.rows
|
const foundRows = response.rows
|
||||||
|
|
||||||
// eslint-disable-next-line jest/no-standalone-expect
|
// eslint-disable-next-line jest/no-standalone-expect
|
||||||
|
@ -211,7 +242,7 @@ describe.each([
|
||||||
expect.objectContaining(this.popRow(expectedRow, foundRows))
|
expect.objectContaining(this.popRow(expectedRow, foundRows))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return response
|
return cloned
|
||||||
}
|
}
|
||||||
|
|
||||||
// Asserts that the query returns rows matching exactly the set of rows
|
// Asserts that the query returns rows matching exactly the set of rows
|
||||||
|
@ -219,6 +250,7 @@ describe.each([
|
||||||
// cause the assertion to fail.
|
// cause the assertion to fail.
|
||||||
async toContainExactly(expectedRows: any[]) {
|
async toContainExactly(expectedRows: any[]) {
|
||||||
const response = await this.performSearch()
|
const response = await this.performSearch()
|
||||||
|
const cloned = cloneDeep(response)
|
||||||
const foundRows = response.rows
|
const foundRows = response.rows
|
||||||
|
|
||||||
// eslint-disable-next-line jest/no-standalone-expect
|
// eslint-disable-next-line jest/no-standalone-expect
|
||||||
|
@ -231,7 +263,7 @@ describe.each([
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return response
|
return cloned
|
||||||
}
|
}
|
||||||
|
|
||||||
// Asserts that the query returns some property values - this cannot be used
|
// Asserts that the query returns some property values - this cannot be used
|
||||||
|
@ -239,6 +271,7 @@ describe.each([
|
||||||
// typing for this has to be any, Jest doesn't expose types for matchers like expect.any(...)
|
// typing for this has to be any, Jest doesn't expose types for matchers like expect.any(...)
|
||||||
async toMatch(properties: Record<string, any>) {
|
async toMatch(properties: Record<string, any>) {
|
||||||
const response = await this.performSearch()
|
const response = await this.performSearch()
|
||||||
|
const cloned = cloneDeep(response)
|
||||||
const keys = Object.keys(properties) as Array<keyof SearchResponse<Row>>
|
const keys = Object.keys(properties) as Array<keyof SearchResponse<Row>>
|
||||||
for (let key of keys) {
|
for (let key of keys) {
|
||||||
// eslint-disable-next-line jest/no-standalone-expect
|
// eslint-disable-next-line jest/no-standalone-expect
|
||||||
|
@ -248,17 +281,18 @@ describe.each([
|
||||||
expect(response[key]).toEqual(properties[key])
|
expect(response[key]).toEqual(properties[key])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return response
|
return cloned
|
||||||
}
|
}
|
||||||
|
|
||||||
// Asserts that the query doesn't return a property, e.g. pagination parameters.
|
// Asserts that the query doesn't return a property, e.g. pagination parameters.
|
||||||
async toNotHaveProperty(properties: (keyof SearchResponse<Row>)[]) {
|
async toNotHaveProperty(properties: (keyof SearchResponse<Row>)[]) {
|
||||||
const response = await this.performSearch()
|
const response = await this.performSearch()
|
||||||
|
const cloned = cloneDeep(response)
|
||||||
for (let property of properties) {
|
for (let property of properties) {
|
||||||
// eslint-disable-next-line jest/no-standalone-expect
|
// eslint-disable-next-line jest/no-standalone-expect
|
||||||
expect(response[property]).toBeUndefined()
|
expect(response[property]).toBeUndefined()
|
||||||
}
|
}
|
||||||
return response
|
return cloned
|
||||||
}
|
}
|
||||||
|
|
||||||
// Asserts that the query returns rows matching the set of rows passed in.
|
// Asserts that the query returns rows matching the set of rows passed in.
|
||||||
|
@ -266,6 +300,7 @@ describe.each([
|
||||||
// assertion to fail.
|
// assertion to fail.
|
||||||
async toContain(expectedRows: any[]) {
|
async toContain(expectedRows: any[]) {
|
||||||
const response = await this.performSearch()
|
const response = await this.performSearch()
|
||||||
|
const cloned = cloneDeep(response)
|
||||||
const foundRows = response.rows
|
const foundRows = response.rows
|
||||||
|
|
||||||
// eslint-disable-next-line jest/no-standalone-expect
|
// eslint-disable-next-line jest/no-standalone-expect
|
||||||
|
@ -276,7 +311,7 @@ describe.each([
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return response
|
return cloned
|
||||||
}
|
}
|
||||||
|
|
||||||
async toFindNothing() {
|
async toFindNothing() {
|
||||||
|
@ -2196,28 +2231,10 @@ describe.each([
|
||||||
let productCategoryTable: Table, productCatRows: Row[]
|
let productCategoryTable: Table, productCatRows: Row[]
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
productCategoryTable = await createTable(
|
const { relatedTable } = await basicRelationshipTables(
|
||||||
{
|
RelationshipType.ONE_TO_MANY
|
||||||
name: { name: "name", type: FieldType.STRING },
|
|
||||||
},
|
|
||||||
"productCategory"
|
|
||||||
)
|
|
||||||
table = await createTable(
|
|
||||||
{
|
|
||||||
name: { name: "name", type: FieldType.STRING },
|
|
||||||
productCat: {
|
|
||||||
type: FieldType.LINK,
|
|
||||||
relationshipType: RelationshipType.ONE_TO_MANY,
|
|
||||||
name: "productCat",
|
|
||||||
fieldName: "product",
|
|
||||||
tableId: productCategoryTable._id!,
|
|
||||||
constraints: {
|
|
||||||
type: "array",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"product"
|
|
||||||
)
|
)
|
||||||
|
productCategoryTable = relatedTable
|
||||||
|
|
||||||
productCatRows = await Promise.all([
|
productCatRows = await Promise.all([
|
||||||
config.api.row.save(productCategoryTable._id!, { name: "foo" }),
|
config.api.row.save(productCategoryTable._id!, { name: "foo" }),
|
||||||
|
@ -2262,6 +2279,31 @@ describe.each([
|
||||||
}).toContainExactly([{ name: "baz", productCat: undefined }])
|
}).toContainExactly([{ name: "baz", productCat: undefined }])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
isSql &&
|
||||||
|
describe("big relations", () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
const { relatedTable } = await basicRelationshipTables(
|
||||||
|
RelationshipType.MANY_TO_ONE
|
||||||
|
)
|
||||||
|
const mainRow = await config.api.row.save(table._id!, {
|
||||||
|
name: "foo",
|
||||||
|
})
|
||||||
|
for (let i = 0; i < 11; i++) {
|
||||||
|
await config.api.row.save(relatedTable._id!, {
|
||||||
|
name: i,
|
||||||
|
product: [mainRow._id!],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("can only pull 500 related rows", async () => {
|
||||||
|
await withCoreEnv({ SQL_MAX_RELATED_ROWS: "10" }, async () => {
|
||||||
|
const response = await expectQuery({}).toContain([{ name: "foo" }])
|
||||||
|
expect(response.rows[0].productCat).toBeArrayOfSize(10)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
;(isSqs || isLucene) &&
|
;(isSqs || isLucene) &&
|
||||||
describe("relations to same table", () => {
|
describe("relations to same table", () => {
|
||||||
let relatedTable: Table, relatedRows: Row[]
|
let relatedTable: Table, relatedRows: Row[]
|
||||||
|
|
|
@ -198,6 +198,7 @@ export async function save(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
generateRelatedSchema(schema, relatedTable, tableToSave, relatedColumnName)
|
generateRelatedSchema(schema, relatedTable, tableToSave, relatedColumnName)
|
||||||
|
tables[relatedTable.name] = relatedTable
|
||||||
schema.main = true
|
schema.main = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +232,10 @@ export async function save(
|
||||||
// remove the rename prop
|
// remove the rename prop
|
||||||
delete tableToSave._rename
|
delete tableToSave._rename
|
||||||
|
|
||||||
datasource.entities[tableToSave.name] = tableToSave
|
datasource.entities = {
|
||||||
|
...datasource.entities,
|
||||||
|
...tables,
|
||||||
|
}
|
||||||
|
|
||||||
// store it into couch now for budibase reference
|
// store it into couch now for budibase reference
|
||||||
await db.put(populateExternalTableSchemas(datasource))
|
await db.put(populateExternalTableSchemas(datasource))
|
||||||
|
|
|
@ -22,12 +22,16 @@ export function cleanupRelationships(
|
||||||
tables: Record<string, Table>,
|
tables: Record<string, Table>,
|
||||||
oldTable?: Table
|
oldTable?: Table
|
||||||
) {
|
) {
|
||||||
|
if (!oldTable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const tableToIterate = oldTable ? oldTable : table
|
const tableToIterate = oldTable ? oldTable : table
|
||||||
// clean up relationships in couch table schemas
|
// clean up relationships in couch table schemas
|
||||||
for (let [key, schema] of Object.entries(tableToIterate.schema)) {
|
for (let [key, schema] of Object.entries(tableToIterate.schema)) {
|
||||||
if (
|
if (
|
||||||
schema.type === FieldType.LINK &&
|
schema.type === FieldType.LINK &&
|
||||||
(!oldTable || table.schema[key] == null)
|
oldTable.schema[key] != null &&
|
||||||
|
table.schema[key] == null
|
||||||
) {
|
) {
|
||||||
const schemaTableId = schema.tableId
|
const schemaTableId = schema.tableId
|
||||||
const relatedTable = Object.values(tables).find(
|
const relatedTable = Object.values(tables).find(
|
||||||
|
|
Loading…
Reference in New Issue