100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
|
|
/**
|
||
|
|
* Custom error class for Freebit API errors
|
||
|
|
*/
|
||
|
|
export class FreebitError extends Error {
|
||
|
|
public readonly resultCode?: string | number;
|
||
|
|
public readonly statusCode?: string | number;
|
||
|
|
public readonly statusMessage?: string;
|
||
|
|
|
||
|
|
constructor(
|
||
|
|
message: string,
|
||
|
|
resultCode?: string | number,
|
||
|
|
statusCode?: string | number,
|
||
|
|
statusMessage?: string
|
||
|
|
) {
|
||
|
|
super(message);
|
||
|
|
this.name = "FreebitError";
|
||
|
|
this.resultCode = resultCode;
|
||
|
|
this.statusCode = statusCode;
|
||
|
|
this.statusMessage = statusMessage;
|
||
|
|
|
||
|
|
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
||
|
|
if (Error.captureStackTrace) {
|
||
|
|
Error.captureStackTrace(this, FreebitError);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if error indicates authentication failure
|
||
|
|
*/
|
||
|
|
isAuthError(): boolean {
|
||
|
|
return (
|
||
|
|
this.resultCode === "401" ||
|
||
|
|
this.statusCode === "401" ||
|
||
|
|
this.message.toLowerCase().includes("authentication") ||
|
||
|
|
this.message.toLowerCase().includes("unauthorized")
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if error indicates rate limiting
|
||
|
|
*/
|
||
|
|
isRateLimitError(): boolean {
|
||
|
|
return (
|
||
|
|
this.resultCode === "429" ||
|
||
|
|
this.statusCode === "429" ||
|
||
|
|
this.message.toLowerCase().includes("rate limit") ||
|
||
|
|
this.message.toLowerCase().includes("too many requests")
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if error is retryable
|
||
|
|
*/
|
||
|
|
isRetryable(): boolean {
|
||
|
|
const retryableCodes = ["500", "502", "503", "504", "408", "429"];
|
||
|
|
return (
|
||
|
|
retryableCodes.includes(String(this.resultCode)) ||
|
||
|
|
retryableCodes.includes(String(this.statusCode)) ||
|
||
|
|
this.message.toLowerCase().includes("timeout") ||
|
||
|
|
this.message.toLowerCase().includes("network")
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get user-friendly error message
|
||
|
|
*/
|
||
|
|
getUserFriendlyMessage(): string {
|
||
|
|
if (this.isAuthError()) {
|
||
|
|
return "SIM service is temporarily unavailable. Please try again later.";
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.isRateLimitError()) {
|
||
|
|
return "Service is busy. Please wait a moment and try again.";
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.message.toLowerCase().includes("account not found")) {
|
||
|
|
return "SIM account not found. Please contact support to verify your SIM configuration.";
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.message.toLowerCase().includes("timeout")) {
|
||
|
|
return "SIM service request timed out. Please try again.";
|
||
|
|
}
|
||
|
|
|
||
|
|
// Specific error codes
|
||
|
|
if (this.resultCode === "215" || this.statusCode === "215") {
|
||
|
|
return "Plan change failed. This may be due to: (1) Account has existing scheduled operations, (2) Invalid plan code for this account, (3) Account restrictions. Please check the Freebit Partner Tools for account status or contact support.";
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.resultCode === "381" || this.statusCode === "381") {
|
||
|
|
return "Network type change rejected. The current plan does not allow switching to the requested contract line. Adjust the plan first or contact support.";
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.resultCode === "382" || this.statusCode === "382") {
|
||
|
|
return "Network type change rejected because the contract line is not eligible for modification at this time. Please verify the SIM's status in Freebit before retrying.";
|
||
|
|
}
|
||
|
|
|
||
|
|
return "SIM operation failed. Please try again or contact support.";
|
||
|
|
}
|
||
|
|
}
|