Skip to content

Commit

Permalink
✨ Add webhook blocks API public endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Nov 30, 2022
1 parent f9ffdbc commit c799717
Show file tree
Hide file tree
Showing 67 changed files with 3,033 additions and 432 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ChangePlanForm } from '../ChangePlanForm'
export const BillingContent = () => {
const { workspace, refreshWorkspace } = useWorkspace()

console.log(workspace)
if (!workspace) return null
return (
<Stack spacing="10" w="full">
Expand All @@ -20,13 +21,7 @@ export const BillingContent = () => {
<CurrentSubscriptionContent
plan={workspace.plan}
stripeId={workspace.stripeId}
onCancelSuccess={() =>
refreshWorkspace({
plan: Plan.FREE,
additionalChatsIndex: 0,
additionalStorageIndex: 0,
})
}
onCancelSuccess={refreshWorkspace}
/>
<HStack maxW="500px">
<StripeClimateLogo />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,7 @@ export const ChangePlanForm = () => {
additionalChatsIndex: selectedChatsLimitIndex,
additionalStorageIndex: selectedStorageLimitIndex,
})
refreshWorkspace({
plan,
additionalChatsIndex: selectedChatsLimitIndex,
additionalStorageIndex: selectedStorageLimitIndex,
})
refreshWorkspace()
showToast({
status: 'success',
description: `Workspace ${plan} plan successfully updated 🎉`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './router'
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { getLinkedTypebots } from '@/features/blocks/logic/typebotLink/api'
import prisma from '@/lib/prisma'
import { canReadTypebot } from '@/utils/api/dbRules'
import { authenticatedProcedure } from '@/utils/server/trpc'
import { TRPCError } from '@trpc/server'
import { Typebot, Webhook } from 'models'
import { z } from 'zod'
import { parseResultExample } from '../utils'

export const getResultExampleProcedure = authenticatedProcedure
.meta({
openapi: {
method: 'GET',
path: '/typebots/{typebotId}/webhookBlocks/{blockId}/getResultExample',
protect: true,
summary: 'Get result example',
description:
'Returns "fake" result for webhook block to help you anticipate how the webhook will behave.',
tags: ['Webhook'],
},
})
.input(
z.object({
typebotId: z.string(),
blockId: z.string(),
})
)
.output(
z.object({
resultExample: z
.object({
message: z.literal(
'This is a sample result, it has been generated ⬇️'
),
'Submitted at': z.string(),
})
.and(z.record(z.string().optional()))
.describe('Can contain any fields.'),
})
)
.query(async ({ input: { typebotId, blockId }, ctx: { user } }) => {
const typebot = (await prisma.typebot.findFirst({
where: canReadTypebot(typebotId, user),
select: {
groups: true,
edges: true,
variables: true,
webhooks: true,
},
})) as
| (Pick<Typebot, 'groups' | 'edges' | 'variables'> & {
webhooks: Webhook[]
})
| null

if (!typebot)
throw new TRPCError({ code: 'NOT_FOUND', message: 'Typebot not found' })

const block = typebot.groups
.flatMap((g) => g.blocks)
.find((s) => s.id === blockId)

if (!block)
throw new TRPCError({ code: 'NOT_FOUND', message: 'Block not found' })

const linkedTypebots = await getLinkedTypebots(typebot, user)

return {
resultExample: await parseResultExample(
typebot,
linkedTypebots
)(block.groupId),
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './getResultExampleProcedure'
export * from './listWebhookBlocksProcedure'
export * from './subscribeWebhookProcedure'
export * from './unsubscribeWebhookProcedure'
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import prisma from '@/lib/prisma'
import { canReadTypebot } from '@/utils/api/dbRules'
import { authenticatedProcedure } from '@/utils/server/trpc'
import { TRPCError } from '@trpc/server'
import { Group, Typebot, Webhook, WebhookBlock } from 'models'
import { byId, isWebhookBlock } from 'utils'
import { z } from 'zod'

export const listWebhookBlocksProcedure = authenticatedProcedure
.meta({
openapi: {
method: 'GET',
path: '/typebots/{typebotId}/webhookBlocks',
protect: true,
summary: 'List webhook blocks',
description:
'Returns a list of all the webhook blocks that you can subscribe to.',
tags: ['Webhook'],
},
})
.input(
z.object({
typebotId: z.string(),
})
)
.output(
z.object({
webhookBlocks: z.array(
z.object({
id: z.string(),
label: z.string(),
url: z.string().optional(),
})
),
})
)
.query(async ({ input: { typebotId }, ctx: { user } }) => {
const typebot = (await prisma.typebot.findFirst({
where: canReadTypebot(typebotId, user),
select: {
groups: true,
webhooks: true,
},
})) as (Pick<Typebot, 'groups'> & { webhooks: Webhook[] }) | null
if (!typebot)
throw new TRPCError({ code: 'NOT_FOUND', message: 'Typebot not found' })

const webhookBlocks = (typebot?.groups as Group[]).reduce<
{ id: string; label: string; url: string | undefined }[]
>((webhookBlocks, group) => {
const blocks = group.blocks.filter((block) =>
isWebhookBlock(block)
) as WebhookBlock[]
return [
...webhookBlocks,
...blocks.map((b) => ({
id: b.id,
label: `${group.title} > ${b.id}`,
url: typebot?.webhooks.find(byId(b.webhookId))?.url ?? undefined,
})),
]
}, [])

return { webhookBlocks }
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import prisma from '@/lib/prisma'
import { canWriteTypebot } from '@/utils/api/dbRules'
import { authenticatedProcedure } from '@/utils/server/trpc'
import { TRPCError } from '@trpc/server'
import { Typebot, Webhook, WebhookBlock } from 'models'
import { byId, isWebhookBlock } from 'utils'
import { z } from 'zod'

export const subscribeWebhookProcedure = authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/typebots/{typebotId}/webhookBlocks/{blockId}/subscribe',
protect: true,
summary: 'Subscribe to webhook block',
tags: ['Webhook'],
},
})
.input(
z.object({
typebotId: z.string(),
blockId: z.string(),
url: z.string(),
})
)
.output(
z.object({
id: z.string(),
url: z.string().nullable(),
})
)
.query(async ({ input: { typebotId, blockId, url }, ctx: { user } }) => {
const typebot = (await prisma.typebot.findFirst({
where: canWriteTypebot(typebotId, user),
select: {
groups: true,
webhooks: true,
},
})) as (Pick<Typebot, 'groups'> & { webhooks: Webhook[] }) | null

if (!typebot)
throw new TRPCError({ code: 'NOT_FOUND', message: 'Typebot not found' })

const webhookBlock = typebot.groups
.flatMap((g) => g.blocks)
.find(byId(blockId)) as WebhookBlock | null

if (!webhookBlock || !isWebhookBlock(webhookBlock))
throw new TRPCError({
code: 'NOT_FOUND',
message: 'Webhook block not found',
})

await prisma.webhook.upsert({
where: { id: webhookBlock.webhookId },
update: { url, body: '{{state}}', method: 'POST' },
create: { url, body: '{{state}}', method: 'POST', typebotId },
})

return {
id: blockId,
url,
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import prisma from '@/lib/prisma'
import { canWriteTypebot } from '@/utils/api/dbRules'
import { authenticatedProcedure } from '@/utils/server/trpc'
import { TRPCError } from '@trpc/server'
import { Typebot, Webhook, WebhookBlock } from 'models'
import { byId, isWebhookBlock } from 'utils'
import { z } from 'zod'

export const unsubscribeWebhookProcedure = authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/typebots/{typebotId}/webhookBlocks/{blockId}/unsubscribe',
protect: true,
summary: 'Unsubscribe from webhook block',
tags: ['Webhook'],
},
})
.input(
z.object({
typebotId: z.string(),
blockId: z.string(),
})
)
.output(
z.object({
id: z.string(),
url: z.string().nullable(),
})
)
.query(async ({ input: { typebotId, blockId }, ctx: { user } }) => {
const typebot = (await prisma.typebot.findFirst({
where: canWriteTypebot(typebotId, user),
select: {
groups: true,
webhooks: true,
},
})) as (Pick<Typebot, 'groups'> & { webhooks: Webhook[] }) | null

if (!typebot)
throw new TRPCError({ code: 'NOT_FOUND', message: 'Typebot not found' })

const webhookBlock = typebot.groups
.flatMap((g) => g.blocks)
.find(byId(blockId)) as WebhookBlock | null

if (!webhookBlock || !isWebhookBlock(webhookBlock))
throw new TRPCError({
code: 'NOT_FOUND',
message: 'Webhook block not found',
})

await prisma.webhook.update({
where: { id: webhookBlock.webhookId },
data: { url: null },
})

return {
id: blockId,
url: null,
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { router } from '@/utils/server/trpc'
import {
listWebhookBlocksProcedure,
subscribeWebhookProcedure,
unsubscribeWebhookProcedure,
getResultExampleProcedure,
} from './procedures'

export const webhookRouter = router({
listWebhookBlocks: listWebhookBlocksProcedure,
getResultExample: getResultExampleProcedure,
subscribeWebhook: subscribeWebhookProcedure,
unsubscribeWebhook: unsubscribeWebhookProcedure,
})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './parseResultExample'
Loading

5 comments on commit c799717

@vercel
Copy link

@vercel vercel bot commented on c799717 Nov 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on c799717 Nov 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs – ./apps/docs

docs-git-main-typebot-io.vercel.app
docs-typebot-io.vercel.app
docs.typebot.io

@vercel
Copy link

@vercel vercel bot commented on c799717 Nov 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

viewer-v2-alpha – ./apps/viewer

ns8.vn
yobot.me
247987.com
8jours.top
bot.aws.bj
bot.bbc.bj
finplex.be
sat.cr8.ai
bot.aipr.kr
minipost.uk
bt.id8rs.com
bot.krdfy.com
goldorayo.com
vhpage.cr8.ai
am.nigerias.io
an.nigerias.io
ar.nigerias.io
bot.enreso.org
bot.lalmon.com
ticketfute.com
apo.nigerias.io
apr.nigerias.io
aso.nigerias.io
bot.ageenda.com
bot.artiweb.app
bot.devitus.com
bot.tc-mail.com
chat.sureb4.com
eventhub.com.au
games.klujo.com
sakuranembro.it
typebot.aloe.do
bot.piccinato.co
botc.ceox.com.br
clo.closeer.work
faqs.nigerias.io
feedback.ofx.one
form.syncwin.com
kw.wpwakanda.com
myrentalhost.com
stan.vselise.com
start.taxtree.io
typebot.aloe.bot
voicehelp.cr8.ai
app.chatforms.net
bot.agfunnel.tech
bot.hostnation.de
bot.maitempah.com
bot.phuonghub.com
bot.reviewzer.com
cares.urlabout.me
fmm.wpwakanda.com
gentleman-shop.fr
k1.kandabrand.com
lb.ticketfute.com
ov1.wpwakanda.com
ov2.wpwakanda.com
ov3.wpwakanda.com
1988.bouclidom.com
andreimayer.com.br
bot.megafox.com.br
bot.neferlopez.com
bots.robomotion.io
cadu.uninta.edu.br
dicanatural.online
goalsettingbot.com
positivobra.com.br
survey.digienge.io
this-is-a-test.com
zap.techadviser.in
bot.digitalbled.com
bot.eventhub.com.au
bot.jepierre.com.br
bot.winglabs.com.br
carsalesenquiry.com
demo.botscientis.us
forms.webisharp.com
kbsub.wpwakanda.com
live.botscientis.us
mentoria.omelhor.vc
nutrisamirbayde.com
order.maitempah.com
quest.wpwakanda.com
survey1.digienge.io
test.botscientis.us
typebot.stillio.com
wordsandimagery.com
bium.gratirabbit.com
bot.ansuraniphone.my
bot.cotemeuplano.com
chat.hayurihijab.com
chatbee.agfunnel.com
click.sevenoways.com
connect.growthguy.in
kuiz.sistemniaga.com
offer.botscientis.us
sellmycarglasgow.com
tenorioadvogados.com
uppity.wpwakanda.com
abutton.wpwakanda.com

@vercel
Copy link

@vercel vercel bot commented on c799717 Nov 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

builder-v2-typebot-io.vercel.app
app.typebot.io
builder-v2-git-main-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on c799717 Nov 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.