diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 7d09451614..7f1e08601a 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -170,7 +170,8 @@ jobs: docker pull mongo:7.0-jammy & docker pull mariadb:lts & 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 minio/minio & docker pull redis & wait $(jobs -p) diff --git a/charts/budibase/templates/automation-worker-service-hpa.yaml b/charts/budibase/templates/automation-worker-service-hpa.yaml index f29223b61b..18f9690c00 100644 --- a/charts/budibase/templates/automation-worker-service-hpa.yaml +++ b/charts/budibase/templates/automation-worker-service-hpa.yaml @@ -2,7 +2,7 @@ apiVersion: {{ ternary "autoscaling/v2" "autoscaling/v2beta2" (.Capabilities.APIVersions.Has "autoscaling/v2") }} kind: HorizontalPodAutoscaler metadata: - name: {{ include "budibase.fullname" . }}-apps + name: {{ include "budibase.fullname" . }}-automation-worker labels: {{- include "budibase.labels" . | nindent 4 }} spec: diff --git a/globalSetup.ts b/globalSetup.ts index dd1a7dbaa0..dd1454b6e1 100644 --- a/globalSetup.ts +++ b/globalSetup.ts @@ -46,7 +46,7 @@ export default async function setup() { await killContainers(containers) try { - let couchdb = new GenericContainer("budibase/couchdb:v3.2.1-sqs") + const couchdb = new GenericContainer("budibase/couchdb:v3.2.1-sqs") .withExposedPorts(5984, 4984) .withEnvironment({ COUCHDB_PASSWORD: "budibase", @@ -69,7 +69,20 @@ export default async function setup() { ).withStartupTimeout(20000) ) - await couchdb.start() + const minio = new GenericContainer("minio/minio") + .withExposedPorts(9000) + .withCommand(["server", "/data"]) + .withEnvironment({ + MINIO_ACCESS_KEY: "budibase", + MINIO_SECRET_KEY: "budibase", + }) + .withLabels({ "com.budibase": "true" }) + .withReuse() + .withWaitStrategy( + Wait.forHttp("/minio/health/ready", 9000).withStartupTimeout(10000) + ) + + await Promise.all([couchdb.start(), minio.start()]) } finally { lockfile.unlockSync(lockPath) } diff --git a/lerna.json b/lerna.json index 6ba05e19ee..9c5a6c6bab 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.24.1", + "version": "2.24.2", "npmClient": "yarn", "packages": [ "packages/*", diff --git a/packages/backend-core/src/db/lucene.ts b/packages/backend-core/src/db/lucene.ts index d9dddd0097..f5ad7e6433 100644 --- a/packages/backend-core/src/db/lucene.ts +++ b/packages/backend-core/src/db/lucene.ts @@ -12,6 +12,10 @@ import { dataFilters } from "@budibase/shared-core" export const removeKeyNumbering = dataFilters.removeKeyNumbering +function isEmpty(value: any) { + return value == null || value === "" +} + /** * Class to build lucene query URLs. * Optionally takes a base lucene query object. @@ -282,15 +286,14 @@ export class QueryBuilder { } const equal = (key: string, value: any) => { - // 0 evaluates to false, which means we would return all rows if we don't check it - if (!value && value !== 0) { + if (isEmpty(value)) { return null } return `${key}:${builder.preprocess(value, allPreProcessingOpts)}` } const contains = (key: string, value: any, mode = "AND") => { - if (!value || (Array.isArray(value) && value.length === 0)) { + if (isEmpty(value)) { return null } if (!Array.isArray(value)) { @@ -306,7 +309,7 @@ export class QueryBuilder { } const fuzzy = (key: string, value: any) => { - if (!value) { + if (isEmpty(value)) { return null } value = builder.preprocess(value, { @@ -328,7 +331,7 @@ export class QueryBuilder { } const oneOf = (key: string, value: any) => { - if (!value) { + if (isEmpty(value)) { return `*:*` } if (!Array.isArray(value)) { @@ -386,7 +389,7 @@ export class QueryBuilder { // Construct the actual lucene search query string from JSON structure if (this.#query.string) { build(this.#query.string, (key: string, value: any) => { - if (!value) { + if (isEmpty(value)) { return null } value = builder.preprocess(value, { @@ -399,7 +402,7 @@ export class QueryBuilder { } if (this.#query.range) { build(this.#query.range, (key: string, value: any) => { - if (!value) { + if (isEmpty(value)) { return null } if (value.low == null || value.low === "") { @@ -421,7 +424,7 @@ export class QueryBuilder { } if (this.#query.notEqual) { build(this.#query.notEqual, (key: string, value: any) => { - if (!value) { + if (isEmpty(value)) { return null } if (typeof value === "boolean") { @@ -431,10 +434,28 @@ export class QueryBuilder { }) } if (this.#query.empty) { - build(this.#query.empty, (key: string) => `(*:* -${key}:["" TO *])`) + build(this.#query.empty, (key: string) => { + // Because the structure of an empty filter looks like this: + // { empty: { someKey: null } } + // + // The check inside of `build` does not set `allFiltersEmpty`, which results + // in weird behaviour when the empty filter is the only filter. We get around + // this by setting `allFiltersEmpty` to false here. + allFiltersEmpty = false + return `(*:* -${key}:["" TO *])` + }) } if (this.#query.notEmpty) { - build(this.#query.notEmpty, (key: string) => `${key}:["" TO *]`) + build(this.#query.notEmpty, (key: string) => { + // Because the structure of a notEmpty filter looks like this: + // { notEmpty: { someKey: null } } + // + // The check inside of `build` does not set `allFiltersEmpty`, which results + // in weird behaviour when the empty filter is the only filter. We get around + // this by setting `allFiltersEmpty` to false here. + allFiltersEmpty = false + return `${key}:["" TO *]` + }) } if (this.#query.oneOf) { build(this.#query.oneOf, oneOf) diff --git a/packages/backend-core/src/objectStore/objectStore.ts b/packages/backend-core/src/objectStore/objectStore.ts index aa5365c5c3..2bef91ffef 100644 --- a/packages/backend-core/src/objectStore/objectStore.ts +++ b/packages/backend-core/src/objectStore/objectStore.ts @@ -83,7 +83,7 @@ export function ObjectStore( bucket: string, opts: { presigning: boolean } = { presigning: false } ) { - const config: any = { + const config: AWS.S3.ClientConfiguration = { s3ForcePathStyle: true, signatureVersion: "v4", apiVersion: "2006-03-01", diff --git a/packages/backend-core/tests/core/utilities/index.ts b/packages/backend-core/tests/core/utilities/index.ts index b2f19a0286..787d69be2c 100644 --- a/packages/backend-core/tests/core/utilities/index.ts +++ b/packages/backend-core/tests/core/utilities/index.ts @@ -4,6 +4,3 @@ export { generator } from "./structures" export * as testContainerUtils from "./testContainerUtils" export * as utils from "./utils" export * from "./jestUtils" -import * as minio from "./minio" - -export const objectStoreTestProviders = { minio } diff --git a/packages/backend-core/tests/core/utilities/minio.ts b/packages/backend-core/tests/core/utilities/minio.ts deleted file mode 100644 index cef33daa91..0000000000 --- a/packages/backend-core/tests/core/utilities/minio.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { GenericContainer, Wait, StartedTestContainer } from "testcontainers" -import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy" -import env from "../../../src/environment" - -let container: StartedTestContainer | undefined - -class ObjectStoreWaitStrategy extends AbstractWaitStrategy { - async waitUntilReady(container: any, boundPorts: any, startTime?: Date) { - const logs = Wait.forListeningPorts() - await logs.waitUntilReady(container, boundPorts, startTime) - } -} - -export async function start(): Promise { - container = await new GenericContainer("minio/minio") - .withExposedPorts(9000) - .withCommand(["server", "/data"]) - .withEnvironment({ - MINIO_ACCESS_KEY: "budibase", - MINIO_SECRET_KEY: "budibase", - }) - .withWaitStrategy(new ObjectStoreWaitStrategy().withStartupTimeout(30000)) - .start() - - const port = container.getMappedPort(9000) - env._set("MINIO_URL", `http://0.0.0.0:${port}`) -} - -export async function stop() { - if (container) { - await container.stop() - container = undefined - } -} diff --git a/packages/backend-core/tests/core/utilities/testContainerUtils.ts b/packages/backend-core/tests/core/utilities/testContainerUtils.ts index 32841e4c3a..1a25bb28f4 100644 --- a/packages/backend-core/tests/core/utilities/testContainerUtils.ts +++ b/packages/backend-core/tests/core/utilities/testContainerUtils.ts @@ -86,10 +86,18 @@ export function setupEnv(...envs: any[]) { throw new Error("CouchDB SQL port not found") } + const minio = getContainerByImage("minio/minio") + + const minioPort = getExposedV4Port(minio, 9000) + if (!minioPort) { + throw new Error("Minio port not found") + } + const configs = [ { key: "COUCH_DB_PORT", value: `${couchPort}` }, { key: "COUCH_DB_URL", value: `http://127.0.0.1:${couchPort}` }, { key: "COUCH_DB_SQL_URL", value: `http://127.0.0.1:${couchSqlPort}` }, + { key: "MINIO_URL", value: `http://127.0.0.1:${minioPort}` }, ] for (const config of configs.filter(x => !!x.value)) { diff --git a/packages/bbui/src/Banner/Banner.svelte b/packages/bbui/src/Banner/Banner.svelte index a04d469cc7..2ce9795d70 100644 --- a/packages/bbui/src/Banner/Banner.svelte +++ b/packages/bbui/src/Banner/Banner.svelte @@ -8,6 +8,8 @@ export let size = "S" export let extraButtonText export let extraButtonAction + export let extraLinkText + export let extraLinkAction export let showCloseButton = true let show = true @@ -28,8 +30,13 @@
-
+
+ {#if extraLinkText} + + {/if}
{#if extraButtonText && extraButtonAction}