Type everywhere!

This commit is contained in:
Adria Navarro 2024-03-20 19:33:39 +01:00
parent 0b3a48b2b7
commit 0827cc6bda
7 changed files with 49 additions and 62 deletions

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)

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

@ -159,10 +159,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 +235,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

@ -41,12 +41,13 @@ export enum FieldSubtype {
SINGLE = "single", 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: { ATTACHMENT: {
SINGLE: FieldSubtype.SINGLE, SINGLE: FieldSubtype.SINGLE as FieldSubtype.SINGLE,
}, },
} }

View File

@ -17,13 +17,14 @@ export interface UIFieldMetadata {
} }
interface BaseRelationshipFieldMetadata interface BaseRelationshipFieldMetadata
extends Omit<BaseFieldSchema, "subtype"> { extends BaseFieldSchema<
AutoFieldSubType.CREATED_BY | AutoFieldSubType.UPDATED_BY | undefined
> {
type: FieldType.LINK type: FieldType.LINK
main?: boolean main?: boolean
fieldName: string fieldName: string
tableId: string tableId: string
tableRev?: string tableRev?: string
subtype?: AutoFieldSubType.CREATED_BY | AutoFieldSubType.UPDATED_BY
} }
// External tables use junction tables, internal tables don't require them // External tables use junction tables, internal tables don't require them
@ -60,18 +61,17 @@ export type RelationshipFieldMetadata =
| ManyToOneRelationshipFieldMetadata | ManyToOneRelationshipFieldMetadata
export interface AutoColumnFieldMetadata export interface AutoColumnFieldMetadata
extends Omit<BaseFieldSchema, "subtype"> { extends BaseFieldSchema<AutoFieldSubType | undefined> {
type: FieldType.AUTO type: FieldType.AUTO
autocolumn: true autocolumn: true
subtype?: AutoFieldSubType
lastID?: number lastID?: number
// if the column was turned to an auto-column for SQL, explains why (primary, foreign etc) // if the column was turned to an auto-column for SQL, explains why (primary, foreign etc)
autoReason?: AutoReason autoReason?: AutoReason
} }
export interface NumberFieldMetadata extends Omit<BaseFieldSchema, "subtype"> { export interface NumberFieldMetadata
extends BaseFieldSchema<AutoFieldSubType.AUTO_ID | undefined> {
type: FieldType.NUMBER type: FieldType.NUMBER
subtype?: AutoFieldSubType.AUTO_ID
lastID?: number lastID?: number
autoReason?: AutoReason.FOREIGN_KEY autoReason?: AutoReason.FOREIGN_KEY
// used specifically when Budibase generates external tables, this denotes if a number field // used specifically when Budibase generates external tables, this denotes if a number field
@ -82,16 +82,18 @@ export interface NumberFieldMetadata extends Omit<BaseFieldSchema, "subtype"> {
} }
} }
export interface JsonFieldMetadata extends Omit<BaseFieldSchema, "subtype"> { export interface JsonFieldMetadata
extends BaseFieldSchema<JsonFieldSubType.ARRAY | undefined> {
type: FieldType.JSON type: FieldType.JSON
subtype?: JsonFieldSubType.ARRAY
} }
export interface DateFieldMetadata extends Omit<BaseFieldSchema, "subtype"> { export interface DateFieldMetadata
extends BaseFieldSchema<
AutoFieldSubType.CREATED_AT | AutoFieldSubType.UPDATED_AT | undefined
> {
type: FieldType.DATETIME type: FieldType.DATETIME
ignoreTimezones?: boolean ignoreTimezones?: boolean
timeOnly?: boolean timeOnly?: boolean
subtype?: AutoFieldSubType.CREATED_AT | AutoFieldSubType.UPDATED_AT
} }
export interface LongFormFieldMetadata extends BaseFieldSchema { export interface LongFormFieldMetadata extends BaseFieldSchema {
@ -106,12 +108,17 @@ export interface FormulaFieldMetadata extends BaseFieldSchema {
} }
export interface BBReferenceFieldMetadata export interface BBReferenceFieldMetadata
extends Omit<BaseFieldSchema, "subtype"> { extends BaseFieldSchema<FieldSubtype.USER | FieldSubtype.USERS> {
type: FieldType.BB_REFERENCE type: FieldType.BB_REFERENCE
subtype: FieldSubtype.USER | FieldSubtype.USERS
relationshipType?: RelationshipType relationshipType?: RelationshipType
} }
export interface AttachmentFieldMetadata
extends BaseFieldSchema<FieldSubtype.SINGLE | undefined> {
type: FieldType.ATTACHMENT
}
export interface FieldConstraints { export interface FieldConstraints {
type?: string type?: string
email?: boolean email?: boolean
@ -136,7 +143,7 @@ export interface FieldConstraints {
} }
} }
interface BaseFieldSchema extends UIFieldMetadata { interface BaseFieldSchema<TSubtype = never> extends UIFieldMetadata {
type: FieldType type: FieldType
name: string name: string
sortable?: boolean sortable?: boolean
@ -145,11 +152,7 @@ interface BaseFieldSchema extends UIFieldMetadata {
constraints?: FieldConstraints constraints?: FieldConstraints
autocolumn?: boolean autocolumn?: boolean
autoReason?: AutoReason.FOREIGN_KEY autoReason?: AutoReason.FOREIGN_KEY
subtype?: never subtype: TSubtype
}
interface AttachmentFieldMetadata extends Omit<BaseFieldSchema, "subtype"> {
type: FieldType.ATTACHMENT
subtype?: FieldSubtype.SINGLE
} }
interface OtherFieldMetadata extends BaseFieldSchema { interface OtherFieldMetadata extends BaseFieldSchema {