UNPKG

fauna

Version:

A driver to query Fauna databases in browsers, Node.js, and other Javascript runtimes

381 lines (340 loc) 10.8 kB
import type { ConstraintFailure, QueryFailure, QueryInfo, QueryStats, QueryValue, } from "./wire-protocol"; /** * A common error base class for all other errors. */ export abstract class FaunaError extends Error { constructor(...args: any[]) { super(...args); } } /** * An error representing a query failure returned by Fauna. */ export class ServiceError extends FaunaError { /** * The HTTP Status Code of the error. */ readonly httpStatus?: number; /** * A code for the error. Codes indicate the cause of the error. * It is safe to write programmatic logic against the code. They are * part of the API contract. */ readonly code: string; /** * Details about the query sent along with the response */ readonly queryInfo?: QueryInfo; /** * A machine readable description of any constraint failures encountered by the query. * Present only if this query encountered constraint failures. */ readonly constraint_failures?: Array<ConstraintFailure>; constructor(failure: QueryFailure, httpStatus?: number) { super(failure.error.message); // Maintains proper stack trace for where our error was thrown (only available on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, ServiceError); } this.name = "ServiceError"; this.code = failure.error.code; this.httpStatus = httpStatus; const info: QueryInfo = { txn_ts: failure.txn_ts, summary: failure.summary, query_tags: failure.query_tags, stats: failure.stats, }; this.queryInfo = info; this.constraint_failures = failure.error.constraint_failures; } } /** * An error response that is the result of the query failing during execution. * QueryRuntimeError's occur when a bug in your query causes an invalid execution * to be requested. * The 'code' field will vary based on the specific error cause. */ export class QueryRuntimeError extends ServiceError { constructor(failure: QueryFailure, httpStatus?: number) { super(failure, httpStatus); if (Error.captureStackTrace) { Error.captureStackTrace(this, QueryRuntimeError); } this.name = "QueryRuntimeError"; // TODO trace, txn_ts, and stats not yet returned for QueryRuntimeError // flip to check for those rather than a specific code. } } /** * An error due to a "compile-time" check of the query * failing. */ export class QueryCheckError extends ServiceError { constructor(failure: QueryFailure, httpStatus?: number) { super(failure, httpStatus); if (Error.captureStackTrace) { Error.captureStackTrace(this, QueryCheckError); } this.name = "QueryCheckError"; } } /** * An error due to an invalid request to Fauna. Either the request body was not * valid JSON or did not conform to the API specification */ export class InvalidRequestError extends ServiceError { constructor(failure: QueryFailure, httpStatus?: number) { super(failure, httpStatus); if (Error.captureStackTrace) { Error.captureStackTrace(this, InvalidRequestError); } this.name = "InvalidRequestError"; } } /** * A runtime error due to failing schema constraints. */ export class ConstraintFailureError extends ServiceError { /** * The list of constraints that failed. */ readonly constraint_failures: Array<ConstraintFailure>; constructor( failure: QueryFailure & { error: { constraint_failures: Array<ConstraintFailure> }; }, httpStatus?: number, ) { super(failure, httpStatus); if (Error.captureStackTrace) { Error.captureStackTrace(this, QueryCheckError); } this.name = "ConstraintFailureError"; this.constraint_failures = failure.error.constraint_failures; } } /** * An error due to calling the FQL `abort` function. */ export class AbortError extends ServiceError { /** * The user provided value passed to the originating `abort()` call. * Present only when the query encountered an `abort()` call, which is denoted * by the error code `"abort"` */ readonly abort: QueryValue; constructor( failure: QueryFailure & { error: { abort: QueryValue } }, httpStatus?: number, ) { super(failure, httpStatus); if (Error.captureStackTrace) { Error.captureStackTrace(this, QueryCheckError); } this.name = "AbortError"; this.abort = failure.error.abort; } } /** * AuthenticationError indicates invalid credentials were * used. */ export class AuthenticationError extends ServiceError { constructor(failure: QueryFailure, httpStatus?: number) { super(failure, httpStatus); if (Error.captureStackTrace) { Error.captureStackTrace(this, AuthenticationError); } this.name = "AuthenticationError"; } } /** * AuthorizationError indicates the credentials used do not have * permission to perform the requested action. */ export class AuthorizationError extends ServiceError { constructor(failure: QueryFailure, httpStatus?: number) { super(failure, httpStatus); if (Error.captureStackTrace) { Error.captureStackTrace(this, AuthorizationError); } this.name = "AuthorizationError"; } } /** * An error due to a contended transaction. */ export class ContendedTransactionError extends ServiceError { constructor(failure: QueryFailure, httpStatus?: number) { super(failure, httpStatus); if (Error.captureStackTrace) { Error.captureStackTrace(this, InvalidRequestError); } this.name = "ContendedTransactionError"; } } /** * ThrottlingError indicates some capacity limit was exceeded * and thus the request could not be served. */ export class ThrottlingError extends ServiceError { constructor(failure: QueryFailure, httpStatus?: number) { super(failure, httpStatus); if (Error.captureStackTrace) { Error.captureStackTrace(this, ThrottlingError); } this.name = "ThrottlingError"; } } /** * A failure due to the query timeout being exceeded. * * This error can have one of two sources: * 1. Fauna is behaving expectedly, but the query timeout provided was too * aggressive and lower than the query's expected processing time. * 2. Fauna was not available to service the request before the timeout was * reached. * * In either case, consider increasing the `query_timeout_ms` configuration for * your client. */ export class QueryTimeoutError extends ServiceError { /** * Statistics regarding the query. * * TODO: Deprecate this `stats` field. All `ServiceError`s already provide * access to stats through `queryInfo.stats` */ readonly stats?: QueryStats; constructor(failure: QueryFailure, httpStatus?: number) { super(failure, httpStatus); if (Error.captureStackTrace) { Error.captureStackTrace(this, QueryTimeoutError); } this.name = "QueryTimeoutError"; this.stats = failure.stats; } } /** * ServiceInternalError indicates Fauna failed unexpectedly. */ export class ServiceInternalError extends ServiceError { constructor(failure: QueryFailure, httpStatus?: number) { super(failure, httpStatus); if (Error.captureStackTrace) { Error.captureStackTrace(this, ServiceInternalError); } this.name = "ServiceInternalError"; } } /** * An error representing a failure internal to the client, itself. * This indicates Fauna was never called - the client failed internally * prior to sending the request. */ export class ClientError extends FaunaError { constructor(message: string, options?: { cause: any }) { super(message, options); // Maintains proper stack trace for where our error was thrown (only available on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, ClientError); } this.name = "ClientError"; } } /** * An error thrown if you try to call the client after it has been closed. */ export class ClientClosedError extends FaunaError { constructor(message: string, options?: { cause: any }) { super(message, options); // Maintains proper stack trace for where our error was thrown (only available on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, ClientClosedError); } this.name = "ClientClosedError"; } } /** * An error representing a failure due to the network. * This indicates Fauna was never reached. */ export class NetworkError extends FaunaError { constructor(message: string, options: { cause: any }) { super(message, options); // Maintains proper stack trace for where our error was thrown (only available on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, NetworkError); } this.name = "NetworkError"; } } /** * An error representing a HTTP failure - but one not directly * emitted by Fauna. */ export class ProtocolError extends FaunaError { /** * The HTTP Status Code of the error. */ readonly httpStatus: number; constructor(error: { message: string; httpStatus: number }) { super(error.message); // Maintains proper stack trace for where our error was thrown (only available on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, ProtocolError); } this.name = "ProtocolError"; this.httpStatus = error.httpStatus; } } export const getServiceError = ( failure: QueryFailure, httpStatus?: number, ): ServiceError => { const failureCode = failure.error.code; switch (failureCode) { case "invalid_query": return new QueryCheckError(failure, httpStatus); case "invalid_request": return new InvalidRequestError(failure, httpStatus); case "abort": if (failure.error.abort !== undefined) { return new AbortError( failure as QueryFailure & { error: { abort: QueryValue } }, httpStatus, ); } break; case "constraint_failure": if (failure.error.constraint_failures !== undefined) { return new ConstraintFailureError( failure as QueryFailure & { error: { constraint_failures: Array<ConstraintFailure> }; }, httpStatus, ); } break; case "unauthorized": return new AuthenticationError(failure, httpStatus); case "forbidden": return new AuthorizationError(failure, httpStatus); case "contended_transaction": return new ContendedTransactionError(failure, httpStatus); case "limit_exceeded": return new ThrottlingError(failure, httpStatus); case "time_out": return new QueryTimeoutError(failure, httpStatus); case "internal_error": return new ServiceInternalError(failure, httpStatus); } return new QueryRuntimeError(failure, httpStatus); };