1
0
Fork 0
mirror of synced 2024-06-29 19:41:03 +12:00

Merge branch 'master' into feature/deprecate-table-component

This commit is contained in:
Andrew Kingston 2024-03-21 19:15:46 +00:00 committed by GitHub
commit 322425cd54
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 139 additions and 77 deletions

View file

@ -1,5 +1,5 @@
{ {
"version": "2.22.8", "version": "2.22.11",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*", "packages/*",

View file

@ -34,7 +34,12 @@
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 { ValidColumnNameRegex } from "@budibase/shared-core" import { ValidColumnNameRegex } from "@budibase/shared-core"
import { FieldType, FieldSubtype, SourceName } from "@budibase/types" import {
FieldType,
FieldSubtype,
SourceName,
FieldTypeSubtypes,
} 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"
@ -191,8 +196,10 @@
// don't make field IDs for auto types // don't make field IDs for auto types
if (type === AUTO_TYPE || autocolumn) { if (type === AUTO_TYPE || autocolumn) {
return type.toUpperCase() return type.toUpperCase()
} else { } else if (type === FieldType.BB_REFERENCE) {
return `${type}${subtype || ""}`.toUpperCase() return `${type}${subtype || ""}`.toUpperCase()
} else {
return type.toUpperCase()
} }
} }
@ -705,17 +712,14 @@
/> />
{:else if editableColumn.type === FieldType.ATTACHMENT} {:else if editableColumn.type === FieldType.ATTACHMENT}
<Toggle <Toggle
value={editableColumn.constraints?.length?.maximum !== 1} value={editableColumn.subtype !== FieldTypeSubtypes.ATTACHMENT.SINGLE &&
// Checking config before the subtype was added
editableColumn.constraints?.length?.maximum !== 1}
on:change={e => { on:change={e => {
if (!e.detail) { if (!e.detail) {
editableColumn.constraints ??= { length: {} } editableColumn.subtype = FieldTypeSubtypes.ATTACHMENT.SINGLE
editableColumn.constraints.length ??= {}
editableColumn.constraints.length.maximum = 1
editableColumn.constraints.length.message =
"cannot contain multiple files"
} else { } else {
delete editableColumn.constraints?.length?.maximum delete editableColumn.subtype
delete editableColumn.constraints?.length?.message
} }
}} }}
thin thin

View file

@ -30,6 +30,8 @@ import {
View, View,
RelationshipFieldMetadata, RelationshipFieldMetadata,
FieldType, FieldType,
FieldTypeSubtypes,
AttachmentFieldMetadata,
} from "@budibase/types" } from "@budibase/types"
export async function clearColumns(table: Table, columnNames: string[]) { export async function clearColumns(table: Table, columnNames: string[]) {
@ -88,6 +90,27 @@ export async function checkForColumnUpdates(
// Update views // Update views
await checkForViewUpdates(updatedTable, deletedColumns, columnRename) await checkForViewUpdates(updatedTable, deletedColumns, columnRename)
} }
const changedAttachmentSubtypeColumns = Object.values(
updatedTable.schema
).filter(
(column): column is AttachmentFieldMetadata =>
column.type === FieldType.ATTACHMENT &&
column.subtype !== oldTable?.schema[column.name]?.subtype
)
for (const attachmentColumn of changedAttachmentSubtypeColumns) {
if (attachmentColumn.subtype === FieldTypeSubtypes.ATTACHMENT.SINGLE) {
attachmentColumn.constraints ??= { length: {} }
attachmentColumn.constraints.length ??= {}
attachmentColumn.constraints.length.maximum = 1
attachmentColumn.constraints.length.message =
"cannot contain multiple files"
} else {
delete attachmentColumn.constraints?.length?.maximum
delete attachmentColumn.constraints?.length?.message
}
}
return { rows: updatedRows, table: updatedTable } return { rows: updatedRows, table: updatedTable }
} }

View file

@ -60,7 +60,7 @@ function generateSchema(
schema.text(key) schema.text(key)
break break
case FieldType.BB_REFERENCE: { case FieldType.BB_REFERENCE: {
const subtype = column.subtype as FieldSubtype const subtype = column.subtype
switch (subtype) { switch (subtype) {
case FieldSubtype.USER: case FieldSubtype.USER:
schema.text(key) schema.text(key)

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,5 @@
const { // eslint-disable-next-line local-rules/no-budibase-imports
getJsHelperList, import { getJsHelperList } from "@budibase/string-templates/src/helpers/list"
} = require("../../../../string-templates/src/helpers/list.js")
export default { export default {
...getJsHelperList(), ...getJsHelperList(),

View file

@ -1,3 +1,3 @@
"use strict";var snippets=(()=>{var u=Object.create;var n=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var x=Object.getPrototypeOf,C=Object.prototype.hasOwnProperty;var l=(i,e)=>()=>(e||i((e={exports:{}}).exports,e),e.exports),W=(i,e)=>{for(var p in e)n(i,p,{get:e[p],enumerable:!0})},f=(i,e,p,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of h(e))!C.call(i,t)&&t!==p&&n(i,t,{get:()=>e[t],enumerable:!(r=a(e,t))||r.enumerable});return i};var d=(i,e,p)=>(p=i!=null?u(x(i)):{},f(e||!i||!i.__esModule?n(p,"default",{value:i,enumerable:!0}):p,i)),g=i=>f(n({},"__esModule",{value:!0}),i);var s=l((D,o)=>{o.exports.iifeWrapper=i=>`(function(){ "use strict";var snippets=(()=>{var p=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var s=Object.getOwnPropertyNames;var c=Object.prototype.hasOwnProperty;var u=(i,e)=>{for(var n in e)p(i,n,{get:e[n],enumerable:!0})},a=(i,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of s(e))!c.call(i,t)&&t!==n&&p(i,t,{get:()=>e[t],enumerable:!(r=f(e,t))||r.enumerable});return i};var h=i=>a(p({},"__esModule",{value:!0}),i);var C={};u(C,{default:()=>x});var o=i=>`(function(){
${i} ${i}
})();`});var w={};W(w,{default:()=>v});var c=d(s()),v=new Proxy({},{get:function(i,e){return e in snippetCache||(snippetCache[e]=[eval][0]((0,c.iifeWrapper)(snippetDefinitions[e]))),snippetCache[e]}});return g(w);})(); })();`;var x=new Proxy({},{get:function(i,e){return e in snippetCache||(snippetCache[e]=[eval][0](o(snippetDefinitions[e]))),snippetCache[e]}});return h(C);})();

View file

@ -67,7 +67,7 @@ export function searchInputMapping(table: Table, options: SearchParams) {
for (let [key, column] of Object.entries(table.schema)) { for (let [key, column] of Object.entries(table.schema)) {
switch (column.type) { switch (column.type) {
case FieldType.BB_REFERENCE: { case FieldType.BB_REFERENCE: {
const subtype = column.subtype as FieldSubtype const subtype = column.subtype
switch (subtype) { switch (subtype) {
case FieldSubtype.USER: case FieldSubtype.USER:
case FieldSubtype.USERS: case FieldSubtype.USERS:

View file

@ -7,7 +7,7 @@ const ROW_PREFIX = DocumentType.ROW + SEPARATOR
export async function processInputBBReferences( export async function processInputBBReferences(
value: string | string[] | { _id: string } | { _id: string }[], value: string | string[] | { _id: string } | { _id: string }[],
subtype: FieldSubtype subtype: FieldSubtype.USER | FieldSubtype.USERS
): Promise<string | string[] | null> { ): Promise<string | string[] | null> {
let referenceIds: string[] = [] let referenceIds: string[] = []
@ -61,7 +61,7 @@ export async function processInputBBReferences(
export async function processOutputBBReferences( export async function processOutputBBReferences(
value: string | string[], value: string | string[],
subtype: FieldSubtype subtype: FieldSubtype.USER | FieldSubtype.USERS
) { ) {
if (value === null || value === undefined) { if (value === null || value === undefined) {
// Already processed or nothing to process // Already processed or nothing to process

View file

@ -6,7 +6,6 @@ import { TYPE_TRANSFORM_MAP } from "./map"
import { import {
FieldType, FieldType,
AutoFieldSubType, AutoFieldSubType,
FieldSubtype,
Row, Row,
RowAttachment, RowAttachment,
Table, Table,
@ -159,10 +158,7 @@ export async function inputProcessing(
} }
if (field.type === FieldType.BB_REFERENCE && value) { if (field.type === FieldType.BB_REFERENCE && value) {
clonedRow[key] = await processInputBBReferences( clonedRow[key] = await processInputBBReferences(value, field.subtype)
value,
field.subtype as FieldSubtype
)
} }
} }
@ -238,7 +234,7 @@ export async function outputProcessing<T extends Row[] | Row>(
for (let row of enriched) { for (let row of enriched) {
row[property] = await processOutputBBReferences( row[property] = await processOutputBBReferences(
row[property], row[property],
column.subtype as FieldSubtype column.subtype
) )
} }
} }

View file

@ -1,26 +1,14 @@
import { FieldType, FieldSubtype } from "@budibase/types" import {
FieldType,
FieldSubtype,
TableSchema,
FieldSchema,
Row,
} from "@budibase/types"
import { ValidColumnNameRegex, utils } from "@budibase/shared-core" import { ValidColumnNameRegex, utils } from "@budibase/shared-core"
import { db } from "@budibase/backend-core" import { db } from "@budibase/backend-core"
import { parseCsvExport } from "../api/controllers/view/exporters" import { parseCsvExport } from "../api/controllers/view/exporters"
interface SchemaColumn {
readonly name: string
readonly type: FieldType
readonly subtype: FieldSubtype
readonly autocolumn?: boolean
readonly constraints?: {
presence: boolean
}
}
interface Schema {
readonly [index: string]: SchemaColumn
}
interface Row {
[index: string]: any
}
type Rows = Array<Row> type Rows = Array<Row>
interface SchemaValidation { interface SchemaValidation {
@ -34,12 +22,10 @@ interface ValidationResults {
errors: Record<string, string> errors: Record<string, string>
} }
export function isSchema(schema: any): schema is Schema { export function isSchema(schema: any): schema is TableSchema {
return ( return (
typeof schema === "object" && typeof schema === "object" &&
Object.values(schema).every(rawColumn => { Object.values<FieldSchema>(schema).every(column => {
const column = rawColumn as SchemaColumn
return ( return (
column !== null && column !== null &&
typeof column === "object" && typeof column === "object" &&
@ -54,7 +40,7 @@ export function isRows(rows: any): rows is Rows {
return Array.isArray(rows) && rows.every(row => typeof row === "object") return Array.isArray(rows) && rows.every(row => typeof row === "object")
} }
export function validate(rows: Rows, schema: Schema): ValidationResults { export function validate(rows: Rows, schema: TableSchema): ValidationResults {
const results: ValidationResults = { const results: ValidationResults = {
schemaValidation: {}, schemaValidation: {},
allValid: false, allValid: false,
@ -64,9 +50,11 @@ export function validate(rows: Rows, schema: Schema): ValidationResults {
rows.forEach(row => { rows.forEach(row => {
Object.entries(row).forEach(([columnName, columnData]) => { Object.entries(row).forEach(([columnName, columnData]) => {
const columnType = schema[columnName]?.type const {
const columnSubtype = schema[columnName]?.subtype type: columnType,
const isAutoColumn = schema[columnName]?.autocolumn subtype: columnSubtype,
autocolumn: isAutoColumn,
} = schema[columnName]
// If the column had an invalid value we don't want to override it // If the column had an invalid value we don't want to override it
if (results.schemaValidation[columnName] === false) { if (results.schemaValidation[columnName] === false) {
@ -123,7 +111,7 @@ export function validate(rows: Rows, schema: Schema): ValidationResults {
return results return results
} }
export function parse(rows: Rows, schema: Schema): Rows { export function parse(rows: Rows, schema: TableSchema): Rows {
return rows.map(row => { return rows.map(row => {
const parsedRow: Row = {} const parsedRow: Row = {}
@ -133,9 +121,7 @@ export function parse(rows: Rows, schema: Schema): Rows {
return return
} }
const columnType = schema[columnName].type const { type: columnType, subtype: columnSubtype } = schema[columnName]
const columnSubtype = schema[columnName].subtype
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
@ -172,7 +158,7 @@ export function parse(rows: Rows, schema: Schema): Rows {
function isValidBBReference( function isValidBBReference(
columnData: any, columnData: any,
columnSubtype: FieldSubtype columnSubtype: FieldSubtype.USER | FieldSubtype.USERS
): boolean { ): boolean {
switch (columnSubtype) { switch (columnSubtype) {
case FieldSubtype.USER: case FieldSubtype.USER:

View file

@ -18,7 +18,8 @@
"@budibase/backend-core/*": ["../backend-core/*"], "@budibase/backend-core/*": ["../backend-core/*"],
"@budibase/shared-core": ["../shared-core/src"], "@budibase/shared-core": ["../shared-core/src"],
"@budibase/pro": ["../pro/src"], "@budibase/pro": ["../pro/src"],
"@budibase/string-templates": ["../string-templates/src"] "@budibase/string-templates": ["../string-templates/src"],
"@budibase/string-templates/*": ["../string-templates/*"]
}, },
"allowArbitraryExtensions": true "allowArbitraryExtensions": true
}, },

View file

@ -1,17 +1,29 @@
import { date, duration } from "./date" import { date, duration } from "./date"
import { /*
math, @budibase/handlebars-helpers is not treeshakeable, so we can't use the barrel files.
array, Otherwise, we have issues when generating the isolated-vm bundle because of the treeshaking
number, */
url, /* eslint-disable local-rules/no-budibase-imports */
string, // @ts-expect-error
comparison, import math from "@budibase/handlebars-helpers/lib/math"
object, // @ts-expect-error
regex, import array from "@budibase/handlebars-helpers/lib/array"
uuid, // @ts-expect-error
// @ts-expect-error import number from "@budibase/handlebars-helpers/lib/number"
} from "@budibase/handlebars-helpers" // @ts-expect-error
import url from "@budibase/handlebars-helpers/lib/url"
// @ts-expect-error
import string from "@budibase/handlebars-helpers/lib/string"
// @ts-expect-error
import comparison from "@budibase/handlebars-helpers/lib/comparison"
// @ts-expect-error
import object from "@budibase/handlebars-helpers/lib/object"
// @ts-expect-error
import regex from "@budibase/handlebars-helpers/lib/regex"
// @ts-expect-error
import uuid from "@budibase/handlebars-helpers/lib/uuid"
/* eslint-enable local-rules/no-budibase-imports */
// https://github.com/evanw/esbuild/issues/56 // https://github.com/evanw/esbuild/issues/56
const externalCollections = { const externalCollections = {
@ -42,14 +54,14 @@ export function getJsHelperList() {
helpers = {} helpers = {}
for (let collection of Object.values(externalCollections)) { for (let collection of Object.values(externalCollections)) {
for (let [key, func] of Object.entries<any>(collection())) { for (let [key, func] of Object.entries<any>(collection)) {
// Handlebars injects the hbs options to the helpers by default. We are adding an empty {} as a last parameter to simulate it // Handlebars injects the hbs options to the helpers by default. We are adding an empty {} as a last parameter to simulate it
helpers[key] = (...props: any) => func(...props, {}) helpers[key] = (...props: any) => func(...props, {})
} }
} }
helpers = { helpers = {
...helpers, ...helpers,
addedHelpers, ...addedHelpers,
} }
for (const toRemove of helpersToRemoveForJs) { for (const toRemove of helpersToRemoveForJs) {

View file

@ -94,7 +94,7 @@ export async function processObject<T extends Record<string, any>>(
for (const key of Object.keys(object || {})) { for (const key of Object.keys(object || {})) {
if (object[key] != null) { if (object[key] != null) {
const val = object[key] const val = object[key]
let parsedValue let parsedValue = val
if (typeof val === "string") { if (typeof val === "string") {
parsedValue = await processString(object[key], context, opts) parsedValue = await processString(object[key], context, opts)
} else if (typeof val === "object") { } else if (typeof val === "object") {

View file

@ -104,6 +104,26 @@ describe("Test that the object processing works correctly", () => {
} }
expect(error).toBeNull() expect(error).toBeNull()
}) })
it("should be able to handle booleans", async () => {
const output = await processObject(
{
first: true,
second: "true",
third: "another string",
forth: "with {{ template }}",
},
{
template: "value",
}
)
expect(output).toEqual({
first: true,
second: "true",
third: "another string",
forth: "with value",
})
})
}) })
describe("check returning objects", () => { describe("check returning objects", () => {

View file

@ -38,11 +38,16 @@ export interface Row extends Document {
export enum FieldSubtype { export enum FieldSubtype {
USER = "user", USER = "user",
USERS = "users", USERS = "users",
SINGLE = "single",
} }
// The 'as' are required for typescript not to type the outputs as generic FieldSubtype
export const FieldTypeSubtypes = { export const FieldTypeSubtypes = {
BB_REFERENCE: { BB_REFERENCE: {
USER: FieldSubtype.USER, USER: FieldSubtype.USER as FieldSubtype.USER,
USERS: FieldSubtype.USERS, USERS: FieldSubtype.USERS as FieldSubtype.USERS,
},
ATTACHMENT: {
SINGLE: FieldSubtype.SINGLE as FieldSubtype.SINGLE,
}, },
} }

View file

@ -112,6 +112,12 @@ export interface BBReferenceFieldMetadata
relationshipType?: RelationshipType relationshipType?: RelationshipType
} }
export interface AttachmentFieldMetadata
extends Omit<BaseFieldSchema, "subtype"> {
type: FieldType.ATTACHMENT
subtype?: FieldSubtype.SINGLE
}
export interface FieldConstraints { export interface FieldConstraints {
type?: string type?: string
email?: boolean email?: boolean
@ -119,6 +125,7 @@ export interface FieldConstraints {
length?: { length?: {
minimum?: string | number | null minimum?: string | number | null
maximum?: string | number | null maximum?: string | number | null
message?: string
} }
numericality?: { numericality?: {
greaterThanOrEqualTo: string | null greaterThanOrEqualTo: string | null
@ -156,6 +163,8 @@ interface OtherFieldMetadata extends BaseFieldSchema {
| FieldType.FORMULA | FieldType.FORMULA
| FieldType.NUMBER | FieldType.NUMBER
| FieldType.LONGFORM | FieldType.LONGFORM
| FieldType.BB_REFERENCE
| FieldType.ATTACHMENT
> >
} }
@ -169,6 +178,7 @@ export type FieldSchema =
| LongFormFieldMetadata | LongFormFieldMetadata
| BBReferenceFieldMetadata | BBReferenceFieldMetadata
| JsonFieldMetadata | JsonFieldMetadata
| AttachmentFieldMetadata
export interface TableSchema { export interface TableSchema {
[key: string]: FieldSchema [key: string]: FieldSchema
@ -203,3 +213,9 @@ export function isBBReferenceField(
): field is BBReferenceFieldMetadata { ): field is BBReferenceFieldMetadata {
return field.type === FieldType.BB_REFERENCE return field.type === FieldType.BB_REFERENCE
} }
export function isAttachmentField(
field: FieldSchema
): field is AttachmentFieldMetadata {
return field.type === FieldType.ATTACHMENT
}