diff --git a/lerna.json b/lerna.json index 8b7e036ddc..c1939719a8 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.190-alpha.8", + "version": "0.9.190-alpha.12", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 72ca7c90d7..2aec96c6f8 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.190-alpha.8", + "version": "0.9.190-alpha.12", "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 6d0bc1a8fc..b50a3ded2c 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.190-alpha.8", + "version": "0.9.190-alpha.12", "license": "MPL-2.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index ecda1be172..7774dfbffc 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.190-alpha.8", + "version": "0.9.190-alpha.12", "license": "GPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.190-alpha.8", - "@budibase/client": "^0.9.190-alpha.8", + "@budibase/bbui": "^0.9.190-alpha.12", + "@budibase/client": "^0.9.190-alpha.12", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.190-alpha.8", + "@budibase/string-templates": "^0.9.190-alpha.12", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 1f1fb035a4..c94c759792 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -524,7 +524,7 @@ export const getFrontendStore = () => { } } }, - paste: async (targetComponent, mode) => { + paste: async (targetComponent, mode, preserveBindings = false) => { let promises = [] store.update(state => { // Stop if we have nothing to paste @@ -536,7 +536,7 @@ export const getFrontendStore = () => { const cut = state.componentToPaste.isCut // immediately need to remove bindings, currently these aren't valid when pasted - if (!cut) { + if (!cut && !preserveBindings) { state.componentToPaste = removeBindings(state.componentToPaste) } diff --git a/packages/builder/src/components/design/NavigationPanel/ComponentDropdownMenu.svelte b/packages/builder/src/components/design/NavigationPanel/ComponentDropdownMenu.svelte index 06293e4168..56c5eef2ad 100644 --- a/packages/builder/src/components/design/NavigationPanel/ComponentDropdownMenu.svelte +++ b/packages/builder/src/components/design/NavigationPanel/ComponentDropdownMenu.svelte @@ -53,7 +53,7 @@ const duplicateComponent = () => { storeComponentForCopy(false) - pasteComponent("below") + pasteComponent("below", true) } const deleteComponent = async () => { @@ -69,9 +69,9 @@ store.actions.components.copy(component, cut) } - const pasteComponent = mode => { + const pasteComponent = (mode, preserveBindings = false) => { // lives in store - also used by drag drop - store.actions.components.paste(component, mode) + store.actions.components.paste(component, mode, preserveBindings) } diff --git a/packages/builder/src/components/design/NavigationPanel/FrontendNavigatePane.svelte b/packages/builder/src/components/design/NavigationPanel/FrontendNavigatePane.svelte index 51dd44026e..ea9ebbf2c5 100644 --- a/packages/builder/src/components/design/NavigationPanel/FrontendNavigatePane.svelte +++ b/packages/builder/src/components/design/NavigationPanel/FrontendNavigatePane.svelte @@ -25,8 +25,7 @@ key: "layout", }, ] - - let modal + let newLayoutModal $: selected = tabs.find(t => t.key === $params.assetType)?.title || "Screens" const navigate = ({ detail }) => { @@ -93,14 +92,18 @@ {#each $store.layouts as layout, idx (layout._id)} 0} /> {/each} - +
- +
diff --git a/packages/builder/src/components/design/NavigationPanel/NewScreenModal.svelte b/packages/builder/src/components/design/NavigationPanel/NewScreenModal.svelte index fbd1fc9256..67dbf581f2 100644 --- a/packages/builder/src/components/design/NavigationPanel/NewScreenModal.svelte +++ b/packages/builder/src/components/design/NavigationPanel/NewScreenModal.svelte @@ -10,16 +10,39 @@ ProgressCircle, } from "@budibase/bbui" import getTemplates from "builderStore/store/screenTemplates" + import { onDestroy } from "svelte" + + import { createEventDispatcher } from "svelte" - export let selectedScreens = [] export let chooseModal export let save export let showProgressCircle = false + + let selectedScreens = [] + const blankScreen = "createFromScratch" + const dispatch = createEventDispatcher() + + function setScreens() { + dispatch("save", { + screens: selectedScreens, + }) + } $: blankSelected = selectedScreens?.length === 1 $: autoSelected = selectedScreens?.length > 0 && !blankSelected + let templates = getTemplates($store, $tables.list) + + const confirm = async () => { + if (autoSelected) { + setScreens() + await save() + } else { + setScreens() + chooseModal(1) + } + } const toggleScreenSelection = table => { if (selectedScreens.find(s => s.table === table.name)) { selectedScreens = selectedScreens.filter( @@ -32,14 +55,18 @@ selectedScreens = [...partialTemplates, ...selectedScreens] } } + + onDestroy(() => { + selectedScreens = [] + }) -
+
(autoSelected ? save() : chooseModal(1))} + onConfirm={() => confirm()} disabled={!selectedScreens.length} size="L" > @@ -70,29 +97,31 @@ {/if}
- Autogenerated Screens + {#if $tables.list.filter(table => table._id !== "ta_users").length > 0} + Autogenerated Screens - {#each $tables.list.filter(table => table._id !== "ta_users") as table} -
x.table === table.name)} - on:click={() => toggleScreenSelection(table)} - class="item" - > -
-
{table.name}
-
+ {#each $tables.list.filter(table => table._id !== "ta_users") as table}
x.table === table.name)} + on:click={() => toggleScreenSelection(table)} + class="item" > - {#if selectedScreens.find(x => x.table === table.name)} -
- -
- {/if} +
+
{table.name}
+
+
+ {#if selectedScreens.find(x => x.table === table.name)} +
+ +
+ {/if} +
-
- {/each} + {/each} + {/if}
{#if showProgressCircle} diff --git a/packages/builder/src/components/design/NavigationPanel/ScreenDetailsModal.svelte b/packages/builder/src/components/design/NavigationPanel/ScreenDetailsModal.svelte index e5cc839045..6d906da742 100644 --- a/packages/builder/src/components/design/NavigationPanel/ScreenDetailsModal.svelte +++ b/packages/builder/src/components/design/NavigationPanel/ScreenDetailsModal.svelte @@ -2,6 +2,7 @@ import { ModalContent, Input, ProgressCircle } from "@budibase/bbui" import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl" import { selectedAccessRole, allScreens } from "builderStore" + import { onDestroy } from "svelte" export let screenName export let url @@ -32,6 +33,11 @@ screen.routing.roleId === roleId ) } + + onDestroy(() => { + screenName = "" + url = "" + }) { let existingScreenCount = $store.screens.filter( s => s.props._instanceName == draftScreen.props._instanceName @@ -90,17 +89,14 @@ ) } - onDestroy(() => { - selectedScreens = [] - screenName = "" - url = "" - createdScreens = [] - }) - export const showModal = () => { newScreenModal.show() } + const setScreens = evt => { + selectedScreens = evt.detail.screens + } + const chooseModal = index => { /* 0 = newScreenModal @@ -119,7 +115,7 @@ { - dispatch("change", value) + dispatch("change", links) drawer.hide() } @@ -19,5 +21,5 @@ Configure the links in your navigation bar. - + diff --git a/packages/cli/package.json b/packages/cli/package.json index e37a6f6cad..b43b28a9fc 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.190-alpha.8", + "version": "0.9.190-alpha.12", "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 0ab9d0fc65..32781cfedc 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.190-alpha.8", + "version": "0.9.190-alpha.12", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.190-alpha.8", + "@budibase/bbui": "^0.9.190-alpha.12", "@budibase/standard-components": "^0.9.139", - "@budibase/string-templates": "^0.9.190-alpha.8", + "@budibase/string-templates": "^0.9.190-alpha.12", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/client/src/components/app/Layout.svelte b/packages/client/src/components/app/Layout.svelte index 87e5ac3b5b..59765f9305 100644 --- a/packages/client/src/components/app/Layout.svelte +++ b/packages/client/src/components/app/Layout.svelte @@ -313,6 +313,9 @@ height: 100%; overflow: auto; } + .desktop.layout--left .links { + overflow-y: auto; + } .desktop .nav--left { width: 250px; @@ -379,6 +382,7 @@ justify-content: flex-start; align-items: stretch; padding: var(--spacing-xl); + overflow-y: auto; } .mobile .link { width: calc(100% - 30px); diff --git a/packages/server/package.json b/packages/server/package.json index a16ff8bc49..5966cc81fc 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.190-alpha.8", + "version": "0.9.190-alpha.12", "description": "Budibase Web Server", "main": "src/index.ts", "repository": { @@ -69,9 +69,9 @@ "author": "Budibase", "license": "GPL-3.0", "dependencies": { - "@budibase/auth": "^0.9.190-alpha.8", - "@budibase/client": "^0.9.190-alpha.8", - "@budibase/string-templates": "^0.9.190-alpha.8", + "@budibase/auth": "^0.9.190-alpha.12", + "@budibase/client": "^0.9.190-alpha.12", + "@budibase/string-templates": "^0.9.190-alpha.12", "@bull-board/api": "^3.7.0", "@bull-board/koa": "^3.7.0", "@elastic/elasticsearch": "7.10.0", diff --git a/packages/server/scripts/integrations/oracle/docker-compose.yml b/packages/server/scripts/integrations/oracle/docker-compose.yml index 5cd5e02f81..c54cd0a40b 100644 --- a/packages/server/scripts/integrations/oracle/docker-compose.yml +++ b/packages/server/scripts/integrations/oracle/docker-compose.yml @@ -4,7 +4,7 @@ version: "3.8" services: db: - container_name: oracle-xe + restart: always platform: linux/x86_64 image: container-registry.oracle.com/database/express:18.4.0-xe environment: diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 2226dc99be..af199561dc 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -1,4 +1,5 @@ import { + FilterTypes, IncludeRelationships, Operation, PaginationJson, @@ -118,8 +119,13 @@ module External { } // check the row and filters to make sure they aren't a key of some sort if (config.filters) { - for (let filter of Object.values(config.filters)) { - if (typeof filter !== "object" || Object.keys(filter).length === 0) { + for (let [key, filter] of Object.entries(config.filters)) { + // oneOf is an array, don't iterate it + if ( + typeof filter !== "object" || + Object.keys(filter).length === 0 || + key === FilterTypes.ONE_OF + ) { continue } iterateObject(filter) diff --git a/packages/server/src/definitions/datasource.ts b/packages/server/src/definitions/datasource.ts index 3760f54f2c..eb65ac994f 100644 --- a/packages/server/src/definitions/datasource.ts +++ b/packages/server/src/definitions/datasource.ts @@ -54,6 +54,17 @@ export enum IncludeRelationships { EXCLUDE = 0, } +export enum FilterTypes { + STRING = "string", + FUZZY = "fuzzy", + RANGE = "range", + EQUAL = "equal", + NOT_EQUAL = "notEqual", + EMPTY = "empty", + NOT_EMPTY = "notEmpty", + ONE_OF = "oneOf", +} + export interface QueryDefinition { type: QueryTypes displayName?: string diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 471774db3d..df4f2d511b 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -93,7 +93,7 @@ class InternalBuilder { if (filters.oneOf) { iterate(filters.oneOf, (key, array) => { const fnc = allOr ? "orWhereIn" : "whereIn" - query = query[fnc](key, array) + query = query[fnc](key, Array.isArray(array) ? array : [array]) }) } if (filters.string) { @@ -435,8 +435,6 @@ class SqlQueryBuilder extends SqlTableQueryBuilder { id = results?.[0].id } else if (sqlClient === SqlClients.MY_SQL) { id = results?.insertId - } else if (sqlClient === SqlClients.ORACLE) { - id = response.outBinds[0][0] } row = processFn( await this.getReturningRow(queryFn, this.checkLookupKeys(id, json)) diff --git a/packages/server/src/integrations/oracle.ts b/packages/server/src/integrations/oracle.ts index 13658399db..bf8e83350e 100644 --- a/packages/server/src/integrations/oracle.ts +++ b/packages/server/src/integrations/oracle.ts @@ -348,27 +348,7 @@ module OracleModule { this.schemaErrors = final.errors } - /** - * Knex default returning behaviour does not work with oracle - * Manually add the behaviour for the return column - */ - private addReturning( - query: SqlQuery, - bindings: BindParameters, - returnColumn: string - ) { - if (bindings instanceof Array) { - bindings.push({ dir: oracledb.BIND_OUT }) - query.sql = - query.sql + ` returning \"${returnColumn}\" into :${bindings.length}` - } - } - - private async internalQuery( - query: SqlQuery, - returnColum?: string, - operation?: string - ): Promise> { + private async internalQuery(query: SqlQuery): Promise> { let connection try { connection = await this.getConnection() @@ -376,13 +356,6 @@ module OracleModule { const options: ExecuteOptions = { autoCommit: true } const bindings: BindParameters = query.bindings || [] - if ( - returnColum && - (operation === Operation.CREATE || operation === Operation.UPDATE) - ) { - this.addReturning(query, bindings, returnColum) - } - const result: Result = await connection.execute( query.sql, bindings, @@ -441,13 +414,46 @@ module OracleModule { } async query(json: QueryJson) { - const primaryKeys = json.meta!.table!.primary - const primaryKey = primaryKeys ? primaryKeys[0] : undefined - const queryFn = (query: any, operation: string) => - this.internalQuery(query, primaryKey, operation) - const processFn = (response: any) => (response.rows ? response.rows : []) - const output = await this.queryWithReturning(json, queryFn, processFn) - return output + const operation = this._operation(json) + const input = this._query(json, { disableReturning: true }) + if (Array.isArray(input)) { + const responses = [] + for (let query of input) { + responses.push(await this.internalQuery(query)) + } + return responses + } else { + // read the row to be deleted up front for the return + let deletedRows + if (operation === Operation.DELETE) { + const queryFn = (query: any) => this.internalQuery(query) + deletedRows = await this.getReturningRow(queryFn, json) + } + + // run the query + const response = await this.internalQuery(input) + + // get the results or return the created / updated / deleted row + if (deletedRows?.rows?.length) { + return deletedRows.rows + } else if (response.rows?.length) { + return response.rows + } else { + // get the last row that was updated + if ( + response.lastRowid && + json.endpoint?.entityId && + operation !== Operation.DELETE + ) { + const lastRow = await this.internalQuery({ + sql: `SELECT * FROM \"${json.endpoint.entityId}\" WHERE ROWID = '${response.lastRowid}'`, + }) + return lastRow.rows + } else { + return [{ [ operation.toLowerCase() ]: true }] + } + } + } } } diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index b4203429f4..2fafa1f7ab 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.190-alpha.8", + "version": "0.9.190-alpha.12", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 10389c2f1d..6ac6b2ab61 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.190-alpha.8", + "version": "0.9.190-alpha.12", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -29,8 +29,8 @@ "author": "Budibase", "license": "GPL-3.0", "dependencies": { - "@budibase/auth": "^0.9.190-alpha.8", - "@budibase/string-templates": "^0.9.190-alpha.8", + "@budibase/auth": "^0.9.190-alpha.12", + "@budibase/string-templates": "^0.9.190-alpha.12", "@koa/router": "^8.0.0", "@sentry/node": "^6.0.0", "@techpass/passport-openidconnect": "^0.3.0",