2
0

feat(api): Add list results endpoint

This commit is contained in:
Baptiste Arnaud
2022-02-21 15:51:40 +01:00
parent e3704f6dd9
commit 9dfcb30365
7 changed files with 140 additions and 5 deletions

View File

@@ -53,6 +53,7 @@ type HeaderCell = {
Header: JSX.Element
accessor: string
}
export const parseSubmissionsColumns = (
typebot: PublicTypebot
): HeaderCell[] => {

View File

@@ -1,9 +1,9 @@
import { Result, VariableWithValue } from 'models'
import { ResultWithAnswers, Typebot, VariableWithValue } from 'models'
import useSWRInfinite from 'swr/infinite'
import { fetcher } from './utils'
import { stringify } from 'qs'
import { Answer } from 'db'
import { isDefined, sendRequest } from 'utils'
import { byId, isDefined, sendRequest } from 'utils'
const paginationLimit = 50
@@ -21,7 +21,6 @@ const getKey = (
}&limit=${paginationLimit}`
}
export type ResultWithAnswers = Result & { answers: Answer[] }
export const useResults = ({
typebotId,
onError,
@@ -113,3 +112,28 @@ export const convertResultsToTableData = (results?: ResultWithAnswers[]) =>
return { ...o, [variable.id]: variable.value }
}, {}),
}))
export const parseAnswers = (
result: ResultWithAnswers,
{ blocks, variables }: Pick<Typebot, 'blocks' | 'variables'>
) => ({
submittedAt: result.createdAt,
...[...result.answers, ...result.prefilledVariables].reduce<{
[key: string]: string
}>((o, answerOrVariable) => {
if ('blockId' in answerOrVariable) {
const answer = answerOrVariable as Answer
const key = answer.variableId
? variables.find(byId(answer.variableId))?.name
: blocks.find(byId(answer.blockId))?.title
if (!key) return o
return {
...o,
[key]: answer.content,
}
}
const variable = answerOrVariable as VariableWithValue
if (isDefined(o[variable.id])) return o
return { ...o, [variable.id]: variable.value }
}, {}),
})

View File

@@ -0,0 +1,28 @@
import prisma from 'libs/prisma'
import { ResultWithAnswers, Typebot } from 'models'
import { NextApiRequest, NextApiResponse } from 'next'
import { authenticateUser } from 'services/api/utils'
import { methodNotAllowed, parseAnswers } from 'utils'
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === 'GET') {
const user = await authenticateUser(req)
if (!user) return res.status(401).json({ message: 'Not authenticated' })
const typebotId = req.query.typebotId.toString()
const typebot = await prisma.typebot.findUnique({
where: { id_ownerId: { id: typebotId, ownerId: user.id } },
})
if (!typebot) return res.status(400).send({ message: 'Typebot not found' })
const limit = Number(req.query.limit)
const results = (await prisma.result.findMany({
where: { typebotId: typebot.id },
orderBy: { createdAt: 'desc' },
take: limit,
include: { answers: true },
})) as unknown as ResultWithAnswers[]
res.send({ results: results.map(parseAnswers(typebot as Typebot)) })
}
methodNotAllowed(res)
}
export default handler

View File

@@ -145,3 +145,37 @@ export const importTypebotInDatabase = async (
),
})
}
export const createResults = async ({ typebotId }: { typebotId: string }) => {
await prisma.result.deleteMany()
await prisma.result.createMany({
data: [
...Array.from(Array(200)).map((_, idx) => {
const today = new Date()
const rand = Math.random()
return {
id: `result${idx}`,
typebotId,
createdAt: new Date(
today.setTime(today.getTime() + 1000 * 60 * 60 * 24 * idx)
),
isCompleted: rand > 0.5,
}
}),
],
})
return createAnswers()
}
const createAnswers = () => {
return prisma.answer.createMany({
data: [
...Array.from(Array(200)).map((_, idx) => ({
resultId: `result${idx}`,
content: `content${idx}`,
stepId: 'step1',
blockId: 'block1',
})),
],
})
}

View File

@@ -1,5 +1,9 @@
import test, { expect } from '@playwright/test'
import { createTypebots, parseDefaultBlockWithStep } from '../services/database'
import {
createResults,
createTypebots,
parseDefaultBlockWithStep,
} from '../services/database'
import {
IntegrationStepType,
defaultWebhookOptions,
@@ -19,6 +23,7 @@ test.beforeAll(async () => {
}),
},
])
await createResults({ typebotId })
} catch (err) {}
})
@@ -98,3 +103,18 @@ test('can unsubscribe webhook', async ({ request }) => {
message: 'success',
})
})
test('can list results', async ({ request }) => {
expect(
(await request.get(`/api/typebots/${typebotId}/results`)).status()
).toBe(401)
const response = await request.get(
`/api/typebots/${typebotId}/results?limit=10`,
{
headers: { Authorization: 'Bearer userToken' },
}
)
const { results } = await response.json()
expect(results).toHaveLength(10)
expect(results[0]).toMatchObject({ 'Block #1': 'content199' })
})

View File

@@ -1,7 +1,9 @@
import { Result as ResultFromPrisma } from 'db'
import { VariableWithValue } from '.'
import { Answer, VariableWithValue } from '.'
export type Result = Omit<
ResultFromPrisma,
'createdAt' | 'prefilledVariables'
> & { createdAt: string; prefilledVariables: VariableWithValue[] }
export type ResultWithAnswers = Result & { answers: Answer[] }

View File

@@ -1,4 +1,6 @@
import { Typebot, Answer, VariableWithValue, ResultWithAnswers } from 'models'
import { NextApiRequest, NextApiResponse } from 'next'
import { byId, isDefined } from '.'
export const methodNotAllowed = (res: NextApiResponse) =>
res.status(405).json({ message: 'Method Not Allowed' })
@@ -20,3 +22,27 @@ export const initMiddleware =
return resolve(result)
})
})
export const parseAnswers =
({ blocks, variables }: Pick<Typebot, 'blocks' | 'variables'>) =>
(result: ResultWithAnswers) => ({
submittedAt: result.createdAt,
...[...result.answers, ...result.prefilledVariables].reduce<{
[key: string]: string
}>((o, answerOrVariable) => {
if ('blockId' in answerOrVariable) {
const answer = answerOrVariable as Answer
const key = answer.variableId
? variables.find(byId(answer.variableId))?.name
: blocks.find(byId(answer.blockId))?.title
if (!key) return o
return {
...o,
[key]: answer.content,
}
}
const variable = answerOrVariable as VariableWithValue
if (isDefined(o[variable.id])) return o
return { ...o, [variable.id]: variable.value }
}, {}),
})