merge
This commit is contained in:
commit
e37e6af4f6
|
@ -0,0 +1,15 @@
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Versions
|
||||||
|
|
||||||
|
As an open source product, we will only patch the latest major version for security vulnerabilities. Previous versions of budibase will not be retroactively patched.
|
||||||
|
|
||||||
|
## Disclosing
|
||||||
|
|
||||||
|
You can get in touch with us regarding a vulnerability via email at community@budibase.com.
|
||||||
|
|
||||||
|
You can also disclose via huntr.dev. If you believe you have found a vulnerability, please disclose it on huntr and let us know.
|
||||||
|
|
||||||
|
https://huntr.dev/bounties/disclose
|
||||||
|
|
||||||
|
This will enable us to review the vulnerability and potentially reward you for your work.
|
|
@ -33,11 +33,19 @@ static_resources:
|
||||||
route:
|
route:
|
||||||
cluster: app-service
|
cluster: app-service
|
||||||
|
|
||||||
# special case for worker admin API
|
# special cases for worker admin (deprecated), global and system API
|
||||||
|
- match: { prefix: "/api/global/" }
|
||||||
|
route:
|
||||||
|
cluster: worker-service
|
||||||
|
|
||||||
- match: { prefix: "/api/admin/" }
|
- match: { prefix: "/api/admin/" }
|
||||||
route:
|
route:
|
||||||
cluster: worker-service
|
cluster: worker-service
|
||||||
|
|
||||||
|
- match: { prefix: "/api/system/" }
|
||||||
|
route:
|
||||||
|
cluster: worker-service
|
||||||
|
|
||||||
- match: { path: "/" }
|
- match: { path: "/" }
|
||||||
route:
|
route:
|
||||||
cluster: app-service
|
cluster: app-service
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "0.9.121",
|
"version": "0.9.123-alpha.1",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
"setup": "node ./hosting/scripts/setup.js && yarn && yarn bootstrap && yarn build && yarn dev",
|
"setup": "node ./hosting/scripts/setup.js && yarn && yarn bootstrap && yarn build && yarn dev",
|
||||||
"bootstrap": "lerna link && lerna bootstrap",
|
"bootstrap": "lerna link && lerna bootstrap",
|
||||||
"build": "lerna run build",
|
"build": "lerna run build",
|
||||||
"initialise": "lerna run initialise",
|
|
||||||
"publishdev": "lerna run publishdev",
|
"publishdev": "lerna run publishdev",
|
||||||
"publishnpm": "yarn build && lerna publish --force-publish",
|
"publishnpm": "yarn build && lerna publish --force-publish",
|
||||||
"release": "yarn build && lerna publish patch --yes --force-publish",
|
"release": "yarn build && lerna publish patch --yes --force-publish",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/auth",
|
"name": "@budibase/auth",
|
||||||
"version": "0.9.121",
|
"version": "0.9.123-alpha.1",
|
||||||
"description": "Authentication middlewares for budibase builder and apps",
|
"description": "Authentication middlewares for budibase builder and apps",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
|
|
|
@ -2,12 +2,13 @@ const { setTenantId } = require("../tenancy")
|
||||||
const ContextFactory = require("../tenancy/FunctionContext")
|
const ContextFactory = require("../tenancy/FunctionContext")
|
||||||
const { buildMatcherRegex, matches } = require("./matchers")
|
const { buildMatcherRegex, matches } = require("./matchers")
|
||||||
|
|
||||||
module.exports = (allowQueryStringPatterns, noTenancyPatterns) => {
|
module.exports = (allowQueryStringPatterns, noTenancyPatterns, opts = {}) => {
|
||||||
const allowQsOptions = buildMatcherRegex(allowQueryStringPatterns)
|
const allowQsOptions = buildMatcherRegex(allowQueryStringPatterns)
|
||||||
const noTenancyOptions = buildMatcherRegex(noTenancyPatterns)
|
const noTenancyOptions = buildMatcherRegex(noTenancyPatterns)
|
||||||
|
|
||||||
return ContextFactory.getMiddleware(ctx => {
|
return ContextFactory.getMiddleware(ctx => {
|
||||||
const allowNoTenant = !!matches(ctx, noTenancyOptions)
|
const allowNoTenant =
|
||||||
|
opts.noTenancyRequired || !!matches(ctx, noTenancyOptions)
|
||||||
const allowQs = !!matches(ctx, allowQsOptions)
|
const allowQs = !!matches(ctx, allowQsOptions)
|
||||||
setTenantId(ctx, { allowQs, allowNoTenant })
|
setTenantId(ctx, { allowQs, allowNoTenant })
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/bbui",
|
"name": "@budibase/bbui",
|
||||||
"description": "A UI solution used in the different Budibase projects.",
|
"description": "A UI solution used in the different Budibase projects.",
|
||||||
"version": "0.9.121",
|
"version": "0.9.123-alpha.1",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"module": "dist/bbui.es.js",
|
"module": "dist/bbui.es.js",
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import "@spectrum-css/table/dist/index-vars.css"
|
import "@spectrum-css/table/dist/index-vars.css"
|
||||||
import CellRenderer from "./CellRenderer.svelte"
|
import CellRenderer from "./CellRenderer.svelte"
|
||||||
import SelectEditRenderer from "./SelectEditRenderer.svelte"
|
import SelectEditRenderer from "./SelectEditRenderer.svelte"
|
||||||
|
import { cloneDeep } from "lodash"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The expected schema is our normal couch schemas for our tables.
|
* The expected schema is our normal couch schemas for our tables.
|
||||||
|
@ -197,7 +198,7 @@
|
||||||
|
|
||||||
const editRow = (e, row) => {
|
const editRow = (e, row) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
dispatch("editrow", row)
|
dispatch("editrow", cloneDeep(row))
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleSelectRow = row => {
|
const toggleSelectRow = row => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "0.9.121",
|
"version": "0.9.123-alpha.1",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -65,10 +65,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^0.9.121",
|
"@budibase/bbui": "^0.9.123-alpha.1",
|
||||||
"@budibase/client": "^0.9.121",
|
"@budibase/client": "^0.9.123-alpha.1",
|
||||||
"@budibase/colorpicker": "1.1.2",
|
"@budibase/colorpicker": "1.1.2",
|
||||||
"@budibase/string-templates": "^0.9.121",
|
"@budibase/string-templates": "^0.9.123-alpha.1",
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
"@spectrum-css/page": "^3.0.1",
|
"@spectrum-css/page": "^3.0.1",
|
||||||
"@spectrum-css/vars": "^3.0.1",
|
"@spectrum-css/vars": "^3.0.1",
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { findComponent, findComponentPath } from "./storeUtils"
|
import {
|
||||||
|
findComponent,
|
||||||
|
findComponentPath,
|
||||||
|
findAllMatchingComponents,
|
||||||
|
} from "./storeUtils"
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import { tables as tablesStore, queries as queriesStores } from "stores/backend"
|
import { tables as tablesStore, queries as queriesStores } from "stores/backend"
|
||||||
import { makePropSafe } from "@budibase/string-templates"
|
import { makePropSafe } from "@budibase/string-templates"
|
||||||
|
@ -18,7 +22,9 @@ export const getBindableProperties = (asset, componentId) => {
|
||||||
const userBindings = getUserBindings()
|
const userBindings = getUserBindings()
|
||||||
const urlBindings = getUrlBindings(asset)
|
const urlBindings = getUrlBindings(asset)
|
||||||
const deviceBindings = getDeviceBindings()
|
const deviceBindings = getDeviceBindings()
|
||||||
|
const stateBindings = getStateBindings()
|
||||||
return [
|
return [
|
||||||
|
...stateBindings,
|
||||||
...deviceBindings,
|
...deviceBindings,
|
||||||
...urlBindings,
|
...urlBindings,
|
||||||
...contextBindings,
|
...contextBindings,
|
||||||
|
@ -256,6 +262,18 @@ const getDeviceBindings = () => {
|
||||||
return bindings
|
return bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all state bindings that are globally available.
|
||||||
|
*/
|
||||||
|
const getStateBindings = () => {
|
||||||
|
const safeState = makePropSafe("state")
|
||||||
|
return getAllStateVariables().map(key => ({
|
||||||
|
type: "context",
|
||||||
|
runtimeBinding: `${safeState}.${makePropSafe(key)}`,
|
||||||
|
readableBinding: `State.${key}`,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all bindable properties from URL parameters.
|
* Gets all bindable properties from URL parameters.
|
||||||
*/
|
*/
|
||||||
|
@ -458,3 +476,49 @@ export function runtimeToReadableBinding(bindableProperties, textWithBindings) {
|
||||||
"readableBinding"
|
"readableBinding"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of the keys of any state variables which are set anywhere
|
||||||
|
* in the app.
|
||||||
|
*/
|
||||||
|
export const getAllStateVariables = () => {
|
||||||
|
let allComponents = []
|
||||||
|
|
||||||
|
// Find all onClick settings in all layouts
|
||||||
|
get(store).layouts.forEach(layout => {
|
||||||
|
const components = findAllMatchingComponents(
|
||||||
|
layout.props,
|
||||||
|
c => c.onClick != null
|
||||||
|
)
|
||||||
|
allComponents = allComponents.concat(components || [])
|
||||||
|
})
|
||||||
|
|
||||||
|
// Find all onClick settings in all screens
|
||||||
|
get(store).screens.forEach(screen => {
|
||||||
|
const components = findAllMatchingComponents(
|
||||||
|
screen.props,
|
||||||
|
c => c.onClick != null
|
||||||
|
)
|
||||||
|
allComponents = allComponents.concat(components || [])
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add state bindings for all state actions
|
||||||
|
let bindingSet = new Set()
|
||||||
|
allComponents.forEach(component => {
|
||||||
|
if (!Array.isArray(component.onClick)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
component.onClick.forEach(action => {
|
||||||
|
if (
|
||||||
|
action["##eventHandlerType"] === "Update State" &&
|
||||||
|
action.parameters?.type === "set" &&
|
||||||
|
action.parameters?.key &&
|
||||||
|
action.parameters?.value
|
||||||
|
) {
|
||||||
|
bindingSet.add(action.parameters.key)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return Array.from(bindingSet)
|
||||||
|
}
|
||||||
|
|
|
@ -18,12 +18,10 @@
|
||||||
let exportFormat = FORMATS[0].key
|
let exportFormat = FORMATS[0].key
|
||||||
|
|
||||||
async function exportView() {
|
async function exportView() {
|
||||||
const filename = `export.${exportFormat}`
|
|
||||||
download(
|
download(
|
||||||
`/api/views/export?view=${encodeURIComponent(
|
`/api/views/export?view=${encodeURIComponent(
|
||||||
view
|
view
|
||||||
)}&format=${exportFormat}`,
|
)}&format=${exportFormat}`
|
||||||
filename
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
<script>
|
||||||
|
import { Select, Label, Combobox, Checkbox, Body } from "@budibase/bbui"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
||||||
|
import { getAllStateVariables } from "builderStore/dataBinding"
|
||||||
|
|
||||||
|
export let parameters
|
||||||
|
export let bindings = []
|
||||||
|
|
||||||
|
const keyOptions = getAllStateVariables()
|
||||||
|
const typeOptions = [
|
||||||
|
{
|
||||||
|
label: "Set value",
|
||||||
|
value: "set",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Delete value",
|
||||||
|
value: "delete",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (!parameters.type) {
|
||||||
|
parameters.type = "set"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="root">
|
||||||
|
<Label small>Type</Label>
|
||||||
|
<Select
|
||||||
|
placeholder={null}
|
||||||
|
bind:value={parameters.type}
|
||||||
|
options={typeOptions}
|
||||||
|
/>
|
||||||
|
<Label small>Key</Label>
|
||||||
|
<Combobox bind:value={parameters.key} options={keyOptions} />
|
||||||
|
{#if parameters.type === "set"}
|
||||||
|
<Label small>Value</Label>
|
||||||
|
<DrawerBindableInput
|
||||||
|
{bindings}
|
||||||
|
value={parameters.value}
|
||||||
|
on:change={e => (parameters.value = e.detail)}
|
||||||
|
/>
|
||||||
|
<div />
|
||||||
|
<Checkbox bind:value={parameters.persist} text="Persist this value" />
|
||||||
|
<div />
|
||||||
|
<Body size="XS">
|
||||||
|
Persisted values will remain even after reloading the page or closing the
|
||||||
|
browser.
|
||||||
|
</Body>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.root {
|
||||||
|
display: grid;
|
||||||
|
column-gap: var(--spacing-l);
|
||||||
|
row-gap: var(--spacing-s);
|
||||||
|
grid-template-columns: 60px 1fr;
|
||||||
|
align-items: center;
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -8,6 +8,7 @@ import LogOut from "./LogOut.svelte"
|
||||||
import ClearForm from "./ClearForm.svelte"
|
import ClearForm from "./ClearForm.svelte"
|
||||||
import CloseScreenModal from "./CloseScreenModal.svelte"
|
import CloseScreenModal from "./CloseScreenModal.svelte"
|
||||||
import ChangeFormStep from "./ChangeFormStep.svelte"
|
import ChangeFormStep from "./ChangeFormStep.svelte"
|
||||||
|
import UpdateStateStep from "./UpdateState.svelte"
|
||||||
|
|
||||||
// Defines which actions are available to configure in the front end.
|
// Defines which actions are available to configure in the front end.
|
||||||
// Unfortunately the "name" property is used as the identifier so please don't
|
// Unfortunately the "name" property is used as the identifier so please don't
|
||||||
|
@ -57,4 +58,8 @@ export default [
|
||||||
name: "Change Form Step",
|
name: "Change Form Step",
|
||||||
component: ChangeFormStep,
|
component: ChangeFormStep,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Update State",
|
||||||
|
component: UpdateStateStep,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/cli",
|
"name": "@budibase/cli",
|
||||||
"version": "0.9.121",
|
"version": "0.9.123-alpha.1",
|
||||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "0.9.121",
|
"version": "0.9.123-alpha.1",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"module": "dist/budibase-client.js",
|
"module": "dist/budibase-client.js",
|
||||||
"main": "dist/budibase-client.js",
|
"main": "dist/budibase-client.js",
|
||||||
|
@ -18,9 +18,9 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^0.9.121",
|
"@budibase/bbui": "^0.9.123-alpha.1",
|
||||||
"@budibase/standard-components": "^0.9.121",
|
"@budibase/standard-components": "^0.9.123-alpha.1",
|
||||||
"@budibase/string-templates": "^0.9.121",
|
"@budibase/string-templates": "^0.9.123-alpha.1",
|
||||||
"regexparam": "^1.3.0",
|
"regexparam": "^1.3.0",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
"svelte-spa-router": "^3.0.5"
|
"svelte-spa-router": "^3.0.5"
|
||||||
|
|
|
@ -112,16 +112,24 @@ export const enrichRows = async (rows, tableId) => {
|
||||||
if (!Array.isArray(rows)) {
|
if (!Array.isArray(rows)) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
if (rows.length && tableId) {
|
if (rows.length) {
|
||||||
// Fetch table schema so we can check column types
|
// map of tables, incase a row being loaded is not from the same table
|
||||||
const tableDefinition = await fetchTableDefinition(tableId)
|
const tables = {}
|
||||||
const schema = tableDefinition && tableDefinition.schema
|
for (let row of rows) {
|
||||||
if (schema) {
|
// fallback to passed in tableId if row doesn't have it specified
|
||||||
const keys = Object.keys(schema)
|
let rowTableId = row.tableId || tableId
|
||||||
rows.forEach(row => {
|
let table = tables[rowTableId]
|
||||||
|
if (!table) {
|
||||||
|
// Fetch table schema so we can check column types
|
||||||
|
table = await fetchTableDefinition(rowTableId)
|
||||||
|
tables[rowTableId] = table
|
||||||
|
}
|
||||||
|
const schema = table?.schema
|
||||||
|
if (schema) {
|
||||||
|
const keys = Object.keys(schema)
|
||||||
for (let key of keys) {
|
for (let key of keys) {
|
||||||
const type = schema[key].type
|
const type = schema[key].type
|
||||||
if (type === "link") {
|
if (type === "link" && Array.isArray(row[key])) {
|
||||||
// Enrich row a string join of relationship fields
|
// Enrich row a string join of relationship fields
|
||||||
row[`${key}_text`] =
|
row[`${key}_text`] =
|
||||||
row[key]
|
row[key]
|
||||||
|
@ -137,7 +145,7 @@ export const enrichRows = async (rows, tableId) => {
|
||||||
row[`${key}_first`] = url
|
row[`${key}_first`] = url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rows
|
return rows
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
import ErrorSVG from "../../../builder/assets/error.svg"
|
import ErrorSVG from "../../../builder/assets/error.svg"
|
||||||
import UserBindingsProvider from "./UserBindingsProvider.svelte"
|
import UserBindingsProvider from "./UserBindingsProvider.svelte"
|
||||||
import DeviceBindingsProvider from "./DeviceBindingsProvider.svelte"
|
import DeviceBindingsProvider from "./DeviceBindingsProvider.svelte"
|
||||||
|
import StateBindingsProvider from "./StateBindingsProvider.svelte"
|
||||||
|
|
||||||
// Provide contexts
|
// Provide contexts
|
||||||
setContext("sdk", SDK)
|
setContext("sdk", SDK)
|
||||||
|
@ -85,28 +86,30 @@
|
||||||
{:else if $screenStore.activeLayout}
|
{:else if $screenStore.activeLayout}
|
||||||
<UserBindingsProvider>
|
<UserBindingsProvider>
|
||||||
<DeviceBindingsProvider>
|
<DeviceBindingsProvider>
|
||||||
<div id="app-root" class:preview={$builderStore.inBuilder}>
|
<StateBindingsProvider>
|
||||||
{#key $screenStore.activeLayout._id}
|
<div id="app-root" class:preview={$builderStore.inBuilder}>
|
||||||
<Component instance={$screenStore.activeLayout.props} />
|
{#key $screenStore.activeLayout._id}
|
||||||
|
<Component instance={$screenStore.activeLayout.props} />
|
||||||
|
{/key}
|
||||||
|
</div>
|
||||||
|
<NotificationDisplay />
|
||||||
|
<ConfirmationDisplay />
|
||||||
|
<PeekScreenDisplay />
|
||||||
|
<!-- Key block needs to be outside the if statement or it breaks -->
|
||||||
|
{#key $builderStore.selectedComponentId}
|
||||||
|
{#if $builderStore.inBuilder}
|
||||||
|
<SettingsBar />
|
||||||
|
{/if}
|
||||||
{/key}
|
{/key}
|
||||||
</div>
|
<!--
|
||||||
<NotificationDisplay />
|
We don't want to key these by componentID as they control their own
|
||||||
<ConfirmationDisplay />
|
re-mounting to avoid flashes.
|
||||||
<PeekScreenDisplay />
|
-->
|
||||||
<!-- Key block needs to be outside the if statement or it breaks -->
|
|
||||||
{#key $builderStore.selectedComponentId}
|
|
||||||
{#if $builderStore.inBuilder}
|
{#if $builderStore.inBuilder}
|
||||||
<SettingsBar />
|
<SelectionIndicator />
|
||||||
|
<HoverIndicator />
|
||||||
{/if}
|
{/if}
|
||||||
{/key}
|
</StateBindingsProvider>
|
||||||
<!--
|
|
||||||
We don't want to key these by componentID as they control their own
|
|
||||||
re-mounting to avoid flashes.
|
|
||||||
-->
|
|
||||||
{#if $builderStore.inBuilder}
|
|
||||||
<SelectionIndicator />
|
|
||||||
<HoverIndicator />
|
|
||||||
{/if}
|
|
||||||
</DeviceBindingsProvider>
|
</DeviceBindingsProvider>
|
||||||
</UserBindingsProvider>
|
</UserBindingsProvider>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<script>
|
||||||
|
import Provider from "./Provider.svelte"
|
||||||
|
import { stateStore } from "../store"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Provider key="state" data={$stateStore}>
|
||||||
|
<slot />
|
||||||
|
</Provider>
|
|
@ -7,6 +7,7 @@ export { builderStore } from "./builder"
|
||||||
export { dataSourceStore } from "./dataSource"
|
export { dataSourceStore } from "./dataSource"
|
||||||
export { confirmationStore } from "./confirmation"
|
export { confirmationStore } from "./confirmation"
|
||||||
export { peekStore } from "./peek"
|
export { peekStore } from "./peek"
|
||||||
|
export { stateStore } from "./state"
|
||||||
|
|
||||||
// Context stores are layered and duplicated, so it is not a singleton
|
// Context stores are layered and duplicated, so it is not a singleton
|
||||||
export { createContextStore } from "./context"
|
export { createContextStore } from "./context"
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { writable, get, derived } from "svelte/store"
|
||||||
|
import { localStorageStore } from "../../../builder/src/builderStore/store/localStorage"
|
||||||
|
import { appStore } from "./app"
|
||||||
|
|
||||||
|
const createStateStore = () => {
|
||||||
|
const localStorageKey = `${get(appStore).appId}.state`
|
||||||
|
const persistentStore = localStorageStore(localStorageKey, {})
|
||||||
|
|
||||||
|
// Initialise the temp store to mirror the persistent store
|
||||||
|
const tempStore = writable(get(persistentStore))
|
||||||
|
|
||||||
|
// Sets a value to state, optionally persistent
|
||||||
|
const setValue = (key, value, persist = false) => {
|
||||||
|
const storeToSave = persist ? persistentStore : tempStore
|
||||||
|
const storeToClear = persist ? tempStore : persistentStore
|
||||||
|
storeToSave.update(state => {
|
||||||
|
state[key] = value
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
storeToClear.update(state => {
|
||||||
|
delete state[key]
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a certain key from both stores
|
||||||
|
const deleteValue = key => {
|
||||||
|
const stores = [tempStore, persistentStore]
|
||||||
|
stores.forEach(store => {
|
||||||
|
store.update(state => {
|
||||||
|
delete state[key]
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive the combination of both persisted and non persisted stores
|
||||||
|
const store = derived(
|
||||||
|
[tempStore, persistentStore],
|
||||||
|
([$tempStore, $persistentStore]) => {
|
||||||
|
return {
|
||||||
|
...$tempStore,
|
||||||
|
...$persistentStore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe: store.subscribe,
|
||||||
|
actions: { setValue, deleteValue },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const stateStore = createStateStore()
|
|
@ -5,6 +5,7 @@ import {
|
||||||
confirmationStore,
|
confirmationStore,
|
||||||
authStore,
|
authStore,
|
||||||
peekStore,
|
peekStore,
|
||||||
|
stateStore,
|
||||||
} from "../store"
|
} from "../store"
|
||||||
import { saveRow, deleteRow, executeQuery, triggerAutomation } from "../api"
|
import { saveRow, deleteRow, executeQuery, triggerAutomation } from "../api"
|
||||||
import { ActionTypes } from "../constants"
|
import { ActionTypes } from "../constants"
|
||||||
|
@ -122,6 +123,15 @@ const closeScreenModalHandler = () => {
|
||||||
window.dispatchEvent(new Event("close-screen-modal"))
|
window.dispatchEvent(new Event("close-screen-modal"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateStateHandler = action => {
|
||||||
|
const { type, key, value, persist } = action.parameters
|
||||||
|
if (type === "set") {
|
||||||
|
stateStore.actions.setValue(key, value, persist)
|
||||||
|
} else if (type === "delete") {
|
||||||
|
stateStore.actions.deleteValue(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handlerMap = {
|
const handlerMap = {
|
||||||
["Save Row"]: saveRowHandler,
|
["Save Row"]: saveRowHandler,
|
||||||
["Delete Row"]: deleteRowHandler,
|
["Delete Row"]: deleteRowHandler,
|
||||||
|
@ -134,6 +144,7 @@ const handlerMap = {
|
||||||
["Clear Form"]: clearFormHandler,
|
["Clear Form"]: clearFormHandler,
|
||||||
["Close Screen Modal"]: closeScreenModalHandler,
|
["Close Screen Modal"]: closeScreenModalHandler,
|
||||||
["Change Form Step"]: changeFormStepHandler,
|
["Change Form Step"]: changeFormStepHandler,
|
||||||
|
["Update State"]: updateStateHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmTextMap = {
|
const confirmTextMap = {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "0.9.121",
|
"version": "0.9.123-alpha.1",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -23,7 +23,6 @@
|
||||||
"format": "prettier --config ../../.prettierrc.json 'src/**/*.ts' --write",
|
"format": "prettier --config ../../.prettierrc.json 'src/**/*.ts' --write",
|
||||||
"lint": "eslint --fix src/",
|
"lint": "eslint --fix src/",
|
||||||
"lint:fix": "yarn run format && yarn run lint",
|
"lint:fix": "yarn run format && yarn run lint",
|
||||||
"initialise": "node scripts/initialise.js",
|
|
||||||
"multi:enable": "node scripts/multiTenancy.js enable",
|
"multi:enable": "node scripts/multiTenancy.js enable",
|
||||||
"multi:disable": "node scripts/multiTenancy.js disable"
|
"multi:disable": "node scripts/multiTenancy.js disable"
|
||||||
},
|
},
|
||||||
|
@ -62,9 +61,9 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/auth": "^0.9.121",
|
"@budibase/auth": "^0.9.123-alpha.1",
|
||||||
"@budibase/client": "^0.9.121",
|
"@budibase/client": "^0.9.123-alpha.1",
|
||||||
"@budibase/string-templates": "^0.9.121",
|
"@budibase/string-templates": "^0.9.123-alpha.1",
|
||||||
"@elastic/elasticsearch": "7.10.0",
|
"@elastic/elasticsearch": "7.10.0",
|
||||||
"@koa/router": "8.0.0",
|
"@koa/router": "8.0.0",
|
||||||
"@sendgrid/mail": "7.1.1",
|
"@sendgrid/mail": "7.1.1",
|
||||||
|
@ -117,7 +116,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.14.3",
|
"@babel/core": "^7.14.3",
|
||||||
"@babel/preset-env": "^7.14.4",
|
"@babel/preset-env": "^7.14.4",
|
||||||
"@budibase/standard-components": "^0.9.121",
|
"@budibase/standard-components": "^0.9.123-alpha.1",
|
||||||
"@jest/test-sequencer": "^24.8.0",
|
"@jest/test-sequencer": "^24.8.0",
|
||||||
"@types/bull": "^3.15.1",
|
"@types/bull": "^3.15.1",
|
||||||
"@types/jest": "^26.0.23",
|
"@types/jest": "^26.0.23",
|
||||||
|
|
|
@ -6,8 +6,16 @@ import {
|
||||||
SearchFilters,
|
SearchFilters,
|
||||||
SortJson,
|
SortJson,
|
||||||
} from "../../../definitions/datasource"
|
} from "../../../definitions/datasource"
|
||||||
import {Datasource, FieldSchema, Row, Table} from "../../../definitions/common"
|
import {
|
||||||
import {breakRowIdField, generateRowIdField} from "../../../integrations/utils"
|
Datasource,
|
||||||
|
FieldSchema,
|
||||||
|
Row,
|
||||||
|
Table,
|
||||||
|
} from "../../../definitions/common"
|
||||||
|
import {
|
||||||
|
breakRowIdField,
|
||||||
|
generateRowIdField,
|
||||||
|
} from "../../../integrations/utils"
|
||||||
import { RelationshipTypes } from "../../../constants"
|
import { RelationshipTypes } from "../../../constants"
|
||||||
|
|
||||||
interface ManyRelationship {
|
interface ManyRelationship {
|
||||||
|
@ -348,7 +356,7 @@ module External {
|
||||||
* information.
|
* information.
|
||||||
*/
|
*/
|
||||||
async lookupRelations(tableId: string, row: Row) {
|
async lookupRelations(tableId: string, row: Row) {
|
||||||
const related: {[key: string]: any} = {}
|
const related: { [key: string]: any } = {}
|
||||||
const { tableName } = breakExternalTableId(tableId)
|
const { tableName } = breakExternalTableId(tableId)
|
||||||
const table = this.tables[tableName]
|
const table = this.tables[tableName]
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -387,7 +395,11 @@ module External {
|
||||||
* isn't supposed to exist anymore and delete those. This is better than the usual method of delete them
|
* isn't supposed to exist anymore and delete those. This is better than the usual method of delete them
|
||||||
* all and then re-create, as theres no chance of losing data (e.g. delete succeed, but write fail).
|
* all and then re-create, as theres no chance of losing data (e.g. delete succeed, but write fail).
|
||||||
*/
|
*/
|
||||||
async handleManyRelationships(mainTableId: string, row: Row, relationships: ManyRelationship[]) {
|
async handleManyRelationships(
|
||||||
|
mainTableId: string,
|
||||||
|
row: Row,
|
||||||
|
relationships: ManyRelationship[]
|
||||||
|
) {
|
||||||
const { appId } = this
|
const { appId } = this
|
||||||
// if we're creating (in a through table) need to wipe the existing ones first
|
// if we're creating (in a through table) need to wipe the existing ones first
|
||||||
const promises = []
|
const promises = []
|
||||||
|
@ -399,8 +411,10 @@ module External {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const linkPrimary = linkTable.primary[0]
|
const linkPrimary = linkTable.primary[0]
|
||||||
const rows = related[key].rows || []
|
const rows = related[key].rows || []
|
||||||
const found = rows.find((row: { [key: string]: any }) =>
|
const found = rows.find(
|
||||||
row[linkPrimary] === relationship.id || row[linkPrimary] === body[linkPrimary]
|
(row: { [key: string]: any }) =>
|
||||||
|
row[linkPrimary] === relationship.id ||
|
||||||
|
row[linkPrimary] === body[linkPrimary]
|
||||||
)
|
)
|
||||||
const operation = isUpdate
|
const operation = isUpdate
|
||||||
? DataSourceOperation.UPDATE
|
? DataSourceOperation.UPDATE
|
||||||
|
@ -420,13 +434,17 @@ module External {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// finally cleanup anything that needs to be removed
|
// finally cleanup anything that needs to be removed
|
||||||
for (let [colName, {isMany, rows, tableId}] of Object.entries(related)) {
|
for (let [colName, { isMany, rows, tableId }] of Object.entries(
|
||||||
|
related
|
||||||
|
)) {
|
||||||
const table = this.getTable(tableId)
|
const table = this.getTable(tableId)
|
||||||
for (let row of rows) {
|
for (let row of rows) {
|
||||||
const filters = buildFilters(generateIdForRow(row, table), {}, table)
|
const filters = buildFilters(generateIdForRow(row, table), {}, table)
|
||||||
// safety check, if there are no filters on deletion bad things happen
|
// safety check, if there are no filters on deletion bad things happen
|
||||||
if (Object.keys(filters).length !== 0) {
|
if (Object.keys(filters).length !== 0) {
|
||||||
const op = isMany ? DataSourceOperation.DELETE : DataSourceOperation.UPDATE
|
const op = isMany
|
||||||
|
? DataSourceOperation.DELETE
|
||||||
|
: DataSourceOperation.UPDATE
|
||||||
const body = isMany ? null : { [colName]: null }
|
const body = isMany ? null : { [colName]: null }
|
||||||
promises.push(
|
promises.push(
|
||||||
makeExternalQuery(this.appId, {
|
makeExternalQuery(this.appId, {
|
||||||
|
@ -448,7 +466,10 @@ module External {
|
||||||
* Creating the specific list of fields that we desire, and excluding the ones that are no use to us
|
* Creating the specific list of fields that we desire, and excluding the ones that are no use to us
|
||||||
* is more performant and has the added benefit of protecting against this scenario.
|
* is more performant and has the added benefit of protecting against this scenario.
|
||||||
*/
|
*/
|
||||||
buildFields(table: Table, includeRelations: IncludeRelationships = IncludeRelationships.INCLUDE) {
|
buildFields(
|
||||||
|
table: Table,
|
||||||
|
includeRelations: IncludeRelationships = IncludeRelationships.INCLUDE
|
||||||
|
) {
|
||||||
function extractNonLinkFieldNames(table: Table, existing: string[] = []) {
|
function extractNonLinkFieldNames(table: Table, existing: string[] = []) {
|
||||||
return Object.entries(table.schema)
|
return Object.entries(table.schema)
|
||||||
.filter(
|
.filter(
|
||||||
|
@ -523,7 +544,10 @@ module External {
|
||||||
// can't really use response right now
|
// can't really use response right now
|
||||||
const response = await makeExternalQuery(appId, json)
|
const response = await makeExternalQuery(appId, json)
|
||||||
// handle many to many relationships now if we know the ID (could be auto increment)
|
// handle many to many relationships now if we know the ID (could be auto increment)
|
||||||
if (operation !== DataSourceOperation.READ && processed.manyRelationships) {
|
if (
|
||||||
|
operation !== DataSourceOperation.READ &&
|
||||||
|
processed.manyRelationships
|
||||||
|
) {
|
||||||
await this.handleManyRelationships(
|
await this.handleManyRelationships(
|
||||||
table._id || "",
|
table._id || "",
|
||||||
response[0],
|
response[0],
|
||||||
|
|
|
@ -10,27 +10,6 @@ const env = require("../environment")
|
||||||
|
|
||||||
const router = new Router()
|
const router = new Router()
|
||||||
|
|
||||||
const NO_TENANCY_ENDPOINTS = [
|
|
||||||
{
|
|
||||||
route: "/api/analytics",
|
|
||||||
method: "GET",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
route: "/builder",
|
|
||||||
method: "GET",
|
|
||||||
},
|
|
||||||
// when using this locally there can be pass through, need
|
|
||||||
// to allow all pass through endpoints to go without tenancy
|
|
||||||
{
|
|
||||||
route: "/api/global",
|
|
||||||
method: "ALL",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
route: "/api/system",
|
|
||||||
method: "ALL",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
router
|
router
|
||||||
.use(
|
.use(
|
||||||
compress({
|
compress({
|
||||||
|
@ -53,13 +32,21 @@ router
|
||||||
})
|
})
|
||||||
.use("/health", ctx => (ctx.status = 200))
|
.use("/health", ctx => (ctx.status = 200))
|
||||||
.use("/version", ctx => (ctx.body = pkg.version))
|
.use("/version", ctx => (ctx.body = pkg.version))
|
||||||
|
// re-direct before any middlewares occur
|
||||||
|
.redirect("/", "/builder")
|
||||||
.use(
|
.use(
|
||||||
buildAuthMiddleware(null, {
|
buildAuthMiddleware(null, {
|
||||||
publicAllowed: true,
|
publicAllowed: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
// nothing in the server should allow query string tenants
|
// nothing in the server should allow query string tenants
|
||||||
.use(buildTenancyMiddleware(null, NO_TENANCY_ENDPOINTS))
|
// the server can be public anywhere, so nowhere should throw errors
|
||||||
|
// if the tenancy has not been set, it'll have to be discovered at application layer
|
||||||
|
.use(
|
||||||
|
buildTenancyMiddleware(null, null, {
|
||||||
|
noTenancyRequired: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
.use(currentApp)
|
.use(currentApp)
|
||||||
.use(auditLog)
|
.use(auditLog)
|
||||||
|
|
||||||
|
@ -93,7 +80,4 @@ for (let route of mainRoutes) {
|
||||||
router.use(staticRoutes.routes())
|
router.use(staticRoutes.routes())
|
||||||
router.use(staticRoutes.allowedMethods())
|
router.use(staticRoutes.allowedMethods())
|
||||||
|
|
||||||
// add a redirect for when hitting server directly
|
|
||||||
router.redirect("/", "/builder")
|
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -203,19 +203,17 @@ exports.attachFullLinkedDocs = async (ctx, table, rows) => {
|
||||||
exports.squashLinksToPrimaryDisplay = async (appId, table, enriched) => {
|
exports.squashLinksToPrimaryDisplay = async (appId, table, enriched) => {
|
||||||
const db = new CouchDB(appId)
|
const db = new CouchDB(appId)
|
||||||
// will populate this as we find them
|
// will populate this as we find them
|
||||||
const linkedTables = []
|
const linkedTables = [table]
|
||||||
for (let [column, schema] of Object.entries(table.schema)) {
|
for (let row of enriched) {
|
||||||
if (schema.type !== FieldTypes.LINK) {
|
// this only fetches the table if its not already in array
|
||||||
continue
|
const rowTable = await getLinkedTable(db, row.tableId, linkedTables)
|
||||||
}
|
for (let [column, schema] of Object.entries(rowTable.schema)) {
|
||||||
for (let row of enriched) {
|
if (schema.type !== FieldTypes.LINK || !Array.isArray(row[column])) {
|
||||||
if (!row[column] || !row[column].length) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const newLinks = []
|
const newLinks = []
|
||||||
for (let link of row[column]) {
|
for (let link of row[column]) {
|
||||||
const linkTblId = link.tableId || getRelatedTableForField(table, column)
|
const linkTblId = link.tableId || getRelatedTableForField(table, column)
|
||||||
// this only fetches the table if its not already in array
|
|
||||||
const linkedTable = await getLinkedTable(db, linkTblId, linkedTables)
|
const linkedTable = await getLinkedTable(db, linkTblId, linkedTables)
|
||||||
const obj = { _id: link._id }
|
const obj = { _id: link._id }
|
||||||
if (link[linkedTable.primaryDisplay]) {
|
if (link[linkedTable.primaryDisplay]) {
|
||||||
|
|
|
@ -42,7 +42,7 @@ export enum SourceNames {
|
||||||
|
|
||||||
export enum IncludeRelationships {
|
export enum IncludeRelationships {
|
||||||
INCLUDE = 1,
|
INCLUDE = 1,
|
||||||
EXCLUDE = 0
|
EXCLUDE = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QueryDefinition {
|
export interface QueryDefinition {
|
||||||
|
|
|
@ -943,10 +943,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@budibase/auth@^0.9.121":
|
"@budibase/auth@^0.9.123-alpha.1":
|
||||||
version "0.9.121"
|
version "0.9.123-alpha.1"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.121.tgz#509ba49ca542fa741424b9a1310cf8e16dc59eeb"
|
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.123-alpha.1.tgz#48e011b2afbf0133ebcd564416d0f2399a107b82"
|
||||||
integrity sha512-SQbp6PsIg0C9IMFMBaFJ8UgJdantheOrCjphMCxpaq3rATl2MOsh9koyo5R/WMYYeBtdG7V+WKd4O5kBUtrfXQ==
|
integrity sha512-mI0rN0uwCCVjQiwfELjgIm2WpUHfDdzD84lSumRQ8HWAXywD94MdR/qe+B1r7YqjboXxC5SWydoiOivU7jhaTA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@techpass/passport-openidconnect" "^0.3.0"
|
"@techpass/passport-openidconnect" "^0.3.0"
|
||||||
aws-sdk "^2.901.0"
|
aws-sdk "^2.901.0"
|
||||||
|
@ -966,10 +966,10 @@
|
||||||
uuid "^8.3.2"
|
uuid "^8.3.2"
|
||||||
zlib "^1.0.5"
|
zlib "^1.0.5"
|
||||||
|
|
||||||
"@budibase/bbui@^0.9.121":
|
"@budibase/bbui@^0.9.123-alpha.1":
|
||||||
version "0.9.121"
|
version "0.9.123-alpha.1"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.121.tgz#d374f2856c8e335b0167440dbc2819671203be58"
|
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.123-alpha.1.tgz#704b693938efdb23eed9d1f089ad86a7f06f35d0"
|
||||||
integrity sha512-60zNUJ/Nil8yfWdKgjAo4Xxk3mbxipCpZBIgGl/RuE0r6Am0oylRx8pRMmwHaQ92cofbXwApWyC/9VLQQGSJzw==
|
integrity sha512-QGeR3f/xsalkRC3/HRUdG7VaNakYK9MCL3819M5bQxMjyhVoFdZw18mnc7zNfcCL3kTyw0C6capJULho4ic+AA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
|
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
|
||||||
"@spectrum-css/actionbutton" "^1.0.1"
|
"@spectrum-css/actionbutton" "^1.0.1"
|
||||||
|
@ -1015,14 +1015,14 @@
|
||||||
svelte-flatpickr "^3.1.0"
|
svelte-flatpickr "^3.1.0"
|
||||||
svelte-portal "^1.0.0"
|
svelte-portal "^1.0.0"
|
||||||
|
|
||||||
"@budibase/client@^0.9.121":
|
"@budibase/client@^0.9.123-alpha.1":
|
||||||
version "0.9.121"
|
version "0.9.123-alpha.1"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.121.tgz#fe00d672eea51bd2c8e60c283332a072168971d1"
|
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.123-alpha.1.tgz#a70199937c546544a2750e4273c8347e2fddab53"
|
||||||
integrity sha512-1IMjIy2IuL5Y3GuIBgNe7ASGloA+As/CUBLtHQMe7oM8Q3FbRBOm7SKyV6FZGYXiG7hggq0fKJqm5YcRO9Lqgw==
|
integrity sha512-s/diiHoN/1kd2JavHy+otYDryfH3ijdjna/jqsZYPK/6Y9ZGcHwtVL43+VVtz9YlBw5aD4Av+YW2t1sqNHCsJw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/bbui" "^0.9.121"
|
"@budibase/bbui" "^0.9.123-alpha.1"
|
||||||
"@budibase/standard-components" "^0.9.121"
|
"@budibase/standard-components" "^0.9.123-alpha.1"
|
||||||
"@budibase/string-templates" "^0.9.121"
|
"@budibase/string-templates" "^0.9.123-alpha.1"
|
||||||
regexparam "^1.3.0"
|
regexparam "^1.3.0"
|
||||||
shortid "^2.2.15"
|
shortid "^2.2.15"
|
||||||
svelte-spa-router "^3.0.5"
|
svelte-spa-router "^3.0.5"
|
||||||
|
@ -1055,12 +1055,12 @@
|
||||||
to-gfm-code-block "^0.1.1"
|
to-gfm-code-block "^0.1.1"
|
||||||
year "^0.2.1"
|
year "^0.2.1"
|
||||||
|
|
||||||
"@budibase/standard-components@^0.9.121":
|
"@budibase/standard-components@^0.9.123-alpha.1":
|
||||||
version "0.9.121"
|
version "0.9.123-alpha.1"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/standard-components/-/standard-components-0.9.121.tgz#7950a83b1111310b700790a933f68ba9bbababb6"
|
resolved "https://registry.yarnpkg.com/@budibase/standard-components/-/standard-components-0.9.123-alpha.1.tgz#944e4906e88ce9f8be6a26dc46d50afa17ef3a58"
|
||||||
integrity sha512-VPbJCisPprPUEVrkFn/yz+ZOTpAJQN+8PRo7gSWtuXLPlLXKdPZ+B8ICDQNA8i3QBswfFACoIZQ7QwEJ+kBJVg==
|
integrity sha512-tCaiOhmmLAOBh+A8rFXnPHKZaSYRK7xlrI23r1IRgvDTkmuflr0o/VrRBVQuaxwWClKQPGkQ3wgnOJgXvk6CgQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/bbui" "^0.9.121"
|
"@budibase/bbui" "^0.9.123-alpha.1"
|
||||||
"@spectrum-css/button" "^3.0.3"
|
"@spectrum-css/button" "^3.0.3"
|
||||||
"@spectrum-css/card" "^3.0.3"
|
"@spectrum-css/card" "^3.0.3"
|
||||||
"@spectrum-css/divider" "^1.0.3"
|
"@spectrum-css/divider" "^1.0.3"
|
||||||
|
@ -1073,10 +1073,10 @@
|
||||||
svelte-apexcharts "^1.0.2"
|
svelte-apexcharts "^1.0.2"
|
||||||
svelte-flatpickr "^3.1.0"
|
svelte-flatpickr "^3.1.0"
|
||||||
|
|
||||||
"@budibase/string-templates@^0.9.121":
|
"@budibase/string-templates@^0.9.123-alpha.1":
|
||||||
version "0.9.121"
|
version "0.9.123-alpha.1"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.121.tgz#bc16761e9571cd44748b414bda24a3830a867672"
|
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.123-alpha.1.tgz#6b2e718478d98aa8ba046ad23e7190894360906a"
|
||||||
integrity sha512-7j+o9D/qwP1lbclptu5tRLj5AzNRB6RyqAB13DkvfqCDxYkxK5qO3+Gdnm7eVZRguESwCcVEJHe+a/qVBUWhog==
|
integrity sha512-Id5AUdzTiYKGo/TTU3cuy1m4hS1Wy66hMIxVCM6GKoEnDinEB/rEA6IwP3IDFyTs2Uld8uWLBnEru93sP982AQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/handlebars-helpers" "^0.11.4"
|
"@budibase/handlebars-helpers" "^0.11.4"
|
||||||
dayjs "^1.10.4"
|
dayjs "^1.10.4"
|
||||||
|
@ -11353,9 +11353,9 @@ typescript@^4.3.5:
|
||||||
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
|
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
|
||||||
|
|
||||||
uglify-js@^3.1.4:
|
uglify-js@^3.1.4:
|
||||||
version "3.14.1"
|
version "3.14.2"
|
||||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.1.tgz#e2cb9fe34db9cb4cf7e35d1d26dfea28e09a7d06"
|
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.2.tgz#d7dd6a46ca57214f54a2d0a43cad0f35db82ac99"
|
||||||
integrity sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g==
|
integrity sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==
|
||||||
|
|
||||||
uid2@0.0.x:
|
uid2@0.0.x:
|
||||||
version "0.0.4"
|
version "0.0.4"
|
||||||
|
|
|
@ -29,11 +29,11 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"svelte"
|
"svelte"
|
||||||
],
|
],
|
||||||
"version": "0.9.121",
|
"version": "0.9.123-alpha.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
|
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^0.9.121",
|
"@budibase/bbui": "^0.9.123-alpha.1",
|
||||||
"@spectrum-css/button": "^3.0.3",
|
"@spectrum-css/button": "^3.0.3",
|
||||||
"@spectrum-css/card": "^3.0.3",
|
"@spectrum-css/card": "^3.0.3",
|
||||||
"@spectrum-css/divider": "^1.0.3",
|
"@spectrum-css/divider": "^1.0.3",
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
const labelPosition = fieldGroupContext?.labelPosition || "above"
|
const labelPosition = fieldGroupContext?.labelPosition || "above"
|
||||||
const formField = formApi?.registerField(
|
const formField = formApi?.registerField(
|
||||||
field,
|
field,
|
||||||
|
type,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
disabled,
|
disabled,
|
||||||
validation,
|
validation,
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
// Reactive derived stores to derive form state from field array
|
// Reactive derived stores to derive form state from field array
|
||||||
$: values = deriveFieldProperty(fields, f => f.fieldState.value)
|
$: values = deriveFieldProperty(fields, f => f.fieldState.value)
|
||||||
$: errors = deriveFieldProperty(fields, f => f.fieldState.error)
|
$: errors = deriveFieldProperty(fields, f => f.fieldState.error)
|
||||||
|
$: enrichments = deriveBindingEnrichments(fields)
|
||||||
$: valid = !Object.values($errors).some(error => error != null)
|
$: valid = !Object.values($errors).some(error => error != null)
|
||||||
|
|
||||||
// Derive whether the current form step is valid
|
// Derive whether the current form step is valid
|
||||||
|
@ -57,6 +58,26 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Derives any enrichments which need to be made so that bindings work for
|
||||||
|
// special data types like attachments. Relationships are currently not
|
||||||
|
// handled as we don't have the primaryDisplay field that is required.
|
||||||
|
const deriveBindingEnrichments = fieldStores => {
|
||||||
|
return derived(fieldStores, fieldValues => {
|
||||||
|
let enrichments = {}
|
||||||
|
fieldValues.forEach(field => {
|
||||||
|
if (field.type === "attachment") {
|
||||||
|
const value = field.fieldState.value
|
||||||
|
let url = null
|
||||||
|
if (Array.isArray(value) && value[0] != null) {
|
||||||
|
url = value[0].url
|
||||||
|
}
|
||||||
|
enrichments[`${field.name}_first`] = url
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return enrichments
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Searches the field array for a certain field
|
// Searches the field array for a certain field
|
||||||
const getField = name => {
|
const getField = name => {
|
||||||
return fields.find(field => get(field).name === name)
|
return fields.find(field => get(field).name === name)
|
||||||
|
@ -65,6 +86,7 @@
|
||||||
const formApi = {
|
const formApi = {
|
||||||
registerField: (
|
registerField: (
|
||||||
field,
|
field,
|
||||||
|
type,
|
||||||
defaultValue = null,
|
defaultValue = null,
|
||||||
fieldDisabled = false,
|
fieldDisabled = false,
|
||||||
validationRules,
|
validationRules,
|
||||||
|
@ -100,6 +122,7 @@
|
||||||
// Construct field info
|
// Construct field info
|
||||||
const fieldInfo = writable({
|
const fieldInfo = writable({
|
||||||
name: field,
|
name: field,
|
||||||
|
type,
|
||||||
step: step || 1,
|
step: step || 1,
|
||||||
fieldState: {
|
fieldState: {
|
||||||
fieldId: `id-${generateID()}`,
|
fieldId: `id-${generateID()}`,
|
||||||
|
@ -262,6 +285,7 @@
|
||||||
$: dataContext = {
|
$: dataContext = {
|
||||||
...initialValues,
|
...initialValues,
|
||||||
...$values,
|
...$values,
|
||||||
|
...$enrichments,
|
||||||
|
|
||||||
// These static values are prefixed to avoid clashes with actual columns
|
// These static values are prefixed to avoid clashes with actual columns
|
||||||
__valid: valid,
|
__valid: valid,
|
||||||
|
|
|
@ -105,12 +105,12 @@ export const luceneQuery = (docs, query) => {
|
||||||
|
|
||||||
// Process an equal match (fails if the value is different)
|
// Process an equal match (fails if the value is different)
|
||||||
const equalMatch = match("equal", (key, value, doc) => {
|
const equalMatch = match("equal", (key, value, doc) => {
|
||||||
return doc[key] !== value
|
return value != null && value !== "" && doc[key] !== value
|
||||||
})
|
})
|
||||||
|
|
||||||
// Process a not-equal match (fails if the value is the same)
|
// Process a not-equal match (fails if the value is the same)
|
||||||
const notEqualMatch = match("notEqual", (key, value, doc) => {
|
const notEqualMatch = match("notEqual", (key, value, doc) => {
|
||||||
return doc[key] === value
|
return value != null && value !== "" && doc[key] === value
|
||||||
})
|
})
|
||||||
|
|
||||||
// Process an empty match (fails if the value is not empty)
|
// Process an empty match (fails if the value is not empty)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/string-templates",
|
"name": "@budibase/string-templates",
|
||||||
"version": "0.9.121",
|
"version": "0.9.123-alpha.1",
|
||||||
"description": "Handlebars wrapper for Budibase templating.",
|
"description": "Handlebars wrapper for Budibase templating.",
|
||||||
"main": "src/index.cjs",
|
"main": "src/index.cjs",
|
||||||
"module": "dist/bundle.mjs",
|
"module": "dist/bundle.mjs",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/worker",
|
"name": "@budibase/worker",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "0.9.121",
|
"version": "0.9.123-alpha.1",
|
||||||
"description": "Budibase background service",
|
"description": "Budibase background service",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -23,8 +23,8 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/auth": "^0.9.121",
|
"@budibase/auth": "^0.9.123-alpha.1",
|
||||||
"@budibase/string-templates": "^0.9.121",
|
"@budibase/string-templates": "^0.9.123-alpha.1",
|
||||||
"@koa/router": "^8.0.0",
|
"@koa/router": "^8.0.0",
|
||||||
"@techpass/passport-openidconnect": "^0.3.0",
|
"@techpass/passport-openidconnect": "^0.3.0",
|
||||||
"aws-sdk": "^2.811.0",
|
"aws-sdk": "^2.811.0",
|
||||||
|
|
|
@ -35,6 +35,7 @@ const PUBLIC_ENDPOINTS = [
|
||||||
method: "GET",
|
method: "GET",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// TODO: Add an provisioning API key to this endpoint in the cloud
|
||||||
route: "/api/global/users/init",
|
route: "/api/global/users/init",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
},
|
},
|
||||||
|
@ -46,6 +47,10 @@ const PUBLIC_ENDPOINTS = [
|
||||||
route: "api/system/flags",
|
route: "api/system/flags",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
route: "/api/global/users/tenant/:id",
|
||||||
|
method: "GET",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const NO_TENANCY_ENDPOINTS = [
|
const NO_TENANCY_ENDPOINTS = [
|
||||||
|
|
|
@ -94,7 +94,7 @@ router
|
||||||
controller.adminUser
|
controller.adminUser
|
||||||
)
|
)
|
||||||
.get("/api/global/users/self", controller.getSelf)
|
.get("/api/global/users/self", controller.getSelf)
|
||||||
.get("/api/global/users/tenant/:id", adminOnly, controller.tenantLookup)
|
.get("/api/global/users/tenant/:id", controller.tenantLookup)
|
||||||
// global endpoint but needs to come at end (blocks other endpoints otherwise)
|
// global endpoint but needs to come at end (blocks other endpoints otherwise)
|
||||||
.get("/api/global/users/:id", adminOnly, controller.find)
|
.get("/api/global/users/:id", adminOnly, controller.find)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue