diff --git a/.github/ISSUE_TEMPLATE/epic.md b/.github/ISSUE_TEMPLATE/epic.md new file mode 100644 index 0000000000..b8cf652125 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/epic.md @@ -0,0 +1,24 @@ +--- +name: Epic +about: Plan a new project +title: '' +labels: epic +assignees: '' + +--- + +## Description +Brief summary of what this Epic is, whether it's a larger project, goal, or user story. Describe the job to be done, which persona this Epic is mainly for, or if more multiple, break it down by user and job story. + +## Spec +Link to confluence spec + +## Teams and Stakeholders +Describe who needs to be kept up-to-date about this Epic, included in discussions, or updated along the way. Stakeholders can be both in Product/Engineering, as well as other teams like Customer Success who might want to keep customers updated on the Epic project. + + +## Workflow +- [ ] Spec Created and pasted above +- [ ] Product Review +- [ ] Designs created +- [ ] Individual Tasks created and assigned to Epic diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index e940e6fa10..42a0c0a273 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -59,3 +59,9 @@ jobs: with: install: false command: yarn test:e2e:ci + + - name: QA Core Integration Tests + run: | + cd qa-core + yarn + yarn api:test:ci \ No newline at end of file diff --git a/.gitignore b/.gitignore index 32c6faf980..e1d3e6db0e 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,7 @@ typings/ # dotenv environment variables file .env +!qa-core/.env !hosting/.env hosting/.generated-nginx.dev.conf hosting/proxy/.generated-nginx.prod.conf diff --git a/.prettierignore b/.prettierignore index bbeff65da7..ad36a86b99 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,4 +8,4 @@ packages/server/client packages/server/src/definitions/openapi.ts packages/builder/.routify packages/builder/cypress/support/queryLevelTransformerFunction.js -packages/builder/cypress/support/queryLevelTransformerFunctionWithData.js +packages/builder/cypress/support/queryLevelTransformerFunctionWithData.js \ No newline at end of file diff --git a/lerna.json b/lerna.json index 7eb5a19c79..5bca2d4f87 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.3.19-alpha.0", + "version": "1.4.2", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/package.json b/package.json index 4c24e0025b..d9b78368ba 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "js-yaml": "^4.1.0", "kill-port": "^1.6.1", "lerna": "3.14.1", + "madge": "^5.0.1", "prettier": "^2.3.1", "prettier-plugin-svelte": "^2.3.0", "rimraf": "^3.0.2", @@ -25,6 +26,7 @@ "bootstrap": "lerna bootstrap && lerna link && ./scripts/link-dependencies.sh", "build": "lerna run build", "build:dev": "lerna run prebuild && tsc --build --watch --preserveWatchOutput", + "deps:circular": "madge packages/server/dist/index.js packages/worker/src/index.ts packages/backend-core/dist/src/index.js packages/cli/src/index.js --circular", "release": "lerna publish ${RELEASE_VERSION_TYPE:-patch} --yes --force-publish && yarn release:pro", "release:develop": "lerna publish prerelease --yes --force-publish --dist-tag develop --exact && yarn release:pro:develop", "release:pro": "bash scripts/pro/release.sh", @@ -45,8 +47,8 @@ "lint:eslint": "eslint packages", "lint:prettier": "prettier --check \"packages/**/*.{js,ts,svelte}\"", "lint": "yarn run lint:eslint && yarn run lint:prettier", - "lint:fix:eslint": "eslint --fix packages", - "lint:fix:prettier": "prettier --write \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\"", + "lint:fix:eslint": "eslint --fix packages qa-core", + "lint:fix:prettier": "prettier --write \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\" && prettier --write \"qa-core/**/*.{js,ts,svelte}\"", "lint:fix": "yarn run lint:fix:prettier && yarn run lint:fix:eslint", "test:e2e": "lerna run cy:test --stream", "test:e2e:ci": "lerna run cy:ci --stream", diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 60fc965793..be291c5adc 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "1.3.19-alpha.0", + "version": "1.4.2", "description": "Budibase backend core libraries used in server and worker", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -20,7 +20,7 @@ "test:watch": "jest --watchAll" }, "dependencies": { - "@budibase/types": "1.3.19-alpha.0", + "@budibase/types": "^1.4.2", "@shopify/jest-koa-mocks": "5.0.1", "@techpass/passport-openidconnect": "0.3.2", "aws-sdk": "2.1030.0", diff --git a/packages/backend-core/src/context/index.ts b/packages/backend-core/src/context/index.ts index 78ce764d55..8e3cf8a0a2 100644 --- a/packages/backend-core/src/context/index.ts +++ b/packages/backend-core/src/context/index.ts @@ -2,7 +2,7 @@ import env from "../environment" import { SEPARATOR, DocumentType } from "../db/constants" import cls from "./FunctionContext" import { dangerousGetDB, closeDB } from "../db" -import { baseGlobalDBName } from "../tenancy/utils" +import { baseGlobalDBName } from "../db/tenancy" import { IdentityContext } from "@budibase/types" import { DEFAULT_TENANT_ID as _DEFAULT_TENANT_ID } from "../constants" import { ContextKey } from "./constants" diff --git a/packages/backend-core/src/db/constants.ts b/packages/backend-core/src/db/constants.ts index 2c2c29cee2..62f4e8820f 100644 --- a/packages/backend-core/src/db/constants.ts +++ b/packages/backend-core/src/db/constants.ts @@ -44,6 +44,7 @@ export enum DocumentType { DEV_INFO = "devinfo", AUTOMATION_LOG = "log_au", ACCOUNT_METADATA = "acc_metadata", + PLUGIN = "plg", } export const StaticDatabases = { diff --git a/packages/backend-core/src/tenancy/utils.ts b/packages/backend-core/src/db/tenancy.ts similarity index 91% rename from packages/backend-core/src/tenancy/utils.ts rename to packages/backend-core/src/db/tenancy.ts index f99f1e30af..d920f7cd41 100644 --- a/packages/backend-core/src/tenancy/utils.ts +++ b/packages/backend-core/src/db/tenancy.ts @@ -1,5 +1,5 @@ import { DEFAULT_TENANT_ID } from "../constants" -import { StaticDatabases, SEPARATOR } from "../db/constants" +import { StaticDatabases, SEPARATOR } from "./constants" import { getTenantId } from "../context" export const getGlobalDBName = (tenantId?: string) => { diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts index c93c7b5662..a12c6bed4f 100644 --- a/packages/backend-core/src/db/utils.ts +++ b/packages/backend-core/src/db/utils.ts @@ -3,7 +3,7 @@ import { DEFAULT_TENANT_ID, Configs } from "../constants" import env from "../environment" import { SEPARATOR, DocumentType, UNICODE_MAX, ViewName } from "./constants" import { getTenantId, getGlobalDB } from "../context" -import { getGlobalDBName } from "../tenancy/utils" +import { getGlobalDBName } from "./tenancy" import fetch from "node-fetch" import { doWithDB, allDbs } from "./index" import { getCouchInfo } from "./pouch" @@ -16,6 +16,7 @@ import * as events from "../events" export * from "./constants" export * from "./conversions" export { default as Replication } from "./Replication" +export * from "./tenancy" /** * Generates a new app ID. @@ -367,6 +368,21 @@ export const generateDevInfoID = (userId: any) => { return `${DocumentType.DEV_INFO}${SEPARATOR}${userId}` } +/** + * Generates a new plugin ID - to be used in the global DB. + * @returns {string} The new plugin ID which a plugin metadata document can be stored under. + */ +export const generatePluginID = (name: string) => { + return `${DocumentType.PLUGIN}${SEPARATOR}${name}` +} + +/** + * Gets parameters for retrieving automations, this is a utility function for the getDocParams function. + */ +export const getPluginParams = (pluginId?: string | null, otherProps = {}) => { + return getDocParams(DocumentType.PLUGIN, pluginId, otherProps) +} + /** * Returns the most granular configuration document from the DB based on the type, workspace and userID passed. * @param {Object} db - db instance to query diff --git a/packages/backend-core/src/migrations/migrations.ts b/packages/backend-core/src/migrations/migrations.ts index ca238ff80e..90a12acec2 100644 --- a/packages/backend-core/src/migrations/migrations.ts +++ b/packages/backend-core/src/migrations/migrations.ts @@ -3,12 +3,8 @@ import { doWithDB } from "../db" import { DocumentType, StaticDatabases } from "../db/constants" import { getAllApps } from "../db/utils" import environment from "../environment" -import { - doInTenant, - getTenantIds, - getGlobalDBName, - getTenantId, -} from "../tenancy" +import { doInTenant, getTenantIds, getTenantId } from "../tenancy" +import { getGlobalDBName } from "../db/tenancy" import * as context from "../context" import { DEFINITIONS } from "." import { diff --git a/packages/backend-core/src/tenancy/index.ts b/packages/backend-core/src/tenancy/index.ts index d1ccbbf001..e0006abab2 100644 --- a/packages/backend-core/src/tenancy/index.ts +++ b/packages/backend-core/src/tenancy/index.ts @@ -1,11 +1,9 @@ import * as context from "../context" import * as tenancy from "./tenancy" -import * as utils from "./utils" const pkg = { ...context, ...tenancy, - ...utils, } export = pkg diff --git a/packages/backend-core/src/tenancy/tenancy.ts b/packages/backend-core/src/tenancy/tenancy.ts index 1c71935eb0..a100888212 100644 --- a/packages/backend-core/src/tenancy/tenancy.ts +++ b/packages/backend-core/src/tenancy/tenancy.ts @@ -1,7 +1,7 @@ import { doWithDB } from "../db" import { queryPlatformView } from "../db/views" import { StaticDatabases, ViewName } from "../db/constants" -import { getGlobalDBName } from "./utils" +import { getGlobalDBName } from "../db/tenancy" import { getTenantId, DEFAULT_TENANT_ID, @@ -9,7 +9,7 @@ import { getTenantIDFromAppID, } from "../context" import env from "../environment" -import { PlatformUser, PlatformUserByEmail } from "@budibase/types" +import { PlatformUser } from "@budibase/types" const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants const PLATFORM_INFO_DB = StaticDatabases.PLATFORM_INFO.name diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 0095277d39..8c39d95424 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": "1.3.19-alpha.0", + "version": "1.4.2", "license": "MPL-2.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", @@ -38,7 +38,7 @@ ], "dependencies": { "@adobe/spectrum-css-workflow-icons": "^1.2.1", - "@budibase/string-templates": "1.3.19-alpha.0", + "@budibase/string-templates": "^1.4.2", "@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/avatar": "^3.0.2", diff --git a/packages/bbui/src/Drawer/Drawer.svelte b/packages/bbui/src/Drawer/Drawer.svelte index e1880d0ed4..43729cd794 100644 --- a/packages/bbui/src/Drawer/Drawer.svelte +++ b/packages/bbui/src/Drawer/Drawer.svelte @@ -78,7 +78,7 @@ bottom: 0; background: var(--background); border-top: var(--border-light); - z-index: 2; + z-index: 3; } .fillWidth { diff --git a/packages/bbui/src/Tooltip/TooltipWrapper.svelte b/packages/bbui/src/Tooltip/TooltipWrapper.svelte index e7a96f6cd8..09998d2c52 100644 --- a/packages/bbui/src/Tooltip/TooltipWrapper.svelte +++ b/packages/bbui/src/Tooltip/TooltipWrapper.svelte @@ -48,7 +48,7 @@ display: flex; justify-content: center; top: 15px; - z-index: 100; + z-index: 200; width: 160px; } .icon { diff --git a/packages/builder/cypress/integration/createComponents.spec.js b/packages/builder/cypress/integration/createComponents.spec.js index e39ce4a4a8..7f29466258 100644 --- a/packages/builder/cypress/integration/createComponents.spec.js +++ b/packages/builder/cypress/integration/createComponents.spec.js @@ -2,7 +2,7 @@ import filterTests from "../support/filterTests" const interact = require("../support/interact") filterTests(["all"], () => { - context("Create Components", () => { + xcontext("Create Components", () => { let headlineId before(() => { diff --git a/packages/builder/package.json b/packages/builder/package.json index 208e7ffb7a..dae011ed3e 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "1.3.19-alpha.0", + "version": "1.4.2", "license": "GPL-3.0", "private": true, "scripts": { @@ -9,6 +9,7 @@ "dev:builder": "routify -c dev:vite", "dev:vite": "vite --host 0.0.0.0", "rollup": "rollup -c -w", + "test": "jest", "cy:setup": "ts-node ./cypress/ts/setup.ts", "cy:setup:ci": "node ./cypress/setup.js", "cy:open": "cypress open", @@ -36,7 +37,8 @@ "components(.*)$": "/src/components$1", "builderStore(.*)$": "/src/builderStore$1", "stores(.*)$": "/src/stores$1", - "analytics(.*)$": "/src/analytics$1" + "analytics(.*)$": "/src/analytics$1", + "constants/backend": "/src/constants/backend/index.js" }, "moduleFileExtensions": [ "js", @@ -69,10 +71,10 @@ } }, "dependencies": { - "@budibase/bbui": "1.3.19-alpha.0", - "@budibase/client": "1.3.19-alpha.0", - "@budibase/frontend-core": "1.3.19-alpha.0", - "@budibase/string-templates": "1.3.19-alpha.0", + "@budibase/bbui": "^1.4.2", + "@budibase/client": "^1.4.2", + "@budibase/frontend-core": "^1.4.2", + "@budibase/string-templates": "^1.4.2", "@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 d961a3a1cd..b6312ef8e8 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -9,14 +9,14 @@ import { import { store } from "builderStore" import { queries as queriesStores, - tables as tablesStore, roles as rolesStore, + tables as tablesStore, } from "stores/backend" import { - makePropSafe, - isJSBinding, decodeJSBinding, encodeJSBinding, + isJSBinding, + makePropSafe, } from "@budibase/string-templates" import { TableNames } from "../constants" import { JSONUtils } from "@budibase/frontend-core" @@ -118,8 +118,7 @@ export const readableToRuntimeMap = (bindings, ctx) => { return {} } return Object.keys(ctx).reduce((acc, key) => { - let parsedQuery = readableToRuntimeBinding(bindings, ctx[key]) - acc[key] = parsedQuery + acc[key] = readableToRuntimeBinding(bindings, ctx[key]) return acc }, {}) } @@ -132,8 +131,7 @@ export const runtimeToReadableMap = (bindings, ctx) => { return {} } return Object.keys(ctx).reduce((acc, key) => { - let parsedQuery = runtimeToReadableBinding(bindings, ctx[key]) - acc[key] = parsedQuery + acc[key] = runtimeToReadableBinding(bindings, ctx[key]) return acc }, {}) } @@ -379,7 +377,7 @@ const getProviderContextBindings = (asset, dataProviders) => { /** * Gets all bindable properties from the logged in user. */ -const getUserBindings = () => { +export const getUserBindings = () => { let bindings = [] const { schema } = getSchemaForTable(TableNames.USERS) const keys = Object.keys(schema).sort() diff --git a/packages/builder/src/components/common/TemplateDisplay.svelte b/packages/builder/src/components/common/TemplateDisplay.svelte index 6041a904fd..d6ba4bd8e7 100644 --- a/packages/builder/src/components/common/TemplateDisplay.svelte +++ b/packages/builder/src/components/common/TemplateDisplay.svelte @@ -97,7 +97,7 @@ backgroundColour={templateEntry.background} icon={templateEntry.icon} > - {#if $licensing?.usageMetrics?.apps < 100} + {#if !($licensing?.usageMetrics?.apps >= 100)} + {/if} + + + {#if !bindable} + Bindings come in two parts: the binding name, and a default/fallback + value. These bindings can be used as Handlebars expressions throughout the + query. + {:else} + Enter a value for each binding. The default values will be used for any + values left blank. + {/if} + +
+ { + queryBindings = e.detail.map(binding => { + return { + name: binding.name, + default: binding.value, + } + }) + }} + /> +
+ + + diff --git a/packages/builder/src/components/portal/licensing/LicensingOverlays.svelte b/packages/builder/src/components/portal/licensing/LicensingOverlays.svelte index 8431b860e8..736aac862f 100644 --- a/packages/builder/src/components/portal/licensing/LicensingOverlays.svelte +++ b/packages/builder/src/components/portal/licensing/LicensingOverlays.svelte @@ -8,6 +8,7 @@ import { ExpiringKeys } from "./constants" import { getBanners } from "./licensingBanners" import { banner } from "@budibase/bbui" + import { FEATURE_FLAGS, isEnabled } from "../../../helpers/featureFlags" const oneDayInSeconds = 86400 @@ -81,7 +82,12 @@ } } - $: if (userLoaded && licensingLoaded && loaded) { + $: if ( + userLoaded && + licensingLoaded && + loaded && + isEnabled(FEATURE_FLAGS.LICENSING) + ) { queuedModals = processModals() queuedBanners = getBanners() showNextModal() diff --git a/packages/builder/src/helpers/data/utils.js b/packages/builder/src/helpers/data/utils.js index cd6a8cf481..d1ff4c5f80 100644 --- a/packages/builder/src/helpers/data/utils.js +++ b/packages/builder/src/helpers/data/utils.js @@ -1,4 +1,5 @@ import { IntegrationTypes } from "constants/backend" +import { findHBSBlocks } from "@budibase/string-templates" export function schemaToFields(schema) { const response = {} @@ -31,7 +32,7 @@ export function breakQueryString(qs) { let paramObj = {} for (let param of params) { const split = param.split("=") - paramObj[split[0]] = split.slice(1).join("=") + paramObj[split[0]] = decodeURIComponent(split.slice(1).join("=")) } return paramObj } @@ -46,7 +47,19 @@ export function buildQueryString(obj) { if (str !== "") { str += "&" } - str += `${key}=${encodeURIComponent(value || "")}` + const bindings = findHBSBlocks(value) + let count = 0 + const bindingMarkers = {} + bindings.forEach(binding => { + const marker = `BINDING...${count++}` + value = value.replace(binding, marker) + bindingMarkers[marker] = binding + }) + let encoded = encodeURIComponent(value || "") + Object.entries(bindingMarkers).forEach(([marker, binding]) => { + encoded = encoded.replace(marker, binding) + }) + str += `${key}=${encoded}` } } return str diff --git a/packages/builder/src/helpers/tests/dataUtils.spec.js b/packages/builder/src/helpers/tests/dataUtils.spec.js new file mode 100644 index 0000000000..83172af6ee --- /dev/null +++ b/packages/builder/src/helpers/tests/dataUtils.spec.js @@ -0,0 +1,37 @@ +import { breakQueryString, buildQueryString } from "../data/utils" + +describe("check query string utils", () => { + const obj1 = { + key1: "123", + key2: " ", + key3: "333", + } + + const obj2 = { + key1: "{{ binding.awd }}", + key2: "{{ binding.sed }} ", + } + + it("should build a basic query string", () => { + const queryString = buildQueryString(obj1) + expect(queryString).toBe("key1=123&key2=%20%20%20&key3=333") + }) + + it("should be able to break a basic query string", () => { + const broken = breakQueryString("key1=123&key2=%20%20%20&key3=333") + expect(broken.key1).toBe(obj1.key1) + expect(broken.key2).toBe(obj1.key2) + expect(broken.key3).toBe(obj1.key3) + }) + + it("should be able to build with a binding", () => { + const queryString = buildQueryString(obj2) + expect(queryString).toBe("key1={{ binding.awd }}&key2={{ binding.sed }}%20%20") + }) + + it("should be able to break with a binding", () => { + const broken = breakQueryString("key1={{ binding.awd }}&key2={{ binding.sed }}%20%20") + expect(broken.key1).toBe(obj2.key1) + expect(broken.key2).toBe(obj2.key2) + }) +}) \ No newline at end of file diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte index d2c1630416..1698677b66 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte @@ -708,6 +708,7 @@ .url-block { display: flex; gap: var(--spacing-s); + z-index: 200; } .verb { flex: 1; diff --git a/packages/builder/src/pages/builder/portal/_layout.svelte b/packages/builder/src/pages/builder/portal/_layout.svelte index bdf18ab508..38017fdc42 100644 --- a/packages/builder/src/pages/builder/portal/_layout.svelte +++ b/packages/builder/src/pages/builder/portal/_layout.svelte @@ -56,7 +56,7 @@ { title: "Plugins", href: "/builder/portal/manage/plugins", - badge: "New", + badge: "Beta", }, { diff --git a/packages/builder/src/pages/builder/portal/manage/plugins/index.svelte b/packages/builder/src/pages/builder/portal/manage/plugins/index.svelte index 5d73447710..c03232d091 100644 --- a/packages/builder/src/pages/builder/portal/manage/plugins/index.svelte +++ b/packages/builder/src/pages/builder/portal/manage/plugins/index.svelte @@ -10,7 +10,7 @@ Search, } from "@budibase/bbui" import { onMount } from "svelte" - import { plugins } from "stores/portal" + import { plugins, admin } from "stores/portal" import PluginRow from "./_components/PluginRow.svelte" import AddPluginModal from "./_components/AddPluginModal.svelte" @@ -20,9 +20,12 @@ let filterOptions = [ { label: "All plugins", value: "all" }, { label: "Components", value: "component" }, - { label: "Datasources", value: "datasource" }, ] + if (!$admin.cloud) { + filterOptions.push({ label: "Datasources", value: "datasource" }) + } + $: filteredPlugins = $plugins .filter(plugin => { return filter === "all" || plugin.schema.type === filter diff --git a/packages/builder/src/pages/builder/portal/settings/organisation.svelte b/packages/builder/src/pages/builder/portal/settings/organisation.svelte index 6d1f26ce36..5f352a32e4 100644 --- a/packages/builder/src/pages/builder/portal/settings/organisation.svelte +++ b/packages/builder/src/pages/builder/portal/settings/organisation.svelte @@ -88,14 +88,14 @@ Information Here you can update your logo and organization name. -
-
+
+
-
+