diff --git a/.github/workflows/release-selfhost.yml b/.github/workflows/release-selfhost.yml index b746c76bc3..da064f3e32 100644 --- a/.github/workflows/release-selfhost.yml +++ b/.github/workflows/release-selfhost.yml @@ -12,19 +12,28 @@ jobs: runs-on: ubuntu-latest steps: + - name: Fail if branch is not master + if: github.ref != 'refs/heads/master' + run: | + echo "Ref is not master, you must run this job from master." + exit 1 + - uses: actions/checkout@v2 with: node-version: 14.x fetch_depth: 0 + - name: Get the latest budibase release version + id: version + run: | + release_version=$(cat lerna.json | jq -r '.version') + echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV + - name: Tag and release Docker images (Self Host) run: | docker login -u $DOCKER_USER -p $DOCKER_PASSWORD - # Get latest release version - release_version=$(cat lerna.json | jq -r '.version') - echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV - release_tag=v$release_version + release_tag=v${{ env.RELEASE_VERSION }} # Pull apps and worker images docker pull budibase/apps:$release_tag diff --git a/hosting/docker-compose.yaml b/hosting/docker-compose.yaml index 5281155545..7d3e6960dc 100644 --- a/hosting/docker-compose.yaml +++ b/hosting/docker-compose.yaml @@ -75,7 +75,7 @@ services: ports: - "${MAIN_PORT}:10000" container_name: bbproxy - image: proxy-service + image: budibase/proxy environment: - PROXY_RATE_LIMIT_WEBHOOKS_PER_SECOND=10 depends_on: diff --git a/hosting/scripts/build-target-paths.sh b/hosting/scripts/build-target-paths.sh index 4c165d12e7..6d5a8429f8 100644 --- a/hosting/scripts/build-target-paths.sh +++ b/hosting/scripts/build-target-paths.sh @@ -3,15 +3,14 @@ echo ${TARGETBUILD} > /buildtarget.txt if [[ "${TARGETBUILD}" = "aas" ]]; then # Azure AppService uses /home for persisent data & SSH on port 2222 - mkdir -p /home/{search,minio,couch} - mkdir -p /home/couch/{dbs,views} - chown -R couchdb:couchdb /home/couch/ + DATA_DIR=/home + mkdir -p $DATA_DIR/{search,minio,couchdb} + mkdir -p $DATA_DIR/couchdb/{dbs,views} + chown -R couchdb:couchdb $DATA_DIR/couchdb/ apt update apt-get install -y openssh-server - sed -i 's#dir=/opt/couchdb/data/search#dir=/home/search#' /opt/clouseau/clouseau.ini - sed -i 's#/minio/minio server /minio &#/minio/minio server /home/minio &#' /runner.sh - sed -i 's#database_dir = ./data#database_dir = /home/couch/dbs#' /opt/couchdb/etc/default.ini - sed -i 's#view_index_dir = ./data#view_index_dir = /home/couch/views#' /opt/couchdb/etc/default.ini sed -i "s/#Port 22/Port 2222/" /etc/ssh/sshd_config /etc/init.d/ssh restart fi + +sed -i 's#DATA_DIR#$DATA_DIR#' /opt/clouseau/clouseau.ini /opt/couchdb/etc/local.ini diff --git a/hosting/single/Dockerfile b/hosting/single/Dockerfile index b631782ac2..476a6e5e94 100644 --- a/hosting/single/Dockerfile +++ b/hosting/single/Dockerfile @@ -20,10 +20,10 @@ RUN node /pinVersions.js && yarn && yarn build && /cleanup.sh FROM couchdb:3.2.1 # TARGETARCH can be amd64 or arm e.g. docker build --build-arg TARGETARCH=amd64 -ARG TARGETARCH amd64 +ARG TARGETARCH=amd64 #TARGETBUILD can be set to single (for single docker image) or aas (for azure app service) # e.g. docker build --build-arg TARGETBUILD=aas .... -ARG TARGETBUILD single +ARG TARGETBUILD=single ENV TARGETBUILD $TARGETBUILD COPY --from=build /app /app @@ -35,6 +35,7 @@ ENV \ BUDIBASE_ENVIRONMENT=PRODUCTION \ CLUSTER_PORT=80 \ # CUSTOM_DOMAIN=budi001.custom.com \ + DATA_DIR=/data \ DEPLOYMENT_ENVIRONMENT=docker \ MINIO_URL=http://localhost:9000 \ POSTHOG_TOKEN=phc_bIjZL7oh2GEUd2vqvTBH8WvrX0fWTFQMs6H5KQxiUxU \ @@ -114,6 +115,7 @@ RUN chmod +x ./healthcheck.sh ADD hosting/scripts/build-target-paths.sh . RUN chmod +x ./build-target-paths.sh +# Script below sets the path for storing data based on $DATA_DIR # For Azure App Service install SSH & point data locations to /home RUN /build-target-paths.sh diff --git a/hosting/single/clouseau/clouseau.ini b/hosting/single/clouseau/clouseau.ini index 78e43744e5..578a5acafa 100644 --- a/hosting/single/clouseau/clouseau.ini +++ b/hosting/single/clouseau/clouseau.ini @@ -7,7 +7,7 @@ name=clouseau@127.0.0.1 cookie=monster ; the path where you would like to store the search index files -dir=/data/search +dir=DATA_DIR/search ; the number of search indexes that can be open simultaneously max_indexes_open=500 diff --git a/hosting/single/couch/local.ini b/hosting/single/couch/local.ini index 72872a60e1..266c0d4b60 100644 --- a/hosting/single/couch/local.ini +++ b/hosting/single/couch/local.ini @@ -1,5 +1,5 @@ ; CouchDB Configuration Settings [couchdb] -database_dir = /data/couch/dbs -view_index_dir = /data/couch/views +database_dir = DATA_DIR/couch/dbs +view_index_dir = DATA_DIR/couch/views diff --git a/hosting/single/runner.sh b/hosting/single/runner.sh index 9abb2fd093..09387343ba 100644 --- a/hosting/single/runner.sh +++ b/hosting/single/runner.sh @@ -1,7 +1,16 @@ #!/bin/bash -declare -a ENV_VARS=("COUCHDB_USER" "COUCHDB_PASSWORD" "MINIO_ACCESS_KEY" "MINIO_SECRET_KEY" "INTERNAL_API_KEY" "JWT_SECRET" "REDIS_PASSWORD") -if [ -f "/data/.env" ]; then - export $(cat /data/.env | xargs) +declare -a ENV_VARS=("COUCHDB_USER" "COUCHDB_PASSWORD" "DATA_DIR" "MINIO_ACCESS_KEY" "MINIO_SECRET_KEY" "INTERNAL_API_KEY" "JWT_SECRET" "REDIS_PASSWORD") + +# Azure App Service customisations +if [[ "${TARGETBUILD}" = "aas" ]]; then + DATA_DIR=/home + /etc/init.d/ssh start +else + DATA_DIR=${DATA_DIR:-/data} +fi + +if [ -f "${DATA_DIR}/.env" ]; then + export $(cat ${DATA_DIR}/.env | xargs) fi # first randomise any unset environment variables for ENV_VAR in "${ENV_VARS[@]}" @@ -14,21 +23,26 @@ done if [[ -z "${COUCH_DB_URL}" ]]; then export COUCH_DB_URL=http://$COUCHDB_USER:$COUCHDB_PASSWORD@localhost:5984 fi -if [ ! -f "/data/.env" ]; then - touch /data/.env +if [ ! -f "${DATA_DIR}/.env" ]; then + touch ${DATA_DIR}/.env for ENV_VAR in "${ENV_VARS[@]}" do temp=$(eval "echo \$$ENV_VAR") - echo "$ENV_VAR=$temp" >> /data/.env + echo "$ENV_VAR=$temp" >> ${DATA_DIR}/.env done + echo "COUCH_DB_URL=${COUCH_DB_URL}" >> ${DATA_DIR}/.env fi +export COUCH_DB_URL=http://$COUCHDB_USER:$COUCHDB_PASSWORD@localhost:5984 + # make these directories in runner, incase of mount -mkdir -p /data/couch/{dbs,views} /home/couch/{dbs,views} -chown -R couchdb:couchdb /data/couch /home/couch +mkdir -p ${DATA_DIR}/couchdb/{dbs,views} +mkdir -p ${DATA_DIR}/minio +mkdir -p ${DATA_DIR}/search +chown -R couchdb:couchdb ${DATA_DIR}/couchdb redis-server --requirepass $REDIS_PASSWORD & /opt/clouseau/bin/clouseau & -/minio/minio server /data/minio & +/minio/minio server ${DATA_DIR}/minio & /docker-entrypoint.sh /opt/couchdb/bin/couchdb & /etc/init.d/nginx restart if [[ ! -z "${CUSTOM_DOMAIN}" ]]; then diff --git a/lerna.json b/lerna.json index 9e190e668b..92a1d5913f 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.2.28-alpha.0", + "version": "1.2.38", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 23d4c07fe7..780c157aef 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "1.2.28-alpha.0", + "version": "1.2.38", "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.2.28-alpha.0", + "@budibase/types": "^1.2.38", "@techpass/passport-openidconnect": "0.3.2", "aws-sdk": "2.1030.0", "bcrypt": "5.0.1", diff --git a/packages/backend-core/src/cache/appMetadata.js b/packages/backend-core/src/cache/appMetadata.js index b0d9481cbd..a7ff0d2fc1 100644 --- a/packages/backend-core/src/cache/appMetadata.js +++ b/packages/backend-core/src/cache/appMetadata.js @@ -1,6 +1,6 @@ const redis = require("../redis/init") const { doWithDB } = require("../db") -const { DocumentTypes } = require("../db/constants") +const { DocumentType } = require("../db/constants") const AppState = { INVALID: "invalid", @@ -14,7 +14,7 @@ const populateFromDB = async appId => { return doWithDB( appId, db => { - return db.get(DocumentTypes.APP_METADATA) + return db.get(DocumentType.APP_METADATA) }, { skip_setup: true } ) diff --git a/packages/backend-core/src/context/constants.ts b/packages/backend-core/src/context/constants.ts index ef8dcd7821..937ad8f248 100644 --- a/packages/backend-core/src/context/constants.ts +++ b/packages/backend-core/src/context/constants.ts @@ -1,4 +1,4 @@ -export enum ContextKeys { +export enum ContextKey { TENANT_ID = "tenantId", GLOBAL_DB = "globalDb", APP_ID = "appId", diff --git a/packages/backend-core/src/context/index.ts b/packages/backend-core/src/context/index.ts index 1e430f01de..78ce764d55 100644 --- a/packages/backend-core/src/context/index.ts +++ b/packages/backend-core/src/context/index.ts @@ -1,11 +1,11 @@ import env from "../environment" -import { SEPARATOR, DocumentTypes } from "../db/constants" +import { SEPARATOR, DocumentType } from "../db/constants" import cls from "./FunctionContext" import { dangerousGetDB, closeDB } from "../db" import { baseGlobalDBName } from "../tenancy/utils" import { IdentityContext } from "@budibase/types" import { DEFAULT_TENANT_ID as _DEFAULT_TENANT_ID } from "../constants" -import { ContextKeys } from "./constants" +import { ContextKey } from "./constants" import { updateUsing, closeWithUsing, @@ -33,8 +33,8 @@ export const closeTenancy = async () => { } await closeDB(db) // clear from context now that database is closed/task is finished - cls.setOnContext(ContextKeys.TENANT_ID, null) - cls.setOnContext(ContextKeys.GLOBAL_DB, null) + cls.setOnContext(ContextKey.TENANT_ID, null) + cls.setOnContext(ContextKey.GLOBAL_DB, null) } // export const isDefaultTenant = () => { @@ -54,7 +54,7 @@ export const getTenantIDFromAppID = (appId: string) => { return null } const split = appId.split(SEPARATOR) - const hasDev = split[1] === DocumentTypes.DEV + const hasDev = split[1] === DocumentType.DEV if ((hasDev && split.length === 3) || (!hasDev && split.length === 2)) { return null } @@ -83,14 +83,14 @@ export const doInTenant = (tenantId: string | null, task: any) => { // invoke the task return await task() } finally { - await closeWithUsing(ContextKeys.TENANCY_IN_USE, () => { + await closeWithUsing(ContextKey.TENANCY_IN_USE, () => { return closeTenancy() }) } } - const existing = cls.getFromContext(ContextKeys.TENANT_ID) === tenantId - return updateUsing(ContextKeys.TENANCY_IN_USE, existing, internal) + const existing = cls.getFromContext(ContextKey.TENANT_ID) === tenantId + return updateUsing(ContextKey.TENANCY_IN_USE, existing, internal) } export const doInAppContext = (appId: string, task: any) => { @@ -108,7 +108,7 @@ export const doInAppContext = (appId: string, task: any) => { setAppTenantId(appId) } // set the app ID - cls.setOnContext(ContextKeys.APP_ID, appId) + cls.setOnContext(ContextKey.APP_ID, appId) // preserve the identity if (identity) { @@ -118,14 +118,14 @@ export const doInAppContext = (appId: string, task: any) => { // invoke the task return await task() } finally { - await closeWithUsing(ContextKeys.APP_IN_USE, async () => { + await closeWithUsing(ContextKey.APP_IN_USE, async () => { await closeAppDBs() await closeTenancy() }) } } - const existing = cls.getFromContext(ContextKeys.APP_ID) === appId - return updateUsing(ContextKeys.APP_IN_USE, existing, internal) + const existing = cls.getFromContext(ContextKey.APP_ID) === appId + return updateUsing(ContextKey.APP_IN_USE, existing, internal) } export const doInIdentityContext = (identity: IdentityContext, task: any) => { @@ -135,7 +135,7 @@ export const doInIdentityContext = (identity: IdentityContext, task: any) => { async function internal(opts = { existing: false }) { if (!opts.existing) { - cls.setOnContext(ContextKeys.IDENTITY, identity) + cls.setOnContext(ContextKey.IDENTITY, identity) // set the tenant so that doInTenant will preserve identity if (identity.tenantId) { updateTenantId(identity.tenantId) @@ -146,27 +146,27 @@ export const doInIdentityContext = (identity: IdentityContext, task: any) => { // invoke the task return await task() } finally { - await closeWithUsing(ContextKeys.IDENTITY_IN_USE, async () => { + await closeWithUsing(ContextKey.IDENTITY_IN_USE, async () => { setIdentity(null) await closeTenancy() }) } } - const existing = cls.getFromContext(ContextKeys.IDENTITY) - return updateUsing(ContextKeys.IDENTITY_IN_USE, existing, internal) + const existing = cls.getFromContext(ContextKey.IDENTITY) + return updateUsing(ContextKey.IDENTITY_IN_USE, existing, internal) } export const getIdentity = (): IdentityContext | undefined => { try { - return cls.getFromContext(ContextKeys.IDENTITY) + return cls.getFromContext(ContextKey.IDENTITY) } catch (e) { // do nothing - identity is not in context } } export const updateTenantId = (tenantId: string | null) => { - cls.setOnContext(ContextKeys.TENANT_ID, tenantId) + cls.setOnContext(ContextKey.TENANT_ID, tenantId) if (env.USE_COUCH) { setGlobalDB(tenantId) } @@ -176,7 +176,7 @@ export const updateAppId = async (appId: string) => { try { // have to close first, before removing the databases from context await closeAppDBs() - cls.setOnContext(ContextKeys.APP_ID, appId) + cls.setOnContext(ContextKey.APP_ID, appId) } catch (err) { if (env.isTest()) { TEST_APP_ID = appId @@ -189,12 +189,12 @@ export const updateAppId = async (appId: string) => { export const setGlobalDB = (tenantId: string | null) => { const dbName = baseGlobalDBName(tenantId) const db = dangerousGetDB(dbName) - cls.setOnContext(ContextKeys.GLOBAL_DB, db) + cls.setOnContext(ContextKey.GLOBAL_DB, db) return db } export const getGlobalDB = () => { - const db = cls.getFromContext(ContextKeys.GLOBAL_DB) + const db = cls.getFromContext(ContextKey.GLOBAL_DB) if (!db) { throw new Error("Global DB not found") } @@ -202,7 +202,7 @@ export const getGlobalDB = () => { } export const isTenantIdSet = () => { - const tenantId = cls.getFromContext(ContextKeys.TENANT_ID) + const tenantId = cls.getFromContext(ContextKey.TENANT_ID) return !!tenantId } @@ -210,7 +210,7 @@ export const getTenantId = () => { if (!isMultiTenant()) { return DEFAULT_TENANT_ID } - const tenantId = cls.getFromContext(ContextKeys.TENANT_ID) + const tenantId = cls.getFromContext(ContextKey.TENANT_ID) if (!tenantId) { throw new Error("Tenant id not found") } @@ -218,7 +218,7 @@ export const getTenantId = () => { } export const getAppId = () => { - const foundId = cls.getFromContext(ContextKeys.APP_ID) + const foundId = cls.getFromContext(ContextKey.APP_ID) if (!foundId && env.isTest() && TEST_APP_ID) { return TEST_APP_ID } else { @@ -231,7 +231,7 @@ export const getAppId = () => { * contained, dev or prod. */ export const getAppDB = (opts?: any) => { - return getContextDB(ContextKeys.CURRENT_DB, opts) + return getContextDB(ContextKey.CURRENT_DB, opts) } /** @@ -239,7 +239,7 @@ export const getAppDB = (opts?: any) => { * contained a development app ID, this will open the prod one. */ export const getProdAppDB = (opts?: any) => { - return getContextDB(ContextKeys.PROD_DB, opts) + return getContextDB(ContextKey.PROD_DB, opts) } /** @@ -247,5 +247,5 @@ export const getProdAppDB = (opts?: any) => { * contained a prod app ID, this will open the dev one. */ export const getDevAppDB = (opts?: any) => { - return getContextDB(ContextKeys.DEV_DB, opts) + return getContextDB(ContextKey.DEV_DB, opts) } diff --git a/packages/backend-core/src/context/utils.ts b/packages/backend-core/src/context/utils.ts index 62693f18e8..6e7100b594 100644 --- a/packages/backend-core/src/context/utils.ts +++ b/packages/backend-core/src/context/utils.ts @@ -6,7 +6,7 @@ import { } from "./index" import cls from "./FunctionContext" import { IdentityContext } from "@budibase/types" -import { ContextKeys } from "./constants" +import { ContextKey } from "./constants" import { dangerousGetDB, closeDB } from "../db" import { isEqual } from "lodash" import { getDevelopmentAppID, getProdAppID } from "../db/conversions" @@ -47,17 +47,13 @@ export const setAppTenantId = (appId: string) => { } export const setIdentity = (identity: IdentityContext | null) => { - cls.setOnContext(ContextKeys.IDENTITY, identity) + cls.setOnContext(ContextKey.IDENTITY, identity) } // this function makes sure the PouchDB objects are closed and // fully deleted when finished - this protects against memory leaks export async function closeAppDBs() { - const dbKeys = [ - ContextKeys.CURRENT_DB, - ContextKeys.PROD_DB, - ContextKeys.DEV_DB, - ] + const dbKeys = [ContextKey.CURRENT_DB, ContextKey.PROD_DB, ContextKey.DEV_DB] for (let dbKey of dbKeys) { const db = cls.getFromContext(dbKey) if (!db) { @@ -68,16 +64,16 @@ export async function closeAppDBs() { cls.setOnContext(dbKey, null) } // clear the app ID now that the databases are closed - if (cls.getFromContext(ContextKeys.APP_ID)) { - cls.setOnContext(ContextKeys.APP_ID, null) + if (cls.getFromContext(ContextKey.APP_ID)) { + cls.setOnContext(ContextKey.APP_ID, null) } - if (cls.getFromContext(ContextKeys.DB_OPTS)) { - cls.setOnContext(ContextKeys.DB_OPTS, null) + if (cls.getFromContext(ContextKey.DB_OPTS)) { + cls.setOnContext(ContextKey.DB_OPTS, null) } } export function getContextDB(key: string, opts: any) { - const dbOptsKey = `${key}${ContextKeys.DB_OPTS}` + const dbOptsKey = `${key}${ContextKey.DB_OPTS}` let storedOpts = cls.getFromContext(dbOptsKey) let db = cls.getFromContext(key) if (db && isEqual(opts, storedOpts)) { @@ -88,13 +84,13 @@ export function getContextDB(key: string, opts: any) { let toUseAppId switch (key) { - case ContextKeys.CURRENT_DB: + case ContextKey.CURRENT_DB: toUseAppId = appId break - case ContextKeys.PROD_DB: + case ContextKey.PROD_DB: toUseAppId = getProdAppID(appId) break - case ContextKeys.DEV_DB: + case ContextKey.DEV_DB: toUseAppId = getDevelopmentAppID(appId) break } diff --git a/packages/backend-core/src/db/constants.ts b/packages/backend-core/src/db/constants.ts index 9c6be25424..460476da24 100644 --- a/packages/backend-core/src/db/constants.ts +++ b/packages/backend-core/src/db/constants.ts @@ -4,13 +4,13 @@ export const UNICODE_MAX = "\ufff0" /** * Can be used to create a few different forms of querying a view. */ -export enum AutomationViewModes { +export enum AutomationViewMode { ALL = "all", AUTOMATION = "automation", STATUS = "status", } -export enum ViewNames { +export enum ViewName { USER_BY_APP = "by_app", USER_BY_EMAIL = "by_email2", BY_API_KEY = "by_api_key", @@ -21,13 +21,13 @@ export enum ViewNames { } export const DeprecatedViews = { - [ViewNames.USER_BY_EMAIL]: [ + [ViewName.USER_BY_EMAIL]: [ // removed due to inaccuracy in view doc filter logic "by_email", ], } -export enum DocumentTypes { +export enum DocumentType { USER = "us", GROUP = "gr", WORKSPACE = "workspace", @@ -62,6 +62,6 @@ export const StaticDatabases = { }, } -export const APP_PREFIX = exports.DocumentTypes.APP + exports.SEPARATOR -export const APP_DEV = exports.DocumentTypes.APP_DEV + exports.SEPARATOR +export const APP_PREFIX = DocumentType.APP + SEPARATOR +export const APP_DEV = DocumentType.APP_DEV + SEPARATOR export const APP_DEV_PREFIX = APP_DEV diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts index 8ab6fa6e98..321ebd7f58 100644 --- a/packages/backend-core/src/db/utils.ts +++ b/packages/backend-core/src/db/utils.ts @@ -1,7 +1,7 @@ import { newid } from "../hashing" import { DEFAULT_TENANT_ID, Configs } from "../constants" import env from "../environment" -import { SEPARATOR, DocumentTypes, UNICODE_MAX, ViewNames } from "./constants" +import { SEPARATOR, DocumentType, UNICODE_MAX, ViewName } from "./constants" import { getTenantId, getGlobalDBName, getGlobalDB } from "../tenancy" import fetch from "node-fetch" import { doWithDB, allDbs } from "./index" @@ -58,7 +58,7 @@ export function getDocParams( /** * Retrieve the correct index for a view based on default design DB. */ -export function getQueryIndex(viewName: ViewNames) { +export function getQueryIndex(viewName: ViewName) { return `database/${viewName}` } @@ -67,7 +67,7 @@ export function getQueryIndex(viewName: ViewNames) { * @returns {string} The new workspace ID which the workspace doc can be stored under. */ export function generateWorkspaceID() { - return `${DocumentTypes.WORKSPACE}${SEPARATOR}${newid()}` + return `${DocumentType.WORKSPACE}${SEPARATOR}${newid()}` } /** @@ -76,8 +76,8 @@ export function generateWorkspaceID() { export function getWorkspaceParams(id = "", otherProps = {}) { return { ...otherProps, - startkey: `${DocumentTypes.WORKSPACE}${SEPARATOR}${id}`, - endkey: `${DocumentTypes.WORKSPACE}${SEPARATOR}${id}${UNICODE_MAX}`, + startkey: `${DocumentType.WORKSPACE}${SEPARATOR}${id}`, + endkey: `${DocumentType.WORKSPACE}${SEPARATOR}${id}${UNICODE_MAX}`, } } @@ -86,7 +86,7 @@ export function getWorkspaceParams(id = "", otherProps = {}) { * @returns {string} The new user ID which the user doc can be stored under. */ export function generateGlobalUserID(id?: any) { - return `${DocumentTypes.USER}${SEPARATOR}${id || newid()}` + return `${DocumentType.USER}${SEPARATOR}${id || newid()}` } /** @@ -102,8 +102,8 @@ export function getGlobalUserParams(globalId: any, otherProps: any = {}) { // need to include this incase pagination startkey: startkey ? startkey - : `${DocumentTypes.USER}${SEPARATOR}${globalId}`, - endkey: `${DocumentTypes.USER}${SEPARATOR}${globalId}${UNICODE_MAX}`, + : `${DocumentType.USER}${SEPARATOR}${globalId}`, + endkey: `${DocumentType.USER}${SEPARATOR}${globalId}${UNICODE_MAX}`, } } @@ -121,7 +121,7 @@ export function getUsersByAppParams(appId: any, otherProps: any = {}) { * @param ownerId The owner/user of the template, this could be global or a workspace level. */ export function generateTemplateID(ownerId: any) { - return `${DocumentTypes.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}${newid()}` + return `${DocumentType.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}${newid()}` } export function generateAppUserID(prodAppId: string, userId: string) { @@ -143,7 +143,7 @@ export function getTemplateParams( if (templateId) { final = templateId } else { - final = `${DocumentTypes.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}` + final = `${DocumentType.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}` } return { ...otherProps, @@ -157,14 +157,14 @@ export function getTemplateParams( * @returns {string} The new role ID which the role doc can be stored under. */ export function generateRoleID(id: any) { - return `${DocumentTypes.ROLE}${SEPARATOR}${id || newid()}` + return `${DocumentType.ROLE}${SEPARATOR}${id || newid()}` } /** * Gets parameters for retrieving a role, this is a utility function for the getDocParams function. */ export function getRoleParams(roleId = null, otherProps = {}) { - return getDocParams(DocumentTypes.ROLE, roleId, otherProps) + return getDocParams(DocumentType.ROLE, roleId, otherProps) } export function getStartEndKeyURL(base: any, baseKey: any, tenantId = null) { @@ -211,9 +211,9 @@ export async function getAllDbs(opts = { efficient: false }) { await addDbs(couchUrl) } else { // get prod apps - await addDbs(getStartEndKeyURL(couchUrl, DocumentTypes.APP, tenantId)) + await addDbs(getStartEndKeyURL(couchUrl, DocumentType.APP, tenantId)) // get dev apps - await addDbs(getStartEndKeyURL(couchUrl, DocumentTypes.APP_DEV, tenantId)) + await addDbs(getStartEndKeyURL(couchUrl, DocumentType.APP_DEV, tenantId)) // add global db name dbs.push(getGlobalDBName(tenantId)) } @@ -233,14 +233,18 @@ export async function getAllApps({ dev, all, idsOnly, efficient }: any = {}) { } let dbs = await getAllDbs({ efficient }) const appDbNames = dbs.filter((dbName: any) => { + if (env.isTest() && !dbName) { + return false + } + const split = dbName.split(SEPARATOR) // it is an app, check the tenantId - if (split[0] === DocumentTypes.APP) { + if (split[0] === DocumentType.APP) { // tenantId is always right before the UUID const possibleTenantId = split[split.length - 2] const noTenantId = - split.length === 2 || possibleTenantId === DocumentTypes.DEV + split.length === 2 || possibleTenantId === DocumentType.DEV return ( (tenantId === DEFAULT_TENANT_ID && noTenantId) || @@ -326,7 +330,7 @@ export async function dbExists(dbName: any) { export const generateConfigID = ({ type, workspace, user }: any) => { const scope = [type, workspace, user].filter(Boolean).join(SEPARATOR) - return `${DocumentTypes.CONFIG}${SEPARATOR}${scope}` + return `${DocumentType.CONFIG}${SEPARATOR}${scope}` } /** @@ -340,8 +344,8 @@ export const getConfigParams = ( return { ...otherProps, - startkey: `${DocumentTypes.CONFIG}${SEPARATOR}${scope}`, - endkey: `${DocumentTypes.CONFIG}${SEPARATOR}${scope}${UNICODE_MAX}`, + startkey: `${DocumentType.CONFIG}${SEPARATOR}${scope}`, + endkey: `${DocumentType.CONFIG}${SEPARATOR}${scope}${UNICODE_MAX}`, } } @@ -350,7 +354,7 @@ export const getConfigParams = ( * @returns {string} The new dev info ID which info for dev (like api key) can be stored under. */ export const generateDevInfoID = (userId: any) => { - return `${DocumentTypes.DEV_INFO}${SEPARATOR}${userId}` + return `${DocumentType.DEV_INFO}${SEPARATOR}${userId}` } /** diff --git a/packages/backend-core/src/db/views.js b/packages/backend-core/src/db/views.js index baf1807ca5..3a45611a8f 100644 --- a/packages/backend-core/src/db/views.js +++ b/packages/backend-core/src/db/views.js @@ -1,6 +1,6 @@ const { - DocumentTypes, - ViewNames, + DocumentType, + ViewName, DeprecatedViews, SEPARATOR, } = require("./utils") @@ -44,14 +44,14 @@ exports.createNewUserEmailView = async () => { const view = { // if using variables in a map function need to inject them before use map: `function(doc) { - if (doc._id.startsWith("${DocumentTypes.USER}${SEPARATOR}")) { + if (doc._id.startsWith("${DocumentType.USER}${SEPARATOR}")) { emit(doc.email.toLowerCase(), doc._id) } }`, } designDoc.views = { ...designDoc.views, - [ViewNames.USER_BY_EMAIL]: view, + [ViewName.USER_BY_EMAIL]: view, } await db.put(designDoc) } @@ -68,7 +68,7 @@ exports.createUserAppView = async () => { const view = { // if using variables in a map function need to inject them before use map: `function(doc) { - if (doc._id.startsWith("${DocumentTypes.USER}${SEPARATOR}") && doc.roles) { + if (doc._id.startsWith("${DocumentType.USER}${SEPARATOR}") && doc.roles) { for (let prodAppId of Object.keys(doc.roles)) { let emitted = prodAppId + "${SEPARATOR}" + doc._id emit(emitted, null) @@ -78,7 +78,7 @@ exports.createUserAppView = async () => { } designDoc.views = { ...designDoc.views, - [ViewNames.USER_BY_APP]: view, + [ViewName.USER_BY_APP]: view, } await db.put(designDoc) } @@ -93,14 +93,14 @@ exports.createApiKeyView = async () => { } const view = { map: `function(doc) { - if (doc._id.startsWith("${DocumentTypes.DEV_INFO}") && doc.apiKey) { + if (doc._id.startsWith("${DocumentType.DEV_INFO}") && doc.apiKey) { emit(doc.apiKey, doc.userId) } }`, } designDoc.views = { ...designDoc.views, - [ViewNames.BY_API_KEY]: view, + [ViewName.BY_API_KEY]: view, } await db.put(designDoc) } @@ -123,17 +123,17 @@ exports.createUserBuildersView = async () => { } designDoc.views = { ...designDoc.views, - [ViewNames.USER_BY_BUILDERS]: view, + [ViewName.USER_BY_BUILDERS]: view, } await db.put(designDoc) } exports.queryGlobalView = async (viewName, params, db = null) => { const CreateFuncByName = { - [ViewNames.USER_BY_EMAIL]: exports.createNewUserEmailView, - [ViewNames.BY_API_KEY]: exports.createApiKeyView, - [ViewNames.USER_BY_BUILDERS]: exports.createUserBuildersView, - [ViewNames.USER_BY_APP]: exports.createUserAppView, + [ViewName.USER_BY_EMAIL]: exports.createNewUserEmailView, + [ViewName.BY_API_KEY]: exports.createApiKeyView, + [ViewName.USER_BY_BUILDERS]: exports.createUserBuildersView, + [ViewName.USER_BY_APP]: exports.createUserAppView, } // can pass DB in if working with something specific if (!db) { diff --git a/packages/backend-core/src/events/emit/BBEventEmitter.ts b/packages/backend-core/src/events/emit/BBEventEmitter.ts deleted file mode 100644 index 41668858e1..0000000000 --- a/packages/backend-core/src/events/emit/BBEventEmitter.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { EventEmitter } from "events" -import * as context from "../../context" -import { Identity, Event } from "@budibase/types" - -export interface EmittedEvent { - tenantId: string - identity: Identity - appId: string | undefined - properties: any -} - -class BBEventEmitter extends EventEmitter { - emitEvent(event: Event, properties: any, identity: Identity) { - const tenantId = context.getTenantId() - const appId = context.getAppId() - - const emittedEvent: EmittedEvent = { - tenantId, - identity, - appId, - properties, - } - this.emit(event, emittedEvent) - } -} - -export const emitter = new BBEventEmitter() diff --git a/packages/backend-core/src/events/emit/index.ts b/packages/backend-core/src/events/emit/index.ts deleted file mode 100644 index 60e47f7a13..0000000000 --- a/packages/backend-core/src/events/emit/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./BBEventEmitter" diff --git a/packages/backend-core/src/events/events.ts b/packages/backend-core/src/events/events.ts index 63fedf4754..cda90d12c9 100644 --- a/packages/backend-core/src/events/events.ts +++ b/packages/backend-core/src/events/events.ts @@ -2,41 +2,6 @@ import { Event } from "@budibase/types" import { processors } from "./processors" import * as identification from "./identification" import * as backfill from "./backfill" -import { emitter, EmittedEvent } from "./emit" -import * as context from "../context" -import * as logging from "../logging" - -const USE_EMITTER: any[] = [ - Event.SERVED_BUILDER, - Event.SERVED_APP, - Event.SERVED_APP_PREVIEW, -] - -for (let event of USE_EMITTER) { - emitter.on(event, async (props: EmittedEvent) => { - try { - await context.doInTenant(props.tenantId, async () => { - if (props.appId) { - await context.doInAppContext(props.appId, async () => { - await processors.processEvent( - event as Event, - props.identity, - props.properties - ) - }) - } else { - await processors.processEvent( - event as Event, - props.identity, - props.properties - ) - } - }) - } catch (e) { - logging.logAlert(`Unable to process async event ${event}`, e) - } - }) -} export const publishEvent = async ( event: Event, @@ -46,11 +11,6 @@ export const publishEvent = async ( // in future this should use async events via a distributed queue. const identity = await identification.getCurrentIdentity() - if (USE_EMITTER.includes(event)) { - emitter.emitEvent(event, properties, identity) - return - } - const backfilling = await backfill.isBackfillingEvent(event) // no backfill - send the event and exit if (!backfilling) { diff --git a/packages/backend-core/src/events/publishers/serve.ts b/packages/backend-core/src/events/publishers/serve.ts index 13afede029..128e0b9b11 100644 --- a/packages/backend-core/src/events/publishers/serve.ts +++ b/packages/backend-core/src/events/publishers/serve.ts @@ -7,22 +7,26 @@ import { AppServedEvent, } from "@budibase/types" -export async function servedBuilder() { - const properties: BuilderServedEvent = {} +export async function servedBuilder(timezone: string) { + const properties: BuilderServedEvent = { + timezone, + } await publishEvent(Event.SERVED_BUILDER, properties) } -export async function servedApp(app: App) { +export async function servedApp(app: App, timezone: string) { const properties: AppServedEvent = { appVersion: app.version, + timezone, } await publishEvent(Event.SERVED_APP, properties) } -export async function servedAppPreview(app: App) { +export async function servedAppPreview(app: App, timezone: string) { const properties: AppPreviewServedEvent = { appId: app.appId, appVersion: app.version, + timezone, } await publishEvent(Event.SERVED_APP_PREVIEW, properties) } diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index e585d4b6c3..6d2e8dcd10 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -1,4 +1,5 @@ import errors from "./errors" + const errorClasses = errors.errors import * as events from "./events" import * as migrations from "./migrations" diff --git a/packages/backend-core/src/middleware/authenticated.ts b/packages/backend-core/src/middleware/authenticated.ts index 3406b00812..b51ead46b9 100644 --- a/packages/backend-core/src/middleware/authenticated.ts +++ b/packages/backend-core/src/middleware/authenticated.ts @@ -4,7 +4,7 @@ import { getUser } from "../cache/user" import { getSession, updateSessionTTL } from "../security/sessions" import { buildMatcherRegex, matches } from "./matchers" import { SEPARATOR } from "../db/constants" -import { ViewNames } from "../db/utils" +import { ViewName } from "../db/utils" import { queryGlobalView } from "../db/views" import { getGlobalDB, doInTenant } from "../tenancy" import { decrypt } from "../security/encryption" @@ -43,7 +43,7 @@ async function checkApiKey(apiKey: string, populateUser?: Function) { const db = getGlobalDB() // api key is encrypted in the database const userId = await queryGlobalView( - ViewNames.BY_API_KEY, + ViewName.BY_API_KEY, { key: apiKey, }, diff --git a/packages/backend-core/src/migrations/migrations.ts b/packages/backend-core/src/migrations/migrations.ts index 2e4ef0da76..ca238ff80e 100644 --- a/packages/backend-core/src/migrations/migrations.ts +++ b/packages/backend-core/src/migrations/migrations.ts @@ -1,6 +1,6 @@ import { DEFAULT_TENANT_ID } from "../constants" import { doWithDB } from "../db" -import { DocumentTypes, StaticDatabases } from "../db/constants" +import { DocumentType, StaticDatabases } from "../db/constants" import { getAllApps } from "../db/utils" import environment from "../environment" import { @@ -21,10 +21,10 @@ import { export const getMigrationsDoc = async (db: any) => { // get the migrations doc try { - return await db.get(DocumentTypes.MIGRATIONS) + return await db.get(DocumentType.MIGRATIONS) } catch (err: any) { if (err.status && err.status === 404) { - return { _id: DocumentTypes.MIGRATIONS } + return { _id: DocumentType.MIGRATIONS } } else { console.error(err) throw err diff --git a/packages/backend-core/src/security/roles.js b/packages/backend-core/src/security/roles.js index 30869da68e..983aebf676 100644 --- a/packages/backend-core/src/security/roles.js +++ b/packages/backend-core/src/security/roles.js @@ -3,7 +3,7 @@ const { BUILTIN_PERMISSION_IDS, PermissionLevels } = require("./permissions") const { generateRoleID, getRoleParams, - DocumentTypes, + DocumentType, SEPARATOR, } = require("../db/utils") const { getAppDB } = require("../context") @@ -338,7 +338,7 @@ class AccessController { * Adds the "role_" for builtin role IDs which are to be written to the DB (for permissions). */ exports.getDBRoleID = roleId => { - if (roleId.startsWith(DocumentTypes.ROLE)) { + if (roleId.startsWith(DocumentType.ROLE)) { return roleId } return generateRoleID(roleId) @@ -349,8 +349,8 @@ exports.getDBRoleID = roleId => { */ exports.getExternalRoleID = roleId => { // for built in roles we want to remove the DB role ID element (role_) - if (roleId.startsWith(DocumentTypes.ROLE) && isBuiltin(roleId)) { - return roleId.split(`${DocumentTypes.ROLE}${SEPARATOR}`)[1] + if (roleId.startsWith(DocumentType.ROLE) && isBuiltin(roleId)) { + return roleId.split(`${DocumentType.ROLE}${SEPARATOR}`)[1] } return roleId } diff --git a/packages/backend-core/src/users.js b/packages/backend-core/src/users.js index 34d546a8bb..de5ce238c1 100644 --- a/packages/backend-core/src/users.js +++ b/packages/backend-core/src/users.js @@ -1,5 +1,5 @@ const { - ViewNames, + ViewName, getUsersByAppParams, getProdAppID, generateAppUserID, @@ -18,7 +18,7 @@ exports.getGlobalUserByEmail = async email => { throw "Must supply an email address to view" } - return await queryGlobalView(ViewNames.USER_BY_EMAIL, { + return await queryGlobalView(ViewName.USER_BY_EMAIL, { key: email.toLowerCase(), include_docs: true, }) @@ -32,7 +32,7 @@ exports.searchGlobalUsersByApp = async (appId, opts) => { include_docs: true, }) params.startkey = opts && opts.startkey ? opts.startkey : params.startkey - let response = await queryGlobalView(ViewNames.USER_BY_APP, params) + let response = await queryGlobalView(ViewName.USER_BY_APP, params) if (!response) { response = [] } @@ -56,7 +56,7 @@ exports.searchGlobalUsersByEmail = async (email, opts) => { const lcEmail = email.toLowerCase() // handle if passing up startkey for pagination const startkey = opts && opts.startkey ? opts.startkey : lcEmail - let response = await queryGlobalView(ViewNames.USER_BY_EMAIL, { + let response = await queryGlobalView(ViewName.USER_BY_EMAIL, { ...opts, startkey, endkey: `${lcEmail}${UNICODE_MAX}`, diff --git a/packages/backend-core/src/utils.js b/packages/backend-core/src/utils.js index 1e143968d9..0587267e9a 100644 --- a/packages/backend-core/src/utils.js +++ b/packages/backend-core/src/utils.js @@ -1,9 +1,4 @@ -const { - DocumentTypes, - SEPARATOR, - ViewNames, - getAllApps, -} = require("./db/utils") +const { DocumentType, SEPARATOR, ViewName, getAllApps } = require("./db/utils") const jwt = require("jsonwebtoken") const { options } = require("./middleware/passport/jwt") const { queryGlobalView } = require("./db/views") @@ -17,7 +12,7 @@ const { const events = require("./events") const tenancy = require("./tenancy") -const APP_PREFIX = DocumentTypes.APP + SEPARATOR +const APP_PREFIX = DocumentType.APP + SEPARATOR const PROD_APP_PREFIX = "/app/" function confirmAppId(possibleAppId) { @@ -154,7 +149,7 @@ exports.isClient = ctx => { } const getBuilders = async () => { - const builders = await queryGlobalView(ViewNames.USER_BY_BUILDERS, { + const builders = await queryGlobalView(ViewName.USER_BY_BUILDERS, { include_docs: false, }) diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 431570af14..2483b8b662 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.2.28-alpha.0", + "version": "1.2.38", "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.2.28-alpha.0", + "@budibase/string-templates": "^1.2.38", "@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/avatar": "^3.0.2", diff --git a/packages/bbui/src/Form/Core/DatePicker.svelte b/packages/bbui/src/Form/Core/DatePicker.svelte index 39a7d9d626..d75350d8e8 100644 --- a/packages/bbui/src/Form/Core/DatePicker.svelte +++ b/packages/bbui/src/Form/Core/DatePicker.svelte @@ -16,6 +16,7 @@ export let appendTo = undefined export let timeOnly = false export let ignoreTimezones = false + export let time24hr = false const dispatch = createEventDispatcher() const flatpickrId = `${uuid()}-wrapper` @@ -37,6 +38,7 @@ enableTime: timeOnly || enableTime || false, noCalendar: timeOnly || false, altInput: true, + time_24hr: time24hr || false, altFormat: timeOnly ? "H:i" : enableTime ? "F j Y, H:i" : "F j, Y", wrap: true, appendTo, @@ -49,6 +51,12 @@ }, } + $: redrawOptions = { + timeOnly, + enableTime, + time24hr, + } + const handleChange = event => { const [dates] = event.detail const noTimezone = enableTime && !timeOnly && ignoreTimezones @@ -142,7 +150,7 @@ } -{#key timeOnly} +{#key redrawOptions} { cy.get(interact.SPECTRUM_ICON).click({ force: true }) }) cy.get(interact.SPECTRUM_MENU).within(() => { - cy.get(interact.SPECTRUM_MENU_ITEM).contains("Force Password Reset").click({ force: true }) + cy.get(interact.SPECTRUM_MENU_ITEM).contains("Force password reset").click({ force: true }) }) cy.get(interact.SPECTRUM_DIALOG_GRID) @@ -41,10 +41,25 @@ filterTests(["smoke", "all"], () => { cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).eq(i).type("test") } cy.get(interact.SPECTRUM_BUTTON).contains("Reset your password").click({ force: true }) + //cy.logoutNoAppGrid() + }) + + it("should verify Standard Portal", () => { + // Development access should be disabled (Admin access is already disabled) + cy.login() + cy.setUserRole("bbuser", "App User") + bbUserLogin() + + // Verify Standard Portal + cy.get(interact.SPECTRUM_SIDENAV).should('not.exist') // No config sections + cy.get(interact.CREATE_APP_BUTTON).should('not.exist') // No create app button + cy.get(".app").should('not.exist') // No apps -> no roles assigned to user + cy.get(interact.CONTAINER).should('contain', bbUserEmail) // Message containing users email + cy.logoutNoAppGrid() }) - xit("should verify Admin Portal", () => { + it("should verify Admin Portal", () => { cy.login() // Configure user role cy.setUserRole("bbuser", "Admin") @@ -86,21 +101,6 @@ filterTests(["smoke", "all"], () => { cy.logOut() }) - it("should verify Standard Portal", () => { - // Development access should be disabled (Admin access is already disabled) - cy.login() - cy.setUserRole("bbuser", "App User") - bbUserLogin() - - // Verify Standard Portal - cy.get(interact.SPECTRUM_SIDENAV).should('not.exist') // No config sections - cy.get(interact.CREATE_APP_BUTTON).should('not.exist') // No create app button - cy.get(".app").should('not.exist') // No apps -> no roles assigned to user - cy.get(interact.CONTAINER).should('contain', bbUserEmail) // Message containing users email - - cy.logoutNoAppGrid() - }) - const bbUserLogin = () => { // Login as bbuser cy.logOut() diff --git a/packages/builder/cypress/integration/adminAndManagement/userManagement.spec.js b/packages/builder/cypress/integration/adminAndManagement/userManagement.spec.js index 3dfffb1d39..5a864e3bb3 100644 --- a/packages/builder/cypress/integration/adminAndManagement/userManagement.spec.js +++ b/packages/builder/cypress/integration/adminAndManagement/userManagement.spec.js @@ -17,7 +17,7 @@ filterTests(["smoke", "all"], () => { it("should confirm App User role for a New User", () => { cy.contains("bbuser").click() - cy.get(".spectrum-Form-itemField").eq(2).should('contain', 'App User') + cy.get(".spectrum-Form-itemField").eq(3).should('contain', 'App User') // User should not have app access cy.get(interact.LIST_ITEMS, { timeout: 500 }).should("contain", "No apps") @@ -166,12 +166,12 @@ filterTests(["smoke", "all"], () => { it("Should edit user details within user details page", () => { // Add First name - cy.get(interact.FIELD, { timeout: 1000 }).eq(0).within(() => { + cy.get(interact.FIELD, { timeout: 1000 }).eq(1).within(() => { cy.wait(500) cy.get(interact.SPECTRUM_TEXTFIELD_INPUT, { timeout: 1000 }).wait(500).clear().click().type("bb") }) // Add Last name - cy.get(interact.FIELD, { timeout: 1000 }).eq(1).within(() => { + cy.get(interact.FIELD, { timeout: 1000 }).eq(2).within(() => { cy.wait(500) cy.get(interact.SPECTRUM_TEXTFIELD_INPUT, { timeout: 1000 }).click().wait(500).clear().type("test") }) @@ -180,10 +180,10 @@ filterTests(["smoke", "all"], () => { cy.reload() // Confirm details have been saved - cy.get(interact.FIELD, { timeout: 1000 }).eq(0).within(() => { + cy.get(interact.FIELD, { timeout: 1000 }).eq(1).within(() => { cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).should('have.value', "bb") }) - cy.get(interact.FIELD, { timeout: 1000 }).eq(1).within(() => { + cy.get(interact.FIELD, { timeout: 1000 }).eq(2).within(() => { cy.get(interact.SPECTRUM_TEXTFIELD_INPUT, { timeout: 1000 }).should('have.value', "test") }) }) @@ -193,13 +193,14 @@ filterTests(["smoke", "all"], () => { cy.get(interact.SPECTRUM_ICON).click({ force: true }) }) cy.get(interact.SPECTRUM_MENU).within(() => { - cy.get(interact.SPECTRUM_MENU_ITEM).contains("Force Password Reset").click({ force: true }) + cy.get(interact.SPECTRUM_MENU_ITEM).contains("Force password reset").click({ force: true }) }) // Reset password modal cy.get(interact.SPECTRUM_DIALOG_GRID) .find(interact.SPECTRUM_TEXTFIELD_INPUT).invoke('val').as('pwd') cy.get(interact.SPECTRUM_BUTTON).contains("Reset password").click({ force: true }) + cy.get(interact.SPECTRUM_BUTTON).contains("Reset password").should('not.exist') // Logout, then login with new password cy.logOut() @@ -214,6 +215,7 @@ filterTests(["smoke", "all"], () => { cy.get(interact.SPECTRUM_BUTTON).contains("Reset your password").click({ force: true }) // Confirm user logged in afer password change + cy.login("bbuser@test.com", "test") cy.get(".avatar > .icon").click({ force: true }) cy.get(".spectrum-Menu-item").contains("Update user information").click({ force: true }) diff --git a/packages/builder/cypress/integration/adminAndManagement/userSettings.spec.js b/packages/builder/cypress/integration/adminAndManagement/userSettings.spec.js index d388d9c7a5..a2b0d32d02 100644 --- a/packages/builder/cypress/integration/adminAndManagement/userSettings.spec.js +++ b/packages/builder/cypress/integration/adminAndManagement/userSettings.spec.js @@ -19,10 +19,10 @@ filterTests(["smoke", "all"], () => { cy.contains("Users").click() cy.contains("test@test.com").click() - cy.get(interact.FIELD, { timeout: 1000 }).eq(0).within(() => { + cy.get(interact.FIELD, { timeout: 1000 }).eq(1).within(() => { cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).should('have.value', fname) }) - cy.get(interact.FIELD).eq(1).within(() => { + cy.get(interact.FIELD).eq(2).within(() => { cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).should('have.value', lname) }) }) @@ -72,7 +72,7 @@ filterTests(["smoke", "all"], () => { }) // Logout & in with new password - cy.logOut() + //cy.logOut() cy.login("test@test.com", "newpwd") }) @@ -90,7 +90,6 @@ filterTests(["smoke", "all"], () => { cy.get(interact.SPECTRUM_MENU_ITEM).contains("Open developer mode").click({ force: true }) cy.get(interact.SPECTRUM_SIDENAV).should('exist') // config sections available cy.get(interact.CREATE_APP_BUTTON).should('exist') // create app button available - cy.get(interact.APP_TABLE).should('exist') // App table available }) after(() => { diff --git a/packages/builder/cypress/integration/createApp.spec.js b/packages/builder/cypress/integration/createApp.spec.js index 516489b093..179741e21a 100644 --- a/packages/builder/cypress/integration/createApp.spec.js +++ b/packages/builder/cypress/integration/createApp.spec.js @@ -94,6 +94,7 @@ filterTests(['smoke', 'all'], () => { }) it("should create the first application from scratch with a default name", () => { + cy.updateUserInformation("", "") cy.createApp("", false) cy.applicationInAppTable("My app") cy.deleteApp("My app") diff --git a/packages/builder/cypress/integration/createTable.spec.js b/packages/builder/cypress/integration/createTable.spec.js index da73c19fa6..36d78afb29 100644 --- a/packages/builder/cypress/integration/createTable.spec.js +++ b/packages/builder/cypress/integration/createTable.spec.js @@ -48,7 +48,7 @@ filterTests(["smoke", "all"], () => { it("deletes a row", () => { cy.get(interact.SPECTRUM_CHECKBOX_INPUT).check({ force: true }) - cy.contains("Delete 1 row(s)").click() + cy.contains("Delete 1 row").click() cy.get(interact.SPECTRUM_MODAL).contains("Delete").click() cy.contains("RoverUpdated").should("not.exist") }) diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js index 7efd32d258..dbcdb04acb 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -140,14 +140,14 @@ Cypress.Commands.add("setUserRole", (user, role) => { // Set Role cy.wait(500) cy.get(".spectrum-Form-itemField") - .eq(2) + .eq(3) .within(() => { cy.get(".spectrum-Picker-label").click({ force: true }) }) cy.get(".spectrum-Menu").within(() => { cy.get(".spectrum-Menu-itemLabel").contains(role).click({ force: true }) }) - cy.get(".spectrum-Form-itemField").eq(2).should("contain", role) + cy.get(".spectrum-Form-itemField").eq(3).should("contain", role) }) // APPLICATIONS diff --git a/packages/builder/package.json b/packages/builder/package.json index ca7b3b938a..6c8d837a50 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "1.2.28-alpha.0", + "version": "1.2.38", "license": "GPL-3.0", "private": true, "scripts": { @@ -69,10 +69,10 @@ } }, "dependencies": { - "@budibase/bbui": "1.2.28-alpha.0", - "@budibase/client": "1.2.28-alpha.0", - "@budibase/frontend-core": "1.2.28-alpha.0", - "@budibase/string-templates": "1.2.28-alpha.0", + "@budibase/bbui": "^1.2.38", + "@budibase/client": "^1.2.38", + "@budibase/frontend-core": "^1.2.38", + "@budibase/string-templates": "^1.2.38", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index 90e7ab661c..8016283094 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -79,7 +79,7 @@ automationStore.actions.addTestDataToAutomation({ body: { [key]: e.detail, - ...$automationStore.selectedAutomation.automation.testData.body, + ...$automationStore.selectedAutomation.automation.testData?.body, }, }) } diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ExecuteQuery.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ExecuteQuery.svelte index 462ee71cbe..6b9efa76d3 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ExecuteQuery.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ExecuteQuery.svelte @@ -3,6 +3,7 @@ import { datasources, integrations, queries } from "stores/backend" import BindingBuilder from "components/integration/QueryBindingBuilder.svelte" import IntegrationQueryEditor from "components/integration/index.svelte" + import { BUDIBASE_DATASOURCE_ID } from "constants/backend" export let parameters export let bindings = [] @@ -11,6 +12,10 @@ $: datasource = $datasources.list.find( ds => ds._id === parameters.datasourceId ) + // Executequery must exclude budibase datasource + $: executeQueryDatasources = $datasources.list.filter( + x => x._id !== BUDIBASE_DATASOURCE_ID + ) function fetchQueryDefinition(query) { const source = $datasources.list.find( @@ -24,7 +29,7 @@