Skip to main content
Glama
schema-validator.ts10.5 kB
import { z } from 'zod'; import { logger } from '../../../src/utils/logger.ts'; import { projectSchema, personSchema, taskSchema, clientSchema, allocationSchema, departmentSchema, statusSchema, phaseSchema, milestoneSchema, timeOffSchema, timeOffTypeSchema, publicHolidaySchema, teamHolidaySchema, accountSchema, roleSchema, projectTaskSchema, } from '../../../src/types/float.ts'; // Schema validation results export interface ValidationResult { isValid: boolean; errors: string[]; warnings: string[]; data?: any; } // Schema validator class export class SchemaValidator { private static instance: SchemaValidator; private constructor() {} static getInstance(): SchemaValidator { if (!SchemaValidator.instance) { SchemaValidator.instance = new SchemaValidator(); } return SchemaValidator.instance; } // Validate data against a schema validate<T>(data: any, schema: z.ZodSchema<T>): ValidationResult { try { const validatedData = schema.parse(data); return { isValid: true, errors: [], warnings: [], data: validatedData, }; } catch (error) { if (error instanceof z.ZodError) { return { isValid: false, errors: error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`), warnings: [], data: data, }; } return { isValid: false, errors: [ `Unknown validation error: ${error instanceof Error ? error.message : 'Unknown error'}`, ], warnings: [], data: data, }; } } // Validate with warnings for optional fields validateWithWarnings<T>(data: any, schema: z.ZodSchema<T>): ValidationResult { const result = this.validate(data, schema); // Check for missing optional fields that might be expected const warnings: string[] = []; if (result.isValid && data) { // Add warnings for null values that might be unexpected this.checkForNullValues(data, '', warnings); } return { ...result, warnings, }; } private checkForNullValues(obj: any, path: string, warnings: string[]): void { if (obj === null || obj === undefined) { warnings.push(`${path}: Value is null or undefined`); return; } if (typeof obj === 'object' && !Array.isArray(obj)) { Object.entries(obj).forEach(([key, value]) => { const newPath = path ? `${path}.${key}` : key; if (value === null) { warnings.push(`${newPath}: Value is null`); } else if (typeof value === 'object') { this.checkForNullValues(value, newPath, warnings); } }); } if (Array.isArray(obj)) { obj.forEach((item, index) => { const newPath = `${path}[${index}]`; this.checkForNullValues(item, newPath, warnings); }); } } // Validate API response structure validateApiResponse(response: any): ValidationResult { const baseResponseSchema = z.object({ success: z.boolean().optional(), data: z.any().optional(), error: z.string().optional(), }); return this.validate(response, baseResponseSchema); } // Validate paginated response validatePaginatedResponse(response: any): ValidationResult { const paginatedSchema = z.object({ data: z.array(z.any()), pagination: z .object({ page: z.number(), per_page: z.number(), total: z.number(), total_pages: z.number(), }) .optional(), }); return this.validate(response, paginatedSchema); } // Validate error response validateErrorResponse(response: any): ValidationResult { const errorSchema = z.object({ error: z.string(), message: z.string().optional(), code: z.string().optional(), status: z.number().optional(), }); return this.validate(response, errorSchema); } } // Schema validation helpers for specific entities export class EntitySchemaValidator extends SchemaValidator { // Project validation validateProject(data: any): ValidationResult { return this.validateWithWarnings(data, projectSchema); } validateProjects(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(projectSchema)); } // Person validation validatePerson(data: any): ValidationResult { return this.validateWithWarnings(data, personSchema); } validatePeople(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(personSchema)); } // Task validation validateTask(data: any): ValidationResult { return this.validateWithWarnings(data, taskSchema); } validateTasks(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(taskSchema)); } // Client validation validateClient(data: any): ValidationResult { return this.validateWithWarnings(data, clientSchema); } validateClients(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(clientSchema)); } // Allocation validation validateAllocation(data: any): ValidationResult { return this.validateWithWarnings(data, allocationSchema); } validateAllocations(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(allocationSchema)); } // Department validation validateDepartment(data: any): ValidationResult { return this.validateWithWarnings(data, departmentSchema); } validateDepartments(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(departmentSchema)); } // Status validation validateStatus(data: any): ValidationResult { return this.validateWithWarnings(data, statusSchema); } validateStatuses(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(statusSchema)); } // Phase validation validatePhase(data: any): ValidationResult { return this.validateWithWarnings(data, phaseSchema); } validatePhases(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(phaseSchema)); } // Milestone validation validateMilestone(data: any): ValidationResult { return this.validateWithWarnings(data, milestoneSchema); } validateMilestones(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(milestoneSchema)); } // Time Off validation validateTimeOff(data: any): ValidationResult { return this.validateWithWarnings(data, timeOffSchema); } validateTimeOffs(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(timeOffSchema)); } // Time Off Type validation validateTimeOffType(data: any): ValidationResult { return this.validateWithWarnings(data, timeOffTypeSchema); } validateTimeOffTypes(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(timeOffTypeSchema)); } // Public Holiday validation validatePublicHoliday(data: any): ValidationResult { return this.validateWithWarnings(data, publicHolidaySchema); } validatePublicHolidays(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(publicHolidaySchema)); } // Team Holiday validation validateTeamHoliday(data: any): ValidationResult { return this.validateWithWarnings(data, teamHolidaySchema); } validateTeamHolidays(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(teamHolidaySchema)); } // Account validation validateAccount(data: any): ValidationResult { return this.validateWithWarnings(data, accountSchema); } validateAccounts(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(accountSchema)); } // Role validation validateRole(data: any): ValidationResult { return this.validateWithWarnings(data, roleSchema); } validateRoles(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(roleSchema)); } // Project Task validation validateProjectTask(data: any): ValidationResult { return this.validateWithWarnings(data, projectTaskSchema); } validateProjectTasks(data: any): ValidationResult { return this.validateWithWarnings(data, z.array(projectTaskSchema)); } } // Test assertion helpers export const assertValidSchema = <T>(data: any, schema: z.ZodSchema<T>): T => { const validator = SchemaValidator.getInstance(); const result = validator.validate(data, schema); if (!result.isValid) { throw new Error(`Schema validation failed:\n${result.errors.join('\n')}`); } return result.data; }; export const assertValidEntitySchema = (entityType: string, data: any): any => { const validator = new EntitySchemaValidator(); let result: ValidationResult; switch (entityType) { case 'project': result = validator.validateProject(data); break; case 'person': result = validator.validatePerson(data); break; case 'task': result = validator.validateTask(data); break; case 'client': result = validator.validateClient(data); break; case 'allocation': result = validator.validateAllocation(data); break; case 'department': result = validator.validateDepartment(data); break; case 'status': result = validator.validateStatus(data); break; case 'phase': result = validator.validatePhase(data); break; case 'milestone': result = validator.validateMilestone(data); break; case 'timeoff': result = validator.validateTimeOff(data); break; case 'account': result = validator.validateAccount(data); break; case 'role': result = validator.validateRole(data); break; case 'project-task': result = validator.validateProjectTask(data); break; default: throw new Error(`Unknown entity type: ${entityType}`); } if (!result.isValid) { throw new Error(`Schema validation failed for ${entityType}:\n${result.errors.join('\n')}`); } // Log warnings if (result.warnings.length > 0) { logger.warn(`Schema validation warnings for ${entityType}:`, result.warnings); } return result.data; }; // Export singleton instance export const schemaValidator = SchemaValidator.getInstance(); export const entitySchemaValidator = new EntitySchemaValidator();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/asachs01/float-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server