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/charts/budibase/values.yaml b/charts/budibase/values.yaml index a15504d58c..5c4004cb57 100644 --- a/charts/budibase/values.yaml +++ b/charts/budibase/values.yaml @@ -76,6 +76,7 @@ affinity: {} globals: appVersion: "latest" budibaseEnv: PRODUCTION + tenantFeatureFlags: "*:LICENSING,*:USER_GROUPS" enableAnalytics: "1" sentryDSN: "" posthogToken: "phc_bIjZL7oh2GEUd2vqvTBH8WvrX0fWTFQMs6H5KQxiUxU" diff --git a/hosting/single/runner.sh b/hosting/single/runner.sh index cf82e6701b..9320afac94 100644 --- a/hosting/single/runner.sh +++ b/hosting/single/runner.sh @@ -1,6 +1,6 @@ #!/bin/bash declare -a ENV_VARS=("COUCHDB_USER" "COUCHDB_PASSWORD" "DATA_DIR" "MINIO_ACCESS_KEY" "MINIO_SECRET_KEY" "INTERNAL_API_KEY" "JWT_SECRET" "REDIS_PASSWORD") -declare -a DOCKER_VARS=("APP_PORT" "APPS_URL" "ARCHITECTURE" "BUDIBASE_ENVIRONMENT" "CLUSTER_PORT" "DEPLOYMENT_ENVIRONMENT" "MINIO_URL" "NODE_ENV" "POSTHOG_TOKEN" "REDIS_URL" "SELF_HOSTED" "WORKER_PORT" "WORKER_URL") +declare -a DOCKER_VARS=("APP_PORT" "APPS_URL" "ARCHITECTURE" "BUDIBASE_ENVIRONMENT" "CLUSTER_PORT" "DEPLOYMENT_ENVIRONMENT" "MINIO_URL" "NODE_ENV" "POSTHOG_TOKEN" "REDIS_URL" "SELF_HOSTED" "WORKER_PORT" "WORKER_URL" "TENANT_FEATURE_FLAGS") # Check the env vars set in Dockerfile have come through, AAS seems to drop them [[ -z "${APP_PORT}" ]] && export APP_PORT=4001 [[ -z "${ARCHITECTURE}" ]] && export ARCHITECTURE=amd @@ -10,6 +10,7 @@ declare -a DOCKER_VARS=("APP_PORT" "APPS_URL" "ARCHITECTURE" "BUDIBASE_ENVIRONME [[ -z "${MINIO_URL}" ]] && export MINIO_URL=http://localhost:9000 [[ -z "${NODE_ENV}" ]] && export NODE_ENV=production [[ -z "${POSTHOG_TOKEN}" ]] && export POSTHOG_TOKEN=phc_bIjZL7oh2GEUd2vqvTBH8WvrX0fWTFQMs6H5KQxiUxU +[[ -z "${TENANT_FEATURE_FLAGS}" ]] && export TENANT_FEATURE_FLAGS="*:LICENSING,*:USER_GROUPS" [[ -z "${REDIS_URL}" ]] && export REDIS_URL=localhost:6379 [[ -z "${SELF_HOSTED}" ]] && export SELF_HOSTED=1 [[ -z "${WORKER_PORT}" ]] && export WORKER_PORT=4002 diff --git a/lerna.json b/lerna.json index 172a1dbaa4..ee755553d0 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.3.21-alpha.0", + "version": "1.4.8-alpha.3", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/package.json b/package.json index 71acd886d3..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", diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 2bea6f846a..27112626a9 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "1.3.21-alpha.0", + "version": "1.4.8-alpha.3", "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.21-alpha.0", + "@budibase/types": "1.4.8-alpha.3", "@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 d0afb860a1..45ca675fa6 100644 --- a/packages/backend-core/src/db/constants.ts +++ b/packages/backend-core/src/db/constants.ts @@ -45,6 +45,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 09a7ac17d9..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" +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/events/processors/LoggingProcessor.ts b/packages/backend-core/src/events/processors/LoggingProcessor.ts index a517fba09c..d41a82fbb4 100644 --- a/packages/backend-core/src/events/processors/LoggingProcessor.ts +++ b/packages/backend-core/src/events/processors/LoggingProcessor.ts @@ -23,9 +23,11 @@ export default class LoggingProcessor implements EventProcessor { return } let timestampString = getTimestampString(timestamp) - console.log( - `[audit] [tenant=${identity.tenantId}] [identityType=${identity.type}] [identity=${identity.id}] ${timestampString} ${event} ` - ) + let message = `[audit] [tenant=${identity.tenantId}] [identityType=${identity.type}] [identity=${identity.id}] ${timestampString} ${event} ` + if (env.isDev()) { + message = message + `[debug: [properties=${JSON.stringify(properties)}] ]` + } + console.log(message) } async identify(identity: Identity, timestamp?: string | number) { diff --git a/packages/backend-core/src/events/publishers/license.ts b/packages/backend-core/src/events/publishers/license.ts index 1adc71652e..84472e408f 100644 --- a/packages/backend-core/src/events/publishers/license.ts +++ b/packages/backend-core/src/events/publishers/license.ts @@ -1,27 +1,78 @@ import { publishEvent } from "../events" import { Event, - License, LicenseActivatedEvent, - LicenseDowngradedEvent, - LicenseUpdatedEvent, - LicenseUpgradedEvent, + LicensePlanChangedEvent, + LicenseTierChangedEvent, + PlanType, + Account, + LicensePortalOpenedEvent, + LicenseCheckoutSuccessEvent, + LicenseCheckoutOpenedEvent, + LicensePaymentFailedEvent, + LicensePaymentRecoveredEvent, } from "@budibase/types" -// TODO -export async function updgraded(license: License) { - const properties: LicenseUpgradedEvent = {} - await publishEvent(Event.LICENSE_UPGRADED, properties) +export async function tierChanged(account: Account, from: number, to: number) { + const properties: LicenseTierChangedEvent = { + accountId: account.accountId, + to, + from, + } + await publishEvent(Event.LICENSE_TIER_CHANGED, properties) } -// TODO -export async function downgraded(license: License) { - const properties: LicenseDowngradedEvent = {} - await publishEvent(Event.LICENSE_DOWNGRADED, properties) +export async function planChanged( + account: Account, + from: PlanType, + to: PlanType +) { + const properties: LicensePlanChangedEvent = { + accountId: account.accountId, + to, + from, + } + await publishEvent(Event.LICENSE_PLAN_CHANGED, properties) } -// TODO -export async function activated(license: License) { - const properties: LicenseActivatedEvent = {} +export async function activated(account: Account) { + const properties: LicenseActivatedEvent = { + accountId: account.accountId, + } await publishEvent(Event.LICENSE_ACTIVATED, properties) } + +export async function checkoutOpened(account: Account) { + const properties: LicenseCheckoutOpenedEvent = { + accountId: account.accountId, + } + await publishEvent(Event.LICENSE_CHECKOUT_OPENED, properties) +} + +export async function checkoutSuccess(account: Account) { + const properties: LicenseCheckoutSuccessEvent = { + accountId: account.accountId, + } + await publishEvent(Event.LICENSE_CHECKOUT_SUCCESS, properties) +} + +export async function portalOpened(account: Account) { + const properties: LicensePortalOpenedEvent = { + accountId: account.accountId, + } + await publishEvent(Event.LICENSE_PORTAL_OPENED, properties) +} + +export async function paymentFailed(account: Account) { + const properties: LicensePaymentFailedEvent = { + accountId: account.accountId, + } + await publishEvent(Event.LICENSE_PAYMENT_FAILED, properties) +} + +export async function paymentRecovered(account: Account) { + const properties: LicensePaymentRecoveredEvent = { + accountId: account.accountId, + } + await publishEvent(Event.LICENSE_PAYMENT_RECOVERED, properties) +} 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/plugin/utils.js b/packages/backend-core/src/plugin/utils.js index 020fb4484d..ade84bf44a 100644 --- a/packages/backend-core/src/plugin/utils.js +++ b/packages/backend-core/src/plugin/utils.js @@ -75,6 +75,15 @@ function validateDatasource(schema) { }) .unknown(true) .required(), + extra: joi.object().pattern( + joi.string(), + joi.object({ + type: joi.string().required(), + displayName: joi.string().required(), + required: joi.boolean(), + data: joi.object(), + }) + ), }), }) runJoi(validator, schema) 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 d318648a89..ad5c6b5287 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 a98c76e679..fd23d8e402 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.21-alpha.0", + "version": "1.4.8-alpha.3", "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.21-alpha.0", + "@budibase/string-templates": "1.4.8-alpha.3", "@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/avatar": "^3.0.2", 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 a65f840f3d..a11e8ccaf6 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "1.3.21-alpha.0", + "version": "1.4.8-alpha.3", "license": "GPL-3.0", "private": true, "scripts": { @@ -71,10 +71,10 @@ } }, "dependencies": { - "@budibase/bbui": "1.3.21-alpha.0", - "@budibase/client": "1.3.21-alpha.0", - "@budibase/frontend-core": "1.3.21-alpha.0", - "@budibase/string-templates": "1.3.21-alpha.0", + "@budibase/bbui": "1.4.8-alpha.3", + "@budibase/client": "1.4.8-alpha.3", + "@budibase/frontend-core": "1.4.8-alpha.3", + "@budibase/string-templates": "1.4.8-alpha.3", "@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 b6312ef8e8..ca36380077 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, - roles as rolesStore, tables as tablesStore, + roles as rolesStore, } from "stores/backend" import { + makePropSafe, + isJSBinding, decodeJSBinding, encodeJSBinding, - isJSBinding, - makePropSafe, } from "@budibase/string-templates" import { TableNames } from "../constants" import { JSONUtils } from "@budibase/frontend-core" @@ -71,17 +71,19 @@ export const getAuthBindings = () => { runtime: `${safeUser}.${safeOAuth2}.${safeAccessToken}`, readable: `Current User.OAuthToken`, key: "accessToken", + display: { name: "OAuthToken" }, }, ] - bindings = Object.keys(authBindings).map(key => { - const fieldBinding = authBindings[key] + bindings = authBindings.map(fieldBinding => { return { type: "context", runtimeBinding: fieldBinding.runtime, readableBinding: fieldBinding.readable, fieldSchema: { type: "string", name: fieldBinding.key }, providerId: "user", + category: "Current User", + display: fieldBinding.display, } }) return bindings @@ -93,7 +95,7 @@ export const getAuthBindings = () => { * @param {string} prefix A contextual string prefix/path for a user readable binding * @return {object[]} An array containing readable/runtime binding objects */ -export const toBindingsArray = (valueMap, prefix) => { +export const toBindingsArray = (valueMap, prefix, category) => { if (!valueMap) { return [] } @@ -101,11 +103,20 @@ export const toBindingsArray = (valueMap, prefix) => { if (!binding || !valueMap[binding]) { return acc } - acc.push({ + + let config = { type: "context", runtimeBinding: binding, readableBinding: `${prefix}.${binding}`, - }) + icon: "Brackets", + } + + if (category) { + config.category = category + } + + acc.push(config) + return acc }, []) } @@ -382,21 +393,25 @@ export const getUserBindings = () => { const { schema } = getSchemaForTable(TableNames.USERS) const keys = Object.keys(schema).sort() const safeUser = makePropSafe("user") - keys.forEach(key => { + + bindings = keys.reduce((acc, key) => { const fieldSchema = schema[key] - bindings.push({ - type: "context", - runtimeBinding: `${safeUser}.${makePropSafe(key)}`, - readableBinding: `Current User.${key}`, - // Field schema and provider are required to construct relationship - // datasource options, based on bindable properties - fieldSchema, - providerId: "user", - category: "Current User", - icon: "User", - display: fieldSchema, - }) - }) + if (fieldSchema.type !== "link") { + acc.push({ + type: "context", + runtimeBinding: `${safeUser}.${makePropSafe(key)}`, + readableBinding: `Current User.${key}`, + // Field schema and provider are required to construct relationship + // datasource options, based on bindable properties + fieldSchema, + providerId: "user", + category: "Current User", + icon: "User", + }) + } + return acc + }, []) + return bindings } 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)}