All files / src/utils error-stackone-api.ts

100% Statements 40/40
100% Branches 39/39
100% Functions 5/5
100% Lines 40/40

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120                                        99x 99x 99x 99x 99x     99x           11x         16x           16x     16x 16x 1x       16x 16x 16x     16x 4x 4x 4x 2x 2x 1x   1x     1x         16x 10x     16x         10x 10x   10x 8x   8x 7x       8x             1x       8x 1x       10x         32x 32x      
import { USER_AGENT } from '../consts';
import { StackOneError } from './error-stackone';
 
/**
 * Raised when the StackOne API returns an error. Carries the HTTP status and the response
 * body, so a caller can branch on a 412 (a dead account) rather than parse the message.
 */
export class StackOneAPIError extends StackOneError {
	statusCode: number;
	responseBody: unknown;
	providerErrors?: unknown[];
	requestBody?: unknown;
 
	constructor(
		message: string,
		statusCode: number,
		responseBody: unknown,
		requestBody?: unknown,
		options?: ErrorOptions,
	) {
		super(message, options);
		this.name = 'StackOneAPIError';
		this.statusCode = statusCode;
		this.responseBody = responseBody;
		this.requestBody = requestBody;
 
		// Extract provider errors if they exist
		if (
			responseBody &&
			typeof responseBody === 'object' &&
			'provider_errors' in responseBody &&
			Array.isArray(responseBody.provider_errors)
		) {
			this.providerErrors = responseBody.provider_errors;
		}
	}
 
	toString(): string {
		return this._formatErrorMessage();
	}
 
	// Format the error message for better readability
	private _formatErrorMessage(): string {
		// Format the main error message
		let errorMessage = `API Error: ${this.statusCode} - ${this.message.replace(` for ${this._getUrlFromMessage()}`, '')}`;
 
		// Add the URL on a new line for better readability
		const url = this._getUrlFromMessage();
		if (url) {
			errorMessage += `\nEndpoint: ${url}`;
		}
 
		// Add request headers information (for debugging)
		errorMessage += '\n\nRequest Headers:';
		errorMessage += '\n- Authorization: [REDACTED]';
		errorMessage += `\n- User-Agent: ${USER_AGENT}`;
 
		// Add request body information if available
		if (this.requestBody) {
			errorMessage += '\n\nRequest Body:';
			try {
				if (typeof this.requestBody === 'object') {
					errorMessage += `\n${JSON.stringify(this.requestBody, null, 2)}`;
				} else if (typeof this.requestBody === 'string') {
					errorMessage += ` ${this.requestBody}`;
				} else {
					errorMessage += ` ${JSON.stringify(this.requestBody)}`;
				}
			} catch {
				errorMessage += ' [Unable to stringify request body]';
			}
		}
 
		// Add provider error information if available
		if (this.providerErrors && this.providerErrors.length > 0) {
			errorMessage += this._formatProviderErrors();
		}
 
		return errorMessage;
	}
 
	// Format provider errors
	private _formatProviderErrors(): string {
		let errorMessage = '';
		const providerError = this.providerErrors?.[0];
 
		if (typeof providerError === 'object' && providerError !== null) {
			errorMessage += '\n\nProvider Error:';
 
			if ('status' in providerError && typeof providerError.status === 'number') {
				errorMessage += ` ${providerError.status}`;
			}
 
			// Include raw error message if available
			if (
				'raw' in providerError &&
				typeof providerError.raw === 'object' &&
				providerError.raw !== null &&
				'error' in providerError.raw &&
				typeof providerError.raw.error === 'string'
			) {
				errorMessage += ` - ${providerError.raw.error}`;
			}
 
			// Add provider URL on a new line
			if ('url' in providerError && typeof providerError.url === 'string') {
				errorMessage += `\nProvider Endpoint: ${providerError.url}`;
			}
		}
 
		return errorMessage;
	}
 
	// Helper method to extract URL from the error message
	private _getUrlFromMessage(): string | null {
		const match = this.message.match(/ for (https?:\/\/[^\s:]+)/);
		return match ? match[1] : null;
	}
}