diff --git a/packages/backend-core/src/cache/user.ts b/packages/backend-core/src/cache/user.ts index b3fd7c08cd..481d3691e4 100644 --- a/packages/backend-core/src/cache/user.ts +++ b/packages/backend-core/src/cache/user.ts @@ -120,7 +120,7 @@ export async function getUsers( ): Promise<{ users: User[]; notFoundIds?: string[] }> { const client = await redis.getUserClient() // try cache - let usersFromCache = await client.bulkGet(userIds) + let usersFromCache = await client.bulkGet(userIds) const missingUsersFromCache = userIds.filter(uid => !usersFromCache[uid]) const users = Object.values(usersFromCache) let notFoundIds diff --git a/packages/backend-core/src/redis/redis.ts b/packages/backend-core/src/redis/redis.ts index 78817d0aa0..e7755f275d 100644 --- a/packages/backend-core/src/redis/redis.ts +++ b/packages/backend-core/src/redis/redis.ts @@ -242,7 +242,7 @@ class RedisWrapper { } } - async bulkGet(keys: string[]) { + async bulkGet(keys: string[]) { const db = this._db if (keys.length === 0) { return {} @@ -250,7 +250,7 @@ class RedisWrapper { const prefixedKeys = keys.map(key => addDbPrefix(db, key)) let response = await this.getClient().mget(prefixedKeys) if (Array.isArray(response)) { - let final: Record = {} + let final: Record = {} let count = 0 for (let result of response) { if (result) { diff --git a/packages/backend-core/tests/extra/DBTestConfiguration.ts b/packages/backend-core/tests/extra/DBTestConfiguration.ts index a2550a6e24..99a5bcba46 100644 --- a/packages/backend-core/tests/extra/DBTestConfiguration.ts +++ b/packages/backend-core/tests/extra/DBTestConfiguration.ts @@ -18,7 +18,7 @@ class DBTestConfiguration { // TENANCY - doInTenant(task: any) { + doInTenant(task: () => Promise) { return context.doInTenant(this.tenantId, () => { return task() }) diff --git a/packages/backend-core/tests/index.ts b/packages/backend-core/tests/index.ts index 50fc1dc431..cdbacc12d8 100644 --- a/packages/backend-core/tests/index.ts +++ b/packages/backend-core/tests/index.ts @@ -1 +1,2 @@ export * from "./core/utilities" +export * from "./extra" diff --git a/packages/bbui/src/Table/CellRenderer.svelte b/packages/bbui/src/Table/CellRenderer.svelte index c64b975884..4ad6e22d7e 100644 --- a/packages/bbui/src/Table/CellRenderer.svelte +++ b/packages/bbui/src/Table/CellRenderer.svelte @@ -25,6 +25,7 @@ longform: StringRenderer, array: ArrayRenderer, internal: InternalRenderer, + bb_reference: RelationshipRenderer, } $: type = getType(schema) $: customRenderer = customRenderers?.find(x => x.column === schema?.name) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 8f0c5a0b7e..7d77942815 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -33,6 +33,7 @@ import { getBindings } from "components/backend/DataTable/formula" import JSONSchemaModal from "./JSONSchemaModal.svelte" import { ValidColumnNameRegex } from "@budibase/shared-core" + import { FieldSubtype, FieldType } from "@budibase/types" import RelationshipSelector from "components/common/RelationshipSelector.svelte" const AUTO_TYPE = "auto" @@ -42,6 +43,11 @@ const NUMBER_TYPE = FIELDS.NUMBER.type const JSON_TYPE = FIELDS.JSON.type const DATE_TYPE = FIELDS.DATETIME.type + const BB_REFERENCE_TYPE = FieldType.BB_REFERENCE + const BB_USER_REFERENCE_TYPE = composeType( + BB_REFERENCE_TYPE, + FieldSubtype.USER + ) const dispatch = createEventDispatcher() const PROHIBITED_COLUMN_NAMES = ["type", "_id", "_rev", "tableId"] @@ -68,12 +74,39 @@ let jsonSchemaModal let allowedTypes = [] let editableColumn = { - type: "string", + type: fieldDefinitions.STRING.type, constraints: fieldDefinitions.STRING.constraints, // Initial value for column name in other table for linked records fieldName: $tables.selected.name, } + const bbRefTypeMapping = {} + + function composeType(fieldType, subtype) { + return `${fieldType}_${subtype}` + } + + // Handling fields with subtypes + fieldDefinitions = Object.entries(fieldDefinitions).reduce( + (p, [key, field]) => { + if (field.type === BB_REFERENCE_TYPE) { + const composedType = composeType(field.type, field.subtype) + p[key] = { + ...field, + type: composedType, + } + bbRefTypeMapping[composedType] = { + type: field.type, + subtype: field.subtype, + } + } else { + p[key] = field + } + return p + }, + {} + ) + $: if (primaryDisplay) { editableColumn.constraints.presence = { allowEmpty: false } } @@ -107,6 +140,7 @@ const initialiseField = (field, savingColumn) => { isCreating = !field + if (field && !savingColumn) { editableColumn = cloneDeep(field) originalName = editableColumn.name ? editableColumn.name + "" : null @@ -114,6 +148,14 @@ primaryDisplay = $tables.selected.primaryDisplay == null || $tables.selected.primaryDisplay === editableColumn.name + + const mapped = Object.entries(bbRefTypeMapping).find( + ([_, v]) => v.type === field.type && v.subtype === field.subtype + ) + if (mapped) { + editableColumn.type = mapped[0] + delete editableColumn.subtype + } } else if (!savingColumn) { let highestNumber = 0 Object.keys(table.schema).forEach(columnName => { @@ -130,6 +172,7 @@ editableColumn.name = "Column 01" } } + allowedTypes = getAllowedTypes() if (editableColumn.type === LINK_TYPE && editableColumn.tableId) { @@ -145,6 +188,8 @@ $: initialiseField(field, savingColumn) + $: isBBReference = !!bbRefTypeMapping[editableColumn.type] + $: checkConstraints(editableColumn) $: required = !!editableColumn?.constraints?.presence || primaryDisplay $: uneditable = @@ -220,6 +265,13 @@ let saveColumn = cloneDeep(editableColumn) + if (bbRefTypeMapping[saveColumn.type]) { + saveColumn = { + ...saveColumn, + ...bbRefTypeMapping[saveColumn.type], + } + } + if (saveColumn.type === AUTO_TYPE) { saveColumn = buildAutoColumn( $tables.selected.name, @@ -298,9 +350,10 @@ // Default relationships many to many if (editableColumn.type === LINK_TYPE) { editableColumn.relationshipType = RelationshipType.MANY_TO_MANY - } - if (editableColumn.type === FORMULA_TYPE) { + } else if (editableColumn.type === FORMULA_TYPE) { editableColumn.formulaType = "dynamic" + } else if (editableColumn.type === BB_USER_REFERENCE_TYPE) { + editableColumn.relationshipType = RelationshipType.ONE_TO_MANY } } @@ -339,7 +392,9 @@ ALLOWABLE_NUMBER_TYPES.indexOf(editableColumn.type) !== -1 ) { return ALLOWABLE_NUMBER_OPTIONS - } else if (!external) { + } + + if (!external) { return [ ...Object.values(fieldDefinitions), { name: "Auto Column", type: AUTO_TYPE }, @@ -360,6 +415,9 @@ if (!external || table.sql) { fields = [...fields, FIELDS.LINK, FIELDS.ARRAY] } + if (fieldDefinitions.USER) { + fields.push(fieldDefinitions.USER) + } return fields } } @@ -613,6 +671,17 @@ + {:else if isBBReference} + + (editableColumn.relationshipType = e.detail + ? RelationshipType.MANY_TO_MANY + : RelationshipType.ONE_TO_MANY)} + disabled={!isCreating} + thin + text="Allow multiple users" + /> {/if} {#if editableColumn.type === AUTO_TYPE || editableColumn.autocolumn}