Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support user in automation context #14825

Merged
merged 14 commits into from
Oct 22, 2024
3 changes: 3 additions & 0 deletions packages/server/src/api/controllers/automation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export async function trigger(ctx: UserCtx) {
automation,
{
fields: ctx.request.body.fields,
user: sdk.users.getUserContextBindings(ctx.user),
timeout:
ctx.request.body.timeout * 1000 || env.AUTOMATION_THREAD_TIMEOUT,
},
Expand All @@ -183,6 +184,7 @@ export async function trigger(ctx: UserCtx) {
await triggers.externalTrigger(automation, {
...ctx.request.body,
appId: ctx.appId,
user: sdk.users.getUserContextBindings(ctx.user),
})
ctx.body = {
message: `Automation ${automation._id} has been triggered.`,
Expand Down Expand Up @@ -212,6 +214,7 @@ export async function test(ctx: UserCtx) {
{
...testInput,
appId: ctx.appId,
user: sdk.users.getUserContextBindings(ctx.user),
},
{ getResponses: true }
)
Expand Down
39 changes: 35 additions & 4 deletions packages/server/src/api/controllers/row/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
sdk.users.getUserContextBindings(ctx.user)
)
ctx.message = `${table.name} updated successfully.`
ctx.body = row
gridSocket?.emitRowUpdate(ctx, row)
Expand Down Expand Up @@ -96,7 +103,15 @@ 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,
sdk.users.getUserContextBindings(ctx.user)
)
ctx.message = `${table.name} saved successfully`
// prefer squashed for response
ctx.body = row || squashed
Expand Down Expand Up @@ -168,7 +183,15 @@ 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,
sdk.users.getUserContextBindings(ctx.user)
)
gridSocket?.emitRowDeletion(ctx, row)
}

Expand All @@ -184,7 +207,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,
sdk.users.getUserContextBindings(ctx.user)
)
gridSocket?.emitRowDeletion(ctx, resp.row)

return resp
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/api/controllers/rowAction/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
1 change: 1 addition & 0 deletions packages/server/src/api/routes/tests/rowAction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,7 @@ describe("/rowsActions", () => {
...(await config.api.table.get(tableId)),
views: expect.anything(),
},
user: expect.anything(),
automation: expect.objectContaining({
_id: rowAction.automationId,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
})
})
8 changes: 7 additions & 1 deletion packages/server/src/automations/triggers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
AutomationStoppedReason,
AutomationStatus,
AutomationRowEvent,
UserBindings,
} from "@budibase/types"
import { executeInThread } from "../threads/automation"
import { dataFilters, sdk } from "@budibase/shared-core"
Expand Down Expand Up @@ -140,7 +141,12 @@ 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?: UserBindings | undefined
},
{ getResponses }: { getResponses?: boolean } = {}
): Promise<any> {
if (automation.disabled) {
Expand Down
3 changes: 2 additions & 1 deletion packages/server/src/definitions/automations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AutomationResults, LoopStepType } from "@budibase/types"
import { AutomationResults, LoopStepType, UserBindings } from "@budibase/types"

export interface LoopInput {
option: LoopStepType
Expand All @@ -18,5 +18,6 @@ export interface AutomationContext extends AutomationResults {
stepsById: Record<string, any>
stepsByName: Record<string, any>
env?: Record<string, string>
user?: UserBindings
trigger: any
}
7 changes: 4 additions & 3 deletions packages/server/src/events/BudibaseEmitter.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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) {
Expand Down
6 changes: 5 additions & 1 deletion packages/server/src/events/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Table, Row } from "@budibase/types"
import { Table, Row, User } from "@budibase/types"
import BudibaseEmitter from "./BudibaseEmitter"

type BBEventOpts = {
Expand All @@ -9,6 +9,7 @@ type BBEventOpts = {
row?: Row
oldRow?: Row
metadata?: any
user?: User
}

interface BBEventTable extends Table {
Expand All @@ -24,6 +25,7 @@ type BBEvent = {
id?: string
revision?: string
metadata?: any
user?: User
}

export function rowEmission({
Expand All @@ -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
Expand Down
9 changes: 8 additions & 1 deletion packages/server/src/sdk/app/rowActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
AutomationTriggerStepId,
SEPARATOR,
TableRowActions,
User,
VirtualDocumentType,
} from "@budibase/types"
import { generateRowActionsID } from "../../db/utils"
Expand Down Expand Up @@ -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)
Expand All @@ -258,6 +264,7 @@ export async function run(tableId: any, rowActionId: any, rowId: string) {
row,
table,
},
user,
appId: context.getAppId(),
},
{ getResponses: true }
Expand Down
3 changes: 2 additions & 1 deletion packages/server/src/sdk/users/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
UserMetadata,
Database,
ContextUserMetadata,
UserBindings,
} from "@budibase/types"

export function combineMetadataAndUser(
Expand Down Expand Up @@ -125,7 +126,7 @@ export async function syncGlobalUsers() {
}
}

export function getUserContextBindings(user: ContextUser) {
export function getUserContextBindings(user: ContextUser): UserBindings {
if (!user) {
return {}
}
Expand Down
5 changes: 4 additions & 1 deletion packages/server/src/threads/automation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
BranchStep,
LoopStep,
SearchFilters,
UserBindings,
} from "@budibase/types"
import { AutomationContext, TriggerOutput } from "../definitions/automations"
import { WorkerCallback } from "./definitions"
Expand Down Expand Up @@ -75,6 +76,7 @@ class Orchestrator {
private loopStepOutputs: LoopStep[]
private stopped: boolean
private executionOutput: Omit<AutomationContext, "stepsByName" | "stepsById">
private currentUser: UserBindings | undefined

constructor(job: AutomationJob) {
let automation = job.data.automation
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -258,6 +261,7 @@ class Orchestrator {
automationId: this.automation._id,
})
this.context.env = await sdkUtils.getEnvironmentVariables()
this.context.user = this.currentUser

let metadata

Expand Down Expand Up @@ -572,7 +576,6 @@ class Orchestrator {
originalStepInput,
this.processContext(this.context)
)

inputs = automationUtils.cleanInputValues(inputs, step.schema.inputs)

const outputs = await stepFn({
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/documents/app/automation/automation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ export type UpdatedRowEventEmitter = {
oldRow: Row
table: Table
appId: string
user: User
}

export enum LoopStepType {
Expand Down
10 changes: 10 additions & 0 deletions packages/types/src/documents/global/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
8 changes: 7 additions & 1 deletion packages/types/src/sdk/automations/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { Automation, AutomationMetadata, Row } from "../../documents"
import {
Automation,
AutomationMetadata,
Row,
UserBindings,
} from "../../documents"
import { Job } from "bull"

export interface AutomationDataEvent {
Expand All @@ -8,6 +13,7 @@ export interface AutomationDataEvent {
timeout?: number
row?: Row
oldRow?: Row
user?: UserBindings
}

export interface AutomationData {
Expand Down
Loading