diff --git a/lerna.json b/lerna.json index 146b4bee75..105efb7cd5 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.25", + "version": "0.9.27", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 75be8583be..d2704c8618 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.25", + "version": "0.9.27", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index cd6fb44f1b..834e9dcd60 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.25", + "version": "0.9.27", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/bbui/src/Form/Core/RadioGroup.svelte b/packages/bbui/src/Form/Core/RadioGroup.svelte index 4ead9ed311..d7941b2518 100644 --- a/packages/bbui/src/Form/Core/RadioGroup.svelte +++ b/packages/bbui/src/Form/Core/RadioGroup.svelte @@ -37,3 +37,9 @@ {/each} {/if} + + diff --git a/packages/builder/package.json b/packages/builder/package.json index a713ff3516..96a5ac7bf3 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.25", + "version": "0.9.27", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.25", - "@budibase/client": "^0.9.25", + "@budibase/bbui": "^0.9.27", + "@budibase/client": "^0.9.27", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.25", + "@budibase/string-templates": "^0.9.27", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index 89532e3bc3..5ce8e407c1 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -136,7 +136,7 @@ const getContextBindings = (asset, componentId) => { if (!datasource) { return } - const info = getSchemaForDatasource(datasource) + const info = getSchemaForDatasource(asset, datasource) schema = info.schema readablePrefix = info.table?.name } @@ -191,7 +191,7 @@ const getContextBindings = (asset, componentId) => { */ const getUserBindings = () => { let bindings = [] - const { schema } = getSchemaForDatasource({ + const { schema } = getSchemaForDatasource(null, { type: "table", tableId: TableNames.USERS, }) @@ -244,11 +244,15 @@ const getUrlBindings = asset => { /** * Gets a schema for a datasource object. */ -export const getSchemaForDatasource = (datasource, isForm = false) => { +export const getSchemaForDatasource = (asset, datasource, isForm = false) => { let schema, table if (datasource) { const { type } = datasource - if (type === "query") { + if (type === "provider") { + const component = findComponent(asset.props, datasource.providerId) + const source = getDatasourceForProvider(asset, component) + return getSchemaForDatasource(asset, source, isForm) + } else if (type === "query") { const queries = get(queriesStores).list table = queries.find(query => query._id === datasource._id) } else { diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js index 02b3c20a2f..e234a1a770 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js +++ b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js @@ -174,7 +174,7 @@ const fieldTypeToComponentMap = { } export function makeDatasourceFormComponents(datasource) { - const { schema } = getSchemaForDatasource(datasource, true) + const { schema } = getSchemaForDatasource(null, datasource, true) let components = [] let fields = Object.keys(schema || {}) fields.forEach(field => { diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 00e53a8c64..15657e1e86 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -43,6 +43,7 @@ } let originalName = field.name + const linkEditDisabled = originalName != null let primaryDisplay = $tables.selected.primaryDisplay == null || $tables.selected.primaryDisplay === field.name @@ -197,7 +198,7 @@ + {:else if field.type === FORMULA_TYPE} - import { getBindableProperties } from "builderStore/dataBinding" + import { + getBindableProperties, + getDataProviderComponents, + } from "builderStore/dataBinding" import { Button, Popover, @@ -61,6 +64,17 @@ $currentAsset, $store.selectedComponentId ) + $: dataProviders = getDataProviderComponents( + $currentAsset, + $store.selectedComponentId + ).map(provider => ({ + label: provider._instanceName, + name: provider._instanceName, + providerId: provider._id, + value: `{{ literal [${provider._id}] }}`, + type: "provider", + schema: provider.schema, + })) $: queryBindableProperties = bindableProperties.map(property => ({ ...property, category: property.type === "instance" ? "Component" : "Table", @@ -182,7 +196,20 @@ {/each} - + +
+ Data Providers +
+ {#if otherSources?.length}
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte index 70e44ea83a..664129ee02 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte @@ -14,11 +14,11 @@ $currentAsset, $store.selectedComponentId ) - $: schemaFields = getSchemaFields(parameters?.tableId) + $: schemaFields = getSchemaFields($currentAsset, parameters?.tableId) $: tableOptions = $tables.list || [] - const getSchemaFields = tableId => { - const { schema } = getSchemaForDatasource({ type: "table", tableId }) + const getSchemaFields = (asset, tableId) => { + const { schema } = getSchemaForDatasource(asset, { type: "table", tableId }) return Object.values(schema || {}) } diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js index e851bdb4be..a3e43cb045 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js @@ -5,11 +5,13 @@ import ExecuteQuery from "./ExecuteQuery.svelte" import TriggerAutomation from "./TriggerAutomation.svelte" import ValidateForm from "./ValidateForm.svelte" -// defines what actions are available, when adding a new one -// the component is the setup panel for the action -// NOTE that the "name" is used by the client library, -// so if you want to change it, you must change it client lib too - +// Defines which actions are available to configure in the front end. +// Unfortunately the "name" property is used as the identifier so please don't +// change them. +// The client library removes any spaces when processing actions, so they can +// be considered as camel case too. +// There is technical debt here to sanitize all these and standardise them +// across the packages but it's a breaking change to existing apps. export default [ { name: "Save Row", diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FieldSelect.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FieldSelect.svelte index dd5436c1f7..9b6aec4584 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FieldSelect.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FieldSelect.svelte @@ -14,7 +14,7 @@ const dispatch = createEventDispatcher() $: datasource = getDatasourceForProvider($currentAsset, componentInstance) - $: schema = getSchemaForDatasource(datasource).schema + $: schema = getSchemaForDatasource($currentAsset, datasource).schema $: options = Object.keys(schema || {}) $: boundValue = getValidValue(value, options) diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte index 1662337409..20a7b9d9ff 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte @@ -27,19 +27,16 @@ ? tempValue.length : Object.keys(tempValue || {}).length $: dataSource = getDatasourceForProvider($currentAsset, componentInstance) - $: schema = getSchemaForDatasource(dataSource)?.schema + $: schema = getSchemaForDatasource($currentAsset, dataSource)?.schema $: schemaFields = Object.values(schema || {}) $: internalTable = dataSource?.type === "table" // Reset value if value is wrong type for the datasource. // Lucene editor needs an array, and simple editor needs an object. $: { - if (internalTable && !Array.isArray(value)) { + if (!Array.isArray(value)) { tempValue = [] dispatch("change", []) - } else if (!internalTable && Array.isArray(value)) { - tempValue = {} - dispatch("change", {}) } } @@ -63,28 +60,7 @@ constaints. {/if} - {#if internalTable} - - {:else} -
- (tempValue = e.detail)} - /> -
- {/if} + - - diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FormFieldSelect.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FormFieldSelect.svelte index 1d7f5367df..1824bf7f58 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FormFieldSelect.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FormFieldSelect.svelte @@ -18,7 +18,7 @@ component => component._component === "@budibase/standard-components/form" ) $: datasource = getDatasourceForProvider($currentAsset, form) - $: schema = getSchemaForDatasource(datasource, true).schema + $: schema = getSchemaForDatasource($currentAsset, datasource, true).schema $: options = getOptions(schema, type) const getOptions = (schema, fieldType) => { diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/MultiFieldSelect.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/MultiFieldSelect.svelte index 44cea7a627..e3f38d88d8 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/MultiFieldSelect.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/MultiFieldSelect.svelte @@ -14,7 +14,7 @@ const dispatch = createEventDispatcher() $: datasource = getDatasourceForProvider($currentAsset, componentInstance) - $: schema = getSchemaForDatasource(datasource).schema + $: schema = getSchemaForDatasource($currentAsset, datasource).schema $: options = Object.keys(schema || {}) $: boundValue = getValidOptions(value, options) diff --git a/packages/cli/package.json b/packages/cli/package.json index 584416a9e7..519db352f3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.25", + "version": "0.9.27", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 7452014c4e..ee022f6558 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.25", + "version": "0.9.27", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -18,13 +18,13 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/string-templates": "^0.9.25", + "@budibase/string-templates": "^0.9.27", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" }, "devDependencies": { - "@budibase/standard-components": "^0.9.25", + "@budibase/standard-components": "^0.9.27", "@rollup/plugin-commonjs": "^18.0.0", "@rollup/plugin-node-resolve": "^11.2.1", "fs-extra": "^8.1.0", diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index b32222ce2f..4d2dddffcc 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -55,13 +55,13 @@ } // Enriches any string component props using handlebars - const updateComponentProps = async (definition, context) => { + const updateComponentProps = (definition, context) => { // Record the timestamp so we can reference it after enrichment latestUpdateTime = Date.now() const enrichmentTime = latestUpdateTime // Enrich props with context - const enrichedProps = await enrichProps(definition, context) + const enrichedProps = enrichProps(definition, context) // Abandon this update if a newer update has started if (enrichmentTime !== latestUpdateTime) { diff --git a/packages/client/src/constants.js b/packages/client/src/constants.js index 3aa302bec9..31ac4b285e 100644 --- a/packages/client/src/constants.js +++ b/packages/client/src/constants.js @@ -5,4 +5,5 @@ export const TableNames = { export const ActionTypes = { ValidateForm: "ValidateForm", RefreshDatasource: "RefreshDatasource", + SetDataProviderQuery: "SetDataProviderQuery", } diff --git a/packages/client/src/sdk.js b/packages/client/src/sdk.js index 7eef69441d..bf467e141f 100644 --- a/packages/client/src/sdk.js +++ b/packages/client/src/sdk.js @@ -9,6 +9,7 @@ import { import { styleable } from "./utils/styleable" import transition from "./utils/transition" import { linkable } from "./utils/linkable" +import { getAction } from "./utils/getAction" import Provider from "./components/Provider.svelte" import { ActionTypes } from "./constants" @@ -22,6 +23,7 @@ export default { styleable, transition, linkable, + getAction, Provider, ActionTypes, } diff --git a/packages/client/src/utils/componentProps.js b/packages/client/src/utils/componentProps.js index 559fc54486..14516fdb4c 100644 --- a/packages/client/src/utils/componentProps.js +++ b/packages/client/src/utils/componentProps.js @@ -21,7 +21,7 @@ export const propsAreSame = (a, b) => { * Enriches component props. * Data bindings are enriched, and button actions are enriched. */ -export const enrichProps = async (props, context) => { +export const enrichProps = (props, context) => { // Exclude all private props that start with an underscore let validProps = {} Object.entries(props) @@ -41,7 +41,7 @@ export const enrichProps = async (props, context) => { } // Enrich all data bindings in top level props - let enrichedProps = await enrichDataBindings(validProps, totalContext) + let enrichedProps = enrichDataBindings(validProps, totalContext) // Enrich click actions if they exist if (enrichedProps.onClick) { diff --git a/packages/client/src/utils/enrichDataBinding.js b/packages/client/src/utils/enrichDataBinding.js index b8c5020c74..34bfb78539 100644 --- a/packages/client/src/utils/enrichDataBinding.js +++ b/packages/client/src/utils/enrichDataBinding.js @@ -1,5 +1,5 @@ import { cloneDeep } from "lodash/fp" -import { processString, processObject } from "@budibase/string-templates" +import { processString, processObjectSync } from "@budibase/string-templates" // Regex to test inputs with to see if they are likely candidates for template strings const looksLikeTemplate = /{{.*}}/ @@ -23,6 +23,6 @@ export const enrichDataBinding = async (input, context) => { * Recursively enriches all props in a props object and returns the new props. * Props are deeply cloned so that no mutation is done to the source object. */ -export const enrichDataBindings = async (props, context) => { - return await processObject(cloneDeep(props), context) +export const enrichDataBindings = (props, context) => { + return processObjectSync(cloneDeep(props), context) } diff --git a/packages/client/src/utils/getAction.js b/packages/client/src/utils/getAction.js new file mode 100644 index 0000000000..cd97fcf079 --- /dev/null +++ b/packages/client/src/utils/getAction.js @@ -0,0 +1,19 @@ +import { get } from "svelte/store" +import { getContext } from "svelte" + +/** + * Gets a component action. + * @param id The component ID that provides the action + * @param type The action type to get + * @returns {null|*} The action function + */ +export const getAction = (id, type) => { + if (!id || !type) { + return null + } + const context = getContext("context") + if (!context) { + return null + } + return get(context)?.[`${id}_${type}`] +} diff --git a/packages/server/package.json b/packages/server/package.json index 18fe05f5c8..b5f885a881 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.25", + "version": "0.9.27", "description": "Budibase Web Server", "main": "src/electron.js", "repository": { @@ -55,9 +55,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.25", - "@budibase/client": "^0.9.25", - "@budibase/string-templates": "^0.9.25", + "@budibase/auth": "^0.9.27", + "@budibase/client": "^0.9.27", + "@budibase/string-templates": "^0.9.27", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", @@ -110,7 +110,7 @@ "devDependencies": { "@babel/core": "^7.14.3", "@babel/preset-env": "^7.14.4", - "@budibase/standard-components": "^0.9.25", + "@budibase/standard-components": "^0.9.27", "@jest/test-sequencer": "^24.8.0", "babel-jest": "^27.0.2", "docker-compose": "^0.23.6", diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index 70b63c977a..fc09077edc 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -99,12 +99,18 @@ async function createInstance(template) { // replicate the template data to the instance DB // this is currently very hard to test, downloading and importing template files /* istanbul ignore next */ + let _rev if (template && template.useTemplate === "true") { const { ok } = await db.load(await getTemplateStream(template)) if (!ok) { throw "Error loading database dump from template." } - var { _rev } = await db.get(DocumentTypes.APP_METADATA) + try { + const response = await db.get(DocumentTypes.APP_METADATA) + _rev = response._rev + } catch (err) { + _rev = null + } } else { // create the users table await db.put(USERS_TABLE_SCHEMA) diff --git a/packages/server/src/utilities/rowProcessor.js b/packages/server/src/utilities/rowProcessor.js index 2267c9e986..807563d47e 100644 --- a/packages/server/src/utilities/rowProcessor.js +++ b/packages/server/src/utilities/rowProcessor.js @@ -169,9 +169,14 @@ exports.inputProcessing = (user = {}, table, row) => { let clonedRow = cloneDeep(row) // need to copy the table so it can be differenced on way out const copiedTable = cloneDeep(table) + const dontCleanseKeys = ["type", "_id", "_rev", "tableId"] for (let [key, value] of Object.entries(clonedRow)) { const field = table.schema[key] + // cleanse fields that aren't in the schema if (!field) { + if (dontCleanseKeys.indexOf(key) === -1) { + delete clonedRow[key] + } continue } clonedRow[key] = exports.coerce(value, field.type) diff --git a/packages/standard-components/manifest.json b/packages/standard-components/manifest.json index 9f67af6489..8f0f63f681 100644 --- a/packages/standard-components/manifest.json +++ b/packages/standard-components/manifest.json @@ -1505,5 +1505,38 @@ "context": { "type": "schema" } + }, + "daterangepicker": { + "name": "Date Range", + "icon": "Date", + "styleable": true, + "hasChildren": false, + "info": "Your data provider will be automatically filtered to the given date range.", + "settings": [ + { + "type": "dataProvider", + "label": "Provider", + "key": "dataProvider" + }, + { + "type": "field", + "label": "Date field", + "key": "field" + }, + { + "type": "select", + "label": "Default range", + "key": "defaultValue", + "options": [ + "Last 1 day", + "Last 7 days", + "Last 30 days", + "Last 3 months", + "Last 6 months", + "Last 1 year" + ], + "defaultValue": "Last 30 days" + } + ] } } diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index 143b91402c..7b9b683edf 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -29,14 +29,15 @@ "keywords": [ "svelte" ], - "version": "0.9.25", + "version": "0.9.27", "license": "MIT", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "dependencies": { - "@budibase/bbui": "^0.9.25", + "@budibase/bbui": "^0.9.27", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", "apexcharts": "^3.22.1", + "dayjs": "^1.10.5", "svelte-apexcharts": "^1.0.2", "svelte-flatpickr": "^3.1.0" } diff --git a/packages/standard-components/src/DataProvider.svelte b/packages/standard-components/src/DataProvider.svelte index e0b2ad859a..7b759b5348 100644 --- a/packages/standard-components/src/DataProvider.svelte +++ b/packages/standard-components/src/DataProvider.svelte @@ -1,6 +1,12 @@ + +
+