Skip to content

Commit 3042376

Browse files
authored
Release v1.48.1 (#1130)
feat: allow overriding webhook response in step config hotfix: accept null values in for each row data
2 parents 11d0c5a + 1045c49 commit 3042376

File tree

9 files changed

+80
-19
lines changed

9 files changed

+80
-19
lines changed

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,5 +108,5 @@
108108
"tsconfig-paths": "^4.2.0",
109109
"type-fest": "4.10.3"
110110
},
111-
"version": "1.48.0"
111+
"version": "1.48.1"
112112
}

packages/backend/src/apps/tiles/actions/find-multiple-rows/schema.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import { z } from 'zod'
22

3+
export const tableRowDataSchema = z.record(
4+
z.string(),
5+
z.union([z.string(), z.number(), z.null()]).default(null),
6+
)
7+
38
const tableRowOutputSchema = z.object({
49
rowId: z.string(),
510
// for tiles v1, empty cells is either an empty string or wont even have their key returned
611
// for tiles v2, empty cells return null
7-
data: z.record(z.string(), z.union([z.string(), z.number(), z.null()])),
12+
data: tableRowDataSchema,
813
})
914

1015
export const dataOutSchema = z.object({

packages/backend/src/apps/toolbox/actions/for-each/schema.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { z } from 'zod'
22

3+
import { tableRowDataSchema } from '@/apps/tiles/actions/find-multiple-rows/schema'
4+
35
import {
46
FOR_EACH_INPUT_SOURCE,
57
FOR_EACH_TABLE_SOURCES,
@@ -15,7 +17,7 @@ const tableColumnsSchema = z.array(
1517

1618
const tableRowsSchema = z.array(
1719
z.object({
18-
data: z.record(z.string(), z.string().or(z.number())),
20+
data: tableRowDataSchema,
1921
rowId: z.string().optional(), // only for tiles
2022
}),
2123
)

packages/backend/src/controllers/webhooks/handler.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import logger from '@/helpers/logger'
1313
import tracer from '@/helpers/tracer'
1414
import Flow from '@/models/flow'
1515
import { enqueueActionJob } from '@/queues/action'
16-
import { processTrigger } from '@/services/trigger'
16+
import { type CustomWebhookResponse, processTrigger } from '@/services/trigger'
1717

1818
const DEFAULT_MAX_QPS = 10
1919

@@ -31,6 +31,17 @@ const getRateLimiter = memoize((maxQps: number): RateLimiterRedis => {
3131
})
3232
})
3333

34+
async function sendWebhookResponse(
35+
response: Response,
36+
customWebhookResponse?: CustomWebhookResponse,
37+
) {
38+
if (customWebhookResponse) {
39+
response.setHeader('Content-Type', customWebhookResponse.contentType)
40+
return response.status(200).send(customWebhookResponse.body)
41+
}
42+
return response.sendStatus(200)
43+
}
44+
3445
export default async (request: IRequest, response: Response) => {
3546
const span = tracer.scope().active()
3647
span?.setOperationName('webhooks.handler')
@@ -139,12 +150,13 @@ export default async (request: IRequest, response: Response) => {
139150
* Plumber will still return a 200 status code, so that FormSG does not auto-retry,
140151
* but Plumber not enqueue the job.
141152
*/
142-
const { executionId, shouldExecute } = await processTrigger({
143-
flowId,
144-
stepId: triggerStep.id,
145-
triggerItem,
146-
testRun,
147-
})
153+
const { executionId, shouldExecute, customWebhookResponse } =
154+
await processTrigger({
155+
flowId,
156+
stepId: triggerStep.id,
157+
triggerItem,
158+
testRun,
159+
})
148160

149161
span?.addTags({
150162
flowId,
@@ -155,7 +167,7 @@ export default async (request: IRequest, response: Response) => {
155167
})
156168

157169
if (testRun || !shouldExecute) {
158-
return response.sendStatus(200)
170+
return sendWebhookResponse(response, customWebhookResponse)
159171
}
160172

161173
const nextStep = await triggerStep.getNextStep()
@@ -174,5 +186,5 @@ export default async (request: IRequest, response: Response) => {
174186
jobOptions: DEFAULT_JOB_OPTIONS,
175187
})
176188

177-
return response.sendStatus(200)
189+
return sendWebhookResponse(response, customWebhookResponse)
178190
}

packages/backend/src/services/trigger.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import type { IJSONObject, ITriggerItem } from '@plumber/types'
22

3+
import { z } from 'zod'
4+
35
import logger from '@/helpers/logger'
46
import Execution from '@/models/execution'
7+
import ExecutionStep from '@/models/execution-step'
58
import Step from '@/models/step'
69

710
type ProcessTriggerOptions = {
@@ -12,8 +15,41 @@ type ProcessTriggerOptions = {
1215
testRun?: boolean
1316
}
1417

18+
const customWebhookResponseSchema = z.object({
19+
contentType: z.string(),
20+
body: z.string(),
21+
})
22+
23+
export type CustomWebhookResponse = z.infer<typeof customWebhookResponseSchema>
24+
25+
type ProcessTriggerResult = {
26+
flowId: string
27+
stepId: string
28+
executionId: string
29+
executionStep: ExecutionStep | null
30+
shouldExecute: boolean
31+
customWebhookResponse?: CustomWebhookResponse
32+
}
33+
34+
function getCustomWebhookResponse(
35+
step: Step,
36+
): CustomWebhookResponse | undefined {
37+
const customWebhookResponse =
38+
step.config?.adminOverride?.customWebhookResponse
39+
if (step.appKey !== 'webhook' || !customWebhookResponse) {
40+
return undefined
41+
}
42+
const parsed = customWebhookResponseSchema.safeParse(customWebhookResponse)
43+
if (parsed.success) {
44+
return parsed.data
45+
}
46+
return undefined
47+
}
48+
1549
// TODO(ian): change this function name, it's basically just storing trigger data
16-
export const processTrigger = async (options: ProcessTriggerOptions) => {
50+
export const processTrigger = async (
51+
options: ProcessTriggerOptions,
52+
): Promise<ProcessTriggerResult> => {
1753
const { flowId, stepId, triggerItem, error, testRun } = options
1854

1955
const step = await Step.query().findById(stepId).throwIfNotFound()
@@ -95,11 +131,14 @@ export const processTrigger = async (options: ProcessTriggerOptions) => {
95131
key: step.key,
96132
})
97133

134+
const customWebhookResponse = getCustomWebhookResponse(step)
135+
98136
return {
99137
flowId,
100138
stepId,
101139
executionId: execution.id,
102140
executionStep,
103141
shouldExecute: true,
142+
customWebhookResponse,
104143
}
105144
}

packages/frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "frontend",
3-
"version": "1.48.0",
3+
"version": "1.48.1",
44
"type": "module",
55
"scripts": {
66
"dev": "wait-on tcp:3000 && vite --host --force",

packages/frontend/src/components/VariablesList/schema.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ const RowDataSchema = z.object({
77
z.object({
88
// for tiles v1, empty cells is either an empty string or wont even have their key returned
99
// for tiles v2, empty cells return null
10-
data: z.record(z.string(), z.union([z.string(), z.number(), z.null()])),
10+
data: z.record(
11+
z.string(),
12+
z.union([z.string(), z.number(), z.null()]).default(null),
13+
),
1114
rowId: z.string().optional(), // only Tiles will have this
1215
}),
1316
),

packages/types/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"name": "@plumber/types",
33
"description": "Shared types for plumber",
44
"types": "./index.d.ts",
5-
"version": "1.48.0"
5+
"version": "1.48.1"
66
}

0 commit comments

Comments
 (0)