docs: function Entity(attributeValues, options) {
    var entity = this;
  
    /**
     * This is a read-only property to get the adapterName of an Entity instance.
     * @type {!string}
     * @readonly
     * @example
     * var myDefaultAdapterName = myEntity.adapterName;
     */
    this.adapterName = null;
    /**
     * This is a read-only property to get the adapter of an Entity instance.
     * @type {!module:back4app-entity/adapters.Adapter}
     * @readonly
     * @example
     * var myDefaultAdapter = myEntity.adapter;
     */
    this.adapter = null;
    /**
     * This is a read-only property to get the Entity Class of an instance.
     * @name module:back4app-entity/models.Entity#Entity
     * @type {!Class}
     * @readonly
     * @example
     * var myEntity = new MyEntity();
     * console.log(myEntity.Entity == MyEntity); // Logs "true"
     */
    if (!this.hasOwnProperty('Entity') || !this.Entity) {
      this.Entity = null;
    }
    /**
     * This is a read-only property to get the general Entity Class of an
     * instance. This is just an alias to this.Entity.General.
     * @type {!Class}
     * @readonly
     * @example
     * var MyEntity = Entity.specify('MyEntity');
     * var myEntity = new MyEntity();
     * console.log(myEntity.General == Entity); // Logs "true"
     */
    this.General = null;
    /**
     * This is a property to keep the information if the Entity instance is new
     * and was not saved in the storage yet.
     * @type {!boolean}
     * @example
     * console.log((new MyEntity()).isNew); // Logs "true"
     */
    this.isNew = null;
    /**
     * This is a read-only property to get the id of a new entity
     * instance. It is generated in according to UUID pattern type 4.
     * @type {!string}
     * @readonly
     * @example
     * var MyEntity = Entity.specify('MyEntity');
     * var myEntity = new MyEntity();
     * console.log(myEntity.id); // Logs a string id
     */
    this.id = null;
  
    var _attributeStorageValues = {};
    var _attributeIsSet = {};
  
    this.isDirty = isDirty;
    this.clean = clean;
  
    if (!this.hasOwnProperty('Entity') || !this.Entity) {
      Object.defineProperty(this, 'Entity', {
        value: Entity,
        enumerable: false,
        writable: false,
        configurable: false
      });
    }
  
    Object.defineProperty(this, 'General', {
      get: function () {
        return this.Entity.General;
      },
      set: function () {
        throw new Error(
          'General property of an Entity instance cannot be changed'
        );
      },
      enumerable: false,
      configurable: false
    });
  
    Object.defineProperty(this, 'adapterName', {
      value: this.Entity.adapterName,
      writable: false,
      enumerable: false,
      configurable: false
    });
  
    Object.defineProperty(this, 'adapter', {
      value: this.Entity.adapter,
      writable: false,
      enumerable: false,
      configurable: false
    });
  
    var _isNew = true;
    Object.defineProperty(this, 'isNew', {
      get: function () {
        return _isNew;
      },
      set: function (value) {
        expect(value).to.be.a(
          'boolean',
          'Invalid value when setting isNew value (it has to be a boolean)'
        );
  
        _isNew = value;
      },
      enumerable: false,
      configurable: false
    });
  
    Object.defineProperty(this, 'isDirty', {
      value: isDirty,
      writable: false,
      enumerable: false,
      configurable: false
    });
  
    Object.defineProperty(this, 'clean', {
      value: clean,
      writable: false,
      enumerable: false,
      configurable: false
    });
  
    expect(this).to.be.an(
      'object',
      'The Entity\'s constructor can be only invoked from specialized ' +
      'classes\' constructors'
    );
  
    expect(this.constructor).to.be.a(
      'function',
      'The Entity\'s constructor can be only invoked from specialized ' +
      'classes\' constructors'
    );
  
    expect(this.constructor).to.not.equal(
      Entity,
      'The Entity is an abstract class and cannot be directly initialized'
    );
  
    expect(this).to.be.instanceof(
      Entity,
      'The Entity\'s constructor can be only invoked from specialized ' +
      'classes\' constructors'
    );
  
    expect(arguments).to.have.length.below(
      3,
      'Invalid arguments length when creating "' +
      this.Entity.specification.name +
      '" (it has to be passed less than 3 arguments)');
  
    var _isNewSet = false;
    var _cleanSet = false;
  
    if (options) {
      expect(options).to.be.an(
        'object',
        'Invalid argument "options" when creating a new "' +
        this.Entity.specification.name + '" (it has to be an object)'
      );
  
      if (options.hasOwnProperty('isNew')) {
        expect(options.isNew).to.be.a(
          'boolean',
          'Invalid argument "options.isNew" when creating a new "' +
          this.Entity.specification.name + '" (it has to be a boolean)'
        );
  
        this.isNew = options.isNew;
  
        if (options.isNew) {
          _isNewSet = true;
        }
      }
  
      if (options.hasOwnProperty('clean')) {
        expect(options.clean).to.be.a(
          'boolean',
          'Invalid argument "options.clean" when creating a new "' +
          this.Entity.specification.name + '" (it has to be a boolean)'
        );
  
        if (options.clean) {
          _cleanSet = true;
        }
      }
    }
  
    if (attributeValues) {
      expect(attributeValues).to.be.an(
        'object',
        'Invalid argument "attributeValues" when creating a new "' +
        this.Entity.specification.name + '" (it has to be an object)'
      );
  
      if (!_isNewSet && attributeValues.hasOwnProperty('id')) {
        this.isNew = false;
      }
    }
  
    var attributes = this.Entity.attributes;
  
    for (var attribute in attributeValues) {
      expect(
        attributes,
        'Invalid property "' + attribute + '" when creating a new "' +
        this.Entity.specification.name + '" (it does not exist)'
      ).to.include.keys(attribute);
    }
  
    for (attribute in attributes) {
      var attributeValue = null;
  
      if (attributeValues && attributeValues.hasOwnProperty(attribute)) {
        attributeValue = attributeValues[attribute];
        if (!this.isNew) {
          _attributeStorageValues[attribute] = attributeValue;
        }
      }
  
      _defineAttribute(attribute, attributeValue);
    }
  
    for (attribute in attributes) {
      if (!_cleanSet || attribute === 'id') {
        if (this[attribute] === null && attributes[attribute].default !== null) {
          this[attribute] = attributes[attribute].getDefaultValue(this);
        }
      }
    }
  
    var regex = '^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-' +
      '[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$';
    expect(new RegExp(regex).test(this.id)).to.equal(
      true,
      'Invalid property "id" when creating a new "' +
      this.Entity.specification.name + '" (it has to be a valid uuid)'
    );
  
    if (!_cleanSet) {
      if (this.permissions) {
        for (var propertyName in this.permissions) {
          try {
            var property = this.permissions[propertyName];
            this.permissions[propertyName] = {};
            if (property.read) {
              this.permissions[propertyName].read = true;
            }
            if (property.write) {
              this.permissions[propertyName].write = true;
            }
          } catch (e) {
            e.message = e.message + ' ("' + propertyName + '" is invalid)';
            throw e;
          }
        }
      } else {
        this.permissions = null;
      }
      _attributeStorageValues.permissions = this.permissions;
    }
  
    Object.defineProperty(this, 'id', {
      value: this.id,
      enumerable: true,
      writable: false,
      configurable: false
    });
  
    Object.defineProperty(this, 'permissions', {
      value: !_cleanSet ? this.permissions : null,
      enumerable: true,
      writable: true,
      configurable: false
    });
  
    function _defineAttribute(attributeName, attributeValue) {
      _attributeIsSet[attributeName] = false;
  
      Object.defineProperty(entity, attributeName, {
        get: function () {
          if (
            _cleanSet &&
            !_attributeStorageValues.hasOwnProperty(attributeName) &&
            !_attributeIsSet[attributeName] &&
            attributeName !== 'id'
          ) {
            throw new errors.NotFetchedError(
              entity.Entity.specification.name,
              attributeName,
              null
            );
          } else {
            return attributeValue;
          }
        },
        set: function (value) {
          _attributeIsSet[attributeName] = true;
          attributeValue = value;
        },
        enumerable: true,
        configurable: attributeName === 'id' || attributeName === 'permissions'
      });
    }
  
    /**
     * Checks if an Entity attribute is dirty.
     * @name module:back4app-entity/models.Entity#isDirty
     * @function
     * @param {?string} [attribute] The name of the attribute to be checked. If no
     * attribute is passed, all attributes will be checked.
     * @returns {boolean} True if is dirty and false otherwise.
     * @example
     * console.log(myEntity.isDirty('myAttribute')); // Validates attribute
     *                                               // "myAttribute" of
     *                                               // Entity "myEntity"
     * @example
     * console.log(myEntity.isDirty()); // Checks all attributes of "myEntity"
     */
    function isDirty(attribute) {
      expect(arguments).to.have.length.below(
        2,
        'Invalid arguments length when checking if an Entity attribute is ' +
        'dirty (it has to be passed less than 2 arguments)'
      );
  
      var attributes = this.Entity.attributes;
  
      if (attribute) {
        expect(attribute).to.be.a(
          'string',
          'Invalid argument "attribute" when checking if an Entity attribute ' +
          'is dirty (it has to be a string)'
        );
  
        expect(attributes).to.have.ownProperty(
          attribute,
          'Invalid argument "attribute" when checking an Entity attribute ' +
          'is dirty (this attribute does not exist in the Entity)'
        );
  
        var newAttributes = {};
        newAttributes[attribute] = attributes[attribute];
        attributes = newAttributes;
      }
  
      if (this.isNew) {
        return true;
      }
  
      for (var attributeName in attributes) {
        if (_cleanSet) {
          if (_attributeIsSet[attributeName]) {
            if (
              !_attributeStorageValues.hasOwnProperty(attributeName) ||
              _attributeStorageValues[attributeName] !== this[attributeName]
            ) {
              return true;
            }
          }
        } else {
          if (
            !_attributeStorageValues.hasOwnProperty(attributeName) ||
            _attributeStorageValues[attributeName] !== this[attributeName]
          ) {
            return true;
          }
        }
      }
  
      return false;
    }
  
    /**
     * Cleans an Entity attribute.
     * @name module:back4app-entity/models.Entity#clean
     * @function
     * @param {?string} [attribute] The name of the attribute to be cleaned. If no
     * attribute is passed, all attributes will be cleaned.
     * @example
     * myEntity.clean('myAttribute'); // Cleans attribute "myAttribute" of
     *                                // Entity "myEntity"
     * @example
     * myEntity.clean();              // Cleans all attributes of "myEntity"
     */
    function clean(attribute) {
      expect(arguments).to.have.length.below(
        2,
        'Invalid arguments length when cleaning an Entity attribute (it has ' +
        'to be passed less than 2 arguments)'
      );
  
      var attributes = this.Entity.attributes;
  
      if (attribute) {
        expect(attribute).to.be.a(
          'string',
          'Invalid argument "attribute" when cleaning an Entity attribute (it ' +
          'has to be a string)'
        );
  
        expect(attributes).to.have.ownProperty(
          attribute,
          'Invalid argument "attribute" when cleaning an Entity attribute ' +
          '(this attribute does not exist in the Entity)'
        );
  
        var newAttributes = {};
        newAttributes[attribute] = attributes[attribute];
        attributes = newAttributes;
      }
  
      for (var attributeName in attributes) {
        _attributeStorageValues[attributeName] = this[attributeName];
        _attributeIsSet[attributeName] = false;
      }
    }
  
    Object.preventExtensions(this);
  }