Improve and fix test

This commit is contained in:
Adria Navarro 2024-04-19 12:38:57 +02:00
parent 1732e14353
commit 1685568089
2 changed files with 197 additions and 123 deletions

View File

@ -5,10 +5,11 @@ import { context, events } from "@budibase/backend-core"
import sdk from "../../../sdk" import sdk from "../../../sdk"
import tk from "timekeeper" import tk from "timekeeper"
import { generator, mocks } from "@budibase/backend-core/tests" import { mocks } from "@budibase/backend-core/tests"
import { import {
Datasource, Datasource,
FieldSchema, FieldSchema,
FieldSubtype,
FieldType, FieldType,
QueryPreview, QueryPreview,
RelationshipType, RelationshipType,
@ -250,7 +251,7 @@ describe("/datasources", () => {
const simpleTable = await config.api.table.save( const simpleTable = await config.api.table.save(
tableForDatasource(datasource, { tableForDatasource(datasource, {
name: generator.guid(), name: "simple",
schema: { schema: {
name: { name: {
name: "name", name: "name",
@ -259,86 +260,82 @@ describe("/datasources", () => {
}, },
}) })
) )
const fullSchema: { [type in FieldType]: FieldSchema & { type: type } } =
{
[FieldType.STRING]: {
name: "string",
type: FieldType.STRING,
},
// [FieldType.LONGFORM]: {
// name: "longform",
// type: FieldType.LONGFORM,
// },
// [FieldType.OPTIONS]: {
// name: "options",
// type: FieldType.OPTIONS,
// },
[FieldType.NUMBER]: {
name: "number",
type: FieldType.NUMBER,
},
[FieldType.BOOLEAN]: {
name: "boolean",
type: FieldType.BOOLEAN,
},
// [FieldType.ARRAY]: {
// name: "array",
// type: FieldType.ARRAY,
// },
[FieldType.DATETIME]: {
name: "datetime",
type: FieldType.DATETIME,
},
// [FieldType.ATTACHMENTS]: {
// name: "attachments",
// type: FieldType.ATTACHMENTS,
// },
// [FieldType.ATTACHMENT_SINGLE]: {
// name: "attachment_single",
// type: FieldType.ATTACHMENT_SINGLE,
// },
// [FieldType.LINK]: {
// name: "link",
// type: FieldType.LINK,
// tableId: simpleTable._id!,
// relationshipType: RelationshipType.ONE_TO_MANY,
// fieldName: "link",
// foreignKey: "fk",
// },
// [FieldType.FORMULA]: {
// name: "formula",
// type: FieldType.FORMULA,
// formula: "any formula",
// },
// [FieldType.AUTO]: {
// name: "auto",
// type: FieldType.AUTO,
// },
// [FieldType.JSON]: {
// name: "json",
// type: FieldType.JSON,
// },
// [FieldType.INTERNAL]: {
// name: "internal",
// type: FieldType.INTERNAL,
// },
[FieldType.BARCODEQR]: {
name: "barcodeqr",
type: FieldType.BARCODEQR,
},
// [FieldType.BIGINT]: {
// name: "bigint",
// type: FieldType.BIGINT,
// },
// [FieldType.BB_REFERENCE]: {
// name: "bb_reference",
// type: FieldType.BB_REFERENCE,
// },
}
const fullTable = await config.api.table.save( type SupportedTypes =
| FieldType.STRING
| FieldType.BARCODEQR
| FieldType.LONGFORM
| FieldType.OPTIONS
| FieldType.DATETIME
| FieldType.NUMBER
| FieldType.BOOLEAN
| FieldType.FORMULA
| FieldType.BIGINT
| FieldType.BB_REFERENCE
| FieldType.LINK
| FieldType.ARRAY
const fullSchema: {
[type in SupportedTypes]: FieldSchema & { type: type }
} = {
[FieldType.STRING]: {
name: "string",
type: FieldType.STRING,
},
[FieldType.LONGFORM]: {
name: "longform",
type: FieldType.LONGFORM,
},
[FieldType.OPTIONS]: {
name: "options",
type: FieldType.OPTIONS,
},
[FieldType.NUMBER]: {
name: "number",
type: FieldType.NUMBER,
},
[FieldType.BOOLEAN]: {
name: "boolean",
type: FieldType.BOOLEAN,
},
[FieldType.ARRAY]: {
name: "array",
type: FieldType.ARRAY,
},
[FieldType.DATETIME]: {
name: "datetime",
type: FieldType.DATETIME,
},
[FieldType.LINK]: {
name: "link",
type: FieldType.LINK,
tableId: simpleTable._id!,
relationshipType: RelationshipType.ONE_TO_MANY,
fieldName: "link",
},
[FieldType.FORMULA]: {
name: "formula",
type: FieldType.FORMULA,
formula: "any formula",
},
[FieldType.BARCODEQR]: {
name: "barcodeqr",
type: FieldType.BARCODEQR,
},
[FieldType.BIGINT]: {
name: "bigint",
type: FieldType.BIGINT,
},
[FieldType.BB_REFERENCE]: {
name: "bb_reference",
type: FieldType.BB_REFERENCE,
subtype: FieldSubtype.USERS,
},
}
await config.api.table.save(
tableForDatasource(datasource, { tableForDatasource(datasource, {
name: generator.guid(), name: "full",
schema: fullSchema, schema: fullSchema,
}) })
) )
@ -362,9 +359,6 @@ describe("/datasources", () => {
(acc, [fieldName, field]) => { (acc, [fieldName, field]) => {
acc[fieldName] = expect.objectContaining({ acc[fieldName] = expect.objectContaining({
...field, ...field,
externalType: expect.not.stringMatching(
new RegExp(`^${field.externalType || ""}$`)
),
}) })
return acc return acc
}, },

View File

@ -15,7 +15,28 @@ const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`
const ROW_ID_REGEX = /^\[.*]$/g const ROW_ID_REGEX = /^\[.*]$/g
const ENCODED_SPACE = encodeURIComponent(" ") const ENCODED_SPACE = encodeURIComponent(" ")
const SQL_NUMBER_TYPE_MAP = { type PrimitiveTypes =
| FieldType.STRING
| FieldType.NUMBER
| FieldType.BOOLEAN
| FieldType.DATETIME
| FieldType.JSON
| FieldType.BIGINT
| FieldType.OPTIONS
function isPrimitiveType(type: FieldType): type is PrimitiveTypes {
return [
FieldType.STRING,
FieldType.NUMBER,
FieldType.BOOLEAN,
FieldType.DATETIME,
FieldType.JSON,
FieldType.BIGINT,
FieldType.OPTIONS,
].includes(type)
}
const SQL_NUMBER_TYPE_MAP: Record<string, PrimitiveTypes> = {
integer: FieldType.NUMBER, integer: FieldType.NUMBER,
int: FieldType.NUMBER, int: FieldType.NUMBER,
decimal: FieldType.NUMBER, decimal: FieldType.NUMBER,
@ -35,7 +56,7 @@ const SQL_NUMBER_TYPE_MAP = {
smallmoney: FieldType.NUMBER, smallmoney: FieldType.NUMBER,
} }
const SQL_DATE_TYPE_MAP = { const SQL_DATE_TYPE_MAP: Record<string, PrimitiveTypes> = {
timestamp: FieldType.DATETIME, timestamp: FieldType.DATETIME,
time: FieldType.DATETIME, time: FieldType.DATETIME,
datetime: FieldType.DATETIME, datetime: FieldType.DATETIME,
@ -46,7 +67,7 @@ const SQL_DATE_TYPE_MAP = {
const SQL_DATE_ONLY_TYPES = ["date"] const SQL_DATE_ONLY_TYPES = ["date"]
const SQL_TIME_ONLY_TYPES = ["time"] const SQL_TIME_ONLY_TYPES = ["time"]
const SQL_STRING_TYPE_MAP = { const SQL_STRING_TYPE_MAP: Record<string, PrimitiveTypes> = {
varchar: FieldType.STRING, varchar: FieldType.STRING,
char: FieldType.STRING, char: FieldType.STRING,
nchar: FieldType.STRING, nchar: FieldType.STRING,
@ -58,22 +79,22 @@ const SQL_STRING_TYPE_MAP = {
text: FieldType.STRING, text: FieldType.STRING,
} }
const SQL_BOOLEAN_TYPE_MAP = { const SQL_BOOLEAN_TYPE_MAP: Record<string, PrimitiveTypes> = {
boolean: FieldType.BOOLEAN, boolean: FieldType.BOOLEAN,
bit: FieldType.BOOLEAN, bit: FieldType.BOOLEAN,
tinyint: FieldType.BOOLEAN, tinyint: FieldType.BOOLEAN,
} }
const SQL_OPTIONS_TYPE_MAP = { const SQL_OPTIONS_TYPE_MAP: Record<string, PrimitiveTypes> = {
"user-defined": FieldType.OPTIONS, "user-defined": FieldType.OPTIONS,
} }
const SQL_MISC_TYPE_MAP = { const SQL_MISC_TYPE_MAP: Record<string, PrimitiveTypes> = {
json: FieldType.JSON, json: FieldType.JSON,
bigint: FieldType.BIGINT, bigint: FieldType.BIGINT,
} }
const SQL_TYPE_MAP = { const SQL_TYPE_MAP: Record<string, PrimitiveTypes> = {
...SQL_NUMBER_TYPE_MAP, ...SQL_NUMBER_TYPE_MAP,
...SQL_DATE_TYPE_MAP, ...SQL_DATE_TYPE_MAP,
...SQL_STRING_TYPE_MAP, ...SQL_STRING_TYPE_MAP,
@ -317,6 +338,80 @@ function shouldCopySpecialColumn(
return fetchedIsNumber && column.type === FieldType.BOOLEAN return fetchedIsNumber && column.type === FieldType.BOOLEAN
} }
enum CopyAction {
ALWAYS_KEEP = "alwaysKeep",
COPY_IF_TYPE = "copyIfType",
}
SQL_TYPE_MAP
const SqlCopyTypeByFieldMapping: Record<
FieldType,
| { action: CopyAction.ALWAYS_KEEP }
| { action: CopyAction.COPY_IF_TYPE; types: PrimitiveTypes[] }
> = {
[FieldType.LINK]: { action: CopyAction.ALWAYS_KEEP },
[FieldType.FORMULA]: { action: CopyAction.ALWAYS_KEEP },
[FieldType.STRING]: {
action: CopyAction.COPY_IF_TYPE,
types: [FieldType.STRING],
},
[FieldType.OPTIONS]: {
action: CopyAction.COPY_IF_TYPE,
types: [FieldType.STRING],
},
[FieldType.LONGFORM]: {
action: CopyAction.COPY_IF_TYPE,
types: [FieldType.STRING],
},
[FieldType.NUMBER]: {
action: CopyAction.COPY_IF_TYPE,
types: [FieldType.BOOLEAN, FieldType.NUMBER],
},
[FieldType.BOOLEAN]: {
action: CopyAction.COPY_IF_TYPE,
types: [FieldType.BOOLEAN, FieldType.NUMBER],
},
[FieldType.ARRAY]: {
action: CopyAction.COPY_IF_TYPE,
types: [FieldType.JSON, FieldType.STRING],
},
[FieldType.DATETIME]: {
action: CopyAction.COPY_IF_TYPE,
types: [FieldType.DATETIME, FieldType.STRING],
},
[FieldType.ATTACHMENTS]: {
action: CopyAction.COPY_IF_TYPE,
types: [FieldType.JSON, FieldType.STRING],
},
[FieldType.ATTACHMENT_SINGLE]: {
action: CopyAction.COPY_IF_TYPE,
types: [FieldType.JSON, FieldType.STRING],
},
[FieldType.AUTO]: {
action: CopyAction.ALWAYS_KEEP,
},
[FieldType.JSON]: {
action: CopyAction.COPY_IF_TYPE,
types: [FieldType.JSON, FieldType.STRING],
},
[FieldType.INTERNAL]: {
action: CopyAction.ALWAYS_KEEP,
},
[FieldType.BARCODEQR]: {
action: CopyAction.COPY_IF_TYPE,
types: [FieldType.STRING],
},
[FieldType.BIGINT]: {
action: CopyAction.COPY_IF_TYPE,
types: [FieldType.BIGINT, FieldType.NUMBER],
},
[FieldType.BB_REFERENCE]: {
action: CopyAction.COPY_IF_TYPE,
types: [FieldType.JSON, FieldType.STRING],
},
}
/** /**
* Looks for columns which need to be copied over into the new table definitions, like relationships, * Looks for columns which need to be copied over into the new table definitions, like relationships,
* options types and views. * options types and views.
@ -338,6 +433,9 @@ function copyExistingPropsOver(
if (entities[tableName]?.created) { if (entities[tableName]?.created) {
table.created = entities[tableName]?.created table.created = entities[tableName]?.created
} }
if (entities[tableName]?.constrained) {
table.constrained = entities[tableName]?.constrained
}
table.views = entities[tableName].views table.views = entities[tableName].views
@ -351,41 +449,23 @@ function copyExistingPropsOver(
const existingColumnType = column?.type const existingColumnType = column?.type
const updatedColumnType = table.schema[key]?.type const updatedColumnType = table.schema[key]?.type
// If the db column type changed to a non-compatible one, we want to re-fetch it const map = SqlCopyTypeByFieldMapping[existingColumnType]
if (
updatedColumnType && let keepExistingSchema = map.action === CopyAction.ALWAYS_KEEP
updatedColumnType !== existingColumnType && if (map.action === CopyAction.COPY_IF_TYPE) {
!SWITCHABLE_TYPES[updatedColumnType]?.includes(existingColumnType) keepExistingSchema =
) { isPrimitiveType(updatedColumnType) &&
continue table.schema[key] &&
map.types?.includes(updatedColumnType)
} }
if ( if (keepExistingSchema) {
column.type === FieldType.LINK && table.schema[key] = {
!shouldCopyRelationship(column, tableIds) ...existingTableSchema[key],
) { externalType:
continue existingTableSchema[key].externalType ||
} table.schema[key]?.externalType,
}
const specialTypes = [
FieldType.OPTIONS,
FieldType.LONGFORM,
FieldType.ARRAY,
FieldType.FORMULA,
FieldType.BB_REFERENCE,
]
if (
specialTypes.includes(column.type) &&
!shouldCopySpecialColumn(column, table.schema[key])
) {
continue
}
table.schema[key] = {
...existingTableSchema[key],
externalType:
existingTableSchema[key].externalType ||
table.schema[key]?.externalType,
} }
} }
} }