♻️ (results) Introduce tRPC and use it for the results
This commit is contained in:
@@ -3,53 +3,51 @@ import { canWriteTypebot } from '@/utils/api/dbRules'
|
||||
import { deleteFiles } from '@/utils/api/storage'
|
||||
import { User, Prisma } from 'db'
|
||||
import { InputBlockType, Typebot } from 'models'
|
||||
import { NextApiResponse } from 'next'
|
||||
import { forbidden } from 'utils/api'
|
||||
|
||||
export const archiveResults =
|
||||
(res: NextApiResponse) =>
|
||||
async ({
|
||||
typebotId,
|
||||
user,
|
||||
resultsFilter,
|
||||
}: {
|
||||
typebotId: string
|
||||
user: User
|
||||
resultsFilter?: Prisma.ResultWhereInput
|
||||
}) => {
|
||||
const typebot = await prisma.typebot.findFirst({
|
||||
where: canWriteTypebot(typebotId, user),
|
||||
select: { groups: true },
|
||||
export const archiveResults = async ({
|
||||
typebotId,
|
||||
user,
|
||||
resultsFilter,
|
||||
}: {
|
||||
typebotId: string
|
||||
user: User
|
||||
resultsFilter?: Prisma.ResultWhereInput
|
||||
}) => {
|
||||
const typebot = await prisma.typebot.findFirst({
|
||||
where: canWriteTypebot(typebotId, user),
|
||||
select: { groups: true },
|
||||
})
|
||||
if (!typebot) return { success: false }
|
||||
const fileUploadBlockIds = (typebot as Typebot).groups
|
||||
.flatMap((g) => g.blocks)
|
||||
.filter((b) => b.type === InputBlockType.FILE)
|
||||
.map((b) => b.id)
|
||||
if (fileUploadBlockIds.length > 0) {
|
||||
const filesToDelete = await prisma.answer.findMany({
|
||||
where: { result: resultsFilter, blockId: { in: fileUploadBlockIds } },
|
||||
})
|
||||
if (!typebot) return forbidden(res)
|
||||
const fileUploadBlockIds = (typebot as Typebot).groups
|
||||
.flatMap((g) => g.blocks)
|
||||
.filter((b) => b.type === InputBlockType.FILE)
|
||||
.map((b) => b.id)
|
||||
if (fileUploadBlockIds.length > 0) {
|
||||
const filesToDelete = await prisma.answer.findMany({
|
||||
where: { result: resultsFilter, blockId: { in: fileUploadBlockIds } },
|
||||
if (filesToDelete.length > 0)
|
||||
await deleteFiles({
|
||||
urls: filesToDelete.flatMap((a) => a.content.split(', ')),
|
||||
})
|
||||
if (filesToDelete.length > 0)
|
||||
await deleteFiles({
|
||||
urls: filesToDelete.flatMap((a) => a.content.split(', ')),
|
||||
})
|
||||
}
|
||||
await prisma.log.deleteMany({
|
||||
where: {
|
||||
result: resultsFilter,
|
||||
},
|
||||
})
|
||||
await prisma.answer.deleteMany({
|
||||
where: {
|
||||
result: resultsFilter,
|
||||
},
|
||||
})
|
||||
await prisma.result.updateMany({
|
||||
where: resultsFilter,
|
||||
data: {
|
||||
isArchived: true,
|
||||
variables: [],
|
||||
},
|
||||
})
|
||||
}
|
||||
await prisma.log.deleteMany({
|
||||
where: {
|
||||
result: resultsFilter,
|
||||
},
|
||||
})
|
||||
await prisma.answer.deleteMany({
|
||||
where: {
|
||||
result: resultsFilter,
|
||||
},
|
||||
})
|
||||
await prisma.result.updateMany({
|
||||
where: resultsFilter,
|
||||
data: {
|
||||
isArchived: true,
|
||||
variables: [],
|
||||
},
|
||||
})
|
||||
|
||||
return { success: true }
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './archiveResults'
|
||||
export * from './router'
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { canWriteTypebot } from '@/utils/api/dbRules'
|
||||
import { authenticatedProcedure } from '@/utils/server/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { z } from 'zod'
|
||||
import { archiveResults } from '../archiveResults'
|
||||
|
||||
export const deleteResultsProcedure = authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'DELETE',
|
||||
path: '/typebots/{typebotId}/results',
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
typebotId: z.string(),
|
||||
ids: z.string().optional(),
|
||||
})
|
||||
)
|
||||
.output(z.void())
|
||||
.mutation(async ({ input, ctx: { user } }) => {
|
||||
const idsArray = input.ids?.split(',')
|
||||
const { typebotId } = input
|
||||
const { success } = await archiveResults({
|
||||
typebotId,
|
||||
user,
|
||||
resultsFilter: {
|
||||
id: (idsArray?.length ?? 0) > 0 ? { in: idsArray } : undefined,
|
||||
typebot: canWriteTypebot(typebotId, user),
|
||||
},
|
||||
})
|
||||
|
||||
if (!success)
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Typebot not found',
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { canReadTypebot } from '@/utils/api/dbRules'
|
||||
import { authenticatedProcedure } from '@/utils/server/trpc'
|
||||
import { logSchema } from 'models'
|
||||
import { z } from 'zod'
|
||||
|
||||
export const getResultLogsProcedure = authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'GET',
|
||||
path: '/typebots/{typebotId}/results/{resultId}/logs',
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
typebotId: z.string(),
|
||||
resultId: z.string(),
|
||||
})
|
||||
)
|
||||
.output(z.object({ logs: z.array(logSchema) }))
|
||||
.query(async ({ input: { typebotId, resultId }, ctx: { user } }) => {
|
||||
const logs = await prisma.log.findMany({
|
||||
where: {
|
||||
result: { id: resultId, typebot: canReadTypebot(typebotId, user) },
|
||||
},
|
||||
})
|
||||
|
||||
return { logs }
|
||||
})
|
||||
@@ -0,0 +1,59 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { canReadTypebot } from '@/utils/api/dbRules'
|
||||
import { authenticatedProcedure } from '@/utils/server/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { ResultWithAnswers, resultWithAnswersSchema } from 'models'
|
||||
import { z } from 'zod'
|
||||
|
||||
const maxLimit = 200
|
||||
|
||||
export const getResultsProcedure = authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'GET',
|
||||
path: '/typebots/{typebotId}/results',
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
typebotId: z.string(),
|
||||
limit: z.string().regex(/^[0-9]{1,3}$/),
|
||||
cursor: z.string().optional(),
|
||||
})
|
||||
)
|
||||
.output(
|
||||
z.object({
|
||||
results: z.array(resultWithAnswersSchema),
|
||||
nextCursor: z.string().nullish(),
|
||||
})
|
||||
)
|
||||
.query(async ({ input, ctx: { user } }) => {
|
||||
const limit = Number(input.limit)
|
||||
if (limit < 1 || limit > maxLimit)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'limit must be between 1 and 200',
|
||||
})
|
||||
const { cursor } = input
|
||||
const results = (await prisma.result.findMany({
|
||||
take: limit + 1,
|
||||
cursor: cursor ? { id: cursor } : undefined,
|
||||
where: {
|
||||
typebot: canReadTypebot(input.typebotId, user),
|
||||
answers: { some: {} },
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
include: { answers: true },
|
||||
})) as ResultWithAnswers[]
|
||||
|
||||
let nextCursor: typeof cursor | undefined
|
||||
if (results.length > limit) {
|
||||
const nextResult = results.pop()
|
||||
nextCursor = nextResult?.id
|
||||
}
|
||||
|
||||
return { results, nextCursor }
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './deleteResultsProcedure'
|
||||
export * from './getResultLogsProcedure'
|
||||
export * from './getResultsProcedure'
|
||||
12
apps/builder/src/features/results/api/router.ts
Normal file
12
apps/builder/src/features/results/api/router.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { router } from '@/utils/server/trpc'
|
||||
import {
|
||||
deleteResultsProcedure,
|
||||
getResultLogsProcedure,
|
||||
getResultsProcedure,
|
||||
} from './procedures'
|
||||
|
||||
export const resultsRouter = router({
|
||||
getResults: getResultsProcedure,
|
||||
deleteResults: deleteResultsProcedure,
|
||||
getResultLogs: getResultLogsProcedure,
|
||||
})
|
||||
Reference in New Issue
Block a user