BaseService.ts
Abstract base class for micro-block services with comprehensive metadata, operation framework, and validation. Includes all service interfaces and operation parameter validation.
BaseService.tsv1.0.012.9 KB
BaseService.ts(typescript)
1/**
2 * Validation rules for operation parameters
3 */
4export interface ParameterValidation {
5 min?: number; // Minimum value (numbers) or length (strings/arrays)
6 max?: number; // Maximum value (numbers) or length (strings/arrays)
7 options?: string[]; // Valid options for dropdown/select
8 pattern?: string; // Regex pattern for string validation
9 step?: number; // Step increment for numbers
10}
11
12/**
13 * Definition of a parameter for a service operation
14 */
15export interface OperationParameter {
16 name: string; // Parameter name (matches method signature)
17 type: 'string' | 'number' | 'boolean' | 'object' | 'array';
18 required: boolean; // Whether parameter is required
19 defaultValue?: any; // Default value to use in UI
20 description?: string; // Human-readable description
21 displayName?: string; // UI-friendly name (defaults to name)
22 validation?: ParameterValidation; // Validation rules
23 group?: string; // Group parameters in UI (e.g., 'Performance', 'Security')
24 sensitive?: boolean; // Mark as sensitive (password field, etc.)
25}
26
27/**
28 * Definition of a service operation that can be executed via admin interface
29 */
30export interface ServiceOperation {
31 name: string; // Method name to call (e.g., 'start', 'stop')
32 displayName: string; // Human-friendly name for UI
33 description: string; // What this operation does
34 parameters: OperationParameter[]; // Parameters this operation accepts
35 category?: string; // Group operations (e.g., 'control', 'configuration', 'maintenance')
36 permissions?: string[]; // Required permissions to execute
37 dangerous?: boolean; // Requires confirmation dialog
38 confirmationMessage?: string; // Custom confirmation message for dangerous operations
39 icon?: string; // UI icon name/class
40 buttonStyle?: 'primary' | 'secondary' | 'success' | 'warning' | 'danger'; // UI button styling
41}
42
43/**
44 * Result of executing an operation
45 */
46export interface OperationResult<T = any> {
47 success: boolean;
48 data?: T;
49 message?: string;
50 error?: string;
51 metadata?: Record<string, any>;
52}
53
54/**
55 * Service metadata interface for comprehensive service discovery and selection
56 */
57export interface ServiceMetadata {
58 // === Identity & Basic Info ===
59 name: string; // Unique service identifier
60 displayName: string; // Human-friendly name
61 description: string; // What this service does
62 contract: string; // Interface it implements (e.g., 'ICacheService')
63 implementation: string; // Implementation type (e.g., 'InMemory', 'Redis')
64 version: string; // Service implementation version
65 contractVersion: string; // Interface contract version
66
67 // === Capabilities & Features ===
68 features: string[]; // Key features this implementation provides
69 limitations?: string[]; // Known limitations or constraints
70
71 // === Dependencies (Portable Service Pattern) ===
72 dependencies: {
73 services?: string[]; // Required service interfaces (e.g., ['IDatabaseService'])
74 };
75
76 // === Requirements ===
77 requirements: {
78 services?: string[]; // Other service dependencies (e.g., ['IDatabaseService'])
79 runtime?: string[]; // External dependencies (e.g., ['Redis 6.0+', 'Node.js 18+'])
80 configuration: {
81 required: string[]; // Required config keys (e.g., ['REDIS_URL'])
82 optional?: string[]; // Optional config keys (e.g., ['REDIS_POOL_SIZE'])
83 };
84 };
85
86 // === Use Case Recommendations ===
87 recommendations: {
88 idealFor: string[]; // Perfect use cases
89 acceptableFor?: string[]; // Works but not optimal
90 notRecommendedFor?: string[]; // Poor fit scenarios
91 };
92
93 // === Configuration Schema (optional) ===
94 configurationSchema?: {
95 [key: string]: {
96 type: 'string' | 'number' | 'boolean' | 'array' | 'object';
97 description: string;
98 default?: any;
99 validation?: string; // Validation rule or regex
100 example?: any;
101 };
102 };
103}
104
105/**
106 * Abstract base class for all services in the micro-block architecture.
107 * Enforces metadata requirements for service discovery and selection.
108 */
109export abstract class BaseService {
110 /**
111 * Static metadata that describes this service implementation.
112 * Must be accessible before instantiation for discovery purposes.
113 */
114 static readonly metadata: ServiceMetadata;
115
116 /**
117 * Get the metadata for this service.
118 * Returns the static metadata property.
119 */
120 abstract getMetadata(): ServiceMetadata;
121
122 /**
123 * Initialize the service. Configuration should be provided via constructor.
124 * Override this method to implement service-specific initialization.
125 */
126 abstract initialize(): Promise<void> | void;
127
128 /**
129 * Health check for the service.
130 * Override this method to implement service-specific health checks.
131 */
132 abstract isHealthy(): Promise<boolean> | boolean;
133
134 /**
135 * Graceful shutdown of the service.
136 * Override this method to implement service-specific cleanup.
137 */
138 abstract destroy(): Promise<void> | void;
139
140 /**
141 * Get service-specific status information.
142 * Override this method to provide custom status details.
143 */
144 getStatus(): Record<string, any> {
145 return {
146 healthy: this.isHealthy(),
147 metadata: this.getMetadata()
148 };
149 }
150
151 /**
152 * Get available operations for this service.
153 * Override this method to expose service-specific operations for admin interface.
154 * Can be async to allow services to determine available operations based on current state.
155 */
156 getOperations(): ServiceOperation[] | Promise<ServiceOperation[]> {
157 return [];
158 }
159
160 /**
161 * Execute a service-specific operation with enhanced error handling and result formatting.
162 * This provides a generic way for services to expose custom operations
163 * while maintaining type safety through service-specific interfaces.
164 */
165 async executeOperation<T = any>(
166 operation: string,
167 params?: Record<string, any>
168 ): Promise<OperationResult<T>> {
169 try {
170 // Validate that the operation exists - handle both sync and async getOperations
171 const operationsResult = this.getOperations();
172 const availableOperations = await Promise.resolve(operationsResult);
173 const operationDef = availableOperations.find((op: ServiceOperation) => op.name === operation);
174
175 if (!operationDef) {
176 return {
177 success: false,
178 error: `Operation '${operation}' is not supported by service '${this.getMetadata().name}'`,
179 metadata: { availableOperations: availableOperations.map((op: ServiceOperation) => op.name) }
180 };
181 }
182
183 // Validate parameters
184 const validationResult = this.validateOperationParameters(operationDef, params || {});
185 if (!validationResult.valid) {
186 return {
187 success: false,
188 error: `Parameter validation failed: ${validationResult.errors.join(', ')}`,
189 metadata: { validationErrors: validationResult.errors }
190 };
191 }
192
193 // Execute the operation
194 const result = await this.executeOperationInternal(operation, params || {});
195
196 return {
197 success: true,
198 data: result,
199 message: `Operation '${operation}' completed successfully`,
200 metadata: { operation: operationDef.name, executedAt: new Date().toISOString() }
201 };
202
203 } catch (error) {
204 return {
205 success: false,
206 error: error instanceof Error ? error.message : String(error),
207 metadata: { operation, executedAt: new Date().toISOString() }
208 };
209 }
210 }
211
212 /**
213 * Internal operation execution - override this in derived classes.
214 * This method should handle the actual operation logic.
215 */
216 protected async executeOperationInternal<T = any>(
217 operation: string,
218 params: Record<string, any>
219 ): Promise<T> {
220 throw new Error(
221 `Operation '${operation}' is not implemented by service '${this.getMetadata().name}'`
222 );
223 }
224
225 /**
226 * Validate operation parameters against their definitions.
227 */
228 protected validateOperationParameters(
229 operation: ServiceOperation,
230 params: Record<string, any>
231 ): { valid: boolean; errors: string[] } {
232 const errors: string[] = [];
233
234 // Check required parameters
235 for (const param of operation.parameters) {
236 if (param.required && (params[param.name] === undefined || params[param.name] === null)) {
237 errors.push(`Required parameter '${param.name}' is missing`);
238 continue;
239 }
240
241 const value = params[param.name];
242 if (value !== undefined && value !== null) {
243 // Type validation
244 const actualType = Array.isArray(value) ? 'array' : typeof value;
245 if (actualType !== param.type) {
246 errors.push(`Parameter '${param.name}' must be of type '${param.type}', got '${actualType}'`);
247 continue;
248 }
249
250 // Validation rules
251 if (param.validation) {
252 const validationErrors = this.validateParameterValue(param.name, value, param.validation);
253 errors.push(...validationErrors);
254 }
255 }
256 }
257
258 // Check for unknown parameters
259 const allowedParams = new Set(operation.parameters.map(p => p.name));
260 for (const paramName of Object.keys(params)) {
261 if (!allowedParams.has(paramName)) {
262 errors.push(`Unknown parameter '${paramName}'`);
263 }
264 }
265
266 return { valid: errors.length === 0, errors };
267 }
268
269 /**
270 * Validate a single parameter value against validation rules.
271 */
272 private validateParameterValue(name: string, value: any, validation: ParameterValidation): string[] {
273 const errors: string[] = [];
274
275 // Min/Max validation
276 if (typeof value === 'number') {
277 if (validation.min !== undefined && value < validation.min) {
278 errors.push(`Parameter '${name}' must be at least ${validation.min}`);
279 }
280 if (validation.max !== undefined && value > validation.max) {
281 errors.push(`Parameter '${name}' must be at most ${validation.max}`);
282 }
283 }
284
285 // String/Array length validation
286 if (typeof value === 'string' || Array.isArray(value)) {
287 if (validation.min !== undefined && value.length < validation.min) {
288 errors.push(`Parameter '${name}' must have at least ${validation.min} characters/items`);
289 }
290 if (validation.max !== undefined && value.length > validation.max) {
291 errors.push(`Parameter '${name}' must have at most ${validation.max} characters/items`);
292 }
293 }
294
295 // Options validation (enum-like)
296 if (validation.options && !validation.options.includes(String(value))) {
297 errors.push(`Parameter '${name}' must be one of: ${validation.options.join(', ')}`);
298 }
299
300 // Pattern validation (regex)
301 if (validation.pattern && typeof value === 'string') {
302 const regex = new RegExp(validation.pattern);
303 if (!regex.test(value)) {
304 errors.push(`Parameter '${name}' does not match required pattern`);
305 }
306 }
307
308 return errors;
309 }
310
311 /**
312 * Validate configuration against the service's requirements.
313 * Uses the metadata to check required and optional configuration keys.
314 */
315 protected validateConfiguration(config: Record<string, any>): void {
316 const metadata = this.getMetadata();
317 const required = metadata.requirements.configuration.required || [];
318
319 // Check required configuration
320 for (const key of required) {
321 if (config[key] === undefined || config[key] === null) {
322 throw new Error(
323 `Required configuration key '${key}' is missing for service '${metadata.name}'`
324 );
325 }
326 }
327
328 // Validate configuration against schema if provided
329 if (metadata.configurationSchema) {
330 for (const [key, value] of Object.entries(config)) {
331 const schema = metadata.configurationSchema[key];
332 if (schema && !this.validateConfigValue(value, schema)) {
333 throw new Error(
334 `Configuration key '${key}' is invalid for service '${metadata.name}': ${schema.validation || 'Type mismatch'}`
335 );
336 }
337 }
338 }
339 }
340
341 /**
342 * Validate a single configuration value against its schema.
343 */
344 private validateConfigValue(value: any, schema: NonNullable<ServiceMetadata['configurationSchema']>[string]): boolean {
345 // Basic type checking
346 const actualType = Array.isArray(value) ? 'array' : typeof value;
347 if (actualType !== schema.type) {
348 return false;
349 }
350
351 // Additional validation rules can be added here
352 if (schema.validation) {
353 // For now, just basic validation - could be extended with regex, range checks, etc.
354 if (schema.type === 'string' && schema.validation.includes('Must be')) {
355 // Could implement more sophisticated validation
356 return value.length > 0;
357 }
358 }
359
360 return true;
361 }
362}
Metadata
- Path
- utaba/main/services/micro-block/BaseService.ts
- Namespace
- utaba/main/services/micro-block
- Author
- utaba
- Category
- services
- Technology
- typescript
- Contract Version
- 1.0.0
- MIME Type
- application/typescript
- Published
- 18-Jul-2025
- Last Updated
- 18-Jul-2025