Home Reference Source

packages/skygear-core/lib/error.js

/**
 * Copyright 2015 Oursky Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import _ from 'lodash';

/**
 * Enum for error codes
 * @readonly
 * @enum {number}
 */
export const ErrorCodes = {
  NotAuthenticated: 101,
  PermissionDenied: 102,
  AccessKeyNotAccepted: 103,
  AccessTokenNotAccepted: 104,
  InvalidCredentials: 105,
  InvalidSignature: 106,
  BadRequest: 107,
  InvalidArgument: 108,
  Duplicated: 109,
  ResourceNotFound: 110,
  NotSupported: 111,
  NotImplemented: 112,
  ConstraintViolated: 113,
  IncompatibleSchema: 114,
  AtomicOperationFailure: 115,
  PartialOperationFailure: 116,
  UndefinedOperation: 117,
  PluginUnavailable: 118,
  PluginTimeout: 119,
  RecordQueryInvalid: 120,
  PluginInitializing: 121,
  ResponseTimeout: 122,
  DeniedArgument: 123,
  RecordQueryDenied: 124,
  NotConfigured: 125,
  PasswordPolicyViolated: 126,
  UserDisabled: 127,
  VerificationRequired: 128,
  AssetSizeTooLarge: 129,
  UnexpectedError: 10000
};

function codeToString(code) {
  return _.findKey(ErrorCodes, function (value) {
    return code === value;
  });
}

/**
 * SkygearError is an error object containing information of an error
 * occurred.
 *
 * @example
 * let err = new SkygearError(
 *   'Unable to parse data',
 *   UnexpectedError,
 *   { content: 'BADDATA' }
 * );
 */
export class SkygearError extends Error {
  /**
   * Creates a SkygearError.
   * @param {string} message - an error message
   * @param {number} code - a code for the error condition
   * @param {Object} info - more information about the error
   */
  constructor(message, code, info) {
    super(message);
    this.message = message;
    this.code = code || ErrorCodes.UnexpectedError;
    this.info = info || null;
  }

  /**
   * Description of the error
   *
   * @return {String} description
   */
  toString() {
    return `SkygearError: ${this.message}`;
  }

  /**
   * Description of the error code of the error
   *
   * @return {String} description
   */
  /* eslint-disable complexity */
  toLocaleString() {
    switch (this.code) {
    case ErrorCodes.NotAuthenticated:
      return 'You have to be authenticated to perform this operation.';
    case ErrorCodes.PermissionDenied:
    case ErrorCodes.AccessKeyNotAccepted:
    case ErrorCodes.AccessTokenNotAccepted:
      return 'You are not allowed to perform this operation.';
    case ErrorCodes.InvalidCredentials:
      return 'You are not allowed to log in because '
        + 'the credentials you provided are not valid.';
    case ErrorCodes.InvalidSignature:
    case ErrorCodes.BadRequest:
      return 'The server is unable to process the request.';
    case ErrorCodes.InvalidArgument:
      return 'The server is unable to process the data.';
    case ErrorCodes.Duplicated:
      return 'This request contains duplicate of an existing '
        + 'resource on the server.';
    case ErrorCodes.ResourceNotFound:
      return 'The requested resource is not found.';
    case ErrorCodes.NotSupported:
      return 'This operation is not supported.';
    case ErrorCodes.NotImplemented:
      return 'This operation is not implemented.';
    case ErrorCodes.ConstraintViolated:
    case ErrorCodes.IncompatibleSchema:
    case ErrorCodes.AtomicOperationFailure:
    case ErrorCodes.PartialOperationFailure:
      return 'A problem occurred while processing this request.';
    case ErrorCodes.UndefinedOperation:
      return 'The requested operation is not available.';
    case ErrorCodes.PluginInitializing:
    case ErrorCodes.PluginUnavailable:
      return 'The server is not ready yet.';
    case ErrorCodes.PluginTimeout:
      return 'The server took too long to process.';
    case ErrorCodes.RecordQueryInvalid:
      return 'A problem occurred while processing this request.';
    case ErrorCodes.ResponseTimeout:
      return 'The server timed out while processing the request.';
    case ErrorCodes.DeniedArgument:
      return 'The server is unable to process the data.';
    case ErrorCodes.RecordQueryDenied:
      return 'You are not allowed to perform this operation.';
    case ErrorCodes.NotConfigured:
      return 'The server is not configured for this operation.';
    case ErrorCodes.PasswordPolicyViolated:
      return 'The password does not meet policy requirement.';
    case ErrorCodes.UserDisabled:
      if (this.info && this.info.message) {
        return 'The user is disabled: ${this.info.message}';
      } else {
        return 'The user is disabled.';
      }
    default:
      return 'An unexpected error has occurred.';
    }
  }
  /* eslint-enable complexity */

  /**
   * Serializes SkyearError to a JSON object.
   *
   * @return {Object} the JSON object
   */
  toJSON() {
    const result = {
      name: codeToString(this.code),
      code: this.code,
      message: this.message
    };
    if (this.info) {
      result.info = this.info;
    }
    return result;
  }

  /**
   * Constructs a new SkyearError object from JSON object.
   *
   * @param {Object} attrs - the JSON object
   * @param {String} attrs.message - an error message
   * @param {Number} [attrs.code] - a code for the error condition
   * @param {Object} [attrs.info] - more information about the error
   * @return {SkyearError} the created SkyearError object
   */
  static fromJSON(attrs) {
    return new SkygearError(
      attrs.message,
      attrs.code || ErrorCodes.UnexpectedError,
      attrs.info || null
    );
  }
}