Merge pull request #13594 from Budibase/budi-8123/usercolumn-migration
Fix user relationship to user column migration
This commit is contained in:
commit
7bcdcda10d
|
@ -13,7 +13,11 @@
|
||||||
Layout,
|
Layout,
|
||||||
AbsTooltip,
|
AbsTooltip,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { SWITCHABLE_TYPES, ValidColumnNameRegex } from "@budibase/shared-core"
|
import {
|
||||||
|
SWITCHABLE_TYPES,
|
||||||
|
ValidColumnNameRegex,
|
||||||
|
helpers,
|
||||||
|
} from "@budibase/shared-core"
|
||||||
import { createEventDispatcher, getContext, onMount } from "svelte"
|
import { createEventDispatcher, getContext, onMount } from "svelte"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { tables, datasources } from "stores/builder"
|
import { tables, datasources } from "stores/builder"
|
||||||
|
@ -29,7 +33,11 @@
|
||||||
import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte"
|
import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte"
|
||||||
import { getBindings } from "components/backend/DataTable/formula"
|
import { getBindings } from "components/backend/DataTable/formula"
|
||||||
import JSONSchemaModal from "./JSONSchemaModal.svelte"
|
import JSONSchemaModal from "./JSONSchemaModal.svelte"
|
||||||
import { FieldType, SourceName } from "@budibase/types"
|
import {
|
||||||
|
BBReferenceFieldSubType,
|
||||||
|
FieldType,
|
||||||
|
SourceName,
|
||||||
|
} from "@budibase/types"
|
||||||
import RelationshipSelector from "components/common/RelationshipSelector.svelte"
|
import RelationshipSelector from "components/common/RelationshipSelector.svelte"
|
||||||
import { RowUtils } from "@budibase/frontend-core"
|
import { RowUtils } from "@budibase/frontend-core"
|
||||||
import ServerBindingPanel from "components/common/bindings/ServerBindingPanel.svelte"
|
import ServerBindingPanel from "components/common/bindings/ServerBindingPanel.svelte"
|
||||||
|
@ -356,9 +364,29 @@
|
||||||
|
|
||||||
function getAllowedTypes(datasource) {
|
function getAllowedTypes(datasource) {
|
||||||
if (originalName) {
|
if (originalName) {
|
||||||
const possibleTypes = SWITCHABLE_TYPES[field.type] || [
|
let possibleTypes = SWITCHABLE_TYPES[field.type] || [editableColumn.type]
|
||||||
editableColumn.type,
|
if (helpers.schema.isDeprecatedSingleUserColumn(editableColumn)) {
|
||||||
]
|
// This will handle old single users columns
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
...FIELDS.USER,
|
||||||
|
type: FieldType.BB_REFERENCE,
|
||||||
|
subtype: BBReferenceFieldSubType.USER,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} else if (
|
||||||
|
editableColumn.type === FieldType.BB_REFERENCE &&
|
||||||
|
editableColumn.subtype === BBReferenceFieldSubType.USERS
|
||||||
|
) {
|
||||||
|
// This will handle old multi users columns
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
...FIELDS.USERS,
|
||||||
|
subtype: BBReferenceFieldSubType.USERS,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
return Object.entries(FIELDS)
|
return Object.entries(FIELDS)
|
||||||
.filter(([_, field]) => possibleTypes.includes(field.type))
|
.filter(([_, field]) => possibleTypes.includes(field.type))
|
||||||
.map(([_, fieldDefinition]) => fieldDefinition)
|
.map(([_, fieldDefinition]) => fieldDefinition)
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
value: FieldType.ATTACHMENTS,
|
value: FieldType.ATTACHMENTS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "User",
|
label: "Users",
|
||||||
value: `${FieldType.BB_REFERENCE}${BBReferenceFieldSubType.USER}`,
|
value: `${FieldType.BB_REFERENCE}${BBReferenceFieldSubType.USER}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -165,20 +165,11 @@ export const FIELDS = {
|
||||||
BBReferenceFieldSubType.USER
|
BBReferenceFieldSubType.USER
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// Used for display of editing existing columns
|
|
||||||
DEPRECATED_USER: {
|
|
||||||
name: "User",
|
|
||||||
type: FieldType.BB_REFERENCE,
|
|
||||||
subtype: BBReferenceFieldSubType.USER,
|
|
||||||
icon: TypeIconMap[FieldType.BB_REFERENCE_SINGLE][
|
|
||||||
BBReferenceFieldSubType.USER
|
|
||||||
],
|
|
||||||
},
|
|
||||||
USERS: {
|
USERS: {
|
||||||
name: "User List",
|
name: "User List",
|
||||||
type: FieldType.BB_REFERENCE,
|
type: FieldType.BB_REFERENCE,
|
||||||
subtype: BBReferenceFieldSubType.USERS,
|
subtype: BBReferenceFieldSubType.USER,
|
||||||
icon: TypeIconMap[FieldType.BB_REFERENCE][BBReferenceFieldSubType.USERS],
|
icon: TypeIconMap[FieldType.BB_REFERENCE][BBReferenceFieldSubType.USER],
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "array",
|
type: "array",
|
||||||
},
|
},
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
// Persist the initial values as options, allowing them to be present in the dropdown,
|
// Persist the initial values as options, allowing them to be present in the dropdown,
|
||||||
// even if they are not in the inital fetch results
|
// even if they are not in the inital fetch results
|
||||||
let valueAsSafeArray = fieldState.value || []
|
let valueAsSafeArray = fieldState.value || []
|
||||||
if (!Array.isArray(fieldState.value)) {
|
if (!Array.isArray(valueAsSafeArray)) {
|
||||||
valueAsSafeArray = [fieldState.value]
|
valueAsSafeArray = [fieldState.value]
|
||||||
}
|
}
|
||||||
optionsObj = valueAsSafeArray.reduce((accumulator, value) => {
|
optionsObj = valueAsSafeArray.reduce((accumulator, value) => {
|
||||||
|
|
|
@ -1,20 +1,27 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
import { helpers } from "@budibase/shared-core"
|
||||||
import RelationshipCell from "./RelationshipCell.svelte"
|
import RelationshipCell from "./RelationshipCell.svelte"
|
||||||
import { BBReferenceFieldSubType, RelationshipType } from "@budibase/types"
|
import {
|
||||||
|
BBReferenceFieldSubType,
|
||||||
|
FieldType,
|
||||||
|
RelationshipType,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
export let api
|
export let api
|
||||||
export let hideCounter = false
|
export let hideCounter = false
|
||||||
|
export let schema
|
||||||
|
|
||||||
const { API } = getContext("grid")
|
const { API } = getContext("grid")
|
||||||
const { subtype } = $$props.schema
|
const { type, subtype } = schema
|
||||||
|
|
||||||
const schema = {
|
$: schema = {
|
||||||
...$$props.schema,
|
...$$props.schema,
|
||||||
// This is not really used, just adding some content to be able to render the relationship cell
|
// This is not really used, just adding some content to be able to render the relationship cell
|
||||||
tableId: "external",
|
tableId: "external",
|
||||||
relationshipType:
|
relationshipType:
|
||||||
subtype === BBReferenceFieldSubType.USER
|
type === FieldType.BB_REFERENCE_SINGLE ||
|
||||||
|
helpers.schema.isDeprecatedSingleUserColumn(schema)
|
||||||
? RelationshipType.ONE_TO_MANY
|
? RelationshipType.ONE_TO_MANY
|
||||||
: RelationshipType.MANY_TO_MANY,
|
: RelationshipType.MANY_TO_MANY,
|
||||||
}
|
}
|
||||||
|
@ -45,7 +52,7 @@
|
||||||
|
|
||||||
<RelationshipCell
|
<RelationshipCell
|
||||||
bind:api
|
bind:api
|
||||||
{...$$props}
|
{...$$restProps}
|
||||||
{schema}
|
{schema}
|
||||||
{searchFunction}
|
{searchFunction}
|
||||||
primaryDisplay={"email"}
|
primaryDisplay={"email"}
|
||||||
|
|
|
@ -7,11 +7,6 @@
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import { ValidColumnNameRegex } from "@budibase/shared-core"
|
import { ValidColumnNameRegex } from "@budibase/shared-core"
|
||||||
import {
|
|
||||||
BBReferenceFieldSubType,
|
|
||||||
FieldType,
|
|
||||||
RelationshipType,
|
|
||||||
} from "@budibase/types"
|
|
||||||
|
|
||||||
const { API, definition, rows } = getContext("grid")
|
const { API, definition, rows } = getContext("grid")
|
||||||
|
|
||||||
|
@ -33,20 +28,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const migrateUserColumn = async () => {
|
const migrateUserColumn = async () => {
|
||||||
let subtype = BBReferenceFieldSubType.USERS
|
|
||||||
if (column.schema.relationshipType === RelationshipType.ONE_TO_MANY) {
|
|
||||||
subtype = BBReferenceFieldSubType.USER
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await API.migrateColumn({
|
await API.migrateColumn({
|
||||||
tableId: $definition._id,
|
tableId: $definition._id,
|
||||||
oldColumn: column.schema,
|
oldColumn: column.schema.name,
|
||||||
newColumn: {
|
newColumn: newColumnName,
|
||||||
name: newColumnName,
|
|
||||||
type: FieldType.BB_REFERENCE,
|
|
||||||
subtype,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
notifications.success("Column migrated")
|
notifications.success("Column migrated")
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { helpers } from "@budibase/shared-core"
|
||||||
import { TypeIconMap } from "../../../constants"
|
import { TypeIconMap } from "../../../constants"
|
||||||
|
|
||||||
export const getColor = (idx, opacity = 0.3) => {
|
export const getColor = (idx, opacity = 0.3) => {
|
||||||
|
@ -11,8 +12,12 @@ export const getColumnIcon = column => {
|
||||||
if (column.schema.autocolumn) {
|
if (column.schema.autocolumn) {
|
||||||
return "MagicWand"
|
return "MagicWand"
|
||||||
}
|
}
|
||||||
const { type, subtype } = column.schema
|
|
||||||
|
|
||||||
|
if (helpers.schema.isDeprecatedSingleUserColumn(column.schema)) {
|
||||||
|
return "User"
|
||||||
|
}
|
||||||
|
|
||||||
|
const { type, subtype } = column.schema
|
||||||
const result =
|
const result =
|
||||||
typeof TypeIconMap[type] === "object" && subtype
|
typeof TypeIconMap[type] === "object" && subtype
|
||||||
? TypeIconMap[type][subtype]
|
? TypeIconMap[type][subtype]
|
||||||
|
|
|
@ -132,7 +132,7 @@ export const TypeIconMap = {
|
||||||
[FieldType.BIGINT]: "TagBold",
|
[FieldType.BIGINT]: "TagBold",
|
||||||
[FieldType.AUTO]: "MagicWand",
|
[FieldType.AUTO]: "MagicWand",
|
||||||
[FieldType.BB_REFERENCE]: {
|
[FieldType.BB_REFERENCE]: {
|
||||||
[BBReferenceFieldSubType.USER]: "User",
|
[BBReferenceFieldSubType.USER]: "UserGroup",
|
||||||
[BBReferenceFieldSubType.USERS]: "UserGroup",
|
[BBReferenceFieldSubType.USERS]: "UserGroup",
|
||||||
},
|
},
|
||||||
[FieldType.BB_REFERENCE_SINGLE]: {
|
[FieldType.BB_REFERENCE_SINGLE]: {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// need to handle table name + field or just field, depending on if relationships used
|
// need to handle table name + field or just field, depending on if relationships used
|
||||||
import { FieldType, Row, Table } from "@budibase/types"
|
import { FieldType, Row, Table } from "@budibase/types"
|
||||||
|
import { helpers } from "@budibase/shared-core"
|
||||||
import { generateRowIdField } from "../../../../integrations/utils"
|
import { generateRowIdField } from "../../../../integrations/utils"
|
||||||
import { CONSTANT_INTERNAL_ROW_COLS } from "../../../../db/utils"
|
import { CONSTANT_INTERNAL_ROW_COLS } from "../../../../db/utils"
|
||||||
|
|
||||||
|
@ -111,8 +112,10 @@ export function fixArrayTypes(row: Row, table: Table) {
|
||||||
try {
|
try {
|
||||||
row[fieldName] = JSON.parse(row[fieldName])
|
row[fieldName] = JSON.parse(row[fieldName])
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// couldn't convert back to array, ignore
|
if (!helpers.schema.isDeprecatedSingleUserColumn(schema)) {
|
||||||
delete row[fieldName]
|
// couldn't convert back to array, ignore
|
||||||
|
delete row[fieldName]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,5 +180,5 @@ export async function migrate(ctx: UserCtx<MigrateRequest, MigrateResponse>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
ctx.body = { message: `Column ${oldColumn.name} migrated.` }
|
ctx.body = { message: `Column ${oldColumn} migrated.` }
|
||||||
}
|
}
|
||||||
|
|
|
@ -493,16 +493,16 @@ describe.each([
|
||||||
)
|
)
|
||||||
|
|
||||||
await config.api.table.migrate(table._id!, {
|
await config.api.table.migrate(table._id!, {
|
||||||
oldColumn: table.schema["user relationship"],
|
oldColumn: "user relationship",
|
||||||
newColumn: {
|
newColumn: "user column",
|
||||||
name: "user column",
|
|
||||||
type: FieldType.BB_REFERENCE,
|
|
||||||
subtype: BBReferenceFieldSubType.USER,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const migratedTable = await config.api.table.get(table._id!)
|
const migratedTable = await config.api.table.get(table._id!)
|
||||||
expect(migratedTable.schema["user column"]).toBeDefined()
|
expect(migratedTable.schema["user column"]).toEqual({
|
||||||
|
name: "user column",
|
||||||
|
type: FieldType.BB_REFERENCE_SINGLE,
|
||||||
|
subtype: BBReferenceFieldSubType.USER,
|
||||||
|
})
|
||||||
expect(migratedTable.schema["user relationship"]).not.toBeDefined()
|
expect(migratedTable.schema["user relationship"]).not.toBeDefined()
|
||||||
|
|
||||||
const migratedRows = await config.api.row.fetch(table._id!)
|
const migratedRows = await config.api.row.fetch(table._id!)
|
||||||
|
@ -515,7 +515,7 @@ describe.each([
|
||||||
expect(migratedRow["user column"]).toBeDefined()
|
expect(migratedRow["user column"]).toBeDefined()
|
||||||
expect(migratedRow["user relationship"]).not.toBeDefined()
|
expect(migratedRow["user relationship"]).not.toBeDefined()
|
||||||
expect(row["user relationship"][0]._id).toEqual(
|
expect(row["user relationship"][0]._id).toEqual(
|
||||||
migratedRow["user column"][0]._id
|
migratedRow["user column"]._id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -558,16 +558,19 @@ describe.each([
|
||||||
)
|
)
|
||||||
|
|
||||||
await config.api.table.migrate(table._id!, {
|
await config.api.table.migrate(table._id!, {
|
||||||
oldColumn: table.schema["user relationship"],
|
oldColumn: "user relationship",
|
||||||
newColumn: {
|
newColumn: "user column",
|
||||||
name: "user column",
|
|
||||||
type: FieldType.BB_REFERENCE,
|
|
||||||
subtype: BBReferenceFieldSubType.USERS,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const migratedTable = await config.api.table.get(table._id!)
|
const migratedTable = await config.api.table.get(table._id!)
|
||||||
expect(migratedTable.schema["user column"]).toBeDefined()
|
expect(migratedTable.schema["user column"]).toEqual({
|
||||||
|
name: "user column",
|
||||||
|
type: FieldType.BB_REFERENCE,
|
||||||
|
subtype: BBReferenceFieldSubType.USER,
|
||||||
|
constraints: {
|
||||||
|
type: "array",
|
||||||
|
},
|
||||||
|
})
|
||||||
expect(migratedTable.schema["user relationship"]).not.toBeDefined()
|
expect(migratedTable.schema["user relationship"]).not.toBeDefined()
|
||||||
|
|
||||||
const migratedRow = await config.api.row.get(table._id!, testRow._id!)
|
const migratedRow = await config.api.row.get(table._id!, testRow._id!)
|
||||||
|
@ -610,16 +613,19 @@ describe.each([
|
||||||
})
|
})
|
||||||
|
|
||||||
await config.api.table.migrate(table._id!, {
|
await config.api.table.migrate(table._id!, {
|
||||||
oldColumn: table.schema["user relationship"],
|
oldColumn: "user relationship",
|
||||||
newColumn: {
|
newColumn: "user column",
|
||||||
name: "user column",
|
|
||||||
type: FieldType.BB_REFERENCE,
|
|
||||||
subtype: BBReferenceFieldSubType.USERS,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const migratedTable = await config.api.table.get(table._id!)
|
const migratedTable = await config.api.table.get(table._id!)
|
||||||
expect(migratedTable.schema["user column"]).toBeDefined()
|
expect(migratedTable.schema["user column"]).toEqual({
|
||||||
|
name: "user column",
|
||||||
|
type: FieldType.BB_REFERENCE,
|
||||||
|
subtype: BBReferenceFieldSubType.USER,
|
||||||
|
constraints: {
|
||||||
|
type: "array",
|
||||||
|
},
|
||||||
|
})
|
||||||
expect(migratedTable.schema["user relationship"]).not.toBeDefined()
|
expect(migratedTable.schema["user relationship"]).not.toBeDefined()
|
||||||
|
|
||||||
const row1Migrated = await config.api.row.get(table._id!, row1._id!)
|
const row1Migrated = await config.api.row.get(table._id!, row1._id!)
|
||||||
|
@ -665,16 +671,19 @@ describe.each([
|
||||||
})
|
})
|
||||||
|
|
||||||
await config.api.table.migrate(table._id!, {
|
await config.api.table.migrate(table._id!, {
|
||||||
oldColumn: table.schema["user relationship"],
|
oldColumn: "user relationship",
|
||||||
newColumn: {
|
newColumn: "user column",
|
||||||
name: "user column",
|
|
||||||
type: FieldType.BB_REFERENCE,
|
|
||||||
subtype: BBReferenceFieldSubType.USERS,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const migratedTable = await config.api.table.get(table._id!)
|
const migratedTable = await config.api.table.get(table._id!)
|
||||||
expect(migratedTable.schema["user column"]).toBeDefined()
|
expect(migratedTable.schema["user column"]).toEqual({
|
||||||
|
name: "user column",
|
||||||
|
type: FieldType.BB_REFERENCE,
|
||||||
|
subtype: BBReferenceFieldSubType.USER,
|
||||||
|
constraints: {
|
||||||
|
type: "array",
|
||||||
|
},
|
||||||
|
})
|
||||||
expect(migratedTable.schema["user relationship"]).not.toBeDefined()
|
expect(migratedTable.schema["user relationship"]).not.toBeDefined()
|
||||||
|
|
||||||
const row1Migrated = await config.api.row.get(table._id!, row1._id!)
|
const row1Migrated = await config.api.row.get(table._id!, row1._id!)
|
||||||
|
@ -724,12 +733,8 @@ describe.each([
|
||||||
await config.api.table.migrate(
|
await config.api.table.migrate(
|
||||||
table._id!,
|
table._id!,
|
||||||
{
|
{
|
||||||
oldColumn: table.schema["user relationship"],
|
oldColumn: "user relationship",
|
||||||
newColumn: {
|
newColumn: "",
|
||||||
name: "",
|
|
||||||
type: FieldType.BB_REFERENCE,
|
|
||||||
subtype: BBReferenceFieldSubType.USERS,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{ status: 400 }
|
{ status: 400 }
|
||||||
)
|
)
|
||||||
|
@ -739,12 +744,8 @@ describe.each([
|
||||||
await config.api.table.migrate(
|
await config.api.table.migrate(
|
||||||
table._id!,
|
table._id!,
|
||||||
{
|
{
|
||||||
oldColumn: table.schema["user relationship"],
|
oldColumn: "user relationship",
|
||||||
newColumn: {
|
newColumn: "_id",
|
||||||
name: "_id",
|
|
||||||
type: FieldType.BB_REFERENCE,
|
|
||||||
subtype: BBReferenceFieldSubType.USERS,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{ status: 400 }
|
{ status: 400 }
|
||||||
)
|
)
|
||||||
|
@ -754,12 +755,8 @@ describe.each([
|
||||||
await config.api.table.migrate(
|
await config.api.table.migrate(
|
||||||
table._id!,
|
table._id!,
|
||||||
{
|
{
|
||||||
oldColumn: table.schema["user relationship"],
|
oldColumn: "user relationship",
|
||||||
newColumn: {
|
newColumn: "num",
|
||||||
name: "num",
|
|
||||||
type: FieldType.BB_REFERENCE,
|
|
||||||
subtype: BBReferenceFieldSubType.USERS,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{ status: 400 }
|
{ status: 400 }
|
||||||
)
|
)
|
||||||
|
@ -769,16 +766,8 @@ describe.each([
|
||||||
await config.api.table.migrate(
|
await config.api.table.migrate(
|
||||||
table._id!,
|
table._id!,
|
||||||
{
|
{
|
||||||
oldColumn: {
|
oldColumn: "not a column",
|
||||||
name: "not a column",
|
newColumn: "new column",
|
||||||
type: FieldType.BB_REFERENCE,
|
|
||||||
subtype: BBReferenceFieldSubType.USERS,
|
|
||||||
},
|
|
||||||
newColumn: {
|
|
||||||
name: "new column",
|
|
||||||
type: FieldType.BB_REFERENCE,
|
|
||||||
subtype: BBReferenceFieldSubType.USERS,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{ status: 400 }
|
{ status: 400 }
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,7 +12,6 @@ import SqlTableQueryBuilder from "./sqlTable"
|
||||||
import {
|
import {
|
||||||
BBReferenceFieldMetadata,
|
BBReferenceFieldMetadata,
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
BBReferenceFieldSubType,
|
|
||||||
FieldType,
|
FieldType,
|
||||||
JsonFieldMetadata,
|
JsonFieldMetadata,
|
||||||
Operation,
|
Operation,
|
||||||
|
@ -767,7 +766,8 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
|
||||||
return (
|
return (
|
||||||
field.type === FieldType.JSON ||
|
field.type === FieldType.JSON ||
|
||||||
(field.type === FieldType.BB_REFERENCE &&
|
(field.type === FieldType.BB_REFERENCE &&
|
||||||
field.subtype === BBReferenceFieldSubType.USERS)
|
// Handling old single user type
|
||||||
|
field.constraints?.type === "array")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Knex, knex } from "knex"
|
import { Knex, knex } from "knex"
|
||||||
import {
|
import {
|
||||||
BBReferenceFieldSubType,
|
|
||||||
FieldType,
|
FieldType,
|
||||||
NumberFieldMetadata,
|
NumberFieldMetadata,
|
||||||
Operation,
|
Operation,
|
||||||
|
@ -63,20 +62,6 @@ function generateSchema(
|
||||||
case FieldType.BB_REFERENCE_SINGLE:
|
case FieldType.BB_REFERENCE_SINGLE:
|
||||||
schema.text(key)
|
schema.text(key)
|
||||||
break
|
break
|
||||||
case FieldType.BB_REFERENCE: {
|
|
||||||
const subtype = column.subtype
|
|
||||||
switch (subtype) {
|
|
||||||
case BBReferenceFieldSubType.USER:
|
|
||||||
schema.text(key)
|
|
||||||
break
|
|
||||||
case BBReferenceFieldSubType.USERS:
|
|
||||||
schema.json(key)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw utils.unreachable(subtype)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case FieldType.NUMBER:
|
case FieldType.NUMBER:
|
||||||
// if meta is specified then this is a junction table entry
|
// if meta is specified then this is a junction table entry
|
||||||
if (column.meta && column.meta.toKey && column.meta.toTable) {
|
if (column.meta && column.meta.toKey && column.meta.toTable) {
|
||||||
|
@ -99,6 +84,7 @@ function generateSchema(
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case FieldType.ARRAY:
|
case FieldType.ARRAY:
|
||||||
|
case FieldType.BB_REFERENCE:
|
||||||
schema.json(key)
|
schema.json(key)
|
||||||
break
|
break
|
||||||
case FieldType.LINK:
|
case FieldType.LINK:
|
||||||
|
|
|
@ -99,15 +99,7 @@ export function searchInputMapping(table: Table, options: RowSearchParams) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case FieldType.BB_REFERENCE: {
|
case FieldType.BB_REFERENCE: {
|
||||||
const subtype = column.subtype
|
userColumnMapping(key, options)
|
||||||
switch (subtype) {
|
|
||||||
case BBReferenceFieldSubType.USER:
|
|
||||||
case BBReferenceFieldSubType.USERS:
|
|
||||||
userColumnMapping(key, options)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
utils.unreachable(subtype)
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import {
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
BBReferenceFieldSubType,
|
BBReferenceFieldSubType,
|
||||||
InternalTable,
|
InternalTable,
|
||||||
isBBReferenceField,
|
|
||||||
isRelationshipField,
|
isRelationshipField,
|
||||||
LinkDocument,
|
LinkDocument,
|
||||||
LinkInfo,
|
LinkInfo,
|
||||||
|
@ -12,6 +11,8 @@ import {
|
||||||
RelationshipType,
|
RelationshipType,
|
||||||
Row,
|
Row,
|
||||||
Table,
|
Table,
|
||||||
|
FieldType,
|
||||||
|
BBReferenceSingleFieldMetadata,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
import { isExternalTableID } from "../../../integrations/utils"
|
import { isExternalTableID } from "../../../integrations/utils"
|
||||||
|
@ -24,25 +25,58 @@ export interface MigrationResult {
|
||||||
|
|
||||||
export async function migrate(
|
export async function migrate(
|
||||||
table: Table,
|
table: Table,
|
||||||
oldColumn: FieldSchema,
|
oldColumnName: string,
|
||||||
newColumn: FieldSchema
|
newColumnName: string
|
||||||
): Promise<MigrationResult> {
|
): Promise<MigrationResult> {
|
||||||
if (newColumn.name in table.schema) {
|
if (newColumnName in table.schema) {
|
||||||
throw new BadRequestError(`Column "${newColumn.name}" already exists`)
|
throw new BadRequestError(`Column "${newColumnName}" already exists`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newColumn.name === "") {
|
if (newColumnName === "") {
|
||||||
throw new BadRequestError(`Column name cannot be empty`)
|
throw new BadRequestError(`Column name cannot be empty`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dbCore.isInternalColumnName(newColumn.name)) {
|
if (dbCore.isInternalColumnName(newColumnName)) {
|
||||||
throw new BadRequestError(`Column name cannot be a reserved column name`)
|
throw new BadRequestError(`Column name cannot be a reserved column name`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const oldColumn = table.schema[oldColumnName]
|
||||||
|
|
||||||
|
if (!oldColumn) {
|
||||||
|
throw new BadRequestError(
|
||||||
|
`Column "${oldColumnName}" does not exist on table "${table.name}"`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
oldColumn.type !== FieldType.LINK ||
|
||||||
|
oldColumn.tableId !== InternalTable.USER_METADATA
|
||||||
|
) {
|
||||||
|
throw new BadRequestError(
|
||||||
|
`Only user relationship migration columns is currently supported`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const type =
|
||||||
|
oldColumn.relationshipType === RelationshipType.ONE_TO_MANY
|
||||||
|
? FieldType.BB_REFERENCE_SINGLE
|
||||||
|
: FieldType.BB_REFERENCE
|
||||||
|
const newColumn: FieldSchema = {
|
||||||
|
name: newColumnName,
|
||||||
|
type,
|
||||||
|
subtype: BBReferenceFieldSubType.USER,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newColumn.type === FieldType.BB_REFERENCE) {
|
||||||
|
newColumn.constraints = {
|
||||||
|
type: "array",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
table.schema[newColumn.name] = newColumn
|
table.schema[newColumn.name] = newColumn
|
||||||
table = await sdk.tables.saveTable(table)
|
table = await sdk.tables.saveTable(table)
|
||||||
|
|
||||||
let migrator = getColumnMigrator(table, oldColumn, newColumn)
|
const migrator = getColumnMigrator(table, oldColumn, newColumn)
|
||||||
try {
|
try {
|
||||||
return await migrator.doMigration()
|
return await migrator.doMigration()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -75,11 +109,14 @@ function getColumnMigrator(
|
||||||
throw new BadRequestError(`Column "${oldColumn.name}" does not exist`)
|
throw new BadRequestError(`Column "${oldColumn.name}" does not exist`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isBBReferenceField(newColumn)) {
|
if (
|
||||||
|
newColumn.type !== FieldType.BB_REFERENCE_SINGLE &&
|
||||||
|
newColumn.type !== FieldType.BB_REFERENCE
|
||||||
|
) {
|
||||||
throw new BadRequestError(`Column "${newColumn.name}" is not a user column`)
|
throw new BadRequestError(`Column "${newColumn.name}" is not a user column`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newColumn.subtype !== "user" && newColumn.subtype !== "users") {
|
if (newColumn.subtype !== BBReferenceFieldSubType.USER) {
|
||||||
throw new BadRequestError(`Column "${newColumn.name}" is not a user column`)
|
throw new BadRequestError(`Column "${newColumn.name}" is not a user column`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +133,7 @@ function getColumnMigrator(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldColumn.relationshipType === RelationshipType.ONE_TO_MANY) {
|
if (oldColumn.relationshipType === RelationshipType.ONE_TO_MANY) {
|
||||||
if (newColumn.subtype !== BBReferenceFieldSubType.USER) {
|
if (newColumn.type !== FieldType.BB_REFERENCE_SINGLE) {
|
||||||
throw new BadRequestError(
|
throw new BadRequestError(
|
||||||
`Column "${oldColumn.name}" is a one-to-many column but "${newColumn.name}" is not a single user column`
|
`Column "${oldColumn.name}" is a one-to-many column but "${newColumn.name}" is not a single user column`
|
||||||
)
|
)
|
||||||
|
@ -107,22 +144,23 @@ function getColumnMigrator(
|
||||||
oldColumn.relationshipType === RelationshipType.MANY_TO_MANY ||
|
oldColumn.relationshipType === RelationshipType.MANY_TO_MANY ||
|
||||||
oldColumn.relationshipType === RelationshipType.MANY_TO_ONE
|
oldColumn.relationshipType === RelationshipType.MANY_TO_ONE
|
||||||
) {
|
) {
|
||||||
if (newColumn.subtype !== BBReferenceFieldSubType.USERS) {
|
if (newColumn.type !== FieldType.BB_REFERENCE) {
|
||||||
throw new BadRequestError(
|
throw new BadRequestError(
|
||||||
`Column "${oldColumn.name}" is a ${oldColumn.relationshipType} column but "${newColumn.name}" is not a multi user column`
|
`Column "${oldColumn.name}" is a ${oldColumn.relationshipType} column but "${newColumn.name}" is not a multi user column`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MultiUserColumnMigrator(table, oldColumn, newColumn)
|
return new MultiUserColumnMigrator(table, oldColumn, newColumn)
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new BadRequestError(`Unknown migration type`)
|
throw new BadRequestError(`Unknown migration type`)
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class UserColumnMigrator implements ColumnMigrator {
|
abstract class UserColumnMigrator<T> implements ColumnMigrator {
|
||||||
constructor(
|
constructor(
|
||||||
protected table: Table,
|
protected table: Table,
|
||||||
protected oldColumn: RelationshipFieldMetadata,
|
protected oldColumn: RelationshipFieldMetadata,
|
||||||
protected newColumn: BBReferenceFieldMetadata
|
protected newColumn: T
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
abstract updateRow(row: Row, linkInfo: LinkInfo): void
|
abstract updateRow(row: Row, linkInfo: LinkInfo): void
|
||||||
|
@ -192,7 +230,7 @@ abstract class UserColumnMigrator implements ColumnMigrator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SingleUserColumnMigrator extends UserColumnMigrator {
|
class SingleUserColumnMigrator extends UserColumnMigrator<BBReferenceSingleFieldMetadata> {
|
||||||
updateRow(row: Row, linkInfo: LinkInfo): void {
|
updateRow(row: Row, linkInfo: LinkInfo): void {
|
||||||
row[this.newColumn.name] = dbCore.getGlobalIDFromUserMetadataID(
|
row[this.newColumn.name] = dbCore.getGlobalIDFromUserMetadataID(
|
||||||
linkInfo.rowId
|
linkInfo.rowId
|
||||||
|
@ -200,7 +238,7 @@ class SingleUserColumnMigrator extends UserColumnMigrator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MultiUserColumnMigrator extends UserColumnMigrator {
|
class MultiUserColumnMigrator extends UserColumnMigrator<BBReferenceFieldMetadata> {
|
||||||
updateRow(row: Row, linkInfo: LinkInfo): void {
|
updateRow(row: Row, linkInfo: LinkInfo): void {
|
||||||
if (!row[this.newColumn.name]) {
|
if (!row[this.newColumn.name]) {
|
||||||
row[this.newColumn.name] = []
|
row[this.newColumn.name] = []
|
||||||
|
|
|
@ -129,7 +129,7 @@ export function parse(rows: Rows, schema: TableSchema): Rows {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { type: columnType, subtype: columnSubtype } = schema[columnName]
|
const { type: columnType } = schema[columnName]
|
||||||
if (columnType === FieldType.NUMBER) {
|
if (columnType === FieldType.NUMBER) {
|
||||||
// If provided must be a valid number
|
// If provided must be a valid number
|
||||||
parsedRow[columnName] = columnData ? Number(columnData) : columnData
|
parsedRow[columnName] = columnData ? Number(columnData) : columnData
|
||||||
|
@ -140,21 +140,9 @@ export function parse(rows: Rows, schema: TableSchema): Rows {
|
||||||
: columnData
|
: columnData
|
||||||
} else if (columnType === FieldType.BB_REFERENCE) {
|
} else if (columnType === FieldType.BB_REFERENCE) {
|
||||||
const parsedValues =
|
const parsedValues =
|
||||||
!!columnData && parseCsvExport<{ _id: string }[]>(columnData)
|
(!!columnData && parseCsvExport<{ _id: string }[]>(columnData)) || []
|
||||||
if (!parsedValues) {
|
|
||||||
parsedRow[columnName] = undefined
|
parsedRow[columnName] = parsedValues?.map(u => u._id)
|
||||||
} else {
|
|
||||||
switch (columnSubtype) {
|
|
||||||
case BBReferenceFieldSubType.USER:
|
|
||||||
parsedRow[columnName] = parsedValues[0]?._id
|
|
||||||
break
|
|
||||||
case BBReferenceFieldSubType.USERS:
|
|
||||||
parsedRow[columnName] = parsedValues.map(u => u._id)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
utils.unreachable(columnSubtype)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (columnType === FieldType.BB_REFERENCE_SINGLE) {
|
} else if (columnType === FieldType.BB_REFERENCE_SINGLE) {
|
||||||
const parsedValue =
|
const parsedValue =
|
||||||
columnData && parseCsvExport<{ _id: string }>(columnData)
|
columnData && parseCsvExport<{ _id: string }>(columnData)
|
||||||
|
@ -200,10 +188,6 @@ function isValidBBReference(
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subtype === BBReferenceFieldSubType.USER && userArray.length > 1) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const constainsWrongId = userArray.find(
|
const constainsWrongId = userArray.find(
|
||||||
user => !db.isGlobalUserID(user._id)
|
user => !db.isGlobalUserID(user._id)
|
||||||
)
|
)
|
||||||
|
|
|
@ -51,7 +51,7 @@ export const getValidOperatorsForType = (
|
||||||
value: string
|
value: string
|
||||||
label: string
|
label: string
|
||||||
}[] = []
|
}[] = []
|
||||||
const { type, subtype, formulaType } = fieldType
|
const { type, formulaType } = fieldType
|
||||||
if (type === FieldType.STRING) {
|
if (type === FieldType.STRING) {
|
||||||
ops = stringOps
|
ops = stringOps
|
||||||
} else if (type === FieldType.NUMBER || type === FieldType.BIGINT) {
|
} else if (type === FieldType.NUMBER || type === FieldType.BIGINT) {
|
||||||
|
@ -68,16 +68,9 @@ export const getValidOperatorsForType = (
|
||||||
ops = numOps
|
ops = numOps
|
||||||
} else if (type === FieldType.FORMULA && formulaType === FormulaType.STATIC) {
|
} else if (type === FieldType.FORMULA && formulaType === FormulaType.STATIC) {
|
||||||
ops = stringOps.concat([Op.MoreThan, Op.LessThan])
|
ops = stringOps.concat([Op.MoreThan, Op.LessThan])
|
||||||
} else if (
|
} else if (type === FieldType.BB_REFERENCE_SINGLE) {
|
||||||
(type === FieldType.BB_REFERENCE_SINGLE ||
|
|
||||||
type === FieldType.BB_REFERENCE) &&
|
|
||||||
subtype == BBReferenceFieldSubType.USER
|
|
||||||
) {
|
|
||||||
ops = [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty, Op.In]
|
ops = [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty, Op.In]
|
||||||
} else if (
|
} else if (type === FieldType.BB_REFERENCE) {
|
||||||
type === FieldType.BB_REFERENCE &&
|
|
||||||
subtype == BBReferenceFieldSubType.USERS
|
|
||||||
) {
|
|
||||||
ops = [Op.Contains, Op.NotContains, Op.ContainsAny, Op.Empty, Op.NotEmpty]
|
ops = [Op.Contains, Op.NotContains, Op.ContainsAny, Op.Empty, Op.NotEmpty]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
export * from "./helpers"
|
export * from "./helpers"
|
||||||
export * from "./integrations"
|
export * from "./integrations"
|
||||||
export * as cron from "./cron"
|
export * as cron from "./cron"
|
||||||
|
export * as schema from "./schema"
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import {
|
||||||
|
BBReferenceFieldSubType,
|
||||||
|
FieldSchema,
|
||||||
|
FieldType,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
export function isDeprecatedSingleUserColumn(schema: FieldSchema) {
|
||||||
|
const result =
|
||||||
|
schema.type === FieldType.BB_REFERENCE &&
|
||||||
|
schema.subtype === BBReferenceFieldSubType.USER &&
|
||||||
|
schema.constraints?.type !== "array"
|
||||||
|
return result
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
FieldSchema,
|
|
||||||
Row,
|
Row,
|
||||||
Table,
|
Table,
|
||||||
TableRequest,
|
TableRequest,
|
||||||
|
@ -31,8 +30,8 @@ export interface BulkImportResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MigrateRequest {
|
export interface MigrateRequest {
|
||||||
oldColumn: FieldSchema
|
oldColumn: string
|
||||||
newColumn: FieldSchema
|
newColumn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MigrateResponse {
|
export interface MigrateResponse {
|
||||||
|
|
|
@ -214,15 +214,3 @@ export function isManyToOne(
|
||||||
): field is ManyToOneRelationshipFieldMetadata {
|
): field is ManyToOneRelationshipFieldMetadata {
|
||||||
return field.relationshipType === RelationshipType.MANY_TO_ONE
|
return field.relationshipType === RelationshipType.MANY_TO_ONE
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isBBReferenceField(
|
|
||||||
field: FieldSchema
|
|
||||||
): field is BBReferenceFieldMetadata {
|
|
||||||
return field.type === FieldType.BB_REFERENCE
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isAttachmentField(
|
|
||||||
field: FieldSchema
|
|
||||||
): field is AttachmentFieldMetadata {
|
|
||||||
return field.type === FieldType.ATTACHMENTS
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue