diff --git a/apps/builder/playwright/tests/integrations/webhook.spec.ts b/apps/builder/playwright/tests/integrations/webhook.spec.ts
index c17972abac..986edcc507 100644
--- a/apps/builder/playwright/tests/integrations/webhook.spec.ts
+++ b/apps/builder/playwright/tests/integrations/webhook.spec.ts
@@ -31,31 +31,7 @@ test.describe('Webhook block', () => {
`"Group #1": "answer value", "Group #2": "20", "Group #2 (1)": "Yes"`
)
})
- test('Generated body should work', async ({ page }) => {
- const typebotId = cuid()
- await importTypebotInDatabase(
- path.join(__dirname, '../../fixtures/typebots/integrations/webhook.json'),
- {
- id: typebotId,
- }
- )
- await createWebhook(typebotId)
-
- await page.goto(`/typebots/${typebotId}/edit`)
- await page.click('text=Configure...')
- await page.fill(
- 'input[placeholder="Paste webhook URL..."]',
- `${process.env.PLAYWRIGHT_BUILDER_TEST_BASE_URL}/api/mock/webhook-easy-config`
- )
- await page.click('text=Advanced configuration')
- await page.click('text=GET')
- await page.click('text=POST')
- await page.click('text=Test the request')
- await expect(page.locator('div[role="textbox"] >> nth=-1')).toContainText(
- '"message": "This is a sample result, it has been generated ⬇️"'
- )
- })
test('its configuration should work', async ({ page }) => {
const typebotId = cuid()
await importTypebotInDatabase(
diff --git a/apps/viewer/playwright/fixtures/typebots/webhook.json b/apps/viewer/playwright/fixtures/typebots/webhook.json
index 79ce0f45b2..cd8130310d 100644
--- a/apps/viewer/playwright/fixtures/typebots/webhook.json
+++ b/apps/viewer/playwright/fixtures/typebots/webhook.json
@@ -1,72 +1,193 @@
{
- "id": "cl26li8fl0407iez0w2tlw8fn",
- "createdAt": "2022-04-19T20:25:30.417Z",
- "updatedAt": "2022-04-19T20:40:48.366Z",
+ "id": "cl9ip9u0l00001ad79a2lzm55",
+ "createdAt": "2022-10-21T16:22:07.414Z",
+ "updatedAt": "2022-10-21T16:30:57.642Z",
"icon": null,
"name": "My typebot",
"publishedTypebotId": null,
"folderId": null,
"groups": [
{
- "id": "cl26li8fj0000iez05x7razkg",
+ "id": "cl9ip9u0j0000d71a5d98gwni",
+ "title": "Start",
"blocks": [
{
- "id": "cl26li8fj0001iez0bqfraw9h",
+ "id": "cl9ip9u0j0001d71a44dsd2p1",
"type": "start",
"label": "Start",
- "groupId": "cl26li8fj0000iez05x7razkg",
- "outgoingEdgeId": "cl26liqj6000g2e6ed2cwkvse"
+ "groupId": "cl9ip9u0j0000d71a5d98gwni",
+ "outgoingEdgeId": "cl9ipkkb2001b3b6oh3vptq9k"
}
],
- "title": "Start",
"graphCoordinates": { "x": 0, "y": 0 }
},
{
- "id": "cl26lidjz000a2e6etf4v03hv",
+ "id": "cl9ipa38j00083b6o69e90m4t",
+ "graphCoordinates": { "x": 340, "y": 341 },
+ "title": "Group #1",
"blocks": [
{
- "id": "cl26lidk4000b2e6es2fos0nl",
+ "id": "cl9ipaaut000a3b6ovrqlec3x",
+ "groupId": "cl9ipa38j00083b6o69e90m4t",
+ "type": "text input",
+ "options": {
+ "isLong": false,
+ "labels": { "button": "Send", "placeholder": "Type a name..." },
+ "variableId": "vcl9ipajth000c3b6okl97r81j"
+ }
+ },
+ {
+ "id": "cl9ipan8f000d3b6oo2ovi3ac",
+ "groupId": "cl9ipa38j00083b6o69e90m4t",
+ "type": "number input",
+ "options": {
+ "labels": { "button": "Send", "placeholder": "Type an age..." },
+ "variableId": "vcl9ipaszl000e3b6ousjxuw7b"
+ }
+ },
+ {
+ "id": "cl9ipb08n000f3b6ok3mi2p48",
+ "groupId": "cl9ipa38j00083b6o69e90m4t",
"type": "choice input",
+ "options": {
+ "buttonLabel": "Send",
+ "isMultipleChoice": false,
+ "variableId": "vcl9ipg4tb00103b6oue08w3nm"
+ },
"items": [
{
- "id": "cl26lidk5000c2e6e39wyc7wq",
+ "id": "cl9ipb08n000g3b6okr691uad",
+ "blockId": "cl9ipb08n000f3b6ok3mi2p48",
+ "type": 0,
+ "content": "Male"
+ },
+ {
+ "blockId": "cl9ipb08n000f3b6ok3mi2p48",
"type": 0,
- "blockId": "cl26lidk4000b2e6es2fos0nl",
- "content": "Send success webhook"
+ "id": "cl9ipb2kk000h3b6oadwtonnz",
+ "content": "Female"
}
],
- "groupId": "cl26lidjz000a2e6etf4v03hv",
- "options": { "buttonLabel": "Send", "isMultipleChoice": false }
+ "outgoingEdgeId": "cl9ipcp83000o3b6odsn0a9a1"
+ }
+ ]
+ },
+ {
+ "id": "cl9ipbcjy000j3b6oqngo7luv",
+ "graphCoordinates": { "x": 781, "y": 91 },
+ "title": "Group #2",
+ "blocks": [
+ {
+ "id": "cl9ipbl6l000m3b6o3evn41kv",
+ "groupId": "cl9ipbcjy000j3b6oqngo7luv",
+ "type": "Set variable",
+ "options": {
+ "variableId": "vcl9ipbokm000n3b6o06hvarrf",
+ "expressionToEvaluate": "{\n \"name\": \"John\",\n \"age\": 25,\n \"gender\": \"male\"\n}"
+ }
},
{
- "id": "cl26lip76000e2e6ebmph843a",
+ "id": "cl9ipbcjy000k3b6oe8lta5c1",
+ "groupId": "cl9ipbcjy000j3b6oqngo7luv",
"type": "Webhook",
- "groupId": "cl26lidjz000a2e6etf4v03hv",
"options": {
- "isCustomBody": false,
- "isAdvancedConfig": false,
+ "responseVariableMapping": [
+ {
+ "id": "cl9ipdspg000p3b6ognbfvmdx",
+ "variableId": "vcl9ipdxnj000q3b6oy55th4xb",
+ "bodyPath": "data"
+ }
+ ],
+ "variablesForTest": [],
+ "isAdvancedConfig": true,
+ "isCustomBody": true
+ },
+ "webhookId": "full-body-webhook"
+ },
+ {
+ "id": "cl9ipe5t8000s3b6ocswre500",
+ "groupId": "cl9ipbcjy000j3b6oqngo7luv",
+ "type": "text",
+ "content": {
+ "html": "
Data of first request:
{{Data}}
",
+ "richText": [
+ {
+ "type": "p",
+ "children": [{ "text": "Data of first request:" }]
+ },
+ { "type": "p", "children": [{ "text": "" }] },
+ { "type": "p", "children": [{ "text": "{{Data}}" }] }
+ ],
+ "plainText": "Data of first request:{{Data}}"
+ },
+ "outgoingEdgeId": "cl9ipet83000z3b6of6zfqota"
+ }
+ ]
+ },
+ {
+ "id": "cl9ipej6b000u3b6oeaz305l6",
+ "graphCoordinates": { "x": 1138, "y": 85 },
+ "title": "Group #2 copy",
+ "blocks": [
+ {
+ "id": "cl9ipej6c000w3b6otzk247vl",
+ "groupId": "cl9ipej6b000u3b6oeaz305l6",
+ "type": "Webhook",
+ "options": {
+ "responseVariableMapping": [
+ {
+ "id": "cl9ipdspg000p3b6ognbfvmdx",
+ "variableId": "vcl9ipdxnj000q3b6oy55th4xb",
+ "bodyPath": "data"
+ }
+ ],
"variablesForTest": [],
- "responseVariableMapping": []
+ "isAdvancedConfig": true,
+ "isCustomBody": true
},
- "webhookId": "success-webhook"
+ "webhookId": "partial-body-webhook"
},
{
- "id": "cl26m0pdz00042e6ebjdoclaa",
- "groupId": "cl26lidjz000a2e6etf4v03hv",
+ "id": "cl9ipej6c000y3b6oegzkgloq",
+ "groupId": "cl9ipej6b000u3b6oeaz305l6",
+ "type": "text",
+ "content": {
+ "html": "Data of second request:
{{Data}}
",
+ "richText": [
+ {
+ "type": "p",
+ "children": [{ "text": "Data of second request:" }]
+ },
+ { "type": "p", "children": [{ "text": "" }] },
+ { "type": "p", "children": [{ "text": "{{Data}}" }] }
+ ],
+ "plainText": "Data of second request:{{Data}}"
+ }
+ }
+ ]
+ },
+ {
+ "id": "cl9ipkaer00153b6ov230yuv2",
+ "graphCoordinates": { "x": 333, "y": 26 },
+ "title": "Group #4",
+ "blocks": [
+ {
+ "id": "cl9ipkaer00163b6o0ohmmscn",
+ "groupId": "cl9ipkaer00153b6ov230yuv2",
"type": "choice input",
"options": { "buttonLabel": "Send", "isMultipleChoice": false },
"items": [
{
- "id": "cl26m0pdz00052e6ecmxwfz44",
- "blockId": "cl26m0pdz00042e6ebjdoclaa",
+ "id": "cl9ipkaer00173b6oxof4zrqn",
+ "blockId": "cl9ipkaer00163b6o0ohmmscn",
"type": 0,
- "content": "Send failed webhook"
+ "content": "Send failing webhook"
}
]
},
{
- "id": "cl26m0w9b00072e6eld1ei291",
- "groupId": "cl26lidjz000a2e6etf4v03hv",
+ "id": "cl9ipki9u00193b6okmhudo0f",
+ "groupId": "cl9ipkaer00153b6ov230yuv2",
"type": "Webhook",
"options": {
"responseVariableMapping": [],
@@ -74,26 +195,51 @@
"isAdvancedConfig": false,
"isCustomBody": false
},
- "webhookId": "failed-webhook"
+ "webhookId": "failing-webhook",
+ "outgoingEdgeId": "cl9ipklm0001c3b6oy0a5nbhr"
}
- ],
- "title": "Group #1",
- "graphCoordinates": { "x": 386, "y": 117 }
+ ]
}
],
"variables": [
- { "id": "vcl26lzmg100012e6e9rn57c3o", "name": "var1" },
- { "id": "vcl26lzo7q00022e6edw3pe7lf", "name": "var2" },
- { "id": "vcl26lzq6s00032e6ecuhh80qz", "name": "var3" }
+ { "id": "vcl9ipajth000c3b6okl97r81j", "name": "Name" },
+ { "id": "vcl9ipaszl000e3b6ousjxuw7b", "name": "Age" },
+ { "id": "vcl9ipbokm000n3b6o06hvarrf", "name": "Full body" },
+ { "id": "vcl9ipdxnj000q3b6oy55th4xb", "name": "Data" },
+ { "id": "vcl9ipg4tb00103b6oue08w3nm", "name": "Gender" }
],
"edges": [
{
- "id": "cl26liqj6000g2e6ed2cwkvse",
- "to": { "groupId": "cl26lidjz000a2e6etf4v03hv" },
"from": {
- "blockId": "cl26li8fj0001iez0bqfraw9h",
- "groupId": "cl26li8fj0000iez05x7razkg"
- }
+ "groupId": "cl9ipa38j00083b6o69e90m4t",
+ "blockId": "cl9ipb08n000f3b6ok3mi2p48"
+ },
+ "to": { "groupId": "cl9ipbcjy000j3b6oqngo7luv" },
+ "id": "cl9ipcp83000o3b6odsn0a9a1"
+ },
+ {
+ "from": {
+ "groupId": "cl9ipbcjy000j3b6oqngo7luv",
+ "blockId": "cl9ipe5t8000s3b6ocswre500"
+ },
+ "to": { "groupId": "cl9ipej6b000u3b6oeaz305l6" },
+ "id": "cl9ipet83000z3b6of6zfqota"
+ },
+ {
+ "from": {
+ "groupId": "cl9ip9u0j0000d71a5d98gwni",
+ "blockId": "cl9ip9u0j0001d71a44dsd2p1"
+ },
+ "to": { "groupId": "cl9ipkaer00153b6ov230yuv2" },
+ "id": "cl9ipkkb2001b3b6oh3vptq9k"
+ },
+ {
+ "from": {
+ "groupId": "cl9ipkaer00153b6ov230yuv2",
+ "blockId": "cl9ipki9u00193b6okmhudo0f"
+ },
+ "to": { "groupId": "cl9ipa38j00083b6o69e90m4t" },
+ "id": "cl9ipklm0001c3b6oy0a5nbhr"
}
],
"theme": {
@@ -115,8 +261,9 @@
},
"settings": {
"general": {
- "isBrandingEnabled": true,
+ "isBrandingEnabled": false,
"isInputPrefillEnabled": true,
+ "isHideQueryParamsEnabled": true,
"isNewResultOnRefreshEnabled": false
},
"metadata": {
@@ -125,5 +272,8 @@
"typingEmulation": { "speed": 300, "enabled": true, "maxDelay": 1.5 }
},
"publicId": null,
- "customDomain": null
+ "customDomain": null,
+ "workspaceId": "proWorkspace",
+ "isArchived": false,
+ "isClosed": false
}
diff --git a/apps/viewer/playwright/tests/webhook.spec.ts b/apps/viewer/playwright/tests/webhook.spec.ts
index 0de8f26e8b..ee0c4a3d0f 100644
--- a/apps/viewer/playwright/tests/webhook.spec.ts
+++ b/apps/viewer/playwright/tests/webhook.spec.ts
@@ -14,40 +14,50 @@ test('should execute webhooks properly', async ({ page }) => {
path.join(__dirname, '../fixtures/typebots/webhook.json'),
{ id: typebotId, publicId: `${typebotId}-public` }
)
+
await createWebhook(typebotId, {
- id: 'success-webhook',
- url: 'http://localhost:3001/api/mock/success',
+ id: 'failing-webhook',
+ url: 'http://localhost:3001/api/mock/fail',
method: HttpMethod.POST,
})
+
await createWebhook(typebotId, {
- id: 'failed-webhook',
- url: 'http://localhost:3001/api/mock/fail',
+ id: 'partial-body-webhook',
+ url: 'http://localhost:3000/api/mock/webhook-easy-config',
+ method: HttpMethod.POST,
+ body: `{
+ "name": "{{Name}}",
+ "age": {{Age}},
+ "gender": "{{Gender}}"
+ }`,
+ })
+
+ await createWebhook(typebotId, {
+ id: 'full-body-webhook',
+ url: 'http://localhost:3000/api/mock/webhook-easy-config',
method: HttpMethod.POST,
+ body: `{{Full body}}`,
})
await page.goto(`/${typebotId}-public`)
- await Promise.all([
- page.waitForResponse(
- async (resp) =>
- resp.request().url().includes(`/api/typebots/${typebotId}/blocks`) &&
- resp.status() === 200 &&
- (await resp.json()).statusCode === 200
- ),
- typebotViewer(page).locator('text=Send success webhook').click(),
- ])
- await Promise.all([
- page.waitForResponse(
- async (resp) =>
- resp.request().url().includes(`/api/typebots/${typebotId}/blocks`) &&
- resp.status() === 200 &&
- (await resp.json()).statusCode === 500
- ),
- typebotViewer(page).locator('text=Send failed webhook').click(),
- ])
+ await typebotViewer(page).locator('text=Send failing webhook').click()
+ await typebotViewer(page)
+ .locator('[placeholder="Type a name..."]')
+ .fill('John')
+ await typebotViewer(page).locator('text="Send"').click()
+ await typebotViewer(page).locator('[placeholder="Type an age..."]').fill('30')
+ await typebotViewer(page).locator('text="Send"').click()
+ await typebotViewer(page).locator('text="Male"').click()
+ await expect(
+ typebotViewer(page).getByText('{"name":"John","age":25,"gender":"male"}')
+ ).toBeVisible()
+ await expect(
+ typebotViewer(page).getByText('{"name":"John","age":30,"gender":"Male"}')
+ ).toBeVisible()
await page.goto(`http://localhost:3000/typebots/${typebotId}/results`)
await page.click('text="See logs"')
await expect(
- page.locator('text="Webhook successfuly executed."')
+ page.locator('text="Webhook successfuly executed." >> nth=1')
).toBeVisible()
await expect(page.locator('text="Webhook returned an error"')).toBeVisible()
})
diff --git a/packages/bot-engine/src/services/variable.ts b/packages/bot-engine/src/services/variable.ts
index 9fb71243a1..99b37eca7b 100644
--- a/packages/bot-engine/src/services/variable.ts
+++ b/packages/bot-engine/src/services/variable.ts
@@ -1,4 +1,4 @@
-import { Variable } from 'models'
+import { Variable, VariableWithValue } from 'models'
import { isDefined, isNotDefined } from 'utils'
export const stringContainsVariable = (str: string): boolean =>
@@ -18,11 +18,10 @@ export const parseVariables =
const matchedVarName = fullVariableString.replace(/{{|}}/g, '')
const variable = variables.find((v) => {
return matchedVarName === v.name && isDefined(v.value)
- })
+ }) as VariableWithValue | undefined
if (!variable) return ''
if (options.fieldToParse === 'id') return variable.id
const { value } = variable
- if (isNotDefined(value)) return ''
if (options.escapeForJson) return jsonParse(value)
const parsedValue = safeStringify(value)
if (!parsedValue) return ''