table.spec.ts passing for internal and postgres

This commit is contained in:
Sam Rose 2024-04-05 16:38:57 +01:00
parent bc072e1424
commit bcc09bd86e
No known key found for this signature in database
5 changed files with 166 additions and 194 deletions

View File

@ -84,8 +84,8 @@ export async function save(ctx: UserCtx<SaveTableRequest, SaveTableResponse>) {
} }
let savedTable = await api.save(ctx, renaming) let savedTable = await api.save(ctx, renaming)
if (!table._id) { if (!table._id) {
await events.table.created(savedTable)
savedTable = sdk.tables.enrichViewSchemas(savedTable) savedTable = sdk.tables.enrichViewSchemas(savedTable)
await events.table.created(savedTable)
} else { } else {
await events.table.updated(savedTable) await events.table.updated(savedTable)
} }

View File

@ -722,6 +722,39 @@ describe.each([
}) })
}) })
describe("bulkImport", () => {
isInternal &&
it("should update Auto ID field after bulk import", async () => {
const table = await config.api.table.save(
saveTableRequest({
primary: ["autoId"],
schema: {
autoId: {
name: "autoId",
type: FieldType.NUMBER,
subtype: AutoFieldSubType.AUTO_ID,
autocolumn: true,
constraints: {
type: "number",
presence: false,
},
},
},
})
)
let row = await config.api.row.save(table._id!, {})
expect(row.autoId).toEqual(1)
await config.api.row.bulkImport(table._id!, {
rows: [{ autoId: 2 }],
})
row = await config.api.row.save(table._id!, {})
expect(row.autoId).toEqual(3)
})
})
describe("enrich", () => { describe("enrich", () => {
beforeAll(async () => { beforeAll(async () => {
table = await config.api.table.save(defaultTable()) table = await config.api.table.save(defaultTable())

View File

@ -6,7 +6,6 @@ import {
FieldType, FieldType,
INTERNAL_TABLE_SOURCE_ID, INTERNAL_TABLE_SOURCE_ID,
InternalTable, InternalTable,
NumberFieldMetadata,
RelationshipType, RelationshipType,
Row, Row,
SaveTableRequest, SaveTableRequest,
@ -14,17 +13,16 @@ import {
TableSourceType, TableSourceType,
User, User,
ViewCalculation, ViewCalculation,
ViewV2,
} from "@budibase/types" } from "@budibase/types"
import { checkBuilderEndpoint } from "./utilities/TestFunctions" import { checkBuilderEndpoint } from "./utilities/TestFunctions"
import * as setup from "./utilities" import * as setup from "./utilities"
import * as uuid from "uuid" import * as uuid from "uuid"
import tk from "timekeeper" import { generator } from "@budibase/backend-core/tests"
import { generator, mocks } from "@budibase/backend-core/tests"
import { DatabaseName, getDatasource } from "../../../integrations/tests/utils" import { DatabaseName, getDatasource } from "../../../integrations/tests/utils"
import { tableForDatasource } from "../../../tests/utilities/structures" import { tableForDatasource } from "../../../tests/utilities/structures"
import timekeeper from "timekeeper"
tk.freeze(mocks.date.MOCK_DATE)
const { basicTable } = setup.structures const { basicTable } = setup.structures
const ISO_REGEX_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/ const ISO_REGEX_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/
@ -57,7 +55,7 @@ describe.each([
jest.clearAllMocks() jest.clearAllMocks()
}) })
it("returns a success message when the table is successfully created", async () => { it("creates a table successfully", async () => {
const name = generator.guid() const name = generator.guid()
const table = await config.api.table.save( const table = await config.api.table.save(
tableForDatasource(datasource, { name }) tableForDatasource(datasource, { name })
@ -67,64 +65,6 @@ describe.each([
expect(events.table.created).toHaveBeenCalledWith(table) expect(events.table.created).toHaveBeenCalledWith(table)
}) })
it("creates all the passed fields", async () => {
const tableData = tableForDatasource(datasource, {
schema: {
autoId: {
name: "id",
type: FieldType.NUMBER,
subtype: AutoFieldSubType.AUTO_ID,
autocolumn: true,
constraints: {
type: "number",
presence: false,
},
},
},
views: {
"table view": {
id: "viewId",
version: 2,
name: "table view",
tableId: "tableId",
},
},
})
const testTable = await config.api.table.save(tableData)
const expected: Table = {
...tableData,
type: "table",
views: {
"table view": {
...tableData.views!["table view"],
schema: {
autoId: {
autocolumn: true,
constraints: {
presence: false,
type: "number",
},
name: "id",
type: FieldType.NUMBER,
subtype: AutoFieldSubType.AUTO_ID,
visible: false,
} as NumberFieldMetadata,
},
},
},
_rev: expect.stringMatching(/^1-.+/),
_id: expect.any(String),
createdAt: mocks.date.MOCK_DATE.toISOString(),
updatedAt: mocks.date.MOCK_DATE.toISOString(),
}
expect(testTable).toEqual(expected)
const persistedTable = await config.api.table.get(testTable._id!)
expect(persistedTable).toEqual(expected)
})
it("creates a table via data import", async () => { it("creates a table via data import", async () => {
const table: SaveTableRequest = basicTable() const table: SaveTableRequest = basicTable()
table.rows = [{ name: "test-name", description: "test-desc" }] table.rows = [{ name: "test-name", description: "test-desc" }]
@ -165,17 +105,17 @@ describe.each([
}) })
) )
await config.api.table.save({ const updatedTable = await config.api.table.save({
...table, ...table,
name: generator.guid(), name: generator.guid(),
}) })
expect(events.table.updated).toHaveBeenCalledTimes(1) expect(events.table.updated).toHaveBeenCalledTimes(1)
expect(events.table.updated).toHaveBeenCalledWith(table) expect(events.table.updated).toHaveBeenCalledWith(updatedTable)
}) })
it("updates all the row fields for a table when a schema key is renamed", async () => { it("updates all the row fields for a table when a schema key is renamed", async () => {
const testTable = await config.createTable() const testTable = await config.api.table.save(basicTable(datasource))
await config.createLegacyView({ await config.createLegacyView({
name: "TestView", name: "TestView",
field: "Price", field: "Price",
@ -215,64 +155,68 @@ describe.each([
}) })
it("updates only the passed fields", async () => { it("updates only the passed fields", async () => {
const table = await config.api.table.save( await timekeeper.withFreeze(new Date(2021, 1, 1), async () => {
tableForDatasource(datasource, { const table = await config.api.table.save(
name: "TestTable", tableForDatasource(datasource, {
schema: { schema: {
autoId: { autoId: {
name: "id", name: "id",
type: FieldType.NUMBER, type: FieldType.NUMBER,
subtype: AutoFieldSubType.AUTO_ID, subtype: AutoFieldSubType.AUTO_ID,
autocolumn: true, autocolumn: true,
constraints: { constraints: {
type: "number", type: "number",
presence: false, presence: false,
},
}, },
}, },
}, })
)
const newName = generator.guid()
const updatedTable = await config.api.table.save({
...table,
name: newName,
}) })
)
const updatedTable = await config.api.table.save({ let expected: Table = {
...table, ...table,
name: "UpdatedName", name: newName,
_id: expect.any(String),
}
if (isInternal) {
expected._rev = expect.stringMatching(/^2-.+/)
}
expect(updatedTable).toEqual(expected)
const persistedTable = await config.api.table.get(updatedTable._id!)
expected = {
...table,
name: newName,
_id: updatedTable._id,
}
if (datasource?.isSQL) {
expected.sql = true
}
if (isInternal) {
expected._rev = expect.stringMatching(/^2-.+/)
}
expect(persistedTable).toEqual(expected)
}) })
let expected: Table = {
...table,
name: "UpdatedName",
_id: expect.any(String),
}
if (isInternal) {
expected._rev = expect.stringMatching(/^2-.+/)
}
expect(updatedTable).toEqual(expected)
const persistedTable = await config.api.table.get(updatedTable._id!)
expected = {
...table,
name: "UpdatedName",
_id: updatedTable._id,
}
if (datasource?.isSQL) {
expected.sql = true
}
if (isInternal) {
expected._rev = expect.stringMatching(/^2-.+/)
}
expect(persistedTable).toEqual(expected)
}) })
describe("user table", () => { describe("user table", () => {
it("should add roleId and email field when adjusting user table schema", async () => { isInternal &&
const table = await config.api.table.save({ it("should add roleId and email field when adjusting user table schema", async () => {
...basicTable(datasource), const table = await config.api.table.save({
_id: "ta_users", ...basicTable(datasource),
_id: "ta_users",
})
expect(table.schema.email).toBeDefined()
expect(table.schema.roleId).toBeDefined()
}) })
expect(table.schema.email).toBeDefined()
expect(table.schema.roleId).toBeDefined()
})
}) })
it("should add a new column for an internal DB table", async () => { it("should add a new column for an internal DB table", async () => {
@ -300,7 +244,10 @@ describe.each([
describe("import", () => { describe("import", () => {
it("imports rows successfully", async () => { it("imports rows successfully", async () => {
const table = await config.api.table.save(basicTable(datasource)) const name = generator.guid()
const table = await config.api.table.save(
basicTable(datasource, { name })
)
const importRequest = { const importRequest = {
schema: table.schema, schema: table.schema,
rows: [{ name: "test-name", description: "test-desc" }], rows: [{ name: "test-name", description: "test-desc" }],
@ -314,43 +261,12 @@ describe.each([
expect(events.rows.imported).toHaveBeenCalledTimes(1) expect(events.rows.imported).toHaveBeenCalledTimes(1)
expect(events.rows.imported).toHaveBeenCalledWith( expect(events.rows.imported).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
name: "TestTable", name,
_id: table._id, _id: table._id,
}), }),
1 1
) )
}) })
it("should update Auto ID field after bulk import", async () => {
const table = await config.api.table.save(
tableForDatasource(datasource, {
name: "TestTable",
schema: {
autoId: {
name: "id",
type: FieldType.NUMBER,
subtype: AutoFieldSubType.AUTO_ID,
autocolumn: true,
constraints: {
type: "number",
presence: false,
},
},
},
})
)
let row = await config.api.row.save(table._id!, {})
expect(row.autoId).toEqual(1)
await config.api.row.bulkImport(table._id!, {
rows: [{ autoId: 2 }],
identifierFields: [],
})
row = await config.api.row.save(table._id!, {})
expect(row.autoId).toEqual(3)
})
}) })
describe("fetch", () => { describe("fetch", () => {
@ -396,7 +312,8 @@ describe.each([
const table = res.find(t => t._id === testTable._id) const table = res.find(t => t._id === testTable._id)
expect(table).toBeDefined() expect(table).toBeDefined()
expect(table!.views![viewV2.name]).toBeDefined() expect(table!.views![viewV2.name]).toBeDefined()
expect(table!.views![viewV2.name!]).toEqual({
const expectedViewV2: ViewV2 = {
...viewV2, ...viewV2,
schema: { schema: {
description: { description: {
@ -404,7 +321,7 @@ describe.each([
type: "string", type: "string",
}, },
name: "description", name: "description",
type: "string", type: FieldType.STRING,
visible: false, visible: false,
}, },
name: { name: {
@ -412,11 +329,22 @@ describe.each([
type: "string", type: "string",
}, },
name: "name", name: "name",
type: "string", type: FieldType.STRING,
visible: false, visible: false,
}, },
}, },
}) }
if (!isInternal) {
expectedViewV2.schema!.id = {
name: "id",
type: FieldType.NUMBER,
visible: false,
autocolumn: true,
}
}
expect(table!.views![viewV2.name!]).toEqual(expectedViewV2)
if (isInternal) { if (isInternal) {
expect(table!.views![legacyView.name!]).toBeDefined() expect(table!.views![legacyView.name!]).toBeDefined()
@ -440,23 +368,16 @@ describe.each([
}, },
}) })
} }
})
})
// expect(res).toEqual( describe("get", () => {
// expect.arrayContaining([ it("returns a table", async () => {
// expect.objectContaining({ const table = await config.api.table.save(
// _id: tableId, basicTable(datasource, { name: generator.guid() })
// views: { )
// view1: { const res = await config.api.table.get(table._id!)
// version: 2, expect(res).toEqual(table)
// name: "view1",
// schema: {},
// id: "new_view_id",
// tableId,
// },
// },
// }),
// ])
// )
}) })
}) })
@ -781,33 +702,31 @@ describe.each([
describe("unhappy paths", () => { describe("unhappy paths", () => {
let table: Table let table: Table
beforeAll(async () => { beforeAll(async () => {
table = await config.api.table.save({ table = await config.api.table.save(
name: "table", tableForDatasource(datasource, {
type: "table", schema: {
sourceId: INTERNAL_TABLE_SOURCE_ID, "user relationship": {
sourceType: TableSourceType.INTERNAL, type: FieldType.LINK,
schema: { fieldName: "test",
"user relationship": { name: "user relationship",
type: FieldType.LINK, constraints: {
fieldName: "test", type: "array",
name: "user relationship", presence: false,
constraints: { },
type: "array", relationshipType: RelationshipType.MANY_TO_ONE,
presence: false, tableId: InternalTable.USER_METADATA,
}, },
relationshipType: RelationshipType.MANY_TO_ONE, num: {
tableId: InternalTable.USER_METADATA, type: FieldType.NUMBER,
}, name: "num",
num: { constraints: {
type: FieldType.NUMBER, type: "number",
name: "num", presence: false,
constraints: { },
type: "number",
presence: false,
}, },
}, },
}, })
}) )
}) })
it("should fail if the new column name is blank", async () => { it("should fail if the new column name is blank", async () => {

View File

@ -48,6 +48,18 @@ export async function save(
oldTable = await getTable(tableId) oldTable = await getTable(tableId)
} }
if (
!oldTable &&
(tableToSave.primary == null || tableToSave.primary.length === 0)
) {
tableToSave.primary = ["id"]
tableToSave.schema.id = {
type: FieldType.NUMBER,
autocolumn: true,
name: "id",
}
}
if (hasTypeChanged(tableToSave, oldTable)) { if (hasTypeChanged(tableToSave, oldTable)) {
throw new Error("A column type has changed.") throw new Error("A column type has changed.")
} }
@ -183,6 +195,10 @@ export async function save(
// that the datasource definition changed // that the datasource definition changed
const updatedDatasource = await datasourceSdk.get(datasource._id!) const updatedDatasource = await datasourceSdk.get(datasource._id!)
if (updatedDatasource.isSQL) {
tableToSave.sql = true
}
return { datasource: updatedDatasource, table: tableToSave } return { datasource: updatedDatasource, table: tableToSave }
} }

View File

@ -10,10 +10,14 @@ import {
} from "./constants" } from "./constants"
export interface UIFieldMetadata { export interface UIFieldMetadata {
constraints?: FieldConstraints
name?: string
type?: FieldType
order?: number order?: number
width?: number width?: number
visible?: boolean visible?: boolean
icon?: string icon?: string
autocolumn?: boolean
} }
interface BaseRelationshipFieldMetadata interface BaseRelationshipFieldMetadata