Files
GRH/water-api/src/validators/project.validator.ts
2026-02-02 17:37:10 -06:00

129 lines
3.3 KiB
TypeScript

import { z } from 'zod';
import { Request, Response, NextFunction } from 'express';
/**
* Project status enum values
*/
export const ProjectStatus = {
ACTIVE: 'ACTIVE',
INACTIVE: 'INACTIVE',
COMPLETED: 'COMPLETED',
} as const;
export type ProjectStatusType = (typeof ProjectStatus)[keyof typeof ProjectStatus];
/**
* Schema for creating a new project
* - name: required, non-empty string
* - description: optional string
* - area_name: optional string
* - location: optional string
* - status: optional, defaults to ACTIVE
*/
export const createProjectSchema = z.object({
name: z
.string({ required_error: 'Name is required' })
.min(1, 'Name cannot be empty')
.max(255, 'Name must be at most 255 characters'),
description: z
.string()
.max(1000, 'Description must be at most 1000 characters')
.optional()
.nullable(),
area_name: z
.string()
.max(255, 'Area name must be at most 255 characters')
.optional()
.nullable(),
location: z
.string()
.max(500, 'Location must be at most 500 characters')
.optional()
.nullable(),
status: z
.enum([ProjectStatus.ACTIVE, ProjectStatus.INACTIVE, ProjectStatus.COMPLETED])
.default(ProjectStatus.ACTIVE)
.optional(),
meter_type_id: z
.string()
.uuid('Meter type ID must be a valid UUID')
.optional()
.nullable(),
});
/**
* Schema for updating a project
* All fields are optional
*/
export const updateProjectSchema = z.object({
name: z
.string()
.min(1, 'Name cannot be empty')
.max(255, 'Name must be at most 255 characters')
.optional(),
description: z
.string()
.max(1000, 'Description must be at most 1000 characters')
.optional()
.nullable(),
area_name: z
.string()
.max(255, 'Area name must be at most 255 characters')
.optional()
.nullable(),
location: z
.string()
.max(500, 'Location must be at most 500 characters')
.optional()
.nullable(),
status: z
.enum([ProjectStatus.ACTIVE, ProjectStatus.INACTIVE, ProjectStatus.COMPLETED])
.optional(),
meter_type_id: z
.string()
.uuid('Meter type ID must be a valid UUID')
.optional()
.nullable(),
});
/**
* Type definitions derived from schemas
*/
export type CreateProjectInput = z.infer<typeof createProjectSchema>;
export type UpdateProjectInput = z.infer<typeof updateProjectSchema>;
/**
* Generic validation middleware factory
* Creates a middleware that validates request body against a Zod schema
* @param schema - Zod schema to validate against
*/
function validate<T extends z.ZodTypeAny>(schema: T) {
return (req: Request, res: Response, next: NextFunction): void => {
const result = schema.safeParse(req.body);
if (!result.success) {
const errors = result.error.errors.map((err) => ({
field: err.path.join('.'),
message: err.message,
}));
res.status(400).json({
success: false,
error: 'Validation failed',
details: errors,
});
return;
}
// Replace body with validated and typed data
req.body = result.data;
next();
};
}
/**
* Pre-configured validation middlewares for projects
*/
export const validateCreateProject = validate(createProjectSchema);
export const validateUpdateProject = validate(updateProjectSchema);