From 7b3975c4ae76017e5716cfd8f93defb9311b786c Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Sat, 2 Aug 2025 20:15:48 -0600 Subject: [PATCH] update schemas --- apps/server/src/api/v2/reports/GET.ts | 60 +++++++++---------- apps/server/src/api/v2/reports/[id].ts | 38 ++++++------ .../src/reports/reports.types.ts | 26 ++++++-- .../server-shared/src/reports/requests.ts | 22 +++---- .../server-shared/src/reports/responses.ts | 10 ++-- .../src/type-utilities/pagination.ts | 5 ++ 6 files changed, 89 insertions(+), 72 deletions(-) diff --git a/apps/server/src/api/v2/reports/GET.ts b/apps/server/src/api/v2/reports/GET.ts index 207774191..b7df62a32 100644 --- a/apps/server/src/api/v2/reports/GET.ts +++ b/apps/server/src/api/v2/reports/GET.ts @@ -1,8 +1,5 @@ import type { User } from '@buster/database'; -import type { - GetReportsListRequest, - GetReportsListResponse -} from '@buster/server-shared/reports'; +import type { GetReportsListRequest, GetReportsListResponse } from '@buster/server-shared/reports'; import { GetReportsListRequestSchema } from '@buster/server-shared/reports'; import { zValidator } from '@hono/zod-validator'; import { Hono } from 'hono'; @@ -25,16 +22,16 @@ async function getReportsListHandler( content: [ { type: 'h1' as const, - children: [{ text: 'Sales Analysis Q4' }] + children: [{ text: 'Sales Analysis Q4' }], }, { type: 'p' as const, - children: [{ text: 'This report analyzes our Q4 sales performance.' }] - } - ] + children: [{ text: 'This report analyzes our Q4 sales performance.' }], + }, + ], }, { - id: 'report-2', + id: 'report-2', name: 'Customer Metrics Dashboard', file_name: 'customer_metrics.md', description: 'Key customer engagement metrics', @@ -46,9 +43,9 @@ async function getReportsListHandler( content: [ { type: 'h1' as const, - children: [{ text: 'Customer Metrics' }] - } - ] + children: [{ text: 'Customer Metrics' }], + }, + ], }, { id: 'report-3', @@ -63,39 +60,40 @@ async function getReportsListHandler( content: [ { type: 'h1' as const, - children: [{ text: 'Marketing Campaign Results' }] + children: [{ text: 'Marketing Campaign Results' }], }, { type: 'p' as const, - children: [{ text: 'Overview of our recent marketing initiatives and their performance.' }] - } - ] - } + children: [ + { text: 'Overview of our recent marketing initiatives and their performance.' }, + ], + }, + ], + }, ]; - const { page_token, page_size } = request; - const startIndex = page_token * page_size; + const { page, page_size } = request; + const startIndex = page * page_size; const endIndex = startIndex + page_size; const paginatedReports = stubbedReports.slice(startIndex, endIndex); return { data: paginatedReports, pagination: { - page: page_token, - page_size: page_size, + page, + page_size, total: stubbedReports.length, - total_pages: Math.ceil(stubbedReports.length / page_size) - } + total_pages: Math.ceil(stubbedReports.length / page_size), + }, }; } -const app = new Hono() - .get('/', zValidator('query', GetReportsListRequestSchema), async (c) => { - const request = c.req.valid('query'); - const user = c.get('busterUser'); - - const response = await getReportsListHandler(request, user); - return c.json(response); - }); +const app = new Hono().get('/', zValidator('query', GetReportsListRequestSchema), async (c) => { + const request = c.req.valid('query'); + const user = c.get('busterUser'); + + const response = await getReportsListHandler(request, user); + return c.json(response); +}); export default app; diff --git a/apps/server/src/api/v2/reports/[id].ts b/apps/server/src/api/v2/reports/[id].ts index 62a06c78b..e892278f4 100644 --- a/apps/server/src/api/v2/reports/[id].ts +++ b/apps/server/src/api/v2/reports/[id].ts @@ -1,8 +1,5 @@ import type { User } from '@buster/database'; -import type { - UpdateReportRequest, - UpdateReportResponse -} from '@buster/server-shared/reports'; +import type { UpdateReportRequest, UpdateReportResponse } from '@buster/server-shared/reports'; import { UpdateReportRequestSchema } from '@buster/server-shared/reports'; import { zValidator } from '@hono/zod-validator'; import { Hono } from 'hono'; @@ -16,7 +13,7 @@ async function updateReportHandler( const existingReport = { id: reportId, name: 'Sales Analysis Q4', - file_name: 'sales_analysis_q4.md', + file_name: 'sales_analysis_q4.md', description: 'Quarterly sales performance analysis', created_by: user.id, created_at: '2024-01-15T10:00:00Z', @@ -26,13 +23,13 @@ async function updateReportHandler( content: [ { type: 'h1' as const, - children: [{ text: 'Sales Analysis Q4' }] + children: [{ text: 'Sales Analysis Q4' }], }, { type: 'p' as const, - children: [{ text: 'This report analyzes our Q4 sales performance.' }] - } - ] + children: [{ text: 'This report analyzes our Q4 sales performance.' }], + }, + ], }; if (!reportId || reportId === 'invalid') { @@ -42,20 +39,23 @@ async function updateReportHandler( const updatedReport = { ...existingReport, ...request, - updated_at: new Date().toISOString() + updated_at: new Date().toISOString(), }; return updatedReport; } -const app = new Hono() - .put('/', zValidator('json', UpdateReportRequestSchema), async (c) => { - const reportId = c.req.param('id'); - const request = c.req.valid('json'); - const user = c.get('busterUser'); - - const response = await updateReportHandler(reportId, request, user); - return c.json(response); - }); +const app = new Hono().put('/', zValidator('json', UpdateReportRequestSchema), async (c) => { + const reportId = c.req.param('id'); + const request = c.req.valid('json'); + const user = c.get('busterUser'); + + if (!reportId) { + throw new HTTPException(404, { message: 'Report ID is required' }); + } + + const response = await updateReportHandler(reportId, request, user); + return c.json(response); +}); export default app; diff --git a/packages/server-shared/src/reports/reports.types.ts b/packages/server-shared/src/reports/reports.types.ts index 4ab5db284..a9106c7c9 100644 --- a/packages/server-shared/src/reports/reports.types.ts +++ b/packages/server-shared/src/reports/reports.types.ts @@ -1,7 +1,23 @@ import { z } from 'zod'; -import type { ReportElements } from './report-elements'; +import type { ReportElement } from './report-elements'; +import { ReportElementSchema } from './report-elements'; -export const ReportSchema = z.object({ +// Define the type explicitly +export type ReportResponse = { + id: string; + name: string; + file_name: string; + description: string; + created_by: string; + created_at: string; + updated_at: string; + deleted_at: string | null; + publicly_accessible: boolean; + content: ReportElement[]; +}; + +// Create schema with explicit type annotation +export const ReportResponseSchema = z.object({ id: z.string(), name: z.string(), file_name: z.string(), @@ -11,7 +27,5 @@ export const ReportSchema = z.object({ updated_at: z.string(), deleted_at: z.string().nullable(), publicly_accessible: z.boolean(), - content: z.any() as z.ZodType, -}); - -export type Report = z.infer; + content: z.lazy(() => z.array(ReportElementSchema)), // Now using the actual schema +}) as z.ZodType; diff --git a/packages/server-shared/src/reports/requests.ts b/packages/server-shared/src/reports/requests.ts index cfd158849..0a8c47890 100644 --- a/packages/server-shared/src/reports/requests.ts +++ b/packages/server-shared/src/reports/requests.ts @@ -1,17 +1,17 @@ -import { z } from 'zod'; +import type { z } from 'zod'; +import { PaginatedRequestSchema } from '../type-utilities/pagination'; import type { ReportElements } from './report-elements'; +import { ReportResponseSchema } from './reports.types'; -export const GetReportsListRequestSchema = z.object({ - page_token: z.number().optional().default(0), - page_size: z.number().optional().default(50), -}); +export const GetReportsListRequestSchema = PaginatedRequestSchema; -export const UpdateReportRequestSchema = z.object({ - name: z.string().optional(), - description: z.string().optional(), - publicly_accessible: z.boolean().optional(), - content: z.any().optional() as z.ZodOptional>, -}); +// UpdateReportRequestSchema uses zod's .pick to select updatable fields from ReportResponseSchema +export const UpdateReportRequestSchema = ReportResponseSchema.pick({ + name: true, + description: true, + publicly_accessible: true, + content: true, +}).partial(); export type GetReportsListRequest = z.infer; export type UpdateReportRequest = z.infer; diff --git a/packages/server-shared/src/reports/responses.ts b/packages/server-shared/src/reports/responses.ts index 8e2511c95..65e525fc6 100644 --- a/packages/server-shared/src/reports/responses.ts +++ b/packages/server-shared/src/reports/responses.ts @@ -1,9 +1,9 @@ -import { z } from 'zod'; +import type { z } from 'zod'; import { PaginatedResponseSchema } from '../type-utilities/pagination'; -import { ReportSchema, type Report } from './reports.types'; +import { ReportResponseSchema } from './reports.types'; -export const GetReportsListResponseSchema = PaginatedResponseSchema(ReportSchema); -export const UpdateReportResponseSchema = ReportSchema; +export const GetReportsListResponseSchema = PaginatedResponseSchema(ReportResponseSchema); +export const UpdateReportResponseSchema = ReportResponseSchema; export type GetReportsListResponse = z.infer; -export type UpdateReportResponse = Report; +export type UpdateReportResponse = z.infer; diff --git a/packages/server-shared/src/type-utilities/pagination.ts b/packages/server-shared/src/type-utilities/pagination.ts index 742093f0e..a3591819c 100644 --- a/packages/server-shared/src/type-utilities/pagination.ts +++ b/packages/server-shared/src/type-utilities/pagination.ts @@ -16,3 +16,8 @@ export const PaginatedResponseSchema = (schema: z.ZodType) => }); export type PaginatedResponse = z.infer>>; + +export const PaginatedRequestSchema = z.object({ + page: z.number(), + page_size: z.number(), +});