UNPKG

@kenniy/godeye-data-contracts

Version:

Enterprise-grade base repository architecture for GOD-EYE microservices with zero overhead and maximum code reuse

288 lines (287 loc) 11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResponseFactory = void 0; const response_builder_utils_1 = require("../utils/response-builder.utils"); /** * ResponseFactory - Standardized response creation across all services * * This factory ensures consistent response format throughout the entire * microservices ecosystem while providing performance monitoring capabilities. */ class ResponseFactory { /** * Create a successful response with auto-detection * Automatically detects if data is paginated and formats accordingly */ static success(data, message = "Operation successful", metadata, start_time) { // Auto-detect if this is paginated data if (this.isPaginatedData(data)) { const paginatedData = data; // Extract pagination data from various formats const { items, total, page, limit } = this.extractPaginationData(paginatedData); // Extract embedded metadata from repository result const embeddedMetadata = paginatedData.metadata || {}; const combinedMetadata = { ...embeddedMetadata, ...metadata }; return this.paginated(items, total, page, limit, message, combinedMetadata, start_time); } // Standard success response (single entity or array with embedded metadata) const baseMetadata = (0, response_builder_utils_1.buildBaseResponseMetadata)(start_time); // Extract embedded metadata for single entity results let embeddedMetadata = {}; let actualData = data; if (data && typeof data === "object" && "data" in data && "metadata" in data) { embeddedMetadata = data.metadata || {}; actualData = data.data; } else if (data && typeof data === "object" && "metadata" in data && !("items" in data)) { // Handle case where metadata is at root level but not pagination embeddedMetadata = data.metadata || {}; const { metadata: _, ...dataWithoutMetadata } = data; actualData = dataWithoutMetadata; } const combinedMetadata = { ...embeddedMetadata, ...metadata }; return { success: true, data: actualData, message, status_code: 200, ...baseMetadata, metadata: combinedMetadata, }; } /** * BLAZING FAST: Create paginated response with pre-computed values * Optimized for maximum performance with minimal calculations */ static paginated(items, total, page, limit, message = "Data retrieved successfully", metadata, start_time) { // OPTIMIZATION: Pre-compute all values in single pass const total_pages = Math.ceil(total / limit); const has_next = page < total_pages; const has_prev = page > 1; const baseMetadata = (0, response_builder_utils_1.buildBaseResponseMetadata)(start_time); // OPTIMIZATION: Create objects directly without intermediate variables return { success: true, data: { items, total, page, limit, totalPages: total_pages, hasNext: has_next, hasPrev: has_prev, }, message, status_code: 200, ...baseMetadata, metadata, }; } /** * Create an error response */ static error(error, message, status_code = 400, metadata, start_time) { const baseMetadata = (0, response_builder_utils_1.buildBaseResponseMetadata)(start_time); return { success: false, error, message, status_code, ...baseMetadata, metadata, }; } // ============================================================================ // COMMON ERROR TYPES - Convenient methods for standard HTTP errors // ============================================================================ /** * 400 Bad Request */ static badRequest(message, error = "Bad Request", metadata, start_time) { return this.error(error, message, 400, metadata, start_time); } /** * 401 Unauthorized */ static unauthorized(message = "Unauthorized access", metadata, start_time) { return this.error("Unauthorized", message, 401, metadata, start_time); } /** * 403 Forbidden */ static forbidden(message = "Access forbidden", metadata, start_time) { return this.error("Forbidden", message, 403, metadata, start_time); } /** * 404 Not Found */ static notFound(message = "Resource not found", metadata, start_time) { return this.error("Not Found", message, 404, metadata, start_time); } /** * 422 Validation Error */ static validationError(message, validation_errors, metadata, start_time) { const enhanced_metadata = { ...metadata, validation_errors, }; return this.error("Validation Error", message, 422, enhanced_metadata, start_time); } /** * 429 Rate Limited */ static rateLimited(message = "Rate limit exceeded", rate_limit_remaining = 0, metadata, start_time) { const enhanced_metadata = { ...metadata, rate_limit_remaining, }; return this.error("Rate Limited", message, 429, enhanced_metadata, start_time); } /** * 500 Internal Server Error */ static serverError(message = "Internal server error", metadata, start_time) { return this.error("Internal Server Error", message, 500, metadata, start_time); } // ============================================================================ // UTILITY METHODS // ============================================================================ /** * Auto-detect if data is paginated by checking for various pagination formats * Supports multiple common pagination patterns */ static isPaginatedData(data) { if (!data || typeof data !== "object") return false; // Format 1: { items: [...], total: 50, page?: 1, limit?: 20 } if ("items" in data && "total" in data && Array.isArray(data.items) && typeof data.total === "number") { return true; } // Format 2: { data: [...], total: 50, page?: 1, limit?: 20 } if ("data" in data && "total" in data && Array.isArray(data.data) && typeof data.total === "number") { return true; } // Format 3: { results: [...], count: 50, page?: 1, limit?: 20 } if ("results" in data && "count" in data && Array.isArray(data.results) && typeof data.count === "number") { return true; } // Format 4: { rows: [...], totalCount: 50, page?: 1, limit?: 20 } if ("rows" in data && "totalCount" in data && Array.isArray(data.rows) && typeof data.totalCount === "number") { return true; } // Format 5: { data: [...], pagination: { total: 50, page: 1, limit: 20, ... } } if ("data" in data && "pagination" in data && Array.isArray(data.data) && data.pagination && typeof data.pagination === "object" && typeof data.pagination.total === "number") { return true; } return false; } /** * Extract pagination data from various formats * Normalizes different pagination structures to standard format */ static extractPaginationData(data) { // Format 1: { items: [...], total: 50, page?: 1, limit?: 20 } if ("items" in data && "total" in data) { return { items: data.items, total: data.total, page: data.page || 1, limit: data.limit || 20, }; } // Format 2: { data: [...], total: 50, page?: 1, limit?: 20 } if ("data" in data && "total" in data) { return { items: data.data, total: data.total, page: data.page || 1, limit: data.limit || 20, }; } // Format 3: { results: [...], count: 50, page?: 1, limit?: 20 } if ("results" in data && "count" in data) { return { items: data.results, total: data.count, page: data.page || 1, limit: data.limit || 20, }; } // Format 4: { rows: [...], totalCount: 50, page?: 1, limit?: 20 } if ("rows" in data && "totalCount" in data) { return { items: data.rows, total: data.totalCount, page: data.page || 1, limit: data.limit || 20, }; } // Format 5: { data: [...], pagination: { total: 50, page: 1, limit: 20, ... } } if ("data" in data && "pagination" in data && data.pagination) { return { items: data.data, total: data.pagination.total, page: data.pagination.page || 1, limit: data.pagination.limit || 20, }; } // Fallback (shouldn't happen if isPaginatedData works correctly) return { items: [], total: 0, page: 1, limit: 20, }; } /** * Get current system performance metrics * Call this to automatically populate performance metadata */ static getPerformanceMetrics() { if (typeof process !== "undefined" && process.memoryUsage) { const memUsage = process.memoryUsage(); const cpuUsage = process.cpuUsage ? process.cpuUsage() : null; return { heap_used_mb: Math.round((memUsage.heapUsed / 1024 / 1024) * 100) / 100, heap_total_mb: Math.round((memUsage.heapTotal / 1024 / 1024) * 100) / 100, memory_used_mb: Math.round((memUsage.rss / 1024 / 1024) * 100) / 100, // Note: CPU usage calculation would need additional implementation // for accurate real-time values }; } return {}; } /** * Create response with automatic performance monitoring */ static successWithMetrics(data, message = "Operation successful", custom_metadata, start_time) { const performance_metrics = this.getPerformanceMetrics(); const metadata = { ...performance_metrics, ...custom_metadata }; return this.success(data, message, metadata, start_time); } } exports.ResponseFactory = ResponseFactory;