diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 288a0462e7..d63596f08f 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -108,7 +108,7 @@ jobs: - name: Pull testcontainers images run: | docker pull testcontainers/ryuk:0.5.1 & - docker pull budibase/couchdb:v3.2.1-sql & + docker pull budibase/couchdb:v3.2.1-sqs & docker pull redis & wait $(jobs -p) diff --git a/lerna.json b/lerna.json index 4d2b5a6cf8..efcbb7694c 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "2.29.15", + "version": "2.29.20", "npmClient": "yarn", "packages": [ "packages/*", diff --git a/packages/backend-core/src/db/couch/connections.ts b/packages/backend-core/src/db/couch/connections.ts index 454715e2cb..5c7d7ec81d 100644 --- a/packages/backend-core/src/db/couch/connections.ts +++ b/packages/backend-core/src/db/couch/connections.ts @@ -25,7 +25,10 @@ export const getCouchInfo = (connection?: string) => { } const authCookie = Buffer.from(`${username}:${password}`).toString("base64") let sqlUrl = env.COUCH_DB_SQL_URL - if (!sqlUrl && urlInfo.url) { + // default for dev + if (env.isDev() && !sqlUrl) { + sqlUrl = "http://localhost:4006" + } else if (!sqlUrl && urlInfo.url) { const parsed = new URL(urlInfo.url) // attempt to connect on default port sqlUrl = urlInfo.url.replace(parsed.port, "4984") diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts index 906a95e1db..a7ad453066 100644 --- a/packages/backend-core/src/db/utils.ts +++ b/packages/backend-core/src/db/utils.ts @@ -1,6 +1,6 @@ import env from "../environment" import { DEFAULT_TENANT_ID, SEPARATOR, DocumentType } from "../constants" -import { getTenantId, getGlobalDBName } from "../context" +import { getTenantId, getGlobalDBName, isMultiTenant } from "../context" import { doWithDB, directCouchAllDbs } from "./db" import { AppState, DeletedApp, getAppMetadata } from "../cache/appMetadata" import { isDevApp, isDevAppID, getProdAppID } from "../docIds/conversions" @@ -206,3 +206,34 @@ export function pagination( nextPage, } } + +export function isSqsEnabledForTenant(): boolean { + const tenantId = getTenantId() + if (!env.SQS_SEARCH_ENABLE) { + return false + } + + // single tenant (self host and dev) always enabled if flag set + if (!isMultiTenant()) { + return true + } + + // This is to guard against the situation in tests where tests pass because + // we're not actually using SQS, we're using Lucene and the tests pass due to + // parity. + if (env.isTest() && env.SQS_SEARCH_ENABLE_TENANTS.length === 0) { + throw new Error( + "to enable SQS you must specify a list of tenants in the SQS_SEARCH_ENABLE_TENANTS env var" + ) + } + + // Special case to enable all tenants, for testing in QA. + if ( + env.SQS_SEARCH_ENABLE_TENANTS.length === 1 && + env.SQS_SEARCH_ENABLE_TENANTS[0] === "*" + ) { + return true + } + + return env.SQS_SEARCH_ENABLE_TENANTS.includes(tenantId) +} diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index e06d51f918..64e3187956 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -114,8 +114,11 @@ const environment = { ENCRYPTION_KEY: process.env.ENCRYPTION_KEY, API_ENCRYPTION_KEY: getAPIEncryptionKey(), COUCH_DB_URL: process.env.COUCH_DB_URL || "http://localhost:4005", - COUCH_DB_SQL_URL: process.env.COUCH_DB_SQL_URL || "http://localhost:4006", + COUCH_DB_SQL_URL: process.env.COUCH_DB_SQL_URL, SQS_SEARCH_ENABLE: process.env.SQS_SEARCH_ENABLE, + SQS_SEARCH_ENABLE_TENANTS: + process.env.SQS_SEARCH_ENABLE_TENANTS?.split(",") || [], + SQS_MIGRATION_ENABLE: process.env.SQS_MIGRATION_ENABLE, COUCH_DB_USERNAME: process.env.COUCH_DB_USER, COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD, GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID, diff --git a/packages/backend-core/src/redis/tests/redis.spec.ts b/packages/backend-core/src/redis/tests/redis.spec.ts index 4d11caf220..9160c6a6dd 100644 --- a/packages/backend-core/src/redis/tests/redis.spec.ts +++ b/packages/backend-core/src/redis/tests/redis.spec.ts @@ -2,6 +2,7 @@ import { GenericContainer, StartedTestContainer } from "testcontainers" import { generator, structures } from "../../../tests" import RedisWrapper from "../redis" import { env } from "../.." +import { randomUUID } from "crypto" jest.setTimeout(30000) @@ -52,10 +53,10 @@ describe("redis", () => { describe("bulkStore", () => { function createRandomObject( keyLength: number, - valueGenerator: () => any = () => generator.word() + valueGenerator: () => any = () => randomUUID() ) { return generator - .unique(() => generator.word(), keyLength) + .unique(() => randomUUID(), keyLength) .reduce((acc, key) => { acc[key] = valueGenerator() return acc diff --git a/packages/backend-core/src/users/users.ts b/packages/backend-core/src/users/users.ts index 7d62a6ef39..0c994d8287 100644 --- a/packages/backend-core/src/users/users.ts +++ b/packages/backend-core/src/users/users.ts @@ -18,9 +18,10 @@ import { CouchFindOptions, DatabaseQueryOpts, SearchFilters, - SearchFilterOperator, SearchUsersRequest, User, + BasicOperator, + ArrayOperator, } from "@budibase/types" import * as context from "../context" import { getGlobalDB } from "../context" @@ -46,9 +47,9 @@ function removeUserPassword(users: User | User[]) { export function isSupportedUserSearch(query: SearchFilters) { const allowed = [ - { op: SearchFilterOperator.STRING, key: "email" }, - { op: SearchFilterOperator.EQUAL, key: "_id" }, - { op: SearchFilterOperator.ONE_OF, key: "_id" }, + { op: BasicOperator.STRING, key: "email" }, + { op: BasicOperator.EQUAL, key: "_id" }, + { op: ArrayOperator.ONE_OF, key: "_id" }, ] for (let [key, operation] of Object.entries(query)) { if (typeof operation !== "object") { diff --git a/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte b/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte index 7898e13ec8..b5fe6d03fd 100644 --- a/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte @@ -30,6 +30,16 @@ return lowerA > lowerB ? 1 : -1 }) + $: groupedAutomations = filteredAutomations.reduce((acc, auto) => { + acc[auto.definition.trigger.event] ??= { + icon: auto.definition.trigger.icon, + name: (auto.definition.trigger?.name || "").toUpperCase(), + entries: [], + } + acc[auto.definition.trigger.event].entries.push(auto) + return acc + }, {}) + $: showNoResults = searchString && !filteredAutomations.length onMount(async () => { @@ -55,16 +65,25 @@ />