Files
elasticsearch/ts/core/errors/elasticsearch-error.ts

328 lines
7.2 KiB
TypeScript
Raw Normal View History

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,
});
}
}