271 lines
11 KiB
JavaScript
271 lines
11 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.isValidErrorCode = exports.httpStatusFromErrorCode = exports.TwirpErrorCode = exports.BadRouteError = exports.InternalServerErrorWith = exports.InternalServerError = exports.RequiredArgumentError = exports.InvalidArgumentError = exports.NotFoundError = exports.TwirpError = void 0;
|
|
/**
|
|
* Represents a twirp error
|
|
*/
|
|
class TwirpError extends Error {
|
|
constructor(code, msg) {
|
|
super(msg);
|
|
this.code = TwirpErrorCode.Internal;
|
|
this.meta = {};
|
|
this.code = code;
|
|
this.msg = msg;
|
|
Object.setPrototypeOf(this, TwirpError.prototype);
|
|
}
|
|
/**
|
|
* Adds a metadata kv to the error
|
|
* @param key
|
|
* @param value
|
|
*/
|
|
withMeta(key, value) {
|
|
this.meta[key] = value;
|
|
return this;
|
|
}
|
|
/**
|
|
* Returns a single metadata value
|
|
* return "" if not found
|
|
* @param key
|
|
*/
|
|
getMeta(key) {
|
|
return this.meta[key] || "";
|
|
}
|
|
/**
|
|
* Add the original error cause
|
|
* @param err
|
|
* @param addMeta
|
|
*/
|
|
withCause(err, addMeta = false) {
|
|
this._originalCause = err;
|
|
if (addMeta) {
|
|
this.withMeta("cause", err.message);
|
|
}
|
|
return this;
|
|
}
|
|
cause() {
|
|
return this._originalCause;
|
|
}
|
|
/**
|
|
* Returns the error representation to JSON
|
|
*/
|
|
toJSON() {
|
|
try {
|
|
return JSON.stringify({
|
|
code: this.code,
|
|
msg: this.msg,
|
|
meta: this.meta,
|
|
});
|
|
}
|
|
catch (e) {
|
|
return `{"code": "internal", "msg": "There was an error but it could not be serialized into JSON"}`;
|
|
}
|
|
}
|
|
/**
|
|
* Create a twirp error from an object
|
|
* @param obj
|
|
*/
|
|
static fromObject(obj) {
|
|
const code = obj["code"] || TwirpErrorCode.Unknown;
|
|
const msg = obj["msg"] || "unknown";
|
|
const error = new TwirpError(code, msg);
|
|
if (obj["meta"]) {
|
|
Object.keys(obj["meta"]).forEach((key) => {
|
|
error.withMeta(key, obj["meta"][key]);
|
|
});
|
|
}
|
|
return error;
|
|
}
|
|
}
|
|
exports.TwirpError = TwirpError;
|
|
/**
|
|
* NotFoundError constructor for the common NotFound error.
|
|
*/
|
|
class NotFoundError extends TwirpError {
|
|
constructor(msg) {
|
|
super(TwirpErrorCode.NotFound, msg);
|
|
}
|
|
}
|
|
exports.NotFoundError = NotFoundError;
|
|
/**
|
|
* InvalidArgumentError constructor for the common InvalidArgument error. Can be
|
|
* used when an argument has invalid format, is a number out of range, is a bad
|
|
* option, etc).
|
|
*/
|
|
class InvalidArgumentError extends TwirpError {
|
|
constructor(argument, validationMsg) {
|
|
super(TwirpErrorCode.InvalidArgument, argument + " " + validationMsg);
|
|
this.withMeta("argument", argument);
|
|
}
|
|
}
|
|
exports.InvalidArgumentError = InvalidArgumentError;
|
|
/**
|
|
* RequiredArgumentError is a more specific constructor for InvalidArgument
|
|
* error. Should be used when the argument is required (expected to have a
|
|
* non-zero value).
|
|
*/
|
|
class RequiredArgumentError extends InvalidArgumentError {
|
|
constructor(argument) {
|
|
super(argument, "is required");
|
|
}
|
|
}
|
|
exports.RequiredArgumentError = RequiredArgumentError;
|
|
/**
|
|
* InternalError constructor for the common Internal error. Should be used to
|
|
* specify that something bad or unexpected happened.
|
|
*/
|
|
class InternalServerError extends TwirpError {
|
|
constructor(msg) {
|
|
super(TwirpErrorCode.Internal, msg);
|
|
}
|
|
}
|
|
exports.InternalServerError = InternalServerError;
|
|
/**
|
|
* InternalErrorWith makes an internal error, wrapping the original error and using it
|
|
* for the error message, and with metadata "cause" with the original error type.
|
|
* This function is used by Twirp services to wrap non-Twirp errors as internal errors.
|
|
* The wrapped error can be extracted later with err.cause()
|
|
*/
|
|
class InternalServerErrorWith extends InternalServerError {
|
|
constructor(err) {
|
|
super(err.message);
|
|
this.withMeta("cause", err.name);
|
|
this.withCause(err);
|
|
}
|
|
}
|
|
exports.InternalServerErrorWith = InternalServerErrorWith;
|
|
/**
|
|
* A standard BadRoute Error
|
|
*/
|
|
class BadRouteError extends TwirpError {
|
|
constructor(msg, method, url) {
|
|
super(TwirpErrorCode.BadRoute, msg);
|
|
this.withMeta("twirp_invalid_route", method + " " + url);
|
|
}
|
|
}
|
|
exports.BadRouteError = BadRouteError;
|
|
var TwirpErrorCode;
|
|
(function (TwirpErrorCode) {
|
|
// Canceled indicates the operation was cancelled (typically by the caller).
|
|
TwirpErrorCode["Canceled"] = "canceled";
|
|
// Unknown error. For example when handling errors raised by APIs that do not
|
|
// return enough error information.
|
|
TwirpErrorCode["Unknown"] = "unknown";
|
|
// InvalidArgument indicates client specified an invalid argument. It
|
|
// indicates arguments that are problematic regardless of the state of the
|
|
// system (i.e. a malformed file name, required argument, number out of range,
|
|
// etc.).
|
|
TwirpErrorCode["InvalidArgument"] = "invalid_argument";
|
|
// Malformed indicates an error occurred while decoding the client's request.
|
|
// This may mean that the message was encoded improperly, or that there is a
|
|
// disagreement in message format between the client and server.
|
|
TwirpErrorCode["Malformed"] = "malformed";
|
|
// DeadlineExceeded means operation expired before completion. For operations
|
|
// that change the state of the system, this error may be returned even if the
|
|
// operation has completed successfully (timeout).
|
|
TwirpErrorCode["DeadlineExceeded"] = "deadline_exceeded";
|
|
// NotFound means some requested entity was not found.
|
|
TwirpErrorCode["NotFound"] = "not_found";
|
|
// BadRoute means that the requested URL path wasn't routable to a Twirp
|
|
// service and method. This is returned by the generated server, and usually
|
|
// shouldn't be returned by applications. Instead, applications should use
|
|
// NotFound or Unimplemented.
|
|
TwirpErrorCode["BadRoute"] = "bad_route";
|
|
// AlreadyExists means an attempt to create an entity failed because one
|
|
// already exists.
|
|
TwirpErrorCode["AlreadyExists"] = "already_exists";
|
|
// PermissionDenied indicates the caller does not have permission to execute
|
|
// the specified operation. It must not be used if the caller cannot be
|
|
// identified (Unauthenticated).
|
|
TwirpErrorCode["PermissionDenied"] = "permission_denied";
|
|
// Unauthenticated indicates the request does not have valid authentication
|
|
// credentials for the operation.
|
|
TwirpErrorCode["Unauthenticated"] = "unauthenticated";
|
|
// ResourceExhausted indicates some resource has been exhausted, perhaps a
|
|
// per-user quota, or perhaps the entire file system is out of space.
|
|
TwirpErrorCode["ResourceExhausted"] = "resource_exhausted";
|
|
// FailedPrecondition indicates operation was rejected because the system is
|
|
// not in a state required for the operation's execution. For example, doing
|
|
// an rmdir operation on a directory that is non-empty, or on a non-directory
|
|
// object, or when having conflicting read-modify-write on the same resource.
|
|
TwirpErrorCode["FailedPrecondition"] = "failed_precondition";
|
|
// Aborted indicates the operation was aborted, typically due to a concurrency
|
|
// issue like sequencer check failures, transaction aborts, etc.
|
|
TwirpErrorCode["Aborted"] = "aborted";
|
|
// OutOfRange means operation was attempted past the valid range. For example,
|
|
// seeking or reading past end of a paginated collection.
|
|
//
|
|
// Unlike InvalidArgument, this error indicates a problem that may be fixed if
|
|
// the system state changes (i.e. adding more items to the collection).
|
|
//
|
|
// There is a fair bit of overlap between FailedPrecondition and OutOfRange.
|
|
// We recommend using OutOfRange (the more specific error) when it applies so
|
|
// that callers who are iterating through a space can easily look for an
|
|
// OutOfRange error to detect when they are done.
|
|
TwirpErrorCode["OutOfRange"] = "out_of_range";
|
|
// Unimplemented indicates operation is not implemented or not
|
|
// supported/enabled in this service.
|
|
TwirpErrorCode["Unimplemented"] = "unimplemented";
|
|
// Internal errors. When some invariants expected by the underlying system
|
|
// have been broken. In other words, something bad happened in the library or
|
|
// backend service. Do not confuse with HTTP Internal Server Error; an
|
|
// Internal error could also happen on the client code, i.e. when parsing a
|
|
// server response.
|
|
TwirpErrorCode["Internal"] = "internal";
|
|
// Unavailable indicates the service is currently unavailable. This is a most
|
|
// likely a transient condition and may be corrected by retrying with a
|
|
// backoff.
|
|
TwirpErrorCode["Unavailable"] = "unavailable";
|
|
// DataLoss indicates unrecoverable data loss or corruption.
|
|
TwirpErrorCode["DataLoss"] = "data_loss";
|
|
})(TwirpErrorCode = exports.TwirpErrorCode || (exports.TwirpErrorCode = {}));
|
|
// ServerHTTPStatusFromErrorCode maps a Twirp error type into a similar HTTP
|
|
// response status. It is used by the Twirp server handler to set the HTTP
|
|
// response status code. Returns 0 if the ErrorCode is invalid.
|
|
function httpStatusFromErrorCode(code) {
|
|
switch (code) {
|
|
case TwirpErrorCode.Canceled:
|
|
return 408; // RequestTimeout
|
|
case TwirpErrorCode.Unknown:
|
|
return 500; // Internal Server Error
|
|
case TwirpErrorCode.InvalidArgument:
|
|
return 400; // BadRequest
|
|
case TwirpErrorCode.Malformed:
|
|
return 400; // BadRequest
|
|
case TwirpErrorCode.DeadlineExceeded:
|
|
return 408; // RequestTimeout
|
|
case TwirpErrorCode.NotFound:
|
|
return 404; // Not Found
|
|
case TwirpErrorCode.BadRoute:
|
|
return 404; // Not Found
|
|
case TwirpErrorCode.AlreadyExists:
|
|
return 409; // Conflict
|
|
case TwirpErrorCode.PermissionDenied:
|
|
return 403; // Forbidden
|
|
case TwirpErrorCode.Unauthenticated:
|
|
return 401; // Unauthorized
|
|
case TwirpErrorCode.ResourceExhausted:
|
|
return 429; // Too Many Requests
|
|
case TwirpErrorCode.FailedPrecondition:
|
|
return 412; // Precondition Failed
|
|
case TwirpErrorCode.Aborted:
|
|
return 409; // Conflict
|
|
case TwirpErrorCode.OutOfRange:
|
|
return 400; // Bad Request
|
|
case TwirpErrorCode.Unimplemented:
|
|
return 501; // Not Implemented
|
|
case TwirpErrorCode.Internal:
|
|
return 500; // Internal Server Error
|
|
case TwirpErrorCode.Unavailable:
|
|
return 503; // Service Unavailable
|
|
case TwirpErrorCode.DataLoss:
|
|
return 500; // Internal Server Error
|
|
default:
|
|
return 0; // Invalid!
|
|
}
|
|
}
|
|
exports.httpStatusFromErrorCode = httpStatusFromErrorCode;
|
|
// IsValidErrorCode returns true if is one of the valid predefined constants.
|
|
function isValidErrorCode(code) {
|
|
return httpStatusFromErrorCode(code) != 0;
|
|
}
|
|
exports.isValidErrorCode = isValidErrorCode;
|