Improved Webhook Handling (#676)

This commit is contained in:
Chris Anderson 2025-06-25 12:17:44 -05:00 committed by GitHub
parent d1e9df8f70
commit 6374fbabd2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 55 additions and 16 deletions

View file

@ -29,7 +29,9 @@ export class SQLModel extends RawModel {
// Take JSON attributes and stringify before insertion
for (const attribute of this.jsonAttributes) {
obj[attribute] = JSON.stringify(obj[attribute])
obj[attribute] = obj[attribute] == null
? undefined
: JSON.stringify(obj[attribute])
}
return this.formatJson(obj)

View file

@ -221,9 +221,7 @@ export const notifyJourney = async (reference_id: string, response?: any) => {
// Save response into user step
if (response) {
await JourneyUserStep.update(q => q.where('id', referenceId), {
data: {
response,
},
data: response,
})
}

View file

@ -39,7 +39,7 @@ export default class LocalWebhookProvider extends WebhookProvider {
if (response.ok) {
return {
message: options,
request: options,
success: true,
response: responseBody,
}

View file

@ -25,7 +25,7 @@ export default class LoggerWebhookProvider extends WebhookProvider {
logger.info(options, 'provider:webhook:logger')
return {
message: options,
request: options,
success: true,
response: '',
}

View file

@ -6,7 +6,7 @@ export interface Webhook {
}
export interface WebhookResponse {
message: Webhook
request: Webhook
success: boolean
response: Record<string, any> | string
}

View file

@ -37,7 +37,7 @@ export default class WebhookJob extends Job {
try {
const result = await channel.send(template, data)
await finalizeSend(data, result)
await finalizeSend(data, result.response)
} catch (error: any) {
await failSend(data, error)
} finally {

View file

@ -124,7 +124,7 @@ export const sendProof = async (template: TemplateType, variables: Variables, re
logger.info(response, 'template:proof:push:result')
} else if (template.type === 'webhook') {
const channel = await loadWebhookChannel(campaign.provider_id, project.id)
await channel?.send(template, variables)
response = await channel?.send(template, variables)
} else {
throw new RequestError('Sending template proofs is only supported for email and text message types as this time.')
}
@ -141,6 +141,8 @@ export const sendProof = async (template: TemplateType, variables: Variables, re
},
},
}).queue()
return response
}
// Determine what template to send to the user based on the following:

View file

@ -48,6 +48,14 @@
padding: 5px 0;
}
.heading.heading-h5 {
margin: 5px 0;
}
.heading.heading-h4 h4 {
padding: 5px 0;
}
.heading label, .heading input {
margin: 0;
}

View file

@ -2,7 +2,7 @@ import './Heading.css'
interface HeadingProps {
title: React.ReactNode
size?: 'h2' | 'h3' | 'h4'
size?: 'h2' | 'h3' | 'h4' | 'h5'
actions?: React.ReactNode
children?: React.ReactNode
}

View file

@ -157,6 +157,17 @@
width: 100%;
}
.webhook-frame .heading {
background: var(--color-grey);
border-radius: var(--border-radius);
padding: 5px 10px;
margin-bottom: 10px;
}
.webhook-frame .webhook-block {
margin-bottom: 20px;
}
.preview.small .email-frame iframe {
width: 200%;
height: 200%;
@ -173,4 +184,11 @@
border-radius: 0;
transform: scale(0.8);
transform-origin: top left;
}
.preview.small .webhook-frame {
padding: 5px 10px;
transform: scale(0.8);
transform-origin: top left;
width: 125%;
}

View file

@ -6,13 +6,15 @@ import { ReactNode, useContext } from 'react'
import { ProjectContext } from '../contexts'
import JsonPreview from './JsonPreview'
import clsx from 'clsx'
import Heading from './Heading'
interface PreviewProps {
template: Pick<Template, 'type' | 'data'>
response?: any
size?: 'small' | 'large'
}
export default function Preview({ template, size = 'large' }: PreviewProps) {
export default function Preview({ template, response, size = 'large' }: PreviewProps) {
const [project] = useContext(ProjectContext)
const { data, type } = template
@ -59,7 +61,14 @@ export default function Preview({ template, size = 'large' }: PreviewProps) {
} else if (type === 'webhook') {
preview = (
<div className="webhook-frame">
<JsonPreview value={data} />
<div className="webhook-block">
<Heading title="Request" size="h5" />
<JsonPreview value={data} />
</div>
{response && <div className="webhook-block">
<Heading title="Response" size="h5" />
<JsonPreview value={response.data} />
</div>}
</div>
)
}

View file

@ -5,7 +5,7 @@
--color-primary: var(--color-black);
--color-primary-soft: #6b707b;
--color-grey: #eae8e8;
--color-grey: #e8e8ea;
--color-grey-soft: #F5F5F7;
--color-grey-hard: #cecfd2;

View file

@ -52,6 +52,7 @@ export default function CampaignPreview() {
const [isUserLookupOpen, setIsUserLookupOpen] = useState(false)
const [isSendProofOpen, setIsSendProofOpen] = useState(false)
const template = campaignState[0].templates.find(template => template.locale === currentLocale?.key)
const [proofResponse, setProofResponse] = useState<any>(undefined)
if (!template) {
return (<>
@ -82,10 +83,11 @@ export default function CampaignPreview() {
const handleSendProof = async (recipient: string) => {
try {
await api.templates.proof(project.id, template.id, {
const response = await api.templates.proof(project.id, template.id, {
variables: JSON.parse(value ?? '{}'),
recipient,
})
setProofResponse(response)
} catch (error: any) {
if (error.response.data.error) {
toast.error(error.response.data.error)
@ -136,7 +138,7 @@ export default function CampaignPreview() {
variant="secondary"
onClick={() => setIsSendProofOpen(true)}>{t('send_proof')}</Button>
} />
<Preview template={{ type: template.type, data }} />
<Preview template={{ type: template.type, data }} response={proofResponse} />
</Column>
</Columns>

View file

@ -3,7 +3,7 @@ title: Weekly Summary
sidebar_position: 3
---
# Weely Summary
# Weekly Summary
## Scenario
You have an e-commerce site and you want to sent weekly stats to all vendors that sell products through your platform, letting them know how many people viewed and purchased their products in the past 7 days.