2
0

🐛 (sendMessage) Correctly preprocess and parse fetched bot

This commit is contained in:
Baptiste Arnaud
2023-08-24 09:11:10 +02:00
parent ee3b94c35d
commit 06ecdf040e
15 changed files with 63 additions and 45 deletions

View File

@ -1,7 +1,9 @@
import { useToast } from '@/hooks/useToast'
import { Button, ButtonProps, chakra } from '@chakra-ui/react'
import { Typebot, typebotCreateSchema } from '@typebot.io/schemas'
import { preprocessTypebot } from '@typebot.io/schemas/features/typebot/helpers/preprocessTypebot'
import React, { ChangeEvent } from 'react'
import { z } from 'zod'
type Props = {
onNewTypebot: (typebot: Typebot) => void
@ -18,7 +20,9 @@ export const ImportTypebotFromFileButton = ({
const file = e.target.files[0]
const fileContent = await readFile(file)
try {
const typebot = typebotCreateSchema.parse(JSON.parse(fileContent))
const typebot = z
.preprocess(preprocessTypebot, typebotCreateSchema)
.parse(JSON.parse(fileContent))
onNewTypebot(typebot as Typebot)
} catch (err) {
console.error(err)

View File

@ -8,9 +8,10 @@ import {
SessionState,
Variable,
ReplyLog,
Typebot,
VariableWithValue,
Edge,
typebotInSessionStateSchema,
TypebotInSession,
} from '@typebot.io/schemas'
import { ExecuteLogicResponse } from '@/features/chat/types'
import { createId } from '@paralleldrive/cuid2'
@ -73,7 +74,7 @@ export const executeTypebotLink = async (
const addLinkedTypebotToState = async (
state: SessionState,
block: TypebotLinkBlock,
linkedTypebot: Pick<Typebot, 'id' | 'edges' | 'groups' | 'variables'>
linkedTypebot: TypebotInSession
): Promise<SessionState> => {
const currentTypebotInQueue = state.typebotsQueue[0]
const isPreview = isNotDefined(currentTypebotInQueue.resultId)
@ -191,17 +192,19 @@ const fetchTypebot = async (state: SessionState, typebotId: string) => {
const typebot = await prisma.typebot.findUnique({
where: { id: typebotId },
select: {
version: true,
id: true,
edges: true,
groups: true,
variables: true,
},
})
return typebot as Pick<Typebot, 'id' | 'edges' | 'groups' | 'variables'>
return typebotInSessionStateSchema.parse(typebot)
}
const typebot = await prisma.publicTypebot.findUnique({
where: { typebotId },
select: {
version: true,
id: true,
edges: true,
groups: true,
@ -209,8 +212,8 @@ const fetchTypebot = async (state: SessionState, typebotId: string) => {
},
})
if (!typebot) return null
return {
return typebotInSessionStateSchema.parse({
...typebot,
id: typebotId,
} as Pick<Typebot, 'id' | 'edges' | 'groups' | 'variables'>
})
}

View File

@ -11,8 +11,8 @@ import {
SessionState,
StartParams,
StartTypebot,
startTypebotSchema,
Theme,
Typebot,
Variable,
VariableWithValue,
} from '@typebot.io/schemas'
@ -152,6 +152,7 @@ const startSession = async (
{
resultId: result?.id,
typebot: {
version: typebot.version,
id: typebot.id,
groups: typebot.groups,
edges: typebot.edges,
@ -268,17 +269,14 @@ const getTypebot = async (
const parsedTypebot =
typebotQuery && 'typebot' in typebotQuery
? ({
? {
id: typebotQuery.typebotId,
...omit(typebotQuery.typebot, 'workspace'),
...omit(typebotQuery, 'typebot', 'typebotId'),
} as StartTypebot & Pick<Typebot, 'isArchived' | 'isClosed'>)
: (typebotQuery as StartTypebot & Pick<Typebot, 'isArchived'>)
}
: typebotQuery
if (
!parsedTypebot ||
('isArchived' in parsedTypebot && parsedTypebot.isArchived)
)
if (!parsedTypebot || parsedTypebot.isArchived)
throw new TRPCError({
code: 'NOT_FOUND',
message: 'Typebot not found',
@ -299,7 +297,7 @@ const getTypebot = async (
message: 'Typebot is closed',
})
return parsedTypebot
return startTypebotSchema.parse(parsedTypebot)
}
const getResult = async ({

View File

@ -142,6 +142,7 @@ test('API chat execution should work on published bot', async ({ request }) => {
data: { message: '8', sessionId: chatSessionId },
})
).json()
console.log(messages, input)
expect(messages[0].content.richText).toStrictEqual([
{
children: [{ text: "I'm gonna shoot multiple inputs now..." }],

View File

@ -137,7 +137,7 @@ export const continueBotFlow =
formattedReply !== reply ? formattedReply : undefined,
}
const nextGroup = getNextGroup(newSessionState)(nextEdgeId)
const nextGroup = await getNextGroup(newSessionState)(nextEdgeId)
newSessionState = nextGroup.newSessionState

View File

@ -124,7 +124,9 @@ export const executeGroup =
if (!nextEdgeId && state.typebotsQueue.length === 1)
return { messages, newSessionState, clientSideActions, logs }
const nextGroup = getNextGroup(newSessionState)(nextEdgeId ?? undefined)
const nextGroup = await getNextGroup(newSessionState)(
nextEdgeId ?? undefined
)
newSessionState = nextGroup.newSessionState

View File

@ -1,5 +1,6 @@
import { byId } from '@typebot.io/lib'
import { Group, SessionState } from '@typebot.io/schemas'
import { upsertResult } from '../queries/upsertResult'
export type NextGroup = {
group?: Group
@ -8,12 +9,20 @@ export type NextGroup = {
export const getNextGroup =
(state: SessionState) =>
(edgeId?: string): NextGroup => {
async (edgeId?: string): Promise<NextGroup> => {
const nextEdge = state.typebotsQueue[0].typebot.edges.find(byId(edgeId))
if (!nextEdge) {
if (state.typebotsQueue.length > 1) {
const nextEdgeId = state.typebotsQueue[0].edgeIdToTriggerWhenDone
const isMergingWithParent = state.typebotsQueue[0].isMergingWithParent
const currentResultId = state.typebotsQueue[0].resultId
if (!isMergingWithParent && currentResultId)
await upsertResult({
resultId: currentResultId,
typebot: state.typebotsQueue[0].typebot,
isCompleted: true,
hasStarted: state.typebotsQueue[0].answers.length > 0,
})
const newSessionState = {
...state,
typebotsQueue: [
@ -48,7 +57,7 @@ export const getNextGroup =
...state.typebotsQueue.slice(2),
],
} satisfies SessionState
const nextGroup = getNextGroup(newSessionState)(nextEdgeId)
const nextGroup = await getNextGroup(newSessionState)(nextEdgeId)
if (!nextGroup)
return {
newSessionState,

View File

@ -21,7 +21,7 @@ export const startBotFlow = async (
const firstEdgeId =
state.typebotsQueue[0].typebot.groups[0].blocks[0].outgoingEdgeId
if (!firstEdgeId) return { messages: [], newSessionState: state }
const nextGroup = getNextGroup(state)(firstEdgeId)
const nextGroup = await getNextGroup(state)(firstEdgeId)
if (!nextGroup.group) return { messages: [], newSessionState: state }
return executeGroup(state)(nextGroup.group)
}

View File

@ -8,6 +8,7 @@ export const findPublicTypebot = ({ publicId }: Props) =>
prisma.publicTypebot.findFirst({
where: { typebot: { publicId } },
select: {
version: true,
groups: true,
edges: true,
settings: true,

View File

@ -9,6 +9,7 @@ export const findTypebot = ({ id, userId }: Props) =>
prisma.typebot.findFirst({
where: { id, workspace: { members: { some: { userId } } } },
select: {
version: true,
id: true,
groups: true,
edges: true,

View File

@ -10,6 +10,7 @@ import {
} from '@typebot.io/schemas'
export const leadGenerationTypebot: StartTypebot = {
version: null,
id: 'clckrl4q5000t3b6sabwokaar',
groups: [
{

View File

@ -20,6 +20,7 @@ import { inputBlockSchemas } from '../blocks/schemas'
import { chatCompletionMessageSchema } from '../blocks/integrations/openai'
import { sessionStateSchema } from './sessionState'
import { dynamicThemeSchema } from './shared'
import { preprocessTypebot } from '../typebot/helpers/preprocessTypebot'
const chatSessionSchema = z.object({
id: z.string(),
@ -84,14 +85,18 @@ const scriptToExecuteSchema = z.object({
),
})
const startTypebotSchema = typebotSchema._def.schema.pick({
id: true,
groups: true,
edges: true,
variables: true,
settings: true,
theme: true,
})
export const startTypebotSchema = z.preprocess(
preprocessTypebot,
typebotSchema._def.schema.pick({
version: true,
id: true,
groups: true,
edges: true,
variables: true,
settings: true,
theme: true,
})
)
const startParamsSchema = z.object({
typebot: startTypebotSchema

View File

@ -1,13 +1,16 @@
import { z } from 'zod'
import { publicTypebotSchema } from '../publicTypebot'
import { preprocessTypebot } from '../typebot/helpers/preprocessTypebot'
export const typebotInSessionStateSchema = publicTypebotSchema._def.schema.pick(
{
export const typebotInSessionStateSchema = z.preprocess(
preprocessTypebot,
publicTypebotSchema._def.schema.pick({
version: true,
id: true,
groups: true,
edges: true,
variables: true,
}
})
)
export type TypebotInSession = z.infer<typeof typebotInSessionStateSchema>

View File

@ -5,7 +5,6 @@ import {
variableSchema,
themeSchema,
settingsSchema,
typebotSchema,
} from './typebot'
import { z } from 'zod'
import { preprocessTypebot } from './typebot/helpers/preprocessTypebot'
@ -26,13 +25,4 @@ export const publicTypebotSchema = z.preprocess(
})
) satisfies z.ZodType<PrismaPublicTypebot, z.ZodTypeDef, unknown>
const publicTypebotWithName = publicTypebotSchema._def.schema.merge(
typebotSchema._def.schema.pick({
name: true,
isArchived: true,
isClosed: true,
})
)
export type PublicTypebot = z.infer<typeof publicTypebotSchema>
export type PublicTypebotWithName = z.infer<typeof publicTypebotWithName>

View File

@ -5,10 +5,10 @@ export const preprocessTypebot = (typebot: any) => {
if (!typebot || typebot.version === '5') return typebot
return {
...typebot,
groups: typebot.groups.map(preprocessGroup),
edges: typebot.edges?.filter(
(edge: any) => edgeSchema.safeParse(edge).success
),
groups: typebot.groups ? typebot.groups.map(preprocessGroup) : [],
edges: typebot.edges
? typebot.edges?.filter((edge: any) => edgeSchema.safeParse(edge).success)
: [],
}
}