fix(api): return Zod validation errors from public API routes (#708)
* fix(api): return Zod validation errors from public API routes Public API handlers now surface ZodError details as 400 VALIDATION_ERROR responses instead of generic 500 INTERNAL_ERROR payloads. Course import services also rethrow ZodError so validation failures are not wrapped as server errors. Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * refactor(utils): centralize course type values without db dependency Define COURSE_TYPE_VALUES once in @cio/utils/constants and use it for ZCourseType validation and the Drizzle COURSE_TYPE pgEnum. Removes the circular @cio/utils -> @cio/db import while keeping course-import schemas aligned with all four course types including PUBLIC. Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * fix(validation): default missing course metadata allowNewStudent to true Courses stored without allowNewStudent in metadata failed structure export validation. Align Zod preprocessing with runtime behavior where absent values mean enrollments are open. Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * chore(db): drop unused COURSE_TYPE_VALUES re-export from schema Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
This commit is contained in:
co-authored by
Rotimi Best
Cursor Agent
parent
a337363965
commit
0ed0e3864d
@@ -46,6 +46,7 @@ import {
|
||||
import { getCourseOrganizationId } from '@cio/db/queries/tag';
|
||||
import { assignCourseTagsByName } from '@api/services/tag';
|
||||
import { generateSlug } from '@cio/utils/functions';
|
||||
import { ZodError } from 'zod';
|
||||
|
||||
type DraftSummary = {
|
||||
sectionCount: number;
|
||||
@@ -537,7 +538,7 @@ export async function createCourseImportDraftService(orgId: string, profileId: s
|
||||
sourceArtifacts: data.sourceArtifacts ?? data.draft.sourceReferences ?? []
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
if (error instanceof AppError || error instanceof ZodError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -579,7 +580,7 @@ export async function getCourseImportStructureService(orgId: string, courseId: s
|
||||
try {
|
||||
return buildCourseStructureSnapshot(orgId, courseId);
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
if (error instanceof AppError || error instanceof ZodError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -600,7 +601,7 @@ export async function getCourseImportDraftService(orgId: string, draftId: string
|
||||
|
||||
return draft;
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
if (error instanceof AppError || error instanceof ZodError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -641,7 +642,7 @@ export async function updateCourseImportDraftService(orgId: string, draftId: str
|
||||
|
||||
return updated;
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
if (error instanceof AppError || error instanceof ZodError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -845,7 +846,7 @@ export async function publishCourseImportDraftService(
|
||||
localeCount: new Set(draft.lessonLanguages.map((item) => item.locale)).size
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
if (error instanceof AppError || error instanceof ZodError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -1132,7 +1133,7 @@ export async function publishCourseImportDraftToExistingCourseService(
|
||||
localeCount: new Set(draft.lessonLanguages.map((item) => item.locale)).size
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
if (error instanceof AppError || error instanceof ZodError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user