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/auth/src/security/roles.js b/packages/auth/src/security/roles.js index d652c25b00..53e1b90d73 100644 --- a/packages/auth/src/security/roles.js +++ b/packages/auth/src/security/roles.js @@ -13,7 +13,6 @@ const BUILTIN_IDS = { POWER: "POWER", BASIC: "BASIC", PUBLIC: "PUBLIC", - BUILDER: "BUILDER", } // exclude internal roles like builder 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/builder/src/stores/backend/permissions.js b/packages/builder/src/stores/backend/permissions.js index 9172210872..78a4c49753 100644 --- a/packages/builder/src/stores/backend/permissions.js +++ b/packages/builder/src/stores/backend/permissions.js @@ -6,6 +6,13 @@ export function createPermissionStore() { return { subscribe, + save: async ({ level, role, resource }) => { + const response = await api.post( + `/api/permission/${role}/${resource}/${level}` + ) + const json = await response.json() + return json + }, forResource: async resourceId => { const response = await api.get(`/api/permission/${resourceId}`) const json = await response.json() 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 92c6cac5e9..4d1691d37e 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", @@ -109,7 +109,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/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js index 92d731cfbb..da863f5493 100644 --- a/packages/server/src/api/controllers/auth.js +++ b/packages/server/src/api/controllers/auth.js @@ -5,7 +5,7 @@ const { getFullUser } = require("../../utilities/users") exports.fetchSelf = async ctx => { const appId = ctx.appId - const { userId } = ctx.user + let userId = ctx.user.userId || ctx.user._id /* istanbul ignore next */ if (!userId) { ctx.body = {} diff --git a/packages/server/src/api/controllers/routing.js b/packages/server/src/api/controllers/routing.js index 1bbb521eab..d281b92fe2 100644 --- a/packages/server/src/api/controllers/routing.js +++ b/packages/server/src/api/controllers/routing.js @@ -63,10 +63,6 @@ exports.fetch = async ctx => { exports.clientFetch = async ctx => { const routing = await getRoutingStructure(ctx.appId) let roleId = ctx.user.role._id - // builder is a special case, always return the full routing structure - if (roleId === BUILTIN_ROLE_IDS.BUILDER) { - roleId = BUILTIN_ROLE_IDS.ADMIN - } const roleIds = await getUserRoleHierarchy(ctx.appId, roleId) for (let topLevel of Object.values(routing.routes)) { for (let subpathKey of Object.keys(topLevel.subpaths)) { diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index 73ba56943a..fc207c479f 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -4,7 +4,6 @@ const { getUserMetadataParams, } = require("../../db/utils") const { InternalTables } = require("../../db/utils") -const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") const { getGlobalUsers, addAppRoleToUser, @@ -47,10 +46,6 @@ exports.fetchMetadata = async function (ctx) { exports.updateSelfMetadata = async function (ctx) { // overwrite the ID with current users ctx.request.body._id = ctx.user._id - if (ctx.user.builder && ctx.user.builder.global) { - // specific case, update self role in global user - await addAppRoleToUser(ctx, ctx.appId, BUILTIN_ROLE_IDS.ADMIN) - } // make sure no stale rev delete ctx.request.body._rev await exports.updateMetadata(ctx) diff --git a/packages/server/src/api/routes/tests/routing.spec.js b/packages/server/src/api/routes/tests/routing.spec.js index 622552c77f..38cd62ae76 100644 --- a/packages/server/src/api/routes/tests/routing.spec.js +++ b/packages/server/src/api/routes/tests/routing.spec.js @@ -28,9 +28,7 @@ describe("/routing", () => { it("returns the correct routing for basic user", async () => { workerRequests.getGlobalUsers.mockImplementationOnce((ctx, appId) => { return { - roles: { - [appId]: BUILTIN_ROLE_IDS.BASIC, - } + roleId: BUILTIN_ROLE_IDS.BASIC, } }) const res = await request diff --git a/packages/server/src/api/routes/tests/utilities/TestFunctions.js b/packages/server/src/api/routes/tests/utilities/TestFunctions.js index c49e44c949..dfd77eec7a 100644 --- a/packages/server/src/api/routes/tests/utilities/TestFunctions.js +++ b/packages/server/src/api/routes/tests/utilities/TestFunctions.js @@ -2,6 +2,7 @@ const rowController = require("../../../controllers/row") const appController = require("../../../controllers/application") const CouchDB = require("../../../../db") const { AppStatus } = require("../../../../db/utils") +const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") function Request(appId, params) { this.appId = appId @@ -77,11 +78,17 @@ exports.checkPermissionsEndpoint = async ({ .set(passHeader) .expect(200) - user = await config.createUser("fail@budibase.com", password, failRole) - const failHeader = await config.login("fail@budibase.com", password, { - roleId: failRole, - userId: user.globalId, - }) + let failHeader + if (failRole === BUILTIN_ROLE_IDS.PUBLIC) { + failHeader = config.publicHeaders() + } else { + user = await config.createUser("fail@budibase.com", password, failRole) + failHeader = await config.login("fail@budibase.com", password, { + roleId: failRole, + userId: user.globalId, + builder: false, + }) + } await exports .createRequest(config.request, method, url, body) diff --git a/packages/server/src/middleware/currentapp.js b/packages/server/src/middleware/currentapp.js index ae83da8ba6..e47c9894fa 100644 --- a/packages/server/src/middleware/currentapp.js +++ b/packages/server/src/middleware/currentapp.js @@ -33,7 +33,7 @@ module.exports = async (ctx, next) => { updateCookie = true appId = requestAppId // retrieving global user gets the right role - roleId = globalUser.roleId + roleId = globalUser.roleId || BUILTIN_ROLE_IDS.PUBLIC } else if (appCookie != null) { appId = appCookie.appId roleId = appCookie.roleId || BUILTIN_ROLE_IDS.PUBLIC diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 60e503c128..4ba5abbb59 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -101,7 +101,7 @@ class TestConfiguration { userId: GLOBAL_USER_ID, } const app = { - roleId: BUILTIN_ROLE_IDS.BUILDER, + roleId: BUILTIN_ROLE_IDS.ADMIN, appId: this.appId, } const authToken = jwt.sign(auth, env.JWT_SECRET) @@ -306,12 +306,9 @@ class TestConfiguration { return await this._req(config, null, controllers.layout.save) } - async createUser(roleId = BUILTIN_ROLE_IDS.POWER) { + async createUser() { const globalId = `us_${Math.random()}` - const resp = await this.globalUser( - globalId, - roleId === BUILTIN_ROLE_IDS.BUILDER - ) + const resp = await this.globalUser(globalId) return { ...resp, globalId, @@ -319,7 +316,6 @@ class TestConfiguration { } async login(email, password, { roleId, userId, builder } = {}) { - roleId = !roleId ? BUILTIN_ROLE_IDS.BUILDER : roleId userId = !userId ? `us_uuid1` : userId if (!this.request) { throw "Server has not been opened, cannot login." 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/server/src/utilities/workerRequests.js b/packages/server/src/utilities/workerRequests.js index 99d9a1c3e2..59ab2c296c 100644 --- a/packages/server/src/utilities/workerRequests.js +++ b/packages/server/src/utilities/workerRequests.js @@ -9,19 +9,26 @@ function getAppRole(appId, user) { if (!user.roles) { return user } - // always use the deployed app - user.roleId = user.roles[getDeployedAppID(appId)] - if (!user.roleId) { - user.roleId = BUILTIN_ROLE_IDS.PUBLIC + if (user.builder && user.builder.global) { + user.roleId = BUILTIN_ROLE_IDS.ADMIN + } else { + // always use the deployed app + user.roleId = user.roles[getDeployedAppID(appId)] + if (!user.roleId) { + user.roleId = BUILTIN_ROLE_IDS.PUBLIC + } } delete user.roles return user } -function request(ctx, request) { +function request(ctx, request, noApiKey) { if (!request.headers) { request.headers = {} } + if (!noApiKey) { + request.headers["x-budibase-api-key"] = env.INTERNAL_API_KEY + } if (request.body && Object.keys(request.body).length > 0) { request.headers["Content-Type"] = "application/json" request.body = @@ -44,9 +51,6 @@ exports.sendSmtpEmail = async (to, from, subject, contents) => { checkSlashesInUrl(env.WORKER_URL + `/api/admin/email/send`), request(null, { method: "POST", - headers: { - "x-budibase-api-key": env.INTERNAL_API_KEY, - }, body: { email: to, from, @@ -86,16 +90,6 @@ exports.getDeployedApps = async ctx => { } } -exports.deleteGlobalUser = async (ctx, globalId) => { - const endpoint = `/api/admin/users/${globalId}` - const reqCfg = { method: "DELETE" } - const response = await fetch( - checkSlashesInUrl(env.WORKER_URL + endpoint), - request(ctx, reqCfg) - ) - return response.json() -} - exports.getGlobalUsers = async (ctx, appId = null, globalId = null) => { const endpoint = globalId ? `/api/admin/users/${globalId}` @@ -121,7 +115,8 @@ exports.getGlobalSelf = async (ctx, appId = null) => { const endpoint = `/api/admin/users/self` const response = await fetch( checkSlashesInUrl(env.WORKER_URL + endpoint), - request(ctx, { method: "GET" }) + // we don't want to use API key when getting self + request(ctx, { method: "GET" }, true) ) if (response.status !== 200) { ctx.throw(400, "Unable to get self globally.") @@ -172,9 +167,6 @@ exports.removeAppFromUserRoles = async appId => { checkSlashesInUrl(env.WORKER_URL + `/api/admin/roles/${deployedAppId}`), request(null, { method: "DELETE", - headers: { - "x-budibase-api-key": env.INTERNAL_API_KEY, - }, }) ) if (response.status !== 200) { 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 @@ + +
+