diff --git a/.eslintignore b/.eslintignore index a79f9e2879..8d4c64d960 100644 --- a/.eslintignore +++ b/.eslintignore @@ -10,4 +10,5 @@ packages/builder/.routify packages/sdk/sdk packages/account-portal/packages/server/build packages/account-portal/packages/ui/.routify -packages/account-portal/packages/ui/build \ No newline at end of file +packages/account-portal/packages/ui/build +**/*.ivm.bundle.js \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index 917443014b..3de9d13046 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -43,7 +43,8 @@ "no-useless-escape": "off", "no-undef": "off", "no-prototype-builtins": "off", - "local-rules/no-budibase-imports": "error" + "local-rules/no-budibase-imports": "error", + "local-rules/no-test-com": "error" } }, { @@ -53,7 +54,7 @@ "packages/frontend-core/**/*" ], "rules": { - "no-console": ["error", { "allow": ["warn", "error", "debug"] } ] + "no-console": ["error", { "allow": ["warn", "error", "debug"] }] } } ], diff --git a/.prettierignore b/.prettierignore index 2444f0e753..87f0191a94 100644 --- a/.prettierignore +++ b/.prettierignore @@ -11,4 +11,5 @@ packages/sdk/sdk packages/pro/coverage packages/account-portal/packages/ui/build packages/account-portal/packages/ui/.routify -packages/account-portal/packages/server/build \ No newline at end of file +packages/account-portal/packages/server/build +**/*.ivm.bundle.js \ No newline at end of file diff --git a/eslint-local-rules/index.js b/eslint-local-rules/index.js index af02599c90..71bb5068da 100644 --- a/eslint-local-rules/index.js +++ b/eslint-local-rules/index.js @@ -18,4 +18,37 @@ module.exports = { } }, }, + "no-test-com": { + meta: { + type: "problem", + docs: { + description: + "disallow the use of 'test.com' in strings and replace it with 'example.com'", + category: "Possible Errors", + recommended: false, + }, + schema: [], // no options + fixable: "code", // Indicates that this rule supports automatic fixing + }, + create: function (context) { + return { + Literal(node) { + if ( + typeof node.value === "string" && + node.value.includes("test.com") + ) { + context.report({ + node, + message: + "test.com is a privately owned domain and could point anywhere, use example.com instead.", + fix: function (fixer) { + const newText = node.raw.replace(/test\.com/g, "example.com") + return fixer.replaceText(node, newText) + }, + }) + } + }, + } + }, + }, } diff --git a/hosting/couchdb/Dockerfile b/hosting/couchdb/Dockerfile index f83df7038b..36abc2dd19 100644 --- a/hosting/couchdb/Dockerfile +++ b/hosting/couchdb/Dockerfile @@ -1,4 +1,101 @@ -FROM couchdb:3.2.1 +# Modified from https://github.com/apache/couchdb-docker/blob/main/3.3.3/Dockerfile +# +# Everything in this `base` image is adapted from the official `couchdb` image's +# Dockerfile. Only modifications related to upgrading from Debian bullseye to +# bookworm have been included. The `runner` image contains Budibase's +# customisations to the image, e.g. adding Clouseau. +FROM node:20-slim AS base + +# Add CouchDB user account to make sure the IDs are assigned consistently +RUN groupadd -g 5984 -r couchdb && useradd -u 5984 -d /opt/couchdb -g couchdb couchdb + +# be sure GPG and apt-transport-https are available and functional +RUN set -ex; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + dirmngr \ + gnupg \ + ; \ + rm -rf /var/lib/apt/lists/* + +# grab tini for signal handling and zombie reaping +# see https://github.com/apache/couchdb-docker/pull/28#discussion_r141112407 +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends tini; \ + rm -rf /var/lib/apt/lists/*; \ + tini --version + +# http://docs.couchdb.org/en/latest/install/unix.html#installing-the-apache-couchdb-packages +ENV GPG_COUCH_KEY \ +# gpg: rsa8192 205-01-19 The Apache Software Foundation (Package repository signing key) + 390EF70BB1EA12B2773962950EE62FB37A00258D +RUN set -eux; \ + apt-get update; \ + apt-get install -y curl; \ + export GNUPGHOME="$(mktemp -d)"; \ + curl -fL -o keys.asc https://couchdb.apache.org/repo/keys.asc; \ + gpg --batch --import keys.asc; \ + gpg --batch --export "${GPG_COUCH_KEY}" > /usr/share/keyrings/couchdb-archive-keyring.gpg; \ + command -v gpgconf && gpgconf --kill all || :; \ + rm -rf "$GNUPGHOME"; \ + apt-key list; \ + apt purge -y --autoremove curl; \ + rm -rf /var/lib/apt/lists/* + +ENV COUCHDB_VERSION 3.3.3 + +RUN . /etc/os-release; \ + echo "deb [signed-by=/usr/share/keyrings/couchdb-archive-keyring.gpg] https://apache.jfrog.io/artifactory/couchdb-deb/ ${VERSION_CODENAME} main" | \ + tee /etc/apt/sources.list.d/couchdb.list >/dev/null + +# https://github.com/apache/couchdb-pkg/blob/master/debian/README.Debian +RUN set -eux; \ + apt-get update; \ + \ + echo "couchdb couchdb/mode select none" | debconf-set-selections; \ +# we DO want recommends this time + DEBIAN_FRONTEND=noninteractive apt-get install -y --allow-downgrades --allow-remove-essential --allow-change-held-packages \ + couchdb="$COUCHDB_VERSION"~bookworm \ + ; \ +# Undo symlinks to /var/log and /var/lib + rmdir /var/lib/couchdb /var/log/couchdb; \ + rm /opt/couchdb/data /opt/couchdb/var/log; \ + mkdir -p /opt/couchdb/data /opt/couchdb/var/log; \ + chown couchdb:couchdb /opt/couchdb/data /opt/couchdb/var/log; \ + chmod 777 /opt/couchdb/data /opt/couchdb/var/log; \ +# Remove file that sets logging to a file + rm /opt/couchdb/etc/default.d/10-filelog.ini; \ +# Check we own everything in /opt/couchdb. Matches the command in dockerfile_entrypoint.sh + find /opt/couchdb \! \( -user couchdb -group couchdb \) -exec chown -f couchdb:couchdb '{}' +; \ +# Setup directories and permissions for config. Technically these could be 555 and 444 respectively +# but we keep them as 755 and 644 for consistency with CouchDB defaults and the dockerfile_entrypoint.sh. + find /opt/couchdb/etc -type d ! -perm 0755 -exec chmod -f 0755 '{}' +; \ + find /opt/couchdb/etc -type f ! -perm 0644 -exec chmod -f 0644 '{}' +; \ +# only local.d needs to be writable for the docker_entrypoint.sh + chmod -f 0777 /opt/couchdb/etc/local.d; \ +# apt clean-up + rm -rf /var/lib/apt/lists/*; + +# Add configuration +COPY --chown=couchdb:couchdb couch/10-docker-default.ini /opt/couchdb/etc/default.d/ +# COPY --chown=couchdb:couchdb vm.args /opt/couchdb/etc/ + +COPY docker-entrypoint.sh /usr/local/bin +RUN ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh # backwards compat +ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"] + +VOLUME /opt/couchdb/data + +# 5984: Main CouchDB endpoint +# 4369: Erlang portmap daemon (epmd) +# 9100: CouchDB cluster communication port +EXPOSE 5984 4369 9100 +CMD ["/opt/couchdb/bin/couchdb"] + +FROM base as runner ENV COUCHDB_USER admin ENV COUCHDB_PASSWORD admin @@ -6,9 +103,9 @@ EXPOSE 5984 RUN apt-get update && apt-get install -y --no-install-recommends software-properties-common wget unzip curl && \ wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | apt-key add - && \ - apt-add-repository 'deb http://security.debian.org/debian-security bullseye-security/updates main' && \ + apt-add-repository 'deb http://security.debian.org/debian-security bookworm-security/updates main' && \ apt-add-repository 'deb http://archive.debian.org/debian stretch-backports main' && \ - apt-add-repository 'deb https://packages.adoptium.net/artifactory/deb bullseye main' && \ + apt-add-repository 'deb https://packages.adoptium.net/artifactory/deb bookworm main' && \ apt-get update && apt-get install -y --no-install-recommends temurin-8-jdk && \ rm -rf /var/lib/apt/lists/ diff --git a/hosting/couchdb/clouseau/clouseau.ini b/hosting/couchdb/clouseau/clouseau.ini index 578a5acafa..014fb854f3 100644 --- a/hosting/couchdb/clouseau/clouseau.ini +++ b/hosting/couchdb/clouseau/clouseau.ini @@ -4,7 +4,7 @@ name=clouseau@127.0.0.1 ; set this to the same distributed Erlang cookie used by the CouchDB nodes -cookie=monster +cookie=COUCHDB_ERLANG_COOKIE ; the path where you would like to store the search index files dir=DATA_DIR/search diff --git a/hosting/couchdb/couch/10-docker-default.ini b/hosting/couchdb/couch/10-docker-default.ini new file mode 100644 index 0000000000..1aa633cbfc --- /dev/null +++ b/hosting/couchdb/couch/10-docker-default.ini @@ -0,0 +1,8 @@ +; CouchDB Configuration Settings + +; Custom settings should be made in this file. They will override settings +; in default.ini, but unlike changes made to default.ini, this file won't be +; overwritten on server upgrade. + +[chttpd] +bind_address = any diff --git a/hosting/couchdb/couch/vm.args b/hosting/couchdb/couch/vm.args index e9e4416863..11845dd6d4 100644 --- a/hosting/couchdb/couch/vm.args +++ b/hosting/couchdb/couch/vm.args @@ -12,7 +12,7 @@ # erlang cookie for clouseau security -name couchdb@127.0.0.1 --setcookie monster +-setcookie COUCHDB_ERLANG_COOKIE # Ensure that the Erlang VM listens on a known port -kernel inet_dist_listen_min 9100 diff --git a/hosting/couchdb/docker-entrypoint.sh b/hosting/couchdb/docker-entrypoint.sh new file mode 100755 index 0000000000..bd709b7b73 --- /dev/null +++ b/hosting/couchdb/docker-entrypoint.sh @@ -0,0 +1,122 @@ +#!/bin/bash +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +set -e + +# first arg is `-something` or `+something` +if [ "${1#-}" != "$1" ] || [ "${1#+}" != "$1" ]; then + set -- /opt/couchdb/bin/couchdb "$@" +fi + +# first arg is the bare word `couchdb` +if [ "$1" = 'couchdb' ]; then + shift + set -- /opt/couchdb/bin/couchdb "$@" +fi + +if [ "$1" = '/opt/couchdb/bin/couchdb' ]; then + # this is where runtime configuration changes will be written. + # we need to explicitly touch it here in case /opt/couchdb/etc has + # been mounted as an external volume, in which case it won't exist. + # If running as the couchdb user (i.e. container starts as root), + # write permissions will be granted below. + touch /opt/couchdb/etc/local.d/docker.ini + + # if user is root, assume running under the couchdb user (default) + # and ensure it is able to access files and directories that may be mounted externally + if [ "$(id -u)" = '0' ]; then + # Check that we own everything in /opt/couchdb and fix if necessary. We also + # add the `-f` flag in all the following invocations because there may be + # cases where some of these ownership and permissions issues are non-fatal + # (e.g. a config file owned by root with o+r is actually fine), and we don't + # to be too aggressive about crashing here ... + find /opt/couchdb \! \( -user couchdb -group couchdb \) -exec chown -f couchdb:couchdb '{}' + + + # Ensure that data files have the correct permissions. We were previously + # preventing any access to these files outside of couchdb:couchdb, but it + # turns out that CouchDB itself does not set such restrictive permissions + # when it creates the files. The approach taken here ensures that the + # contents of the datadir have the same permissions as they had when they + # were initially created. This should minimize any startup delay. + find /opt/couchdb/data -type d ! -perm 0755 -exec chmod -f 0755 '{}' + + find /opt/couchdb/data -type f ! -perm 0644 -exec chmod -f 0644 '{}' + + + # Do the same thing for configuration files and directories. Technically + # CouchDB only needs read access to the configuration files as all online + # changes will be applied to the "docker.ini" file below, but we set 644 + # for the sake of consistency. + find /opt/couchdb/etc -type d ! -perm 0755 -exec chmod -f 0755 '{}' + + find /opt/couchdb/etc -type f ! -perm 0644 -exec chmod -f 0644 '{}' + + fi + + if [ ! -z "$NODENAME" ] && ! grep "couchdb@" /opt/couchdb/etc/vm.args; then + echo "-name couchdb@$NODENAME" >> /opt/couchdb/etc/vm.args + fi + + if [ "$COUCHDB_USER" ] && [ "$COUCHDB_PASSWORD" ]; then + # Create admin only if not already present + if ! grep -Pzoqr "\[admins\]\n$COUCHDB_USER =" /opt/couchdb/etc/local.d/*.ini /opt/couchdb/etc/local.ini; then + printf "\n[admins]\n%s = %s\n" "$COUCHDB_USER" "$COUCHDB_PASSWORD" >> /opt/couchdb/etc/local.d/docker.ini + fi + fi + + if [ "$COUCHDB_SECRET" ]; then + # Set secret only if not already present + if ! grep -Pzoqr "\[chttpd_auth\]\nsecret =" /opt/couchdb/etc/local.d/*.ini /opt/couchdb/etc/local.ini; then + printf "\n[chttpd_auth]\nsecret = %s\n" "$COUCHDB_SECRET" >> /opt/couchdb/etc/local.d/docker.ini + fi + fi + + if [ "$COUCHDB_ERLANG_COOKIE" ]; then + cookieFile='/opt/couchdb/.erlang.cookie' + if [ -e "$cookieFile" ]; then + if [ "$(cat "$cookieFile" 2>/dev/null)" != "$COUCHDB_ERLANG_COOKIE" ]; then + echo >&2 + echo >&2 "warning: $cookieFile contents do not match COUCHDB_ERLANG_COOKIE" + echo >&2 + fi + else + echo "$COUCHDB_ERLANG_COOKIE" > "$cookieFile" + fi + chown couchdb:couchdb "$cookieFile" + chmod 600 "$cookieFile" + fi + + if [ "$(id -u)" = '0' ]; then + chown -f couchdb:couchdb /opt/couchdb/etc/local.d/docker.ini || true + fi + + # if we don't find an [admins] section followed by a non-comment, display a warning + if ! grep -Pzoqr '\[admins\]\n[^;]\w+' /opt/couchdb/etc/default.d/*.ini /opt/couchdb/etc/local.d/*.ini /opt/couchdb/etc/local.ini; then + # The - option suppresses leading tabs but *not* spaces. :) + cat >&2 <<-'EOWARN' +************************************************************* +ERROR: CouchDB 3.0+ will no longer run in "Admin Party" + mode. You *MUST* specify an admin user and + password, either via your own .ini file mapped + into the container at /opt/couchdb/etc/local.ini + or inside /opt/couchdb/etc/local.d, or with + "-e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password" + to set it via "docker run". +************************************************************* +EOWARN + exit 1 + fi + + if [ "$(id -u)" = '0' ]; then + export HOME=$(echo ~couchdb) + exec setpriv --reuid=couchdb --regid=couchdb --clear-groups "$@" + fi +fi + +exec "$@" \ No newline at end of file diff --git a/hosting/couchdb/runner.sh b/hosting/couchdb/runner.sh index 9f6a853ca7..aaadee6b43 100644 --- a/hosting/couchdb/runner.sh +++ b/hosting/couchdb/runner.sh @@ -1,6 +1,7 @@ #!/bin/bash DATA_DIR=${DATA_DIR:-/data} +COUCHDB_ERLANG_COOKIE=${COUCHDB_ERLANG_COOKIE:-B9CFC32C-3458-4A86-8448-B3C753991CA7} mkdir -p ${DATA_DIR} mkdir -p ${DATA_DIR}/couch/{dbs,views} @@ -60,6 +61,9 @@ else sed -i "s#DATA_DIR#/data#g" /opt/couchdb/etc/local.ini fi +sed -i "s#COUCHDB_ERLANG_COOKIE#${COUCHDB_ERLANG_COOKIE}#g" /opt/couchdb/etc/vm.args +sed -i "s#COUCHDB_ERLANG_COOKIE#${COUCHDB_ERLANG_COOKIE}#g" /opt/clouseau/clouseau.ini + # Start Clouseau. Budibase won't function correctly without Clouseau running, it # powers the search API endpoints which are used to do all sorts, including # populating app grids. diff --git a/hosting/docker-compose.yaml b/hosting/docker-compose.yaml index 36b88466fe..a72b36aef1 100644 --- a/hosting/docker-compose.yaml +++ b/hosting/docker-compose.yaml @@ -98,7 +98,6 @@ services: couchdb-service: restart: unless-stopped image: budibase/couchdb - pull_policy: always environment: - COUCHDB_PASSWORD=${COUCH_DB_PASSWORD} - COUCHDB_USER=${COUCH_DB_USER} diff --git a/hosting/single/Dockerfile b/hosting/single/Dockerfile index 67ac677984..f89967cb0b 100644 --- a/hosting/single/Dockerfile +++ b/hosting/single/Dockerfile @@ -3,7 +3,6 @@ FROM node:20-slim as build # install node-gyp dependencies RUN apt-get update && apt-get install -y --no-install-recommends g++ make python3 jq - # copy and install dependencies WORKDIR /app COPY package.json . @@ -42,7 +41,6 @@ COPY packages/string-templates packages/string-templates FROM budibase/couchdb as runner ARG TARGETARCH ENV TARGETARCH $TARGETARCH -ENV NODE_MAJOR 20 #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 @@ -60,10 +58,8 @@ RUN apt install -y software-properties-common apt-transport-https ca-certificate && apt install postgresql-client-15 -y \ && apt remove software-properties-common apt-transport-https gpg -y -# install other dependencies, nodejs, oracle requirements, jdk8, redis, nginx -WORKDIR /nodejs -COPY scripts/install-node.sh ./install.sh -RUN chmod +x install.sh && ./install.sh +# We use pm2 in order to run multiple node processes in a single container +RUN npm install --global pm2 # setup nginx COPY hosting/single/nginx/nginx.conf /etc/nginx @@ -124,6 +120,8 @@ HEALTHCHECK --interval=15s --timeout=15s --start-period=45s CMD "/healthcheck.sh # must set this just before running ENV NODE_ENV=production +# this is required for isolated-vm to work on Node 20+ +ENV NODE_OPTIONS="--no-node-snapshot" WORKDIR / CMD ["./runner.sh"] diff --git a/hosting/single/runner.sh b/hosting/single/runner.sh index 886da4c916..3126eedfb1 100644 --- a/hosting/single/runner.sh +++ b/hosting/single/runner.sh @@ -97,10 +97,12 @@ fi sleep 10 pushd app -pm2 start -l /dev/stdout --name app "yarn run:docker" +pm2 start --name app "yarn run:docker" popd pushd worker -pm2 start -l /dev/stdout --name worker "yarn run:docker" +pm2 start --name worker "yarn run:docker" popd echo "end of runner.sh, sleeping ..." + +tail -f $HOME/.pm2/logs/*.log sleep infinity diff --git a/lerna.json b/lerna.json index 9d0593bead..3386214dcf 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.17.7", + "version": "2.20.0", "npmClient": "yarn", "packages": [ "packages/*", diff --git a/package.json b/package.json index af4c540604..4407fd33f3 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "lint": "yarn run lint:eslint && yarn run lint:prettier", "lint:fix:eslint": "eslint --fix --max-warnings=0 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", + "lint:fix": "yarn run lint:fix:eslint && yarn run lint:fix:prettier", "build:specs": "lerna run --stream specs", "build:docker:airgap": "node hosting/scripts/airgapped/airgappedDockerBuild", "build:docker:airgap:single": "SINGLE_IMAGE=1 node hosting/scripts/airgapped/airgappedDockerBuild", @@ -97,7 +97,16 @@ "@budibase/backend-core": "0.0.0", "@budibase/shared-core": "0.0.0", "@budibase/string-templates": "0.0.0", - "@budibase/types": "0.0.0" + "@budibase/types": "0.0.0", + "tough-cookie": "4.1.3", + "node-fetch": "2.6.7", + "semver": "7.5.3", + "http-cache-semantics": "4.1.1", + "msgpackr": "1.10.1", + "axios": "1.6.3", + "xml2js": "0.6.2", + "unset-value": "2.0.1", + "passport": "0.6.0" }, "engines": { "node": ">=20.0.0 <21.0.0" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 85644488f5..3f8c34f823 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -25,19 +25,19 @@ "@budibase/pouchdb-replication-stream": "1.2.10", "@budibase/shared-core": "0.0.0", "@budibase/types": "0.0.0", - "@techpass/passport-openidconnect": "0.3.2", + "@govtechsg/passport-openidconnect": "^1.0.2", "aws-cloudfront-sign": "3.0.2", "aws-sdk": "2.1030.0", "bcrypt": "5.1.0", "bcryptjs": "2.4.3", "bull": "4.10.1", "correlation-id": "4.0.0", - "dd-trace": "5.0.0", + "dd-trace": "5.2.0", "dotenv": "16.0.1", "ioredis": "5.3.2", "joi": "17.6.0", "jsonwebtoken": "9.0.2", - "koa-passport": "4.1.4", + "koa-passport": "^6.0.0", "koa-pino-logger": "4.0.0", "lodash": "4.17.21", "node-fetch": "2.6.7", @@ -52,9 +52,9 @@ "redlock": "4.2.0", "rotating-file-stream": "3.1.0", "sanitize-s3-objectkey": "0.0.1", - "semver": "7.3.7", + "semver": "^7.5.4", "tar-fs": "2.1.1", - "uuid": "8.3.2" + "uuid": "^8.3.2" }, "devDependencies": { "@shopify/jest-koa-mocks": "5.1.1", diff --git a/packages/backend-core/src/context/types.ts b/packages/backend-core/src/context/types.ts index f73dc9f5c7..6fb9f44fad 100644 --- a/packages/backend-core/src/context/types.ts +++ b/packages/backend-core/src/context/types.ts @@ -1,4 +1,4 @@ -import { IdentityContext } from "@budibase/types" +import { IdentityContext, VM } from "@budibase/types" import { ExecutionTimeTracker } from "../timers" // keep this out of Budibase types, don't want to expose context info @@ -11,4 +11,5 @@ export type ContextMap = { automationId?: string isMigrating?: boolean jsExecutionTracker?: ExecutionTimeTracker + vm?: VM } diff --git a/packages/backend-core/src/events/publishers/query.ts b/packages/backend-core/src/events/publishers/query.ts index 7d28129cf6..48603257d2 100644 --- a/packages/backend-core/src/events/publishers/query.ts +++ b/packages/backend-core/src/events/publishers/query.ts @@ -3,6 +3,7 @@ import { Event, Datasource, Query, + QueryPreview, QueryCreatedEvent, QueryUpdatedEvent, QueryDeletedEvent, @@ -68,9 +69,9 @@ const run = async (count: number, timestamp?: string | number) => { await publishEvent(Event.QUERIES_RUN, properties, timestamp) } -const previewed = async (datasource: Datasource, query: Query) => { +const previewed = async (datasource: Datasource, query: QueryPreview) => { const properties: QueryPreviewedEvent = { - queryId: query._id, + queryId: query.queryId, datasourceId: datasource._id as string, source: datasource.source, queryVerb: query.queryVerb, diff --git a/packages/backend-core/src/installation.ts b/packages/backend-core/src/installation.ts index ca35b926fb..83166880cc 100644 --- a/packages/backend-core/src/installation.ts +++ b/packages/backend-core/src/installation.ts @@ -6,6 +6,7 @@ import * as context from "./context" import semver from "semver" import { bustCache, withCache, TTL, CacheKey } from "./cache/generic" import environment from "./environment" +import { logAlert } from "./logging" export const getInstall = async (): Promise => { return withCache(CacheKey.INSTALLATION, TTL.ONE_DAY, getInstallFromDB, { @@ -80,27 +81,35 @@ export const checkInstallVersion = async (): Promise => { const currentVersion = install.version const newVersion = environment.VERSION - if (currentVersion !== newVersion) { - const isUpgrade = semver.gt(newVersion, currentVersion) - const isDowngrade = semver.lt(newVersion, currentVersion) + try { + if (currentVersion !== newVersion) { + const isUpgrade = semver.gt(newVersion, currentVersion) + const isDowngrade = semver.lt(newVersion, currentVersion) - const success = await updateVersion(newVersion) + const success = await updateVersion(newVersion) - if (success) { - await context.doInIdentityContext( - { - _id: install.installId, - type: IdentityType.INSTALLATION, - }, - async () => { - if (isUpgrade) { - await events.installation.upgraded(currentVersion, newVersion) - } else if (isDowngrade) { - await events.installation.downgraded(currentVersion, newVersion) + if (success) { + await context.doInIdentityContext( + { + _id: install.installId, + type: IdentityType.INSTALLATION, + }, + async () => { + if (isUpgrade) { + await events.installation.upgraded(currentVersion, newVersion) + } else if (isDowngrade) { + await events.installation.downgraded(currentVersion, newVersion) + } } - } - ) - await events.identification.identifyInstallationGroup(install.installId) + ) + await events.identification.identifyInstallationGroup(install.installId) + } + } + } catch (err: any) { + if (err?.message?.includes("Invalid Version")) { + logAlert(`Invalid version "${newVersion}" - is it semver?`) + } else { + logAlert("Failed to retrieve version", err) } } } diff --git a/packages/backend-core/src/logging/correlation/correlation.ts b/packages/backend-core/src/logging/correlation/correlation.ts index 0498bd43d5..13cc7aff8f 100644 --- a/packages/backend-core/src/logging/correlation/correlation.ts +++ b/packages/backend-core/src/logging/correlation/correlation.ts @@ -2,11 +2,12 @@ import { Header } from "../../constants" const correlator = require("correlation-id") -export const setHeader = (headers: any) => { +export const setHeader = (headers: Record) => { const correlationId = correlator.getId() - if (correlationId) { - headers[Header.CORRELATION_ID] = correlationId + if (!correlationId) { + return } + headers[Header.CORRELATION_ID] = correlationId } export function getId() { diff --git a/packages/backend-core/src/objectStore/objectStore.ts b/packages/backend-core/src/objectStore/objectStore.ts index 3a3b9cdaab..8d18fb97fd 100644 --- a/packages/backend-core/src/objectStore/objectStore.ts +++ b/packages/backend-core/src/objectStore/objectStore.ts @@ -255,7 +255,8 @@ export async function listAllObjects(bucketName: string, path: string) { objects = objects.concat(response.Contents) } isTruncated = !!response.IsTruncated - } while (isTruncated) + token = response.NextContinuationToken + } while (isTruncated && token) return objects } diff --git a/packages/backend-core/src/queue/queue.ts b/packages/backend-core/src/queue/queue.ts index b95dace5b2..0bcb25a35f 100644 --- a/packages/backend-core/src/queue/queue.ts +++ b/packages/backend-core/src/queue/queue.ts @@ -2,7 +2,7 @@ import env from "../environment" import { getRedisOptions } from "../redis/utils" import { JobQueue } from "./constants" import InMemoryQueue from "./inMemoryQueue" -import BullQueue, { QueueOptions } from "bull" +import BullQueue, { QueueOptions, JobOptions } from "bull" import { addListeners, StalledFn } from "./listeners" import { Duration } from "../utils" import * as timers from "../timers" @@ -24,17 +24,24 @@ async function cleanup() { export function createQueue( jobQueue: JobQueue, - opts: { removeStalledCb?: StalledFn } = {} + opts: { + removeStalledCb?: StalledFn + maxStalledCount?: number + jobOptions?: JobOptions + } = {} ): BullQueue.Queue { const redisOpts = getRedisOptions() const queueConfig: QueueOptions = { redis: redisOpts, settings: { - maxStalledCount: 0, + maxStalledCount: opts.maxStalledCount ? opts.maxStalledCount : 0, lockDuration: QUEUE_LOCK_MS, lockRenewTime: QUEUE_LOCK_RENEW_INTERNAL_MS, }, } + if (opts.jobOptions) { + queueConfig.defaultJobOptions = opts.jobOptions + } let queue: any if (!env.isTest()) { queue = new BullQueue(jobQueue, queueConfig) diff --git a/packages/backend-core/src/utils/tests/utils.spec.ts b/packages/backend-core/src/utils/tests/utils.spec.ts index 7b411e801c..4dc3855c35 100644 --- a/packages/backend-core/src/utils/tests/utils.spec.ts +++ b/packages/backend-core/src/utils/tests/utils.spec.ts @@ -44,11 +44,11 @@ describe("utils", () => { it("gets appId from url", async () => { await config.doInTenant(async () => { - const url = "http://test.com" + const url = "http://example.com" env._set("PLATFORM_URL", url) const ctx = structures.koa.newContext() - ctx.host = `${config.tenantId}.test.com` + ctx.host = `${config.tenantId}.example.com` const expected = db.generateAppID(config.tenantId) const app = structures.apps.app(expected) @@ -89,7 +89,7 @@ describe("utils", () => { const ctx = structures.koa.newContext() const expected = db.generateAppID() ctx.request.headers = { - referer: `http://test.com/builder/app/${expected}/design/screen_123/screens`, + referer: `http://example.com/builder/app/${expected}/design/screen_123/screens`, } const actual = await utils.getAppIdFromCtx(ctx) @@ -100,7 +100,7 @@ describe("utils", () => { const ctx = structures.koa.newContext() const appId = db.generateAppID() ctx.request.headers = { - referer: `http://test.com/foo/app/${appId}/bar`, + referer: `http://example.com/foo/app/${appId}/bar`, } const actual = await utils.getAppIdFromCtx(ctx) diff --git a/packages/backend-core/tests/core/utilities/structures/common.ts b/packages/backend-core/tests/core/utilities/structures/common.ts index 05b879f36b..9b1b178f0b 100644 --- a/packages/backend-core/tests/core/utilities/structures/common.ts +++ b/packages/backend-core/tests/core/utilities/structures/common.ts @@ -3,5 +3,5 @@ import { v4 as uuid } from "uuid" export { v4 as uuid } from "uuid" export const email = () => { - return `${uuid()}@test.com` + return `${uuid()}@example.com` } diff --git a/packages/backend-core/tests/core/utilities/structures/sso.ts b/packages/backend-core/tests/core/utilities/structures/sso.ts index 2e3af712a9..6492283e6a 100644 --- a/packages/backend-core/tests/core/utilities/structures/sso.ts +++ b/packages/backend-core/tests/core/utilities/structures/sso.ts @@ -61,7 +61,7 @@ export function ssoProfile(user?: User): SSOProfile { }, _json: { email: user.email, - picture: "http://test.com", + picture: "http://example.com", }, provider: generator.string(), } diff --git a/packages/backend-core/tests/core/utilities/structures/users.ts b/packages/backend-core/tests/core/utilities/structures/users.ts index 8f4096d401..db90887af2 100644 --- a/packages/backend-core/tests/core/utilities/structures/users.ts +++ b/packages/backend-core/tests/core/utilities/structures/users.ts @@ -25,7 +25,7 @@ export const user = (userProps?: Partial>): User => { roles: { app_test: "admin" }, firstName: generator.first(), lastName: generator.last(), - pictureUrl: "http://test.com", + pictureUrl: "http://example.com", tenantId: tenant.id(), ...userProps, } diff --git a/packages/bbui/src/Form/Core/EnvDropdown.svelte b/packages/bbui/src/Form/Core/EnvDropdown.svelte index 2edf8a5f9d..c690ffbc6b 100644 --- a/packages/bbui/src/Form/Core/EnvDropdown.svelte +++ b/packages/bbui/src/Form/Core/EnvDropdown.svelte @@ -184,7 +184,7 @@ {#if environmentVariablesEnabled}
showModal()} class="add-variable">
handleUpgradePanel()} class="add-variable">
zapier diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/AutomationBlockTagline.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/AutomationBlockTagline.svelte index c18368b1b1..8732623dcf 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/AutomationBlockTagline.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/AutomationBlockTagline.svelte @@ -1,7 +1,7 @@ diff --git a/packages/builder/src/components/automation/AutomationPanel/AutomationList.svelte b/packages/builder/src/components/automation/AutomationPanel/AutomationList.svelte index cce0f4eeab..9d946fe55d 100644 --- a/packages/builder/src/components/automation/AutomationPanel/AutomationList.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/AutomationList.svelte @@ -4,7 +4,7 @@ automationStore, selectedAutomation, userSelectedResourceMap, - } from "builderStore" + } from "stores/builder" import NavItem from "components/common/NavItem.svelte" import EditAutomationPopover from "./EditAutomationPopover.svelte" import { notifications } from "@budibase/bbui" diff --git a/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte b/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte index d8651f238a..582db950fe 100644 --- a/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte @@ -7,7 +7,7 @@ automationStore, selectedAutomation, userSelectedResourceMap, - } from "builderStore" + } from "stores/builder" import NavItem from "components/common/NavItem.svelte" import EditAutomationPopover from "./EditAutomationPopover.svelte" diff --git a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte index 9f279427d9..41bffba19e 100644 --- a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte @@ -1,5 +1,5 @@ diff --git a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte index 23697bf2c7..23081c92c4 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte @@ -3,13 +3,13 @@ import { Layout } from "@budibase/bbui" import { BUDIBASE_INTERNAL_DB_ID } from "constants/backend" import { - database, datasources, queries, tables, views, viewsV2, - } from "stores/backend" + userSelectedResourceMap, + } from "stores/builder" import EditDatasourcePopover from "./popovers/EditDatasourcePopover.svelte" import EditQueryPopover from "./popovers/EditQueryPopover.svelte" import NavItem from "components/common/NavItem.svelte" @@ -21,7 +21,6 @@ } from "helpers/data/utils" import IntegrationIcon from "./IntegrationIcon.svelte" import { TableNames } from "constants" - import { userSelectedResourceMap } from "builderStore" import { enrichDatasources } from "./datasourceUtils" import { onMount } from "svelte" @@ -75,69 +74,67 @@ searchTerm && !showAppUsersTable && !enrichedDataSources.find(ds => ds.show) -{#if $database?._id} -
- {#if showAppUsersTable} - selectTable(TableNames.USERS)} - selectedBy={$userSelectedResourceMap[TableNames.USERS]} - /> - {/if} - {#each enrichedDataSources.filter(ds => ds.show) as datasource} - selectDatasource(datasource)} - on:iconClick={() => toggleNode(datasource)} - selectedBy={$userSelectedResourceMap[datasource._id]} - > -
- -
- {#if datasource._id !== BUDIBASE_INTERNAL_DB_ID} - - {/if} -
- - {#if datasource.open} - - {#each datasource.queries as query} - $goto(`./query/${query._id}`)} - selectedBy={$userSelectedResourceMap[query._id]} - > - - - {/each} +
+ {#if showAppUsersTable} + selectTable(TableNames.USERS)} + selectedBy={$userSelectedResourceMap[TableNames.USERS]} + /> + {/if} + {#each enrichedDataSources.filter(ds => ds.show) as datasource} + selectDatasource(datasource)} + on:iconClick={() => toggleNode(datasource)} + selectedBy={$userSelectedResourceMap[datasource._id]} + > +
+ +
+ {#if datasource._id !== BUDIBASE_INTERNAL_DB_ID} + {/if} - {/each} - {#if showNoResults} - -
- There aren't any datasources matching that name -
-
+
+ + {#if datasource.open} + + {#each datasource.queries as query} + $goto(`./query/${query._id}`)} + selectedBy={$userSelectedResourceMap[query._id]} + > + + + {/each} {/if} -
-{/if} + {/each} + {#if showNoResults} + +
+ There aren't any datasources matching that name +
+
+ {/if} +
diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DeleteRow.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DeleteRow.svelte index 1e79c51051..b8459ac0eb 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DeleteRow.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DeleteRow.svelte @@ -1,6 +1,6 @@ diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/SaveRow.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/SaveRow.svelte index 9f70272d78..a1fe773455 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/SaveRow.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/SaveRow.svelte @@ -1,8 +1,12 @@ - + diff --git a/packages/builder/src/components/design/settings/controls/ColumnEditor/CellDrawer.svelte b/packages/builder/src/components/design/settings/controls/ColumnEditor/CellDrawer.svelte index 9e53f7f1cf..f5167e3657 100644 --- a/packages/builder/src/components/design/settings/controls/ColumnEditor/CellDrawer.svelte +++ b/packages/builder/src/components/design/settings/controls/ColumnEditor/CellDrawer.svelte @@ -7,7 +7,7 @@ Layout, Label, } from "@budibase/bbui" - import { store } from "builderStore" + import { themeStore } from "stores/builder" import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" export let column @@ -46,7 +46,7 @@ (column.background = e.detail)} - spectrumTheme={$store.theme} + spectrumTheme={$themeStore.theme} /> @@ -54,7 +54,7 @@ (column.color = e.detail)} - spectrumTheme={$store.theme} + spectrumTheme={$themeStore.theme} /> diff --git a/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnEditor.svelte b/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnEditor.svelte index cebb429ac4..2b9fa573c2 100644 --- a/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnEditor.svelte +++ b/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnEditor.svelte @@ -3,11 +3,8 @@ import { createEventDispatcher } from "svelte" import ColumnDrawer from "./ColumnDrawer.svelte" import { cloneDeep } from "lodash/fp" - import { - getDatasourceForProvider, - getSchemaForDatasource, - } from "builderStore/dataBinding" - import { currentAsset } from "builderStore" + import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding" + import { selectedScreen } from "stores/builder" import { getFields } from "helpers/searchFields" export let componentInstance @@ -21,8 +18,8 @@ let boundValue $: text = getText(value) - $: datasource = getDatasourceForProvider($currentAsset, componentInstance) - $: schema = getSchema($currentAsset, datasource) + $: datasource = getDatasourceForProvider($selectedScreen, componentInstance) + $: schema = getSchema($selectedScreen, datasource) $: options = allowCellEditing ? Object.keys(schema || {}) : enrichedSchemaFields?.map(field => field.name) diff --git a/packages/builder/src/components/design/settings/controls/DataProviderSelect.svelte b/packages/builder/src/components/design/settings/controls/DataProviderSelect.svelte index 9fd220e798..2e74cac0f4 100644 --- a/packages/builder/src/components/design/settings/controls/DataProviderSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/DataProviderSelect.svelte @@ -1,14 +1,14 @@ diff --git a/packages/builder/src/components/design/settings/controls/DataSourceSelect/DataSourceSelect.svelte b/packages/builder/src/components/design/settings/controls/DataSourceSelect/DataSourceSelect.svelte index 3eb69e9a3b..0e01c264fc 100644 --- a/packages/builder/src/components/design/settings/controls/DataSourceSelect/DataSourceSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/DataSourceSelect/DataSourceSelect.svelte @@ -2,7 +2,7 @@ import { readableToRuntimeBinding, runtimeToReadableBinding, - } from "builderStore/dataBinding" + } from "dataBinding" import { Button, Popover, @@ -17,19 +17,20 @@ notifications, } from "@budibase/bbui" import { createEventDispatcher } from "svelte" - import { store, currentAsset } from "builderStore" import { tables as tablesStore, queries as queriesStore, viewsV2 as viewsV2Store, views as viewsStore, + selectedScreen, + componentStore, datasources, integrations, - } from "stores/backend" + } from "stores/builder" import BindingBuilder from "components/integration/QueryBindingBuilder.svelte" import IntegrationQueryEditor from "components/integration/index.svelte" import { makePropSafe as safe } from "@budibase/string-templates" - import { findAllComponents } from "builderStore/componentUtils" + import { findAllComponents } from "helpers/components" import ClientBindingPanel from "components/common/bindings/ClientBindingPanel.svelte" import DataSourceCategory from "components/design/settings/controls/DataSourceSelect/DataSourceCategory.svelte" import { API } from "api" @@ -75,11 +76,11 @@ ...query, type: "query", })) - $: dataProviders = findAllComponents($currentAsset.props) + $: dataProviders = findAllComponents($selectedScreen.props) .filter(component => { return ( component._component?.endsWith("/dataprovider") && - component._id !== $store.selectedComponentId + component._id !== $componentStore.selectedComponentId ) }) .map(provider => ({ @@ -126,10 +127,14 @@ } }) $: jsonArrays = bindings - .filter(x => x.fieldSchema?.type === "jsonarray") + .filter( + x => + x.fieldSchema?.type === "jsonarray" || + (x.fieldSchema?.type === "json" && x.fieldSchema?.subtype === "array") + ) .map(binding => { const { providerId, readableBinding, runtimeBinding, tableId } = binding - const { name, type, prefixKeys } = binding.fieldSchema + const { name, type, prefixKeys, subtype } = binding.fieldSchema return { providerId, label: readableBinding, @@ -137,7 +142,8 @@ fieldType: type, tableId, prefixKeys, - type: "jsonarray", + type: type === "jsonarray" ? "jsonarray" : "queryarray", + subtype, value: `{{ literal ${runtimeBinding} }}`, } }) diff --git a/packages/builder/src/components/design/settings/controls/EditComponentPopover.svelte b/packages/builder/src/components/design/settings/controls/EditComponentPopover.svelte index 04bb925873..4e645fe343 100644 --- a/packages/builder/src/components/design/settings/controls/EditComponentPopover.svelte +++ b/packages/builder/src/components/design/settings/controls/EditComponentPopover.svelte @@ -1,9 +1,9 @@
diff --git a/packages/builder/src/components/design/settings/controls/FieldSelect.svelte b/packages/builder/src/components/design/settings/controls/FieldSelect.svelte index 7d1741df17..e50a0e8030 100644 --- a/packages/builder/src/components/design/settings/controls/FieldSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/FieldSelect.svelte @@ -1,10 +1,7 @@
@@ -42,7 +45,7 @@ on:change >
- + {item.field}
diff --git a/packages/builder/src/components/design/settings/controls/GridColumnConfiguration/GridColumnConfiguration.svelte b/packages/builder/src/components/design/settings/controls/GridColumnConfiguration/GridColumnConfiguration.svelte index 4286328367..439bf5e261 100644 --- a/packages/builder/src/components/design/settings/controls/GridColumnConfiguration/GridColumnConfiguration.svelte +++ b/packages/builder/src/components/design/settings/controls/GridColumnConfiguration/GridColumnConfiguration.svelte @@ -1,9 +1,6 @@ diff --git a/packages/builder/src/components/design/settings/controls/GridColumnConfiguration/PrimaryColumnFieldSetting.svelte b/packages/builder/src/components/design/settings/controls/GridColumnConfiguration/PrimaryColumnFieldSetting.svelte index 1cb29ac6e7..57423e8667 100644 --- a/packages/builder/src/components/design/settings/controls/GridColumnConfiguration/PrimaryColumnFieldSetting.svelte +++ b/packages/builder/src/components/design/settings/controls/GridColumnConfiguration/PrimaryColumnFieldSetting.svelte @@ -4,7 +4,7 @@ import { setContext } from "svelte" import { writable } from "svelte/store" import { FieldTypeToComponentMap } from "../FieldConfiguration/utils" - import { store } from "builderStore" + import { componentStore } from "stores/builder" export let item export let anchor @@ -35,7 +35,7 @@ const component = `@budibase/standard-components/${ FieldTypeToComponentMap[item.columnType] }` - return store.actions.components.getDefinition(component)?.icon + return componentStore.getDefinition(component)?.icon } $: icon = getIcon(item) diff --git a/packages/builder/src/components/design/settings/controls/LayoutSelect.svelte b/packages/builder/src/components/design/settings/controls/LayoutSelect.svelte index eed01c0d94..a7966eb671 100644 --- a/packages/builder/src/components/design/settings/controls/LayoutSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/LayoutSelect.svelte @@ -1,5 +1,5 @@ diff --git a/packages/builder/src/components/design/settings/controls/RelationshipFilterEditor.svelte b/packages/builder/src/components/design/settings/controls/RelationshipFilterEditor.svelte index 0eb93732c3..43f158bef3 100644 --- a/packages/builder/src/components/design/settings/controls/RelationshipFilterEditor.svelte +++ b/packages/builder/src/components/design/settings/controls/RelationshipFilterEditor.svelte @@ -1,14 +1,10 @@ diff --git a/packages/builder/src/components/design/settings/controls/ValidationEditor/ValidationDrawer.svelte b/packages/builder/src/components/design/settings/controls/ValidationEditor/ValidationDrawer.svelte index 25c7651d35..3a98de94cd 100644 --- a/packages/builder/src/components/design/settings/controls/ValidationEditor/ValidationDrawer.svelte +++ b/packages/builder/src/components/design/settings/controls/ValidationEditor/ValidationDrawer.svelte @@ -10,12 +10,9 @@ Input, DatePicker, } from "@budibase/bbui" - import { currentAsset, selectedComponent } from "builderStore" - import { findClosestMatchingComponent } from "builderStore/componentUtils" - import { - getSchemaForDatasource, - getDatasourceForProvider, - } from "builderStore/dataBinding" + import { selectedScreen, selectedComponent } from "stores/builder" + import { findClosestMatchingComponent } from "helpers/components" + import { getSchemaForDatasource, getDatasourceForProvider } from "dataBinding" import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" import { generate } from "shortid" @@ -127,13 +124,14 @@ ], } - const resolveDatasource = (currentAsset, componentInstance, parent) => { + const resolveDatasource = (selectedScreen, componentInstance, parent) => { return ( - getDatasourceForProvider(currentAsset, parent || componentInstance) || {} + getDatasourceForProvider(selectedScreen, parent || componentInstance) || + {} ) } - $: dataSourceSchema = getDataSourceSchema($currentAsset, $selectedComponent) + $: dataSourceSchema = getDataSourceSchema($selectedScreen, $selectedComponent) $: field = fieldName || $selectedComponent?.field $: schemaRules = parseRulesFromSchema(field, dataSourceSchema || {}) $: fieldType = type?.split("/")[1] || "string" diff --git a/packages/builder/src/components/integration/AccessLevelSelect.svelte b/packages/builder/src/components/integration/AccessLevelSelect.svelte index 3dc24983d3..05b336c3b3 100644 --- a/packages/builder/src/components/integration/AccessLevelSelect.svelte +++ b/packages/builder/src/components/integration/AccessLevelSelect.svelte @@ -1,6 +1,6 @@ @@ -112,7 +122,9 @@ bind:value={field.name} on:blur={changed} /> - {#if options} + {#if isJsonArray(field.value)} + import { goto } from "@roxi/routify" - import { datasources, integrations, queries } from "stores/backend" + import { datasources, integrations, queries } from "stores/builder" import { Icon, Select, @@ -40,7 +40,9 @@ let schemaType let autoSchema = {} + let nestedSchemaFields = {} let rows = [] + let keys = {} const parseQuery = query => { modified = false @@ -82,13 +84,14 @@ return } + nestedSchemaFields = response.nestedSchemaFields + if (Object.keys(newQuery.schema).length === 0) { // Assign this to a variable instead of directly to the newQuery.schema so that a user // can change the table they're querying and have the schema update until they first // edit it autoSchema = response.schema } - rows = response.rows notifications.success("Query executed successfully") @@ -119,6 +122,7 @@ Object.keys(newQuery.schema).length === 0 ? autoSchema : newQuery.schema, + nestedSchemaFields, }) notifications.success("Query saved successfully") @@ -143,8 +147,20 @@ const handleScroll = e => { scrolling = e.target.scrollTop !== 0 } + + async function handleKeyDown(evt) { + keys[evt.key] = true + if ((keys["Meta"] || keys["Control"]) && keys["Enter"]) { + await runQuery({ suppressErrors: false }) + } + } + + function handleKeyUp(evt) { + delete keys[evt.key] + } + checkIsModified(newQuery)} attemptSave={() => runQuery({ suppressErrors: false }).then(saveQuery)} diff --git a/packages/builder/src/components/integration/QueryViewerBindingBuilder.svelte b/packages/builder/src/components/integration/QueryViewerBindingBuilder.svelte index f8a14a6dd1..7e79209d8b 100644 --- a/packages/builder/src/components/integration/QueryViewerBindingBuilder.svelte +++ b/packages/builder/src/components/integration/QueryViewerBindingBuilder.svelte @@ -1,10 +1,8 @@