BREAKING CHANGE(core): Refactor to v3: introduce modular core/domain architecture, plugin system, observability and strict TypeScript configuration; remove legacy classes
This commit is contained in:
327
ts/core/errors/elasticsearch-error.ts
Normal file
327
ts/core/errors/elasticsearch-error.ts
Normal file
@@ -0,0 +1,327 @@
|
||||
import { ErrorCode, ErrorContext } from './types.js';
|
||||
|
||||
/**
|
||||
* Base error class for all Elasticsearch client errors
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* throw new ElasticsearchError('Connection failed', {
|
||||
* code: ErrorCode.CONNECTION_FAILED,
|
||||
* retryable: true,
|
||||
* context: {
|
||||
* timestamp: new Date(),
|
||||
* operation: 'connect',
|
||||
* statusCode: 503
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export class ElasticsearchError extends Error {
|
||||
/** Error code for categorization */
|
||||
public readonly code: ErrorCode;
|
||||
|
||||
/** Whether this error is retryable */
|
||||
public readonly retryable: boolean;
|
||||
|
||||
/** Additional context about the error */
|
||||
public readonly context: ErrorContext;
|
||||
|
||||
constructor(
|
||||
message: string,
|
||||
options: {
|
||||
code: ErrorCode;
|
||||
retryable: boolean;
|
||||
context: ErrorContext;
|
||||
cause?: Error;
|
||||
}
|
||||
) {
|
||||
super(message, { cause: options.cause });
|
||||
this.name = this.constructor.name;
|
||||
this.code = options.code;
|
||||
this.retryable = options.retryable;
|
||||
this.context = {
|
||||
...options.context,
|
||||
timestamp: options.context.timestamp || new Date(),
|
||||
};
|
||||
|
||||
// Maintains proper stack trace for where error was thrown (V8 only)
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert error to JSON for logging/serialization
|
||||
*/
|
||||
toJSON(): Record<string, unknown> {
|
||||
return {
|
||||
name: this.name,
|
||||
message: this.message,
|
||||
code: this.code,
|
||||
retryable: this.retryable,
|
||||
context: this.context,
|
||||
stack: this.stack,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if error is of a specific code
|
||||
*/
|
||||
is(code: ErrorCode): boolean {
|
||||
return this.code === code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if error is retryable
|
||||
*/
|
||||
canRetry(): boolean {
|
||||
return this.retryable;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection-related errors
|
||||
*/
|
||||
export class ConnectionError extends ElasticsearchError {
|
||||
constructor(message: string, context: Partial<ErrorContext> = {}, cause?: Error) {
|
||||
super(message, {
|
||||
code: ErrorCode.CONNECTION_FAILED,
|
||||
retryable: true,
|
||||
context: {
|
||||
...context,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
cause,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Timeout errors
|
||||
*/
|
||||
export class TimeoutError extends ElasticsearchError {
|
||||
constructor(
|
||||
message: string,
|
||||
operation: string,
|
||||
timeoutMs: number,
|
||||
context: Partial<ErrorContext> = {},
|
||||
cause?: Error
|
||||
) {
|
||||
super(message, {
|
||||
code: ErrorCode.REQUEST_TIMEOUT,
|
||||
retryable: true,
|
||||
context: {
|
||||
...context,
|
||||
operation,
|
||||
timeout: timeoutMs,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
cause,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Index not found error
|
||||
*/
|
||||
export class IndexNotFoundError extends ElasticsearchError {
|
||||
constructor(indexName: string, context: Partial<ErrorContext> = {}, cause?: Error) {
|
||||
super(`Index not found: ${indexName}`, {
|
||||
code: ErrorCode.INDEX_NOT_FOUND,
|
||||
retryable: false,
|
||||
context: {
|
||||
...context,
|
||||
index: indexName,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
cause,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Document not found error
|
||||
*/
|
||||
export class DocumentNotFoundError extends ElasticsearchError {
|
||||
constructor(
|
||||
documentId: string,
|
||||
indexName?: string,
|
||||
context: Partial<ErrorContext> = {},
|
||||
cause?: Error
|
||||
) {
|
||||
super(`Document not found: ${documentId}${indexName ? ` in index ${indexName}` : ''}`, {
|
||||
code: ErrorCode.DOCUMENT_NOT_FOUND,
|
||||
retryable: false,
|
||||
context: {
|
||||
...context,
|
||||
documentId,
|
||||
index: indexName,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
cause,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Document conflict error (version mismatch, optimistic locking)
|
||||
*/
|
||||
export class DocumentConflictError extends ElasticsearchError {
|
||||
constructor(
|
||||
documentId: string,
|
||||
message: string,
|
||||
context: Partial<ErrorContext> = {},
|
||||
cause?: Error
|
||||
) {
|
||||
super(message, {
|
||||
code: ErrorCode.DOCUMENT_CONFLICT,
|
||||
retryable: true, // Can retry with updated version
|
||||
context: {
|
||||
...context,
|
||||
documentId,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
cause,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication error
|
||||
*/
|
||||
export class AuthenticationError extends ElasticsearchError {
|
||||
constructor(message: string, context: Partial<ErrorContext> = {}, cause?: Error) {
|
||||
super(message, {
|
||||
code: ErrorCode.AUTHENTICATION_FAILED,
|
||||
retryable: false,
|
||||
context: {
|
||||
...context,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
cause,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorization error (insufficient permissions)
|
||||
*/
|
||||
export class AuthorizationError extends ElasticsearchError {
|
||||
constructor(
|
||||
operation: string,
|
||||
resource: string,
|
||||
context: Partial<ErrorContext> = {},
|
||||
cause?: Error
|
||||
) {
|
||||
super(`Not authorized to perform ${operation} on ${resource}`, {
|
||||
code: ErrorCode.AUTHORIZATION_FAILED,
|
||||
retryable: false,
|
||||
context: {
|
||||
...context,
|
||||
operation,
|
||||
resource,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
cause,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration error
|
||||
*/
|
||||
export class ConfigurationError extends ElasticsearchError {
|
||||
constructor(message: string, context: Partial<ErrorContext> = {}, cause?: Error) {
|
||||
super(message, {
|
||||
code: ErrorCode.INVALID_CONFIGURATION,
|
||||
retryable: false,
|
||||
context: {
|
||||
...context,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
cause,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query parsing error
|
||||
*/
|
||||
export class QueryParseError extends ElasticsearchError {
|
||||
constructor(query: unknown, reason: string, context: Partial<ErrorContext> = {}, cause?: Error) {
|
||||
super(`Failed to parse query: ${reason}`, {
|
||||
code: ErrorCode.QUERY_PARSE_ERROR,
|
||||
retryable: false,
|
||||
context: {
|
||||
...context,
|
||||
query,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
cause,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk operation error with partial failures
|
||||
*/
|
||||
export class BulkOperationError extends ElasticsearchError {
|
||||
public readonly successfulCount: number;
|
||||
public readonly failedCount: number;
|
||||
public readonly failures: Array<{
|
||||
documentId?: string;
|
||||
error: string;
|
||||
status: number;
|
||||
}>;
|
||||
|
||||
constructor(
|
||||
message: string,
|
||||
successful: number,
|
||||
failed: number,
|
||||
failures: Array<{ documentId?: string; error: string; status: number }>,
|
||||
context: Partial<ErrorContext> = {},
|
||||
cause?: Error
|
||||
) {
|
||||
super(message, {
|
||||
code: failed === 0 ? ErrorCode.BULK_REQUEST_FAILED : ErrorCode.PARTIAL_BULK_FAILURE,
|
||||
retryable: true, // Failed items can be retried
|
||||
context: {
|
||||
...context,
|
||||
successfulCount: successful,
|
||||
failedCount: failed,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
cause,
|
||||
});
|
||||
|
||||
this.successfulCount = successful;
|
||||
this.failedCount = failed;
|
||||
this.failures = failures;
|
||||
}
|
||||
|
||||
toJSON(): Record<string, unknown> {
|
||||
return {
|
||||
...super.toJSON(),
|
||||
successfulCount: this.successfulCount,
|
||||
failedCount: this.failedCount,
|
||||
failures: this.failures,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cluster unavailable error
|
||||
*/
|
||||
export class ClusterUnavailableError extends ElasticsearchError {
|
||||
constructor(message: string, context: Partial<ErrorContext> = {}, cause?: Error) {
|
||||
super(message, {
|
||||
code: ErrorCode.CLUSTER_UNAVAILABLE,
|
||||
retryable: true,
|
||||
context: {
|
||||
...context,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
cause,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user