hikma-engine
Version:
Code Knowledge Graph Indexer - A sophisticated TypeScript-based indexer that transforms Git repositories into multi-dimensional knowledge stores for AI agents
206 lines (205 loc) • 9.04 kB
JavaScript
"use strict";
/**
* @file Request validation middleware using Joi for API input validation.
* Validates query parameters, request bodies, and headers.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateComprehensiveSearch = exports.validateHybridSearch = exports.validateGitSearch = exports.validateStructuralSearch = exports.validateSemanticSearch = exports.ValidationSchemas = void 0;
exports.createValidationMiddleware = createValidationMiddleware;
const joi_1 = __importDefault(require("joi"));
const logger_1 = require("../../utils/logger");
const logger = (0, logger_1.getLogger)('ValidationMiddleware');
/**
* Creates validation middleware for request validation.
*/
function createValidationMiddleware(options) {
return (req, res, next) => {
const requestId = req.headers['x-request-id'] || 'unknown';
const validationErrors = [];
// Validate query parameters
if (options.querySchema) {
const { error } = options.querySchema.validate(req.query, {
abortEarly: options.abortEarly ?? false,
allowUnknown: options.allowUnknown ?? false,
stripUnknown: true,
});
if (error) {
validationErrors.push(...error.details.map(detail => ({
field: `query.${detail.path.join('.')}`,
value: detail.context?.value,
constraint: detail.message,
})));
}
}
// Validate request body
if (options.bodySchema) {
const { error } = options.bodySchema.validate(req.body, {
abortEarly: options.abortEarly ?? false,
allowUnknown: options.allowUnknown ?? false,
stripUnknown: true,
});
if (error) {
validationErrors.push(...error.details.map(detail => ({
field: `body.${detail.path.join('.')}`,
value: detail.context?.value,
constraint: detail.message,
})));
}
}
// Validate path parameters
if (options.paramsSchema) {
const { error } = options.paramsSchema.validate(req.params, {
abortEarly: options.abortEarly ?? false,
allowUnknown: options.allowUnknown ?? false,
stripUnknown: true,
});
if (error) {
validationErrors.push(...error.details.map(detail => ({
field: `params.${detail.path.join('.')}`,
value: detail.context?.value,
constraint: detail.message,
})));
}
}
// If validation errors exist, return 400 response
if (validationErrors.length > 0) {
logger.warn('Request validation failed', {
requestId,
method: req.method,
url: req.url,
errors: validationErrors,
});
const errorResponse = {
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Request validation failed',
details: validationErrors,
},
meta: {
timestamp: new Date().toISOString(),
requestId,
},
};
return res.status(400).json(errorResponse);
}
logger.debug('Request validation passed', {
requestId,
method: req.method,
url: req.url,
});
next();
};
}
/**
* Common validation schemas for search endpoints.
*/
exports.ValidationSchemas = {
/**
* Common query parameters for all search endpoints.
*/
commonQuery: joi_1.default.object({
q: joi_1.default.string().required().min(1).max(500).trim().description('Search query'),
limit: joi_1.default.number().integer().min(1).max(100).default(10).description('Maximum number of results'),
offset: joi_1.default.number().integer().min(0).default(0).description('Pagination offset'),
page: joi_1.default.number().integer().min(1).description('Page number (alternative to offset)'),
}),
/**
* Semantic search query validation schema.
*/
semanticSearch: joi_1.default.object({
q: joi_1.default.string().required().min(1).max(500).trim(),
limit: joi_1.default.number().integer().min(1).max(100).default(10),
nodeTypes: joi_1.default.array().items(joi_1.default.string().valid('function', 'class', 'interface', 'variable', 'file', 'commit')).description('Filter by node types'),
minSimilarity: joi_1.default.number().min(0).max(1).default(0.1).description('Minimum similarity threshold'),
includeMetadata: joi_1.default.boolean().default(true).description('Include metadata in results'),
}),
/**
* Structural search query validation schema.
*/
structuralSearch: joi_1.default.object({
q: joi_1.default.string().required().min(1).max(500).trim(),
limit: joi_1.default.number().integer().min(1).max(100).default(10),
language: joi_1.default.string().valid('javascript', 'typescript', 'python', 'java', 'cpp', 'c', 'go', 'rust', 'php', 'ruby').description('Programming language filter'),
elementType: joi_1.default.string().valid('function', 'class', 'interface', 'variable', 'import', 'export').description('Code element type'),
filePath: joi_1.default.string().max(1000).description('File path pattern'),
}),
/**
* Git history search query validation schema.
*/
gitSearch: joi_1.default.object({
q: joi_1.default.string().required().min(1).max(500).trim(),
limit: joi_1.default.number().integer().min(1).max(100).default(10),
author: joi_1.default.string().max(100).description('Author filter'),
dateFrom: joi_1.default.date().iso().description('Start date for date range filter'),
dateTo: joi_1.default.date().iso().min(joi_1.default.ref('dateFrom')).description('End date for date range filter'),
branch: joi_1.default.string().max(100).description('Branch filter'),
}),
/**
* Hybrid search query validation schema.
*/
hybridSearch: joi_1.default.object({
q: joi_1.default.string().required().min(1).max(500).trim(),
limit: joi_1.default.number().integer().min(1).max(100).default(10),
filters: joi_1.default.object({
languages: joi_1.default.array().items(joi_1.default.string()),
fileTypes: joi_1.default.array().items(joi_1.default.string()),
dateRange: joi_1.default.object({
start: joi_1.default.date().iso(),
end: joi_1.default.date().iso(),
}),
authors: joi_1.default.array().items(joi_1.default.string()),
}).description('Metadata filters'),
weights: joi_1.default.object({
semantic: joi_1.default.number().min(0).max(1).default(0.4),
structural: joi_1.default.number().min(0).max(1).default(0.3),
temporal: joi_1.default.number().min(0).max(1).default(0.3),
}).description('Search dimension weights'),
}),
/**
* Comprehensive search query validation schema.
*/
comprehensiveSearch: joi_1.default.object({
q: joi_1.default.string().required().min(1).max(500).trim(),
limit: joi_1.default.number().integer().min(1).max(100).default(20),
includeTypes: joi_1.default.array().items(joi_1.default.string().valid('semantic', 'structural', 'git', 'metadata')).description('Search types to include'),
}),
};
/**
* Creates validation middleware for semantic search endpoints.
*/
exports.validateSemanticSearch = createValidationMiddleware({
querySchema: exports.ValidationSchemas.semanticSearch,
allowUnknown: false,
});
/**
* Creates validation middleware for structural search endpoints.
*/
exports.validateStructuralSearch = createValidationMiddleware({
querySchema: exports.ValidationSchemas.structuralSearch,
allowUnknown: false,
});
/**
* Creates validation middleware for git search endpoints.
*/
exports.validateGitSearch = createValidationMiddleware({
querySchema: exports.ValidationSchemas.gitSearch,
allowUnknown: false,
});
/**
* Creates validation middleware for hybrid search endpoints.
*/
exports.validateHybridSearch = createValidationMiddleware({
querySchema: exports.ValidationSchemas.hybridSearch,
allowUnknown: false,
});
/**
* Creates validation middleware for comprehensive search endpoints.
*/
exports.validateComprehensiveSearch = createValidationMiddleware({
querySchema: exports.ValidationSchemas.comprehensiveSearch,
allowUnknown: false,
});