From a26b64bb03b97879d7e2cfc3eefa6415230e2f92 Mon Sep 17 00:00:00 2001 From: Peter Clement <peter@budibase.com> Date: Fri, 18 Oct 2024 15:05:17 +0100 Subject: [PATCH 01/14] pass user through to automation context --- .../server/src/api/controllers/automation.ts | 4 +++ .../server/src/api/controllers/row/index.ts | 25 +++++++++++--- .../src/api/controllers/rowAction/run.ts | 2 +- .../tests/scenarios/scenarios.spec.ts | 34 +++++++++++++++++++ packages/server/src/automations/triggers.ts | 10 +++++- .../server/src/definitions/automations.ts | 3 +- packages/server/src/events/BudibaseEmitter.ts | 7 ++-- packages/server/src/events/utils.ts | 6 +++- packages/server/src/sdk/app/rowActions.ts | 9 ++++- packages/server/src/threads/automation.ts | 5 ++- .../documents/app/automation/automation.ts | 1 + packages/types/src/sdk/automations/index.ts | 3 +- 12 files changed, 95 insertions(+), 14 deletions(-) diff --git a/packages/server/src/api/controllers/automation.ts b/packages/server/src/api/controllers/automation.ts index 6177868303..df23f9f3b7 100644 --- a/packages/server/src/api/controllers/automation.ts +++ b/packages/server/src/api/controllers/automation.ts @@ -13,6 +13,7 @@ import { UserCtx, DeleteAutomationResponse, FetchAutomationResponse, + User, } from "@budibase/types" import { getActionDefinitions as actionDefs } from "../../automations/actions" import sdk from "../../sdk" @@ -159,6 +160,7 @@ export async function trigger(ctx: UserCtx) { automation, { fields: ctx.request.body.fields, + user: ctx.user as User, timeout: ctx.request.body.timeout * 1000 || env.AUTOMATION_THREAD_TIMEOUT, }, @@ -183,6 +185,7 @@ export async function trigger(ctx: UserCtx) { await triggers.externalTrigger(automation, { ...ctx.request.body, appId: ctx.appId, + user: ctx.user as User, }) ctx.body = { message: `Automation ${automation._id} has been triggered.`, @@ -212,6 +215,7 @@ export async function test(ctx: UserCtx) { { ...testInput, appId: ctx.appId, + user: ctx.user, }, { getResponses: true } ) diff --git a/packages/server/src/api/controllers/row/index.ts b/packages/server/src/api/controllers/row/index.ts index 60775ce628..67502f8b36 100644 --- a/packages/server/src/api/controllers/row/index.ts +++ b/packages/server/src/api/controllers/row/index.ts @@ -65,7 +65,14 @@ export async function patch( } ctx.status = 200 ctx.eventEmitter && - ctx.eventEmitter.emitRow(`row:update`, appId, row, table, oldRow) + ctx.eventEmitter.emitRow( + `row:update`, + appId, + row, + table, + oldRow, + ctx.user + ) ctx.message = `${table.name} updated successfully.` ctx.body = row gridSocket?.emitRowUpdate(ctx, row) @@ -96,7 +103,8 @@ export const save = async (ctx: UserCtx<Row, Row>) => { sdk.rows.save(sourceId, ctx.request.body, ctx.user?._id) ) ctx.status = 200 - ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:save`, appId, row, table) + ctx.eventEmitter && + ctx.eventEmitter.emitRow(`row:save`, appId, row, table, null, ctx.user) ctx.message = `${table.name} saved successfully` // prefer squashed for response ctx.body = row || squashed @@ -168,7 +176,8 @@ async function deleteRows(ctx: UserCtx<DeleteRowRequest>) { } for (let row of rows) { - ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row) + ctx.eventEmitter && + ctx.eventEmitter.emitRow(`row:delete`, appId, row, null, null, ctx.user) gridSocket?.emitRowDeletion(ctx, row) } @@ -184,7 +193,15 @@ async function deleteRow(ctx: UserCtx<DeleteRowRequest>) { await quotas.removeRow() } - ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, resp.row) + ctx.eventEmitter && + ctx.eventEmitter.emitRow( + `row:delete`, + appId, + resp.row, + null, + null, + ctx.user + ) gridSocket?.emitRowDeletion(ctx, resp.row) return resp diff --git a/packages/server/src/api/controllers/rowAction/run.ts b/packages/server/src/api/controllers/rowAction/run.ts index c4b6a276bf..05bdb7bace 100644 --- a/packages/server/src/api/controllers/rowAction/run.ts +++ b/packages/server/src/api/controllers/rowAction/run.ts @@ -5,6 +5,6 @@ export async function run(ctx: Ctx<RowActionTriggerRequest, void>) { const { tableId, actionId } = ctx.params const { rowId } = ctx.request.body - await sdk.rowActions.run(tableId, actionId, rowId) + await sdk.rowActions.run(tableId, actionId, rowId, ctx.user) ctx.status = 200 } diff --git a/packages/server/src/automations/tests/scenarios/scenarios.spec.ts b/packages/server/src/automations/tests/scenarios/scenarios.spec.ts index 850a04a95a..358aba35c8 100644 --- a/packages/server/src/automations/tests/scenarios/scenarios.spec.ts +++ b/packages/server/src/automations/tests/scenarios/scenarios.spec.ts @@ -482,4 +482,38 @@ describe("Automation Scenarios", () => { } ) }) + + it("Check user is passed through from row trigger", async () => { + const table = await config.createTable() + + const builder = createAutomationBuilder({ + name: "Test a user is successfully passed from the trigger", + }) + + const results = await builder + .rowUpdated( + { tableId: table._id! }, + { + row: { name: "Test", description: "TEST" }, + id: "1234", + } + ) + .serverLog({ text: "{{ [user].[email] }}" }) + .run() + + expect(results.steps[0].outputs.message).toContain("example.com") + }) + + it("Check user is passed through from app trigger", async () => { + const builder = createAutomationBuilder({ + name: "Test a user is successfully passed from the trigger", + }) + + const results = await builder + .appAction({ fields: {} }) + .serverLog({ text: "{{ [user].[email] }}" }) + .run() + + expect(results.steps[0].outputs.message).toContain("example.com") + }) }) diff --git a/packages/server/src/automations/triggers.ts b/packages/server/src/automations/triggers.ts index 110ccfa37a..efcbf27644 100644 --- a/packages/server/src/automations/triggers.ts +++ b/packages/server/src/automations/triggers.ts @@ -19,6 +19,7 @@ import { AutomationStoppedReason, AutomationStatus, AutomationRowEvent, + User, } from "@budibase/types" import { executeInThread } from "../threads/automation" import { dataFilters, sdk } from "@budibase/shared-core" @@ -140,9 +141,15 @@ function rowPassesFilters(row: Row, filters: SearchFilters) { export async function externalTrigger( automation: Automation, - params: { fields: Record<string, any>; timeout?: number; appId?: string }, + params: { + fields: Record<string, any> + timeout?: number + appId?: string + user?: User + }, { getResponses }: { getResponses?: boolean } = {} ): Promise<any> { + console.log("user: " + params.user) if (automation.disabled) { throw new Error("Automation is disabled") } @@ -189,6 +196,7 @@ export async function externalTrigger( appId: context.getAppId(), automation, } + console.log(data) return executeInThread({ data } as AutomationJob) } else { return automationQueue.add(data, JOB_OPTS) diff --git a/packages/server/src/definitions/automations.ts b/packages/server/src/definitions/automations.ts index 9433075da7..70faed9327 100644 --- a/packages/server/src/definitions/automations.ts +++ b/packages/server/src/definitions/automations.ts @@ -1,4 +1,4 @@ -import { AutomationResults, LoopStepType } from "@budibase/types" +import { AutomationResults, LoopStepType, User } from "@budibase/types" export interface LoopInput { option: LoopStepType @@ -18,5 +18,6 @@ export interface AutomationContext extends AutomationResults { stepsById: Record<string, any> stepsByName: Record<string, any> env?: Record<string, string> + user?: User trigger: any } diff --git a/packages/server/src/events/BudibaseEmitter.ts b/packages/server/src/events/BudibaseEmitter.ts index 8feb36bbf5..d09ea20a5d 100644 --- a/packages/server/src/events/BudibaseEmitter.ts +++ b/packages/server/src/events/BudibaseEmitter.ts @@ -1,6 +1,6 @@ import { EventEmitter } from "events" import { rowEmission, tableEmission } from "./utils" -import { Table, Row } from "@budibase/types" +import { Table, Row, User } from "@budibase/types" /** * keeping event emitter in one central location as it might be used for things other than @@ -18,9 +18,10 @@ class BudibaseEmitter extends EventEmitter { appId: string, row: Row, table?: Table, - oldRow?: Row + oldRow?: Row, + user?: User ) { - rowEmission({ emitter: this, eventName, appId, row, table, oldRow }) + rowEmission({ emitter: this, eventName, appId, row, table, oldRow, user }) } emitTable(eventName: string, appId: string, table?: Table) { diff --git a/packages/server/src/events/utils.ts b/packages/server/src/events/utils.ts index b972c8e473..5e4a1bebbf 100644 --- a/packages/server/src/events/utils.ts +++ b/packages/server/src/events/utils.ts @@ -1,4 +1,4 @@ -import { Table, Row } from "@budibase/types" +import { Table, Row, User } from "@budibase/types" import BudibaseEmitter from "./BudibaseEmitter" type BBEventOpts = { @@ -9,6 +9,7 @@ type BBEventOpts = { row?: Row oldRow?: Row metadata?: any + user?: User } interface BBEventTable extends Table { @@ -24,6 +25,7 @@ type BBEvent = { id?: string revision?: string metadata?: any + user?: User } export function rowEmission({ @@ -34,12 +36,14 @@ export function rowEmission({ table, metadata, oldRow, + user, }: BBEventOpts) { let event: BBEvent = { row, oldRow, appId, tableId: row?.tableId, + user, } if (table) { event.table = table diff --git a/packages/server/src/sdk/app/rowActions.ts b/packages/server/src/sdk/app/rowActions.ts index 3d8a6fd9be..ed87133b5d 100644 --- a/packages/server/src/sdk/app/rowActions.ts +++ b/packages/server/src/sdk/app/rowActions.ts @@ -4,6 +4,7 @@ import { AutomationTriggerStepId, SEPARATOR, TableRowActions, + User, VirtualDocumentType, } from "@budibase/types" import { generateRowActionsID } from "../../db/utils" @@ -236,7 +237,12 @@ export async function remove(tableId: string, rowActionId: string) { }) } -export async function run(tableId: any, rowActionId: any, rowId: string) { +export async function run( + tableId: any, + rowActionId: any, + rowId: string, + user: User +) { const table = await sdk.tables.getTable(tableId) if (!table) { throw new HTTPError("Table not found", 404) @@ -258,6 +264,7 @@ export async function run(tableId: any, rowActionId: any, rowId: string) { row, table, }, + user, appId: context.getAppId(), }, { getResponses: true } diff --git a/packages/server/src/threads/automation.ts b/packages/server/src/threads/automation.ts index 3b47634663..a75c0d2870 100644 --- a/packages/server/src/threads/automation.ts +++ b/packages/server/src/threads/automation.ts @@ -26,6 +26,7 @@ import { BranchStep, LoopStep, SearchFilters, + User, } from "@budibase/types" import { AutomationContext, TriggerOutput } from "../definitions/automations" import { WorkerCallback } from "./definitions" @@ -75,6 +76,7 @@ class Orchestrator { private loopStepOutputs: LoopStep[] private stopped: boolean private executionOutput: Omit<AutomationContext, "stepsByName" | "stepsById"> + private currentUser: User | undefined constructor(job: AutomationJob) { let automation = job.data.automation @@ -106,6 +108,7 @@ class Orchestrator { this.updateExecutionOutput(triggerId, triggerStepId, null, triggerOutput) this.loopStepOutputs = [] this.stopped = false + this.currentUser = triggerOutput.user } cleanupTriggerOutputs(stepId: string, triggerOutput: TriggerOutput) { @@ -258,6 +261,7 @@ class Orchestrator { automationId: this.automation._id, }) this.context.env = await sdkUtils.getEnvironmentVariables() + this.context.user = this.currentUser let metadata @@ -572,7 +576,6 @@ class Orchestrator { originalStepInput, this.processContext(this.context) ) - inputs = automationUtils.cleanInputValues(inputs, step.schema.inputs) const outputs = await stepFn({ diff --git a/packages/types/src/documents/app/automation/automation.ts b/packages/types/src/documents/app/automation/automation.ts index effe99a328..1af892d8d1 100644 --- a/packages/types/src/documents/app/automation/automation.ts +++ b/packages/types/src/documents/app/automation/automation.ts @@ -261,6 +261,7 @@ export type UpdatedRowEventEmitter = { oldRow: Row table: Table appId: string + user: User } export enum LoopStepType { diff --git a/packages/types/src/sdk/automations/index.ts b/packages/types/src/sdk/automations/index.ts index d04f126c32..f5c57b54d8 100644 --- a/packages/types/src/sdk/automations/index.ts +++ b/packages/types/src/sdk/automations/index.ts @@ -1,4 +1,4 @@ -import { Automation, AutomationMetadata, Row } from "../../documents" +import { Automation, AutomationMetadata, Row, User } from "../../documents" import { Job } from "bull" export interface AutomationDataEvent { @@ -8,6 +8,7 @@ export interface AutomationDataEvent { timeout?: number row?: Row oldRow?: Row + user?: User } export interface AutomationData { From 2b7742d96fba71cb16ee3741e9732ab35eddb476 Mon Sep 17 00:00:00 2001 From: Peter Clement <peter@budibase.com> Date: Fri, 18 Oct 2024 15:12:26 +0100 Subject: [PATCH 02/14] pro --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index fc4c7f4925..1a749caba9 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit fc4c7f4925139af078480217965c3d6338dc0a7f +Subproject commit 1a749caba9c85aab2645e5d00db479eb53d3f80f From 4cabc09f8abb80fca08bab6375ea35f4353f43d1 Mon Sep 17 00:00:00 2001 From: Peter Clement <peter@budibase.com> Date: Fri, 18 Oct 2024 15:35:31 +0100 Subject: [PATCH 03/14] fix row actions test --- packages/server/src/api/routes/tests/rowAction.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/src/api/routes/tests/rowAction.spec.ts b/packages/server/src/api/routes/tests/rowAction.spec.ts index 7140f3486e..d3b90f6f6b 100644 --- a/packages/server/src/api/routes/tests/rowAction.spec.ts +++ b/packages/server/src/api/routes/tests/rowAction.spec.ts @@ -783,6 +783,7 @@ describe("/rowsActions", () => { ...(await config.api.table.get(tableId)), views: expect.anything(), }, + user: expect.anything(), automation: expect.objectContaining({ _id: rowAction.automationId, }), From 8f46119817b0fd6758e9c7b188571655ba9d86cb Mon Sep 17 00:00:00 2001 From: Peter Clement <peter@budibase.com> Date: Mon, 21 Oct 2024 10:04:51 +0100 Subject: [PATCH 04/14] refs --- packages/account-portal | 2 +- packages/pro | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/account-portal b/packages/account-portal index 8cd052ce82..fedf9957c1 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 8cd052ce8288f343812a514d06c5a9459b3ba1a8 +Subproject commit fedf9957c1acc240b1a3bccb7882d7d763d8f499 diff --git a/packages/pro b/packages/pro index 1a749caba9..297fdc937e 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 1a749caba9c85aab2645e5d00db479eb53d3f80f +Subproject commit 297fdc937e9c650b4964fc1a942b60022b195865 From 09695fabd6983dcbab5f39bd1e8ab0d7383172c3 Mon Sep 17 00:00:00 2001 From: Peter Clement <peter@budibase.com> Date: Tue, 22 Oct 2024 10:52:52 +0100 Subject: [PATCH 05/14] extract necessary user bindings and add types --- .../server/src/api/controllers/automation.ts | 7 +++--- .../server/src/api/controllers/row/index.ts | 22 +++++++++++++++---- packages/server/src/automations/triggers.ts | 6 ++--- .../server/src/definitions/automations.ts | 4 ++-- packages/server/src/sdk/users/utils.ts | 3 ++- packages/server/src/threads/automation.ts | 4 ++-- packages/types/src/documents/global/user.ts | 10 +++++++++ packages/types/src/sdk/automations/index.ts | 9 ++++++-- 8 files changed, 46 insertions(+), 19 deletions(-) diff --git a/packages/server/src/api/controllers/automation.ts b/packages/server/src/api/controllers/automation.ts index df23f9f3b7..b19218647b 100644 --- a/packages/server/src/api/controllers/automation.ts +++ b/packages/server/src/api/controllers/automation.ts @@ -13,7 +13,6 @@ import { UserCtx, DeleteAutomationResponse, FetchAutomationResponse, - User, } from "@budibase/types" import { getActionDefinitions as actionDefs } from "../../automations/actions" import sdk from "../../sdk" @@ -160,7 +159,7 @@ export async function trigger(ctx: UserCtx) { automation, { fields: ctx.request.body.fields, - user: ctx.user as User, + user: sdk.users.getUserContextBindings(ctx.user), timeout: ctx.request.body.timeout * 1000 || env.AUTOMATION_THREAD_TIMEOUT, }, @@ -185,7 +184,7 @@ export async function trigger(ctx: UserCtx) { await triggers.externalTrigger(automation, { ...ctx.request.body, appId: ctx.appId, - user: ctx.user as User, + user: sdk.users.getUserContextBindings(ctx.user), }) ctx.body = { message: `Automation ${automation._id} has been triggered.`, @@ -215,7 +214,7 @@ export async function test(ctx: UserCtx) { { ...testInput, appId: ctx.appId, - user: ctx.user, + user: sdk.users.getUserContextBindings(ctx.user), }, { getResponses: true } ) diff --git a/packages/server/src/api/controllers/row/index.ts b/packages/server/src/api/controllers/row/index.ts index 67502f8b36..33599a0119 100644 --- a/packages/server/src/api/controllers/row/index.ts +++ b/packages/server/src/api/controllers/row/index.ts @@ -71,7 +71,7 @@ export async function patch( row, table, oldRow, - ctx.user + sdk.users.getUserContextBindings(ctx.user) ) ctx.message = `${table.name} updated successfully.` ctx.body = row @@ -104,7 +104,14 @@ export const save = async (ctx: UserCtx<Row, Row>) => { ) ctx.status = 200 ctx.eventEmitter && - ctx.eventEmitter.emitRow(`row:save`, appId, row, table, null, ctx.user) + ctx.eventEmitter.emitRow( + `row:save`, + appId, + row, + table, + null, + sdk.users.getUserContextBindings(ctx.user) + ) ctx.message = `${table.name} saved successfully` // prefer squashed for response ctx.body = row || squashed @@ -177,7 +184,14 @@ async function deleteRows(ctx: UserCtx<DeleteRowRequest>) { for (let row of rows) { ctx.eventEmitter && - ctx.eventEmitter.emitRow(`row:delete`, appId, row, null, null, ctx.user) + ctx.eventEmitter.emitRow( + `row:delete`, + appId, + row, + null, + null, + sdk.users.getUserContextBindings(ctx.user) + ) gridSocket?.emitRowDeletion(ctx, row) } @@ -200,7 +214,7 @@ async function deleteRow(ctx: UserCtx<DeleteRowRequest>) { resp.row, null, null, - ctx.user + sdk.users.getUserContextBindings(ctx.user) ) gridSocket?.emitRowDeletion(ctx, resp.row) diff --git a/packages/server/src/automations/triggers.ts b/packages/server/src/automations/triggers.ts index efcbf27644..e3b7318211 100644 --- a/packages/server/src/automations/triggers.ts +++ b/packages/server/src/automations/triggers.ts @@ -19,7 +19,7 @@ import { AutomationStoppedReason, AutomationStatus, AutomationRowEvent, - User, + UserBindings, } from "@budibase/types" import { executeInThread } from "../threads/automation" import { dataFilters, sdk } from "@budibase/shared-core" @@ -145,11 +145,10 @@ export async function externalTrigger( fields: Record<string, any> timeout?: number appId?: string - user?: User + user?: UserBindings | undefined }, { getResponses }: { getResponses?: boolean } = {} ): Promise<any> { - console.log("user: " + params.user) if (automation.disabled) { throw new Error("Automation is disabled") } @@ -196,7 +195,6 @@ export async function externalTrigger( appId: context.getAppId(), automation, } - console.log(data) return executeInThread({ data } as AutomationJob) } else { return automationQueue.add(data, JOB_OPTS) diff --git a/packages/server/src/definitions/automations.ts b/packages/server/src/definitions/automations.ts index 70faed9327..d551584fd1 100644 --- a/packages/server/src/definitions/automations.ts +++ b/packages/server/src/definitions/automations.ts @@ -1,4 +1,4 @@ -import { AutomationResults, LoopStepType, User } from "@budibase/types" +import { AutomationResults, LoopStepType, UserBindings } from "@budibase/types" export interface LoopInput { option: LoopStepType @@ -18,6 +18,6 @@ export interface AutomationContext extends AutomationResults { stepsById: Record<string, any> stepsByName: Record<string, any> env?: Record<string, string> - user?: User + user?: UserBindings trigger: any } diff --git a/packages/server/src/sdk/users/utils.ts b/packages/server/src/sdk/users/utils.ts index 74389a1444..8266d3e28a 100644 --- a/packages/server/src/sdk/users/utils.ts +++ b/packages/server/src/sdk/users/utils.ts @@ -12,6 +12,7 @@ import { UserMetadata, Database, ContextUserMetadata, + UserBindings, } from "@budibase/types" export function combineMetadataAndUser( @@ -125,7 +126,7 @@ export async function syncGlobalUsers() { } } -export function getUserContextBindings(user: ContextUser) { +export function getUserContextBindings(user: ContextUser): UserBindings { if (!user) { return {} } diff --git a/packages/server/src/threads/automation.ts b/packages/server/src/threads/automation.ts index a75c0d2870..0f17b44424 100644 --- a/packages/server/src/threads/automation.ts +++ b/packages/server/src/threads/automation.ts @@ -26,7 +26,7 @@ import { BranchStep, LoopStep, SearchFilters, - User, + UserBindings, } from "@budibase/types" import { AutomationContext, TriggerOutput } from "../definitions/automations" import { WorkerCallback } from "./definitions" @@ -76,7 +76,7 @@ class Orchestrator { private loopStepOutputs: LoopStep[] private stopped: boolean private executionOutput: Omit<AutomationContext, "stepsByName" | "stepsById"> - private currentUser: User | undefined + private currentUser: UserBindings | undefined constructor(job: AutomationJob) { let automation = job.data.automation diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index a1c5b2506f..85641bf4c5 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -68,6 +68,16 @@ export interface User extends Document { appSort?: string } +export interface UserBindings extends Document { + firstName?: string + lastName?: string + email?: string + status?: string + roleId?: string | undefined | null + globalId?: string + userId?: string +} + export enum UserStatus { ACTIVE = "active", INACTIVE = "inactive", diff --git a/packages/types/src/sdk/automations/index.ts b/packages/types/src/sdk/automations/index.ts index f5c57b54d8..9ceded03ee 100644 --- a/packages/types/src/sdk/automations/index.ts +++ b/packages/types/src/sdk/automations/index.ts @@ -1,4 +1,9 @@ -import { Automation, AutomationMetadata, Row, User } from "../../documents" +import { + Automation, + AutomationMetadata, + Row, + UserBindings, +} from "../../documents" import { Job } from "bull" export interface AutomationDataEvent { @@ -8,7 +13,7 @@ export interface AutomationDataEvent { timeout?: number row?: Row oldRow?: Row - user?: User + user?: UserBindings } export interface AutomationData { From 613e63ccbfdc981029b7e742c20e561a19e1aa0e Mon Sep 17 00:00:00 2001 From: Peter Clement <peter@budibase.com> Date: Tue, 22 Oct 2024 10:58:24 +0100 Subject: [PATCH 06/14] remove update ref --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index fedf9957c1..b5ba3189d5 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit fedf9957c1acc240b1a3bccb7882d7d763d8f499 +Subproject commit b5ba3189d508769ace587ac58008421800a4a316 From 33ea5f09a71236fb0c327089ae55a5e5198d2cd1 Mon Sep 17 00:00:00 2001 From: mike12345567 <me@michaeldrury.co.uk> Date: Tue, 22 Oct 2024 11:06:46 +0100 Subject: [PATCH 07/14] Revert account portal ref. --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index b5ba3189d5..8cd052ce82 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit b5ba3189d508769ace587ac58008421800a4a316 +Subproject commit 8cd052ce8288f343812a514d06c5a9459b3ba1a8 From bce430b57c0f6f68e75d25ab744244bc3260a0b8 Mon Sep 17 00:00:00 2001 From: Peter Clement <peter@budibase.com> Date: Tue, 22 Oct 2024 12:03:16 +0100 Subject: [PATCH 08/14] pr comments --- .../server/src/api/controllers/row/index.ts | 40 ++++++++----------- packages/server/src/automations/triggers.ts | 2 +- packages/server/src/events/BudibaseEmitter.ts | 23 +++++++---- packages/types/src/documents/global/user.ts | 2 +- 4 files changed, 34 insertions(+), 33 deletions(-) diff --git a/packages/server/src/api/controllers/row/index.ts b/packages/server/src/api/controllers/row/index.ts index 33599a0119..4d40476b7c 100644 --- a/packages/server/src/api/controllers/row/index.ts +++ b/packages/server/src/api/controllers/row/index.ts @@ -65,14 +65,14 @@ export async function patch( } ctx.status = 200 ctx.eventEmitter && - ctx.eventEmitter.emitRow( - `row:update`, + ctx.eventEmitter.emitRow({ + eventName: `row:update`, appId, row, table, oldRow, - sdk.users.getUserContextBindings(ctx.user) - ) + user: sdk.users.getUserContextBindings(ctx.user), + }) ctx.message = `${table.name} updated successfully.` ctx.body = row gridSocket?.emitRowUpdate(ctx, row) @@ -104,14 +104,13 @@ export const save = async (ctx: UserCtx<Row, Row>) => { ) ctx.status = 200 ctx.eventEmitter && - ctx.eventEmitter.emitRow( - `row:save`, + ctx.eventEmitter.emitRow({ + eventName: `row:save`, appId, row, table, - null, - sdk.users.getUserContextBindings(ctx.user) - ) + user: sdk.users.getUserContextBindings(ctx.user), + }) ctx.message = `${table.name} saved successfully` // prefer squashed for response ctx.body = row || squashed @@ -184,17 +183,14 @@ async function deleteRows(ctx: UserCtx<DeleteRowRequest>) { for (let row of rows) { ctx.eventEmitter && - ctx.eventEmitter.emitRow( - `row:delete`, + ctx.eventEmitter.emitRow({ + eventName: `row:delete`, appId, row, - null, - null, - sdk.users.getUserContextBindings(ctx.user) - ) + user: sdk.users.getUserContextBindings(ctx.user), + }) gridSocket?.emitRowDeletion(ctx, row) } - return rows } @@ -208,14 +204,12 @@ async function deleteRow(ctx: UserCtx<DeleteRowRequest>) { } ctx.eventEmitter && - ctx.eventEmitter.emitRow( - `row:delete`, + ctx.eventEmitter.emitRow({ + eventName: `row:delete`, appId, - resp.row, - null, - null, - sdk.users.getUserContextBindings(ctx.user) - ) + row: resp.row, + user: sdk.users.getUserContextBindings(ctx.user), + }) gridSocket?.emitRowDeletion(ctx, resp.row) return resp diff --git a/packages/server/src/automations/triggers.ts b/packages/server/src/automations/triggers.ts index e3b7318211..70fda1f237 100644 --- a/packages/server/src/automations/triggers.ts +++ b/packages/server/src/automations/triggers.ts @@ -145,7 +145,7 @@ export async function externalTrigger( fields: Record<string, any> timeout?: number appId?: string - user?: UserBindings | undefined + user?: UserBindings }, { getResponses }: { getResponses?: boolean } = {} ): Promise<any> { diff --git a/packages/server/src/events/BudibaseEmitter.ts b/packages/server/src/events/BudibaseEmitter.ts index d09ea20a5d..c8983096d0 100644 --- a/packages/server/src/events/BudibaseEmitter.ts +++ b/packages/server/src/events/BudibaseEmitter.ts @@ -13,14 +13,21 @@ import { Table, Row, User } from "@budibase/types" * This is specifically quite important for template strings used in automations. */ class BudibaseEmitter extends EventEmitter { - emitRow( - eventName: string, - appId: string, - row: Row, - table?: Table, - oldRow?: Row, - user?: User - ) { + emitRow({ + eventName, + appId, + row, + table, + oldRow, + user, + }: { + eventName: string + appId: string + row: Row + table?: Table + oldRow?: Row + user: User + }) { rowEmission({ emitter: this, eventName, appId, row, table, oldRow, user }) } diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index 85641bf4c5..829a171843 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -73,7 +73,7 @@ export interface UserBindings extends Document { lastName?: string email?: string status?: string - roleId?: string | undefined | null + roleId?: string | null globalId?: string userId?: string } From e67126e6b69898f99cb4c1e27a3f01610b3e07e4 Mon Sep 17 00:00:00 2001 From: Adria Navarro <adria@budibase.com> Date: Tue, 22 Oct 2024 14:30:22 +0200 Subject: [PATCH 09/14] Navigate to automation on creation --- .../automation/AutomationPanel/CreateAutomationModal.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte index 365d3d358f..35fdba970a 100644 --- a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte @@ -1,4 +1,5 @@ <script> + import { goto } from "@roxi/routify" import { automationStore } from "stores/builder" import { notifications, @@ -32,11 +33,12 @@ triggerVal.stepId, triggerVal ) - await automationStore.actions.create(name, trigger) + const automation = await automationStore.actions.create(name, trigger) if (triggerVal.stepId === TriggerStepID.WEBHOOK) { webhookModal.show() } notifications.success(`Automation ${name} created`) + $goto(`../automation/${automation._id}`) } catch (error) { notifications.error("Error creating automation") } From 75f17f5c1251a9d5b7d4b8cd077063894528ea51 Mon Sep 17 00:00:00 2001 From: Peter Clement <peter@budibase.com> Date: Tue, 22 Oct 2024 13:53:31 +0100 Subject: [PATCH 10/14] update test to check for user id --- packages/server/src/api/routes/tests/rowAction.spec.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/routes/tests/rowAction.spec.ts b/packages/server/src/api/routes/tests/rowAction.spec.ts index d3b90f6f6b..9f9a33c73b 100644 --- a/packages/server/src/api/routes/tests/rowAction.spec.ts +++ b/packages/server/src/api/routes/tests/rowAction.spec.ts @@ -767,7 +767,6 @@ describe("/rowsActions", () => { it("can trigger an automation given valid data", async () => { expect(await getAutomationLogs()).toBeEmpty() await config.api.rowAction.trigger(viewId, rowAction.id, { rowId }) - const automationLogs = await getAutomationLogs() expect(automationLogs).toEqual([ expect.objectContaining({ @@ -783,7 +782,10 @@ describe("/rowsActions", () => { ...(await config.api.table.get(tableId)), views: expect.anything(), }, - user: expect.anything(), + user: expect.objectContaining({ + _id: "ro_ta_users_" + config.getUser()._id, + }), + automation: expect.objectContaining({ _id: rowAction.automationId, }), From 94ebd7c6ef300b93441f9a865c9ffe96239371b6 Mon Sep 17 00:00:00 2001 From: Peter Clement <peter@budibase.com> Date: Tue, 22 Oct 2024 14:36:15 +0100 Subject: [PATCH 11/14] update automation emitter --- packages/server/src/events/AutomationEmitter.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/server/src/events/AutomationEmitter.ts b/packages/server/src/events/AutomationEmitter.ts index 32fd130929..a63273bdc0 100644 --- a/packages/server/src/events/AutomationEmitter.ts +++ b/packages/server/src/events/AutomationEmitter.ts @@ -31,7 +31,17 @@ class AutomationEmitter { } } - async emitRow(eventName: string, appId: string, row: Row, table?: Table) { + async emitRow({ + eventName, + appId, + row, + table, + }: { + eventName: string + appId: string + row: Row + table?: Table + }) { let MAX_AUTOMATION_CHAIN = await this.getMaxAutomationChain() // don't emit even if we've reached max automation chain From 06670ba549cebe46427e34edea669e0591af0103 Mon Sep 17 00:00:00 2001 From: Adria Navarro <adria@budibase.com> Date: Tue, 22 Oct 2024 17:32:32 +0200 Subject: [PATCH 12/14] Add local prerelease to version locally --- packages/backend-core/src/environment.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index 4e93e8d9ee..7d3a9f18f5 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -83,7 +83,7 @@ function getPackageJsonFields(): { if (isDev() && !isTest()) { try { const lerna = getParentFile("lerna.json") - localVersion = lerna.version + localVersion = `${lerna.version}+local` } catch { // } From d73643f0b3eefb22ebbf0b7f63902ac7bf71a3dd Mon Sep 17 00:00:00 2001 From: Adria Navarro <adria@budibase.com> Date: Tue, 22 Oct 2024 17:42:36 +0200 Subject: [PATCH 13/14] Allow serving old versions locally --- .../server/src/api/controllers/static/index.ts | 15 ++++++++++----- packages/server/src/constants/index.ts | 2 -- packages/server/src/utilities/fileSystem/app.ts | 6 +++--- .../src/utilities/fileSystem/clientLibrary.ts | 17 +++++++++++++++++ 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index b4b4fdea5d..307867601b 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -2,11 +2,12 @@ import { InvalidFileExtensions } from "@budibase/shared-core" import AppComponent from "./templates/BudibaseApp.svelte" import { join } from "../../../utilities/centralPath" import * as uuid from "uuid" -import { devClientVersion, ObjectStoreBuckets } from "../../../constants" +import { ObjectStoreBuckets } from "../../../constants" import { processString } from "@budibase/string-templates" import { loadHandlebarsFile, NODE_MODULES_PATH, + shouldServeLocally, TOP_LEVEL_PATH, } from "../../../utilities/fileSystem" import env from "../../../environment" @@ -257,25 +258,29 @@ export const serveBuilderPreview = async function (ctx: Ctx) { export const serveClientLibrary = async function (ctx: Ctx) { const version = ctx.request.query.version + if (Array.isArray(version)) { + ctx.throw(400) + } + const appId = context.getAppId() || (ctx.request.query.appId as string) let rootPath = join(NODE_MODULES_PATH, "@budibase", "client", "dist") if (!appId) { ctx.throw(400, "No app ID provided - cannot fetch client library.") } - if (env.isProd() || (env.isDev() && version !== devClientVersion)) { + + const serverLocally = shouldServeLocally(version || "") + if (!serverLocally) { ctx.body = await objectStore.getReadStream( ObjectStoreBuckets.APPS, objectStore.clientLibraryPath(appId!) ) ctx.set("Content-Type", "application/javascript") - } else if (env.isDev() && version === devClientVersion) { + } else { // incase running from TS directly const tsPath = join(require.resolve("@budibase/client"), "..") return send(ctx, "budibase-client.js", { root: !fs.existsSync(rootPath) ? tsPath : rootPath, }) - } else { - ctx.throw(500, "Unable to retrieve client library.") } } diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index 316d27b3fc..bac838b53e 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -152,8 +152,6 @@ export enum AutomationErrors { FAILURE_CONDITION = "FAILURE_CONDITION_MET", } -export const devClientVersion = "0.0.0" - // pass through the list from the auth/core lib export const ObjectStoreBuckets = objectStore.ObjectStoreBuckets export const MAX_AUTOMATION_RECURRING_ERRORS = 5 diff --git a/packages/server/src/utilities/fileSystem/app.ts b/packages/server/src/utilities/fileSystem/app.ts index c708a9422b..9bd88ba0b1 100644 --- a/packages/server/src/utilities/fileSystem/app.ts +++ b/packages/server/src/utilities/fileSystem/app.ts @@ -1,8 +1,8 @@ import { budibaseTempDir } from "../budibaseDir" import fs from "fs" import { join } from "path" -import { ObjectStoreBuckets, devClientVersion } from "../../constants" -import { updateClientLibrary } from "./clientLibrary" +import { ObjectStoreBuckets } from "../../constants" +import { shouldServeLocally, updateClientLibrary } from "./clientLibrary" import env from "../../environment" import { objectStore, context } from "@budibase/backend-core" import { TOP_LEVEL_PATH } from "./filesystem" @@ -40,7 +40,7 @@ export const getComponentLibraryManifest = async (library: string) => { const db = context.getAppDB() const app = await db.get<App>(DocumentType.APP_METADATA) - if (app.version === devClientVersion || env.isTest()) { + if (shouldServeLocally(app.version) || env.isTest()) { const paths = [ join(TOP_LEVEL_PATH, "packages/client", filename), join(process.cwd(), "client", filename), diff --git a/packages/server/src/utilities/fileSystem/clientLibrary.ts b/packages/server/src/utilities/fileSystem/clientLibrary.ts index c994502995..faa328fdc5 100644 --- a/packages/server/src/utilities/fileSystem/clientLibrary.ts +++ b/packages/server/src/utilities/fileSystem/clientLibrary.ts @@ -1,3 +1,4 @@ +import semver from "semver" import path, { join } from "path" import { ObjectStoreBuckets } from "../../constants" import fs from "fs" @@ -183,3 +184,19 @@ export async function revertClientLibrary(appId: string) { return JSON.parse(await manifestSrc) } + +export function shouldServeLocally(version: string) { + if (env.isProd() || !env.isDev()) { + return false + } + + if (version === "0.0.0") { + return true + } + + const parsedSemver = semver.parse(version) + if (parsedSemver?.build?.[0] === "local") { + return true + } + return false +} From 91e3c87a9c56fa1fa1848ad0e3fbb7dbc687ca21 Mon Sep 17 00:00:00 2001 From: Adria Navarro <adria@budibase.com> Date: Tue, 22 Oct 2024 17:58:51 +0200 Subject: [PATCH 14/14] Fix typo --- packages/server/src/api/controllers/static/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index 307867601b..1d04811019 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -268,8 +268,8 @@ export const serveClientLibrary = async function (ctx: Ctx) { ctx.throw(400, "No app ID provided - cannot fetch client library.") } - const serverLocally = shouldServeLocally(version || "") - if (!serverLocally) { + const serveLocally = shouldServeLocally(version || "") + if (!serveLocally) { ctx.body = await objectStore.getReadStream( ObjectStoreBuckets.APPS, objectStore.clientLibraryPath(appId!)