Merge branch 'master' into remove_test_flakiness
This commit is contained in:
commit
d806029075
68 changed files with 1484 additions and 896 deletions
|
@ -1 +1 @@
|
||||||
Subproject commit e9af6686ba135c367e9145a53d26c68325b9bf68
|
Subproject commit 64290ce8957d093bc997190402922df10d092953
|
|
@ -92,7 +92,14 @@ export const findAllMatchingComponents = (rootComponent, selector) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the closes parent component which matches certain criteria
|
* Recurses through the component tree and finds all components.
|
||||||
|
*/
|
||||||
|
export const findAllComponents = rootComponent => {
|
||||||
|
return findAllMatchingComponents(rootComponent, () => true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the closest parent component which matches certain criteria
|
||||||
*/
|
*/
|
||||||
export const findClosestMatchingComponent = (
|
export const findClosestMatchingComponent = (
|
||||||
rootComponent,
|
rootComponent,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import {
|
import {
|
||||||
|
findAllComponents,
|
||||||
findAllMatchingComponents,
|
findAllMatchingComponents,
|
||||||
findComponent,
|
findComponent,
|
||||||
findComponentPath,
|
findComponentPath,
|
||||||
|
@ -102,6 +103,9 @@ export const getAuthBindings = () => {
|
||||||
return bindings
|
return bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all bindings for environment variables
|
||||||
|
*/
|
||||||
export const getEnvironmentBindings = () => {
|
export const getEnvironmentBindings = () => {
|
||||||
let envVars = get(environment).variables
|
let envVars = get(environment).variables
|
||||||
return envVars.map(variable => {
|
return envVars.map(variable => {
|
||||||
|
@ -130,26 +134,22 @@ export const toBindingsArray = (valueMap, prefix, category) => {
|
||||||
if (!binding) {
|
if (!binding) {
|
||||||
return acc
|
return acc
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = {
|
let config = {
|
||||||
type: "context",
|
type: "context",
|
||||||
runtimeBinding: binding,
|
runtimeBinding: binding,
|
||||||
readableBinding: `${prefix}.${binding}`,
|
readableBinding: `${prefix}.${binding}`,
|
||||||
icon: "Brackets",
|
icon: "Brackets",
|
||||||
}
|
}
|
||||||
|
|
||||||
if (category) {
|
if (category) {
|
||||||
config.category = category
|
config.category = category
|
||||||
}
|
}
|
||||||
|
|
||||||
acc.push(config)
|
acc.push(config)
|
||||||
|
|
||||||
return acc
|
return acc
|
||||||
}, [])
|
}, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility - coverting a map of readable bindings to runtime
|
* Utility to covert a map of readable bindings to runtime
|
||||||
*/
|
*/
|
||||||
export const readableToRuntimeMap = (bindings, ctx) => {
|
export const readableToRuntimeMap = (bindings, ctx) => {
|
||||||
if (!bindings || !ctx) {
|
if (!bindings || !ctx) {
|
||||||
|
@ -162,7 +162,7 @@ export const readableToRuntimeMap = (bindings, ctx) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility - coverting a map of runtime bindings to readable
|
* Utility to covert a map of runtime bindings to readable bindings
|
||||||
*/
|
*/
|
||||||
export const runtimeToReadableMap = (bindings, ctx) => {
|
export const runtimeToReadableMap = (bindings, ctx) => {
|
||||||
if (!bindings || !ctx) {
|
if (!bindings || !ctx) {
|
||||||
|
@ -188,15 +188,23 @@ export const getComponentBindableProperties = (asset, componentId) => {
|
||||||
if (!def?.context) {
|
if (!def?.context) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
const contexts = Array.isArray(def.context) ? def.context : [def.context]
|
||||||
|
|
||||||
// Get the bindings for the component
|
// Get the bindings for the component
|
||||||
return getProviderContextBindings(asset, component)
|
const componentContext = {
|
||||||
|
component,
|
||||||
|
definition: def,
|
||||||
|
contexts,
|
||||||
|
}
|
||||||
|
return generateComponentContextBindings(asset, componentContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all data provider components above a component.
|
* Gets all component contexts available to a certain component. This handles
|
||||||
|
* both global and local bindings, taking into account a component's position
|
||||||
|
* in the component tree.
|
||||||
*/
|
*/
|
||||||
export const getContextProviderComponents = (
|
export const getComponentContexts = (
|
||||||
asset,
|
asset,
|
||||||
componentId,
|
componentId,
|
||||||
type,
|
type,
|
||||||
|
@ -205,30 +213,55 @@ export const getContextProviderComponents = (
|
||||||
if (!asset || !componentId) {
|
if (!asset || !componentId) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
let map = {}
|
||||||
|
|
||||||
// Get the component tree leading up to this component, ignoring the component
|
// Processes all contexts exposed by a component
|
||||||
// itself
|
const processContexts = scope => component => {
|
||||||
const path = findComponentPath(asset.props, componentId)
|
|
||||||
if (!options?.includeSelf) {
|
|
||||||
path.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter by only data provider components
|
|
||||||
return path.filter(component => {
|
|
||||||
const def = store.actions.components.getDefinition(component._component)
|
const def = store.actions.components.getDefinition(component._component)
|
||||||
if (!def?.context) {
|
if (!def?.context) {
|
||||||
return false
|
return
|
||||||
}
|
}
|
||||||
|
if (!map[component._id]) {
|
||||||
// If no type specified, return anything that exposes context
|
map[component._id] = {
|
||||||
if (!type) {
|
component,
|
||||||
return true
|
definition: def,
|
||||||
|
contexts: [],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise only match components with the specific context type
|
|
||||||
const contexts = Array.isArray(def.context) ? def.context : [def.context]
|
const contexts = Array.isArray(def.context) ? def.context : [def.context]
|
||||||
return contexts.find(context => context.type === type) != null
|
contexts.forEach(context => {
|
||||||
})
|
// Ensure type matches
|
||||||
|
if (type && context.type !== type) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Ensure scope matches
|
||||||
|
let contextScope = context.scope || "global"
|
||||||
|
if (contextScope !== scope) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Ensure the context is compatible with the component's current settings
|
||||||
|
if (!isContextCompatibleWithComponent(context, component)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
map[component._id].contexts.push(context)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process all global contexts
|
||||||
|
const allComponents = findAllComponents(asset.props)
|
||||||
|
allComponents.forEach(processContexts("global"))
|
||||||
|
|
||||||
|
// Process all local contexts
|
||||||
|
const localComponents = findComponentPath(asset.props, componentId)
|
||||||
|
localComponents.forEach(processContexts("local"))
|
||||||
|
|
||||||
|
// Exclude self if required
|
||||||
|
if (!options?.includeSelf) {
|
||||||
|
delete map[componentId]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only return components which provide at least 1 matching context
|
||||||
|
return Object.values(map).filter(x => x.contexts.length > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -240,20 +273,19 @@ export const getActionProviders = (
|
||||||
actionType,
|
actionType,
|
||||||
options = { includeSelf: false }
|
options = { includeSelf: false }
|
||||||
) => {
|
) => {
|
||||||
if (!asset || !componentId) {
|
if (!asset) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the component tree leading up to this component, ignoring the component
|
// Get all components
|
||||||
// itself
|
const components = findAllComponents(asset.props)
|
||||||
const path = findComponentPath(asset.props, componentId)
|
|
||||||
if (!options?.includeSelf) {
|
|
||||||
path.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find matching contexts and generate bindings
|
// Find matching contexts and generate bindings
|
||||||
let providers = []
|
let providers = []
|
||||||
path.forEach(component => {
|
components.forEach(component => {
|
||||||
|
if (!options?.includeSelf && component._id === componentId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const def = store.actions.components.getDefinition(component._component)
|
const def = store.actions.components.getDefinition(component._component)
|
||||||
const actions = (def?.actions || []).map(action => {
|
const actions = (def?.actions || []).map(action => {
|
||||||
return typeof action === "string" ? { type: action } : action
|
return typeof action === "string" ? { type: action } : action
|
||||||
|
@ -317,142 +349,132 @@ export const getDatasourceForProvider = (asset, component) => {
|
||||||
* Gets all bindable data properties from component data contexts.
|
* Gets all bindable data properties from component data contexts.
|
||||||
*/
|
*/
|
||||||
const getContextBindings = (asset, componentId) => {
|
const getContextBindings = (asset, componentId) => {
|
||||||
// Extract any components which provide data contexts
|
// Get all available contexts for this component
|
||||||
const dataProviders = getContextProviderComponents(asset, componentId)
|
const componentContexts = getComponentContexts(asset, componentId)
|
||||||
|
|
||||||
// Generate bindings for all matching components
|
// Generate bindings for each context
|
||||||
return getProviderContextBindings(asset, dataProviders)
|
return componentContexts
|
||||||
|
.map(componentContext => {
|
||||||
|
return generateComponentContextBindings(asset, componentContext)
|
||||||
|
})
|
||||||
|
.flat()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the context bindings exposed by a set of data provider components.
|
* Generates a set of bindings for a given component context
|
||||||
*/
|
*/
|
||||||
const getProviderContextBindings = (asset, dataProviders) => {
|
const generateComponentContextBindings = (asset, componentContext) => {
|
||||||
if (!asset || !dataProviders) {
|
console.log("Hello ")
|
||||||
|
const { component, definition, contexts } = componentContext
|
||||||
|
if (!component || !definition || !contexts?.length) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure providers is an array
|
|
||||||
if (!Array.isArray(dataProviders)) {
|
|
||||||
dataProviders = [dataProviders]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create bindings for each data provider
|
// Create bindings for each data provider
|
||||||
let bindings = []
|
let bindings = []
|
||||||
dataProviders.forEach(component => {
|
contexts.forEach(context => {
|
||||||
const def = store.actions.components.getDefinition(component._component)
|
if (!context?.type) {
|
||||||
const contexts = Array.isArray(def.context) ? def.context : [def.context]
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Create bindings for each context block provided by this data provider
|
let schema
|
||||||
contexts.forEach(context => {
|
let table
|
||||||
if (!context?.type) {
|
let readablePrefix
|
||||||
|
let runtimeSuffix = context.suffix
|
||||||
|
|
||||||
|
if (context.type === "form") {
|
||||||
|
// Forms do not need table schemas
|
||||||
|
// Their schemas are built from their component field names
|
||||||
|
schema = buildFormSchema(component, asset)
|
||||||
|
readablePrefix = "Fields"
|
||||||
|
} else if (context.type === "static") {
|
||||||
|
// Static contexts are fully defined by the components
|
||||||
|
schema = {}
|
||||||
|
const values = context.values || []
|
||||||
|
values.forEach(value => {
|
||||||
|
schema[value.key] = {
|
||||||
|
name: value.label,
|
||||||
|
type: value.type || "string",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (context.type === "schema") {
|
||||||
|
// Schema contexts are generated dynamically depending on their data
|
||||||
|
const datasource = getDatasourceForProvider(asset, component)
|
||||||
|
if (!datasource) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const info = getSchemaForDatasource(asset, datasource)
|
||||||
|
schema = info.schema
|
||||||
|
table = info.table
|
||||||
|
|
||||||
let schema
|
// Determine what to prefix bindings with
|
||||||
let table
|
if (datasource.type === "jsonarray") {
|
||||||
let readablePrefix
|
// For JSON arrays, use the array name as the readable prefix
|
||||||
let runtimeSuffix = context.suffix
|
const split = datasource.label.split(".")
|
||||||
|
readablePrefix = split[split.length - 1]
|
||||||
if (context.type === "form") {
|
} else if (datasource.type === "viewV2") {
|
||||||
// Forms do not need table schemas
|
// For views, use the view name
|
||||||
// Their schemas are built from their component field names
|
const view = Object.values(table?.views || {}).find(
|
||||||
schema = buildFormSchema(component, asset)
|
view => view.id === datasource.id
|
||||||
readablePrefix = "Fields"
|
|
||||||
} else if (context.type === "static") {
|
|
||||||
// Static contexts are fully defined by the components
|
|
||||||
schema = {}
|
|
||||||
const values = context.values || []
|
|
||||||
values.forEach(value => {
|
|
||||||
schema[value.key] = {
|
|
||||||
name: value.label,
|
|
||||||
type: value.type || "string",
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else if (context.type === "schema") {
|
|
||||||
// Schema contexts are generated dynamically depending on their data
|
|
||||||
const datasource = getDatasourceForProvider(asset, component)
|
|
||||||
if (!datasource) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const info = getSchemaForDatasource(asset, datasource)
|
|
||||||
schema = info.schema
|
|
||||||
table = info.table
|
|
||||||
|
|
||||||
// Determine what to prefix bindings with
|
|
||||||
if (datasource.type === "jsonarray") {
|
|
||||||
// For JSON arrays, use the array name as the readable prefix
|
|
||||||
const split = datasource.label.split(".")
|
|
||||||
readablePrefix = split[split.length - 1]
|
|
||||||
} else if (datasource.type === "viewV2") {
|
|
||||||
// For views, use the view name
|
|
||||||
const view = Object.values(table?.views || {}).find(
|
|
||||||
view => view.id === datasource.id
|
|
||||||
)
|
|
||||||
readablePrefix = view?.name
|
|
||||||
} else {
|
|
||||||
// Otherwise use the table name
|
|
||||||
readablePrefix = info.table?.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!schema) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const keys = Object.keys(schema).sort()
|
|
||||||
|
|
||||||
// Generate safe unique runtime prefix
|
|
||||||
let providerId = component._id
|
|
||||||
if (runtimeSuffix) {
|
|
||||||
providerId += `-${runtimeSuffix}`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filterCategoryByContext(component, context)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const safeComponentId = makePropSafe(providerId)
|
|
||||||
|
|
||||||
// Create bindable properties for each schema field
|
|
||||||
keys.forEach(key => {
|
|
||||||
const fieldSchema = schema[key]
|
|
||||||
|
|
||||||
// Make safe runtime binding
|
|
||||||
const safeKey = key.split(".").map(makePropSafe).join(".")
|
|
||||||
const runtimeBinding = `${safeComponentId}.${safeKey}`
|
|
||||||
|
|
||||||
// Optionally use a prefix with readable bindings
|
|
||||||
let readableBinding = component._instanceName
|
|
||||||
if (readablePrefix) {
|
|
||||||
readableBinding += `.${readablePrefix}`
|
|
||||||
}
|
|
||||||
readableBinding += `.${fieldSchema.name || key}`
|
|
||||||
|
|
||||||
const bindingCategory = getComponentBindingCategory(
|
|
||||||
component,
|
|
||||||
context,
|
|
||||||
def
|
|
||||||
)
|
)
|
||||||
|
readablePrefix = view?.name
|
||||||
|
} else {
|
||||||
|
// Otherwise use the table name
|
||||||
|
readablePrefix = info.table?.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!schema) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Create the binding object
|
const keys = Object.keys(schema).sort()
|
||||||
bindings.push({
|
|
||||||
type: "context",
|
// Generate safe unique runtime prefix
|
||||||
runtimeBinding,
|
let providerId = component._id
|
||||||
readableBinding,
|
if (runtimeSuffix) {
|
||||||
// Field schema and provider are required to construct relationship
|
providerId += `-${runtimeSuffix}`
|
||||||
// datasource options, based on bindable properties
|
}
|
||||||
fieldSchema,
|
const safeComponentId = makePropSafe(providerId)
|
||||||
providerId,
|
|
||||||
// Table ID is used by JSON fields to know what table the field is in
|
// Create bindable properties for each schema field
|
||||||
tableId: table?._id,
|
keys.forEach(key => {
|
||||||
component: component._component,
|
const fieldSchema = schema[key]
|
||||||
category: bindingCategory.category,
|
|
||||||
icon: bindingCategory.icon,
|
// Make safe runtime binding
|
||||||
display: {
|
const safeKey = key.split(".").map(makePropSafe).join(".")
|
||||||
name: fieldSchema.name || key,
|
const runtimeBinding = `${safeComponentId}.${safeKey}`
|
||||||
type: fieldSchema.type,
|
|
||||||
},
|
// Optionally use a prefix with readable bindings
|
||||||
})
|
let readableBinding = component._instanceName
|
||||||
|
if (readablePrefix) {
|
||||||
|
readableBinding += `.${readablePrefix}`
|
||||||
|
}
|
||||||
|
readableBinding += `.${fieldSchema.name || key}`
|
||||||
|
|
||||||
|
// Determine which category this binding belongs in
|
||||||
|
const bindingCategory = getComponentBindingCategory(
|
||||||
|
component,
|
||||||
|
context,
|
||||||
|
definition
|
||||||
|
)
|
||||||
|
// Create the binding object
|
||||||
|
bindings.push({
|
||||||
|
type: "context",
|
||||||
|
runtimeBinding,
|
||||||
|
readableBinding: `${readableBinding}`,
|
||||||
|
// Field schema and provider are required to construct relationship
|
||||||
|
// datasource options, based on bindable properties
|
||||||
|
fieldSchema,
|
||||||
|
providerId,
|
||||||
|
// Table ID is used by JSON fields to know what table the field is in
|
||||||
|
tableId: table?._id,
|
||||||
|
component: component._component,
|
||||||
|
category: bindingCategory.category,
|
||||||
|
icon: bindingCategory.icon,
|
||||||
|
display: {
|
||||||
|
name: `${fieldSchema.name || key}`,
|
||||||
|
type: fieldSchema.type,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -460,25 +482,38 @@ const getProviderContextBindings = (asset, dataProviders) => {
|
||||||
return bindings
|
return bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exclude a data context based on the component settings
|
/**
|
||||||
const filterCategoryByContext = (component, context) => {
|
* Checks if a certain data context is compatible with a certain instance of a
|
||||||
const { _component } = component
|
* configured component.
|
||||||
|
*/
|
||||||
|
const isContextCompatibleWithComponent = (context, component) => {
|
||||||
|
if (!component) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const { _component, actionType } = component
|
||||||
|
const { type } = context
|
||||||
|
|
||||||
|
// Certain types of form blocks only allow certain contexts
|
||||||
if (_component.endsWith("formblock")) {
|
if (_component.endsWith("formblock")) {
|
||||||
if (
|
if (
|
||||||
(component.actionType === "Create" && context.type === "schema") ||
|
(actionType === "Create" && type === "schema") ||
|
||||||
(component.actionType === "View" && context.type === "form")
|
(actionType === "View" && type === "form")
|
||||||
) {
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow the context by default
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enrich binding category information for certain components
|
// Enrich binding category information for certain components
|
||||||
const getComponentBindingCategory = (component, context, def) => {
|
const getComponentBindingCategory = (component, context, def) => {
|
||||||
|
// Default category to component name
|
||||||
let icon = def.icon
|
let icon = def.icon
|
||||||
let category = component._instanceName
|
let category = component._instanceName
|
||||||
|
|
||||||
|
// Form block edge case
|
||||||
if (component._component.endsWith("formblock")) {
|
if (component._component.endsWith("formblock")) {
|
||||||
if (context.type === "form") {
|
if (context.type === "form") {
|
||||||
category = `${component._instanceName} - Fields`
|
category = `${component._instanceName} - Fields`
|
||||||
|
@ -496,7 +531,7 @@ const getComponentBindingCategory = (component, context, def) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all bindable properties from the logged in user.
|
* Gets all bindable properties from the logged-in user.
|
||||||
*/
|
*/
|
||||||
export const getUserBindings = () => {
|
export const getUserBindings = () => {
|
||||||
let bindings = []
|
let bindings = []
|
||||||
|
@ -566,6 +601,7 @@ const getDeviceBindings = () => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all selected rows bindings for tables in the current asset.
|
* Gets all selected rows bindings for tables in the current asset.
|
||||||
|
* TODO: remove in future because we don't need a separate store for this
|
||||||
*/
|
*/
|
||||||
const getSelectedRowsBindings = asset => {
|
const getSelectedRowsBindings = asset => {
|
||||||
let bindings = []
|
let bindings = []
|
||||||
|
@ -608,6 +644,9 @@ const getSelectedRowsBindings = asset => {
|
||||||
return bindings
|
return bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a state binding for a certain key name
|
||||||
|
*/
|
||||||
export const makeStateBinding = key => {
|
export const makeStateBinding = key => {
|
||||||
return {
|
return {
|
||||||
type: "context",
|
type: "context",
|
||||||
|
@ -662,6 +701,9 @@ const getUrlBindings = asset => {
|
||||||
return urlParamBindings.concat([queryParamsBinding])
|
return urlParamBindings.concat([queryParamsBinding])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates all bindings for role IDs
|
||||||
|
*/
|
||||||
const getRoleBindings = () => {
|
const getRoleBindings = () => {
|
||||||
return (get(rolesStore) || []).map(role => {
|
return (get(rolesStore) || []).map(role => {
|
||||||
return {
|
return {
|
||||||
|
@ -1035,11 +1077,48 @@ export const getAllStateVariables = () => {
|
||||||
getAllAssets().forEach(asset => {
|
getAllAssets().forEach(asset => {
|
||||||
findAllMatchingComponents(asset.props, component => {
|
findAllMatchingComponents(asset.props, component => {
|
||||||
const settings = getComponentSettings(component._component)
|
const settings = getComponentSettings(component._component)
|
||||||
settings
|
|
||||||
.filter(setting => setting.type === "event")
|
const parseEventSettings = (settings, comp) => {
|
||||||
.forEach(setting => {
|
settings
|
||||||
eventSettings.push(component[setting.key])
|
.filter(setting => setting.type === "event")
|
||||||
})
|
.forEach(setting => {
|
||||||
|
eventSettings.push(comp[setting.key])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseComponentSettings = (settings, component) => {
|
||||||
|
// Parse the nested button configurations
|
||||||
|
settings
|
||||||
|
.filter(setting => setting.type === "buttonConfiguration")
|
||||||
|
.forEach(setting => {
|
||||||
|
const buttonConfig = component[setting.key]
|
||||||
|
|
||||||
|
if (Array.isArray(buttonConfig)) {
|
||||||
|
buttonConfig.forEach(button => {
|
||||||
|
const nestedSettings = getComponentSettings(button._component)
|
||||||
|
parseEventSettings(nestedSettings, button)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
parseEventSettings(settings, component)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the base component settings
|
||||||
|
parseComponentSettings(settings, component)
|
||||||
|
|
||||||
|
// Parse step configuration
|
||||||
|
const stepSetting = settings.find(
|
||||||
|
setting => setting.type === "stepConfiguration"
|
||||||
|
)
|
||||||
|
const steps = stepSetting ? component[stepSetting.key] : []
|
||||||
|
const stepDefinition = getComponentSettings(
|
||||||
|
"@budibase/standard-components/multistepformblockstep"
|
||||||
|
)
|
||||||
|
|
||||||
|
steps.forEach(step => {
|
||||||
|
parseComponentSettings(stepDefinition, step)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -706,10 +706,9 @@ export const getFrontendStore = () => {
|
||||||
else {
|
else {
|
||||||
if (setting.type === "dataProvider") {
|
if (setting.type === "dataProvider") {
|
||||||
// Validate data provider exists, or else clear it
|
// Validate data provider exists, or else clear it
|
||||||
const treeId = parent?._id || component._id
|
const providers = findAllMatchingComponents(
|
||||||
const path = findComponentPath(screen?.props, treeId)
|
screen?.props,
|
||||||
const providers = path.filter(component =>
|
component => component._component?.endsWith("/dataprovider")
|
||||||
component._component?.endsWith("/dataprovider")
|
|
||||||
)
|
)
|
||||||
// Validate non-empty values
|
// Validate non-empty values
|
||||||
const valid = providers?.some(dp => value.includes?.(dp._id))
|
const valid = providers?.some(dp => value.includes?.(dp._id))
|
||||||
|
@ -731,6 +730,16 @@ export const getFrontendStore = () => {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find all existing components of this type so that we can give this
|
||||||
|
// component a unique name
|
||||||
|
const screen = get(selectedScreen).props
|
||||||
|
const otherComponents = findAllMatchingComponents(
|
||||||
|
screen,
|
||||||
|
x => x._component === definition.component && x._id !== screen._id
|
||||||
|
)
|
||||||
|
let name = definition.friendlyName || definition.name
|
||||||
|
name = `${name} ${otherComponents.length + 1}`
|
||||||
|
|
||||||
// Generate basic component structure
|
// Generate basic component structure
|
||||||
let instance = {
|
let instance = {
|
||||||
_id: Helpers.uuid(),
|
_id: Helpers.uuid(),
|
||||||
|
@ -740,7 +749,7 @@ export const getFrontendStore = () => {
|
||||||
hover: {},
|
hover: {},
|
||||||
active: {},
|
active: {},
|
||||||
},
|
},
|
||||||
_instanceName: `New ${definition.friendlyName || definition.name}`,
|
_instanceName: name,
|
||||||
...presetProps,
|
...presetProps,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { getContextProviderComponents } from "builderStore/dataBinding"
|
import { getComponentContexts } from "builderStore/dataBinding"
|
||||||
import { store } from "builderStore"
|
|
||||||
import { capitalise } from "helpers"
|
import { capitalise } from "helpers"
|
||||||
|
|
||||||
// Generates bindings for all components that provider "datasource like"
|
// Generates bindings for all components that provider "datasource like"
|
||||||
|
@ -8,58 +7,49 @@ import { capitalise } from "helpers"
|
||||||
// Some examples are saving rows or duplicating rows.
|
// Some examples are saving rows or duplicating rows.
|
||||||
export const getDatasourceLikeProviders = ({ asset, componentId, nested }) => {
|
export const getDatasourceLikeProviders = ({ asset, componentId, nested }) => {
|
||||||
// Get all form context providers
|
// Get all form context providers
|
||||||
const formComponents = getContextProviderComponents(
|
const formComponentContexts = getComponentContexts(
|
||||||
asset,
|
asset,
|
||||||
componentId,
|
componentId,
|
||||||
"form",
|
"form",
|
||||||
{ includeSelf: nested }
|
{
|
||||||
|
includeSelf: nested,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get all schema context providers
|
// Get all schema context providers
|
||||||
const schemaComponents = getContextProviderComponents(
|
const schemaComponentContexts = getComponentContexts(
|
||||||
asset,
|
asset,
|
||||||
componentId,
|
componentId,
|
||||||
"schema",
|
"schema",
|
||||||
{ includeSelf: nested }
|
{
|
||||||
|
includeSelf: nested,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generate contexts for all form providers
|
|
||||||
const formContexts = formComponents.map(component => ({
|
|
||||||
component,
|
|
||||||
context: extractComponentContext(component, "form"),
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Generate contexts for all schema providers
|
|
||||||
const schemaContexts = schemaComponents.map(component => ({
|
|
||||||
component,
|
|
||||||
context: extractComponentContext(component, "schema"),
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Check for duplicate contexts by the same component. In this case, attempt
|
// Check for duplicate contexts by the same component. In this case, attempt
|
||||||
// to label contexts with their suffixes
|
// to label contexts with their suffixes
|
||||||
schemaContexts.forEach(schemaContext => {
|
schemaComponentContexts.forEach(schemaContext => {
|
||||||
// Check if we have a form context for this component
|
// Check if we have a form context for this component
|
||||||
const id = schemaContext.component._id
|
const id = schemaContext.component._id
|
||||||
const existing = formContexts.find(x => x.component._id === id)
|
const existing = formComponentContexts.find(x => x.component._id === id)
|
||||||
if (existing) {
|
if (existing) {
|
||||||
if (existing.context.suffix) {
|
if (existing.contexts[0].suffix) {
|
||||||
const suffix = capitalise(existing.context.suffix)
|
const suffix = capitalise(existing.contexts[0].suffix)
|
||||||
existing.readableSuffix = ` - ${suffix}`
|
existing.readableSuffix = ` - ${suffix}`
|
||||||
}
|
}
|
||||||
if (schemaContext.context.suffix) {
|
if (schemaContext.contexts[0].suffix) {
|
||||||
const suffix = capitalise(schemaContext.context.suffix)
|
const suffix = capitalise(schemaContext.contexts[0].suffix)
|
||||||
schemaContext.readableSuffix = ` - ${suffix}`
|
schemaContext.readableSuffix = ` - ${suffix}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Generate bindings for all contexts
|
// Generate bindings for all contexts
|
||||||
const allContexts = formContexts.concat(schemaContexts)
|
const allContexts = formComponentContexts.concat(schemaComponentContexts)
|
||||||
return allContexts.map(({ component, context, readableSuffix }) => {
|
return allContexts.map(({ component, contexts, readableSuffix }) => {
|
||||||
let readableBinding = component._instanceName
|
let readableBinding = component._instanceName
|
||||||
let runtimeBinding = component._id
|
let runtimeBinding = component._id
|
||||||
if (context.suffix) {
|
if (contexts[0].suffix) {
|
||||||
runtimeBinding += `-${context.suffix}`
|
runtimeBinding += `-${contexts[0].suffix}`
|
||||||
}
|
}
|
||||||
if (readableSuffix) {
|
if (readableSuffix) {
|
||||||
readableBinding += readableSuffix
|
readableBinding += readableSuffix
|
||||||
|
@ -70,13 +60,3 @@ export const getDatasourceLikeProviders = ({ asset, componentId, nested }) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets a context definition of a certain type from a component definition
|
|
||||||
const extractComponentContext = (component, contextType) => {
|
|
||||||
const def = store.actions.components.getDefinition(component?._component)
|
|
||||||
if (!def) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const contexts = Array.isArray(def.context) ? def.context : [def.context]
|
|
||||||
return contexts.find(context => context?.type === contextType)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select } from "@budibase/bbui"
|
import { Select } from "@budibase/bbui"
|
||||||
import { makePropSafe } from "@budibase/string-templates"
|
import { makePropSafe } from "@budibase/string-templates"
|
||||||
import { currentAsset, store } from "builderStore"
|
import { currentAsset } from "builderStore"
|
||||||
import { findComponentPath } from "builderStore/componentUtils"
|
import { findAllMatchingComponents } from "builderStore/componentUtils"
|
||||||
|
|
||||||
export let value
|
export let value
|
||||||
|
|
||||||
const getValue = component => `{{ literal ${makePropSafe(component._id)} }}`
|
const getValue = component => `{{ literal ${makePropSafe(component._id)} }}`
|
||||||
|
|
||||||
$: path = findComponentPath($currentAsset?.props, $store.selectedComponentId)
|
$: providers = findAllMatchingComponents($currentAsset?.props, c =>
|
||||||
$: providers = path.filter(c => c._component?.endsWith("/dataprovider"))
|
c._component?.endsWith("/dataprovider")
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import {
|
import {
|
||||||
getContextProviderComponents,
|
|
||||||
readableToRuntimeBinding,
|
readableToRuntimeBinding,
|
||||||
runtimeToReadableBinding,
|
runtimeToReadableBinding,
|
||||||
} from "builderStore/dataBinding"
|
} from "builderStore/dataBinding"
|
||||||
|
@ -30,6 +29,7 @@
|
||||||
import BindingBuilder from "components/integration/QueryBindingBuilder.svelte"
|
import BindingBuilder from "components/integration/QueryBindingBuilder.svelte"
|
||||||
import IntegrationQueryEditor from "components/integration/index.svelte"
|
import IntegrationQueryEditor from "components/integration/index.svelte"
|
||||||
import { makePropSafe as safe } from "@budibase/string-templates"
|
import { makePropSafe as safe } from "@budibase/string-templates"
|
||||||
|
import { findAllComponents } from "builderStore/componentUtils"
|
||||||
import ClientBindingPanel from "components/common/bindings/ClientBindingPanel.svelte"
|
import ClientBindingPanel from "components/common/bindings/ClientBindingPanel.svelte"
|
||||||
import DataSourceCategory from "components/design/settings/controls/DataSourceSelect/DataSourceCategory.svelte"
|
import DataSourceCategory from "components/design/settings/controls/DataSourceSelect/DataSourceCategory.svelte"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
|
@ -75,12 +75,13 @@
|
||||||
...query,
|
...query,
|
||||||
type: "query",
|
type: "query",
|
||||||
}))
|
}))
|
||||||
$: contextProviders = getContextProviderComponents(
|
$: dataProviders = findAllComponents($currentAsset.props)
|
||||||
$currentAsset,
|
.filter(component => {
|
||||||
$store.selectedComponentId
|
return (
|
||||||
)
|
component._component?.endsWith("/dataprovider") &&
|
||||||
$: dataProviders = contextProviders
|
component._id !== $store.selectedComponentId
|
||||||
.filter(component => component._component?.endsWith("/dataprovider"))
|
)
|
||||||
|
})
|
||||||
.map(provider => ({
|
.map(provider => ({
|
||||||
label: provider._instanceName,
|
label: provider._instanceName,
|
||||||
name: provider._instanceName,
|
name: provider._instanceName,
|
||||||
|
|
|
@ -392,6 +392,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const openInviteFlow = () => {
|
const openInviteFlow = () => {
|
||||||
|
// prevent email from getting overwritten if changes are made
|
||||||
|
if (!email) {
|
||||||
|
email = query
|
||||||
|
}
|
||||||
$licensing.userLimitReached
|
$licensing.userLimitReached
|
||||||
? userLimitReachedModal.show()
|
? userLimitReachedModal.show()
|
||||||
: (invitingFlow = true)
|
: (invitingFlow = true)
|
||||||
|
|
|
@ -573,7 +573,6 @@
|
||||||
"description": "A configurable data list that attaches to your backend tables.",
|
"description": "A configurable data list that attaches to your backend tables.",
|
||||||
"icon": "JourneyData",
|
"icon": "JourneyData",
|
||||||
"illegalChildren": ["section"],
|
"illegalChildren": ["section"],
|
||||||
"requiredAncestors": ["dataprovider"],
|
|
||||||
"hasChildren": true,
|
"hasChildren": true,
|
||||||
"size": {
|
"size": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
|
@ -711,10 +710,12 @@
|
||||||
],
|
],
|
||||||
"context": [
|
"context": [
|
||||||
{
|
{
|
||||||
"type": "schema"
|
"type": "schema",
|
||||||
|
"scope": "local"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "static",
|
"type": "static",
|
||||||
|
"scope": "local",
|
||||||
"values": [
|
"values": [
|
||||||
{
|
{
|
||||||
"label": "Row index",
|
"label": "Row index",
|
||||||
|
@ -1564,7 +1565,6 @@
|
||||||
"name": "Bar Chart",
|
"name": "Bar Chart",
|
||||||
"description": "Bar chart",
|
"description": "Bar chart",
|
||||||
"icon": "GraphBarVertical",
|
"icon": "GraphBarVertical",
|
||||||
"requiredAncestors": ["dataprovider"],
|
|
||||||
"size": {
|
"size": {
|
||||||
"width": 600,
|
"width": 600,
|
||||||
"height": 400
|
"height": 400
|
||||||
|
@ -1727,7 +1727,6 @@
|
||||||
"width": 600,
|
"width": 600,
|
||||||
"height": 400
|
"height": 400
|
||||||
},
|
},
|
||||||
"requiredAncestors": ["dataprovider"],
|
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -1881,7 +1880,6 @@
|
||||||
"width": 600,
|
"width": 600,
|
||||||
"height": 400
|
"height": 400
|
||||||
},
|
},
|
||||||
"requiredAncestors": ["dataprovider"],
|
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -2047,7 +2045,6 @@
|
||||||
"width": 600,
|
"width": 600,
|
||||||
"height": 400
|
"height": 400
|
||||||
},
|
},
|
||||||
"requiredAncestors": ["dataprovider"],
|
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -2177,7 +2174,6 @@
|
||||||
"width": 600,
|
"width": 600,
|
||||||
"height": 400
|
"height": 400
|
||||||
},
|
},
|
||||||
"requiredAncestors": ["dataprovider"],
|
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -2307,7 +2303,6 @@
|
||||||
"width": 600,
|
"width": 600,
|
||||||
"height": 400
|
"height": 400
|
||||||
},
|
},
|
||||||
"requiredAncestors": ["dataprovider"],
|
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -4087,7 +4082,6 @@
|
||||||
"width": 400,
|
"width": 400,
|
||||||
"height": 320
|
"height": 320
|
||||||
},
|
},
|
||||||
"requiredAncestors": ["dataprovider"],
|
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"type": "dataProvider",
|
"type": "dataProvider",
|
||||||
|
@ -4643,7 +4637,6 @@
|
||||||
"name": "Table",
|
"name": "Table",
|
||||||
"icon": "Table",
|
"icon": "Table",
|
||||||
"illegalChildren": ["section"],
|
"illegalChildren": ["section"],
|
||||||
"requiredAncestors": ["dataprovider"],
|
|
||||||
"hasChildren": true,
|
"hasChildren": true,
|
||||||
"showEmptyState": false,
|
"showEmptyState": false,
|
||||||
"size": {
|
"size": {
|
||||||
|
@ -4734,7 +4727,6 @@
|
||||||
"name": "Date Range",
|
"name": "Date Range",
|
||||||
"icon": "Calendar",
|
"icon": "Calendar",
|
||||||
"styles": ["size"],
|
"styles": ["size"],
|
||||||
"requiredAncestors": ["dataprovider"],
|
|
||||||
"hasChildren": false,
|
"hasChildren": false,
|
||||||
"size": {
|
"size": {
|
||||||
"width": 200,
|
"width": 200,
|
||||||
|
@ -4842,7 +4834,6 @@
|
||||||
"width": 100,
|
"width": 100,
|
||||||
"height": 35
|
"height": 35
|
||||||
},
|
},
|
||||||
"requiredAncestors": ["dataprovider"],
|
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"type": "dataProvider",
|
"type": "dataProvider",
|
||||||
|
@ -5617,7 +5608,38 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"context": {
|
||||||
|
"type": "static",
|
||||||
|
"suffix": "provider",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"label": "Rows",
|
||||||
|
"key": "rows",
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Extra Info",
|
||||||
|
"key": "info",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Rows Length",
|
||||||
|
"key": "rowsLength",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Schema",
|
||||||
|
"key": "schema",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Page Number",
|
||||||
|
"key": "pageNumber",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"cardsblock": {
|
"cardsblock": {
|
||||||
"block": true,
|
"block": true,
|
||||||
|
@ -5796,7 +5818,8 @@
|
||||||
],
|
],
|
||||||
"context": {
|
"context": {
|
||||||
"type": "schema",
|
"type": "schema",
|
||||||
"suffix": "repeater"
|
"suffix": "repeater",
|
||||||
|
"scope": "local"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"repeaterblock": {
|
"repeaterblock": {
|
||||||
|
@ -6020,7 +6043,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "schema",
|
"type": "schema",
|
||||||
"suffix": "repeater"
|
"suffix": "repeater",
|
||||||
|
"scope": "local"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -6166,6 +6190,10 @@
|
||||||
"type": "form",
|
"type": "form",
|
||||||
"suffix": "form"
|
"suffix": "form"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "schema",
|
||||||
|
"suffix": "repeater"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "static",
|
"type": "static",
|
||||||
"suffix": "form",
|
"suffix": "form",
|
||||||
|
@ -6479,9 +6507,27 @@
|
||||||
],
|
],
|
||||||
"context": {
|
"context": {
|
||||||
"type": "schema",
|
"type": "schema",
|
||||||
"suffix": "repeater"
|
"suffix": "repeater",
|
||||||
|
"scope": "local"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"grid": {
|
||||||
|
"name": "Grid",
|
||||||
|
"icon": "ViewGrid",
|
||||||
|
"hasChildren": true,
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"key": "cols",
|
||||||
|
"label": "Columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"key": "rows",
|
||||||
|
"label": "Rows"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"gridblock": {
|
"gridblock": {
|
||||||
"name": "Grid Block",
|
"name": "Grid Block",
|
||||||
"icon": "Table",
|
"icon": "Table",
|
||||||
|
@ -6625,7 +6671,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"context": {
|
"context": {
|
||||||
"type": "schema"
|
"type": "schema",
|
||||||
|
"scope": "local"
|
||||||
},
|
},
|
||||||
"actions": ["RefreshDatasource"]
|
"actions": ["RefreshDatasource"]
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getContext, setContext, onMount, onDestroy } from "svelte"
|
import { getContext, setContext, onMount } from "svelte"
|
||||||
import { writable, get } from "svelte/store"
|
import { writable, get } from "svelte/store"
|
||||||
import {
|
import {
|
||||||
enrichProps,
|
enrichProps,
|
||||||
|
@ -30,6 +30,15 @@
|
||||||
import ScreenPlaceholder from "components/app/ScreenPlaceholder.svelte"
|
import ScreenPlaceholder from "components/app/ScreenPlaceholder.svelte"
|
||||||
import ComponentErrorState from "components/error-states/ComponentErrorState.svelte"
|
import ComponentErrorState from "components/error-states/ComponentErrorState.svelte"
|
||||||
import { BudibasePrefix } from "../stores/components.js"
|
import { BudibasePrefix } from "../stores/components.js"
|
||||||
|
import {
|
||||||
|
decodeJSBinding,
|
||||||
|
findHBSBlocks,
|
||||||
|
isJSBinding,
|
||||||
|
} from "@budibase/string-templates"
|
||||||
|
import {
|
||||||
|
getActionContextKey,
|
||||||
|
getActionDependentContextKeys,
|
||||||
|
} from "../utils/buttonActions.js"
|
||||||
|
|
||||||
export let instance = {}
|
export let instance = {}
|
||||||
export let isLayout = false
|
export let isLayout = false
|
||||||
|
@ -81,7 +90,6 @@
|
||||||
|
|
||||||
// Keep track of stringified representations of context and instance
|
// Keep track of stringified representations of context and instance
|
||||||
// to avoid enriching bindings as much as possible
|
// to avoid enriching bindings as much as possible
|
||||||
let lastContextKey
|
|
||||||
let lastInstanceKey
|
let lastInstanceKey
|
||||||
|
|
||||||
// Visibility flag used by conditional UI
|
// Visibility flag used by conditional UI
|
||||||
|
@ -98,6 +106,13 @@
|
||||||
// We clear these whenever a new instance is received.
|
// We clear these whenever a new instance is received.
|
||||||
let ephemeralStyles
|
let ephemeralStyles
|
||||||
|
|
||||||
|
// Single string of all HBS blocks, used to check if we use a certain binding
|
||||||
|
// or not
|
||||||
|
let bindingString = ""
|
||||||
|
|
||||||
|
// List of context keys which we use inside bindings
|
||||||
|
let knownContextKeyMap = {}
|
||||||
|
|
||||||
// Set up initial state for each new component instance
|
// Set up initial state for each new component instance
|
||||||
$: initialise(instance)
|
$: initialise(instance)
|
||||||
|
|
||||||
|
@ -155,9 +170,6 @@
|
||||||
hasMissingRequiredSettings)
|
hasMissingRequiredSettings)
|
||||||
$: emptyState = empty && showEmptyState
|
$: emptyState = empty && showEmptyState
|
||||||
|
|
||||||
// Enrich component settings
|
|
||||||
$: enrichComponentSettings($context, settingsDefinitionMap)
|
|
||||||
|
|
||||||
// Evaluate conditional UI settings and store any component setting changes
|
// Evaluate conditional UI settings and store any component setting changes
|
||||||
// which need to be made
|
// which need to be made
|
||||||
$: evaluateConditions(conditions)
|
$: evaluateConditions(conditions)
|
||||||
|
@ -206,6 +218,7 @@
|
||||||
errorState,
|
errorState,
|
||||||
parent: id,
|
parent: id,
|
||||||
ancestors: [...($component?.ancestors ?? []), instance._component],
|
ancestors: [...($component?.ancestors ?? []), instance._component],
|
||||||
|
path: [...($component?.path ?? []), id],
|
||||||
})
|
})
|
||||||
|
|
||||||
const initialise = (instance, force = false) => {
|
const initialise = (instance, force = false) => {
|
||||||
|
@ -214,7 +227,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we're processing a new instance
|
// Ensure we're processing a new instance
|
||||||
const instanceKey = Helpers.hashString(JSON.stringify(instance))
|
const stringifiedInstance = JSON.stringify(instance)
|
||||||
|
const instanceKey = Helpers.hashString(stringifiedInstance)
|
||||||
if (instanceKey === lastInstanceKey && !force) {
|
if (instanceKey === lastInstanceKey && !force) {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
|
@ -274,13 +288,54 @@
|
||||||
return missing
|
return missing
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// When considering bindings we can ignore children, so we remove that
|
||||||
|
// before storing the reference stringified version
|
||||||
|
const noChildren = JSON.stringify({ ...instance, _children: null })
|
||||||
|
const bindings = findHBSBlocks(noChildren).map(binding => {
|
||||||
|
let sanitizedBinding = binding.replace(/\\"/g, '"')
|
||||||
|
if (isJSBinding(sanitizedBinding)) {
|
||||||
|
return decodeJSBinding(sanitizedBinding)
|
||||||
|
} else {
|
||||||
|
return sanitizedBinding
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// The known context key map is built up at runtime, as changes to keys are
|
||||||
|
// encountered. We manually seed this to the required action keys as these
|
||||||
|
// are not encountered at runtime and so need computed in advance.
|
||||||
|
knownContextKeyMap = generateActionKeyMap(instance, settingsDefinition)
|
||||||
|
bindingString = bindings.join(" ")
|
||||||
|
|
||||||
// Run any migrations
|
// Run any migrations
|
||||||
runMigrations(instance, settingsDefinition)
|
runMigrations(instance, settingsDefinition)
|
||||||
|
|
||||||
// Force an initial enrichment of the new settings
|
// Force an initial enrichment of the new settings
|
||||||
enrichComponentSettings(get(context), settingsDefinitionMap, {
|
enrichComponentSettings(get(context), settingsDefinitionMap)
|
||||||
force: true,
|
}
|
||||||
|
|
||||||
|
// Extracts a map of all context keys which are required by action settings
|
||||||
|
// to provide the functions to evaluate at runtime. This needs done manually
|
||||||
|
// as the action definitions themselves do not specify bindings for action
|
||||||
|
// keys, meaning we cannot do this while doing the other normal bindings.
|
||||||
|
const generateActionKeyMap = (instance, settingsDefinition) => {
|
||||||
|
let map = {}
|
||||||
|
settingsDefinition.forEach(setting => {
|
||||||
|
if (setting.type === "event") {
|
||||||
|
instance[setting.key]?.forEach(action => {
|
||||||
|
// We depend on the actual action key
|
||||||
|
const actionKey = getActionContextKey(action)
|
||||||
|
if (actionKey) {
|
||||||
|
map[actionKey] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// We also depend on any manually declared context keys
|
||||||
|
getActionDependentContextKeys(action)?.forEach(key => {
|
||||||
|
map[key] = true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
const runMigrations = (instance, settingsDefinition) => {
|
const runMigrations = (instance, settingsDefinition) => {
|
||||||
|
@ -381,17 +436,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enriches any string component props using handlebars
|
// Enriches any string component props using handlebars
|
||||||
const enrichComponentSettings = (
|
const enrichComponentSettings = (context, settingsDefinitionMap) => {
|
||||||
context,
|
|
||||||
settingsDefinitionMap,
|
|
||||||
options = { force: false }
|
|
||||||
) => {
|
|
||||||
const contextChanged = context.key !== lastContextKey
|
|
||||||
if (!contextChanged && !options?.force) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
lastContextKey = context.key
|
|
||||||
|
|
||||||
// Record the timestamp so we can reference it after enrichment
|
// Record the timestamp so we can reference it after enrichment
|
||||||
latestUpdateTime = Date.now()
|
latestUpdateTime = Date.now()
|
||||||
const enrichmentTime = latestUpdateTime
|
const enrichmentTime = latestUpdateTime
|
||||||
|
@ -506,31 +551,46 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleContextChange = key => {
|
||||||
|
// Check if we already know if this key is used
|
||||||
|
let used = knownContextKeyMap[key]
|
||||||
|
|
||||||
|
// If we don't know, check and cache
|
||||||
|
if (used == null) {
|
||||||
|
used = bindingString.indexOf(`[${key}]`) !== -1
|
||||||
|
knownContextKeyMap[key] = used
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enrich settings if we use this key
|
||||||
|
if (used) {
|
||||||
|
enrichComponentSettings($context, settingsDefinitionMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register an unregister component instance
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (
|
if ($appStore.isDevApp) {
|
||||||
$appStore.isDevApp &&
|
if (!componentStore.actions.isComponentRegistered(id)) {
|
||||||
!componentStore.actions.isComponentRegistered(id)
|
componentStore.actions.registerInstance(id, {
|
||||||
) {
|
component: instance._component,
|
||||||
componentStore.actions.registerInstance(id, {
|
getSettings: () => cachedSettings,
|
||||||
component: instance._component,
|
getRawSettings: () => ({ ...staticSettings, ...dynamicSettings }),
|
||||||
getSettings: () => cachedSettings,
|
getDataContext: () => get(context),
|
||||||
getRawSettings: () => ({ ...staticSettings, ...dynamicSettings }),
|
reload: () => initialise(instance, true),
|
||||||
getDataContext: () => get(context),
|
setEphemeralStyles: styles => (ephemeralStyles = styles),
|
||||||
reload: () => initialise(instance, true),
|
state: store,
|
||||||
setEphemeralStyles: styles => (ephemeralStyles = styles),
|
})
|
||||||
state: store,
|
}
|
||||||
})
|
return () => {
|
||||||
|
if (componentStore.actions.isComponentRegistered(id)) {
|
||||||
|
componentStore.actions.unregisterInstance(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
onDestroy(() => {
|
// Observe changes to context
|
||||||
if (
|
onMount(() => context.actions.observeChanges(handleContextChange))
|
||||||
$appStore.isDevApp &&
|
|
||||||
componentStore.actions.isComponentRegistered(id)
|
|
||||||
) {
|
|
||||||
componentStore.actions.unregisterInstance(id)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if constructor && initialSettings && (visible || inSelectedPath) && !builderHidden}
|
{#if constructor && initialSettings && (visible || inSelectedPath) && !builderHidden}
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
datasource: dataSource || {},
|
datasource: dataSource || {},
|
||||||
schema,
|
schema,
|
||||||
rowsLength: $fetch.rows.length,
|
rowsLength: $fetch.rows.length,
|
||||||
|
pageNumber: $fetch.pageNumber + 1,
|
||||||
// Undocumented properties. These aren't supposed to be used in builder
|
// Undocumented properties. These aren't supposed to be used in builder
|
||||||
// bindings, but are used internally by other components
|
// bindings, but are used internally by other components
|
||||||
id: $component?.id,
|
id: $component?.id,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import { Icon } from "@budibase/bbui"
|
|
||||||
|
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
const { builderStore, componentStore } = getContext("sdk")
|
const { builderStore, componentStore } = getContext("sdk")
|
||||||
|
@ -10,15 +9,7 @@
|
||||||
|
|
||||||
{#if $builderStore.inBuilder}
|
{#if $builderStore.inBuilder}
|
||||||
<div class="component-placeholder">
|
<div class="component-placeholder">
|
||||||
<Icon name="Help" color="var(--spectrum-global-color-blue-600)" />
|
{$component.name || definition?.name || "Component"}
|
||||||
<span
|
|
||||||
class="spectrum-Link"
|
|
||||||
on:click={() => {
|
|
||||||
builderStore.actions.requestAddComponent()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Add components inside your {definition?.name || $component.type}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
@ -32,14 +23,4 @@
|
||||||
font-size: var(--font-size-s);
|
font-size: var(--font-size-s);
|
||||||
gap: var(--spacing-s);
|
gap: var(--spacing-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Common styles for all error states to use */
|
|
||||||
.component-placeholder :global(mark) {
|
|
||||||
background-color: var(--spectrum-global-color-gray-400);
|
|
||||||
padding: 0 4px;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
.component-placeholder :global(.spectrum-Link) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -19,7 +19,36 @@
|
||||||
export let onRowClick = null
|
export let onRowClick = null
|
||||||
export let buttons = null
|
export let buttons = null
|
||||||
|
|
||||||
// parses columns to fix older formats
|
const context = getContext("context")
|
||||||
|
const component = getContext("component")
|
||||||
|
const {
|
||||||
|
styleable,
|
||||||
|
API,
|
||||||
|
builderStore,
|
||||||
|
notificationStore,
|
||||||
|
enrichButtonActions,
|
||||||
|
ActionTypes,
|
||||||
|
createContextStore,
|
||||||
|
Provider,
|
||||||
|
} = getContext("sdk")
|
||||||
|
|
||||||
|
let grid
|
||||||
|
|
||||||
|
$: columnWhitelist = parsedColumns
|
||||||
|
?.filter(col => col.active)
|
||||||
|
?.map(col => col.field)
|
||||||
|
$: schemaOverrides = getSchemaOverrides(parsedColumns)
|
||||||
|
$: enrichedButtons = enrichButtons(buttons)
|
||||||
|
$: parsedColumns = getParsedColumns(columns)
|
||||||
|
$: actions = [
|
||||||
|
{
|
||||||
|
type: ActionTypes.RefreshDatasource,
|
||||||
|
callback: () => grid?.getContext()?.rows.actions.refreshData(),
|
||||||
|
metadata: { dataSource: table },
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// Parses columns to fix older formats
|
||||||
const getParsedColumns = columns => {
|
const getParsedColumns = columns => {
|
||||||
// If the first element has an active key all elements should be in the new format
|
// If the first element has an active key all elements should be in the new format
|
||||||
if (columns?.length && columns[0]?.active !== undefined) {
|
if (columns?.length && columns[0]?.active !== undefined) {
|
||||||
|
@ -33,28 +62,6 @@
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
$: parsedColumns = getParsedColumns(columns)
|
|
||||||
|
|
||||||
const context = getContext("context")
|
|
||||||
const component = getContext("component")
|
|
||||||
const {
|
|
||||||
styleable,
|
|
||||||
API,
|
|
||||||
builderStore,
|
|
||||||
notificationStore,
|
|
||||||
enrichButtonActions,
|
|
||||||
ActionTypes,
|
|
||||||
createContextStore,
|
|
||||||
} = getContext("sdk")
|
|
||||||
|
|
||||||
let grid
|
|
||||||
|
|
||||||
$: columnWhitelist = parsedColumns
|
|
||||||
?.filter(col => col.active)
|
|
||||||
?.map(col => col.field)
|
|
||||||
$: schemaOverrides = getSchemaOverrides(parsedColumns)
|
|
||||||
$: enrichedButtons = enrichButtons(buttons)
|
|
||||||
|
|
||||||
const getSchemaOverrides = columns => {
|
const getSchemaOverrides = columns => {
|
||||||
let overrides = {}
|
let overrides = {}
|
||||||
columns?.forEach(column => {
|
columns?.forEach(column => {
|
||||||
|
@ -78,11 +85,6 @@
|
||||||
const id = get(component).id
|
const id = get(component).id
|
||||||
const gridContext = createContextStore(context)
|
const gridContext = createContextStore(context)
|
||||||
gridContext.actions.provideData(id, row)
|
gridContext.actions.provideData(id, row)
|
||||||
gridContext.actions.provideAction(
|
|
||||||
id,
|
|
||||||
ActionTypes.RefreshDatasource,
|
|
||||||
() => grid?.getContext()?.rows.actions.refreshData()
|
|
||||||
)
|
|
||||||
const fn = enrichButtonActions(settings.onClick, get(gridContext))
|
const fn = enrichButtonActions(settings.onClick, get(gridContext))
|
||||||
return await fn?.({ row })
|
return await fn?.({ row })
|
||||||
},
|
},
|
||||||
|
@ -94,29 +96,31 @@
|
||||||
use:styleable={$component.styles}
|
use:styleable={$component.styles}
|
||||||
class:in-builder={$builderStore.inBuilder}
|
class:in-builder={$builderStore.inBuilder}
|
||||||
>
|
>
|
||||||
<Grid
|
<Provider {actions}>
|
||||||
bind:this={grid}
|
<Grid
|
||||||
datasource={table}
|
bind:this={grid}
|
||||||
{API}
|
datasource={table}
|
||||||
{stripeRows}
|
{API}
|
||||||
{initialFilter}
|
{stripeRows}
|
||||||
{initialSortColumn}
|
{initialFilter}
|
||||||
{initialSortOrder}
|
{initialSortColumn}
|
||||||
{fixedRowHeight}
|
{initialSortOrder}
|
||||||
{columnWhitelist}
|
{fixedRowHeight}
|
||||||
{schemaOverrides}
|
{columnWhitelist}
|
||||||
canAddRows={allowAddRows}
|
{schemaOverrides}
|
||||||
canEditRows={allowEditRows}
|
canAddRows={allowAddRows}
|
||||||
canDeleteRows={allowDeleteRows}
|
canEditRows={allowEditRows}
|
||||||
canEditColumns={false}
|
canDeleteRows={allowDeleteRows}
|
||||||
canExpandRows={false}
|
canEditColumns={false}
|
||||||
canSaveSchema={false}
|
canExpandRows={false}
|
||||||
showControls={false}
|
canSaveSchema={false}
|
||||||
notifySuccess={notificationStore.actions.success}
|
showControls={false}
|
||||||
notifyError={notificationStore.actions.error}
|
notifySuccess={notificationStore.actions.success}
|
||||||
buttons={enrichedButtons}
|
notifyError={notificationStore.actions.error}
|
||||||
on:rowclick={e => onRowClick?.({ row: e.detail })}
|
buttons={enrichedButtons}
|
||||||
/>
|
on:rowclick={e => onRowClick?.({ row: e.detail })}
|
||||||
|
/>
|
||||||
|
</Provider>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import Placeholder from "./Placeholder.svelte"
|
import Placeholder from "./Placeholder.svelte"
|
||||||
import Container from "./Container.svelte"
|
import Container from "./Container.svelte"
|
||||||
|
import { ContextScopes } from "constants"
|
||||||
|
|
||||||
export let dataProvider
|
export let dataProvider
|
||||||
export let noRowsMessage
|
export let noRowsMessage
|
||||||
|
@ -9,6 +10,7 @@
|
||||||
export let hAlign
|
export let hAlign
|
||||||
export let vAlign
|
export let vAlign
|
||||||
export let gap
|
export let gap
|
||||||
|
export let scope = ContextScopes.Local
|
||||||
|
|
||||||
const { Provider } = getContext("sdk")
|
const { Provider } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
@ -22,7 +24,7 @@
|
||||||
<Placeholder />
|
<Placeholder />
|
||||||
{:else if rows.length > 0}
|
{:else if rows.length > 0}
|
||||||
{#each rows as row, index}
|
{#each rows as row, index}
|
||||||
<Provider data={{ ...row, index }}>
|
<Provider data={{ ...row, index }} {scope}>
|
||||||
<slot />
|
<slot />
|
||||||
</Provider>
|
</Provider>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import BlockComponent from "components/BlockComponent.svelte"
|
import BlockComponent from "components/BlockComponent.svelte"
|
||||||
|
import { Helpers } from "@budibase/bbui"
|
||||||
import { getContext, setContext } from "svelte"
|
import { getContext, setContext } from "svelte"
|
||||||
import { builderStore } from "stores"
|
import { builderStore } from "stores"
|
||||||
import { Utils } from "@budibase/frontend-core"
|
import { Utils } from "@budibase/frontend-core"
|
||||||
|
@ -41,7 +42,7 @@
|
||||||
let schema
|
let schema
|
||||||
|
|
||||||
$: fetchSchema(dataSource)
|
$: fetchSchema(dataSource)
|
||||||
$: enrichedSteps = enrichSteps(steps, schema, $component.id)
|
$: enrichedSteps = enrichSteps(steps, schema, $component.id, $currentStep)
|
||||||
$: updateCurrentStep(enrichedSteps, $builderStore, $component)
|
$: updateCurrentStep(enrichedSteps, $builderStore, $component)
|
||||||
|
|
||||||
const updateCurrentStep = (steps, builderStore, component) => {
|
const updateCurrentStep = (steps, builderStore, component) => {
|
||||||
|
@ -115,6 +116,7 @@
|
||||||
dataSource,
|
dataSource,
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
|
_stepId: Helpers.uuid(),
|
||||||
fields: getDefaultFields(fields || [], schema),
|
fields: getDefaultFields(fields || [], schema),
|
||||||
title: title ?? defaultProps.title,
|
title: title ?? defaultProps.title,
|
||||||
desc,
|
desc,
|
||||||
|
@ -142,7 +144,7 @@
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{#each enrichedSteps as step, stepIdx}
|
{#each enrichedSteps as step, stepIdx (step._stepId)}
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
type="formstep"
|
type="formstep"
|
||||||
props={{ step: stepIdx + 1, _instanceName: `Step ${stepIdx + 1}` }}
|
props={{ step: stepIdx + 1, _instanceName: `Step ${stepIdx + 1}` }}
|
||||||
|
@ -186,12 +188,13 @@
|
||||||
</BlockComponent>
|
</BlockComponent>
|
||||||
</BlockComponent>
|
</BlockComponent>
|
||||||
<BlockComponent type="text" props={{ text: step.desc }} order={1} />
|
<BlockComponent type="text" props={{ text: step.desc }} order={1} />
|
||||||
|
|
||||||
<BlockComponent type="container" order={2}>
|
<BlockComponent type="container" order={2}>
|
||||||
<div
|
<div
|
||||||
class="form-block fields"
|
class="form-block fields"
|
||||||
class:mobile={$context.device.mobile}
|
class:mobile={$context.device.mobile}
|
||||||
>
|
>
|
||||||
{#each step.fields as field, fieldIdx (`${field.field || field.name}_${stepIdx}_${fieldIdx}`)}
|
{#each step.fields as field, fieldIdx (`${field.field || field.name}_${fieldIdx}`)}
|
||||||
{#if getComponentForField(field)}
|
{#if getComponentForField(field)}
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
type={getComponentForField(field)}
|
type={getComponentForField(field)}
|
||||||
|
|
|
@ -231,6 +231,7 @@
|
||||||
paginate,
|
paginate,
|
||||||
limit: rowCount,
|
limit: rowCount,
|
||||||
}}
|
}}
|
||||||
|
context="provider"
|
||||||
order={1}
|
order={1}
|
||||||
>
|
>
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
export let noRowsMessage
|
export let noRowsMessage
|
||||||
|
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
const { ContextScopes } = getContext("sdk")
|
||||||
|
|
||||||
$: providerId = `${$component.id}-provider`
|
$: providerId = `${$component.id}-provider`
|
||||||
$: dataProvider = `{{ literal ${safe(providerId)} }}`
|
$: dataProvider = `{{ literal ${safe(providerId)} }}`
|
||||||
|
@ -55,6 +56,7 @@
|
||||||
noRowsMessage: noRowsMessage || "We couldn't find a row to display",
|
noRowsMessage: noRowsMessage || "We couldn't find a row to display",
|
||||||
direction: "column",
|
direction: "column",
|
||||||
hAlign: "center",
|
hAlign: "center",
|
||||||
|
scope: ContextScopes.Global,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
export let editAutoColumns = false
|
export let editAutoColumns = false
|
||||||
|
|
||||||
const context = getContext("context")
|
const context = getContext("context")
|
||||||
|
const component = getContext("component")
|
||||||
const { API, fetchDatasourceSchema } = getContext("sdk")
|
const { API, fetchDatasourceSchema } = getContext("sdk")
|
||||||
|
|
||||||
const getInitialFormStep = () => {
|
const getInitialFormStep = () => {
|
||||||
|
@ -38,28 +39,47 @@
|
||||||
|
|
||||||
$: fetchSchema(dataSource)
|
$: fetchSchema(dataSource)
|
||||||
$: schemaKey = generateSchemaKey(schema)
|
$: schemaKey = generateSchemaKey(schema)
|
||||||
$: initialValues = getInitialValues(actionType, dataSource, $context)
|
$: initialValues = getInitialValues(
|
||||||
|
actionType,
|
||||||
|
dataSource,
|
||||||
|
$component.path,
|
||||||
|
$context
|
||||||
|
)
|
||||||
$: resetKey = Helpers.hashString(
|
$: resetKey = Helpers.hashString(
|
||||||
schemaKey + JSON.stringify(initialValues) + disabled + readonly
|
schemaKey + JSON.stringify(initialValues) + disabled + readonly
|
||||||
)
|
)
|
||||||
|
|
||||||
// Returns the closes data context which isn't a built in context
|
// Returns the closes data context which isn't a built in context
|
||||||
const getInitialValues = (type, dataSource, context) => {
|
const getInitialValues = (type, dataSource, path, context) => {
|
||||||
// Only inherit values for update forms
|
// Only inherit values for update forms
|
||||||
if (type !== "Update") {
|
if (type !== "Update") {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
// Only inherit values for forms targeting internal tables
|
// Only inherit values for forms targeting internal tables
|
||||||
if (!dataSource?.tableId) {
|
const dsType = dataSource?.type
|
||||||
|
if (dsType !== "table" && dsType !== "viewV2") {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
// Don't inherit values representing built in contexts
|
// Look up the component tree and find something that is provided by an
|
||||||
if (["user", "url"].includes(context.closestComponentId)) {
|
// ancestor that matches our datasource. This is for backwards compatibility
|
||||||
return {}
|
// as previously we could use the "closest" context.
|
||||||
|
for (let id of path.reverse().slice(1)) {
|
||||||
|
// Check for matching view datasource
|
||||||
|
if (
|
||||||
|
dataSource.type === "viewV2" &&
|
||||||
|
context[id]?._viewId === dataSource.id
|
||||||
|
) {
|
||||||
|
return context[id]
|
||||||
|
}
|
||||||
|
// Check for matching table datasource
|
||||||
|
if (
|
||||||
|
dataSource.type === "table" &&
|
||||||
|
context[id]?.tableId === dataSource.tableId
|
||||||
|
) {
|
||||||
|
return context[id]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Always inherit the closest datasource
|
return {}
|
||||||
const closestContext = context[`${context.closestComponentId}`] || {}
|
|
||||||
return closestContext || {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetches the form schema from this form's dataSource
|
// Fetches the form schema from this form's dataSource
|
||||||
|
|
|
@ -1,21 +1,24 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, setContext, onDestroy } from "svelte"
|
import { getContext, setContext, onDestroy } from "svelte"
|
||||||
import { dataSourceStore, createContextStore } from "stores"
|
import { dataSourceStore, createContextStore } from "stores"
|
||||||
import { ActionTypes } from "constants"
|
import { ActionTypes, ContextScopes } from "constants"
|
||||||
import { generate } from "shortid"
|
import { generate } from "shortid"
|
||||||
|
|
||||||
export let data
|
export let data
|
||||||
export let actions
|
export let actions
|
||||||
export let key
|
export let key
|
||||||
|
export let scope = ContextScopes.Global
|
||||||
|
|
||||||
// Clone and create new data context for this component tree
|
let context = getContext("context")
|
||||||
const context = getContext("context")
|
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
const newContext = createContextStore(context)
|
|
||||||
setContext("context", newContext)
|
|
||||||
|
|
||||||
const providerKey = key || $component.id
|
const providerKey = key || $component.id
|
||||||
|
|
||||||
|
// Create a new layer of context if we are only locally scoped
|
||||||
|
if (scope === ContextScopes.Local) {
|
||||||
|
context = createContextStore(context)
|
||||||
|
setContext("context", context)
|
||||||
|
}
|
||||||
|
|
||||||
// Generate a permanent unique ID for this component and use it to register
|
// Generate a permanent unique ID for this component and use it to register
|
||||||
// any datasource actions
|
// any datasource actions
|
||||||
const instanceId = generate()
|
const instanceId = generate()
|
||||||
|
@ -30,7 +33,7 @@
|
||||||
const provideData = newData => {
|
const provideData = newData => {
|
||||||
const dataKey = JSON.stringify(newData)
|
const dataKey = JSON.stringify(newData)
|
||||||
if (dataKey !== lastDataKey) {
|
if (dataKey !== lastDataKey) {
|
||||||
newContext.actions.provideData(providerKey, newData)
|
context.actions.provideData(providerKey, newData, scope)
|
||||||
lastDataKey = dataKey
|
lastDataKey = dataKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +43,7 @@
|
||||||
if (actionsKey !== lastActionsKey) {
|
if (actionsKey !== lastActionsKey) {
|
||||||
lastActionsKey = actionsKey
|
lastActionsKey = actionsKey
|
||||||
newActions?.forEach(({ type, callback, metadata }) => {
|
newActions?.forEach(({ type, callback, metadata }) => {
|
||||||
newContext.actions.provideAction(providerKey, type, callback)
|
context.actions.provideAction(providerKey, type, callback, scope)
|
||||||
|
|
||||||
// Register any "refresh datasource" actions with a singleton store
|
// Register any "refresh datasource" actions with a singleton store
|
||||||
// so we can easily refresh data at all levels for any datasource
|
// so we can easily refresh data at all levels for any datasource
|
||||||
|
|
|
@ -12,5 +12,10 @@ export const ActionTypes = {
|
||||||
ScrollTo: "ScrollTo",
|
ScrollTo: "ScrollTo",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ContextScopes = {
|
||||||
|
Local: "local",
|
||||||
|
Global: "global",
|
||||||
|
}
|
||||||
|
|
||||||
export const DNDPlaceholderID = "dnd-placeholder"
|
export const DNDPlaceholderID = "dnd-placeholder"
|
||||||
export const ScreenslotType = "screenslot"
|
export const ScreenslotType = "screenslot"
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { getAction } from "utils/getAction"
|
||||||
import Provider from "components/context/Provider.svelte"
|
import Provider from "components/context/Provider.svelte"
|
||||||
import Block from "components/Block.svelte"
|
import Block from "components/Block.svelte"
|
||||||
import BlockComponent from "components/BlockComponent.svelte"
|
import BlockComponent from "components/BlockComponent.svelte"
|
||||||
import { ActionTypes } from "./constants"
|
import { ActionTypes, ContextScopes } from "./constants"
|
||||||
import { fetchDatasourceSchema } from "./utils/schema.js"
|
import { fetchDatasourceSchema } from "./utils/schema.js"
|
||||||
import { getAPIKey } from "./utils/api.js"
|
import { getAPIKey } from "./utils/api.js"
|
||||||
import { enrichButtonActions } from "./utils/buttonActions.js"
|
import { enrichButtonActions } from "./utils/buttonActions.js"
|
||||||
|
@ -54,6 +54,7 @@ export default {
|
||||||
linkable,
|
linkable,
|
||||||
getAction,
|
getAction,
|
||||||
fetchDatasourceSchema,
|
fetchDatasourceSchema,
|
||||||
|
ContextScopes,
|
||||||
getAPIKey,
|
getAPIKey,
|
||||||
enrichButtonActions,
|
enrichButtonActions,
|
||||||
processStringSync,
|
processStringSync,
|
||||||
|
|
|
@ -1,59 +1,98 @@
|
||||||
import { writable, derived } from "svelte/store"
|
import { writable, derived } from "svelte/store"
|
||||||
import { Helpers } from "@budibase/bbui"
|
import { ContextScopes } from "constants"
|
||||||
|
|
||||||
export const createContextStore = oldContext => {
|
export const createContextStore = parentContext => {
|
||||||
const newContext = writable({})
|
const context = writable({})
|
||||||
const contexts = oldContext ? [oldContext, newContext] : [newContext]
|
let observers = []
|
||||||
|
|
||||||
|
// Derive the total context state at this point in the tree
|
||||||
|
const contexts = parentContext ? [parentContext, context] : [context]
|
||||||
const totalContext = derived(contexts, $contexts => {
|
const totalContext = derived(contexts, $contexts => {
|
||||||
// The key is the serialized representation of context
|
return $contexts.reduce((total, context) => ({ ...total, ...context }), {})
|
||||||
let key = ""
|
|
||||||
for (let i = 0; i < $contexts.length - 1; i++) {
|
|
||||||
key += $contexts[i].key
|
|
||||||
}
|
|
||||||
key = Helpers.hashString(
|
|
||||||
key + JSON.stringify($contexts[$contexts.length - 1])
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reduce global state
|
|
||||||
const reducer = (total, context) => ({ ...total, ...context })
|
|
||||||
const context = $contexts.reduce(reducer, {})
|
|
||||||
|
|
||||||
return {
|
|
||||||
...context,
|
|
||||||
key,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Adds a data context layer to the tree
|
// Subscribe to updates in the parent context, so that we can proxy on any
|
||||||
const provideData = (providerId, data) => {
|
// change messages to our own subscribers
|
||||||
if (!providerId || data === undefined) {
|
if (parentContext) {
|
||||||
return
|
parentContext.actions.observeChanges(key => {
|
||||||
}
|
broadcastChange(key)
|
||||||
newContext.update(state => {
|
|
||||||
state[providerId] = data
|
|
||||||
|
|
||||||
// Keep track of the closest component ID so we can later hydrate a "data" prop.
|
|
||||||
// This is only required for legacy bindings that used "data" rather than a
|
|
||||||
// component ID.
|
|
||||||
state.closestComponentId = providerId
|
|
||||||
|
|
||||||
return state
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds an action context layer to the tree
|
// Provide some data in context
|
||||||
const provideAction = (providerId, actionType, callback) => {
|
const provideData = (providerId, data, scope = ContextScopes.Global) => {
|
||||||
|
if (!providerId || data === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proxy message up the chain if we have a parent and are providing global
|
||||||
|
// context
|
||||||
|
if (scope === ContextScopes.Global && parentContext) {
|
||||||
|
parentContext.actions.provideData(providerId, data, scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise this is either the context root, or we're providing a local
|
||||||
|
// context override, so we need to update the local context instead
|
||||||
|
else {
|
||||||
|
context.update(state => {
|
||||||
|
state[providerId] = data
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
broadcastChange(providerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provides some action in context
|
||||||
|
const provideAction = (
|
||||||
|
providerId,
|
||||||
|
actionType,
|
||||||
|
callback,
|
||||||
|
scope = ContextScopes.Global
|
||||||
|
) => {
|
||||||
if (!providerId || !actionType) {
|
if (!providerId || !actionType) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
newContext.update(state => {
|
|
||||||
state[`${providerId}_${actionType}`] = callback
|
// Proxy message up the chain if we have a parent and are providing global
|
||||||
return state
|
// context
|
||||||
})
|
if (scope === ContextScopes.Global && parentContext) {
|
||||||
|
parentContext.actions.provideAction(
|
||||||
|
providerId,
|
||||||
|
actionType,
|
||||||
|
callback,
|
||||||
|
scope
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise this is either the context root, or we're providing a local
|
||||||
|
// context override, so we need to update the local context instead
|
||||||
|
else {
|
||||||
|
const key = `${providerId}_${actionType}`
|
||||||
|
context.update(state => {
|
||||||
|
state[key] = callback
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
broadcastChange(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const observeChanges = callback => {
|
||||||
|
observers.push(callback)
|
||||||
|
return () => {
|
||||||
|
observers = observers.filter(cb => cb !== callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const broadcastChange = key => {
|
||||||
|
observers.forEach(cb => cb(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe: totalContext.subscribe,
|
subscribe: totalContext.subscribe,
|
||||||
actions: { provideData, provideAction },
|
actions: {
|
||||||
|
provideData,
|
||||||
|
provideAction,
|
||||||
|
observeChanges,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,54 @@ import { ActionTypes } from "constants"
|
||||||
import { enrichDataBindings } from "./enrichDataBinding"
|
import { enrichDataBindings } from "./enrichDataBinding"
|
||||||
import { Helpers } from "@budibase/bbui"
|
import { Helpers } from "@budibase/bbui"
|
||||||
|
|
||||||
|
// Default action handler, which extracts an action from context that was
|
||||||
|
// provided by another component and executes it with all action parameters
|
||||||
|
const contextActionHandler = async (action, context) => {
|
||||||
|
const key = getActionContextKey(action)
|
||||||
|
const fn = context[key]
|
||||||
|
if (fn) {
|
||||||
|
return await fn(action.parameters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates the context key, which is the key that this action depends on in
|
||||||
|
// context to provide the function it will run. This is broken out as a util
|
||||||
|
// because we reuse this inside the core Component.svelte file to determine
|
||||||
|
// what the required action context keys are for all action settings.
|
||||||
|
export const getActionContextKey = action => {
|
||||||
|
const type = action?.["##eventHandlerType"]
|
||||||
|
const key = (componentId, type) => `${componentId}_${type}`
|
||||||
|
switch (type) {
|
||||||
|
case "Scroll To Field":
|
||||||
|
return key(action.parameters.componentId, ActionTypes.ScrollTo)
|
||||||
|
case "Update Field Value":
|
||||||
|
return key(action.parameters.componentId, ActionTypes.UpdateFieldValue)
|
||||||
|
case "Validate Form":
|
||||||
|
return key(action.parameters.componentId, ActionTypes.ValidateForm)
|
||||||
|
case "Refresh Data Provider":
|
||||||
|
return key(action.parameters.componentId, ActionTypes.RefreshDatasource)
|
||||||
|
case "Clear Form":
|
||||||
|
return key(action.parameters.componentId, ActionTypes.ClearForm)
|
||||||
|
case "Change Form Step":
|
||||||
|
return key(action.parameters.componentId, ActionTypes.ChangeFormStep)
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If button actions depend on context, they must declare which keys they need
|
||||||
|
export const getActionDependentContextKeys = action => {
|
||||||
|
const type = action?.["##eventHandlerType"]
|
||||||
|
switch (type) {
|
||||||
|
case "Save Row":
|
||||||
|
case "Duplicate Row":
|
||||||
|
if (action.parameters?.providerId) {
|
||||||
|
return [action.parameters.providerId]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
const saveRowHandler = async (action, context) => {
|
const saveRowHandler = async (action, context) => {
|
||||||
const { fields, providerId, tableId, notificationOverride } =
|
const { fields, providerId, tableId, notificationOverride } =
|
||||||
action.parameters
|
action.parameters
|
||||||
|
@ -32,20 +80,21 @@ const saveRowHandler = async (action, context) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tableId) {
|
if (tableId) {
|
||||||
payload.tableId = tableId
|
if (tableId.startsWith("view")) {
|
||||||
|
payload._viewId = tableId
|
||||||
|
} else {
|
||||||
|
payload.tableId = tableId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const row = await API.saveRow(payload)
|
const row = await API.saveRow(payload)
|
||||||
|
|
||||||
if (!notificationOverride) {
|
if (!notificationOverride) {
|
||||||
notificationStore.actions.success("Row saved")
|
notificationStore.actions.success("Row saved")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh related datasources
|
// Refresh related datasources
|
||||||
await dataSourceStore.actions.invalidateDataSource(tableId, {
|
await dataSourceStore.actions.invalidateDataSource(tableId, {
|
||||||
invalidateRelationships: true,
|
invalidateRelationships: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
return { row }
|
return { row }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Abort next actions
|
// Abort next actions
|
||||||
|
@ -64,7 +113,11 @@ const duplicateRowHandler = async (action, context) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tableId) {
|
if (tableId) {
|
||||||
payload.tableId = tableId
|
if (tableId.startsWith("view")) {
|
||||||
|
payload._viewId = tableId
|
||||||
|
} else {
|
||||||
|
payload.tableId = tableId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
delete payload._id
|
delete payload._id
|
||||||
delete payload._rev
|
delete payload._rev
|
||||||
|
@ -73,12 +126,10 @@ const duplicateRowHandler = async (action, context) => {
|
||||||
if (!notificationOverride) {
|
if (!notificationOverride) {
|
||||||
notificationStore.actions.success("Row saved")
|
notificationStore.actions.success("Row saved")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh related datasources
|
// Refresh related datasources
|
||||||
await dataSourceStore.actions.invalidateDataSource(tableId, {
|
await dataSourceStore.actions.invalidateDataSource(tableId, {
|
||||||
invalidateRelationships: true,
|
invalidateRelationships: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
return { row }
|
return { row }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Abort next actions
|
// Abort next actions
|
||||||
|
@ -190,17 +241,6 @@ const navigationHandler = action => {
|
||||||
routeStore.actions.navigate(url, peek, externalNewTab)
|
routeStore.actions.navigate(url, peek, externalNewTab)
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrollHandler = async (action, context) => {
|
|
||||||
return await executeActionHandler(
|
|
||||||
context,
|
|
||||||
action.parameters.componentId,
|
|
||||||
ActionTypes.ScrollTo,
|
|
||||||
{
|
|
||||||
field: action.parameters.field,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryExecutionHandler = async action => {
|
const queryExecutionHandler = async action => {
|
||||||
const { datasourceId, queryId, queryParams, notificationOverride } =
|
const { datasourceId, queryId, queryParams, notificationOverride } =
|
||||||
action.parameters
|
action.parameters
|
||||||
|
@ -236,47 +276,6 @@ const queryExecutionHandler = async action => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const executeActionHandler = async (
|
|
||||||
context,
|
|
||||||
componentId,
|
|
||||||
actionType,
|
|
||||||
params
|
|
||||||
) => {
|
|
||||||
const fn = context[`${componentId}_${actionType}`]
|
|
||||||
if (fn) {
|
|
||||||
return await fn(params)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateFieldValueHandler = async (action, context) => {
|
|
||||||
return await executeActionHandler(
|
|
||||||
context,
|
|
||||||
action.parameters.componentId,
|
|
||||||
ActionTypes.UpdateFieldValue,
|
|
||||||
{
|
|
||||||
type: action.parameters.type,
|
|
||||||
field: action.parameters.field,
|
|
||||||
value: action.parameters.value,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const validateFormHandler = async (action, context) => {
|
|
||||||
return await executeActionHandler(
|
|
||||||
context,
|
|
||||||
action.parameters.componentId,
|
|
||||||
ActionTypes.ValidateForm
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const refreshDataProviderHandler = async (action, context) => {
|
|
||||||
return await executeActionHandler(
|
|
||||||
context,
|
|
||||||
action.parameters.componentId,
|
|
||||||
ActionTypes.RefreshDatasource
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const logoutHandler = async action => {
|
const logoutHandler = async action => {
|
||||||
await authStore.actions.logOut()
|
await authStore.actions.logOut()
|
||||||
let redirectUrl = "/builder/auth/login"
|
let redirectUrl = "/builder/auth/login"
|
||||||
|
@ -293,23 +292,6 @@ const logoutHandler = async action => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearFormHandler = async (action, context) => {
|
|
||||||
return await executeActionHandler(
|
|
||||||
context,
|
|
||||||
action.parameters.componentId,
|
|
||||||
ActionTypes.ClearForm
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const changeFormStepHandler = async (action, context) => {
|
|
||||||
return await executeActionHandler(
|
|
||||||
context,
|
|
||||||
action.parameters.componentId,
|
|
||||||
ActionTypes.ChangeFormStep,
|
|
||||||
action.parameters
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeScreenModalHandler = action => {
|
const closeScreenModalHandler = action => {
|
||||||
let url
|
let url
|
||||||
if (action?.parameters) {
|
if (action?.parameters) {
|
||||||
|
@ -417,16 +399,10 @@ const handlerMap = {
|
||||||
["Duplicate Row"]: duplicateRowHandler,
|
["Duplicate Row"]: duplicateRowHandler,
|
||||||
["Delete Row"]: deleteRowHandler,
|
["Delete Row"]: deleteRowHandler,
|
||||||
["Navigate To"]: navigationHandler,
|
["Navigate To"]: navigationHandler,
|
||||||
["Scroll To Field"]: scrollHandler,
|
|
||||||
["Execute Query"]: queryExecutionHandler,
|
["Execute Query"]: queryExecutionHandler,
|
||||||
["Trigger Automation"]: triggerAutomationHandler,
|
["Trigger Automation"]: triggerAutomationHandler,
|
||||||
["Validate Form"]: validateFormHandler,
|
|
||||||
["Update Field Value"]: updateFieldValueHandler,
|
|
||||||
["Refresh Data Provider"]: refreshDataProviderHandler,
|
|
||||||
["Log Out"]: logoutHandler,
|
["Log Out"]: logoutHandler,
|
||||||
["Clear Form"]: clearFormHandler,
|
|
||||||
["Close Screen Modal"]: closeScreenModalHandler,
|
["Close Screen Modal"]: closeScreenModalHandler,
|
||||||
["Change Form Step"]: changeFormStepHandler,
|
|
||||||
["Update State"]: updateStateHandler,
|
["Update State"]: updateStateHandler,
|
||||||
["Upload File to S3"]: s3UploadHandler,
|
["Upload File to S3"]: s3UploadHandler,
|
||||||
["Export Data"]: exportDataHandler,
|
["Export Data"]: exportDataHandler,
|
||||||
|
@ -461,7 +437,12 @@ export const enrichButtonActions = (actions, context) => {
|
||||||
return actions
|
return actions
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlers = actions.map(def => handlerMap[def["##eventHandlerType"]])
|
// Get handlers for each action. If no bespoke handler is configured, fall
|
||||||
|
// back to simply executing this action from context.
|
||||||
|
const handlers = actions.map(def => {
|
||||||
|
return handlerMap[def["##eventHandlerType"]] || contextActionHandler
|
||||||
|
})
|
||||||
|
|
||||||
return async eventContext => {
|
return async eventContext => {
|
||||||
// Button context is built up as actions are executed.
|
// Button context is built up as actions are executed.
|
||||||
// Inherit any previous button context which may have come from actions
|
// Inherit any previous button context which may have come from actions
|
||||||
|
|
|
@ -23,16 +23,6 @@ export const propsAreSame = (a, b) => {
|
||||||
* Data bindings are enriched, and button actions are enriched.
|
* Data bindings are enriched, and button actions are enriched.
|
||||||
*/
|
*/
|
||||||
export const enrichProps = (props, context, settingsDefinitionMap) => {
|
export const enrichProps = (props, context, settingsDefinitionMap) => {
|
||||||
// Create context of all bindings and data contexts
|
|
||||||
// Duplicate the closest context as "data" which the builder requires
|
|
||||||
const totalContext = {
|
|
||||||
...context,
|
|
||||||
|
|
||||||
// This is only required for legacy bindings that used "data" rather than a
|
|
||||||
// component ID.
|
|
||||||
data: context[context.closestComponentId],
|
|
||||||
}
|
|
||||||
|
|
||||||
// We want to exclude any button actions from enrichment at this stage.
|
// We want to exclude any button actions from enrichment at this stage.
|
||||||
// Extract top level button action settings.
|
// Extract top level button action settings.
|
||||||
let normalProps = { ...props }
|
let normalProps = { ...props }
|
||||||
|
@ -49,13 +39,13 @@ export const enrichProps = (props, context, settingsDefinitionMap) => {
|
||||||
let rawConditions = normalProps._conditions
|
let rawConditions = normalProps._conditions
|
||||||
|
|
||||||
// Enrich all props except button actions
|
// Enrich all props except button actions
|
||||||
let enrichedProps = enrichDataBindings(normalProps, totalContext)
|
let enrichedProps = enrichDataBindings(normalProps, context)
|
||||||
|
|
||||||
// Enrich button actions.
|
// Enrich button actions.
|
||||||
// Actions are enriched into a function at this stage, but actual data
|
// Actions are enriched into a function at this stage, but actual data
|
||||||
// binding enrichment is done dynamically at runtime.
|
// binding enrichment is done dynamically at runtime.
|
||||||
Object.keys(actionProps).forEach(prop => {
|
Object.keys(actionProps).forEach(prop => {
|
||||||
enrichedProps[prop] = enrichButtonActions(actionProps[prop], totalContext)
|
enrichedProps[prop] = enrichButtonActions(actionProps[prop], context)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Conditions
|
// Conditions
|
||||||
|
@ -66,7 +56,7 @@ export const enrichProps = (props, context, settingsDefinitionMap) => {
|
||||||
// action
|
// action
|
||||||
condition.settingValue = enrichButtonActions(
|
condition.settingValue = enrichButtonActions(
|
||||||
rawConditions[idx].settingValue,
|
rawConditions[idx].settingValue,
|
||||||
totalContext
|
context
|
||||||
)
|
)
|
||||||
|
|
||||||
// Since we can't compare functions, we need to assume that conditions
|
// Since we can't compare functions, we need to assume that conditions
|
||||||
|
|
|
@ -19,11 +19,12 @@ export const buildRowEndpoints = API => ({
|
||||||
* @param suppressErrors whether or not to suppress error notifications
|
* @param suppressErrors whether or not to suppress error notifications
|
||||||
*/
|
*/
|
||||||
saveRow: async (row, suppressErrors = false) => {
|
saveRow: async (row, suppressErrors = false) => {
|
||||||
if (!row?.tableId) {
|
const resourceId = row?._viewId || row?.tableId
|
||||||
|
if (!resourceId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return await API.post({
|
return await API.post({
|
||||||
url: `/api/${row._viewId || row.tableId}/rows`,
|
url: `/api/${resourceId}/rows`,
|
||||||
body: row,
|
body: row,
|
||||||
suppressErrors,
|
suppressErrors,
|
||||||
})
|
})
|
||||||
|
@ -35,11 +36,12 @@ export const buildRowEndpoints = API => ({
|
||||||
* @param suppressErrors whether or not to suppress error notifications
|
* @param suppressErrors whether or not to suppress error notifications
|
||||||
*/
|
*/
|
||||||
patchRow: async (row, suppressErrors = false) => {
|
patchRow: async (row, suppressErrors = false) => {
|
||||||
if (!row?.tableId && !row?._viewId) {
|
const resourceId = row?._viewId || row?.tableId
|
||||||
|
if (!resourceId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return await API.patch({
|
return await API.patch({
|
||||||
url: `/api/${row._viewId || row.tableId}/rows`,
|
url: `/api/${resourceId}/rows`,
|
||||||
body: row,
|
body: row,
|
||||||
suppressErrors,
|
suppressErrors,
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
import { getColumnIcon } from "../lib/utils"
|
import { getColumnIcon } from "../lib/utils"
|
||||||
import MigrationModal from "../controls/MigrationModal.svelte"
|
import MigrationModal from "../controls/MigrationModal.svelte"
|
||||||
import { debounce } from "../../../utils/utils"
|
import { debounce } from "../../../utils/utils"
|
||||||
import { FieldType, FormulaTypes } from "@budibase/types"
|
import { FieldType, FormulaType } from "@budibase/types"
|
||||||
import { TableNames } from "../../../constants"
|
import { TableNames } from "../../../constants"
|
||||||
|
|
||||||
export let column
|
export let column
|
||||||
|
@ -96,7 +96,7 @@
|
||||||
const { type, formulaType } = col.schema
|
const { type, formulaType } = col.schema
|
||||||
return (
|
return (
|
||||||
searchableTypes.includes(type) ||
|
searchableTypes.includes(type) ||
|
||||||
(type === FieldType.FORMULA && formulaType === FormulaTypes.STATIC)
|
(type === FieldType.FORMULA && formulaType === FormulaType.STATIC)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -119,8 +119,8 @@
|
||||||
"@types/google-spreadsheet": "3.1.5",
|
"@types/google-spreadsheet": "3.1.5",
|
||||||
"@types/jest": "29.5.5",
|
"@types/jest": "29.5.5",
|
||||||
"@types/koa": "2.13.4",
|
"@types/koa": "2.13.4",
|
||||||
"@types/koa__router": "8.0.8",
|
|
||||||
"@types/koa-send": "^4.1.6",
|
"@types/koa-send": "^4.1.6",
|
||||||
|
"@types/koa__router": "8.0.8",
|
||||||
"@types/lodash": "4.14.200",
|
"@types/lodash": "4.14.200",
|
||||||
"@types/mssql": "9.1.4",
|
"@types/mssql": "9.1.4",
|
||||||
"@types/node-fetch": "2.6.4",
|
"@types/node-fetch": "2.6.4",
|
||||||
|
@ -142,6 +142,7 @@
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
"supertest": "6.3.3",
|
"supertest": "6.3.3",
|
||||||
"swagger-jsdoc": "6.1.0",
|
"swagger-jsdoc": "6.1.0",
|
||||||
|
"testcontainers": "10.6.0",
|
||||||
"timekeeper": "2.2.0",
|
"timekeeper": "2.2.0",
|
||||||
"ts-node": "10.8.1",
|
"ts-node": "10.8.1",
|
||||||
"tsconfig-paths": "4.0.0",
|
"tsconfig-paths": "4.0.0",
|
||||||
|
|
|
@ -2,7 +2,7 @@ version: "3.8"
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
container_name: postgres
|
container_name: postgres
|
||||||
image: postgres:15-bullseye
|
image: postgres:16.1-bullseye
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: root
|
POSTGRES_USER: root
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { FieldTypes, RelationshipType, FormulaTypes } from "../../src/constants"
|
import { FieldType, FormulaType, RelationshipType } from "@budibase/types"
|
||||||
import { object } from "./utils"
|
import { object } from "./utils"
|
||||||
import Resource from "./utils/Resource"
|
import Resource from "./utils/Resource"
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ const table = {
|
||||||
const baseColumnDef = {
|
const baseColumnDef = {
|
||||||
type: {
|
type: {
|
||||||
type: "string",
|
type: "string",
|
||||||
enum: Object.values(FieldTypes),
|
enum: Object.values(FieldType),
|
||||||
description:
|
description:
|
||||||
"Defines the type of the column, most explain themselves, a link column is a relationship.",
|
"Defines the type of the column, most explain themselves, a link column is a relationship.",
|
||||||
},
|
},
|
||||||
|
@ -81,7 +81,7 @@ const tableSchema = {
|
||||||
...baseColumnDef,
|
...baseColumnDef,
|
||||||
type: {
|
type: {
|
||||||
type: "string",
|
type: "string",
|
||||||
enum: [FieldTypes.LINK],
|
enum: [FieldType.LINK],
|
||||||
description: "A relationship column.",
|
description: "A relationship column.",
|
||||||
},
|
},
|
||||||
fieldName: {
|
fieldName: {
|
||||||
|
@ -128,7 +128,7 @@ const tableSchema = {
|
||||||
...baseColumnDef,
|
...baseColumnDef,
|
||||||
type: {
|
type: {
|
||||||
type: "string",
|
type: "string",
|
||||||
enum: [FieldTypes.FORMULA],
|
enum: [FieldType.FORMULA],
|
||||||
description: "A formula column.",
|
description: "A formula column.",
|
||||||
},
|
},
|
||||||
formula: {
|
formula: {
|
||||||
|
@ -138,7 +138,7 @@ const tableSchema = {
|
||||||
},
|
},
|
||||||
formulaType: {
|
formulaType: {
|
||||||
type: "string",
|
type: "string",
|
||||||
enum: Object.values(FormulaTypes),
|
enum: Object.values(FormulaType),
|
||||||
description:
|
description:
|
||||||
"Defines whether this is a static or dynamic formula.",
|
"Defines whether this is a static or dynamic formula.",
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
AutoFieldSubType,
|
||||||
AutoReason,
|
AutoReason,
|
||||||
Datasource,
|
Datasource,
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
|
@ -27,7 +28,6 @@ import {
|
||||||
isSQL,
|
isSQL,
|
||||||
} from "../../../integrations/utils"
|
} from "../../../integrations/utils"
|
||||||
import { getDatasourceAndQuery } from "../../../sdk/app/rows/utils"
|
import { getDatasourceAndQuery } from "../../../sdk/app/rows/utils"
|
||||||
import { AutoFieldSubTypes, FieldTypes } from "../../../constants"
|
|
||||||
import { processObjectSync } from "@budibase/string-templates"
|
import { processObjectSync } from "@budibase/string-templates"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { processDates, processFormulas } from "../../../utilities/rowProcessor"
|
import { processDates, processFormulas } from "../../../utilities/rowProcessor"
|
||||||
|
@ -111,10 +111,10 @@ function buildFilters(
|
||||||
*/
|
*/
|
||||||
function cleanupConfig(config: RunConfig, table: Table): RunConfig {
|
function cleanupConfig(config: RunConfig, table: Table): RunConfig {
|
||||||
const primaryOptions = [
|
const primaryOptions = [
|
||||||
FieldTypes.STRING,
|
FieldType.STRING,
|
||||||
FieldTypes.LONGFORM,
|
FieldType.LONGFORM,
|
||||||
FieldTypes.OPTIONS,
|
FieldType.OPTIONS,
|
||||||
FieldTypes.NUMBER,
|
FieldType.NUMBER,
|
||||||
]
|
]
|
||||||
// filter out fields which cannot be keys
|
// filter out fields which cannot be keys
|
||||||
const fieldNames = Object.entries(table.schema)
|
const fieldNames = Object.entries(table.schema)
|
||||||
|
@ -241,10 +241,7 @@ function basicProcessing({
|
||||||
|
|
||||||
function fixArrayTypes(row: Row, table: Table) {
|
function fixArrayTypes(row: Row, table: Table) {
|
||||||
for (let [fieldName, schema] of Object.entries(table.schema)) {
|
for (let [fieldName, schema] of Object.entries(table.schema)) {
|
||||||
if (
|
if (schema.type === FieldType.ARRAY && typeof row[fieldName] === "string") {
|
||||||
schema.type === FieldTypes.ARRAY &&
|
|
||||||
typeof row[fieldName] === "string"
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
row[fieldName] = JSON.parse(row[fieldName])
|
row[fieldName] = JSON.parse(row[fieldName])
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -274,8 +271,8 @@ function isEditableColumn(column: FieldSchema) {
|
||||||
const isExternalAutoColumn =
|
const isExternalAutoColumn =
|
||||||
column.autocolumn &&
|
column.autocolumn &&
|
||||||
column.autoReason !== AutoReason.FOREIGN_KEY &&
|
column.autoReason !== AutoReason.FOREIGN_KEY &&
|
||||||
column.subtype !== AutoFieldSubTypes.AUTO_ID
|
column.subtype !== AutoFieldSubType.AUTO_ID
|
||||||
const isFormula = column.type === FieldTypes.FORMULA
|
const isFormula = column.type === FieldType.FORMULA
|
||||||
return !(isExternalAutoColumn || isFormula)
|
return !(isExternalAutoColumn || isFormula)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,11 +319,11 @@ export class ExternalRequest<T extends Operation> {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// parse floats/numbers
|
// parse floats/numbers
|
||||||
if (field.type === FieldTypes.NUMBER && !isNaN(parseFloat(row[key]))) {
|
if (field.type === FieldType.NUMBER && !isNaN(parseFloat(row[key]))) {
|
||||||
newRow[key] = parseFloat(row[key])
|
newRow[key] = parseFloat(row[key])
|
||||||
}
|
}
|
||||||
// if its not a link then just copy it over
|
// if its not a link then just copy it over
|
||||||
if (field.type !== FieldTypes.LINK) {
|
if (field.type !== FieldType.LINK) {
|
||||||
newRow[key] = row[key]
|
newRow[key] = row[key]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -532,7 +529,7 @@ export class ExternalRequest<T extends Operation> {
|
||||||
buildRelationships(table: Table): RelationshipsJson[] {
|
buildRelationships(table: Table): RelationshipsJson[] {
|
||||||
const relationships = []
|
const relationships = []
|
||||||
for (let [fieldName, field] of Object.entries(table.schema)) {
|
for (let [fieldName, field] of Object.entries(table.schema)) {
|
||||||
if (field.type !== FieldTypes.LINK) {
|
if (field.type !== FieldType.LINK) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
||||||
|
@ -586,7 +583,7 @@ export class ExternalRequest<T extends Operation> {
|
||||||
// we need this to work out if any relationships need removed
|
// we need this to work out if any relationships need removed
|
||||||
for (const field of Object.values(table.schema)) {
|
for (const field of Object.values(table.schema)) {
|
||||||
if (
|
if (
|
||||||
field.type !== FieldTypes.LINK ||
|
field.type !== FieldType.LINK ||
|
||||||
!field.fieldName ||
|
!field.fieldName ||
|
||||||
isOneSide(field)
|
isOneSide(field)
|
||||||
) {
|
) {
|
||||||
|
@ -730,15 +727,15 @@ export class ExternalRequest<T extends Operation> {
|
||||||
return Object.entries(table.schema)
|
return Object.entries(table.schema)
|
||||||
.filter(
|
.filter(
|
||||||
column =>
|
column =>
|
||||||
column[1].type !== FieldTypes.LINK &&
|
column[1].type !== FieldType.LINK &&
|
||||||
column[1].type !== FieldTypes.FORMULA &&
|
column[1].type !== FieldType.FORMULA &&
|
||||||
!existing.find((field: string) => field === column[0])
|
!existing.find((field: string) => field === column[0])
|
||||||
)
|
)
|
||||||
.map(column => `${table.name}.${column[0]}`)
|
.map(column => `${table.name}.${column[0]}`)
|
||||||
}
|
}
|
||||||
let fields = extractRealFields(table)
|
let fields = extractRealFields(table)
|
||||||
for (let field of Object.values(table.schema)) {
|
for (let field of Object.values(table.schema)) {
|
||||||
if (field.type !== FieldTypes.LINK || !includeRelations) {
|
if (field.type !== FieldType.LINK || !includeRelations) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { FieldTypes } from "../../../constants"
|
|
||||||
import {
|
import {
|
||||||
breakExternalTableId,
|
breakExternalTableId,
|
||||||
breakRowIdField,
|
breakRowIdField,
|
||||||
|
@ -9,6 +8,7 @@ import {
|
||||||
RunConfig,
|
RunConfig,
|
||||||
} from "./ExternalRequest"
|
} from "./ExternalRequest"
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
Datasource,
|
Datasource,
|
||||||
IncludeRelationship,
|
IncludeRelationship,
|
||||||
Operation,
|
Operation,
|
||||||
|
@ -154,7 +154,7 @@ export async function fetchEnrichedRow(ctx: UserCtx) {
|
||||||
// for a single row, there is probably a better way to do this with some smart multi-layer joins
|
// for a single row, there is probably a better way to do this with some smart multi-layer joins
|
||||||
for (let [fieldName, field] of Object.entries(table.schema)) {
|
for (let [fieldName, field] of Object.entries(table.schema)) {
|
||||||
if (
|
if (
|
||||||
field.type !== FieldTypes.LINK ||
|
field.type !== FieldType.LINK ||
|
||||||
!row[fieldName] ||
|
!row[fieldName] ||
|
||||||
row[fieldName].length === 0
|
row[fieldName].length === 0
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -6,12 +6,12 @@ import {
|
||||||
inputProcessing,
|
inputProcessing,
|
||||||
outputProcessing,
|
outputProcessing,
|
||||||
} from "../../../utilities/rowProcessor"
|
} from "../../../utilities/rowProcessor"
|
||||||
import { FieldTypes } from "../../../constants"
|
|
||||||
import * as utils from "./utils"
|
import * as utils from "./utils"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { context } from "@budibase/backend-core"
|
import { context } from "@budibase/backend-core"
|
||||||
import { finaliseRow, updateRelatedFormula } from "./staticFormula"
|
import { finaliseRow, updateRelatedFormula } from "./staticFormula"
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
LinkDocumentValue,
|
LinkDocumentValue,
|
||||||
PatchRowRequest,
|
PatchRowRequest,
|
||||||
PatchRowResponse,
|
PatchRowResponse,
|
||||||
|
@ -225,7 +225,7 @@ export async function fetchEnrichedRow(ctx: UserCtx) {
|
||||||
// insert the link rows in the correct place throughout the main row
|
// insert the link rows in the correct place throughout the main row
|
||||||
for (let fieldName of Object.keys(table.schema)) {
|
for (let fieldName of Object.keys(table.schema)) {
|
||||||
let field = table.schema[fieldName]
|
let field = table.schema[fieldName]
|
||||||
if (field.type === FieldTypes.LINK) {
|
if (field.type === FieldType.LINK) {
|
||||||
// find the links that pertain to this field
|
// find the links that pertain to this field
|
||||||
const links = linkVals.filter(link => link.fieldName === fieldName)
|
const links = linkVals.filter(link => link.fieldName === fieldName)
|
||||||
// find the rows that the links state are linked to this field
|
// find the rows that the links state are linked to this field
|
||||||
|
|
|
@ -4,9 +4,15 @@ import {
|
||||||
processAutoColumn,
|
processAutoColumn,
|
||||||
processFormulas,
|
processFormulas,
|
||||||
} from "../../../utilities/rowProcessor"
|
} from "../../../utilities/rowProcessor"
|
||||||
import { FieldTypes, FormulaTypes } from "../../../constants"
|
|
||||||
import { context, locks } from "@budibase/backend-core"
|
import { context, locks } from "@budibase/backend-core"
|
||||||
import { Table, Row, LockType, LockName } from "@budibase/types"
|
import {
|
||||||
|
Table,
|
||||||
|
Row,
|
||||||
|
LockType,
|
||||||
|
LockName,
|
||||||
|
FormulaType,
|
||||||
|
FieldType,
|
||||||
|
} from "@budibase/types"
|
||||||
import * as linkRows from "../../../db/linkedRows"
|
import * as linkRows from "../../../db/linkedRows"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
import isEqual from "lodash/isEqual"
|
import isEqual from "lodash/isEqual"
|
||||||
|
@ -35,7 +41,7 @@ export async function updateRelatedFormula(
|
||||||
let relatedRows: Record<string, Row[]> = {}
|
let relatedRows: Record<string, Row[]> = {}
|
||||||
for (let [key, field] of Object.entries(enrichedRow)) {
|
for (let [key, field] of Object.entries(enrichedRow)) {
|
||||||
const columnDefinition = table.schema[key]
|
const columnDefinition = table.schema[key]
|
||||||
if (columnDefinition && columnDefinition.type === FieldTypes.LINK) {
|
if (columnDefinition && columnDefinition.type === FieldType.LINK) {
|
||||||
const relatedTableId = columnDefinition.tableId!
|
const relatedTableId = columnDefinition.tableId!
|
||||||
if (!relatedRows[relatedTableId]) {
|
if (!relatedRows[relatedTableId]) {
|
||||||
relatedRows[relatedTableId] = []
|
relatedRows[relatedTableId] = []
|
||||||
|
@ -63,8 +69,8 @@ export async function updateRelatedFormula(
|
||||||
for (let column of Object.values(relatedTable!.schema)) {
|
for (let column of Object.values(relatedTable!.schema)) {
|
||||||
// needs updated in related rows
|
// needs updated in related rows
|
||||||
if (
|
if (
|
||||||
column.type === FieldTypes.FORMULA &&
|
column.type === FieldType.FORMULA &&
|
||||||
column.formulaType === FormulaTypes.STATIC
|
column.formulaType === FormulaType.STATIC
|
||||||
) {
|
) {
|
||||||
// re-enrich rows for all the related, don't update the related formula for them
|
// re-enrich rows for all the related, don't update the related formula for them
|
||||||
promises = promises.concat(
|
promises = promises.concat(
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { FormulaTypes } from "../../../constants"
|
|
||||||
import { clearColumns } from "./utils"
|
import { clearColumns } from "./utils"
|
||||||
import { doesContainStrings } from "@budibase/string-templates"
|
import { doesContainStrings } from "@budibase/string-templates"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
@ -7,6 +6,7 @@ import uniq from "lodash/uniq"
|
||||||
import { updateAllFormulasInTable } from "../row/staticFormula"
|
import { updateAllFormulasInTable } from "../row/staticFormula"
|
||||||
import { context } from "@budibase/backend-core"
|
import { context } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
|
FormulaType,
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
FieldType,
|
FieldType,
|
||||||
FormulaFieldMetadata,
|
FormulaFieldMetadata,
|
||||||
|
@ -17,10 +17,10 @@ import { isRelationshipColumn } from "../../../db/utils"
|
||||||
|
|
||||||
function isStaticFormula(
|
function isStaticFormula(
|
||||||
column: FieldSchema
|
column: FieldSchema
|
||||||
): column is FormulaFieldMetadata & { formulaType: FormulaTypes.STATIC } {
|
): column is FormulaFieldMetadata & { formulaType: FormulaType.STATIC } {
|
||||||
return (
|
return (
|
||||||
column.type === FieldType.FORMULA &&
|
column.type === FieldType.FORMULA &&
|
||||||
column.formulaType === FormulaTypes.STATIC
|
column.formulaType === FormulaType.STATIC
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { FieldType } from "@budibase/types"
|
import { AutoFieldSubType, FieldType } from "@budibase/types"
|
||||||
import { AutoFieldSubTypes } from "../../../../constants"
|
|
||||||
import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
|
import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
|
||||||
import { importToRows } from "../utils"
|
import { importToRows } from "../utils"
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ describe("utils", () => {
|
||||||
autoId: {
|
autoId: {
|
||||||
name: "autoId",
|
name: "autoId",
|
||||||
type: FieldType.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldType.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
|
@ -69,7 +68,7 @@ describe("utils", () => {
|
||||||
autoId: {
|
autoId: {
|
||||||
name: "autoId",
|
name: "autoId",
|
||||||
type: FieldType.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldType.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
|
|
|
@ -2,8 +2,6 @@ import { parse, isSchema, isRows } from "../../../utilities/schema"
|
||||||
import { getRowParams, generateRowID, InternalTables } from "../../../db/utils"
|
import { getRowParams, generateRowID, InternalTables } from "../../../db/utils"
|
||||||
import isEqual from "lodash/isEqual"
|
import isEqual from "lodash/isEqual"
|
||||||
import {
|
import {
|
||||||
AutoFieldSubTypes,
|
|
||||||
FieldTypes,
|
|
||||||
GOOGLE_SHEETS_PRIMARY_KEY,
|
GOOGLE_SHEETS_PRIMARY_KEY,
|
||||||
USERS_TABLE_SCHEMA,
|
USERS_TABLE_SCHEMA,
|
||||||
SwitchableTypes,
|
SwitchableTypes,
|
||||||
|
@ -19,6 +17,7 @@ import { cloneDeep } from "lodash/fp"
|
||||||
import { quotas } from "@budibase/pro"
|
import { quotas } from "@budibase/pro"
|
||||||
import { events, context } from "@budibase/backend-core"
|
import { events, context } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
|
AutoFieldSubType,
|
||||||
ContextUser,
|
ContextUser,
|
||||||
Datasource,
|
Datasource,
|
||||||
Row,
|
Row,
|
||||||
|
@ -106,7 +105,7 @@ export function makeSureTableUpToDate(table: Table, tableToSave: Table) {
|
||||||
for ([field, column] of Object.entries(table.schema)) {
|
for ([field, column] of Object.entries(table.schema)) {
|
||||||
if (
|
if (
|
||||||
column.autocolumn &&
|
column.autocolumn &&
|
||||||
column.subtype === AutoFieldSubTypes.AUTO_ID &&
|
column.subtype === AutoFieldSubType.AUTO_ID &&
|
||||||
tableToSave.schema[field]
|
tableToSave.schema[field]
|
||||||
) {
|
) {
|
||||||
const tableCol = tableToSave.schema[field] as NumberFieldMetadata
|
const tableCol = tableToSave.schema[field] as NumberFieldMetadata
|
||||||
|
@ -144,8 +143,8 @@ export async function importToRows(
|
||||||
? row[fieldName]
|
? row[fieldName]
|
||||||
: [row[fieldName]]
|
: [row[fieldName]]
|
||||||
if (
|
if (
|
||||||
(schema.type === FieldTypes.OPTIONS ||
|
(schema.type === FieldType.OPTIONS ||
|
||||||
schema.type === FieldTypes.ARRAY) &&
|
schema.type === FieldType.ARRAY) &&
|
||||||
row[fieldName]
|
row[fieldName]
|
||||||
) {
|
) {
|
||||||
let merged = [...schema.constraints!.inclusion!, ...rowVal]
|
let merged = [...schema.constraints!.inclusion!, ...rowVal]
|
||||||
|
@ -403,7 +402,7 @@ export async function checkForViewUpdates(
|
||||||
)
|
)
|
||||||
const newViewTemplate = viewTemplate(
|
const newViewTemplate = viewTemplate(
|
||||||
viewMetadata,
|
viewMetadata,
|
||||||
groupByField?.type === FieldTypes.ARRAY
|
groupByField?.type === FieldType.ARRAY
|
||||||
)
|
)
|
||||||
const viewName = view.name!
|
const viewName = view.name!
|
||||||
await saveView(null, viewName, newViewTemplate)
|
await saveView(null, viewName, newViewTemplate)
|
||||||
|
@ -434,7 +433,7 @@ export function generateJunctionTableName(
|
||||||
|
|
||||||
export function foreignKeyStructure(keyName: string, meta?: any) {
|
export function foreignKeyStructure(keyName: string, meta?: any) {
|
||||||
const structure: any = {
|
const structure: any = {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
constraints: {},
|
constraints: {},
|
||||||
name: keyName,
|
name: keyName,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { fetchView } from "../row"
|
||||||
import { context, events } from "@budibase/backend-core"
|
import { context, events } from "@budibase/backend-core"
|
||||||
import { DocumentType } from "../../../db/utils"
|
import { DocumentType } from "../../../db/utils"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
import { FieldTypes } from "../../../constants"
|
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
Ctx,
|
Ctx,
|
||||||
Row,
|
Row,
|
||||||
Table,
|
Table,
|
||||||
|
@ -37,7 +37,7 @@ export async function save(ctx: Ctx) {
|
||||||
(field: any) => field.name == viewToSave.groupBy
|
(field: any) => field.name == viewToSave.groupBy
|
||||||
)
|
)
|
||||||
|
|
||||||
const view = viewTemplate(viewToSave, groupByField?.type === FieldTypes.ARRAY)
|
const view = viewTemplate(viewToSave, groupByField?.type === FieldType.ARRAY)
|
||||||
const viewName = viewToSave.name
|
const viewName = viewToSave.name
|
||||||
|
|
||||||
if (!viewName) {
|
if (!viewName) {
|
||||||
|
|
|
@ -6,11 +6,11 @@ import * as setup from "./utilities"
|
||||||
import { context, InternalTable, roles, tenancy } from "@budibase/backend-core"
|
import { context, InternalTable, roles, tenancy } from "@budibase/backend-core"
|
||||||
import { quotas } from "@budibase/pro"
|
import { quotas } from "@budibase/pro"
|
||||||
import {
|
import {
|
||||||
AutoFieldSubTypes,
|
AutoFieldSubType,
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
FieldType,
|
FieldType,
|
||||||
FieldTypeSubtypes,
|
FieldTypeSubtypes,
|
||||||
FormulaTypes,
|
FormulaType,
|
||||||
INTERNAL_TABLE_SOURCE_ID,
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
MonthlyQuotaName,
|
MonthlyQuotaName,
|
||||||
PermissionLevel,
|
PermissionLevel,
|
||||||
|
@ -192,7 +192,7 @@ describe.each([
|
||||||
"Row ID": {
|
"Row ID": {
|
||||||
name: "Row ID",
|
name: "Row ID",
|
||||||
type: FieldType.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
|
@ -2032,7 +2032,7 @@ describe.each([
|
||||||
name: "formula",
|
name: "formula",
|
||||||
type: FieldType.FORMULA,
|
type: FieldType.FORMULA,
|
||||||
formula: "{{ links.0.name }}",
|
formula: "{{ links.0.name }}",
|
||||||
formulaType: FormulaTypes.DYNAMIC,
|
formulaType: FormulaType.DYNAMIC,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -2086,7 +2086,7 @@ describe.each([
|
||||||
name: "formula",
|
name: "formula",
|
||||||
type: FieldType.FORMULA,
|
type: FieldType.FORMULA,
|
||||||
formula: `{{ js "${js}"}}`,
|
formula: `{{ js "${js}"}}`,
|
||||||
formulaType: FormulaTypes.DYNAMIC,
|
formulaType: FormulaType.DYNAMIC,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -2129,7 +2129,7 @@ describe.each([
|
||||||
name: "formula",
|
name: "formula",
|
||||||
type: FieldType.FORMULA,
|
type: FieldType.FORMULA,
|
||||||
formula: `{{ js "${js}"}}`,
|
formula: `{{ js "${js}"}}`,
|
||||||
formulaType: FormulaTypes.DYNAMIC,
|
formulaType: FormulaType.DYNAMIC,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { context, events } from "@budibase/backend-core"
|
import { context, events } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
AutoFieldSubTypes,
|
AutoFieldSubType,
|
||||||
FieldSubtype,
|
FieldSubtype,
|
||||||
FieldType,
|
FieldType,
|
||||||
INTERNAL_TABLE_SOURCE_ID,
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
|
@ -205,7 +205,7 @@ describe("/tables", () => {
|
||||||
autoId: {
|
autoId: {
|
||||||
name: "id",
|
name: "id",
|
||||||
type: FieldType.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "number",
|
type: "number",
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import * as rowController from "../../api/controllers/row"
|
import * as rowController from "../../api/controllers/row"
|
||||||
import * as tableController from "../../api/controllers/table"
|
import * as tableController from "../../api/controllers/table"
|
||||||
import { FieldTypes } from "../../constants"
|
|
||||||
import { buildCtx } from "./utils"
|
import { buildCtx } from "./utils"
|
||||||
import * as automationUtils from "../automationUtils"
|
import * as automationUtils from "../automationUtils"
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
AutomationActionStepId,
|
AutomationActionStepId,
|
||||||
AutomationCustomIOType,
|
AutomationCustomIOType,
|
||||||
AutomationFeature,
|
AutomationFeature,
|
||||||
|
@ -115,7 +115,7 @@ function typeCoercion(filters: SearchFilters, table: Table) {
|
||||||
if (!column || typeof value !== "string") {
|
if (!column || typeof value !== "string") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (column.type === FieldTypes.NUMBER) {
|
if (column.type === FieldType.NUMBER) {
|
||||||
if (key === "oneOf") {
|
if (key === "oneOf") {
|
||||||
searchParam[property] = value
|
searchParam[property] = value
|
||||||
.split(",")
|
.split(",")
|
||||||
|
@ -148,11 +148,11 @@ export async function run({ inputs, appId }: AutomationStepInput) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const table = await getTable(appId, tableId)
|
const table = await getTable(appId, tableId)
|
||||||
let sortType = FieldTypes.STRING
|
let sortType = FieldType.STRING
|
||||||
if (table && table.schema && table.schema[sortColumn] && sortColumn) {
|
if (table && table.schema && table.schema[sortColumn] && sortColumn) {
|
||||||
const fieldType = table.schema[sortColumn].type
|
const fieldType = table.schema[sortColumn].type
|
||||||
sortType =
|
sortType =
|
||||||
fieldType === FieldTypes.NUMBER ? FieldTypes.NUMBER : FieldTypes.STRING
|
fieldType === FieldType.NUMBER ? FieldType.NUMBER : FieldType.STRING
|
||||||
}
|
}
|
||||||
const ctx: any = buildCtx(appId, null, {
|
const ctx: any = buildCtx(appId, null, {
|
||||||
params: {
|
params: {
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
import { constants, objectStore, roles } from "@budibase/backend-core"
|
import { constants, objectStore, roles } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
FieldType as FieldTypes,
|
FieldType,
|
||||||
INTERNAL_TABLE_SOURCE_ID,
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
Table,
|
Table,
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
|
||||||
export {
|
|
||||||
FieldType as FieldTypes,
|
|
||||||
RelationshipType,
|
|
||||||
AutoFieldSubTypes,
|
|
||||||
FormulaTypes,
|
|
||||||
} from "@budibase/types"
|
|
||||||
|
|
||||||
export enum FilterTypes {
|
export enum FilterTypes {
|
||||||
STRING = "string",
|
STRING = "string",
|
||||||
FUZZY = "fuzzy",
|
FUZZY = "fuzzy",
|
||||||
|
@ -36,14 +29,14 @@ export const NoEmptyFilterStrings = [
|
||||||
]
|
]
|
||||||
|
|
||||||
export const CanSwitchTypes = [
|
export const CanSwitchTypes = [
|
||||||
[FieldTypes.JSON, FieldTypes.ARRAY],
|
[FieldType.JSON, FieldType.ARRAY],
|
||||||
[
|
[
|
||||||
FieldTypes.STRING,
|
FieldType.STRING,
|
||||||
FieldTypes.OPTIONS,
|
FieldType.OPTIONS,
|
||||||
FieldTypes.LONGFORM,
|
FieldType.LONGFORM,
|
||||||
FieldTypes.BARCODEQR,
|
FieldType.BARCODEQR,
|
||||||
],
|
],
|
||||||
[FieldTypes.BOOLEAN, FieldTypes.NUMBER],
|
[FieldType.BOOLEAN, FieldType.NUMBER],
|
||||||
]
|
]
|
||||||
|
|
||||||
export const SwitchableTypes = CanSwitchTypes.reduce((prev, current) =>
|
export const SwitchableTypes = CanSwitchTypes.reduce((prev, current) =>
|
||||||
|
@ -86,9 +79,9 @@ export const USERS_TABLE_SCHEMA: Table = {
|
||||||
// TODO: ADMIN PANEL - when implemented this doesn't need to be carried out
|
// TODO: ADMIN PANEL - when implemented this doesn't need to be carried out
|
||||||
schema: {
|
schema: {
|
||||||
email: {
|
email: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
email: true,
|
email: true,
|
||||||
length: {
|
length: {
|
||||||
maximum: "",
|
maximum: "",
|
||||||
|
@ -99,34 +92,34 @@ export const USERS_TABLE_SCHEMA: Table = {
|
||||||
},
|
},
|
||||||
firstName: {
|
firstName: {
|
||||||
name: "firstName",
|
name: "firstName",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lastName: {
|
lastName: {
|
||||||
name: "lastName",
|
name: "lastName",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
roleId: {
|
roleId: {
|
||||||
name: "roleId",
|
name: "roleId",
|
||||||
type: FieldTypes.OPTIONS,
|
type: FieldType.OPTIONS,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
presence: false,
|
presence: false,
|
||||||
inclusion: Object.values(roles.BUILTIN_ROLE_IDS),
|
inclusion: Object.values(roles.BUILTIN_ROLE_IDS),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
name: "status",
|
name: "status",
|
||||||
type: FieldTypes.OPTIONS,
|
type: FieldType.OPTIONS,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
presence: false,
|
presence: false,
|
||||||
inclusion: Object.values(constants.UserStatus),
|
inclusion: Object.values(constants.UserStatus),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import {
|
import {
|
||||||
AutoFieldSubTypes,
|
|
||||||
FieldTypes,
|
|
||||||
DEFAULT_BB_DATASOURCE_ID,
|
DEFAULT_BB_DATASOURCE_ID,
|
||||||
DEFAULT_INVENTORY_TABLE_ID,
|
DEFAULT_INVENTORY_TABLE_ID,
|
||||||
DEFAULT_EMPLOYEE_TABLE_ID,
|
DEFAULT_EMPLOYEE_TABLE_ID,
|
||||||
|
@ -16,6 +14,7 @@ import { jobsImport } from "./jobsImport"
|
||||||
import { expensesImport } from "./expensesImport"
|
import { expensesImport } from "./expensesImport"
|
||||||
import { db as dbCore } from "@budibase/backend-core"
|
import { db as dbCore } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
|
AutoFieldSubType,
|
||||||
FieldType,
|
FieldType,
|
||||||
RelationshipType,
|
RelationshipType,
|
||||||
Row,
|
Row,
|
||||||
|
@ -40,7 +39,7 @@ function syncLastIds(table: Table, rowCount: number) {
|
||||||
if (
|
if (
|
||||||
entry.autocolumn &&
|
entry.autocolumn &&
|
||||||
entry.type === FieldType.NUMBER &&
|
entry.type === FieldType.NUMBER &&
|
||||||
entry.subtype == AutoFieldSubTypes.AUTO_ID
|
entry.subtype == AutoFieldSubType.AUTO_ID
|
||||||
) {
|
) {
|
||||||
entry.lastID = rowCount
|
entry.lastID = rowCount
|
||||||
}
|
}
|
||||||
|
@ -58,12 +57,12 @@ async function tableImport(table: Table, data: Row[]) {
|
||||||
const AUTO_COLUMNS: TableSchema = {
|
const AUTO_COLUMNS: TableSchema = {
|
||||||
"Created At": {
|
"Created At": {
|
||||||
name: "Created At",
|
name: "Created At",
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
subtype: AutoFieldSubTypes.CREATED_AT,
|
subtype: AutoFieldSubType.CREATED_AT,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -74,12 +73,12 @@ const AUTO_COLUMNS: TableSchema = {
|
||||||
},
|
},
|
||||||
"Updated At": {
|
"Updated At": {
|
||||||
name: "Updated At",
|
name: "Updated At",
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
subtype: AutoFieldSubTypes.UPDATED_AT,
|
subtype: AutoFieldSubType.UPDATED_AT,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -101,12 +100,12 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
schema: {
|
schema: {
|
||||||
"Item ID": {
|
"Item ID": {
|
||||||
name: "Item ID",
|
name: "Item ID",
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: false,
|
presence: false,
|
||||||
numericality: {
|
numericality: {
|
||||||
greaterThanOrEqualTo: "",
|
greaterThanOrEqualTo: "",
|
||||||
|
@ -115,9 +114,9 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Item Name": {
|
"Item Name": {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {
|
length: {
|
||||||
maximum: null,
|
maximum: null,
|
||||||
},
|
},
|
||||||
|
@ -128,9 +127,9 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
name: "Item Name",
|
name: "Item Name",
|
||||||
},
|
},
|
||||||
"Item Tags": {
|
"Item Tags": {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: {
|
presence: {
|
||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
},
|
},
|
||||||
|
@ -140,9 +139,9 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
Notes: {
|
Notes: {
|
||||||
type: FieldTypes.LONGFORM,
|
type: FieldType.LONGFORM,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
|
@ -150,9 +149,9 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
useRichText: null,
|
useRichText: null,
|
||||||
},
|
},
|
||||||
Status: {
|
Status: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: {
|
presence: {
|
||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
},
|
},
|
||||||
|
@ -162,18 +161,18 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
SKU: {
|
SKU: {
|
||||||
type: FieldTypes.BARCODEQR,
|
type: FieldType.BARCODEQR,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
name: "SKU",
|
name: "SKU",
|
||||||
},
|
},
|
||||||
"Purchase Date": {
|
"Purchase Date": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -185,9 +184,9 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
"Purchase Price": {
|
"Purchase Price": {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: false,
|
presence: false,
|
||||||
numericality: {
|
numericality: {
|
||||||
greaterThanOrEqualTo: null,
|
greaterThanOrEqualTo: null,
|
||||||
|
@ -211,75 +210,75 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
|
||||||
schema: {
|
schema: {
|
||||||
"First Name": {
|
"First Name": {
|
||||||
name: "First Name",
|
name: "First Name",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Last Name": {
|
"Last Name": {
|
||||||
name: "Last Name",
|
name: "Last Name",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Email: {
|
Email: {
|
||||||
name: "Email",
|
name: "Email",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Address: {
|
Address: {
|
||||||
name: "Address",
|
name: "Address",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
City: {
|
City: {
|
||||||
name: "City",
|
name: "City",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Postcode: {
|
Postcode: {
|
||||||
name: "Postcode",
|
name: "Postcode",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Phone: {
|
Phone: {
|
||||||
name: "Phone",
|
name: "Phone",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"EMPLOYEE ID": {
|
"EMPLOYEE ID": {
|
||||||
name: "EMPLOYEE ID",
|
name: "EMPLOYEE ID",
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: false,
|
presence: false,
|
||||||
numericality: {
|
numericality: {
|
||||||
greaterThanOrEqualTo: "",
|
greaterThanOrEqualTo: "",
|
||||||
|
@ -288,9 +287,9 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Employee Level": {
|
"Employee Level": {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: false,
|
presence: false,
|
||||||
inclusion: ["Manager", "Junior", "Senior", "Apprentice", "Contractor"],
|
inclusion: ["Manager", "Junior", "Senior", "Apprentice", "Contractor"],
|
||||||
},
|
},
|
||||||
|
@ -298,18 +297,18 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
"Badge Photo": {
|
"Badge Photo": {
|
||||||
type: FieldTypes.ATTACHMENT,
|
type: FieldType.ATTACHMENT,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
name: "Badge Photo",
|
name: "Badge Photo",
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
Jobs: {
|
Jobs: {
|
||||||
type: FieldTypes.LINK,
|
type: FieldType.LINK,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
fieldName: "Assigned",
|
fieldName: "Assigned",
|
||||||
|
@ -318,9 +317,9 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
|
||||||
tableId: DEFAULT_JOBS_TABLE_ID,
|
tableId: DEFAULT_JOBS_TABLE_ID,
|
||||||
},
|
},
|
||||||
"Start Date": {
|
"Start Date": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -332,9 +331,9 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
"End Date": {
|
"End Date": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -359,12 +358,12 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
schema: {
|
schema: {
|
||||||
"Job ID": {
|
"Job ID": {
|
||||||
name: "Job ID",
|
name: "Job ID",
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: false,
|
presence: false,
|
||||||
numericality: {
|
numericality: {
|
||||||
greaterThanOrEqualTo: "",
|
greaterThanOrEqualTo: "",
|
||||||
|
@ -373,9 +372,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Quote Date": {
|
"Quote Date": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: {
|
presence: {
|
||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
|
@ -389,9 +388,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
"Quote Price": {
|
"Quote Price": {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: {
|
presence: {
|
||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
},
|
},
|
||||||
|
@ -403,9 +402,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
name: "Quote Price",
|
name: "Quote Price",
|
||||||
},
|
},
|
||||||
"Works Start": {
|
"Works Start": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -417,9 +416,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
Address: {
|
Address: {
|
||||||
type: FieldTypes.LONGFORM,
|
type: FieldType.LONGFORM,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
|
@ -427,9 +426,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
useRichText: null,
|
useRichText: null,
|
||||||
},
|
},
|
||||||
"Customer Name": {
|
"Customer Name": {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {
|
length: {
|
||||||
maximum: null,
|
maximum: null,
|
||||||
},
|
},
|
||||||
|
@ -438,9 +437,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
name: "Customer Name",
|
name: "Customer Name",
|
||||||
},
|
},
|
||||||
Notes: {
|
Notes: {
|
||||||
type: FieldTypes.LONGFORM,
|
type: FieldType.LONGFORM,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
|
@ -448,9 +447,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
useRichText: null,
|
useRichText: null,
|
||||||
},
|
},
|
||||||
"Customer Phone": {
|
"Customer Phone": {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {
|
length: {
|
||||||
maximum: null,
|
maximum: null,
|
||||||
},
|
},
|
||||||
|
@ -459,9 +458,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
name: "Customer Phone",
|
name: "Customer Phone",
|
||||||
},
|
},
|
||||||
"Customer Email": {
|
"Customer Email": {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {
|
length: {
|
||||||
maximum: null,
|
maximum: null,
|
||||||
},
|
},
|
||||||
|
@ -471,14 +470,14 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
},
|
},
|
||||||
Assigned: {
|
Assigned: {
|
||||||
name: "Assigned",
|
name: "Assigned",
|
||||||
type: FieldTypes.LINK,
|
type: FieldType.LINK,
|
||||||
tableId: DEFAULT_EMPLOYEE_TABLE_ID,
|
tableId: DEFAULT_EMPLOYEE_TABLE_ID,
|
||||||
fieldName: "Jobs",
|
fieldName: "Jobs",
|
||||||
relationshipType: RelationshipType.MANY_TO_MANY,
|
relationshipType: RelationshipType.MANY_TO_MANY,
|
||||||
// sortable: true,
|
// sortable: true,
|
||||||
},
|
},
|
||||||
"Works End": {
|
"Works End": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
length: {},
|
length: {},
|
||||||
|
@ -492,7 +491,7 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
"Updated Price": {
|
"Updated Price": {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "number",
|
type: "number",
|
||||||
presence: false,
|
presence: false,
|
||||||
|
@ -518,12 +517,12 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
schema: {
|
schema: {
|
||||||
"Expense ID": {
|
"Expense ID": {
|
||||||
name: "Expense ID",
|
name: "Expense ID",
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: false,
|
presence: false,
|
||||||
numericality: {
|
numericality: {
|
||||||
greaterThanOrEqualTo: "",
|
greaterThanOrEqualTo: "",
|
||||||
|
@ -532,9 +531,9 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Expense Tags": {
|
"Expense Tags": {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: {
|
presence: {
|
||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
},
|
},
|
||||||
|
@ -554,9 +553,9 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
Cost: {
|
Cost: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: {
|
presence: {
|
||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
},
|
},
|
||||||
|
@ -568,9 +567,9 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
name: "Cost",
|
name: "Cost",
|
||||||
},
|
},
|
||||||
Notes: {
|
Notes: {
|
||||||
type: FieldTypes.LONGFORM,
|
type: FieldType.LONGFORM,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
|
@ -578,9 +577,9 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
useRichText: null,
|
useRichText: null,
|
||||||
},
|
},
|
||||||
"Payment Due": {
|
"Payment Due": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -592,9 +591,9 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
"Date Paid": {
|
"Date Paid": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -606,9 +605,9 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
Attachment: {
|
Attachment: {
|
||||||
type: FieldTypes.ATTACHMENT,
|
type: FieldType.ATTACHMENT,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
name: "Attachment",
|
name: "Attachment",
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { IncludeDocs, getLinkDocuments } from "./linkUtils"
|
import { IncludeDocs, getLinkDocuments } from "./linkUtils"
|
||||||
import { InternalTables, getUserMetadataParams } from "../utils"
|
import { InternalTables, getUserMetadataParams } from "../utils"
|
||||||
import { FieldTypes } from "../../constants"
|
|
||||||
import { context, logging } from "@budibase/backend-core"
|
import { context, logging } from "@budibase/backend-core"
|
||||||
import LinkDocument from "./LinkDocument"
|
import LinkDocument from "./LinkDocument"
|
||||||
import {
|
import {
|
||||||
|
@ -62,7 +61,7 @@ class LinkController {
|
||||||
}
|
}
|
||||||
for (let fieldName of Object.keys(table.schema)) {
|
for (let fieldName of Object.keys(table.schema)) {
|
||||||
const { type } = table.schema[fieldName]
|
const { type } = table.schema[fieldName]
|
||||||
if (type === FieldTypes.LINK) {
|
if (type === FieldType.LINK) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +95,7 @@ class LinkController {
|
||||||
validateTable(table: Table) {
|
validateTable(table: Table) {
|
||||||
const usedAlready = []
|
const usedAlready = []
|
||||||
for (let schema of Object.values(table.schema)) {
|
for (let schema of Object.values(table.schema)) {
|
||||||
if (schema.type !== FieldTypes.LINK) {
|
if (schema.type !== FieldType.LINK) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const unique = schema.tableId! + schema?.fieldName
|
const unique = schema.tableId! + schema?.fieldName
|
||||||
|
@ -172,7 +171,7 @@ class LinkController {
|
||||||
// get the links this row wants to make
|
// get the links this row wants to make
|
||||||
const rowField = row[fieldName]
|
const rowField = row[fieldName]
|
||||||
const field = table.schema[fieldName]
|
const field = table.schema[fieldName]
|
||||||
if (field.type === FieldTypes.LINK && rowField != null) {
|
if (field.type === FieldType.LINK && rowField != null) {
|
||||||
// check which links actual pertain to the update in this row
|
// check which links actual pertain to the update in this row
|
||||||
const thisFieldLinkDocs = linkDocs.filter(
|
const thisFieldLinkDocs = linkDocs.filter(
|
||||||
linkDoc =>
|
linkDoc =>
|
||||||
|
@ -353,7 +352,7 @@ class LinkController {
|
||||||
const schema = table.schema
|
const schema = table.schema
|
||||||
for (let fieldName of Object.keys(schema)) {
|
for (let fieldName of Object.keys(schema)) {
|
||||||
const field = schema[fieldName]
|
const field = schema[fieldName]
|
||||||
if (field.type === FieldTypes.LINK && field.fieldName) {
|
if (field.type === FieldType.LINK && field.fieldName) {
|
||||||
// handle this in a separate try catch, want
|
// handle this in a separate try catch, want
|
||||||
// the put to bubble up as an error, if can't update
|
// the put to bubble up as an error, if can't update
|
||||||
// table for some reason
|
// table for some reason
|
||||||
|
@ -366,7 +365,7 @@ class LinkController {
|
||||||
}
|
}
|
||||||
const fields = this.handleRelationshipType(field, {
|
const fields = this.handleRelationshipType(field, {
|
||||||
name: field.fieldName,
|
name: field.fieldName,
|
||||||
type: FieldTypes.LINK,
|
type: FieldType.LINK,
|
||||||
// these are the props of the table that initiated the link
|
// these are the props of the table that initiated the link
|
||||||
tableId: table._id!,
|
tableId: table._id!,
|
||||||
fieldName: fieldName,
|
fieldName: fieldName,
|
||||||
|
@ -413,10 +412,7 @@ class LinkController {
|
||||||
for (let fieldName of Object.keys(oldTable?.schema || {})) {
|
for (let fieldName of Object.keys(oldTable?.schema || {})) {
|
||||||
const field = oldTable?.schema[fieldName] as FieldSchema
|
const field = oldTable?.schema[fieldName] as FieldSchema
|
||||||
// this field has been removed from the table schema
|
// this field has been removed from the table schema
|
||||||
if (
|
if (field.type === FieldType.LINK && newTable.schema[fieldName] == null) {
|
||||||
field.type === FieldTypes.LINK &&
|
|
||||||
newTable.schema[fieldName] == null
|
|
||||||
) {
|
|
||||||
await this.removeFieldFromTable(fieldName)
|
await this.removeFieldFromTable(fieldName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -437,7 +433,7 @@ class LinkController {
|
||||||
for (let fieldName of Object.keys(schema)) {
|
for (let fieldName of Object.keys(schema)) {
|
||||||
const field = schema[fieldName]
|
const field = schema[fieldName]
|
||||||
try {
|
try {
|
||||||
if (field.type === FieldTypes.LINK && field.fieldName) {
|
if (field.type === FieldType.LINK && field.fieldName) {
|
||||||
const linkedTable = await this._db.get<Table>(field.tableId)
|
const linkedTable = await this._db.get<Table>(field.tableId)
|
||||||
delete linkedTable.schema[field.fieldName]
|
delete linkedTable.schema[field.fieldName]
|
||||||
field.tableRev = (await this._db.put(linkedTable)).rev
|
field.tableRev = (await this._db.put(linkedTable)).rev
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { generateLinkID } from "../utils"
|
import { generateLinkID } from "../utils"
|
||||||
import { FieldTypes } from "../../constants"
|
import { FieldType, LinkDocument } from "@budibase/types"
|
||||||
import { LinkDocument } from "@budibase/types"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new link document structure which can be put to the database. It is important to
|
* Creates a new link document structure which can be put to the database. It is important to
|
||||||
|
@ -43,7 +42,7 @@ class LinkDocumentImpl implements LinkDocument {
|
||||||
fieldName1,
|
fieldName1,
|
||||||
fieldName2
|
fieldName2
|
||||||
)
|
)
|
||||||
this.type = FieldTypes.LINK
|
this.type = FieldType.LINK
|
||||||
this.doc1 = {
|
this.doc1 = {
|
||||||
tableId: tableId1,
|
tableId: tableId1,
|
||||||
fieldName: fieldName1,
|
fieldName: fieldName1,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { ViewName, getQueryIndex, isRelationshipColumn } from "../utils"
|
import { ViewName, getQueryIndex, isRelationshipColumn } from "../utils"
|
||||||
import { FieldTypes } from "../../constants"
|
|
||||||
import { createLinkView } from "../views/staticViews"
|
import { createLinkView } from "../views/staticViews"
|
||||||
import { context, logging } from "@budibase/backend-core"
|
import { context, logging } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
DatabaseQueryOpts,
|
DatabaseQueryOpts,
|
||||||
LinkDocument,
|
LinkDocument,
|
||||||
LinkDocumentValue,
|
LinkDocumentValue,
|
||||||
|
@ -131,11 +131,11 @@ export async function getLinkedTable(id: string, tables: Table[]) {
|
||||||
export function getRelatedTableForField(table: Table, fieldName: string) {
|
export function getRelatedTableForField(table: Table, fieldName: string) {
|
||||||
// look to see if its on the table, straight in the schema
|
// look to see if its on the table, straight in the schema
|
||||||
const field = table.schema[fieldName]
|
const field = table.schema[fieldName]
|
||||||
if (field?.type === FieldTypes.LINK) {
|
if (field?.type === FieldType.LINK) {
|
||||||
return field.tableId
|
return field.tableId
|
||||||
}
|
}
|
||||||
for (let column of Object.values(table.schema)) {
|
for (let column of Object.values(table.schema)) {
|
||||||
if (column.type === FieldTypes.LINK && column.fieldName === fieldName) {
|
if (column.type === FieldType.LINK && column.fieldName === fieldName) {
|
||||||
return column.tableId
|
return column.tableId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,57 @@
|
||||||
const TestConfig = require("../../tests/utilities/TestConfiguration")
|
import TestConfig from "../../tests/utilities/TestConfiguration"
|
||||||
const {
|
import {
|
||||||
basicRow,
|
|
||||||
basicLinkedRow,
|
basicLinkedRow,
|
||||||
|
basicRow,
|
||||||
basicTable,
|
basicTable,
|
||||||
} = require("../../tests/utilities/structures")
|
} from "../../tests/utilities/structures"
|
||||||
const LinkController = require("../linkedRows/LinkController").default
|
import LinkController from "../linkedRows/LinkController"
|
||||||
const { context } = require("@budibase/backend-core")
|
import { context } from "@budibase/backend-core"
|
||||||
const { RelationshipType } = require("../../constants")
|
import {
|
||||||
const { cloneDeep } = require("lodash/fp")
|
FieldType,
|
||||||
|
ManyToManyRelationshipFieldMetadata,
|
||||||
|
ManyToOneRelationshipFieldMetadata,
|
||||||
|
OneToManyRelationshipFieldMetadata,
|
||||||
|
RelationshipFieldMetadata,
|
||||||
|
RelationshipType,
|
||||||
|
Row,
|
||||||
|
Table,
|
||||||
|
} from "@budibase/types"
|
||||||
|
import { cloneDeep } from "lodash"
|
||||||
|
|
||||||
|
const baseColumn = {
|
||||||
|
type: FieldType.LINK,
|
||||||
|
fieldName: "",
|
||||||
|
tableId: "",
|
||||||
|
name: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockManyToManyColumn(): ManyToManyRelationshipFieldMetadata {
|
||||||
|
return <ManyToManyRelationshipFieldMetadata>{
|
||||||
|
...baseColumn,
|
||||||
|
through: "",
|
||||||
|
throughFrom: "",
|
||||||
|
throughTo: "",
|
||||||
|
relationshipType: RelationshipType.MANY_TO_MANY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockManyToOneColumn(): ManyToOneRelationshipFieldMetadata {
|
||||||
|
return <ManyToOneRelationshipFieldMetadata>{
|
||||||
|
...baseColumn,
|
||||||
|
relationshipType: RelationshipType.MANY_TO_ONE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockOneToManyColumn(): OneToManyRelationshipFieldMetadata {
|
||||||
|
return <OneToManyRelationshipFieldMetadata>{
|
||||||
|
...baseColumn,
|
||||||
|
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe("test the link controller", () => {
|
describe("test the link controller", () => {
|
||||||
let config = new TestConfig()
|
let config = new TestConfig()
|
||||||
let table1, table2, appId
|
let table1: Table, table2: Table, appId: string
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const app = await config.init()
|
const app = await config.init()
|
||||||
|
@ -30,9 +70,18 @@ describe("test the link controller", () => {
|
||||||
|
|
||||||
afterAll(config.end)
|
afterAll(config.end)
|
||||||
|
|
||||||
async function createLinkController(table, row = null, oldTable = null) {
|
async function createLinkController(
|
||||||
|
table: Table,
|
||||||
|
row?: Row,
|
||||||
|
oldTable?: Table
|
||||||
|
) {
|
||||||
return context.doInAppContext(appId, () => {
|
return context.doInAppContext(appId, () => {
|
||||||
const linkConfig = {
|
const linkConfig: {
|
||||||
|
tableId?: string
|
||||||
|
table: Table
|
||||||
|
row?: Row
|
||||||
|
oldTable?: Table
|
||||||
|
} = {
|
||||||
tableId: table._id,
|
tableId: table._id,
|
||||||
table,
|
table,
|
||||||
}
|
}
|
||||||
|
@ -47,11 +96,11 @@ describe("test the link controller", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createLinkedRow(linkField = "link", t1 = table1, t2 = table2) {
|
async function createLinkedRow(linkField = "link", t1 = table1, t2 = table2) {
|
||||||
const row = await config.createRow(basicRow(t2._id))
|
const row = await config.createRow(basicRow(t2._id!))
|
||||||
const { _id } = await config.createRow(
|
const { _id } = await config.createRow(
|
||||||
basicLinkedRow(t1._id, row._id, linkField)
|
basicLinkedRow(t1._id!, row._id!, linkField)
|
||||||
)
|
)
|
||||||
return config.getRow(t1._id, _id)
|
return config.getRow(t1._id!, _id!)
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should be able to confirm if two table schemas are equal", async () => {
|
it("should be able to confirm if two table schemas are equal", async () => {
|
||||||
|
@ -71,6 +120,7 @@ describe("test the link controller", () => {
|
||||||
it("should be able to check the relationship types across two fields", async () => {
|
it("should be able to check the relationship types across two fields", async () => {
|
||||||
const controller = await createLinkController(table1)
|
const controller = await createLinkController(table1)
|
||||||
// empty case
|
// empty case
|
||||||
|
//@ts-ignore
|
||||||
let output = controller.handleRelationshipType({}, {})
|
let output = controller.handleRelationshipType({}, {})
|
||||||
expect(output.linkedField.relationshipType).toEqual(
|
expect(output.linkedField.relationshipType).toEqual(
|
||||||
RelationshipType.MANY_TO_MANY
|
RelationshipType.MANY_TO_MANY
|
||||||
|
@ -79,8 +129,8 @@ describe("test the link controller", () => {
|
||||||
RelationshipType.MANY_TO_MANY
|
RelationshipType.MANY_TO_MANY
|
||||||
)
|
)
|
||||||
output = controller.handleRelationshipType(
|
output = controller.handleRelationshipType(
|
||||||
{ relationshipType: RelationshipType.MANY_TO_MANY },
|
mockManyToManyColumn(),
|
||||||
{}
|
{} as any
|
||||||
)
|
)
|
||||||
expect(output.linkedField.relationshipType).toEqual(
|
expect(output.linkedField.relationshipType).toEqual(
|
||||||
RelationshipType.MANY_TO_MANY
|
RelationshipType.MANY_TO_MANY
|
||||||
|
@ -88,20 +138,14 @@ describe("test the link controller", () => {
|
||||||
expect(output.linkerField.relationshipType).toEqual(
|
expect(output.linkerField.relationshipType).toEqual(
|
||||||
RelationshipType.MANY_TO_MANY
|
RelationshipType.MANY_TO_MANY
|
||||||
)
|
)
|
||||||
output = controller.handleRelationshipType(
|
output = controller.handleRelationshipType(mockManyToOneColumn(), {} as any)
|
||||||
{ relationshipType: RelationshipType.MANY_TO_ONE },
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
expect(output.linkedField.relationshipType).toEqual(
|
expect(output.linkedField.relationshipType).toEqual(
|
||||||
RelationshipType.ONE_TO_MANY
|
RelationshipType.ONE_TO_MANY
|
||||||
)
|
)
|
||||||
expect(output.linkerField.relationshipType).toEqual(
|
expect(output.linkerField.relationshipType).toEqual(
|
||||||
RelationshipType.MANY_TO_ONE
|
RelationshipType.MANY_TO_ONE
|
||||||
)
|
)
|
||||||
output = controller.handleRelationshipType(
|
output = controller.handleRelationshipType(mockOneToManyColumn(), {} as any)
|
||||||
{ relationshipType: RelationshipType.ONE_TO_MANY },
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
expect(output.linkedField.relationshipType).toEqual(
|
expect(output.linkedField.relationshipType).toEqual(
|
||||||
RelationshipType.MANY_TO_ONE
|
RelationshipType.MANY_TO_ONE
|
||||||
)
|
)
|
||||||
|
@ -115,16 +159,16 @@ describe("test the link controller", () => {
|
||||||
const controller = await createLinkController(table1, row)
|
const controller = await createLinkController(table1, row)
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
// get initial count
|
// get initial count
|
||||||
const beforeLinks = await controller.getRowLinkDocs(row._id)
|
const beforeLinks = await controller.getRowLinkDocs(row._id!)
|
||||||
await controller.rowDeleted()
|
await controller.rowDeleted()
|
||||||
let afterLinks = await controller.getRowLinkDocs(row._id)
|
let afterLinks = await controller.getRowLinkDocs(row._id!)
|
||||||
expect(beforeLinks.length).toEqual(1)
|
expect(beforeLinks.length).toEqual(1)
|
||||||
expect(afterLinks.length).toEqual(0)
|
expect(afterLinks.length).toEqual(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("shouldn't throw an error when deleting a row with no links", async () => {
|
it("shouldn't throw an error when deleting a row with no links", async () => {
|
||||||
const row = await config.createRow(basicRow(table1._id))
|
const row = await config.createRow(basicRow(table1._id!))
|
||||||
const controller = await createLinkController(table1, row)
|
const controller = await createLinkController(table1, row)
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
let error
|
let error
|
||||||
|
@ -142,12 +186,13 @@ describe("test the link controller", () => {
|
||||||
const copyTable = {
|
const copyTable = {
|
||||||
...table1,
|
...table1,
|
||||||
}
|
}
|
||||||
|
//@ts-ignore
|
||||||
copyTable.schema.otherTableLink = {
|
copyTable.schema.otherTableLink = {
|
||||||
type: "link",
|
type: FieldType.LINK,
|
||||||
fieldName: "link",
|
fieldName: "link",
|
||||||
tableId: table2._id,
|
tableId: table2._id!,
|
||||||
}
|
}
|
||||||
let error
|
let error: any
|
||||||
try {
|
try {
|
||||||
controller.validateTable(copyTable)
|
controller.validateTable(copyTable)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -166,7 +211,7 @@ describe("test the link controller", () => {
|
||||||
const controller = await createLinkController(table1, row)
|
const controller = await createLinkController(table1, row)
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
await controller.rowSaved()
|
await controller.rowSaved()
|
||||||
let links = await controller.getRowLinkDocs(row._id)
|
let links = await controller.getRowLinkDocs(row._id!)
|
||||||
expect(links.length).toEqual(0)
|
expect(links.length).toEqual(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -186,7 +231,7 @@ describe("test the link controller", () => {
|
||||||
it("should be able to remove a linked field from a table", async () => {
|
it("should be able to remove a linked field from a table", async () => {
|
||||||
await createLinkedRow()
|
await createLinkedRow()
|
||||||
await createLinkedRow("link2")
|
await createLinkedRow("link2")
|
||||||
const controller = await createLinkController(table1, null, table1)
|
const controller = await createLinkController(table1, undefined, table1)
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
let before = await controller.getTableLinkDocs()
|
let before = await controller.getTableLinkDocs()
|
||||||
await controller.removeFieldFromTable("link")
|
await controller.removeFieldFromTable("link")
|
||||||
|
@ -199,7 +244,8 @@ describe("test the link controller", () => {
|
||||||
|
|
||||||
it("should throw an error when overwriting a link column", async () => {
|
it("should throw an error when overwriting a link column", async () => {
|
||||||
const update = cloneDeep(table1)
|
const update = cloneDeep(table1)
|
||||||
update.schema.link.relationshipType = RelationshipType.MANY_TO_ONE
|
const linkSchema = update.schema.link as ManyToOneRelationshipFieldMetadata
|
||||||
|
linkSchema.relationshipType = RelationshipType.MANY_TO_ONE
|
||||||
let error
|
let error
|
||||||
try {
|
try {
|
||||||
const controller = await createLinkController(update)
|
const controller = await createLinkController(update)
|
||||||
|
@ -215,7 +261,7 @@ describe("test the link controller", () => {
|
||||||
await createLinkedRow()
|
await createLinkedRow()
|
||||||
const newTable = cloneDeep(table1)
|
const newTable = cloneDeep(table1)
|
||||||
delete newTable.schema.link
|
delete newTable.schema.link
|
||||||
const controller = await createLinkController(newTable, null, table1)
|
const controller = await createLinkController(newTable, undefined, table1)
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
await controller.tableUpdated()
|
await controller.tableUpdated()
|
||||||
const links = await controller.getTableLinkDocs()
|
const links = await controller.getTableLinkDocs()
|
||||||
|
@ -235,7 +281,7 @@ describe("test the link controller", () => {
|
||||||
let error
|
let error
|
||||||
try {
|
try {
|
||||||
// create another row to initiate the error
|
// create another row to initiate the error
|
||||||
await config.createRow(basicLinkedRow(row.tableId, row.link[0]))
|
await config.createRow(basicLinkedRow(row.tableId!, row.link[0]))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = err
|
error = err
|
||||||
}
|
}
|
||||||
|
@ -245,7 +291,7 @@ describe("test the link controller", () => {
|
||||||
it("should not error if a link being created doesn't exist", async () => {
|
it("should not error if a link being created doesn't exist", async () => {
|
||||||
let error
|
let error
|
||||||
try {
|
try {
|
||||||
await config.createRow(basicLinkedRow(table1._id, "invalid"))
|
await config.createRow(basicLinkedRow(table1._id!, "invalid"))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = err
|
error = err
|
||||||
}
|
}
|
||||||
|
@ -255,10 +301,11 @@ describe("test the link controller", () => {
|
||||||
it("make sure auto column goes onto other row too", async () => {
|
it("make sure auto column goes onto other row too", async () => {
|
||||||
const table = await config.createTable()
|
const table = await config.createTable()
|
||||||
const tableCfg = basicTable()
|
const tableCfg = basicTable()
|
||||||
|
//@ts-ignore
|
||||||
tableCfg.schema.link = {
|
tableCfg.schema.link = {
|
||||||
type: "link",
|
type: FieldType.LINK,
|
||||||
fieldName: "link",
|
fieldName: "link",
|
||||||
tableId: table._id,
|
tableId: table._id!,
|
||||||
name: "link",
|
name: "link",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
}
|
}
|
||||||
|
@ -269,10 +316,11 @@ describe("test the link controller", () => {
|
||||||
|
|
||||||
it("should be able to link to self", async () => {
|
it("should be able to link to self", async () => {
|
||||||
const table = await config.createTable()
|
const table = await config.createTable()
|
||||||
|
//@ts-ignore
|
||||||
table.schema.link = {
|
table.schema.link = {
|
||||||
type: "link",
|
type: FieldType.LINK,
|
||||||
fieldName: "link",
|
fieldName: "link",
|
||||||
tableId: table._id,
|
tableId: table._id!,
|
||||||
name: "link",
|
name: "link",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
}
|
}
|
||||||
|
@ -282,8 +330,9 @@ describe("test the link controller", () => {
|
||||||
it("should be able to remove a linked field from a table, even if the linked table does not exist", async () => {
|
it("should be able to remove a linked field from a table, even if the linked table does not exist", async () => {
|
||||||
await createLinkedRow()
|
await createLinkedRow()
|
||||||
await createLinkedRow("link2")
|
await createLinkedRow("link2")
|
||||||
table1.schema["link"].tableId = "not_found"
|
const linkSchema = table1.schema["link"] as RelationshipFieldMetadata
|
||||||
const controller = await createLinkController(table1, null, table1)
|
linkSchema.tableId = "not_found"
|
||||||
|
const controller = await createLinkController(table1, undefined, table1)
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
let before = await controller.getTableLinkDocs()
|
let before = await controller.getTableLinkDocs()
|
||||||
await controller.removeFieldFromTable("link")
|
await controller.removeFieldFromTable("link")
|
|
@ -1,14 +1,15 @@
|
||||||
const TestConfig = require("../../tests/utilities/TestConfiguration")
|
import TestConfig from "../../tests/utilities/TestConfiguration"
|
||||||
const { basicTable } = require("../../tests/utilities/structures")
|
import { basicTable } from "../../tests/utilities/structures"
|
||||||
const linkUtils = require("../linkedRows/linkUtils")
|
import * as linkUtils from "../linkedRows/linkUtils"
|
||||||
const { context } = require("@budibase/backend-core")
|
import { context } from "@budibase/backend-core"
|
||||||
|
import { FieldType, RelationshipType, Table } from "@budibase/types"
|
||||||
|
|
||||||
describe("test link functionality", () => {
|
describe("test link functionality", () => {
|
||||||
const config = new TestConfig()
|
const config = new TestConfig()
|
||||||
let appId
|
let appId: string
|
||||||
|
|
||||||
describe("getLinkedTable", () => {
|
describe("getLinkedTable", () => {
|
||||||
let table
|
let table: Table
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const app = await config.init()
|
const app = await config.init()
|
||||||
appId = app.appId
|
appId = app.appId
|
||||||
|
@ -17,15 +18,15 @@ describe("test link functionality", () => {
|
||||||
|
|
||||||
it("should be able to retrieve a linked table from a list", async () => {
|
it("should be able to retrieve a linked table from a list", async () => {
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
const retrieved = await linkUtils.getLinkedTable(table._id, [table])
|
const retrieved = await linkUtils.getLinkedTable(table._id!, [table])
|
||||||
expect(retrieved._id).toBe(table._id)
|
expect(retrieved._id).toBe(table._id)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to retrieve a table from DB and update list", async () => {
|
it("should be able to retrieve a table from DB and update list", async () => {
|
||||||
const tables = []
|
const tables: Table[] = []
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
const retrieved = await linkUtils.getLinkedTable(table._id, tables)
|
const retrieved = await linkUtils.getLinkedTable(table._id!, tables)
|
||||||
expect(retrieved._id).toBe(table._id)
|
expect(retrieved._id).toBe(table._id)
|
||||||
expect(tables[0]).toBeDefined()
|
expect(tables[0]).toBeDefined()
|
||||||
})
|
})
|
||||||
|
@ -35,9 +36,11 @@ describe("test link functionality", () => {
|
||||||
describe("getRelatedTableForField", () => {
|
describe("getRelatedTableForField", () => {
|
||||||
let link = basicTable()
|
let link = basicTable()
|
||||||
link.schema.link = {
|
link.schema.link = {
|
||||||
|
name: "link",
|
||||||
|
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||||
fieldName: "otherLink",
|
fieldName: "otherLink",
|
||||||
tableId: "tableID",
|
tableId: "tableID",
|
||||||
type: "link",
|
type: FieldType.LINK,
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should get the field from the table directly", () => {
|
it("should get the field from the table directly", () => {
|
|
@ -1,6 +1,7 @@
|
||||||
import newid from "./newid"
|
import newid from "./newid"
|
||||||
import { db as dbCore } from "@budibase/backend-core"
|
import { db as dbCore } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
DocumentType,
|
DocumentType,
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
RelationshipFieldMetadata,
|
RelationshipFieldMetadata,
|
||||||
|
@ -8,7 +9,6 @@ import {
|
||||||
INTERNAL_TABLE_SOURCE_ID,
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
DatabaseQueryOpts,
|
DatabaseQueryOpts,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { FieldTypes } from "../constants"
|
|
||||||
|
|
||||||
export { DocumentType, VirtualDocumentType } from "@budibase/types"
|
export { DocumentType, VirtualDocumentType } from "@budibase/types"
|
||||||
|
|
||||||
|
@ -315,5 +315,5 @@ export function extractViewInfoFromID(viewId: string) {
|
||||||
export function isRelationshipColumn(
|
export function isRelationshipColumn(
|
||||||
column: FieldSchema
|
column: FieldSchema
|
||||||
): column is RelationshipFieldMetadata {
|
): column is RelationshipFieldMetadata {
|
||||||
return column.type === FieldTypes.LINK
|
return column.type === FieldType.LINK
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Knex, knex } from "knex"
|
import { Knex, knex } from "knex"
|
||||||
import {
|
import {
|
||||||
|
RelationshipType,
|
||||||
FieldSubtype,
|
FieldSubtype,
|
||||||
NumberFieldMetadata,
|
NumberFieldMetadata,
|
||||||
Operation,
|
Operation,
|
||||||
|
@ -11,7 +12,6 @@ import {
|
||||||
import { breakExternalTableId } from "../utils"
|
import { breakExternalTableId } from "../utils"
|
||||||
import SchemaBuilder = Knex.SchemaBuilder
|
import SchemaBuilder = Knex.SchemaBuilder
|
||||||
import CreateTableBuilder = Knex.CreateTableBuilder
|
import CreateTableBuilder = Knex.CreateTableBuilder
|
||||||
import { RelationshipType } from "../../constants"
|
|
||||||
import { utils } from "@budibase/shared-core"
|
import { utils } from "@budibase/shared-core"
|
||||||
|
|
||||||
function isIgnoredType(type: FieldType) {
|
function isIgnoredType(type: FieldType) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
DatasourceFieldType,
|
DatasourceFieldType,
|
||||||
Integration,
|
Integration,
|
||||||
Operation,
|
Operation,
|
||||||
|
@ -21,7 +22,6 @@ import {
|
||||||
SqlClient,
|
SqlClient,
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
import Sql from "./base/sql"
|
import Sql from "./base/sql"
|
||||||
import { FieldTypes } from "../constants"
|
|
||||||
import {
|
import {
|
||||||
BindParameters,
|
BindParameters,
|
||||||
Connection,
|
Connection,
|
||||||
|
@ -302,7 +302,7 @@ class OracleIntegration extends Sql implements DatasourcePlus {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (this.isBooleanType(oracleColumn)) {
|
if (this.isBooleanType(oracleColumn)) {
|
||||||
fieldSchema.type = FieldTypes.BOOLEAN
|
fieldSchema.type = FieldType.BOOLEAN
|
||||||
}
|
}
|
||||||
|
|
||||||
table.schema[columnName] = fieldSchema
|
table.schema[columnName] = fieldSchema
|
||||||
|
|
|
@ -1,27 +1,23 @@
|
||||||
import { Datasource, SourceName } from "@budibase/types"
|
import { Datasource, SourceName } from "@budibase/types"
|
||||||
import { GenericContainer, Wait, StartedTestContainer } from "testcontainers"
|
import { GenericContainer, Wait, StartedTestContainer } from "testcontainers"
|
||||||
import env from "../../../environment"
|
|
||||||
|
|
||||||
let container: StartedTestContainer | undefined
|
let container: StartedTestContainer | undefined
|
||||||
|
|
||||||
const isMac = process.platform === "darwin"
|
|
||||||
|
|
||||||
export async function getDsConfig(): Promise<Datasource> {
|
export async function getDsConfig(): Promise<Datasource> {
|
||||||
try {
|
try {
|
||||||
if (!container) {
|
if (!container) {
|
||||||
// postgres 15-bullseye safer bet on Linux
|
container = await new GenericContainer("postgres:16.1-bullseye")
|
||||||
const version = isMac ? undefined : "15-bullseye"
|
|
||||||
container = await new GenericContainer("postgres", version)
|
|
||||||
.withExposedPorts(5432)
|
.withExposedPorts(5432)
|
||||||
.withEnv("POSTGRES_PASSWORD", "password")
|
.withEnvironment({ POSTGRES_PASSWORD: "password" })
|
||||||
.withWaitStrategy(
|
.withWaitStrategy(
|
||||||
Wait.forLogMessage(
|
Wait.forLogMessage(
|
||||||
"PostgreSQL init process complete; ready for start up."
|
"database system is ready to accept connections",
|
||||||
|
2
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.start()
|
.start()
|
||||||
}
|
}
|
||||||
const host = container.getContainerIpAddress()
|
const host = container.getHost()
|
||||||
const port = container.getMappedPort(5432)
|
const port = container.getMappedPort(5432)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { CouchFindOptions, Table, Row } from "@budibase/types"
|
import { FieldType, CouchFindOptions, Table, Row } from "@budibase/types"
|
||||||
import { db as dbCore } from "@budibase/backend-core"
|
import { db as dbCore } from "@budibase/backend-core"
|
||||||
import { DocumentType, SEPARATOR } from "../../../db/utils"
|
import { DocumentType, SEPARATOR } from "../../../db/utils"
|
||||||
import { FieldTypes } from "../../../constants"
|
|
||||||
|
|
||||||
// default limit - seems to work well for performance
|
// default limit - seems to work well for performance
|
||||||
export const FIND_LIMIT = 25
|
export const FIND_LIMIT = 25
|
||||||
|
@ -31,7 +30,7 @@ export async function getRowsWithAttachments(appId: string, table: Table) {
|
||||||
const db = dbCore.getDB(appId)
|
const db = dbCore.getDB(appId)
|
||||||
const attachmentCols: string[] = []
|
const attachmentCols: string[] = []
|
||||||
for (let [key, column] of Object.entries(table.schema)) {
|
for (let [key, column] of Object.entries(table.schema)) {
|
||||||
if (column.type === FieldTypes.ATTACHMENT) {
|
if (column.type === FieldType.ATTACHMENT) {
|
||||||
attachmentCols.push(key)
|
attachmentCols.push(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ import {
|
||||||
expectAnyExternalColsAttributes,
|
expectAnyExternalColsAttributes,
|
||||||
generator,
|
generator,
|
||||||
} from "@budibase/backend-core/tests"
|
} from "@budibase/backend-core/tests"
|
||||||
import datasource from "../../../../../api/routes/datasource"
|
|
||||||
|
|
||||||
jest.unmock("mysql2/promise")
|
jest.unmock("mysql2/promise")
|
||||||
|
|
||||||
|
@ -30,13 +29,15 @@ describe.skip("external", () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const container = await new GenericContainer("mysql")
|
const container = await new GenericContainer("mysql")
|
||||||
.withExposedPorts(3306)
|
.withExposedPorts(3306)
|
||||||
.withEnv("MYSQL_ROOT_PASSWORD", "admin")
|
.withEnvironment({
|
||||||
.withEnv("MYSQL_DATABASE", "db")
|
MYSQL_ROOT_PASSWORD: "admin",
|
||||||
.withEnv("MYSQL_USER", "user")
|
MYSQL_DATABASE: "db",
|
||||||
.withEnv("MYSQL_PASSWORD", "password")
|
MYSQL_USER: "user",
|
||||||
|
MYSQL_PASSWORD: "password",
|
||||||
|
})
|
||||||
.start()
|
.start()
|
||||||
|
|
||||||
const host = container.getContainerIpAddress()
|
const host = container.getHost()
|
||||||
const port = container.getMappedPort(3306)
|
const port = container.getMappedPort(3306)
|
||||||
|
|
||||||
await config.init()
|
await config.init()
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
FieldType,
|
FieldType,
|
||||||
Table,
|
Table,
|
||||||
AutoFieldSubTypes,
|
AutoFieldSubType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
|
||||||
import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
|
import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
|
||||||
|
@ -117,7 +117,7 @@ describe("sdk >> rows >> internal", () => {
|
||||||
id: {
|
id: {
|
||||||
name: "id",
|
name: "id",
|
||||||
type: FieldType.AUTO,
|
type: FieldType.AUTO,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
lastID: 0,
|
lastID: 0,
|
||||||
},
|
},
|
||||||
|
@ -181,7 +181,7 @@ describe("sdk >> rows >> internal", () => {
|
||||||
id: {
|
id: {
|
||||||
name: "id",
|
name: "id",
|
||||||
type: FieldType.AUTO,
|
type: FieldType.AUTO,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
lastID: 0,
|
lastID: 0,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import cloneDeep from "lodash/cloneDeep"
|
import cloneDeep from "lodash/cloneDeep"
|
||||||
import validateJs from "validate.js"
|
import validateJs from "validate.js"
|
||||||
import { Row, Table, TableSchema } from "@budibase/types"
|
import { FieldType, Row, Table, TableSchema } from "@budibase/types"
|
||||||
import { FieldTypes } from "../../../constants"
|
|
||||||
import { makeExternalQuery } from "../../../integrations/base/query"
|
import { makeExternalQuery } from "../../../integrations/base/query"
|
||||||
import { Format } from "../../../api/controllers/view/exporters"
|
import { Format } from "../../../api/controllers/view/exporters"
|
||||||
import sdk from "../.."
|
import sdk from "../.."
|
||||||
|
@ -22,7 +21,7 @@ export function cleanExportRows(
|
||||||
let cleanRows = [...rows]
|
let cleanRows = [...rows]
|
||||||
|
|
||||||
const relationships = Object.entries(schema)
|
const relationships = Object.entries(schema)
|
||||||
.filter((entry: any[]) => entry[1].type === FieldTypes.LINK)
|
.filter((entry: any[]) => entry[1].type === FieldType.LINK)
|
||||||
.map(entry => entry[0])
|
.map(entry => entry[0])
|
||||||
|
|
||||||
relationships.forEach(column => {
|
relationships.forEach(column => {
|
||||||
|
@ -88,17 +87,17 @@ export async function validate({
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// formulas shouldn't validated, data will be deleted anyway
|
// formulas shouldn't validated, data will be deleted anyway
|
||||||
if (type === FieldTypes.FORMULA || column.autocolumn) {
|
if (type === FieldType.FORMULA || column.autocolumn) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// special case for options, need to always allow unselected (empty)
|
// special case for options, need to always allow unselected (empty)
|
||||||
if (type === FieldTypes.OPTIONS && constraints?.inclusion) {
|
if (type === FieldType.OPTIONS && constraints?.inclusion) {
|
||||||
constraints.inclusion.push(null as any, "")
|
constraints.inclusion.push(null as any, "")
|
||||||
}
|
}
|
||||||
let res
|
let res
|
||||||
|
|
||||||
// Validate.js doesn't seem to handle array
|
// Validate.js doesn't seem to handle array
|
||||||
if (type === FieldTypes.ARRAY && row[fieldName]) {
|
if (type === FieldType.ARRAY && row[fieldName]) {
|
||||||
if (row[fieldName].length) {
|
if (row[fieldName].length) {
|
||||||
if (!Array.isArray(row[fieldName])) {
|
if (!Array.isArray(row[fieldName])) {
|
||||||
row[fieldName] = row[fieldName].split(",")
|
row[fieldName] = row[fieldName].split(",")
|
||||||
|
@ -116,13 +115,13 @@ export async function validate({
|
||||||
errors[fieldName] = [`${fieldName} is required`]
|
errors[fieldName] = [`${fieldName} is required`]
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
(type === FieldTypes.ATTACHMENT || type === FieldTypes.JSON) &&
|
(type === FieldType.ATTACHMENT || type === FieldType.JSON) &&
|
||||||
typeof row[fieldName] === "string"
|
typeof row[fieldName] === "string"
|
||||||
) {
|
) {
|
||||||
// this should only happen if there is an error
|
// this should only happen if there is an error
|
||||||
try {
|
try {
|
||||||
const json = JSON.parse(row[fieldName])
|
const json = JSON.parse(row[fieldName])
|
||||||
if (type === FieldTypes.ATTACHMENT) {
|
if (type === FieldType.ATTACHMENT) {
|
||||||
if (Array.isArray(json)) {
|
if (Array.isArray(json)) {
|
||||||
row[fieldName] = json
|
row[fieldName] = json
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
Operation,
|
Operation,
|
||||||
RelationshipType,
|
RelationshipType,
|
||||||
RenameColumn,
|
RenameColumn,
|
||||||
|
@ -14,7 +15,6 @@ import {
|
||||||
setStaticSchemas,
|
setStaticSchemas,
|
||||||
} from "../../../../api/controllers/table/utils"
|
} from "../../../../api/controllers/table/utils"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { FieldTypes } from "../../../../constants"
|
|
||||||
import { makeTableRequest } from "../../../../api/controllers/table/ExternalRequest"
|
import { makeTableRequest } from "../../../../api/controllers/table/ExternalRequest"
|
||||||
import {
|
import {
|
||||||
isRelationshipSetup,
|
isRelationshipSetup,
|
||||||
|
@ -78,7 +78,7 @@ export async function save(
|
||||||
|
|
||||||
// check if relations need setup
|
// check if relations need setup
|
||||||
for (let schema of Object.values(tableToSave.schema)) {
|
for (let schema of Object.values(tableToSave.schema)) {
|
||||||
if (schema.type !== FieldTypes.LINK || isRelationshipSetup(schema)) {
|
if (schema.type !== FieldType.LINK || isRelationshipSetup(schema)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const schemaTableId = schema.tableId
|
const schemaTableId = schema.tableId
|
||||||
|
|
|
@ -9,7 +9,6 @@ import {
|
||||||
Table,
|
Table,
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { FieldTypes } from "../../../../constants"
|
|
||||||
import {
|
import {
|
||||||
foreignKeyStructure,
|
foreignKeyStructure,
|
||||||
generateForeignKey,
|
generateForeignKey,
|
||||||
|
@ -27,7 +26,7 @@ export function cleanupRelationships(
|
||||||
// clean up relationships in couch table schemas
|
// clean up relationships in couch table schemas
|
||||||
for (let [key, schema] of Object.entries(tableToIterate.schema)) {
|
for (let [key, schema] of Object.entries(tableToIterate.schema)) {
|
||||||
if (
|
if (
|
||||||
schema.type === FieldTypes.LINK &&
|
schema.type === FieldType.LINK &&
|
||||||
(!oldTable || table.schema[key] == null)
|
(!oldTable || table.schema[key] == null)
|
||||||
) {
|
) {
|
||||||
const schemaTableId = schema.tableId
|
const schemaTableId = schema.tableId
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
RenameColumn,
|
RenameColumn,
|
||||||
Table,
|
Table,
|
||||||
ViewStatisticsSchema,
|
ViewStatisticsSchema,
|
||||||
|
@ -10,7 +11,6 @@ import {
|
||||||
hasTypeChanged,
|
hasTypeChanged,
|
||||||
TableSaveFunctions,
|
TableSaveFunctions,
|
||||||
} from "../../../../api/controllers/table/utils"
|
} from "../../../../api/controllers/table/utils"
|
||||||
import { FieldTypes } from "../../../../constants"
|
|
||||||
import { EventType, updateLinks } from "../../../../db/linkedRows"
|
import { EventType, updateLinks } from "../../../../db/linkedRows"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import isEqual from "lodash/isEqual"
|
import isEqual from "lodash/isEqual"
|
||||||
|
@ -63,7 +63,7 @@ export async function save(
|
||||||
}
|
}
|
||||||
|
|
||||||
// rename row fields when table column is renamed
|
// rename row fields when table column is renamed
|
||||||
if (renaming && table.schema[renaming.updated].type === FieldTypes.LINK) {
|
if (renaming && table.schema[renaming.updated].type === FieldType.LINK) {
|
||||||
throw new Error("Cannot rename a linked column.")
|
throw new Error("Cannot rename a linked column.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import { FieldTypes, ObjectStoreBuckets } from "../../constants"
|
import { ObjectStoreBuckets } from "../../constants"
|
||||||
import { context, db as dbCore, objectStore } from "@budibase/backend-core"
|
import { context, db as dbCore, objectStore } from "@budibase/backend-core"
|
||||||
import { RenameColumn, Row, RowAttachment, Table } from "@budibase/types"
|
import {
|
||||||
|
FieldType,
|
||||||
|
RenameColumn,
|
||||||
|
Row,
|
||||||
|
RowAttachment,
|
||||||
|
Table,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
export class AttachmentCleanup {
|
export class AttachmentCleanup {
|
||||||
static async coreCleanup(fileListFn: () => string[]): Promise<void> {
|
static async coreCleanup(fileListFn: () => string[]): Promise<void> {
|
||||||
|
@ -28,7 +34,7 @@ export class AttachmentCleanup {
|
||||||
let files: string[] = []
|
let files: string[] = []
|
||||||
const tableSchema = opts.oldTable?.schema || table.schema
|
const tableSchema = opts.oldTable?.schema || table.schema
|
||||||
for (let [key, schema] of Object.entries(tableSchema)) {
|
for (let [key, schema] of Object.entries(tableSchema)) {
|
||||||
if (schema.type !== FieldTypes.ATTACHMENT) {
|
if (schema.type !== FieldType.ATTACHMENT) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const columnRemoved = opts.oldTable && !table.schema[key]
|
const columnRemoved = opts.oldTable && !table.schema[key]
|
||||||
|
@ -62,7 +68,7 @@ export class AttachmentCleanup {
|
||||||
return AttachmentCleanup.coreCleanup(() => {
|
return AttachmentCleanup.coreCleanup(() => {
|
||||||
let files: string[] = []
|
let files: string[] = []
|
||||||
for (let [key, schema] of Object.entries(table.schema)) {
|
for (let [key, schema] of Object.entries(table.schema)) {
|
||||||
if (schema.type !== FieldTypes.ATTACHMENT) {
|
if (schema.type !== FieldType.ATTACHMENT) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rows.forEach(row => {
|
rows.forEach(row => {
|
||||||
|
@ -79,7 +85,7 @@ export class AttachmentCleanup {
|
||||||
return AttachmentCleanup.coreCleanup(() => {
|
return AttachmentCleanup.coreCleanup(() => {
|
||||||
let files: string[] = []
|
let files: string[] = []
|
||||||
for (let [key, schema] of Object.entries(table.schema)) {
|
for (let [key, schema] of Object.entries(table.schema)) {
|
||||||
if (schema.type !== FieldTypes.ATTACHMENT) {
|
if (schema.type !== FieldType.ATTACHMENT) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const oldKeys =
|
const oldKeys =
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import * as linkRows from "../../db/linkedRows"
|
import * as linkRows from "../../db/linkedRows"
|
||||||
import { FieldTypes, AutoFieldSubTypes } from "../../constants"
|
|
||||||
import { processFormulas, fixAutoColumnSubType } from "./utils"
|
import { processFormulas, fixAutoColumnSubType } from "./utils"
|
||||||
import { objectStore, utils } from "@budibase/backend-core"
|
import { objectStore, utils } from "@budibase/backend-core"
|
||||||
import { InternalTables } from "../../db/utils"
|
import { InternalTables } from "../../db/utils"
|
||||||
import { TYPE_TRANSFORM_MAP } from "./map"
|
import { TYPE_TRANSFORM_MAP } from "./map"
|
||||||
import { FieldSubtype, Row, RowAttachment, Table } from "@budibase/types"
|
import {
|
||||||
|
FieldType,
|
||||||
|
AutoFieldSubType,
|
||||||
|
FieldSubtype,
|
||||||
|
Row,
|
||||||
|
RowAttachment,
|
||||||
|
Table,
|
||||||
|
} from "@budibase/types"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import {
|
import {
|
||||||
processInputBBReferences,
|
processInputBBReferences,
|
||||||
|
@ -54,25 +60,25 @@ export function processAutoColumn(
|
||||||
schema = fixAutoColumnSubType(schema)
|
schema = fixAutoColumnSubType(schema)
|
||||||
}
|
}
|
||||||
switch (schema.subtype) {
|
switch (schema.subtype) {
|
||||||
case AutoFieldSubTypes.CREATED_BY:
|
case AutoFieldSubType.CREATED_BY:
|
||||||
if (creating && shouldUpdateUserFields && userId) {
|
if (creating && shouldUpdateUserFields && userId) {
|
||||||
row[key] = [userId]
|
row[key] = [userId]
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case AutoFieldSubTypes.CREATED_AT:
|
case AutoFieldSubType.CREATED_AT:
|
||||||
if (creating) {
|
if (creating) {
|
||||||
row[key] = now
|
row[key] = now
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case AutoFieldSubTypes.UPDATED_BY:
|
case AutoFieldSubType.UPDATED_BY:
|
||||||
if (shouldUpdateUserFields && userId) {
|
if (shouldUpdateUserFields && userId) {
|
||||||
row[key] = [userId]
|
row[key] = [userId]
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case AutoFieldSubTypes.UPDATED_AT:
|
case AutoFieldSubType.UPDATED_AT:
|
||||||
row[key] = now
|
row[key] = now
|
||||||
break
|
break
|
||||||
case AutoFieldSubTypes.AUTO_ID:
|
case AutoFieldSubType.AUTO_ID:
|
||||||
if (creating) {
|
if (creating) {
|
||||||
schema.lastID = !schema.lastID ? BASE_AUTO_ID : schema.lastID + 1
|
schema.lastID = !schema.lastID ? BASE_AUTO_ID : schema.lastID + 1
|
||||||
row[key] = schema.lastID
|
row[key] = schema.lastID
|
||||||
|
@ -134,7 +140,7 @@ export async function inputProcessing(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// remove any formula values, they are to be generated
|
// remove any formula values, they are to be generated
|
||||||
if (field.type === FieldTypes.FORMULA) {
|
if (field.type === FieldType.FORMULA) {
|
||||||
delete clonedRow[key]
|
delete clonedRow[key]
|
||||||
}
|
}
|
||||||
// otherwise coerce what is there to correct types
|
// otherwise coerce what is there to correct types
|
||||||
|
@ -143,7 +149,7 @@ export async function inputProcessing(
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove any attachment urls, they are generated on read
|
// remove any attachment urls, they are generated on read
|
||||||
if (field.type === FieldTypes.ATTACHMENT) {
|
if (field.type === FieldType.ATTACHMENT) {
|
||||||
const attachments = clonedRow[key]
|
const attachments = clonedRow[key]
|
||||||
if (attachments?.length) {
|
if (attachments?.length) {
|
||||||
attachments.forEach((attachment: RowAttachment) => {
|
attachments.forEach((attachment: RowAttachment) => {
|
||||||
|
@ -152,7 +158,7 @@ export async function inputProcessing(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field.type === FieldTypes.BB_REFERENCE && value) {
|
if (field.type === FieldType.BB_REFERENCE && value) {
|
||||||
clonedRow[key] = await processInputBBReferences(
|
clonedRow[key] = await processInputBBReferences(
|
||||||
value,
|
value,
|
||||||
field.subtype as FieldSubtype
|
field.subtype as FieldSubtype
|
||||||
|
@ -214,7 +220,7 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
|
|
||||||
// process complex types: attachements, bb references...
|
// process complex types: attachements, bb references...
|
||||||
for (let [property, column] of Object.entries(table.schema)) {
|
for (let [property, column] of Object.entries(table.schema)) {
|
||||||
if (column.type === FieldTypes.ATTACHMENT) {
|
if (column.type === FieldType.ATTACHMENT) {
|
||||||
for (let row of enriched) {
|
for (let row of enriched) {
|
||||||
if (row[property] == null || !Array.isArray(row[property])) {
|
if (row[property] == null || !Array.isArray(row[property])) {
|
||||||
continue
|
continue
|
||||||
|
@ -227,7 +233,7 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
!opts.skipBBReferences &&
|
!opts.skipBBReferences &&
|
||||||
column.type == FieldTypes.BB_REFERENCE
|
column.type == FieldType.BB_REFERENCE
|
||||||
) {
|
) {
|
||||||
for (let row of enriched) {
|
for (let row of enriched) {
|
||||||
row[property] = await processOutputBBReferences(
|
row[property] = await processOutputBBReferences(
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// @ts-nocheck
|
import { FieldType } from "@budibase/types"
|
||||||
import { FieldTypes } from "../../constants"
|
|
||||||
|
|
||||||
const parseArrayString = value => {
|
const parseArrayString = (value: any) => {
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
if (value === "") {
|
if (value === "") {
|
||||||
return []
|
return []
|
||||||
|
@ -21,11 +20,13 @@ const parseArrayString = value => {
|
||||||
* A map of how we convert various properties in rows to each other based on the row type.
|
* A map of how we convert various properties in rows to each other based on the row type.
|
||||||
*/
|
*/
|
||||||
export const TYPE_TRANSFORM_MAP: any = {
|
export const TYPE_TRANSFORM_MAP: any = {
|
||||||
[FieldTypes.LINK]: {
|
[FieldType.LINK]: {
|
||||||
"": [],
|
"": [],
|
||||||
|
//@ts-ignore
|
||||||
[null]: [],
|
[null]: [],
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
parse: link => {
|
parse: (link: any) => {
|
||||||
if (Array.isArray(link) && typeof link[0] === "object") {
|
if (Array.isArray(link) && typeof link[0] === "object") {
|
||||||
return link.map(el => (el && el._id ? el._id : el))
|
return link.map(el => (el && el._id ? el._id : el))
|
||||||
}
|
}
|
||||||
|
@ -35,75 +36,97 @@ export const TYPE_TRANSFORM_MAP: any = {
|
||||||
return link
|
return link
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[FieldTypes.OPTIONS]: {
|
[FieldType.OPTIONS]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.ARRAY]: {
|
[FieldType.ARRAY]: {
|
||||||
|
//@ts-ignore
|
||||||
[null]: [],
|
[null]: [],
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
parse: parseArrayString,
|
parse: parseArrayString,
|
||||||
},
|
},
|
||||||
[FieldTypes.STRING]: {
|
[FieldType.STRING]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.BARCODEQR]: {
|
[FieldType.BARCODEQR]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.FORMULA]: {
|
[FieldType.FORMULA]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.LONGFORM]: {
|
[FieldType.LONGFORM]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.NUMBER]: {
|
[FieldType.NUMBER]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
parse: n => parseFloat(n),
|
parse: (n: any) => parseFloat(n),
|
||||||
},
|
},
|
||||||
[FieldTypes.BIGINT]: {
|
[FieldType.BIGINT]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.DATETIME]: {
|
[FieldType.DATETIME]: {
|
||||||
"": null,
|
"": null,
|
||||||
[undefined]: undefined,
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
parse: date => {
|
//@ts-ignore
|
||||||
|
[undefined]: undefined,
|
||||||
|
parse: (date: any) => {
|
||||||
if (date instanceof Date) {
|
if (date instanceof Date) {
|
||||||
return date.toISOString()
|
return date.toISOString()
|
||||||
}
|
}
|
||||||
return date
|
return date
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[FieldTypes.ATTACHMENT]: {
|
[FieldType.ATTACHMENT]: {
|
||||||
|
//@ts-ignore
|
||||||
[null]: [],
|
[null]: [],
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
parse: parseArrayString,
|
parse: parseArrayString,
|
||||||
},
|
},
|
||||||
[FieldTypes.BOOLEAN]: {
|
[FieldType.BOOLEAN]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
true: true,
|
true: true,
|
||||||
false: false,
|
false: false,
|
||||||
},
|
},
|
||||||
[FieldTypes.AUTO]: {
|
[FieldType.AUTO]: {
|
||||||
parse: () => undefined,
|
parse: () => undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.JSON]: {
|
[FieldType.JSON]: {
|
||||||
parse: input => {
|
parse: (input: any) => {
|
||||||
try {
|
try {
|
||||||
if (input === "") {
|
if (input === "") {
|
||||||
return undefined
|
return undefined
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { fixAutoColumnSubType } from "../utils"
|
import { fixAutoColumnSubType } from "../utils"
|
||||||
import { AutoFieldDefaultNames, AutoFieldSubTypes } from "../../../constants"
|
import { AutoFieldDefaultNames } from "../../../constants"
|
||||||
import { FieldSchema, FieldType, RelationshipType } from "@budibase/types"
|
import {
|
||||||
|
AutoFieldSubType,
|
||||||
|
FieldSchema,
|
||||||
|
FieldType,
|
||||||
|
RelationshipType,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
describe("rowProcessor utility", () => {
|
describe("rowProcessor utility", () => {
|
||||||
describe("fixAutoColumnSubType", () => {
|
describe("fixAutoColumnSubType", () => {
|
||||||
|
@ -20,37 +25,37 @@ describe("rowProcessor utility", () => {
|
||||||
it("updates the schema with the correct subtype", async () => {
|
it("updates the schema with the correct subtype", async () => {
|
||||||
schema.name = AutoFieldDefaultNames.CREATED_BY
|
schema.name = AutoFieldDefaultNames.CREATED_BY
|
||||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||||
AutoFieldSubTypes.CREATED_BY
|
AutoFieldSubType.CREATED_BY
|
||||||
)
|
)
|
||||||
schema.subtype = undefined
|
schema.subtype = undefined
|
||||||
|
|
||||||
schema.name = AutoFieldDefaultNames.UPDATED_BY
|
schema.name = AutoFieldDefaultNames.UPDATED_BY
|
||||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||||
AutoFieldSubTypes.UPDATED_BY
|
AutoFieldSubType.UPDATED_BY
|
||||||
)
|
)
|
||||||
schema.subtype = undefined
|
schema.subtype = undefined
|
||||||
|
|
||||||
schema.name = AutoFieldDefaultNames.CREATED_AT
|
schema.name = AutoFieldDefaultNames.CREATED_AT
|
||||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||||
AutoFieldSubTypes.CREATED_AT
|
AutoFieldSubType.CREATED_AT
|
||||||
)
|
)
|
||||||
schema.subtype = undefined
|
schema.subtype = undefined
|
||||||
|
|
||||||
schema.name = AutoFieldDefaultNames.UPDATED_AT
|
schema.name = AutoFieldDefaultNames.UPDATED_AT
|
||||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||||
AutoFieldSubTypes.UPDATED_AT
|
AutoFieldSubType.UPDATED_AT
|
||||||
)
|
)
|
||||||
schema.subtype = undefined
|
schema.subtype = undefined
|
||||||
|
|
||||||
schema.name = AutoFieldDefaultNames.AUTO_ID
|
schema.name = AutoFieldDefaultNames.AUTO_ID
|
||||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||||
AutoFieldSubTypes.AUTO_ID
|
AutoFieldSubType.AUTO_ID
|
||||||
)
|
)
|
||||||
schema.subtype = undefined
|
schema.subtype = undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns the column if subtype exists", async () => {
|
it("returns the column if subtype exists", async () => {
|
||||||
schema.subtype = AutoFieldSubTypes.CREATED_BY
|
schema.subtype = AutoFieldSubType.CREATED_BY
|
||||||
schema.name = AutoFieldDefaultNames.CREATED_AT
|
schema.name = AutoFieldDefaultNames.CREATED_AT
|
||||||
expect(fixAutoColumnSubType(schema)).toEqual(schema)
|
expect(fixAutoColumnSubType(schema)).toEqual(schema)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import {
|
import { AutoFieldDefaultNames } from "../../constants"
|
||||||
AutoFieldDefaultNames,
|
|
||||||
AutoFieldSubTypes,
|
|
||||||
FieldTypes,
|
|
||||||
FormulaTypes,
|
|
||||||
} from "../../constants"
|
|
||||||
import { processStringSync } from "@budibase/string-templates"
|
import { processStringSync } from "@budibase/string-templates"
|
||||||
import {
|
import {
|
||||||
AutoColumnFieldMetadata,
|
AutoColumnFieldMetadata,
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
Row,
|
Row,
|
||||||
Table,
|
Table,
|
||||||
|
FormulaType,
|
||||||
|
AutoFieldSubType,
|
||||||
|
FieldType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import tracer from "dd-trace"
|
import tracer from "dd-trace"
|
||||||
|
|
||||||
|
@ -30,15 +28,15 @@ export function fixAutoColumnSubType(
|
||||||
}
|
}
|
||||||
// the columns which get auto generated
|
// the columns which get auto generated
|
||||||
if (column.name.endsWith(AutoFieldDefaultNames.CREATED_BY)) {
|
if (column.name.endsWith(AutoFieldDefaultNames.CREATED_BY)) {
|
||||||
column.subtype = AutoFieldSubTypes.CREATED_BY
|
column.subtype = AutoFieldSubType.CREATED_BY
|
||||||
} else if (column.name.endsWith(AutoFieldDefaultNames.UPDATED_BY)) {
|
} else if (column.name.endsWith(AutoFieldDefaultNames.UPDATED_BY)) {
|
||||||
column.subtype = AutoFieldSubTypes.UPDATED_BY
|
column.subtype = AutoFieldSubType.UPDATED_BY
|
||||||
} else if (column.name.endsWith(AutoFieldDefaultNames.CREATED_AT)) {
|
} else if (column.name.endsWith(AutoFieldDefaultNames.CREATED_AT)) {
|
||||||
column.subtype = AutoFieldSubTypes.CREATED_AT
|
column.subtype = AutoFieldSubType.CREATED_AT
|
||||||
} else if (column.name.endsWith(AutoFieldDefaultNames.UPDATED_AT)) {
|
} else if (column.name.endsWith(AutoFieldDefaultNames.UPDATED_AT)) {
|
||||||
column.subtype = AutoFieldSubTypes.UPDATED_AT
|
column.subtype = AutoFieldSubType.UPDATED_AT
|
||||||
} else if (column.name.endsWith(AutoFieldDefaultNames.AUTO_ID)) {
|
} else if (column.name.endsWith(AutoFieldDefaultNames.AUTO_ID)) {
|
||||||
column.subtype = AutoFieldSubTypes.AUTO_ID
|
column.subtype = AutoFieldSubType.AUTO_ID
|
||||||
}
|
}
|
||||||
return column
|
return column
|
||||||
}
|
}
|
||||||
|
@ -57,11 +55,11 @@ export function processFormulas<T extends Row | Row[]>(
|
||||||
const rows = Array.isArray(inputRows) ? inputRows : [inputRows]
|
const rows = Array.isArray(inputRows) ? inputRows : [inputRows]
|
||||||
if (rows) {
|
if (rows) {
|
||||||
for (let [column, schema] of Object.entries(table.schema)) {
|
for (let [column, schema] of Object.entries(table.schema)) {
|
||||||
if (schema.type !== FieldTypes.FORMULA) {
|
if (schema.type !== FieldType.FORMULA) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const isStatic = schema.formulaType === FormulaTypes.STATIC
|
const isStatic = schema.formulaType === FormulaType.STATIC
|
||||||
|
|
||||||
if (
|
if (
|
||||||
schema.formula == null ||
|
schema.formula == null ||
|
||||||
|
@ -100,7 +98,7 @@ export function processDates<T extends Row | Row[]>(
|
||||||
let rows = Array.isArray(inputRows) ? inputRows : [inputRows]
|
let rows = Array.isArray(inputRows) ? inputRows : [inputRows]
|
||||||
let datesWithTZ: string[] = []
|
let datesWithTZ: string[] = []
|
||||||
for (let [column, schema] of Object.entries(table.schema)) {
|
for (let [column, schema] of Object.entries(table.schema)) {
|
||||||
if (schema.type !== FieldTypes.DATETIME) {
|
if (schema.type !== FieldType.DATETIME) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (!schema.timeOnly && !schema.ignoreTimezones) {
|
if (!schema.timeOnly && !schema.ignoreTimezones) {
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { FieldSubtype } from "@budibase/types"
|
import { FieldType, FieldSubtype } from "@budibase/types"
|
||||||
import { FieldTypes } from "../constants"
|
|
||||||
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 {
|
interface SchemaColumn {
|
||||||
readonly name: string
|
readonly name: string
|
||||||
readonly type: FieldTypes
|
readonly type: FieldType
|
||||||
readonly subtype: FieldSubtype
|
readonly subtype: FieldSubtype
|
||||||
readonly autocolumn?: boolean
|
readonly autocolumn?: boolean
|
||||||
readonly constraints?: {
|
readonly constraints?: {
|
||||||
|
@ -36,13 +35,13 @@ interface ValidationResults {
|
||||||
}
|
}
|
||||||
|
|
||||||
const PARSERS: any = {
|
const PARSERS: any = {
|
||||||
[FieldTypes.NUMBER]: (attribute?: string) => {
|
[FieldType.NUMBER]: (attribute?: string) => {
|
||||||
if (!attribute) {
|
if (!attribute) {
|
||||||
return attribute
|
return attribute
|
||||||
}
|
}
|
||||||
return Number(attribute)
|
return Number(attribute)
|
||||||
},
|
},
|
||||||
[FieldTypes.DATETIME]: (attribute?: string) => {
|
[FieldType.DATETIME]: (attribute?: string) => {
|
||||||
if (!attribute) {
|
if (!attribute) {
|
||||||
return attribute
|
return attribute
|
||||||
}
|
}
|
||||||
|
@ -60,7 +59,7 @@ export function isSchema(schema: any): schema is Schema {
|
||||||
column !== null &&
|
column !== null &&
|
||||||
typeof column === "object" &&
|
typeof column === "object" &&
|
||||||
typeof column.type === "string" &&
|
typeof column.type === "string" &&
|
||||||
Object.values(FieldTypes).includes(column.type as FieldTypes)
|
Object.values(FieldType).includes(column.type as FieldType)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -110,20 +109,17 @@ export function validate(rows: Rows, schema: Schema): ValidationResults {
|
||||||
isAutoColumn
|
isAutoColumn
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
} else if (
|
} else if (columnType === FieldType.NUMBER && isNaN(Number(columnData))) {
|
||||||
columnType === FieldTypes.NUMBER &&
|
|
||||||
isNaN(Number(columnData))
|
|
||||||
) {
|
|
||||||
// If provided must be a valid number
|
// If provided must be a valid number
|
||||||
results.schemaValidation[columnName] = false
|
results.schemaValidation[columnName] = false
|
||||||
} else if (
|
} else if (
|
||||||
// If provided must be a valid date
|
// If provided must be a valid date
|
||||||
columnType === FieldTypes.DATETIME &&
|
columnType === FieldType.DATETIME &&
|
||||||
isNaN(new Date(columnData).getTime())
|
isNaN(new Date(columnData).getTime())
|
||||||
) {
|
) {
|
||||||
results.schemaValidation[columnName] = false
|
results.schemaValidation[columnName] = false
|
||||||
} else if (
|
} else if (
|
||||||
columnType === FieldTypes.BB_REFERENCE &&
|
columnType === FieldType.BB_REFERENCE &&
|
||||||
!isValidBBReference(columnData, columnSubtype)
|
!isValidBBReference(columnData, columnSubtype)
|
||||||
) {
|
) {
|
||||||
results.schemaValidation[columnName] = false
|
results.schemaValidation[columnName] = false
|
||||||
|
@ -155,15 +151,15 @@ export function parse(rows: Rows, schema: Schema): Rows {
|
||||||
const columnType = schema[columnName].type
|
const columnType = schema[columnName].type
|
||||||
const columnSubtype = schema[columnName].subtype
|
const columnSubtype = schema[columnName].subtype
|
||||||
|
|
||||||
if (columnType === FieldTypes.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
|
||||||
} else if (columnType === FieldTypes.DATETIME) {
|
} else if (columnType === FieldType.DATETIME) {
|
||||||
// If provided must be a valid date
|
// If provided must be a valid date
|
||||||
parsedRow[columnName] = columnData
|
parsedRow[columnName] = columnData
|
||||||
? new Date(columnData).toISOString()
|
? new Date(columnData).toISOString()
|
||||||
: columnData
|
: columnData
|
||||||
} else if (columnType === FieldTypes.BB_REFERENCE) {
|
} else if (columnType === FieldType.BB_REFERENCE) {
|
||||||
const parsedValues =
|
const parsedValues =
|
||||||
!!columnData && parseCsvExport<{ _id: string }[]>(columnData)
|
!!columnData && parseCsvExport<{ _id: string }[]>(columnData)
|
||||||
if (!parsedValues) {
|
if (!parsedValues) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ export enum AutoReason {
|
||||||
FOREIGN_KEY = "foreign_key",
|
FOREIGN_KEY = "foreign_key",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AutoFieldSubTypes {
|
export enum AutoFieldSubType {
|
||||||
CREATED_BY = "createdBy",
|
CREATED_BY = "createdBy",
|
||||||
CREATED_AT = "createdAt",
|
CREATED_AT = "createdAt",
|
||||||
UPDATED_BY = "updatedBy",
|
UPDATED_BY = "updatedBy",
|
||||||
|
@ -16,7 +16,7 @@ export enum AutoFieldSubTypes {
|
||||||
AUTO_ID = "autoID",
|
AUTO_ID = "autoID",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum FormulaTypes {
|
export enum FormulaType {
|
||||||
STATIC = "static",
|
STATIC = "static",
|
||||||
DYNAMIC = "dynamic",
|
DYNAMIC = "dynamic",
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
// column size, position and whether it can be viewed
|
// column size, position and whether it can be viewed
|
||||||
import { FieldSubtype, FieldType } from "../row"
|
import { FieldSubtype, FieldType } from "../row"
|
||||||
import {
|
import {
|
||||||
AutoFieldSubTypes,
|
AutoFieldSubType,
|
||||||
AutoReason,
|
AutoReason,
|
||||||
FormulaTypes,
|
FormulaType,
|
||||||
RelationshipType,
|
RelationshipType,
|
||||||
} from "./constants"
|
} from "./constants"
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ interface BaseRelationshipFieldMetadata
|
||||||
fieldName: string
|
fieldName: string
|
||||||
tableId: string
|
tableId: string
|
||||||
tableRev?: string
|
tableRev?: string
|
||||||
subtype?: AutoFieldSubTypes.CREATED_BY | AutoFieldSubTypes.UPDATED_BY
|
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
|
||||||
|
@ -62,7 +62,7 @@ export interface AutoColumnFieldMetadata
|
||||||
extends Omit<BaseFieldSchema, "subtype"> {
|
extends Omit<BaseFieldSchema, "subtype"> {
|
||||||
type: FieldType.AUTO
|
type: FieldType.AUTO
|
||||||
autocolumn: true
|
autocolumn: true
|
||||||
subtype?: AutoFieldSubTypes
|
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
|
||||||
|
@ -70,7 +70,7 @@ export interface AutoColumnFieldMetadata
|
||||||
|
|
||||||
export interface NumberFieldMetadata extends Omit<BaseFieldSchema, "subtype"> {
|
export interface NumberFieldMetadata extends Omit<BaseFieldSchema, "subtype"> {
|
||||||
type: FieldType.NUMBER
|
type: FieldType.NUMBER
|
||||||
subtype?: AutoFieldSubTypes.AUTO_ID
|
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
|
||||||
|
@ -85,7 +85,7 @@ export interface DateFieldMetadata extends Omit<BaseFieldSchema, "subtype"> {
|
||||||
type: FieldType.DATETIME
|
type: FieldType.DATETIME
|
||||||
ignoreTimezones?: boolean
|
ignoreTimezones?: boolean
|
||||||
timeOnly?: boolean
|
timeOnly?: boolean
|
||||||
subtype?: AutoFieldSubTypes.CREATED_AT | AutoFieldSubTypes.UPDATED_AT
|
subtype?: AutoFieldSubType.CREATED_AT | AutoFieldSubType.UPDATED_AT
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LongFormFieldMetadata extends BaseFieldSchema {
|
export interface LongFormFieldMetadata extends BaseFieldSchema {
|
||||||
|
@ -96,7 +96,7 @@ export interface LongFormFieldMetadata extends BaseFieldSchema {
|
||||||
export interface FormulaFieldMetadata extends BaseFieldSchema {
|
export interface FormulaFieldMetadata extends BaseFieldSchema {
|
||||||
type: FieldType.FORMULA
|
type: FieldType.FORMULA
|
||||||
formula: string
|
formula: string
|
||||||
formulaType?: FormulaTypes
|
formulaType?: FormulaType
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BBReferenceFieldMetadata
|
export interface BBReferenceFieldMetadata
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import { BaseEvent } from "./event"
|
import { BaseEvent } from "./event"
|
||||||
import { ConfigType } from "../../documents"
|
import { ConfigType } from "../../documents"
|
||||||
|
|
||||||
export type LoginSource = "local" | "google" | "oidc" | "google-internal"
|
export type LoginSource =
|
||||||
|
| "local"
|
||||||
|
| "google"
|
||||||
|
| "microsoft"
|
||||||
|
| "oidc"
|
||||||
|
| "google-internal"
|
||||||
export type SSOType = ConfigType.OIDC | ConfigType.GOOGLE
|
export type SSOType = ConfigType.OIDC | ConfigType.GOOGLE
|
||||||
|
|
||||||
export interface LoginEvent extends BaseEvent {
|
export interface LoginEvent extends BaseEvent {
|
||||||
|
|
306
yarn.lock
306
yarn.lock
|
@ -5218,6 +5218,14 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/ms" "*"
|
"@types/ms" "*"
|
||||||
|
|
||||||
|
"@types/docker-modem@*":
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/docker-modem/-/docker-modem-3.0.6.tgz#1f9262fcf85425b158ca725699a03eb23cddbf87"
|
||||||
|
integrity sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
"@types/ssh2" "*"
|
||||||
|
|
||||||
"@types/dockerode@^2.5.34":
|
"@types/dockerode@^2.5.34":
|
||||||
version "2.5.34"
|
version "2.5.34"
|
||||||
resolved "https://registry.yarnpkg.com/@types/dockerode/-/dockerode-2.5.34.tgz#9adb884f7cc6c012a6eb4b2ad794cc5d01439959"
|
resolved "https://registry.yarnpkg.com/@types/dockerode/-/dockerode-2.5.34.tgz#9adb884f7cc6c012a6eb4b2ad794cc5d01439959"
|
||||||
|
@ -5225,6 +5233,14 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/dockerode@^3.3.21":
|
||||||
|
version "3.3.23"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/dockerode/-/dockerode-3.3.23.tgz#07b2084013d01e14d5d97856446f4d9c9f27c223"
|
||||||
|
integrity sha512-Lz5J+NFgZS4cEVhquwjIGH4oQwlVn2h7LXD3boitujBnzOE5o7s9H8hchEjoDK2SlRsJTogdKnQeiJgPPKLIEw==
|
||||||
|
dependencies:
|
||||||
|
"@types/docker-modem" "*"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/eslint-scope@^3.7.3":
|
"@types/eslint-scope@^3.7.3":
|
||||||
version "3.7.4"
|
version "3.7.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16"
|
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16"
|
||||||
|
@ -5563,6 +5579,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~5.26.4"
|
undici-types "~5.26.4"
|
||||||
|
|
||||||
|
"@types/node@^18.11.18":
|
||||||
|
version "18.19.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.10.tgz#4de314ab66faf6bc8ba691021a091ddcdf13a158"
|
||||||
|
integrity sha512-IZD8kAM02AW1HRDTPOlz3npFava678pr8Ie9Vp8uRhBROXAv8MXT2pCnGZZAKYdromsNQLHQcfWQ6EOatVLtqA==
|
||||||
|
dependencies:
|
||||||
|
undici-types "~5.26.4"
|
||||||
|
|
||||||
"@types/nodemailer@^6.4.4":
|
"@types/nodemailer@^6.4.4":
|
||||||
version "6.4.14"
|
version "6.4.14"
|
||||||
resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.14.tgz#5c81a5e856db7f8ede80013e6dbad7c5fb2283e2"
|
resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.14.tgz#5c81a5e856db7f8ede80013e6dbad7c5fb2283e2"
|
||||||
|
@ -5867,6 +5890,28 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/ssh2-streams@*":
|
||||||
|
version "0.1.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/ssh2-streams/-/ssh2-streams-0.1.12.tgz#e68795ba2bf01c76b93f9c9809e1f42f0eaaec5f"
|
||||||
|
integrity sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/ssh2@*":
|
||||||
|
version "1.11.19"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-1.11.19.tgz#4f2ec691b0674ea1590915fe5114a9aeae0eb41d"
|
||||||
|
integrity sha512-ydbQAqEcdNVy2t1w7dMh6eWMr+iOgtEkqM/3K9RMijMaok/ER7L8GW6PwsOypHCN++M+c8S/UR9SgMqNIFstbA==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "^18.11.18"
|
||||||
|
|
||||||
|
"@types/ssh2@^0.5.48":
|
||||||
|
version "0.5.52"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-0.5.52.tgz#9dbd8084e2a976e551d5e5e70b978ed8b5965741"
|
||||||
|
integrity sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
"@types/ssh2-streams" "*"
|
||||||
|
|
||||||
"@types/stack-utils@^2.0.0":
|
"@types/stack-utils@^2.0.0":
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
||||||
|
@ -6653,6 +6698,51 @@ archive-type@^4.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
file-type "^4.2.0"
|
file-type "^4.2.0"
|
||||||
|
|
||||||
|
archiver-utils@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2"
|
||||||
|
integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==
|
||||||
|
dependencies:
|
||||||
|
glob "^7.1.4"
|
||||||
|
graceful-fs "^4.2.0"
|
||||||
|
lazystream "^1.0.0"
|
||||||
|
lodash.defaults "^4.2.0"
|
||||||
|
lodash.difference "^4.5.0"
|
||||||
|
lodash.flatten "^4.4.0"
|
||||||
|
lodash.isplainobject "^4.0.6"
|
||||||
|
lodash.union "^4.6.0"
|
||||||
|
normalize-path "^3.0.0"
|
||||||
|
readable-stream "^2.0.0"
|
||||||
|
|
||||||
|
archiver-utils@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-3.0.4.tgz#a0d201f1cf8fce7af3b5a05aea0a337329e96ec7"
|
||||||
|
integrity sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==
|
||||||
|
dependencies:
|
||||||
|
glob "^7.2.3"
|
||||||
|
graceful-fs "^4.2.0"
|
||||||
|
lazystream "^1.0.0"
|
||||||
|
lodash.defaults "^4.2.0"
|
||||||
|
lodash.difference "^4.5.0"
|
||||||
|
lodash.flatten "^4.4.0"
|
||||||
|
lodash.isplainobject "^4.0.6"
|
||||||
|
lodash.union "^4.6.0"
|
||||||
|
normalize-path "^3.0.0"
|
||||||
|
readable-stream "^3.6.0"
|
||||||
|
|
||||||
|
archiver@^5.3.2:
|
||||||
|
version "5.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.2.tgz#99991d5957e53bd0303a392979276ac4ddccf3b0"
|
||||||
|
integrity sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==
|
||||||
|
dependencies:
|
||||||
|
archiver-utils "^2.1.0"
|
||||||
|
async "^3.2.4"
|
||||||
|
buffer-crc32 "^0.2.1"
|
||||||
|
readable-stream "^3.6.0"
|
||||||
|
readdir-glob "^1.1.2"
|
||||||
|
tar-stream "^2.2.0"
|
||||||
|
zip-stream "^4.1.0"
|
||||||
|
|
||||||
are-we-there-yet@^2.0.0:
|
are-we-there-yet@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c"
|
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c"
|
||||||
|
@ -6820,7 +6910,7 @@ asn1.js@^5.0.0, asn1.js@^5.2.0, asn1.js@^5.4.1:
|
||||||
minimalistic-assert "^1.0.0"
|
minimalistic-assert "^1.0.0"
|
||||||
safer-buffer "^2.1.0"
|
safer-buffer "^2.1.0"
|
||||||
|
|
||||||
asn1@^0.2.4, asn1@~0.2.3:
|
asn1@^0.2.4, asn1@^0.2.6, asn1@~0.2.3:
|
||||||
version "0.2.6"
|
version "0.2.6"
|
||||||
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d"
|
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d"
|
||||||
integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==
|
integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==
|
||||||
|
@ -6852,6 +6942,11 @@ ast-types@0.9.6:
|
||||||
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9"
|
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9"
|
||||||
integrity sha512-qEdtR2UH78yyHX/AUNfXmJTlM48XoFZKBdwi1nzkI1mJL21cmbu0cvjxjpkXJ5NENMq42H+hNs8VLJcqXLerBQ==
|
integrity sha512-qEdtR2UH78yyHX/AUNfXmJTlM48XoFZKBdwi1nzkI1mJL21cmbu0cvjxjpkXJ5NENMq42H+hNs8VLJcqXLerBQ==
|
||||||
|
|
||||||
|
async-lock@^1.4.0:
|
||||||
|
version "1.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.4.1.tgz#56b8718915a9b68b10fce2f2a9a3dddf765ef53f"
|
||||||
|
integrity sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==
|
||||||
|
|
||||||
async-retry@^1.3.3:
|
async-retry@^1.3.3:
|
||||||
version "1.3.3"
|
version "1.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280"
|
resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280"
|
||||||
|
@ -6871,6 +6966,11 @@ async@^3.2.1, async@^3.2.3:
|
||||||
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
|
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
|
||||||
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
|
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
|
||||||
|
|
||||||
|
async@^3.2.4:
|
||||||
|
version "3.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66"
|
||||||
|
integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==
|
||||||
|
|
||||||
asynckit@^0.4.0:
|
asynckit@^0.4.0:
|
||||||
version "0.4.0"
|
version "0.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||||
|
@ -6968,6 +7068,11 @@ axios@^1.0.0, axios@^1.1.3, axios@^1.5.0:
|
||||||
form-data "^4.0.0"
|
form-data "^4.0.0"
|
||||||
proxy-from-env "^1.1.0"
|
proxy-from-env "^1.1.0"
|
||||||
|
|
||||||
|
b4a@^1.6.4:
|
||||||
|
version "1.6.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9"
|
||||||
|
integrity sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==
|
||||||
|
|
||||||
babel-jest@^29.6.2, babel-jest@^29.7.0:
|
babel-jest@^29.6.2, babel-jest@^29.7.0:
|
||||||
version "29.7.0"
|
version "29.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5"
|
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5"
|
||||||
|
@ -7410,7 +7515,7 @@ buffer-alloc@^1.2.0:
|
||||||
buffer-alloc-unsafe "^1.1.0"
|
buffer-alloc-unsafe "^1.1.0"
|
||||||
buffer-fill "^1.0.0"
|
buffer-fill "^1.0.0"
|
||||||
|
|
||||||
buffer-crc32@^0.2.13, buffer-crc32@~0.2.3:
|
buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3:
|
||||||
version "0.2.13"
|
version "0.2.13"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||||
integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
|
integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
|
||||||
|
@ -7492,6 +7597,11 @@ buildcheck@0.0.3:
|
||||||
resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.3.tgz#70451897a95d80f7807e68fc412eb2e7e35ff4d5"
|
resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.3.tgz#70451897a95d80f7807e68fc412eb2e7e35ff4d5"
|
||||||
integrity sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA==
|
integrity sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA==
|
||||||
|
|
||||||
|
buildcheck@~0.0.6:
|
||||||
|
version "0.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238"
|
||||||
|
integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==
|
||||||
|
|
||||||
builtin-modules@^3.1.0:
|
builtin-modules@^3.1.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
|
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
|
||||||
|
@ -8196,6 +8306,16 @@ component-type@^1.2.1:
|
||||||
resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9"
|
resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9"
|
||||||
integrity sha512-Kgy+2+Uwr75vAi6ChWXgHuLvd+QLD7ssgpaRq2zCvt80ptvAfMc/hijcJxXkBa2wMlEZcJvC2H8Ubo+A9ATHIg==
|
integrity sha512-Kgy+2+Uwr75vAi6ChWXgHuLvd+QLD7ssgpaRq2zCvt80ptvAfMc/hijcJxXkBa2wMlEZcJvC2H8Ubo+A9ATHIg==
|
||||||
|
|
||||||
|
compress-commons@^4.1.2:
|
||||||
|
version "4.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.2.tgz#6542e59cb63e1f46a8b21b0e06f9a32e4c8b06df"
|
||||||
|
integrity sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==
|
||||||
|
dependencies:
|
||||||
|
buffer-crc32 "^0.2.13"
|
||||||
|
crc32-stream "^4.0.2"
|
||||||
|
normalize-path "^3.0.0"
|
||||||
|
readable-stream "^3.6.0"
|
||||||
|
|
||||||
compressible@^2.0.0, compressible@^2.0.12:
|
compressible@^2.0.0, compressible@^2.0.12:
|
||||||
version "2.0.18"
|
version "2.0.18"
|
||||||
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
|
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
|
||||||
|
@ -8467,6 +8587,27 @@ cpu-features@~0.0.4:
|
||||||
buildcheck "0.0.3"
|
buildcheck "0.0.3"
|
||||||
nan "^2.15.0"
|
nan "^2.15.0"
|
||||||
|
|
||||||
|
cpu-features@~0.0.9:
|
||||||
|
version "0.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.9.tgz#5226b92f0f1c63122b0a3eb84cb8335a4de499fc"
|
||||||
|
integrity sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==
|
||||||
|
dependencies:
|
||||||
|
buildcheck "~0.0.6"
|
||||||
|
nan "^2.17.0"
|
||||||
|
|
||||||
|
crc-32@^1.2.0:
|
||||||
|
version "1.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff"
|
||||||
|
integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
|
||||||
|
|
||||||
|
crc32-stream@^4.0.2:
|
||||||
|
version "4.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.3.tgz#85dd677eb78fa7cad1ba17cc506a597d41fc6f33"
|
||||||
|
integrity sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==
|
||||||
|
dependencies:
|
||||||
|
crc-32 "^1.2.0"
|
||||||
|
readable-stream "^3.4.0"
|
||||||
|
|
||||||
crc@^3.4.4:
|
crc@^3.4.4:
|
||||||
version "3.8.0"
|
version "3.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6"
|
resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6"
|
||||||
|
@ -9362,6 +9503,13 @@ docker-compose@^0.23.5, docker-compose@^0.23.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
yaml "^1.10.2"
|
yaml "^1.10.2"
|
||||||
|
|
||||||
|
docker-compose@^0.24.2:
|
||||||
|
version "0.24.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.24.3.tgz#298d7bb4aaf37b3b45d0e4ef55c7f58ccc39cca9"
|
||||||
|
integrity sha512-x3/QN3AIOMe7j2c8f/jcycizMft7dl8MluoB9OGPAYCyKHHiPUFqI9GjCcsU0kYy24vYKMCcfR6+5ZaEyQlrxg==
|
||||||
|
dependencies:
|
||||||
|
yaml "^2.2.2"
|
||||||
|
|
||||||
docker-modem@^3.0.0:
|
docker-modem@^3.0.0:
|
||||||
version "3.0.6"
|
version "3.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-3.0.6.tgz#8c76338641679e28ec2323abb65b3276fb1ce597"
|
resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-3.0.6.tgz#8c76338641679e28ec2323abb65b3276fb1ce597"
|
||||||
|
@ -9381,6 +9529,15 @@ dockerode@^3.2.1:
|
||||||
docker-modem "^3.0.0"
|
docker-modem "^3.0.0"
|
||||||
tar-fs "~2.0.1"
|
tar-fs "~2.0.1"
|
||||||
|
|
||||||
|
dockerode@^3.3.5:
|
||||||
|
version "3.3.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-3.3.5.tgz#7ae3f40f2bec53ae5e9a741ce655fff459745629"
|
||||||
|
integrity sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==
|
||||||
|
dependencies:
|
||||||
|
"@balena/dockerignore" "^1.0.2"
|
||||||
|
docker-modem "^3.0.0"
|
||||||
|
tar-fs "~2.0.1"
|
||||||
|
|
||||||
doctrine@3.0.0, doctrine@^3.0.0:
|
doctrine@3.0.0, doctrine@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
|
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
|
||||||
|
@ -10464,6 +10621,11 @@ fast-deep-equal@3.1.3, fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||||
|
|
||||||
|
fast-fifo@^1.1.0, fast-fifo@^1.2.0:
|
||||||
|
version "1.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c"
|
||||||
|
integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==
|
||||||
|
|
||||||
fast-glob@3.2.7:
|
fast-glob@3.2.7:
|
||||||
version "3.2.7"
|
version "3.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1"
|
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1"
|
||||||
|
@ -11344,7 +11506,7 @@ glob@^5.0.15:
|
||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
path-is-absolute "^1.0.0"
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
glob@^7.0.5, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
|
glob@^7.0.5, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0, glob@^7.2.3:
|
||||||
version "7.2.3"
|
version "7.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
|
||||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||||
|
@ -14033,6 +14195,13 @@ lazy@^1.0.11:
|
||||||
resolved "https://registry.yarnpkg.com/lazy/-/lazy-1.0.11.tgz#daa068206282542c088288e975c297c1ae77b690"
|
resolved "https://registry.yarnpkg.com/lazy/-/lazy-1.0.11.tgz#daa068206282542c088288e975c297c1ae77b690"
|
||||||
integrity sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==
|
integrity sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==
|
||||||
|
|
||||||
|
lazystream@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638"
|
||||||
|
integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==
|
||||||
|
dependencies:
|
||||||
|
readable-stream "^2.0.5"
|
||||||
|
|
||||||
lcid@^2.0.0:
|
lcid@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf"
|
resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf"
|
||||||
|
@ -14501,6 +14670,11 @@ lodash.defaults@^4.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
|
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
|
||||||
integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==
|
integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==
|
||||||
|
|
||||||
|
lodash.difference@^4.5.0:
|
||||||
|
version "4.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c"
|
||||||
|
integrity sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==
|
||||||
|
|
||||||
lodash.flatten@^4.4.0:
|
lodash.flatten@^4.4.0:
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
|
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
|
||||||
|
@ -14601,6 +14775,11 @@ lodash.sortby@^4.7.0:
|
||||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||||
integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==
|
integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==
|
||||||
|
|
||||||
|
lodash.union@^4.6.0:
|
||||||
|
version "4.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
|
||||||
|
integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==
|
||||||
|
|
||||||
lodash.uniq@^4.5.0:
|
lodash.uniq@^4.5.0:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||||
|
@ -15136,7 +15315,7 @@ minimatch@3.0.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
minimatch@^5.0.1:
|
minimatch@^5.0.1, minimatch@^5.1.0:
|
||||||
version "5.1.6"
|
version "5.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
|
||||||
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
|
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
|
||||||
|
@ -15483,6 +15662,11 @@ nan@^2.15.0, nan@^2.16.0:
|
||||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb"
|
resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb"
|
||||||
integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==
|
integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==
|
||||||
|
|
||||||
|
nan@^2.17.0, nan@^2.18.0:
|
||||||
|
version "2.18.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554"
|
||||||
|
integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==
|
||||||
|
|
||||||
nanoclone@^0.2.1:
|
nanoclone@^0.2.1:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4"
|
resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4"
|
||||||
|
@ -15585,7 +15769,7 @@ node-fetch@2.6.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
whatwg-url "^5.0.0"
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.6.9:
|
node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.6.9, node-fetch@^2.7.0:
|
||||||
version "2.7.0"
|
version "2.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
|
||||||
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
||||||
|
@ -17874,6 +18058,22 @@ promzard@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
read "^2.0.0"
|
read "^2.0.0"
|
||||||
|
|
||||||
|
proper-lockfile@^4.1.2:
|
||||||
|
version "4.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f"
|
||||||
|
integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==
|
||||||
|
dependencies:
|
||||||
|
graceful-fs "^4.2.4"
|
||||||
|
retry "^0.12.0"
|
||||||
|
signal-exit "^3.0.2"
|
||||||
|
|
||||||
|
properties-reader@^2.3.0:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/properties-reader/-/properties-reader-2.3.0.tgz#f3ab84224c9535a7a36e011ae489a79a13b472b2"
|
||||||
|
integrity sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==
|
||||||
|
dependencies:
|
||||||
|
mkdirp "^1.0.4"
|
||||||
|
|
||||||
property-expr@^2.0.4:
|
property-expr@^2.0.4:
|
||||||
version "2.0.6"
|
version "2.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.6.tgz#f77bc00d5928a6c748414ad12882e83f24aec1e8"
|
resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.6.tgz#f77bc00d5928a6c748414ad12882e83f24aec1e8"
|
||||||
|
@ -18083,6 +18283,11 @@ queue-microtask@^1.2.2:
|
||||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||||
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
||||||
|
|
||||||
|
queue-tick@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142"
|
||||||
|
integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==
|
||||||
|
|
||||||
quick-format-unescaped@^4.0.3:
|
quick-format-unescaped@^4.0.3:
|
||||||
version "4.0.4"
|
version "4.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7"
|
resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7"
|
||||||
|
@ -18250,7 +18455,7 @@ readable-stream@1.1.14, readable-stream@^1.0.26-4, readable-stream@^1.0.27-1:
|
||||||
string_decoder "^1.1.1"
|
string_decoder "^1.1.1"
|
||||||
util-deprecate "^1.0.1"
|
util-deprecate "^1.0.1"
|
||||||
|
|
||||||
readable-stream@^2.0.0, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6:
|
readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6:
|
||||||
version "2.3.8"
|
version "2.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
|
||||||
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
|
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
|
||||||
|
@ -18296,6 +18501,13 @@ readable-web-to-node-stream@^3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
readable-stream "^3.6.0"
|
readable-stream "^3.6.0"
|
||||||
|
|
||||||
|
readdir-glob@^1.1.2:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584"
|
||||||
|
integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==
|
||||||
|
dependencies:
|
||||||
|
minimatch "^5.1.0"
|
||||||
|
|
||||||
readdirp@~3.6.0:
|
readdirp@~3.6.0:
|
||||||
version "3.6.0"
|
version "3.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
||||||
|
@ -19569,6 +19781,14 @@ sqlstring@^2.3.2:
|
||||||
resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c"
|
resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c"
|
||||||
integrity sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==
|
integrity sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==
|
||||||
|
|
||||||
|
ssh-remote-port-forward@^1.0.4:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz#72b0c5df8ec27ca300c75805cc6b266dee07e298"
|
||||||
|
integrity sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/ssh2" "^0.5.48"
|
||||||
|
ssh2 "^1.4.0"
|
||||||
|
|
||||||
ssh2@^1.11.0:
|
ssh2@^1.11.0:
|
||||||
version "1.11.0"
|
version "1.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.11.0.tgz#ce60186216971e12f6deb553dcf82322498fe2e4"
|
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.11.0.tgz#ce60186216971e12f6deb553dcf82322498fe2e4"
|
||||||
|
@ -19580,6 +19800,17 @@ ssh2@^1.11.0:
|
||||||
cpu-features "~0.0.4"
|
cpu-features "~0.0.4"
|
||||||
nan "^2.16.0"
|
nan "^2.16.0"
|
||||||
|
|
||||||
|
ssh2@^1.4.0:
|
||||||
|
version "1.15.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.15.0.tgz#2f998455036a7f89e0df5847efb5421748d9871b"
|
||||||
|
integrity sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==
|
||||||
|
dependencies:
|
||||||
|
asn1 "^0.2.6"
|
||||||
|
bcrypt-pbkdf "^1.0.2"
|
||||||
|
optionalDependencies:
|
||||||
|
cpu-features "~0.0.9"
|
||||||
|
nan "^2.18.0"
|
||||||
|
|
||||||
sshpk@^1.7.0:
|
sshpk@^1.7.0:
|
||||||
version "1.17.0"
|
version "1.17.0"
|
||||||
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5"
|
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5"
|
||||||
|
@ -19687,6 +19918,14 @@ stream-to-array@^2.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
any-promise "^1.1.0"
|
any-promise "^1.1.0"
|
||||||
|
|
||||||
|
streamx@^2.15.0:
|
||||||
|
version "2.15.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.15.6.tgz#28bf36997ebc7bf6c08f9eba958735231b833887"
|
||||||
|
integrity sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==
|
||||||
|
dependencies:
|
||||||
|
fast-fifo "^1.1.0"
|
||||||
|
queue-tick "^1.0.1"
|
||||||
|
|
||||||
strict-uri-encode@^1.0.0:
|
strict-uri-encode@^1.0.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||||
|
@ -20220,6 +20459,15 @@ tar-fs@2.1.1, tar-fs@^2.1.0:
|
||||||
pump "^3.0.0"
|
pump "^3.0.0"
|
||||||
tar-stream "^2.1.4"
|
tar-stream "^2.1.4"
|
||||||
|
|
||||||
|
tar-fs@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.4.tgz#a21dc60a2d5d9f55e0089ccd78124f1d3771dbbf"
|
||||||
|
integrity sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==
|
||||||
|
dependencies:
|
||||||
|
mkdirp-classic "^0.5.2"
|
||||||
|
pump "^3.0.0"
|
||||||
|
tar-stream "^3.1.5"
|
||||||
|
|
||||||
tar-fs@~2.0.1:
|
tar-fs@~2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2"
|
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2"
|
||||||
|
@ -20243,7 +20491,7 @@ tar-stream@^1.5.2:
|
||||||
to-buffer "^1.1.1"
|
to-buffer "^1.1.1"
|
||||||
xtend "^4.0.0"
|
xtend "^4.0.0"
|
||||||
|
|
||||||
tar-stream@^2.0.0, tar-stream@^2.1.4, tar-stream@~2.2.0:
|
tar-stream@^2.0.0, tar-stream@^2.1.4, tar-stream@^2.2.0, tar-stream@~2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
|
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
|
||||||
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
|
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
|
||||||
|
@ -20254,6 +20502,15 @@ tar-stream@^2.0.0, tar-stream@^2.1.4, tar-stream@~2.2.0:
|
||||||
inherits "^2.0.3"
|
inherits "^2.0.3"
|
||||||
readable-stream "^3.1.1"
|
readable-stream "^3.1.1"
|
||||||
|
|
||||||
|
tar-stream@^3.1.5:
|
||||||
|
version "3.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b"
|
||||||
|
integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==
|
||||||
|
dependencies:
|
||||||
|
b4a "^1.6.4"
|
||||||
|
fast-fifo "^1.2.0"
|
||||||
|
streamx "^2.15.0"
|
||||||
|
|
||||||
tar@6.1.11:
|
tar@6.1.11:
|
||||||
version "6.1.11"
|
version "6.1.11"
|
||||||
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
|
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
|
||||||
|
@ -20347,6 +20604,27 @@ test-exclude@^6.0.0:
|
||||||
glob "^7.1.4"
|
glob "^7.1.4"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
|
|
||||||
|
testcontainers@10.6.0:
|
||||||
|
version "10.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/testcontainers/-/testcontainers-10.6.0.tgz#205ad9148e68ff5c43a6209a30b12965acbe89d8"
|
||||||
|
integrity sha512-FDJ3o3J8IMu1V7Uc6lNZ2MAD8+BV4HdpR/Vf5mHtgYHKdn6k1EbGFwtnvVNOxanJ99FCjf/EU8eA5ZQ4yjlsGA==
|
||||||
|
dependencies:
|
||||||
|
"@balena/dockerignore" "^1.0.2"
|
||||||
|
"@types/dockerode" "^3.3.21"
|
||||||
|
archiver "^5.3.2"
|
||||||
|
async-lock "^1.4.0"
|
||||||
|
byline "^5.0.0"
|
||||||
|
debug "^4.3.4"
|
||||||
|
docker-compose "^0.24.2"
|
||||||
|
dockerode "^3.3.5"
|
||||||
|
get-port "^5.1.1"
|
||||||
|
node-fetch "^2.7.0"
|
||||||
|
proper-lockfile "^4.1.2"
|
||||||
|
properties-reader "^2.3.0"
|
||||||
|
ssh-remote-port-forward "^1.0.4"
|
||||||
|
tar-fs "^3.0.4"
|
||||||
|
tmp "^0.2.1"
|
||||||
|
|
||||||
testcontainers@4.7.0:
|
testcontainers@4.7.0:
|
||||||
version "4.7.0"
|
version "4.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/testcontainers/-/testcontainers-4.7.0.tgz#5a9a864b1b0cc86984086dcc737c2f5e73490cf3"
|
resolved "https://registry.yarnpkg.com/testcontainers/-/testcontainers-4.7.0.tgz#5a9a864b1b0cc86984086dcc737c2f5e73490cf3"
|
||||||
|
@ -21954,6 +22232,11 @@ yaml@^2.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.2.tgz#f522db4313c671a0ca963a75670f1c12ea909144"
|
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.2.tgz#f522db4313c671a0ca963a75670f1c12ea909144"
|
||||||
integrity sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==
|
integrity sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==
|
||||||
|
|
||||||
|
yaml@^2.2.2:
|
||||||
|
version "2.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2"
|
||||||
|
integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==
|
||||||
|
|
||||||
yamljs@^0.3.0:
|
yamljs@^0.3.0:
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/yamljs/-/yamljs-0.3.0.tgz#dc060bf267447b39f7304e9b2bfbe8b5a7ddb03b"
|
resolved "https://registry.yarnpkg.com/yamljs/-/yamljs-0.3.0.tgz#dc060bf267447b39f7304e9b2bfbe8b5a7ddb03b"
|
||||||
|
@ -22110,3 +22393,12 @@ zeebe-node@^8.2.5:
|
||||||
stack-trace "0.0.10"
|
stack-trace "0.0.10"
|
||||||
typed-duration "^1.0.12"
|
typed-duration "^1.0.12"
|
||||||
uuid "^7.0.3"
|
uuid "^7.0.3"
|
||||||
|
|
||||||
|
zip-stream@^4.1.0:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.1.tgz#1337fe974dbaffd2fa9a1ba09662a66932bd7135"
|
||||||
|
integrity sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==
|
||||||
|
dependencies:
|
||||||
|
archiver-utils "^3.0.4"
|
||||||
|
compress-commons "^4.1.2"
|
||||||
|
readable-stream "^3.6.0"
|
||||||
|
|
Loading…
Reference in a new issue