From 0a4c4f1cc0bcc2480049fe22eea4bcfc28b8bdfe Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 May 2021 14:54:47 +0100 Subject: [PATCH 01/18] Adding some changes for to redis library, allowing reconnection. --- hosting/docker-compose.yaml | 2 + packages/auth/src/redis/index.js | 95 +++++++++++-------- packages/worker/src/api/routes/admin/users.js | 9 +- 3 files changed, 68 insertions(+), 38 deletions(-) diff --git a/hosting/docker-compose.yaml b/hosting/docker-compose.yaml index 37657ce009..0cd7bc92bf 100644 --- a/hosting/docker-compose.yaml +++ b/hosting/docker-compose.yaml @@ -105,6 +105,8 @@ services: restart: always image: redis command: redis-server --requirepass ${REDIS_PASSWORD} + ports: + - "${REDIS_PORT}:6379" volumes: - redis_data:/data diff --git a/packages/auth/src/redis/index.js b/packages/auth/src/redis/index.js index 78e3ea7acd..0fd0aa8c83 100644 --- a/packages/auth/src/redis/index.js +++ b/packages/auth/src/redis/index.js @@ -3,40 +3,64 @@ const env = require("../environment") const Redis = env.isTest() ? require("ioredis-mock") : require("ioredis") const { addDbPrefix, removeDbPrefix, getRedisOptions } = require("./utils") +const RETRY_PERIOD_MS = 2000 +const MAX_RETRIES = 20 const CLUSTERED = false // for testing just generate the client once let CONNECTED = false let CLIENT = env.isTest() ? new Redis(getRedisOptions()) : null +function retryConnection() { + setTimeout(init, RETRY_PERIOD_MS) +} + /** * Inits the system, will error if unable to connect to redis cluster (may take up to 10 seconds) otherwise * will return the ioredis client which will be ready to use. - * @return {Promise} The ioredis client. */ function init() { - return new Promise((resolve, reject) => { - // testing uses a single in memory client - if (env.isTest() || (CLIENT && CONNECTED)) { - return resolve(CLIENT) + function errorOccurred(err) { + CONNECTED = false; + console.error("Redis connection failed - " + err) + setTimeout(() => { + init() + }, RETRY_PERIOD_MS) + } + // testing uses a single in memory client + if (env.isTest() || (CLIENT && CONNECTED)) { + return + } + if (CLIENT) { + CLIENT.disconnect() + } + const { opts, host, port } = getRedisOptions(CLUSTERED) + if (CLUSTERED) { + CLIENT = new Redis.Cluster([{ host, port }], opts) + } else { + CLIENT = new Redis(opts) + } + CLIENT.on("end", err => { + errorOccurred(err) + }) + CLIENT.on("error", err => { + errorOccurred(err) + }) + CLIENT.on("connect", () => { + CONNECTED = true + }) +} + +function waitForConnection() { + return new Promise(resolve => { + if (CLIENT == null) { + init() + } else if (CONNECTED) { + resolve() + return } - const { opts, host, port } = getRedisOptions(CLUSTERED) - if (CLUSTERED) { - CLIENT = new Redis.Cluster([{ host, port }], opts) - } else { - CLIENT = new Redis(opts) - } - CLIENT.on("end", err => { - reject(err) - CONNECTED = false - }) - CLIENT.on("error", err => { - reject(err) - CONNECTED = false - }) CLIENT.on("connect", () => { - resolve(CLIENT) - CONNECTED = true + resolve() }) }) } @@ -85,31 +109,30 @@ class RedisWrapper { } async init() { - this._client = await init() + init() + await waitForConnection() return this } async finish() { - this._client.disconnect() + CLIENT.disconnect() } async scan() { - const db = this._db, - client = this._client + const db = this._db let stream if (CLUSTERED) { - let node = client.nodes("master") + let node = CLIENT.nodes("master") stream = node[0].scanStream({ match: db + "-*", count: 100 }) } else { - stream = client.scanStream({ match: db + "-*", count: 100 }) + stream = CLIENT.scanStream({ match: db + "-*", count: 100 }) } return promisifyStream(stream) } async get(key) { - const db = this._db, - client = this._client - let response = await client.get(addDbPrefix(db, key)) + const db = this._db + let response = await CLIENT.get(addDbPrefix(db, key)) // overwrite the prefixed key if (response != null && response.key) { response.key = key @@ -123,22 +146,20 @@ class RedisWrapper { } async store(key, value, expirySeconds = null) { - const db = this._db, - client = this._client + const db = this._db if (typeof value === "object") { value = JSON.stringify(value) } const prefixedKey = addDbPrefix(db, key) - await client.set(prefixedKey, value) + await CLIENT.set(prefixedKey, value) if (expirySeconds) { - await client.expire(prefixedKey, expirySeconds) + await CLIENT.expire(prefixedKey, expirySeconds) } } async delete(key) { - const db = this._db, - client = this._client - await client.del(addDbPrefix(db, key)) + const db = this._db + await CLIENT.del(addDbPrefix(db, key)) } async clear() { diff --git a/packages/worker/src/api/routes/admin/users.js b/packages/worker/src/api/routes/admin/users.js index f334f05e7d..cfff67f3e7 100644 --- a/packages/worker/src/api/routes/admin/users.js +++ b/packages/worker/src/api/routes/admin/users.js @@ -6,6 +6,13 @@ const Joi = require("joi") const router = Router() +function buildAdminInitValidation() { + return joiValidator.body(Joi.object({ + email: Joi.string().required(), + password: Joi.string().required(), + }).required().unknown(false)) +} + function buildUserSaveValidation(isSelf = false) { let schema = { email: Joi.string().allow(null, ""), @@ -74,7 +81,7 @@ router buildInviteAcceptValidation(), controller.inviteAccept ) - .post("/api/admin/users/init", controller.adminUser) + .post("/api/admin/users/init", buildAdminInitValidation(), controller.adminUser) .get("/api/admin/users/self", controller.getSelf) // admin endpoint but needs to come at end (blocks other endpoints otherwise) .get("/api/admin/users/:id", adminOnly, controller.find) From 4b786f51d210078a8b17bb098707d2ae81eb6f87 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 May 2021 14:56:23 +0100 Subject: [PATCH 02/18] Formatting. --- packages/auth/src/redis/index.js | 2 +- packages/worker/src/api/routes/admin/users.js | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/auth/src/redis/index.js b/packages/auth/src/redis/index.js index 0fd0aa8c83..7a1b9a3431 100644 --- a/packages/auth/src/redis/index.js +++ b/packages/auth/src/redis/index.js @@ -21,7 +21,7 @@ function retryConnection() { */ function init() { function errorOccurred(err) { - CONNECTED = false; + CONNECTED = false console.error("Redis connection failed - " + err) setTimeout(() => { init() diff --git a/packages/worker/src/api/routes/admin/users.js b/packages/worker/src/api/routes/admin/users.js index cfff67f3e7..eff873a7b3 100644 --- a/packages/worker/src/api/routes/admin/users.js +++ b/packages/worker/src/api/routes/admin/users.js @@ -7,10 +7,14 @@ const Joi = require("joi") const router = Router() function buildAdminInitValidation() { - return joiValidator.body(Joi.object({ - email: Joi.string().required(), - password: Joi.string().required(), - }).required().unknown(false)) + return joiValidator.body( + Joi.object({ + email: Joi.string().required(), + password: Joi.string().required(), + }) + .required() + .unknown(false) + ) } function buildUserSaveValidation(isSelf = false) { @@ -81,7 +85,11 @@ router buildInviteAcceptValidation(), controller.inviteAccept ) - .post("/api/admin/users/init", buildAdminInitValidation(), controller.adminUser) + .post( + "/api/admin/users/init", + buildAdminInitValidation(), + controller.adminUser + ) .get("/api/admin/users/self", controller.getSelf) // admin endpoint but needs to come at end (blocks other endpoints otherwise) .get("/api/admin/users/:id", adminOnly, controller.find) From b7f739d54a2470c9667a672f5f69e0018dee610e Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 May 2021 14:58:54 +0100 Subject: [PATCH 03/18] Logging and adding better messaging around startup. --- packages/cli/src/hosting/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/hosting/index.js b/packages/cli/src/hosting/index.js index 60d9f13e80..159f705483 100644 --- a/packages/cli/src/hosting/index.js +++ b/packages/cli/src/hosting/index.js @@ -101,10 +101,11 @@ async function init(type) { async function start() { await checkDockerConfigured() checkInitComplete() - console.log(info("Starting services, this may take a moment.")) + console.log(info("Starting services, this may take a moment - first time this may take a few minutes to download images.")) const port = makeEnv.get("MAIN_PORT") await handleError(async () => { - await compose.upAll({ cwd: "./", log: false }) + // need to log as it makes it more clear + await compose.upAll({ cwd: "./", log: true }) }) console.log( success( From 7e6f215598c14bf20f68ea400c0d8fb654328733 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 May 2021 16:20:02 +0100 Subject: [PATCH 04/18] Linting and fixing an issue with the dev pass through. --- packages/auth/src/redis/index.js | 5 ----- packages/server/src/api/controllers/dev.js | 9 +++++++++ packages/worker/src/api/controllers/admin/configs.js | 9 +-------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/auth/src/redis/index.js b/packages/auth/src/redis/index.js index 7a1b9a3431..0a21966301 100644 --- a/packages/auth/src/redis/index.js +++ b/packages/auth/src/redis/index.js @@ -4,17 +4,12 @@ const Redis = env.isTest() ? require("ioredis-mock") : require("ioredis") const { addDbPrefix, removeDbPrefix, getRedisOptions } = require("./utils") const RETRY_PERIOD_MS = 2000 -const MAX_RETRIES = 20 const CLUSTERED = false // for testing just generate the client once let CONNECTED = false let CLIENT = env.isTest() ? new Redis(getRedisOptions()) : null -function retryConnection() { - setTimeout(init, RETRY_PERIOD_MS) -} - /** * Inits the system, will error if unable to connect to redis cluster (may take up to 10 seconds) otherwise * will return the ioredis client which will be ready to use. diff --git a/packages/server/src/api/controllers/dev.js b/packages/server/src/api/controllers/dev.js index 2e90fb83e7..068e1e59c0 100644 --- a/packages/server/src/api/controllers/dev.js +++ b/packages/server/src/api/controllers/dev.js @@ -23,7 +23,16 @@ async function redirect(ctx, method) { if (cookie) { ctx.set("set-cookie", cookie) } + let body + try { + body = await response.json() + } catch (err) { + // don't worry about errors, likely no JSON + } ctx.status = response.status + if (body) { + ctx.body = body + } ctx.cookies } diff --git a/packages/worker/src/api/controllers/admin/configs.js b/packages/worker/src/api/controllers/admin/configs.js index 8a6788cdfd..82466249a2 100644 --- a/packages/worker/src/api/controllers/admin/configs.js +++ b/packages/worker/src/api/controllers/admin/configs.js @@ -6,10 +6,8 @@ const { getGlobalUserParams, getScopedFullConfig, } = require("@budibase/auth").db -const fetch = require("node-fetch") const { Configs } = require("../../../constants") const email = require("../../../utilities/email") -const env = require("../../../environment") const { upload, ObjectStoreBuckets } = require("@budibase/auth").objectStore const APP_PREFIX = "app_" @@ -155,12 +153,7 @@ exports.configChecklist = async function (ctx) { // TODO: Watch get started video // Apps exist - let allDbs - if (env.COUCH_DB_URL) { - allDbs = await (await fetch(`${env.COUCH_DB_URL}/_all_dbs`)).json() - } else { - allDbs = await CouchDB.allDbs() - } + let allDbs = await CouchDB.allDbs() const appDbNames = allDbs.filter(dbName => dbName.startsWith(APP_PREFIX)) // They have set up SMTP From 3873d124763768724a06e306070e5363e9d79c4a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 May 2021 16:20:28 +0100 Subject: [PATCH 05/18] Formatting. --- packages/cli/src/hosting/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/hosting/index.js b/packages/cli/src/hosting/index.js index 159f705483..05d221435c 100644 --- a/packages/cli/src/hosting/index.js +++ b/packages/cli/src/hosting/index.js @@ -101,7 +101,11 @@ async function init(type) { async function start() { await checkDockerConfigured() checkInitComplete() - console.log(info("Starting services, this may take a moment - first time this may take a few minutes to download images.")) + console.log( + info( + "Starting services, this may take a moment - first time this may take a few minutes to download images." + ) + ) const port = makeEnv.get("MAIN_PORT") await handleError(async () => { // need to log as it makes it more clear From 3a9a32f5f63955fb3a2c016b2d6932093ff729d0 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 May 2021 16:30:24 +0100 Subject: [PATCH 06/18] Adding an initial connection timeout of 5 seconds which after it will retry again. --- packages/auth/src/redis/index.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/auth/src/redis/index.js b/packages/auth/src/redis/index.js index 0a21966301..a3cb6385e6 100644 --- a/packages/auth/src/redis/index.js +++ b/packages/auth/src/redis/index.js @@ -4,6 +4,7 @@ const Redis = env.isTest() ? require("ioredis-mock") : require("ioredis") const { addDbPrefix, removeDbPrefix, getRedisOptions } = require("./utils") const RETRY_PERIOD_MS = 2000 +const STARTUP_TIMEOUT_MS = 5000 const CLUSTERED = false // for testing just generate the client once @@ -15,7 +16,10 @@ let CLIENT = env.isTest() ? new Redis(getRedisOptions()) : null * will return the ioredis client which will be ready to use. */ function init() { + let timeout function errorOccurred(err) { + // always clear this on error + clearTimeout(timeout) CONNECTED = false console.error("Redis connection failed - " + err) setTimeout(() => { @@ -26,6 +30,14 @@ function init() { if (env.isTest() || (CLIENT && CONNECTED)) { return } + // start the timer - only allowed 5 seconds to connect + timeout = setTimeout(() => { + if (!CONNECTED) { + errorOccurred() + } + }, STARTUP_TIMEOUT_MS) + + // disconnect any lingering client if (CLIENT) { CLIENT.disconnect() } @@ -35,6 +47,7 @@ function init() { } else { CLIENT = new Redis(opts) } + // attach handlers CLIENT.on("end", err => { errorOccurred(err) }) @@ -42,6 +55,7 @@ function init() { errorOccurred(err) }) CLIENT.on("connect", () => { + clearTimeout(timeout) CONNECTED = true }) } From 85102fab4eb832451acb346daf580e9262bf3138 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 May 2021 16:31:50 +0100 Subject: [PATCH 07/18] Changing how connection is waited for. --- packages/auth/src/redis/index.js | 10 +++++++--- packages/builder/cypress/support/commands.js | 19 +++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/packages/auth/src/redis/index.js b/packages/auth/src/redis/index.js index a3cb6385e6..044a8bcaf9 100644 --- a/packages/auth/src/redis/index.js +++ b/packages/auth/src/redis/index.js @@ -68,9 +68,13 @@ function waitForConnection() { resolve() return } - CLIENT.on("connect", () => { - resolve() - }) + // check if the connection is ready + const interval = setInterval(() => { + if (CONNECTED) { + clearInterval(interval) + resolve() + } + }, 500) }) } diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js index 4f759a60ea..62365cbe87 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -11,12 +11,23 @@ Cypress.Commands.add("login", () => { if (cookie) return cy.visit(`localhost:${Cypress.env("PORT")}/builder`) - cy.contains("Create Test User").click() - cy.get("input").first().type("test@test.com") - cy.get('input[type="password"]').type("test") + cy.get("button").then(btn => { + const adminUserButton = "Create super admin user" + console.log(btn.first().first()) + if (!btn.first().contains(adminUserButton)) { + // create admin user + cy.get("input").first().type("test@test.com") + cy.get('input[type="password"]').first().type("test") + cy.get('input[type="password"]').eq(1).type("test") + cy.contains(adminUserButton).click() + } - cy.contains("Login").click() + // login + cy.get("input").first().type("test@test.com") + cy.get('input[type="password"]').type("test") + cy.contains("Login").click() + }) }) }) From 55aba6b824c98bfb6559f51c1da6549dc970af49 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 May 2021 16:32:42 +0100 Subject: [PATCH 08/18] Changing cypress commands. --- packages/builder/cypress/support/commands.js | 30 ++++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js index 62365cbe87..80d38937ac 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -12,22 +12,22 @@ Cypress.Commands.add("login", () => { cy.visit(`localhost:${Cypress.env("PORT")}/builder`) - cy.get("button").then(btn => { - const adminUserButton = "Create super admin user" - console.log(btn.first().first()) - if (!btn.first().contains(adminUserButton)) { - // create admin user - cy.get("input").first().type("test@test.com") - cy.get('input[type="password"]').first().type("test") - cy.get('input[type="password"]').eq(1).type("test") - cy.contains(adminUserButton).click() - } + // cy.get("button").then(btn => { + // const adminUserButton = "Create super admin user" + // console.log(btn.first().first()) + // if (!btn.first().contains(adminUserButton)) { + // // create admin user + // cy.get("input").first().type("test@test.com") + // cy.get('input[type="password"]').first().type("test") + // cy.get('input[type="password"]').eq(1).type("test") + // cy.contains(adminUserButton).click() + // } - // login - cy.get("input").first().type("test@test.com") - cy.get('input[type="password"]').type("test") - cy.contains("Login").click() - }) + // login + cy.get("input").first().type("test@test.com") + cy.get('input[type="password"]').type("test") + cy.contains("Login").click() + // }) }) }) From ab4e8808072664f2b042d727655c938705a590ae Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 May 2021 17:05:46 +0100 Subject: [PATCH 09/18] Fixing issue with redis updates in tests. --- packages/auth/src/redis/index.js | 36 ++++++++++++++++---------- packages/server/src/app.js | 3 ++- packages/server/src/utilities/redis.js | 5 ++++ 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/packages/auth/src/redis/index.js b/packages/auth/src/redis/index.js index 044a8bcaf9..7d9e9ad637 100644 --- a/packages/auth/src/redis/index.js +++ b/packages/auth/src/redis/index.js @@ -8,8 +8,24 @@ const STARTUP_TIMEOUT_MS = 5000 const CLUSTERED = false // for testing just generate the client once -let CONNECTED = false +let CLOSED = false let CLIENT = env.isTest() ? new Redis(getRedisOptions()) : null +// if in test always connected +let CONNECTED = !!env.isTest() + +function connectionError(timeout, err) { + // manually shut down, ignore errors + if (CLOSED) { + return + } + // always clear this on error + clearTimeout(timeout) + CONNECTED = false + console.error("Redis connection failed - " + err) + setTimeout(() => { + init() + }, RETRY_PERIOD_MS) +} /** * Inits the system, will error if unable to connect to redis cluster (may take up to 10 seconds) otherwise @@ -17,15 +33,7 @@ let CLIENT = env.isTest() ? new Redis(getRedisOptions()) : null */ function init() { let timeout - function errorOccurred(err) { - // always clear this on error - clearTimeout(timeout) - CONNECTED = false - console.error("Redis connection failed - " + err) - setTimeout(() => { - init() - }, RETRY_PERIOD_MS) - } + CLOSED = false // testing uses a single in memory client if (env.isTest() || (CLIENT && CONNECTED)) { return @@ -33,7 +41,7 @@ function init() { // start the timer - only allowed 5 seconds to connect timeout = setTimeout(() => { if (!CONNECTED) { - errorOccurred() + connectionError(timeout) } }, STARTUP_TIMEOUT_MS) @@ -49,10 +57,10 @@ function init() { } // attach handlers CLIENT.on("end", err => { - errorOccurred(err) + connectionError(timeout, err) }) CLIENT.on("error", err => { - errorOccurred(err) + connectionError(timeout, err) }) CLIENT.on("connect", () => { clearTimeout(timeout) @@ -122,12 +130,14 @@ class RedisWrapper { } async init() { + CLOSED = false init() await waitForConnection() return this } async finish() { + CLOSED = true CLIENT.disconnect() } diff --git a/packages/server/src/app.js b/packages/server/src/app.js index 9ec6c2c687..50df056b2a 100644 --- a/packages/server/src/app.js +++ b/packages/server/src/app.js @@ -73,10 +73,11 @@ if (env.isProd()) { const server = http.createServer(app.callback()) destroyable(server) -server.on("close", () => { +server.on("close", async () => { if (env.NODE_ENV !== "jest") { console.log("Server Closed") } + await redis.shutdown() }) module.exports = server.listen(env.PORT || 0, async () => { diff --git a/packages/server/src/utilities/redis.js b/packages/server/src/utilities/redis.js index 8e0f774f42..ae18b82e02 100644 --- a/packages/server/src/utilities/redis.js +++ b/packages/server/src/utilities/redis.js @@ -11,6 +11,11 @@ exports.init = async () => { debounceClient = await new Client(utils.Databases.DEBOUNCE).init() } +exports.shutdown = async () => { + await devAppClient.finish() + await debounceClient.finish() +} + exports.doesUserHaveLock = async (devAppId, user) => { const value = await devAppClient.get(devAppId) if (!value) { From 40abd4fe3d230784ada49f68f927e061791bbda3 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 24 May 2021 17:20:20 +0100 Subject: [PATCH 10/18] v0.9.2 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 6 +++--- packages/server/package.json | 10 +++++----- packages/standard-components/package.json | 4 ++-- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lerna.json b/lerna.json index fa5307c017..0117b31b81 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.1", + "version": "0.9.2", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index d8003df421..2717c8fffd 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.1", + "version": "0.9.2", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 1d05abcc19..66dffd5d8a 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": "0.9.1", + "version": "0.9.2", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index acc8c71a10..8d9124c535 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.1", + "version": "0.9.2", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.1", - "@budibase/client": "^0.9.1", + "@budibase/bbui": "^0.9.2", + "@budibase/client": "^0.9.2", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.1", + "@budibase/string-templates": "^0.9.2", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index ab3766c3e2..9a1b64119e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.1", + "version": "0.9.2", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index b4ab3754a0..aac83671c8 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.1", + "version": "0.9.2", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -18,13 +18,13 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/string-templates": "^0.9.1", + "@budibase/string-templates": "^0.9.2", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" }, "devDependencies": { - "@budibase/standard-components": "^0.9.1", + "@budibase/standard-components": "^0.9.2", "@rollup/plugin-commonjs": "^18.0.0", "@rollup/plugin-node-resolve": "^11.2.1", "fs-extra": "^8.1.0", diff --git a/packages/server/package.json b/packages/server/package.json index 2af49cd91d..9f2e487676 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.1", + "version": "0.9.2", "description": "Budibase Web Server", "main": "src/electron.js", "repository": { @@ -81,9 +81,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.1", - "@budibase/client": "^0.9.1", - "@budibase/string-templates": "^0.9.1", + "@budibase/auth": "^0.9.2", + "@budibase/client": "^0.9.2", + "@budibase/string-templates": "^0.9.2", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", @@ -133,7 +133,7 @@ "zlib": "1.0.5" }, "devDependencies": { - "@budibase/standard-components": "^0.9.1", + "@budibase/standard-components": "^0.9.2", "@jest/test-sequencer": "^24.8.0", "docker-compose": "^0.23.6", "eslint": "^6.8.0", diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index d85097aa21..1a36770828 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -29,11 +29,11 @@ "keywords": [ "svelte" ], - "version": "0.9.1", + "version": "0.9.2", "license": "MIT", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "dependencies": { - "@budibase/bbui": "^0.9.1", + "@budibase/bbui": "^0.9.2", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", "apexcharts": "^3.22.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 8ea3663d49..296ae18374 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.1", + "version": "0.9.2", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index ac59d7450e..9ecb58ece3 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.1", + "version": "0.9.2", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -21,8 +21,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.1", - "@budibase/string-templates": "^0.9.1", + "@budibase/auth": "^0.9.2", + "@budibase/string-templates": "^0.9.2", "@koa/router": "^8.0.0", "aws-sdk": "^2.811.0", "bcryptjs": "^2.4.3", From 18dd7322e44a2f685c9958d9a876f4f56feb2355 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 May 2021 18:45:43 +0100 Subject: [PATCH 11/18] Updating system to allow setting builder/admin as a toggle during the invitation phase of a user. --- .../users/_components/AddUserModal.svelte | 25 +++++++++++-- .../_components/BasicOnboardingModal.svelte | 32 +++++++++++++++-- packages/builder/src/stores/portal/users.js | 35 ++++++++++++++----- .../worker/src/api/controllers/admin/users.js | 7 ++-- packages/worker/src/api/routes/admin/users.js | 1 + packages/worker/src/index.js | 9 ++++- packages/worker/src/utilities/email.js | 9 ++--- packages/worker/src/utilities/redis.js | 33 +++++++++++++---- 8 files changed, 124 insertions(+), 27 deletions(-) diff --git a/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte b/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte index b60f15d976..9504f73b68 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte @@ -5,6 +5,8 @@ Select, ModalContent, notifications, + Toggle, + Label, } from "@budibase/bbui" import { createValidationStore, emailValidator } from "helpers/validation" import { users } from "stores/portal" @@ -13,12 +15,12 @@ const options = ["Email onboarding", "Basic onboarding"] let selected = options[0] + let builder, admin const [email, error, touched] = createValidationStore("", emailValidator) async function createUserFlow() { - const res = await users.invite($email) - console.log(res) + const res = await users.invite({ email: $email, builder, admin }) if (res.status) { notifications.error(res.message) } else { @@ -56,4 +58,23 @@ placeholder="john@doe.com" label="Email" /> +
+
+ + +
+
+ + +
+
+ + diff --git a/packages/builder/src/pages/builder/portal/manage/users/_components/BasicOnboardingModal.svelte b/packages/builder/src/pages/builder/portal/manage/users/_components/BasicOnboardingModal.svelte index 74a696191a..8ecb274585 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/_components/BasicOnboardingModal.svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/_components/BasicOnboardingModal.svelte @@ -1,13 +1,22 @@ {#if show} - - -
- google icon -

Sign in with Google

-
-
+ window.open("/api/admin/auth/google", "_blank")} + > +
+ google icon +

Sign in with Google

+
{/if} diff --git a/packages/server/src/utilities/fileSystem/index.js b/packages/server/src/utilities/fileSystem/index.js index d84c6deb8a..0e267b3613 100644 --- a/packages/server/src/utilities/fileSystem/index.js +++ b/packages/server/src/utilities/fileSystem/index.js @@ -12,8 +12,9 @@ const { streamUpload, deleteFolder, downloadTarball, + uploadDirectory, } = require("./utilities") -const { downloadLibraries, newAppPublicPath } = require("./newApp") +const { downloadLibraries, uploadClientLibrary } = require("./newApp") const download = require("download") const env = require("../../environment") const { homedir } = require("os") @@ -134,7 +135,8 @@ exports.performBackup = async (appId, backupName) => { */ exports.createApp = async appId => { await downloadLibraries(appId) - await newAppPublicPath(appId) + await uploadClientLibrary(appId) + await uploadDirectory(ObjectStoreBuckets.APPS, NODE_MODULES_PATH, appId) } /** diff --git a/packages/server/src/utilities/fileSystem/newApp.js b/packages/server/src/utilities/fileSystem/newApp.js index 91cc771743..22113757c9 100644 --- a/packages/server/src/utilities/fileSystem/newApp.js +++ b/packages/server/src/utilities/fileSystem/newApp.js @@ -26,10 +26,9 @@ exports.downloadLibraries = async appId => { return paths } -exports.newAppPublicPath = async appId => { - const path = join(appId, "public") +exports.uploadClientLibrary = async appId => { const sourcepath = require.resolve("@budibase/client") - const destPath = join(path, "budibase-client.js") + const destPath = join(appId, "budibase-client.js") await streamUpload(BUCKET_NAME, destPath, fs.createReadStream(sourcepath)) } From 39fb5f9b78618c946ddf0635cac14965802a292d Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 24 May 2021 19:54:00 +0100 Subject: [PATCH 13/18] removed node modules upload call --- packages/server/src/utilities/fileSystem/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/server/src/utilities/fileSystem/index.js b/packages/server/src/utilities/fileSystem/index.js index 0e267b3613..750414826a 100644 --- a/packages/server/src/utilities/fileSystem/index.js +++ b/packages/server/src/utilities/fileSystem/index.js @@ -12,7 +12,6 @@ const { streamUpload, deleteFolder, downloadTarball, - uploadDirectory, } = require("./utilities") const { downloadLibraries, uploadClientLibrary } = require("./newApp") const download = require("download") @@ -136,7 +135,6 @@ exports.performBackup = async (appId, backupName) => { exports.createApp = async appId => { await downloadLibraries(appId) await uploadClientLibrary(appId) - await uploadDirectory(ObjectStoreBuckets.APPS, NODE_MODULES_PATH, appId) } /** From 064f1e961bdb1ec7a93e27e9fa7c12c9ce505013 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 24 May 2021 21:30:46 +0100 Subject: [PATCH 14/18] sanitizing client lib URL --- packages/auth/src/objectStore/index.js | 4 ++++ packages/server/src/utilities/index.js | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/auth/src/objectStore/index.js b/packages/auth/src/objectStore/index.js index c6d1c3e2ce..a157332ae5 100644 --- a/packages/auth/src/objectStore/index.js +++ b/packages/auth/src/objectStore/index.js @@ -34,11 +34,15 @@ function sanitizeKey(input) { return sanitize(sanitizeBucket(input)).replace(/\\/g, "/") } +exports.sanitizeKey = sanitizeKey + // simply handles the dev app to app conversion function sanitizeBucket(input) { return input.replace(new RegExp(APP_DEV_PREFIX, "g"), APP_PREFIX) } +exports.sanitizeBucket = sanitizeBucket + function publicPolicy(bucketName) { return { Version: "2012-10-17", diff --git a/packages/server/src/utilities/index.js b/packages/server/src/utilities/index.js index 1c85d9c7b4..320d4a3eb5 100644 --- a/packages/server/src/utilities/index.js +++ b/packages/server/src/utilities/index.js @@ -1,6 +1,7 @@ const env = require("../environment") const { OBJ_STORE_DIRECTORY, ObjectStoreBuckets } = require("../constants") const { getAllApps } = require("@budibase/auth/db") +const { sanitizeKey } = require("@budibase/auth/src/objectStore") const BB_CDN = "https://cdn.app.budi.live/assets" @@ -43,7 +44,9 @@ exports.objectStoreUrl = () => { */ exports.clientLibraryPath = appId => { if (env.isProd()) { - return `${exports.objectStoreUrl()}/${appId}/budibase-client.js` + return `${exports.objectStoreUrl()}/${sanitizeKey( + appId + )}/budibase-client.js` } else { return `/api/assets/client` } From 4c9c7b05a3f9a6a5461b0c9117dc216451fa2e93 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 24 May 2021 21:54:28 +0100 Subject: [PATCH 15/18] v0.9.3 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 6 +++--- packages/server/package.json | 10 +++++----- packages/standard-components/package.json | 4 ++-- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lerna.json b/lerna.json index 0117b31b81..01ddc677aa 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.2", + "version": "0.9.3", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 2717c8fffd..29b9ef380c 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.2", + "version": "0.9.3", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 66dffd5d8a..e30c0821af 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": "0.9.2", + "version": "0.9.3", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 8d9124c535..62891c187d 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.2", + "version": "0.9.3", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.2", - "@budibase/client": "^0.9.2", + "@budibase/bbui": "^0.9.3", + "@budibase/client": "^0.9.3", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.2", + "@budibase/string-templates": "^0.9.3", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 9a1b64119e..2c41142a08 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.2", + "version": "0.9.3", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index aac83671c8..879332db7e 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.2", + "version": "0.9.3", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -18,13 +18,13 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/string-templates": "^0.9.2", + "@budibase/string-templates": "^0.9.3", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" }, "devDependencies": { - "@budibase/standard-components": "^0.9.2", + "@budibase/standard-components": "^0.9.3", "@rollup/plugin-commonjs": "^18.0.0", "@rollup/plugin-node-resolve": "^11.2.1", "fs-extra": "^8.1.0", diff --git a/packages/server/package.json b/packages/server/package.json index 9f2e487676..e76c4e4e4b 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.2", + "version": "0.9.3", "description": "Budibase Web Server", "main": "src/electron.js", "repository": { @@ -81,9 +81,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.2", - "@budibase/client": "^0.9.2", - "@budibase/string-templates": "^0.9.2", + "@budibase/auth": "^0.9.3", + "@budibase/client": "^0.9.3", + "@budibase/string-templates": "^0.9.3", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", @@ -133,7 +133,7 @@ "zlib": "1.0.5" }, "devDependencies": { - "@budibase/standard-components": "^0.9.2", + "@budibase/standard-components": "^0.9.3", "@jest/test-sequencer": "^24.8.0", "docker-compose": "^0.23.6", "eslint": "^6.8.0", diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index 1a36770828..ccbdd00f33 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -29,11 +29,11 @@ "keywords": [ "svelte" ], - "version": "0.9.2", + "version": "0.9.3", "license": "MIT", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "dependencies": { - "@budibase/bbui": "^0.9.2", + "@budibase/bbui": "^0.9.3", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", "apexcharts": "^3.22.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 296ae18374..ae1e4f3a51 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.2", + "version": "0.9.3", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 9ecb58ece3..d849d98450 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.2", + "version": "0.9.3", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -21,8 +21,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.2", - "@budibase/string-templates": "^0.9.2", + "@budibase/auth": "^0.9.3", + "@budibase/string-templates": "^0.9.3", "@koa/router": "^8.0.0", "aws-sdk": "^2.811.0", "bcryptjs": "^2.4.3", From 617a3035d291c33712b0845097f6745b50e076e4 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 24 May 2021 22:17:03 +0100 Subject: [PATCH 16/18] amend s3 path URL --- packages/server/src/constants/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/constants/index.js b/packages/server/src/constants/index.js index 728938759e..b4af7b29ee 100644 --- a/packages/server/src/constants/index.js +++ b/packages/server/src/constants/index.js @@ -103,7 +103,7 @@ exports.AutoFieldSubTypes = { AUTO_ID: "autoID", } -exports.OBJ_STORE_DIRECTORY = "/app-assets/assets" +exports.OBJ_STORE_DIRECTORY = "/prod-budi-app-assets/assets" exports.BaseQueryVerbs = { CREATE: "create", READ: "read", From 0fce78d19d789d0f10d9955695fcdb281a914154 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 24 May 2021 22:39:13 +0100 Subject: [PATCH 17/18] removing electron config --- packages/server/package.json | 26 ------------------- .../server/src/api/routes/tests/row.spec.js | 2 +- packages/server/src/constants/index.js | 2 +- 3 files changed, 2 insertions(+), 28 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index e76c4e4e4b..a8446bb76c 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -8,29 +8,6 @@ "type": "git", "url": "https://github.com/Budibase/budibase.git" }, - "build": { - "icon": "./build/icons/512x512.png", - "appId": "com.budibase.builder", - "productName": "Budibase Builder", - "afterSign": "electron-builder-notarize", - "mac": { - "icon": "./assets/icons/icon.icns", - "category": "public.app-category.developer-tools", - "hardenedRuntime": true - }, - "linux": { - "maintainer": "Budibase", - "icon": "./build/icons/", - "target": [ - "deb", - "AppImage" - ], - "category": "Development" - }, - "extraMetadata": { - "name": "Budibase Builder" - } - }, "scripts": { "test": "jest --testPathIgnorePatterns=routes && yarn run test:integration", "test:integration": "jest --coverage --detectOpenHandles", @@ -41,9 +18,6 @@ "dev:stack:down": "node scripts/dev/manage.js down", "dev:stack:nuke": "node scripts/dev/manage.js nuke", "dev:builder": "yarn run dev:stack:up && nodemon src/index.js", - "electron": "electron src/electron.js", - "build:electron": "electron-builder --dir", - "publish:electron": "electron-builder -mwl --publish always", "lint": "eslint --fix src/", "initialise": "node scripts/initialise.js" }, diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js index f67d6254db..9d7413d6af 100644 --- a/packages/server/src/api/routes/tests/row.spec.js +++ b/packages/server/src/api/routes/tests/row.spec.js @@ -392,7 +392,7 @@ describe("/rows", () => { await setup.switchToSelfHosted(async () => { const enriched = await outputProcessing(config.getAppId(), table, [row]) expect(enriched[0].attachment[0].url).toBe( - `/prod-budi-app-assets/assets/${config.getAppId()}/attachment/test/thing.csv` + `/prod-budi-app-assets/${config.getAppId()}/attachment/test/thing.csv` ) }) }) diff --git a/packages/server/src/constants/index.js b/packages/server/src/constants/index.js index b4af7b29ee..7311162db8 100644 --- a/packages/server/src/constants/index.js +++ b/packages/server/src/constants/index.js @@ -103,7 +103,7 @@ exports.AutoFieldSubTypes = { AUTO_ID: "autoID", } -exports.OBJ_STORE_DIRECTORY = "/prod-budi-app-assets/assets" +exports.OBJ_STORE_DIRECTORY = "/prod-budi-app-assets" exports.BaseQueryVerbs = { CREATE: "create", READ: "read", From b923b63a44edef135735e849835afcdf69b4cb26 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 24 May 2021 22:50:58 +0100 Subject: [PATCH 18/18] fix test --- packages/server/src/api/routes/tests/row.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js index 9d7413d6af..20ec4a20c9 100644 --- a/packages/server/src/api/routes/tests/row.spec.js +++ b/packages/server/src/api/routes/tests/row.spec.js @@ -384,7 +384,7 @@ describe("/rows", () => { name: "test", description: "test", attachment: [{ - key: `/assets/${config.getAppId()}/attachment/test/thing.csv`, + key: `${config.getAppId()}/attachment/test/thing.csv`, }], tableId: table._id, })