🐛 (webhook) Update zapier and make.com result sample parser
This commit is contained in:
@ -2,10 +2,11 @@ import prisma from '@typebot.io/lib/prisma'
|
||||
import { canReadTypebots } from '@/helpers/databaseRules'
|
||||
import { authenticatedProcedure } from '@/helpers/server/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { Block, Typebot } from '@typebot.io/schemas'
|
||||
import { Typebot } from '@typebot.io/schemas'
|
||||
import { z } from 'zod'
|
||||
import { fetchLinkedTypebots } from '@/features/blocks/logic/typebotLink/helpers/fetchLinkedTypebots'
|
||||
import { parseResultExample } from '../helpers/parseResultExample'
|
||||
import { parseSampleResult } from '@typebot.io/bot-engine/blocks/integrations/webhook/parseSampleResult'
|
||||
import { getBlockById } from '@typebot.io/lib/getBlockById'
|
||||
|
||||
export const getResultExample = authenticatedProcedure
|
||||
.meta({
|
||||
@ -27,15 +28,7 @@ export const getResultExample = authenticatedProcedure
|
||||
)
|
||||
.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.'),
|
||||
resultExample: z.record(z.any()).describe('Can contain any fields.'),
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { typebotId, blockId }, ctx: { user } }) => {
|
||||
@ -52,20 +45,17 @@ export const getResultExample = authenticatedProcedure
|
||||
if (!typebot)
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'Typebot not found' })
|
||||
|
||||
const block = typebot.groups
|
||||
.flatMap<Block>((group) => group.blocks)
|
||||
.find((block) => block.id === blockId)
|
||||
const { group } = getBlockById(blockId, typebot.groups)
|
||||
|
||||
if (!block)
|
||||
if (!group)
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'Block not found' })
|
||||
|
||||
const linkedTypebots = await fetchLinkedTypebots(typebot, user)
|
||||
|
||||
return {
|
||||
resultExample: await parseResultExample({
|
||||
typebot,
|
||||
linkedTypebots,
|
||||
userEmail: user.email ?? 'test@email.com',
|
||||
})(block.id),
|
||||
resultExample: await parseSampleResult(typebot, linkedTypebots)(
|
||||
group.id,
|
||||
typebot.variables
|
||||
),
|
||||
}
|
||||
})
|
||||
|
@ -1,253 +0,0 @@
|
||||
import {
|
||||
InputBlock,
|
||||
PublicTypebot,
|
||||
ResultHeaderCell,
|
||||
Block,
|
||||
Typebot,
|
||||
TypebotLinkBlock,
|
||||
} from '@typebot.io/schemas'
|
||||
import { isInputBlock, byId, isNotDefined } from '@typebot.io/lib'
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||
import { LogicBlockType } from '@typebot.io/schemas/features/blocks/logic/constants'
|
||||
import { EventType } from '@typebot.io/schemas/features/events/constants'
|
||||
import { parseResultHeader } from '@typebot.io/lib/results/parseResultHeader'
|
||||
|
||||
export const parseResultExample =
|
||||
({
|
||||
typebot,
|
||||
linkedTypebots,
|
||||
userEmail,
|
||||
}: {
|
||||
typebot: Pick<
|
||||
Typebot | PublicTypebot,
|
||||
'groups' | 'variables' | 'edges' | 'events'
|
||||
>
|
||||
linkedTypebots: (Typebot | PublicTypebot)[]
|
||||
userEmail: string
|
||||
}) =>
|
||||
async (
|
||||
currentBlockId: string
|
||||
): Promise<
|
||||
{
|
||||
message: 'This is a sample result, it has been generated ⬇️'
|
||||
'Submitted at': string
|
||||
} & { [k: string]: string | undefined }
|
||||
> => {
|
||||
const header = parseResultHeader(typebot, linkedTypebots)
|
||||
const linkedInputBlocks = await extractLinkedInputBlocks(
|
||||
typebot,
|
||||
linkedTypebots
|
||||
)(currentBlockId)
|
||||
|
||||
return {
|
||||
message: 'This is a sample result, it has been generated ⬇️',
|
||||
'Submitted at': new Date().toISOString(),
|
||||
...parseResultSample({
|
||||
inputBlocks: linkedInputBlocks,
|
||||
headerCells: header,
|
||||
userEmail,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
const extractLinkedInputBlocks =
|
||||
(
|
||||
typebot:
|
||||
| Pick<
|
||||
Typebot | PublicTypebot,
|
||||
'groups' | 'variables' | 'edges' | 'events'
|
||||
>
|
||||
| undefined,
|
||||
linkedTypebots: (Typebot | PublicTypebot)[]
|
||||
) =>
|
||||
async (
|
||||
blockId?: string,
|
||||
direction: 'backward' | 'forward' = 'backward'
|
||||
): Promise<InputBlock[]> => {
|
||||
if (!typebot) return []
|
||||
const previousLinkedTypebotBlocks = walkEdgesAndExtract(
|
||||
'linkedBot',
|
||||
direction,
|
||||
typebot
|
||||
)({
|
||||
blockId,
|
||||
}) as TypebotLinkBlock[]
|
||||
|
||||
const linkedBotInputs =
|
||||
previousLinkedTypebotBlocks.length > 0
|
||||
? await Promise.all(
|
||||
previousLinkedTypebotBlocks.map((linkedBot) => {
|
||||
const typebot = linkedTypebots.find((t) =>
|
||||
'typebotId' in t
|
||||
? t.typebotId === linkedBot.options?.typebotId
|
||||
: t.id === linkedBot.options?.typebotId
|
||||
)
|
||||
const blockId = linkedBot.options?.groupId
|
||||
? typebot?.groups
|
||||
.find(byId(linkedBot.options?.groupId))
|
||||
?.blocks.at(0)?.id
|
||||
: undefined
|
||||
return extractLinkedInputBlocks(typebot, linkedTypebots)(
|
||||
blockId,
|
||||
'forward'
|
||||
)
|
||||
})
|
||||
)
|
||||
: []
|
||||
|
||||
return (
|
||||
walkEdgesAndExtract(
|
||||
'input',
|
||||
direction,
|
||||
typebot
|
||||
)({
|
||||
blockId,
|
||||
}) as InputBlock[]
|
||||
).concat(linkedBotInputs.flatMap((l) => l))
|
||||
}
|
||||
|
||||
const parseResultSample = ({
|
||||
inputBlocks,
|
||||
headerCells,
|
||||
userEmail,
|
||||
}: {
|
||||
inputBlocks: InputBlock[]
|
||||
headerCells: ResultHeaderCell[]
|
||||
userEmail: string
|
||||
}) =>
|
||||
headerCells.reduce<Record<string, string | undefined>>(
|
||||
(resultSample, cell) => {
|
||||
const inputBlock = inputBlocks.find((inputBlock) =>
|
||||
cell.blocks?.some((block) => block.id === inputBlock.id)
|
||||
)
|
||||
if (isNotDefined(inputBlock)) {
|
||||
if (cell.variableIds)
|
||||
return {
|
||||
...resultSample,
|
||||
[cell.label]: 'content',
|
||||
}
|
||||
return resultSample
|
||||
}
|
||||
const value = getSampleValue({ block: inputBlock, userEmail })
|
||||
return {
|
||||
...resultSample,
|
||||
[cell.label]: value,
|
||||
}
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
const getSampleValue = ({
|
||||
block,
|
||||
userEmail,
|
||||
}: {
|
||||
block: InputBlock
|
||||
userEmail: string
|
||||
}) => {
|
||||
switch (block.type) {
|
||||
case InputBlockType.CHOICE:
|
||||
return block.options?.isMultipleChoice
|
||||
? block.items?.map((i) => i.content).join(', ')
|
||||
: block.items?.at(0)?.content ?? 'Item'
|
||||
case InputBlockType.DATE:
|
||||
return new Date().toUTCString()
|
||||
case InputBlockType.EMAIL:
|
||||
return userEmail
|
||||
case InputBlockType.NUMBER:
|
||||
return '20'
|
||||
case InputBlockType.PHONE:
|
||||
return '+33665566773'
|
||||
case InputBlockType.TEXT:
|
||||
return 'answer value'
|
||||
case InputBlockType.URL:
|
||||
return 'https://test.com'
|
||||
}
|
||||
}
|
||||
|
||||
const walkEdgesAndExtract =
|
||||
(
|
||||
type: 'input' | 'linkedBot',
|
||||
direction: 'backward' | 'forward',
|
||||
typebot: Pick<
|
||||
Typebot | PublicTypebot,
|
||||
'groups' | 'variables' | 'edges' | 'events'
|
||||
>
|
||||
) =>
|
||||
({ blockId }: { blockId?: string }): Block[] => {
|
||||
const groupId = typebot.groups.find((g) =>
|
||||
g.blocks.some((b) => b.id === blockId)
|
||||
)?.id
|
||||
const startEventEdgeId = groupId
|
||||
? undefined
|
||||
: typebot.events?.find((e) => e.type === EventType.START)?.outgoingEdgeId
|
||||
const currentGroupId =
|
||||
groupId ??
|
||||
(startEventEdgeId
|
||||
? typebot.edges.find(byId(startEventEdgeId))?.to.groupId
|
||||
: typebot.groups.find((g) => g.blocks[0].type === 'start')?.id)
|
||||
if (!currentGroupId)
|
||||
throw new Error("walkEdgesAndExtract - Can't find currentGroupId")
|
||||
const blocksInGroup = extractBlocksInGroup(
|
||||
type,
|
||||
typebot
|
||||
)({
|
||||
groupId: currentGroupId,
|
||||
blockId,
|
||||
})
|
||||
const otherGroupIds = getConnectedGroups(typebot, direction)(currentGroupId)
|
||||
return [
|
||||
...blocksInGroup,
|
||||
...otherGroupIds.flatMap((groupId) =>
|
||||
extractBlocksInGroup(type, typebot)({ groupId, blockId })
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
const getConnectedGroups =
|
||||
(
|
||||
typebot: Pick<Typebot | PublicTypebot, 'groups' | 'variables' | 'edges'>,
|
||||
direction: 'backward' | 'forward',
|
||||
existingGroupIds?: string[]
|
||||
) =>
|
||||
(groupId: string): string[] => {
|
||||
const groups = typebot.edges.reduce<string[]>((groupIds, edge) => {
|
||||
const fromGroupId = typebot.groups.find((g) =>
|
||||
g.blocks.some(
|
||||
(b) => 'blockId' in edge.from && b.id === edge.from.blockId
|
||||
)
|
||||
)?.id
|
||||
if (!fromGroupId) return groupIds
|
||||
if (direction === 'forward')
|
||||
return (!existingGroupIds ||
|
||||
!existingGroupIds?.includes(edge.to.groupId)) &&
|
||||
fromGroupId === groupId
|
||||
? [...groupIds, edge.to.groupId]
|
||||
: groupIds
|
||||
return (!existingGroupIds || !existingGroupIds.includes(fromGroupId)) &&
|
||||
edge.to.groupId === groupId
|
||||
? [...groupIds, fromGroupId]
|
||||
: groupIds
|
||||
}, [])
|
||||
const newGroups = [...(existingGroupIds ?? []), ...groups]
|
||||
return groups.concat(
|
||||
groups.flatMap(getConnectedGroups(typebot, direction, newGroups))
|
||||
)
|
||||
}
|
||||
|
||||
const extractBlocksInGroup =
|
||||
(
|
||||
type: 'input' | 'linkedBot',
|
||||
typebot: Pick<Typebot | PublicTypebot, 'groups' | 'variables' | 'edges'>
|
||||
) =>
|
||||
({ groupId, blockId }: { groupId: string; blockId: string | undefined }) => {
|
||||
const currentGroup = typebot.groups.find(byId(groupId))
|
||||
if (!currentGroup) return []
|
||||
const blocks: Block[] = []
|
||||
for (const block of currentGroup.blocks) {
|
||||
if (block.id === blockId) break
|
||||
if (type === 'input' && isInputBlock(block)) blocks.push(block)
|
||||
if (type === 'linkedBot' && block.type === LogicBlockType.TYPEBOT_LINK)
|
||||
blocks.push(block)
|
||||
}
|
||||
return blocks
|
||||
}
|
Reference in New Issue
Block a user