🔥 Remove disable response saving option
Doesn't work properly when it comes to keep tracking storage usage
This commit is contained in:
@ -11,17 +11,18 @@ import Stripe from 'stripe'
|
||||
import { decrypt } from 'utils/api/encryption'
|
||||
|
||||
export const computePaymentInputRuntimeOptions =
|
||||
(state: Pick<SessionState, 'isPreview' | 'typebot'>) =>
|
||||
(state: Pick<SessionState, 'result' | 'typebot'>) =>
|
||||
(options: PaymentInputOptions) =>
|
||||
createStripePaymentIntent(state)(options)
|
||||
|
||||
const createStripePaymentIntent =
|
||||
(state: Pick<SessionState, 'isPreview' | 'typebot'>) =>
|
||||
(state: Pick<SessionState, 'result' | 'typebot'>) =>
|
||||
async (options: PaymentInputOptions): Promise<PaymentInputRuntimeOptions> => {
|
||||
const {
|
||||
isPreview,
|
||||
result,
|
||||
typebot: { variables },
|
||||
} = state
|
||||
const isPreview = !result.id
|
||||
if (!options.credentialsId)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
|
@ -50,10 +50,11 @@ if (window.$chatwoot) {
|
||||
}`
|
||||
|
||||
export const executeChatwootBlock = (
|
||||
{ typebot: { variables }, isPreview }: SessionState,
|
||||
{ typebot: { variables }, result }: SessionState,
|
||||
block: ChatwootBlock,
|
||||
lastBubbleBlockId?: string
|
||||
): ExecuteIntegrationResponse => {
|
||||
const isPreview = !result.id
|
||||
const chatwootCode = parseChatwootOpenCode(block.options)
|
||||
return {
|
||||
outgoingEdgeId: block.outgoingEdgeId,
|
||||
|
@ -6,7 +6,7 @@ import { render } from '@faire/mjml-react/utils/render'
|
||||
import { DefaultBotNotificationEmail } from 'emails'
|
||||
import {
|
||||
PublicTypebot,
|
||||
ResultValues,
|
||||
ResultInSession,
|
||||
SendEmailBlock,
|
||||
SendEmailOptions,
|
||||
SessionState,
|
||||
@ -20,11 +20,12 @@ import { decrypt } from 'utils/api'
|
||||
import { defaultFrom, defaultTransportOptions } from '../constants'
|
||||
|
||||
export const executeSendEmailBlock = async (
|
||||
{ result, typebot, isPreview }: SessionState,
|
||||
{ result, typebot }: SessionState,
|
||||
block: SendEmailBlock
|
||||
): Promise<ExecuteIntegrationResponse> => {
|
||||
const { options } = block
|
||||
const { variables } = typebot
|
||||
const isPreview = !result.id
|
||||
if (isPreview)
|
||||
return {
|
||||
outgoingEdgeId: block.outgoingEdgeId,
|
||||
@ -37,7 +38,7 @@ export const executeSendEmailBlock = async (
|
||||
}
|
||||
await sendEmail({
|
||||
typebotId: typebot.id,
|
||||
resultId: result?.id,
|
||||
result,
|
||||
credentialsId: options.credentialsId,
|
||||
recipients: options.recipients.map(parseVariables(variables)),
|
||||
subject: parseVariables(variables)(options.subject ?? ''),
|
||||
@ -57,7 +58,7 @@ export const executeSendEmailBlock = async (
|
||||
|
||||
const sendEmail = async ({
|
||||
typebotId,
|
||||
resultId,
|
||||
result,
|
||||
credentialsId,
|
||||
recipients,
|
||||
body,
|
||||
@ -70,7 +71,7 @@ const sendEmail = async ({
|
||||
fileUrls,
|
||||
}: SendEmailOptions & {
|
||||
typebotId: string
|
||||
resultId?: string
|
||||
result: ResultInSession
|
||||
fileUrls?: string | string[]
|
||||
}) => {
|
||||
const { name: replyToName } = parseEmailRecipient(replyTo)
|
||||
@ -94,12 +95,12 @@ const sendEmail = async ({
|
||||
isCustomBody,
|
||||
isBodyCode,
|
||||
typebotId,
|
||||
resultId,
|
||||
result,
|
||||
})
|
||||
|
||||
if (!emailBody) {
|
||||
await saveErrorLog({
|
||||
resultId,
|
||||
resultId: result.id,
|
||||
message: 'Email not sent',
|
||||
details: {
|
||||
error: 'No email body found',
|
||||
@ -132,7 +133,7 @@ const sendEmail = async ({
|
||||
try {
|
||||
await transporter.sendMail(email)
|
||||
await saveSuccessLog({
|
||||
resultId,
|
||||
resultId: result.id,
|
||||
message: 'Email successfully sent',
|
||||
details: {
|
||||
transportConfig: {
|
||||
@ -144,7 +145,7 @@ const sendEmail = async ({
|
||||
})
|
||||
} catch (err) {
|
||||
await saveErrorLog({
|
||||
resultId,
|
||||
resultId: result.id,
|
||||
message: 'Email not sent',
|
||||
details: {
|
||||
error: err,
|
||||
@ -182,10 +183,10 @@ const getEmailBody = async ({
|
||||
isCustomBody,
|
||||
isBodyCode,
|
||||
typebotId,
|
||||
resultId,
|
||||
result,
|
||||
}: {
|
||||
typebotId: string
|
||||
resultId?: string
|
||||
result: ResultInSession
|
||||
} & Pick<SendEmailOptions, 'isCustomBody' | 'isBodyCode' | 'body'>): Promise<
|
||||
{ html?: string; text?: string } | undefined
|
||||
> => {
|
||||
@ -198,12 +199,7 @@ const getEmailBody = async ({
|
||||
where: { typebotId },
|
||||
})) as unknown as PublicTypebot
|
||||
if (!typebot) return
|
||||
const resultValues = (await prisma.result.findUnique({
|
||||
where: { id: resultId },
|
||||
include: { answers: true },
|
||||
})) as ResultValues | null
|
||||
if (!resultValues) return
|
||||
const answers = parseAnswers(typebot, [])(resultValues)
|
||||
const answers = parseAnswers(typebot, [])(result)
|
||||
return {
|
||||
html: render(
|
||||
<DefaultBotNotificationEmail
|
||||
|
@ -16,16 +16,15 @@ import {
|
||||
WebhookOptions,
|
||||
defaultWebhookAttributes,
|
||||
HttpMethod,
|
||||
ResultValues,
|
||||
PublicTypebot,
|
||||
KeyValue,
|
||||
ReplyLog,
|
||||
ResultInSession,
|
||||
} from 'models'
|
||||
import { stringify } from 'qs'
|
||||
import { byId, omit } from 'utils'
|
||||
import { parseAnswers } from 'utils/results'
|
||||
import got, { Method, Headers, HTTPError } from 'got'
|
||||
import { getResultValues } from '@/features/results/api'
|
||||
import { parseSampleResult } from './parseSampleResult'
|
||||
|
||||
export const executeWebhookBlock = async (
|
||||
@ -50,14 +49,11 @@ export const executeWebhookBlock = async (
|
||||
return { outgoingEdgeId: block.outgoingEdgeId, logs: [log] }
|
||||
}
|
||||
const preparedWebhook = prepareWebhookAttributes(webhook, block.options)
|
||||
const resultValues =
|
||||
(result && (await getResultValues(result.id))) ?? undefined
|
||||
const webhookResponse = await executeWebhook({ typebot })(
|
||||
preparedWebhook,
|
||||
typebot.variables,
|
||||
block.groupId,
|
||||
resultValues,
|
||||
result?.id
|
||||
result
|
||||
)
|
||||
const status = webhookResponse.statusCode.toString()
|
||||
const isError = status.startsWith('4') || status.startsWith('5')
|
||||
@ -139,8 +135,7 @@ export const executeWebhook =
|
||||
webhook: Webhook,
|
||||
variables: Variable[],
|
||||
groupId: string,
|
||||
resultValues?: ResultValues,
|
||||
resultId?: string
|
||||
result: ResultInSession
|
||||
): Promise<WebhookResponse> => {
|
||||
if (!webhook.url || !webhook.method)
|
||||
return {
|
||||
@ -176,7 +171,7 @@ export const executeWebhook =
|
||||
[]
|
||||
)({
|
||||
body: webhook.body,
|
||||
resultValues,
|
||||
result,
|
||||
groupId,
|
||||
variables,
|
||||
})
|
||||
@ -206,7 +201,7 @@ export const executeWebhook =
|
||||
try {
|
||||
const response = await got(request.url, omit(request, 'url'))
|
||||
await saveSuccessLog({
|
||||
resultId,
|
||||
resultId: result.id,
|
||||
message: 'Webhook successfuly executed.',
|
||||
details: {
|
||||
statusCode: response.statusCode,
|
||||
@ -225,7 +220,7 @@ export const executeWebhook =
|
||||
data: safeJsonParse(error.response.body as string).data,
|
||||
}
|
||||
await saveErrorLog({
|
||||
resultId,
|
||||
resultId: result.id,
|
||||
message: 'Webhook returned an error',
|
||||
details: {
|
||||
request,
|
||||
@ -240,7 +235,7 @@ export const executeWebhook =
|
||||
}
|
||||
console.error(error)
|
||||
await saveErrorLog({
|
||||
resultId,
|
||||
resultId: result.id,
|
||||
message: 'Webhook failed to execute',
|
||||
details: {
|
||||
request,
|
||||
@ -258,20 +253,20 @@ const getBodyContent =
|
||||
) =>
|
||||
async ({
|
||||
body,
|
||||
resultValues,
|
||||
result,
|
||||
groupId,
|
||||
variables,
|
||||
}: {
|
||||
body?: string | null
|
||||
resultValues?: ResultValues
|
||||
result?: ResultInSession
|
||||
groupId: string
|
||||
variables: Variable[]
|
||||
}): Promise<string | undefined> => {
|
||||
if (!body) return
|
||||
return body === '{{state}}'
|
||||
? JSON.stringify(
|
||||
resultValues
|
||||
? parseAnswers(typebot, linkedTypebots)(resultValues)
|
||||
result
|
||||
? parseAnswers(typebot, linkedTypebots)(result)
|
||||
: await parseSampleResult(typebot, linkedTypebots)(
|
||||
groupId,
|
||||
variables
|
||||
|
@ -29,7 +29,7 @@ export const parseSampleResult =
|
||||
|
||||
return {
|
||||
message: 'This is a sample result, it has been generated ⬇️',
|
||||
'Submitted at': new Date().toISOString(),
|
||||
submittedAt: new Date().toISOString(),
|
||||
...parseResultSample(linkedInputBlocks, header, variables),
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +115,8 @@ const getLinkedTypebot = async (
|
||||
state: SessionState,
|
||||
typebotId: string
|
||||
): Promise<TypebotInSession | null> => {
|
||||
const { typebot, isPreview } = state
|
||||
const { typebot, result } = state
|
||||
const isPreview = !result.id
|
||||
if (typebotId === 'current') return typebot
|
||||
const availableTypebots =
|
||||
'linkedTypebots' in state
|
||||
@ -123,12 +124,12 @@ const getLinkedTypebot = async (
|
||||
: [typebot]
|
||||
const linkedTypebot =
|
||||
availableTypebots.find(byId(typebotId)) ??
|
||||
(await fetchTypebot({ isPreview }, typebotId))
|
||||
(await fetchTypebot(isPreview, typebotId))
|
||||
return linkedTypebot
|
||||
}
|
||||
|
||||
const fetchTypebot = async (
|
||||
{ isPreview }: Pick<SessionState, 'isPreview'>,
|
||||
isPreview: boolean,
|
||||
typebotId: string
|
||||
): Promise<TypebotInSession | null> => {
|
||||
if (isPreview) {
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
ChatReply,
|
||||
chatReplySchema,
|
||||
ChatSession,
|
||||
Result,
|
||||
ResultInSession,
|
||||
sendMessageInputSchema,
|
||||
SessionState,
|
||||
StartParams,
|
||||
@ -85,7 +85,11 @@ export const sendMessageProcedure = publicProcedure
|
||||
},
|
||||
})
|
||||
|
||||
if (!input && session.state.result?.hasStarted)
|
||||
if (
|
||||
!input &&
|
||||
session.state.result.answers.length > 0 &&
|
||||
session.state.result.id
|
||||
)
|
||||
await setResultAsCompleted(session.state.result.id)
|
||||
|
||||
return {
|
||||
@ -106,9 +110,6 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
|
||||
message: 'No typebot provided in startParams',
|
||||
})
|
||||
|
||||
const isPreview =
|
||||
startParams?.isPreview || typeof startParams?.typebot !== 'string'
|
||||
|
||||
const typebot = await getTypebot(startParams, userId)
|
||||
|
||||
const prefilledVariables = startParams.prefilledVariables
|
||||
@ -117,8 +118,8 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
|
||||
|
||||
const result = await getResult({
|
||||
...startParams,
|
||||
isPreview,
|
||||
typebot: typebot.id,
|
||||
isPreview: startParams.isPreview || typeof startParams.typebot !== 'string',
|
||||
typebotId: typebot.id,
|
||||
prefilledVariables,
|
||||
isNewResultOnRefreshEnabled:
|
||||
typebot.settings.general.isNewResultOnRefreshEnabled ?? false,
|
||||
@ -140,10 +141,11 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
|
||||
typebots: [],
|
||||
queue: [],
|
||||
},
|
||||
result: result
|
||||
? { id: result.id, variables: result.variables, hasStarted: false }
|
||||
: undefined,
|
||||
isPreview,
|
||||
result: {
|
||||
id: result?.id,
|
||||
variables: result?.variables ?? [],
|
||||
answers: result?.answers ?? [],
|
||||
},
|
||||
currentTypebotId: typebot.id,
|
||||
dynamicTheme: parseDynamicThemeInState(typebot.theme),
|
||||
}
|
||||
@ -297,20 +299,21 @@ const getTypebot = async (
|
||||
}
|
||||
|
||||
const getResult = async ({
|
||||
typebot,
|
||||
typebotId,
|
||||
isPreview,
|
||||
resultId,
|
||||
prefilledVariables,
|
||||
isNewResultOnRefreshEnabled,
|
||||
}: Pick<StartParams, 'isPreview' | 'resultId' | 'typebot'> & {
|
||||
}: Pick<StartParams, 'isPreview' | 'resultId'> & {
|
||||
typebotId: string
|
||||
prefilledVariables: Variable[]
|
||||
isNewResultOnRefreshEnabled: boolean
|
||||
}) => {
|
||||
if (isPreview || typeof typebot !== 'string') return
|
||||
if (isPreview) return
|
||||
const select = {
|
||||
id: true,
|
||||
variables: true,
|
||||
hasStarted: true,
|
||||
answers: { select: { blockId: true, variableId: true, content: true } },
|
||||
} satisfies Prisma.ResultSelect
|
||||
|
||||
const existingResult =
|
||||
@ -318,7 +321,7 @@ const getResult = async ({
|
||||
? ((await prisma.result.findFirst({
|
||||
where: { id: resultId },
|
||||
select,
|
||||
})) as Pick<Result, 'id' | 'variables' | 'hasStarted'>)
|
||||
})) as ResultInSession)
|
||||
: undefined
|
||||
|
||||
if (existingResult) {
|
||||
@ -344,19 +347,19 @@ const getResult = async ({
|
||||
return {
|
||||
id: existingResult.id,
|
||||
variables: updatedResult.variables,
|
||||
hasStarted: existingResult.hasStarted,
|
||||
answers: existingResult.answers,
|
||||
}
|
||||
} else {
|
||||
return (await prisma.result.create({
|
||||
data: {
|
||||
isCompleted: false,
|
||||
typebotId: typebot,
|
||||
typebotId,
|
||||
variables: prefilledVariables.filter((variable) =>
|
||||
isDefined(variable.value)
|
||||
),
|
||||
},
|
||||
select,
|
||||
})) as Pick<Result, 'id' | 'variables' | 'hasStarted'>
|
||||
})) as ResultInSession
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import { validateUrl } from '@/features/blocks/inputs/url/api'
|
||||
import { parseVariables, updateVariables } from '@/features/variables'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { Prisma } from 'db'
|
||||
import got from 'got'
|
||||
import {
|
||||
Block,
|
||||
@ -15,6 +16,7 @@ import {
|
||||
ChatReply,
|
||||
InputBlock,
|
||||
InputBlockType,
|
||||
ResultInSession,
|
||||
SessionState,
|
||||
} from 'models'
|
||||
import { isInputBlock, isNotDefined } from 'utils'
|
||||
@ -86,17 +88,9 @@ const processAndSaveAnswer =
|
||||
(state: SessionState, block: InputBlock) =>
|
||||
async (reply: string | null): Promise<SessionState> => {
|
||||
if (!reply) return state
|
||||
if (!state.isPreview && state.result) {
|
||||
await saveAnswer(state.result.id, block)(reply)
|
||||
if (!state.result.hasStarted) await setResultAsStarted(state.result.id)
|
||||
}
|
||||
const newState = await saveVariableValueIfAny(state, block)(reply)
|
||||
return {
|
||||
...newState,
|
||||
result: newState.result
|
||||
? { ...newState.result, hasStarted: true }
|
||||
: undefined,
|
||||
}
|
||||
let newState = await saveAnswer(state, block)(reply)
|
||||
newState = await saveVariableValueIfAny(newState, block)(reply)
|
||||
return newState
|
||||
}
|
||||
|
||||
const saveVariableValueIfAny =
|
||||
@ -115,13 +109,6 @@ const saveVariableValueIfAny =
|
||||
return newSessionState
|
||||
}
|
||||
|
||||
const setResultAsStarted = async (resultId: string) => {
|
||||
await prisma.result.update({
|
||||
where: { id: resultId },
|
||||
data: { hasStarted: true },
|
||||
})
|
||||
}
|
||||
|
||||
export const setResultAsCompleted = async (resultId: string) => {
|
||||
await prisma.result.update({
|
||||
where: { id: resultId },
|
||||
@ -152,31 +139,65 @@ const parseRetryMessage = (
|
||||
}
|
||||
|
||||
const saveAnswer =
|
||||
(resultId: string, block: InputBlock) => async (reply: string) => {
|
||||
(state: SessionState, block: InputBlock) =>
|
||||
async (reply: string): Promise<SessionState> => {
|
||||
const resultId = state.result?.id
|
||||
const answer = {
|
||||
resultId: resultId,
|
||||
resultId,
|
||||
blockId: block.id,
|
||||
groupId: block.groupId,
|
||||
content: reply,
|
||||
variableId: block.options.variableId,
|
||||
storageUsed: 0,
|
||||
}
|
||||
if (state.result.answers.length === 0 && state.result.id)
|
||||
await setResultAsStarted(state.result.id)
|
||||
|
||||
const newSessionState = setNewAnswerInState(state)({
|
||||
blockId: block.id,
|
||||
variableId: block.options.variableId ?? null,
|
||||
content: reply,
|
||||
})
|
||||
|
||||
if (reply.includes('http') && block.type === InputBlockType.FILE) {
|
||||
answer.storageUsed = await computeStorageUsed(reply)
|
||||
}
|
||||
|
||||
await prisma.answer.upsert({
|
||||
where: {
|
||||
resultId_blockId_groupId: {
|
||||
resultId,
|
||||
groupId: block.groupId,
|
||||
blockId: block.id,
|
||||
if (resultId)
|
||||
await prisma.answer.upsert({
|
||||
where: {
|
||||
resultId_blockId_groupId: {
|
||||
resultId,
|
||||
groupId: block.groupId,
|
||||
blockId: block.id,
|
||||
},
|
||||
},
|
||||
create: answer as Prisma.AnswerUncheckedCreateInput,
|
||||
update: answer,
|
||||
})
|
||||
return newSessionState
|
||||
}
|
||||
|
||||
const setResultAsStarted = async (resultId: string) => {
|
||||
await prisma.result.update({
|
||||
where: { id: resultId },
|
||||
data: { hasStarted: true },
|
||||
})
|
||||
}
|
||||
|
||||
const setNewAnswerInState =
|
||||
(state: SessionState) => (newAnswer: ResultInSession['answers'][number]) => {
|
||||
const newAnswers = state.result.answers
|
||||
.filter((answer) => answer.blockId !== newAnswer.blockId)
|
||||
.concat(newAnswer)
|
||||
|
||||
return {
|
||||
...state,
|
||||
result: {
|
||||
...state.result,
|
||||
answers: newAnswers,
|
||||
},
|
||||
create: answer,
|
||||
update: answer,
|
||||
})
|
||||
} satisfies SessionState
|
||||
}
|
||||
|
||||
const computeStorageUsed = async (reply: string) => {
|
||||
|
@ -113,7 +113,7 @@ export const executeGroup =
|
||||
}
|
||||
|
||||
const computeRuntimeOptions =
|
||||
(state: Pick<SessionState, 'isPreview' | 'typebot'>) =>
|
||||
(state: Pick<SessionState, 'result' | 'typebot'>) =>
|
||||
(block: InputBlock): Promise<RuntimeOptions> | undefined => {
|
||||
switch (block.type) {
|
||||
case InputBlockType.PAYMENT: {
|
||||
@ -158,7 +158,7 @@ const parseBubbleBlock =
|
||||
}
|
||||
|
||||
const injectVariablesValueInBlock =
|
||||
(state: Pick<SessionState, 'isPreview' | 'typebot'>) =>
|
||||
(state: Pick<SessionState, 'result' | 'typebot'>) =>
|
||||
async (block: InputBlock): Promise<ChatReply['input']> => {
|
||||
switch (block.type) {
|
||||
case InputBlockType.CHOICE: {
|
||||
|
@ -1 +0,0 @@
|
||||
export * from './utils'
|
@ -1,8 +0,0 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { ResultValues } from 'models'
|
||||
|
||||
export const getResultValues = async (resultId: string) =>
|
||||
(await prisma.result.findUnique({
|
||||
where: { id: resultId },
|
||||
include: { answers: true },
|
||||
})) as ResultValues | null
|
@ -1 +0,0 @@
|
||||
export * from './getResultValues'
|
@ -157,12 +157,10 @@ export const updateVariables =
|
||||
...state.typebot,
|
||||
variables: updateTypebotVariables(state)(newVariables),
|
||||
},
|
||||
result: state.result
|
||||
? {
|
||||
...state.result,
|
||||
variables: await updateResultVariables(state)(newVariables),
|
||||
}
|
||||
: undefined,
|
||||
result: {
|
||||
...state.result,
|
||||
variables: await updateResultVariables(state)(newVariables),
|
||||
},
|
||||
})
|
||||
|
||||
const updateResultVariables =
|
||||
@ -170,7 +168,6 @@ const updateResultVariables =
|
||||
async (
|
||||
newVariables: VariableWithUnknowValue[]
|
||||
): Promise<VariableWithValue[]> => {
|
||||
if (!result) return []
|
||||
const serializedNewVariables = newVariables.map((variable) => ({
|
||||
...variable,
|
||||
value: Array.isArray(variable.value)
|
||||
@ -187,14 +184,15 @@ const updateResultVariables =
|
||||
...serializedNewVariables,
|
||||
].filter((variable) => isDefined(variable.value)) as VariableWithValue[]
|
||||
|
||||
await prisma.result.update({
|
||||
where: {
|
||||
id: result.id,
|
||||
},
|
||||
data: {
|
||||
variables: updatedVariables,
|
||||
},
|
||||
})
|
||||
if (result.id)
|
||||
await prisma.result.update({
|
||||
where: {
|
||||
id: result.id,
|
||||
},
|
||||
data: {
|
||||
variables: updatedVariables,
|
||||
},
|
||||
})
|
||||
|
||||
return updatedVariables
|
||||
}
|
||||
|
Reference in New Issue
Block a user