Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import user column type #12008

Merged
merged 17 commits into from
Oct 10, 2023
5 changes: 5 additions & 0 deletions packages/backend-core/src/docIds/ids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ export function generateGlobalUserID(id?: any) {
return `${DocumentType.USER}${SEPARATOR}${id || newid()}`
}

const isGlobalUserIDRegex = new RegExp(`^${DocumentType.USER}${SEPARATOR}.+`)
export function isGlobalUserID(id: string) {
return isGlobalUserIDRegex.test(id)
}

/**
* Generates a new user ID based on the passed in global ID.
* @param {string} globalId The ID of the global user.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@
label: "Long Form Text",
value: FIELDS.LONGFORM.type,
},

{
label: "User",
value: `${FIELDS.USER.type}${FIELDS.USER.subtype}`,
},
{
label: "Users",
value: `${FIELDS.USERS.type}${FIELDS.USERS.subtype}`,
},
]

$: {
Expand Down Expand Up @@ -143,7 +152,7 @@
<div class="field">
<span>{name}</span>
<Select
value={schema[name]?.type}
value={`${schema[name]?.type}${schema[name]?.subtype || ""}`}
options={typeOptions}
placeholder={null}
getOptionLabel={option => option.label}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,82 @@
export let displayColumn = null
export let promptUpload = false

const typeOptions = [
{
const typeOptions = {
[FIELDS.STRING.type]: {
label: "Text",
value: FIELDS.STRING.type,
config: {
type: FIELDS.STRING.type,
constraints: FIELDS.STRING.constraints,
},
},
{
[FIELDS.NUMBER.type]: {
label: "Number",
value: FIELDS.NUMBER.type,
config: {
type: FIELDS.NUMBER.type,
constraints: FIELDS.NUMBER.constraints,
},
},
{
[FIELDS.DATETIME.type]: {
label: "Date",
value: FIELDS.DATETIME.type,
config: {
type: FIELDS.DATETIME.type,
constraints: FIELDS.DATETIME.constraints,
},
},
{
[FIELDS.OPTIONS.type]: {
label: "Options",
value: FIELDS.OPTIONS.type,
config: {
type: FIELDS.OPTIONS.type,
constraints: FIELDS.OPTIONS.constraints,
},
},
{
[FIELDS.ARRAY.type]: {
label: "Multi-select",
value: FIELDS.ARRAY.type,
config: {
type: FIELDS.ARRAY.type,
constraints: FIELDS.ARRAY.constraints,
},
},
{
[FIELDS.BARCODEQR.type]: {
label: "Barcode/QR",
value: FIELDS.BARCODEQR.type,
config: {
type: FIELDS.BARCODEQR.type,
constraints: FIELDS.BARCODEQR.constraints,
},
},
{
[FIELDS.LONGFORM.type]: {
label: "Long Form Text",
value: FIELDS.LONGFORM.type,
config: {
type: FIELDS.LONGFORM.type,
constraints: FIELDS.LONGFORM.constraints,
},
},
]
user: {
label: "User",
value: "user",
config: {
type: FIELDS.USER.type,
subtype: FIELDS.USER.subtype,
constraints: FIELDS.USER.constraints,
},
},
users: {
label: "Users",
value: "users",
config: {
type: FIELDS.USERS.type,
subtype: FIELDS.USERS.subtype,
constraints: FIELDS.USERS.constraints,
},
},
}

let fileInput
let error = null
Expand All @@ -48,10 +94,12 @@
let validation = {}
let validateHash = ""
let errors = {}
let selectedColumnTypes = {}

$: displayColumnOptions = Object.keys(schema || {}).filter(column => {
return validation[column]
})

$: {
// binding in consumer is causing double renders here
const newValidateHash = JSON.stringify(rows) + JSON.stringify(schema)
Expand All @@ -72,6 +120,13 @@
rows = response.rows
schema = response.schema
fileName = response.fileName
selectedColumnTypes = Object.entries(response.schema).reduce(
(acc, [colName, fieldConfig]) => ({
...acc,
[colName]: fieldConfig.type,
}),
{}
)
} catch (e) {
loading = false
error = e
Expand All @@ -98,8 +153,10 @@
}

const handleChange = (name, e) => {
schema[name].type = e.detail
schema[name].constraints = FIELDS[e.detail.toUpperCase()].constraints
const { config } = typeOptions[e.detail]
schema[name].type = config.type
schema[name].subtype = config.subtype
schema[name].constraints = config.constraints
}

const openFileUpload = (promptUpload, fileInput) => {
Expand Down Expand Up @@ -142,9 +199,9 @@
<div class="field">
<span>{column.name}</span>
<Select
bind:value={column.type}
bind:value={selectedColumnTypes[column.name]}
on:change={e => handleChange(name, e)}
options={typeOptions}
options={Object.values(typeOptions)}
placeholder={null}
getOptionLabel={option => option.label}
getOptionValue={option => option.value}
Expand Down
6 changes: 5 additions & 1 deletion packages/server/src/api/controllers/row/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,10 @@ export async function destroy(ctx: UserCtx) {
}
const table = await sdk.tables.getTable(row.tableId)
// update the row to include full relationships before deleting them
row = await outputProcessing(table, row, { squash: false })
row = await outputProcessing(table, row, {
squash: false,
skipBBReferences: true,
})
// now remove the relationships
await linkRows.updateLinks({
eventType: linkRows.EventType.ROW_DELETE,
Expand Down Expand Up @@ -190,6 +193,7 @@ export async function bulkDestroy(ctx: UserCtx) {
// they need to be the full rows (including previous relationships) for automations
const processedRows = (await outputProcessing(table, rows, {
squash: false,
skipBBReferences: true,
})) as Row[]

// remove the relationships first
Expand Down
8 changes: 4 additions & 4 deletions packages/server/src/api/controllers/table/external.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { handleRequest } from "../row/external"
import { context, events } from "@budibase/backend-core"
import { isRows, isSchema, parse } from "../../../utilities/schema"
import {
AutoReason,
BulkImportRequest,
Datasource,
FieldSchema,
Operation,
Expand Down Expand Up @@ -374,10 +374,10 @@ export async function destroy(ctx: UserCtx) {
return tableToDelete
}

export async function bulkImport(ctx: UserCtx) {
export async function bulkImport(ctx: UserCtx<BulkImportRequest>) {
const table = await sdk.tables.getTable(ctx.params.tableId)
const { rows }: { rows: unknown } = ctx.request.body
const schema: unknown = table.schema
const { rows } = ctx.request.body
const schema = table.schema

if (!rows || !isRows(rows) || !isSchema(schema)) {
ctx.throw(400, "Provided data import information is invalid.")
Expand Down
3 changes: 2 additions & 1 deletion packages/server/src/api/controllers/table/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { isExternalTable, isSQL } from "../../../integrations/utils"
import { events } from "@budibase/backend-core"
import {
BulkImportRequest,
FetchTablesResponse,
SaveTableRequest,
SaveTableResponse,
Expand Down Expand Up @@ -97,7 +98,7 @@ export async function destroy(ctx: UserCtx) {
builderSocket?.emitTableDeletion(ctx, deletedTable)
}

export async function bulkImport(ctx: UserCtx) {
export async function bulkImport(ctx: UserCtx<BulkImportRequest>) {
const tableId = ctx.params.tableId
await pickApi({ tableId }).bulkImport(ctx)
// right now we don't trigger anything for bulk import because it
Expand Down
3 changes: 2 additions & 1 deletion packages/server/src/api/controllers/table/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from "../../../utilities/rowProcessor"
import { runStaticFormulaChecks } from "./bulkFormula"
import {
BulkImportRequest,
RenameColumn,
SaveTableRequest,
SaveTableResponse,
Expand Down Expand Up @@ -206,7 +207,7 @@ export async function destroy(ctx: any) {
return tableToDelete
}

export async function bulkImport(ctx: any) {
export async function bulkImport(ctx: UserCtx<BulkImportRequest>) {
const table = await sdk.tables.getTable(ctx.params.tableId)
const { rows, identifierFields } = ctx.request.body
await handleDataImport(ctx.user, table, rows, identifierFields)
Expand Down
16 changes: 11 additions & 5 deletions packages/server/src/api/controllers/table/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ import viewTemplate from "../view/viewBuilder"
import { cloneDeep } from "lodash/fp"
import { quotas } from "@budibase/pro"
import { events, context } from "@budibase/backend-core"
import { ContextUser, Datasource, SourceName, Table } from "@budibase/types"
import {
ContextUser,
Datasource,
Row,
SourceName,
Table,
} from "@budibase/types"

export async function clearColumns(table: any, columnNames: any) {
const db = context.getAppDB()
Expand Down Expand Up @@ -144,12 +150,12 @@ export async function importToRows(
}

export async function handleDataImport(
user: any,
table: any,
rows: any,
user: ContextUser,
table: Table,
rows: Row[],
identifierFields: Array<string> = []
) {
const schema: unknown = table.schema
const schema = table.schema

if (!rows || !isRows(rows) || !isSchema(schema)) {
return table
Expand Down
4 changes: 4 additions & 0 deletions packages/server/src/api/controllers/view/exporters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,7 @@ export enum Format {
export function isFormat(format: any): format is Format {
return Object.values(Format).includes(format as Format)
}

export function parseCsvExport<T>(value: string) {
return JSON.parse(value?.replace(/'/g, '"')) as T
}
3 changes: 0 additions & 3 deletions packages/server/src/api/routes/tests/row.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1578,9 +1578,6 @@ describe.each([
() => config.createUser(),
(row: Row) => ({
_id: row._id,
email: row.email,
firstName: row.firstName,
lastName: row.lastName,
primaryDisplay: row.email,
}),
],
Expand Down
9 changes: 7 additions & 2 deletions packages/server/src/api/routes/tests/table.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { generator } from "@budibase/backend-core/tests"
import { events, context } from "@budibase/backend-core"
import { FieldType, Table, ViewCalculation } from "@budibase/types"
import {
FieldType,
SaveTableRequest,
Table,
ViewCalculation,
} from "@budibase/types"
import { checkBuilderEndpoint } from "./utilities/TestFunctions"
import * as setup from "./utilities"
const { basicTable } = setup.structures
Expand Down Expand Up @@ -47,7 +52,7 @@ describe("/tables", () => {
})

it("creates a table via data import", async () => {
const table = basicTable()
const table: SaveTableRequest = basicTable()
table.rows = [{ name: "test-name", description: "test-desc" }]

const res = await createTable(table)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,6 @@ export async function processOutputBBReferences(
return users.map(u => ({
_id: u._id,
primaryDisplay: u.email,
email: u.email,
firstName: u.firstName,
lastName: u.lastName,
}))

default:
Expand Down
12 changes: 10 additions & 2 deletions packages/server/src/utilities/rowProcessor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,14 @@ export async function inputProcessing(
export async function outputProcessing<T extends Row[] | Row>(
table: Table,
rows: T,
opts: { squash?: boolean; preserveLinks?: boolean } = {
opts: {
squash?: boolean
preserveLinks?: boolean
skipBBReferences?: boolean
} = {
squash: true,
preserveLinks: false,
skipBBReferences: false,
}
): Promise<T> {
let safeRows: Row[]
Expand All @@ -230,7 +235,10 @@ export async function outputProcessing<T extends Row[] | Row>(
attachment.url = objectStore.getAppFileUrl(attachment.key)
})
}
} else if (column.type == FieldTypes.BB_REFERENCE) {
} else if (
!opts.skipBBReferences &&
column.type == FieldTypes.BB_REFERENCE
) {
for (let row of enriched) {
row[property] = await processOutputBBReferences(
row[property],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,6 @@ describe("bbReferenceProcessor", () => {
{
_id: user._id,
primaryDisplay: user.email,
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
},
])
expect(cacheGetUsersSpy).toBeCalledTimes(1)
Expand All @@ -207,9 +204,6 @@ describe("bbReferenceProcessor", () => {
[user1, user2].map(u => ({
_id: u._id,
primaryDisplay: u.email,
email: u.email,
firstName: u.firstName,
lastName: u.lastName,
}))
)
)
Expand Down
Loading
Loading