Manual Reference Source Test

src/services/UserService.js

import { deprecate } from 'core-decorators';
import Client from '../Client';
import JsonApiService from './JsonApiService';
import AwardedBadge from '../helpers/gamification/AwardedBadge';
import Schedule from '../helpers/reminder/Schedule';
import User from '../helpers/user/User';
import Profile from '../helpers/user/Profile';
import Session from '../helpers/user/Session';
import Entry from '../helpers/ediary/Entry';
import Notification from '../helpers/Notification';
import {
  hasAttribute,
  hasToken,
  isA,
  isRequired,
  isValid,
  validate,
} from '../utilities/ValidationUtility';
import { isEmail } from '../utilities/FormatUtility';

/**
 * Service handling User calls with specific endpoints.
 *
 * In order to manage user-specific data, access, and info, our SDK provides a user sign-in as well
 * as a guest sign-in. With the plugin, as soon as a user launches your app, they will
 * automatically be signed in as a guest. After you build a login page/UI, your users can enter
 * their credentials to sign in to their own account.
 */
class UserService extends JsonApiService {
  /**
   * Update type to be mobile_users
   */
  constructor(type = undefined) {
    super(type || Client.instance.user.type || 'mobile_users',
      {
        title: 'UserService',
        obj: 'user',
        tokenRequired: ['getChildren']
      });

    /** @type {String} - The type of the default data */
    this.type = type || Client.instance.user.type; // mobile_users or users
  }

  /**
   * Get and Login Session From Response
   *
   * @private
   * @param   {Session|User} response   - Session or User object from server with authentication token and user information
   * @param   {Device}        device    - Mobile user's device
   * @return  {Session}                 - Current logged in session
   */
  _loginGetSession(response, device = {}) {
    let session = new Session();
    if (response.type === 'mobile_users') {
      session.user = response;
      if (session.user.devices && device.id) {
        Client.instance.device = session.user.devices.find(obj => obj.id === device.id);
        if (Client.instance.device) {
          session.authenticationToken = Client.instance.device.accessToken;
        }
      }
    } else if (response.type === 'user_sessions') {
      session = response;
    }
    Client.instance.user = session.user;
    Client.instance.authToken = session.authenticationToken;
    return session;
  }

  /**
   * Get and Login User From Response
   *
   * @private
   * @param   {Session|User}  response  - Session or User object from server with authentication token and user information
   * @param   {Device}        device    - Mobile user's device
   * @return  {User}                    - Current logged in user
   */
  _loginGetUser(response, device = {}) {
    let user = response;
    if (response.type === 'mobile_users') {
      if (user.devices && device.id) {
        Client.instance.device = user.devices.find(obj => obj.id === device.id);
        if (Client.instance.device) {
          Client.instance.authToken = Client.instance.device.accessToken;
        }
      }
    } else if (response.type === 'user_sessions') {
      ({ user } = response);
      Client.instance.authToken = response.authenticationToken;
    }
    Client.instance.user = user;
    return user;
  }

  /**
   * Allows a user to accept an invitation given a device and token
   *
   * @throws {Clinical6Error}                             - If missing required parameters
   * @param  {!String} token                              - Token from request password reset email or accepting invitation email
   * @param  {String}  [options]                          - Additional options for accepting invitations
   * @param  {Boolean} [options.anti_spam_accepted]       - If the anti-spam policy was accepted by the user.
   * @param  {Object}  [options.device]                   - Accept invitation with this device
   * @param  {Number}  [options.device.id]                - Must be the device id
   * @param  {String}  [options.email]                    - Email, if it is set with the invitation acceptance page.
   * @param  {String}  [options.password]                 - Password, if it is set with the invitation acceptance page.
   * @param  {Boolean} [options.privacy_policy_accepted]  - If the privacy policy was accepted by the user.
   * @param  {Boolean} [options.terms_of_use_accepted]    - If the terms of use accepted by the user.
   * @return {Promise<User>}                              - Session object is returned.  For Mobile Users, this is a local Session object
   *                                                        with the authentication token and the user object inserted locally.
   *
   * @example <caption>Mobile Users</caption>
   * import { mobileUserService } from 'clinical6';
   * mobileUserService.acceptInvitation('1afw00f...', {
   *    password: 'myPassword!@',
   *    terms_of_use_accepted: true,
   *    privacy_policy_accepted: true,
   *    anti_spam_accepted: true,
   *    device: { id: 15 }
   * }).then(user => console.log(user));
   *
   * @example <caption>Admin Users</caption>
   * import { userService } from 'clinical6';
   * userService.acceptInvitation('1afw00f...', {
   *    password: 'myPassword!@',
   *    terms_of_use_accepted: true,
   *    privacy_policy_accepted: true,
   *    anti_spam_accepted: true
   * }).then(session => console.log(session.user));
   */
  async acceptInvitation(token, options = {}) {
    validate('UserService.acceptInvitation',
      isRequired({ token }),
      isA({ token }, 'string'));
    const data = { type: 'invitations' };

    // add attributes
    data.attributes = { invitation_token: token };
    if (options.email) {
      validate('UserService.acceptInvitation',
        isValid(isEmail(options.email), `email is not a valid email`));
      data.attributes.email = options.email;
    }
    if (options.password) {
      data.attributes.password = options.password;
    }

    if (options.terms_of_use_accepted) {
      data.attributes.terms_of_use_accepted = options.terms_of_use_accepted;
    }

    if (options.privacy_policy_accepted) {
      data.attributes.privacy_policy_accepted = options.privacy_policy_accepted;
    }

    if (options.anti_spam_accepted) {
      data.attributes.anti_spam_accepted = options.anti_spam_accepted;
    }

    // add relationships (if any)
    const device = options.device || Client.instance.device;
    if (options.device || Client.instance.device.id) {
      validate('UserService.acceptInvitation',
        hasAttribute({ device }, 'id'));
      data.relationships = {
        devices: {
          data: {
            type: 'devices',
            id: device.id
          }
        }
      };
    }

    return super.update(data, { url: `/v3/${this.type}/invitation`, cacheMode: 'networkOnly' })
      .then((response => this._loginGetUser(response, device)));
  }

  /**
   * Sets the pin value for an invited user.
   *
   * @throws {Clinical6Error}                 - If missing token or missing required parameters
   * @param  {Object}  json                   - The JSON value with the attributes to provide for
   *                                            set pin.
   * @param  {!String} json.pin               - The PIN
   * @param  {!String} json.pin_confirmation  - The PIN Confirmation
   * @param  {!String} json.invitation_token  - The invitation token
   * @return {Promise}                        - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Accept Invitation</caption>
   * import { userService } from 'clinical6';
   * userService.acceptInvitationPinV2({
   *   "pin": "123456",
   *   "pin_confirmation": "123456",
   *   "invitation_token": "12345678910"
   * });
   *
   * @example <caption>Success Response</caption>
   * {
   *   "status": "ok"
   * }
   *
   * @example <caption>Failure, Password and confirmation do not match</caption>
   * {
   *   "error_detail": "Review the new_password and new_password_confirmation parameters",
   *   "friendly_error": "Password and confirmation do not match",
   *   "internal_code": 50362,
   *   "more_info": "",
   *   "status": "fail",
   *   "message": "Password and confirmation do not match"
   * }
   *
   * @example <caption>Failure, No user found with the specified invitation_token</caption>
   * {
   *   "error_detail": {
   *     "invitation_token": [
   *       "invalid_value"
   *     ]
   *   },
   *   "friendly_error": "No user found with the specified invitation_token",
   *   "internal_code": 50364,
   *   "more_info": "",
   *   "status": "fail",
   *   "message": "No user found with the specified invitation_token"
   * }
   *
   * @example <caption>Failure, Pin already exists</caption>
   * {
   *   "error_detail": {
   *     "password": [
   *       "already_exists"
   *     ]
   *   },
   *   "friendly_error": "Pin already exists",
   *   "internal_code": 50374,
   *   "more_info": "",
   *   "status": "fail",
   *   "message": "Pin already exists"
   * }
   *
   * @deprecated
   */
  @deprecate
  acceptInvitationPinV2(json) {
    validate('UserService.acceptInvitationPinV2',
      hasToken(),
      isRequired({ json }),
      hasAttribute({ json }, 'pin'),
      hasAttribute({ json }, 'pin_confirmation'),
      hasAttribute({ json }, 'invitation_token'));

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/${this.type}/accept_invitation`, 'put', json).then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * Add Awarded Badge
   *
   * @throws {Clinical6Error}                         - If missing token or missing required parameters
   * @param  {!Object}        awardedBadge            - The user, must have id
   * @param  {!Object}        awardedBadge.attributes - Hashmap to add for award badge
   * @param  {!User}          awardedBadge.user       - The user, must have id
   * @param  {!Number}        awardedBadge.user.id    - The user id
   * @param  {!Badge}         awardedBadge.badge      - The user, must have id
   * @param  {!Number}        awardedBadge.badge.id   - The user id
   * @return {Promise<AwardedBadge>}                  - Promise object that returns one object or hashmap
   *
   * @example
   * import { userService, AwardedBadge, Badge, User } from 'clinical6';
   * const awardedBadge = new AwardedBadge({ updatedAt: '2018-06-02' });
   * awardedBadge.awardee = new User({ id: 5, ... });
   * awardedBadge.badge = new Badge({ id: 5, ... });
   *
   * // You can add an awarded badge using the `addAwardedBadge` method
   * userService.addAwardedBadge(awardedBadge).then(result => console.log(result));
   *
   * // Additionally, you can do the same thing using `User.addAwardedBadge()`
   * user.addAwardedBadge(awardedBadge).then(result => console.log(result));
   */
  async addAwardedBadge(awardedBadge) {
    validate('UserService.addAwardedBadge',
      hasToken(),
      isRequired({ awardedBadge }));
    awardedBadge = (awardedBadge instanceof AwardedBadge) ? awardedBadge : new AwardedBadge(awardedBadge);
    if (!awardedBadge.awardee) {
      awardedBadge.awardee = Client.instance.user;
    }
    validate('UserService.addAwardedBadge',
      hasAttribute({ awardedBadge }, 'awardee'),
      hasAttribute({ awardedBadge }, 'badge'));
    validate('UserService.addAwardedBadge',
      hasAttribute({
        awardee: awardedBadge.awardee,
        badge: awardedBadge.badge,
      }, 'id'));

    const url = `/v3/${this.type}/${awardedBadge.awardee.id}/awarded_badges`;
    return super.insert(awardedBadge.toJSON(), { url, cacheMode: 'networkOnly' });
  }

  /**
   * Add Schedule
   *
   * @throws {Clinical6Error}                           - If missing token or missing required parameters
   * @param  {!Object}        schedule                  - The user, must have id
   * @param  {!Object}        schedule.attributes       - Hashmap to add for a schedule
   * @param  {!User}          [schedule.mobileUser]     - The user, must have id
   * @param  {!Number}        [schedule.mobileUser.id]  - The user id
   * @return {Promise<Schedule>}                        - Promise object that returns one object or hashmap
   *
   * @example
   * import { userService, Schedule, User } from 'clinical6';
   * const schedule = new Schedule({ updatedAt: '2018-06-02' });
   *
   * // You can add a schedule using the `addSchedule` method
   * userService.addSchedule(schedule).then(result => console.log(result));
   *
   * // Additionally, you can do the same thing using `User.addSchedule()`
   * user.addSchedule(schedule).then(result => console.log(result));
   */
  async addSchedule(schedule) {
    validate('UserService.addSchedule',
      hasToken(),
      isRequired({ schedule }));
    const user = schedule.mobileUser || Client.instance.user;
    validate('UserService.addSchedule',
      isRequired({ user }),
      hasAttribute({ user }, 'id'));

    schedule = (schedule instanceof Schedule) ? schedule : new Schedule(schedule);

    const url = `/v3/${this.type}/${user.id}/scheduler/personalized_rule_schedules`;
    return super.insert(schedule, { url, cacheMode: 'networkOnly' });
  }

  /**
   * Confirms user with token
   *
   * @throws {Clinical6Error}     - If missing required parameters
   * @param  {String} token       - Token to confirm user with
   * @param  {String} [cacheMode] - Override the caching method for this call
   * @return {Promise}            - Promise object that returns success or failure
   *
   * @example
   * import { userService.confirm('1afji13jiifjadif0ijadf');
   */
  async confirm(token, cacheMode = 'networkOnly') {
    validate('UserService.confirm',
      isRequired({ token }),
      isA({ token }, 'string'));

    const url = `/v3/${this.type}/confirmation?confirmation_token=${token}`;

    return super.get({ }, { url, cacheMode });
  }

  /**
   * @override
   * Call a PATCH request on the main obj.type expecting JSON API information.
   *
   * @param  {User}     user        - User object in which to update
   * @param  {String}   [cacheMode] - Override the caching method for this call
   * @return {Promise}              - Message
   *
   * @example
   * import { mobileUserService } from 'clinical6';
   *
   * // You will be able to delete a User using `delete`.
   * mobileUserService.delete({ id: 23 });
   * // This will also clear the local storage of this type and id
   */
  async delete(user, cacheMode = undefined) {
    validate('UserService.delete',
      hasToken(),
      isRequired({ user }));
    return super.delete(user, { cacheMode });
  }

  /**
   * Removes a related mobile user.
   *
   * @throws {Clinical6Error} - If missing token or missing required parameters
   * @param  {Number} id      - The ID value of the related mobile user to remove.
   * @return {Promise}        - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Delete User</caption>
   * import { userService } from 'clinical6';
   * userService.deleteRelated(32);
   *
   * @example <caption>Success Response</caption>
   * {
   *   "invited": []
   * }
   *
   * @example <caption>Failure, Mobile User Not Found</caption>
   * {
   *   "error_detail": {
   *     "id": "invalid_value"
   *   },
   *   "friendly_error": "Mobile User not found using the value:id",
   *   "internal_code": 50364,
   *   "more_info": "",
   *   "status": "fail",
   *   "message": "Mobile User not found using the value:id"
   * }
   */
  @deprecate
  deleteRelated(id) {
    validate('UserService.deleteRelated',
      hasToken(),
      isRequired({ id }));

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/related_mobile_users/${id}`, 'delete').then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * Deletes the profile attribute value with the given ID.
   *
   * @throws {Clinical6Error} - If missing token or missing required parameters
   * @param  {Number} id      - The ID value of the profile attribute
   * @return {Promise}        - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Delete Profile Attribute</caption>
   * import { userService } from 'clinical6';
   * userService.deleteProfileAttribute(3);
   *
   * @example <caption>Success Response</caption>
   * {
   *   "status": "ok",
   *   "message": "Profile value was successfully deleted"
   * }
   *
   * @example <caption>Failure, Not Found</caption>
   * {
   *   "error_detail": {
   *     "ProfileValue": [
   *       "not_found"
   *     ]
   *   },
   *   "friendly_error": "Profile value not found using the value:-1",
   *   "internal_code": 50364,
   *   "more_info": "",
   *   "status": "fail",
   *   "message": "Profile value not found using the value:-1"
   * }
   */
  @deprecate
  async deleteProfileAttribute(id) {
    validate('UserService.deleteProfileAttribute',
      hasToken(),
      isRequired({ id }));

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/profile_attributes/${id}`, 'delete').then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * Removes the current user from the system. This action cannot be undone.
   *
   * @throws {Clinical6Error} - If missing token
   * @return {Promise}       - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Delete User</caption>
   * import { userService } from 'clinical6';
   * userService.deleteCurrent(32);
   */
  @deprecate
  deleteCurrent() {
    validate('UserService.deleteCurrent',
      hasToken());

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/mobile_users`, 'delete').then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * Disables the current user's account. This action will revoke the user's access to the system.
   * The method does not delete any data related to the user.
   *
   * @throws {Clinical6Error} - If missing token
   * @return {Promise}       - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Disable User</caption>
   * import { userService } from 'clinical6';
   * userService.disableCurrent(32);
   */
  @deprecate
  disableCurrent() {
    validate('UserService.disableCurrent',
      hasToken());

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/${this.type}/disable`, 'delete').then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * Disables a related mobile user.
   *
   * @throws {Clinical6Error} - If missing token or missing required parameters
   * @param  {Number} id      - The ID value of the related mobile user to disable.
   * @return {Promise}        - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Disable User</caption>
   * import { userService } from 'clinical6';
   * userService.disable(32);
   *
   * @example <caption>Failure, Mobile User Not Found</caption>
   * {
   *   "error_detail": {
   *     "id": "invalid_value"
   *   },
   *   "friendly_error": "Mobile User not found using the value:id",
   *   "internal_code": 50364,
   *   "more_info": "",
   *   "status": "fail",
   *   "message": "Mobile User not found using the value:id"
   * }
   */
  @deprecate
  disable(id) {
    validate('UserService.disable',
      hasToken(),
      isRequired({ id }));

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/related_mobile_users/${id}/disable`, 'post', {}).then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * Sends user's account name via email. This method does not require an access token.
   *
   * @throws {Clinical6Error}       - If missing required parameters
   * @param  {String}  emailAddress - The email address of the account.
   * @return {Promise}              - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Email Account Name</caption>
   * import { userService } from 'clinical6';
   * userService.emailAccountName();
   */
  @deprecate
  emailAccountName(emailAddress) {
    validate('UserService.emailAccountName',
      isRequired({ emailAddress }),
      isA({ emailAddress }, 'string'));

    const data = {
      email: emailAddress,
    };

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/${this.type}/forgot_account_name`, 'post', data).then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * Starts the reset password process and sends an email to the user with instructions.
   *
   * @throws {Clinical6Error}       - If missing required parameters
   * @param  {String}  emailAddress - Email address to send the reset password email to.
   * @return {Promise}              - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Email Password</caption>
   * import { userService } from 'clinical6';
   * userService.emailPassword();
   */
  @deprecate
  emailPassword(emailAddress) {
    validate('UserService.emailPassword',
      isRequired({ emailAddress }),
      isA({ emailAddress }, 'string'));

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/${this.type}/forgot_password`, 'post', { email: emailAddress }).then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * Enable a related mobile user.
   *
   * @throws {Clinical6Error} - If missing token or missing required parameters
   * @param  {Number}  id     - The ID value of the related mobile user to enable.
   * @return {Promise}        - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Enable User</caption>
   * import { userService } from 'clinical6';
   * userService.enableUser(19);
   *
   * @example <caption>Success Response</caption>
   * {
   *   "invited": []
   * }
   *
   * @example <caption>Failure, Mobile User Not Found</caption>
   * {
   *   "error_detail": {
   *     "id": "invalid_value"
   *   },
   *   "friendly_error": "Mobile User not found using the value:id",
   *   "internal_code": 50364,
   *   "more_info": "",
   *   "status": "fail",
   *   "message": "Mobile User not found using the value:id"
   * }
   */
  @deprecate
  enable(id) {
    validate('UserService.enable',
      hasToken(),
      isRequired({ id }));

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/related_mobile_users/${id}/enable`, 'post', {}).then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * @override
   * Call a GET request expecting JSON API information.
   *
   * @throws {Clinical6Error}     - If missing token or missing required parameters
   * @param  {Object} [params]    - Parameters used to get information from server (such as id)
   * @param  {String} [cacheMode] - Override the caching method for this call
   * @return {Promise<User[] | User>}
   *                              - Promise object that returns one User or a key:User
   *                                hashtable
   *
   * @example
   * import { mobileUserService } from 'clinical6';
   *
   * // You will be able to access these mobile users using the `get` method.
   * mobileUserService.get().then(users => console.log(users));
   *
   * // Additionally, you can retrieve the subcategories for a specific mobile user with the `get`
   * // method, using the ID of the desired mobile user as a parameter.
   * mobileUserService.get({id: 23}).then(users => console.log(users));
   *
   * // This method will also pull back the `user.profile` should it exist for the user or list of users
   */
  async get(params = {}, cacheMode = undefined) {
    validate('UserService.get',
      hasToken());
    return super.get(params, { cacheMode });
  }

  /**
   * Returns all the values for the given profile attribute.
   *
   * @throws {Clinical6Error} - If missing token or missing required parameters
   * @param  {String}  apiKey - The API key value for the profile attribute.
   * @return {Promise}        - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Get All Profile Attributes</caption>
   * import { userService } from 'clinical6';
   * userService.getAllProfileAttributes('fj103jaf08j23jfds').then(attr => console.log(attr));
   */
  @deprecate
  async getAllProfileAttributes(apiKey) {
    validate('UserService.getAllProfileAttributes',
      hasToken(),
      isRequired({ apiKey }),
      isA({ apiKey }, 'string'));

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/profile_attributes/${apiKey}`).then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * Get User Badges
   *
   * @throws {Clinical6Error}             - If missing token or missing required parameters
   * @param  {!Object}  awardee           - The user, must have id
   * @param  {!Number}  awardee.id        - The user id
   * @param  {Object}   [awardedBadge]    - Filters to modify result (date, entry_group_id)
   * @param  {Number}   [awardedBadge.id] - Pulling back a specific badge via id from the user
   * @param  {String}   [cacheMode]       - Override the caching method for this call
   * @return {Promise<Badge[] | Badge>}
   *                                      - Promise object that returns one object or hashmap
   *
   * @example
   * import { mobileUserService, User } from 'clinical6';
   *
   * // You can get entries using the `getAwardedBadges` method
   * mobileUserService.getAwardedBadges(new User({ id: 5 })).then(entry => console.log(entry));
   *
   * const filters = { entry_group_id: 7, date: '2018-06-02'};
   * mobileUserService.getAwardedBadges({ id: 5 }, filter).then(entry => console.log(entry));
   *
   * // Additionally, you can do the same thing using `User.getAwardedBadges()`
   * const user = new User({ id: 5, ... });
   * user.getAwardedBadges().then(entry => console.log(entry));
   * user.getAwardedBadges(filter).then(entry => console.log(entry));
   */
  async getAwardedBadges(awardee = undefined, awardedBadge = {}, cacheMode = undefined) {
    awardee = awardee || Client.instance.user;
    validate('UserService.getAwardedBadges',
      hasToken(),
      isRequired({ awardee }),
      hasAttribute({ awardee }, 'id'),
      hasAttribute({ awardee }, 'type'));
    awardedBadge.type = AwardedBadge.type;
    if (awardedBadge.awardee) {
      validate('UserService.getAwardedBadges',
        isValid((awardee.id === awardedBadge.awardee.id), `awardee.id must be the same as awardedBadge.awardee.id`));
    }
    return super.getChildren(awardee, awardedBadge, { cacheMode });
  }

  /**
   * Get Flow Data Group for a user. This method can get back data with or without a Flow Data Group ID.
   * This endpoint does not support filters and can only process the page URL params.
   *
   * @throws {Clinical6Error}                 - If missing token or missing required parameters
   * @param  {Object}   [user]                - User - if not defined, use logged in user
   * @param  {!Number}  [user.id]             - User ID must exist
   * @param  {!String}  [user.type]           - User type ('mobile_users' or 'users') must exist
   * @param  {!Object}  [params]              - Parameters used to get information from server (such as id)
   * @param  {!Object}  [params.page]         - Pagination information
   * @param  {Number}   [params.page.number]  - Number of pages of data to get back
   * @param  {Number}   [params.page.size]    - Number of results per page to get back
   * @param  {String}   [cacheMode]           - Override the caching method for this call
   * @return {Promise<FlowDataGroup>}         - Promise object that returns one object
   *
   * @example
   * import { userService, FlowDataGroup } from 'clinical6';
   * userService.getDataGroup({ id: 24 });
   * const capturedValueGroup = new FlowDataGroup( {id: 24 });
   * userService.getDataGroup(capturedValueGroup);
   */
  // async getDataGroup(user, params, cacheMode = undefined) {
  async getDataGroup(user = undefined, params = {}, cacheMode = undefined) {
    user = user || Client.instance.user;
    validate('UserService.getDataGroup',
      hasToken(),
      isRequired({ user }),
      hasAttribute({ user }, 'id'),
      hasAttribute({ user }, 'type'));
    validate('UserService.getDataGroup',
      isA({ userId: user.id }, 'number'));
    params.type = 'data_collection__captured_value_groups';
    if (params.id) {
      validate('UserService.getDataGroup',
        isA({ paramsId: params.id }, 'number'));
    }

    return super.getChildren(user, params, { cacheMode });
  }

  /**
   * Get eDiary entries
   *
   * @throws {Clinical6Error}         - If missing token or missing required parameters
   * @param  {!Object}  user          - The mobile user, must have id
   * @param  {!Number}  user.id       - The mobile user id
   * @param  {Object}   [filters]     - Filters to modify result (date, entry_group_id)
   * @param  {String}   [cacheMode]   - Override the caching method for this call
   * @return {Promise<Entry[] | Entry>} - Promise object that returns one object or hashmap
   *
   * @example
   * import { mobileUserService, User } from 'clinical6';
   *
   * // You can get entries using the `getEntries` method
   * mobileUserService.getEntries({ id: 5 }).then(entry => console.log(entry));
   *
   * const filters = { entry_group_id: 7, date: '2018-06-02'};
   * mobileUserService.getEntries({ id: 5 }, filter).then(entry => console.log(entry));
   *
   * // Additionally, you can do the same thing using `User.getEntries()`
   * const user = new User({ id: 5, ... });
   * user.getEntries().then(entry => console.log(entry));
   * user.getEntries(filter).then(entry => console.log(entry));
   */
  async getEntries(user, filters = undefined, cacheMode = undefined) {
    validate('UserService.getEntries',
      hasToken(),
      isRequired({ user }));
    validate('UserService.getEntries',
      hasAttribute({ user }, 'id'));

    return super.getChildren(user, { type: Entry.type, filters }, { cacheMode });
  }

  /**
   * Invite a user.
   *
   * @throws {Clinical6Error}                         - If missing token or missing required parameters
   * @param  {User}     user                          - The user in which to invite (required)
   * @param  {!String}  user.email                    - The email address of the invited user. (required)
   * @param  {Object}   attributes                    - A list of optional attributes not found in user or profile.  This can contain any key/value.
   * @param  {String}   attributes.invitation_token   - The token used for accepting an invitation
   * @param  {Object}   [relationships]               - A list of optional relationships not found in user or profile.  This can contain any key/value.
   * @return {Promise<User>}                          - Promise object that returns success or failure
   *
   * @example <caption>Get Invitation Status</caption>
   * import { userService, User } from 'clinical6';
   * const user = new User({ email: 'john.brown@parallel6.com' });
   * let status = await userService.getInvitationStatus(user, { invitation_token: 'asdf1234' });
   *
   * @example <caption>Get Default User Invitation Status</caption>
   * import { userService } from 'clinical6';
   * // first param will default to clinical6.user
   * let status = await userService.getInvitationStatus(undefined, { invitation_token: 'asdf1234' });
   */
  async getInvitationStatus(user = undefined, attributes = {}, relationships = {}) {
    user = user || Client.instance.user;
    validate('UserService.getInvitationStatus',
      hasToken(),
      isRequired({ user }),
      isRequired({ invitation_token: attributes.invitation_token }));
    const invitation = user.toInvitation(attributes, relationships);
    validate('UserService.invite',
      hasAttribute(invitation, 'email'),
      hasAttribute(invitation, 'type'));

    return super.insert(invitation, { url: `/v3/${user.type}/invitation/status`, cacheMode: 'networkOnly' });
  }

  /**
   * Get Notifications
   *
   * @throws {Clinical6Error}         - If missing token or missing required parameters
   * @param  {!Object}  [user]        - The user, must have id
   * @param  {!Number}  [user.id]     - The user id
   * @param  {!String}  [user.type]   - The user type
   * @param  {String}   [cacheMode]   - Override the caching method for this call
   * @return {Promise<Notification[]} - Promise object that returns a list of Notifications
   *
   * @example
   * import { mobileUserService, User } from 'clinical6';
   *
   * // You can get entries using the `getNotifications` method
   * mobileUserService.getNotifications().then(notification => console.log(notification));
   *
   * const user = new User({ id: 5, ... });
   * mobileUserService.getNotifications(user).then(notification => console.log(notification));
   *
   * // Additionally, you can do the same thing using `User.getNotifications()`
   * const user = new User({ id: 5, ... });
   * user.getNotifications().then(notification => console.log(notification));
   */
  async getNotifications(user = undefined, cacheMode = undefined) {
    user = user || Client.instance.user;
    validate('UserService.getNotifications',
      hasToken(),
      isRequired({ user }),
      hasAttribute({ user }, 'id'),
      hasAttribute({ user }, 'type'));

    return super.getChildren(user, new Notification(), { cacheMode });
  }

  /**
   * Get the profile of a user (or current user if null).
   *
   * @throws {Clinical6Error}     - If missing token or missing required parameters
   * @param  {Object} [user]      - Parameters used to get information from server (such as id)
   * @param  {String} [cacheMode] - Override the caching method for this call
   * @return {Promise<Profile>}   - Promise object that returns one User or a key:User
   *                                hashtable
   *
   * @example
   * import { mobileUserService, User } from 'clinical6';
   *
   * // You will be able to access these mobile users using the `getProfile` method.
   * mobileUserService.getProfile().then(users => console.log(users));
   *
   * // Additionally, you can retrieve the profile for a specific mobile user with the `getProfile`
   * // method, using the ID of the desired mobile user as a parameter.
   * mobileUserService.getProfile(new User({id: 23})).then(users => console.log(users));
   *
   * // using the default `type`
   * mobileUserService.getProfile({id: 23}).then(users => console.log(users));
   */
  async getProfile(user = undefined, cacheMode = undefined) {
    user = user || Client.instance.user;
    const type = user.type || this.type;
    validate('UserService.getProfile',
      hasToken(),
      isRequired({ type }),
      hasAttribute({ user }, 'id'));
    // const url = `/v3/${type}/${user.id}/profile`;
    // return super.get({ id: user.id, type: 'profiles' }, { url, cacheMode })
    //   .then(profile => (user.profile = profile));
    return super.getChildren(user, { type: 'profile' }, { cacheMode });
  }

  /**
   * Gets all the custom attributes for the profile along with its values.  This is here for
   * ordering and labeling profile attributes.
   *
   * @throws {Clinical6Error}                 - If missing token
   * @param  {Boolean} [includeHidden=false]  - Optional parameter which determines whether the
   *                                            response should include the hidden attributes
   *                                            (Encrypted Data).
   * @param  {Boolean} [includeValues=false]  - Optional parameter which determiens whether the
   *                                            response should include the existing values of each
   *                                            attribute.
   * @return {Promise}                        - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Get Profile Attributes</caption>
   * import { userService } from 'clinical6';
   * userService.getProfileAttributes();
   *
   * @example <caption>Success, both false</caption>
   * {
   *   "status": "ok",
   *   "content": [
   *     {
   *       "id": 2,
   *       "api_key": "dummy_774",
   *       "name": "dummy_773",
   *       "attribute_type": "list",
   *       "options": null,
   *       "created_at": "2016-10-07T00:54:37.000Z",
   *       "updated_at": "2016-10-07T00:54:37.000Z",
   *       "is_visible": true,
   *       "allow_multiple_values": null,
   *       "is_recurrent_capture": false
   *     },
   *     {
   *       "id": 3,
   *       "api_key": "dummy_776",
   *       "name": "dummy_775",
   *       "attribute_type": "list",
   *       "options": null,
   *       "created_at": "2016-10-07T00:54:37.000Z",
   *       "updated_at": "2016-10-07T00:54:37.000Z",
   *       "is_visible": true,
   *       "allow_multiple_values": null,
   *       "is_recurrent_capture": false
   *     },
   *     {
   *       "id": 4,
   *       "api_key": "dummy_778",
   *       "name": "dummy_777",
   *       "attribute_type": "list",
   *       "options": null,
   *       "created_at": "2016-10-07T00:54:37.000Z",
   *       "updated_at": "2016-10-07T00:54:37.000Z",
   *       "is_visible": true,
   *       "allow_multiple_values": null,
   *       "is_recurrent_capture": false
   *     }
   *   ],
   *   "total_pages": 1
   * }
   *
   * @example <caption>Success, include_hidden true</caption>
   * {
   *   "status": "ok",
   *   "content": [
   *     {
   *       "id": 14,
   *       "api_key": "dummy_798",
   *       "name": "dummy_797",
   *       "attribute_type": "list",
   *       "options": null,
   *       "created_at": "2016-10-07T00:54:38.000Z",
   *       "updated_at": "2016-10-07T00:54:38.000Z",
   *       "is_visible": true,
   *       "allow_multiple_values": null,
   *       "is_recurrent_capture": false
   *     },
   *     {
   *       "id": 15,
   *       "api_key": "dummy_800",
   *       "name": "dummy_799",
   *       "attribute_type": "list",
   *       "options": null,
   *       "created_at": "2016-10-07T00:54:38.000Z",
   *       "updated_at": "2016-10-07T00:54:38.000Z",
   *       "is_visible": true,
   *       "allow_multiple_values": null,
   *       "is_recurrent_capture": false
   *     },
   *     {
   *       "id": 16,
   *       "api_key": "dummy_802",
   *       "name": "dummy_801",
   *       "attribute_type": "list",
   *       "options": null,
   *       "created_at": "2016-10-07T00:54:38.000Z",
   *       "updated_at": "2016-10-07T00:54:38.000Z",
   *       "is_visible": true,
   *       "allow_multiple_values": null,
   *       "is_recurrent_capture": false
   *     },
   *     {
   *       "id": 17,
   *       "api_key": "dummy_804",
   *       "name": "dummy_803",
   *       "attribute_type": "list",
   *       "options": null,
   *       "created_at": "2016-10-07T00:54:38.000Z",
   *       "updated_at": "2016-10-07T00:54:38.000Z",
   *       "is_visible": false,
   *       "allow_multiple_values": null,
   *       "is_recurrent_capture": false
   *     },
   *     {
   *       "id": 18,
   *       "api_key": "dummy_806",
   *       "name": "dummy_805",
   *       "attribute_type": "list",
   *       "options": null,
   *       "created_at": "2016-10-07T00:54:38.000Z",
   *       "updated_at": "2016-10-07T00:54:38.000Z",
   *       "is_visible": false,
   *       "allow_multiple_values": null,
   *       "is_recurrent_capture": false
   *     },
   *     {
   *       "id": 19,
   *       "api_key": "dummy_808",
   *       "name": "dummy_807",
   *       "attribute_type": "list",
   *       "options": null,
   *       "created_at": "2016-10-07T00:54:38.000Z",
   *       "updated_at": "2016-10-07T00:54:38.000Z",
   *       "is_visible": false,
   *       "allow_multiple_values": null,
   *       "is_recurrent_capture": false
   *     }
   *   ],
   *   "total_pages": 1
   * }
   */
  @deprecate
  getProfileAttributes(includeHidden = false, includeValues = false) {
    validate('UserService.getProfileAttributes',
      hasToken());

    let httpQuery = '';
    // Use param encoded into http query when including subcategories
    httpQuery = includeHidden ? '?include_hidden=true' : '';
    let prefix = '';
    prefix = httpQuery === '' ? '?' : '&';
    httpQuery = includeValues ? `${httpQuery}${prefix}include_values=true` : httpQuery;

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/profile_attributes${httpQuery}`).then(
        (response) => {
          resolve(response);
        },
        err => reject(err)
      );
    });
  }

  /**
   * Returns the registration status of the user.
   *
   * @param   {Object}    attributes              - The a mobile user or object
   * @param   {String}    attributes.accountName  - Account name to login.
   * @param   {String}    attributes.email        - Email to login.
   * @param   {String}    [meta]                  - Meta data to be passed back
   * @param   {Boolean}   [meta.passwordSet]      - If the password is set
   * @returns {Promise<String>}                   - A Promise with the status
   *
   * @example
   * import { mobileUserService } from 'clinical6';
   *
   * mobileUserService.getRegistrationStatus({ account_name: 'asdf' });
   * mobileUserService.getRegistrationStatus({ email: 'asdf@asdf.com'});
   */
  async getRegistrationStatus(attributes, meta = {}) {
    validate('UserService.getRegistrationStatus',
      isRequired({ attributes }));
    validate('UserService.getRegistrationStatus',
      isValid((
        (attributes.accountName || attributes.account_name)
        || (attributes.email && isEmail(attributes.email))
      ), 'attributes must be valid'));

    const data = {
      type: 'registration_validations',
      attributes
    };

    return new Promise((resolve, reject) => {
      super.insert(data, { url: `/v3/${this.type}/registration_validation`, cacheMode: 'networkOnly', meta })
        .then(
          (response) => {
            if (meta && attributes instanceof User) {
              attributes.meta = meta;
            }
            resolve(response);
          },
          err => reject(err)
        );
    });
  }

  /**
   * Retrieves all the related mobile users.
   *
   * @throws {Clinical6Error} - If missing token
   * @return {Promise<{followed_users: User[], followers: User[]}>} Promise object
   * that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Get Related Mobile Users</caption>
   * import { userService } from 'clinical6';
   * userService.getRelatedMobileUsers().then(related => {
   *  console.log('followed_users', related.followed_users);
   *  console.log('followers', related.followers);
   * });
   */
  @deprecate
  getRelatedMobileUsers() {
    validate('UserService.getRelatedMobileUsers',
      hasToken());

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/related_mobile_users`).then(
        // response => resolve(response), // {followed_users: [], followers: []}
        response => resolve({
          followed_users: response.followed_users.map(obj => new User(obj)),
          followers: response.followers.map(obj => new User(obj))
        }),
        err => reject(err)
      );
    });
  }

  /**
   * Get Schedules for a user based on an id.
   *
   * @throws {Clinical6Error}                 - If missing token or missing required parameters
   * @param  {Object}   [user]                - User - if not defined, use logged in user
   * @param  {!Number}  [user.id]             - User ID must exist
   * @param  {!String}  [user.type]           - User type ('mobile_users' or 'users') must exist
   * @param  {String}   [cacheMode]           - Override the caching method for this call
   * @return {Promise<Schedule[] | Schedule>} - Promise object that returns a schedule or list of schedules
   *
   * @example
   * import { userService } from 'clinical6';
   * userService.getSchedules(); // current user
   * userService.getSchedules({ id: 24, type: 'mobile_users' }); // user with id 24
   */
  async getSchedules(user = undefined, cacheMode = undefined) {
    user = user || Client.instance.user;
    validate('UserService.getSchedules',
      hasToken(),
      isRequired({ user }),
      hasAttribute({ user }, 'id'),
      hasAttribute({ user }, 'type'));
    validate('UserService.getSchedules',
      isA({ userId: user.id }, 'number'));
    const url = `/v3/${user.type}/${user.id}/scheduler/personalized_rule_schedules`;
    return super.get({ type: 'scheduler__personalized_rule_schedules' }, { url, cacheMode });
  }

  /**
   * Get the session of the logged in user.
   *
   * @throws {Clinical6Error}       - If token is not a string
   * @param  {String}  [token]      - Access token
   * @param  {String}  [cacheMode]  - Override the caching method for this call
   * @return {Promise}              - Promise object that returns success or failure
   *
   * @example
   * import { mobileUserService } from 'clinical6';
   * mobileUserService.getSession();
   */
  async getSession(token = undefined, cacheMode) {
    validate('UserService.getSession',
      isA({ token }, 'string'));

    let httpQuery = ``;
    if (token) {
      httpQuery = `?access_token=${token}`;
    }
    const url = `/v3/${this.type}/sessions${httpQuery}`;

    return super.get({ }, { url, cacheMode });
  }

  /**
   * Get User Sites
   *
   * @throws {Clinical6Error}         - If missing token or missing required parameters
   * @param  {!Object}  [user]        - The user, must have id and type
   * @param  {!Number}  [user.id]     - The user id
   * @param  {!String}  [user.type]   - User type ('mobile_users' or 'users') must exist
   * @param  {String}   [cacheMode]   - Override the caching method for this call
   * @return {Promise<Site[] | Site>} - Promise object that returns one object or hashmap
   *
   * @example
   * import { mobileUserService, User } from 'clinical6';
   *
   * // You can get entries using the `getSites` method
   * mobileUserService.getSites({ id: 5, type: 'mobile_users' }).then(sites => console.log(sites));
   *
   * mobileUserService.getSites({ id: 5, type: 'mobile_users' }, filter).then(sites => console.log(sites));
   *
   * // Additionally, you can do the same thing using `User.getSites()`
   * const user = new User({ id: 5, type: 'mobile_user', ... });
   * user.getSites().then(sites => console.log(sites));
   * user.getSites(filter).then(sites => console.log(sites));
   */
  async getSites(user = undefined, cacheMode = undefined) {
    user = user || Client.instance.user;
    validate('UserService.getSites',
      hasToken(),
      isRequired({ user }));
    validate('UserService.getSites',
      hasAttribute({ user }, 'id'),
      hasAttribute({ user }, 'type'));

    const url = `/v3/${user.type}/${user.id}/sites`;
    return super.get({}, { url, cacheMode });
  }

  /**
   * @override
   * Call a POST request on the main obj.type expecting JSON API information.
   *
   * @param  {User}   user        - User object in which to insert
   * @param  {String} [cacheMode] - Override the caching method for this call
   * @return {Promise<User>}      - Inserted user
   *
   * @example
   * import { User, mobileUserService } from 'clinical6';
   * const user = new User({...});
   *
   * // you can insert a user using the `insert` method.
   * mobileUserService.insert(mobileUser).then(user => console.log(user));
   *
   * // you could also just call `save` on the user if it doesn't have an id, which will also
   * // invoke the `insert` method
   * user.save();
   */
  async insert(user, cacheMode = undefined) {
    validate('UserService.insert',
      hasToken(),
      isRequired({ user }));
    return super.insert(user, cacheMode);
  }

  /**
   * Inserts a new value for the given profile attribute.
   *
   * @throws {Clinical6Error}     - If missing token or missing required parameters
   * @param  {String}         key - The key value for the profile.
   * @param  {String|Number}  val - The value of the attribute.
   * @return {Promise}            - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Create Profile Attribute</caption>
   * import { userService } from 'clinical6';
   * userService.insertProfileAttribute('make', 'Toyota');
   *
   * @example <caption>Success Response</caption>
   * {
   *   "status": "ok",
   *   "id": 6,
   *   "content": {
   *     "id": 6,
   *     "value": "new_value",
   *     "profile_attribute_id": 23,
   *     "profile_id": 150,
   *     "created_at": "2016-10-07T00:54:38.460Z",
   *     "updated_at": "2016-10-07T00:54:38.460Z",
   *     "recurrent_capture_id": null,
   *     "recurrent_capture_type": null
   *   }
   * }
   *
   * @example <caption>Failure, Exists</caption>
   * {
   *   "error_detail": "This attribute has an existing value, use update method to overwrite it'",
   *   "friendly_error": null,
   *   "internal_code": 50367,
   *   "more_info": "",
   *   "status": "fail",
   *   "message": null
   * }
   */
  @deprecate
  async insertProfileAttribute(key, val) {
    validate('UserService.insertProfileAttribute',
      hasToken(),
      isRequired({ key }),
      isRequired({ val }));

    const data = {
      api_key: Client.instance.authToken
    };
    data[key] = val;

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/profile_attributes`, 'post', data).then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * Invite a user.
   *
   * @throws {Clinical6Error}                         - If missing token or missing required parameters
   * @param  {User}     user                          - The user in which to invite (required)
   * @param  {!String}  user.email                    - The email address of the invited user. (required)
   * @param  {Profile}  user.profile                  - The user's profile that contains information like first name, last name, and phone
   * @param  {String}   [user.role]                   - The user's role
   * @param  {Object}   [attributes]                  - A list of optional attributes not found in user or profile.  This can contain any key/value.
   * @param  {String}   [attributes.invitation_token] - The token used for accepting an invitation
   * @param  {String}   [attributes.password]         - A password chosen for the user
   * @param  {String}   [attributes.relationship]     - A relationship of the user to patient ('father', 'mother', 'uncle', 'aunt', etc)
   * @param  {Object}   [relationships]               - A list of optional relationships not found in user or profile.  This can contain any key/value.
   * @param  {Site}     [relationships.site]          - The user's site
   * @param  {Role}     [relationships.user_role]     - The user's role (especially used if not defined in the user object)
   * @param  {Language} [relationships.language]      - The user's language (especially used if not defined in the user profile)
   * @return {Promise<User>}                          - Promise object that returns success or failure
   *
   * @example <caption>Invite Simple</caption>
   * import { userService, User } from 'clinical6';
   * const user = new User({ email: 'john.brown@parallel6.com' });
   * user.profile.firstName = 'John';
   * user.profile.lastName = 'Brown';
   * userService.invite(user);
   *
   * @example <caption>Invite With Site, Role, Language, Token and Password</caption>
   * import { userService, Site, Role, Language, User } from 'clinical6';
   * const user = new User({ email: 'john.brown@parallel6.com' });
   * user.profile.firstName = 'John';
   * user.profile.lastName = 'Brown';
   * userService.invite(user, {
   *  invitation_token: 'asdf12124',
   *  password: 'mypassword@1'
   * }, {
   *  followed: new User({ id: 283 }),
   *  language: new Language({ id: 43 }),
   *  site: new Site({ id: 5 }),
   *  user_role: new Role({ id: 23 })
   * });
   */
  async invite(user, attributes = {}, relationships = {}) {
    validate('UserService.invite',
      hasToken(),
      isRequired({ user }));
    const invitation = user.toInvitation(attributes, relationships);
    validate('UserService.invite',
      hasAttribute(invitation, 'email'),
      hasAttribute(invitation, 'type'));

    return super.insert(invitation, { url: `/v3/${user.type}/invitation`, cacheMode: 'networkOnly' });
  }

  /**
   * Register a device for guest, a username, or email
   *
   * @param  {Object} attributes                - User object in which to insert
   * @param  {String} [attributes.guest]        - Guest must be true or false (null)
   * @param  {String} [attributes.account_name] - Either use account name or email
   * @param  {String} [attributes.email]        - Either use account name or email
   * @param  {String} [attributes.password]     - Password must exist if guest is false
   * @param  {Device} [device]                  - Device with id to register
   * @return {Promise<User>}                    - Registered Mobile User
   *
   * @example
   * import { mobileUserService } from 'clinical6';
   * const user = new User({...});
   *
   * // you can register a guest using the `register` method.
   * mobileUserService.register({ guest: true }, device)
   *  .then(user => console.log(user));
   *
   * // Register using an account name
   * mobileUserService.register({ account_name: 'abc', password: 'asdf' }, device)
   *  .then(user => console.log(user));
   *
   * // Register using an email address
   * mobileUserService.register({ email: 'test@test.com', password: 'asdf' }, device)
   *  .then(user => console.log(user));
   */
  async register(attributes, device) {
    validate('UserService.register',
      isRequired({ attributes, device }),
      hasAttribute({ device }, 'id'));
    validate('UserService.register',
      isValid((attributes.guest
        || ((attributes.account_name || attributes.email)
          && attributes.password)), 'attributes must be valid'));

    const data = {
      type: 'mobile_users',
      attributes,
      relationships: {
        devices: {
          data: {
            id: device.id,
            type: 'devices'
          }
        }
      }
    };

    return super.insert(data, { url: `/v3/${data.type}/registrations`, cacheMode: 'networkOnly' })
      .then(user => this._loginGetUser(user));
  }

  /**
   * Remove Awarded Badge
   *
   * @throws {Clinical6Error}                         - If missing token or missing required parameters
   * @param  {!Object}        awardedBadge            - The mobile user, must have id
   * @param  {!Object}        awardedBadge.attributes - Hashmap to add for award badge
   * @param  {!User}          awardedBadge.user       - The mobile user, must have id
   * @param  {!Number}        awardedBadge.user.id    - The mobile user id
   * @param  {!Badge}         awardedBadge.badge      - The mobile user, must have id
   * @param  {!Number}        awardedBadge.badge.id   - The mobile user id
   * @param  {String}         [cacheMode]             - Override the caching method for this call
   * @return {Promise<AwardedBadge>}                  - Promise object that returns one object or hashmap
   *
   * @example
   * import { userService, AwardedBadge, Badge, User } from 'clinical6';
   * const data = new AwardedBadge({ updatedAt: '2018-06-02' });
   * data.awardee = new User({ id: 5, ... });
   * data.badge = new Badge({ id: 5, ... });
   *
   * // You can add an awarded badge using the `removeAwardedBadge` method
   * userService.removeAwardedBadge(data).then(data => console.log(data));
   *
   * // Additionally, you can do the same thing using `User.removeAwardedBadge()`
   * user.removeAwardedBadge(badge).then(data => console.log(data));
   */
  async removeAwardedBadge(awardedBadge, cacheMode = undefined) {
    const vTitle = 'UserService.removeAwardedBadge';
    validate(vTitle, hasToken(), isRequired({ awardedBadge }));
    validate(vTitle, hasAttribute({ awardedBadge }, 'awardee'));
    validate(vTitle, hasAttribute({ awardee: awardedBadge.awardee, awardedBadge }, 'id'));

    const url = `/v3/${this.type}/${awardedBadge.awardee.id}/awarded_badges/${awardedBadge.id}`;
    return super.delete(awardedBadge, { url, cacheMode });
  }

  /**
   * Starts the reset password process and sends an email to the user with instructions.
   *
   * @throws {Clinical6Error}                   - If missing required parameters
   * @param  {Object}  attributes               - The authorization object
   * @param  {String}  attributes.account_name  - Account name to login.
   * @param  {String}  attributes.email         - Email to login.
   * @return {Promise}                          - Promise object that returns success or failure
   *
   * @example <caption>Mobile User</caption>
   * import { mobileUserService } from 'clinical6';
   * mobileUserService.requestPasswordReset({ email: 'test@test.com' });
   * mobileUserService.requestPasswordReset({ account_name: 'jsmith' });
   *
   * @example <caption>User</caption>
   * import { userService } from 'clinical6';
   * userService.requestPasswordReset({ email: 'test@test.com' });
   */
  async requestPasswordReset(attributes) {
    validate('UserService.requestPasswordReset',
      isRequired({ attributes }));
    validate('UserService.requestPasswordReset',
      isValid((attributes.account_name || (attributes.email && isEmail(attributes.email))), 'attributes must be valid'),
      isA({ account_name: attributes.account_name, email: attributes.email }, 'string'));

    const data = { type: 'password_resets', attributes };
    const url = `/v3/${this.type}/password`;
    return super.insert(data, { url, cacheMode: `networkOnly` });
  }

  /**
   * Resends an invite to a related mobile user.
   *
   * @throws {Clinical6Error}     - If missing token or missing required parameters
   * @param  {!Number} id         - The id of the related mobile user to resend the invite to.
   * @param  {any}     [options]  - Additional options submitted to the server
   * @return {Promise}            - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Resend Invite</caption>
   * import { userService } from 'clinical6';
   * userService.resendInvite(23);
   *
   * @example <caption>Resend Invite</caption>
   * import { userService } from 'clinical6';
   * userService.resendInvite(23, {});
   *
   * @deprecated
   */
  @deprecate
  resendInvite(id, options = {}) {
    validate('UserService.resendInvite',
      hasToken(),
      isRequired({ id }),
      isA({ id }, 'number'));

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/related_mobile_users/${id}/reinvite`, 'post', options).then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * Allows users to complete the Password reset flow.
   *
   * @throws {Clinical6Error}                           - If missing required parameters
   * @param  {Object}  attributes                       - The authorization object
   * @param  {!String} attributes.reset_password_token  - Token from request password reset email
   * @param  {!String} attributes.password              - New password to reset to.
   * @param  {String}  [attributes.email]               - Email to login.
   * @param  {Device}  [device]                         - Reset with this device (Default to saved device).
   * @param  {!Number} device.id                        - Reset with this device (Default to saved device).
   * @return {Promise<User>}                            - Promise object that returns the user if successful
   *
   * @example
   * import { mobileUserService } from 'clinical6';
   * mobileUserService.resetPassword({ reset_password_token: 'a820348103828f8as0d8f', password: 'password1234'});
   */
  async resetPassword(attributes, device = undefined) {
    if (Client.instance.device.id && this.type === 'mobile_users') {
      device = device || Client.instance.device;
    }
    validate('UserService.resetPassword',
      isRequired({ attributes }),
      hasAttribute({ attributes }, 'reset_password_token'),
      hasAttribute({ attributes }, 'password'));
    validate('UserService.resetPassword',
      isA({ reset_password_token: attributes.reset_password_token, password: attributes.password }, 'string'));
    if (attributes.email) {
      validate('UserService.resetPassword',
        isA({ email: attributes.email }, 'string'),
        isValid(isEmail(attributes.email), 'email must be a valid email'));
    }

    const data = { type: 'password_resets', attributes };

    if (device) {
      validate('UserService.resetPassword',
        hasAttribute({ device }, 'id'));
      data.relationships = {
        devices: {
          data: {
            id: device.id,
            type: 'devices'
          }
        }
      };
    }

    const url = `/v3/${this.type}/password`;
    return super.update(data, { url, cacheMode: `networkOnly` }).then((response) => {
      // Attempt to log in if a new authToken is present in the device information
      if (response && response.id && response.type === 'mobile_users') {
        Client.instance.user = response;
        if (response.devices && device) {
          const newDevice = response.devices.find(d => (d.id === device.id));
          if (newDevice.accessToken) {
            Client.instance.authToken = newDevice.accessToken;
            Client.instance.device.accessToken = newDevice.accessToken;
          }
        }
      }
      return response;
    });
  }

  /**
   * Sends user's account name via email. This method does not require an access token.
   *
   * @throws {Clinical6Error}           - If missing token or missing required parameters
   * @param  {!String} newPassword      - New password for the account.
   * @param  {!String} newPasswordConf  - Confirmation of the new password.
   * @param  {!String} oldPassword      - New password for the account.
   * @return {Promise}                  - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Reset Password Simple</caption>
   * import { userService } from 'clinical6';
   * userService.resetPasswordSimple('test', 'test', 'test2');
   *
   * @example <caption>Failure, Mismatched Passwords</caption>
   * {
   *   "error_detail": "Review the new_password and new_password_confirmation parameters",
   *   "friendly_error": "Password and confirmation do not match",
   *   "internal_code": 50362,
   *   "more_info": "",
   *   "status": "fail",
   *   "message": "Password and confirmation do not match"
   * }
   *
   * @deprecated
   */
  @deprecate
  resetPasswordSimple(newPassword, newPasswordConf, oldPassword) {
    validate('UserService.resetPasswordSimple',
      hasToken(),
      isRequired({
        newPassword,
        newPasswordConf,
        oldPassword
      }));

    const data = {
      old_password: oldPassword,
      new_password: newPassword,
      new_password_confirmation: newPasswordConf,

    };

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/${this.type}/reset_password`, 'put', data).then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * This method revokes the consent for a user.
   *
   * @throws {Clinical6Error}         - If missing required parameters
   *
   * @param  {User}     user          - User object in which to revoke consent for
   * @param  {String}   [cacheMode]   - Override the caching method for this call
   * @return {Promise}                - Message
   *
   */
  async revokeConsent(user, cacheMode = undefined) {
    validate('UserService.revokeConsent',
      hasToken(),
      isRequired({ user }),
      hasAttribute({ user }, 'id'),
      hasAttribute({ user }, 'type'));

    return super.delete(user, { url: `/v3/${user.type}/${user.id}/consent/consent`, cacheMode });
  }

  /**
   * Sends a confirmation email to an email address
   *
   * @throws {Clinical6Error} - If missing required parameters
   * @param  {String}  email  - Email address to send the reset password email to.
   * @return {Promise}        - Promise object that returns success or failure
   *
   * @example
   * import { mobileUserService } from 'clinical6';
   * mobileUserService.sendConfirmation('test@test.com');
   */
  async sendConfirmation(email) {
    validate('UserService.sendConfirmation',
      isRequired({ email }),
      isA({ email }, 'string'));

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/v3/${this.type}/confirmation`, 'post', {
        data: {
          type: 'confirmations',
          attributes: { email }
        }
      }).then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * Sign in user with account name.
   *
   * @throws {Clinical6Error}                   - If missing required parameters
   * @param  {Object}  attributes               - The authorization object
   * @param  {String}  attributes.account_name  - Account name to login.
   * @param  {String}  attributes.email         - Email to login.
   * @param  {String}  attributes.password      - Login with this password.
   * @param  {Device}  [device]                 - Login with this device (Default to saved device).
   * @param  {String}  [cacheMode]              - Override the caching method for this call
   * @return {Promise<Session>}                 - Promise returning a session with a user and authenticationToken and
   *                                              logging in future calls
   *
   * @example
   * import { mobileUserService } from 'clinical6';
   * mobileUserService.signIn({ account_name: 'jsmith', password: 'password1234'});
   * mobileUserService.signIn({ email: 'jsmith@gmail.com', password: 'password1234'});
   */
  async signIn(attributes, device = Client.instance.device, cacheMode = 'networkOnly') {
    validate('UserService.signIn',
      isRequired({ attributes }));
    const data = { type: 'sessions', attributes };
    Client.instance.user = new User();
    Client.instance.user.type = this.type;
    switch (this.type) {
      case 'mobile_users': {
        validate('UserService.signIn',
          isValid((attributes.guest
            || ((attributes.account_name || (attributes.email && isEmail(attributes.email)))
              && attributes.password)), 'attributes must be valid'));
        validate('UserService.signIn',
          isRequired({ device }),
          hasAttribute({ device }, 'id'));

        data.relationships = {
          devices: {
            data: {
              id: (device && device.id) ? device.id : Client.instance.device.id,
              type: 'devices'
            }
          }
        };
        break;
      }
      case 'users': {
        validate('UserService.signIn',
          isValid((attributes.guest
            || ((attributes.username || (attributes.email && isEmail(attributes.email)))
              && attributes.password)), 'attributes must be valid'));
        break;
      }
      default: {
        break;
      }
    }

    Client.instance.authToken = undefined;
    const url = `/v3/${this.type}/sessions`;
    return super.insert(data, { url, cacheMode }).then((response => this._loginGetSession(response, device)));
  }

  /**
   * Sets the pin value for an invited user. This method will set the initial value of a PIN.  In
   * some workflows it is also possible to specify account_name if it is pre-provisioned.
   *
   * @throws {Clinical6Error}                - If missing token or missing required parameters
   * @param  {Object}  json                  - The JSON value with the attributes to provide for set
   *                                           pin.
   * @param  {!Number} json.pin              - The PIN
   * @param  {!Number} json.pin_confirmation - The PIN Confirmation
   * @param  {String}  [json.account_name]   - The Account Name
   * @return {Promise}                       - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Set Pin</caption>
   * import { userService } from 'clinical6';
   * userService.setPin({
   *  pin: 123456,
   *  pin_confirmation: 123456
   * });
   *
   * @example <caption>Set Pin with Account Name</caption>
   * import { userService } from 'clinical6';
   * userService.setPin({
   *  pin: 123456,
   *  pin_confirmation: 123456,
   *  account_name
   * });
   *
   * @deprecated
   */
  @deprecate
  setPin(json) {
    validate('UserService.setPin',
      hasToken(),
      isRequired({ json }));

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/${this.type}/set_pin`, 'put', json).then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * Detaches the device from the mobile_user. If the current mobile_user is a guest, his
   * information will be destroyed. After this method is executed the device won't have any
   * access_token associated and won't be able to make other requests until it signs in.
   * Destroys a user's session
   *
   * This method does not require access token.
   *
   * @return {Promise} Promise object that returns success or failure
   */
  async signOut() {
    const url = `/v3/${this.type}/sessions`;
    const cacheMode = 'networkOnly';
    let _return = Promise.resolve('');
    try {
      _return = await super.delete(new Session({ id: 1 }), { url, cacheMode });
    } catch (e) {
      _return = Promise.resolve(e.message);
    } finally {
      Client.instance.reset();
    }
    return _return;
  }

  /**
   * Sign up a user with a user profile.  The {@link Client#device} fields `uuid`, `technology`,
   * `version`, and `pushId` are required to exist sign in to occur
   *
   * In order to sign up for a new user account, profile information and credentials can be used to
   * execute the `signUp` method:
   *
   * @throws {Clinical6Error}   - If missing required parameters and device info
   * @param  {String} username  - The username associated with the user account
   * @param  {String} password  - The password for the user
   * @param  {Object} profile   - The user profile to register for the user
   * @return {Promise}          - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Sign Up</caption>
   * import { UserService } from 'clinical6';
   *
   * const userService = new UserService();
   * userService.signUp(username, pw, profile).then(function(data){
   *   // User is created and an authtotken is passed back
   * }, function(err){
   *   // error
   * });
   *
   * @example <caption>Success Response</caption>
   * {
   *   "status": "ok",
   *   "auth_token": "93a46e8f956ea7be0cd6a087629e8550",
   *   "verification_status": "unverified",
   *   "encryption_key": "87206e2605a4bcfeb213e3c702c55190"
   * }
   *
   * @deprecated
   */
  @deprecate
  signUp(username = '', password = '', profile) {
    const { device } = Client.instance;
    validate('UserService.signUp',
      isRequired({
        udid: device.udid,
        technology: device.technology,
        version: device.appVersion,
        username,
        password,
        profile
      }),
      isA({ username }, 'string'),
      isA({ password }, 'string'));

    // Chain our response to extract authToken
    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/${this.type}/sign_up`, 'post', {
        mobile_user: {
          account_name: username,
          password,
        },
        device: {
          udid: device.udid,
          technology: device.technology,
          push_id: device.pushId,
          app_version: device.appVersion,
        },
        profile,
      }).then(
        (response) => {
          // ensure we have an auth token
          if (response.auth_token) {
            Client.instance.authToken = response.auth_token;
            resolve(response);
          }
          // fail when we don't
          reject(response);
        },
        err => reject(err)
      );
    });
  }

  /**
   * Unlocks a user
   *
   * @throws {Clinical6Error}       - If missing required parameters
   * @param  {String}  email        - Email address of the user to unlock.
   * @param  {String}  unlockToken  - Unlock token to unlock the user.
   * @return {Promise}              - Promise object that returns success or failure
   *
   * @example
   * import { mobileUserService } from 'clinical6';
   * mobileUserService.unlockUser('test@test.com', 'fakeToken123');
   */
  async unlockUser(email, unlockToken) {
    validate('UserService.unlockUser',
      isRequired({ email }),
      isRequired({ unlockToken }),
      isA({ email }, 'string'),
      isA({ unlockToken }, 'string'));

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/v3/${this.type}/unlock`, 'patch', {
        data: {
          type: 'mobile_user__unlocks',
          attributes: { email, unlock_token: unlockToken }
        }
      }).then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }

  /**
   * @override
   * Call a PATCH request on the main obj.type expecting JSON API information.
   *
   * @param  {User}   user                - User object in which to update
   * @param  {String} [options]           - Modify the nature of the call and response
   * @param  {String} [options.cacheMode] - Override the caching method for this call
   * @param  {Object} [options.meta]      - Pass and retrieve through reference meta data
   * @return {Promise<User>}              - Updated user
   *
   * @example
   * import { User, mobileUserService } from 'clinical6';
   * const user = new User({...});
   *
   * // you can update a user using the `update` method.
   * mobileUserService.update(user).then(user => console.log(user));
   *
   * // you could also just call `save` on the user if it has an id, which will also
   * // invoke the `update` method
   * user.save();
   */
  update(user = undefined, options = {}) {
    user = user || Client.instance.user;
    validate('UserService.update',
      hasToken(),
      isRequired({ user }));
    return super.update(user, options);
  }

  /**
   * Update Awarded Badge
   *
   * @throws {Clinical6Error}                         - If missing token or missing required parameters
   * @param  {!Object}        awardedBadge            - The mobile user, must have id
   * @param  {!Object}        awardedBadge.attributes - Hashmap to add for award badge
   * @param  {!User}          awardedBadge.user       - The mobile user, must have id
   * @param  {!Number}        awardedBadge.user.id    - The mobile user id
   * @param  {!Badge}         awardedBadge.badge      - The mobile user, must have id
   * @param  {!Number}        awardedBadge.badge.id   - The mobile user id
   * @param  {String}         [cacheMode]             - Override the caching method for this call
   * @return {Promise<AwardedBadge>}                  - Promise object that returns one object or hashmap
   *
   * @example
   * import { userService, AwardedBadge, Badge, User } from 'clinical6';
   * const data = new AwardedBadge({ updatedAt: '2018-06-02' });
   * data.awardee = new User({ id: 5, ... });
   * data.badge = new Badge({ id: 5, ... });
   *
   * // You can update an awarded badge using the `updateAwardedBadge` method
   * userService.updateAwardedBadge(data).then(awardedBadge => console.log(awardedBadge));
   *
   * // Additionally, you can do the same thing using `User.updateAwardedBadge()`
   * user.updateAwardedBadge(data).then(awardedBadge => console.log(awardedBadge));
   */
  async updateAwardedBadge(awardedBadge, cacheMode = undefined) {
    validate('UserService.updateAwardedBadge',
      hasToken(),
      isRequired({ awardedBadge }));
    awardedBadge = (awardedBadge instanceof AwardedBadge) ? awardedBadge : new AwardedBadge(awardedBadge);
    if (!awardedBadge.awardee) {
      awardedBadge.awardee = Client.instance.user;
    }
    validate('UserService.updateAwardedBadge',
      hasAttribute({ awardedBadge }, 'awardee'),
      hasAttribute({ awardedBadge }, 'badge'));
    validate('UserService.updateAwardedBadge',
      hasAttribute({
        awardedBadge,
        awardee: awardedBadge.awardee,
        badge: awardedBadge.badge,
      }, 'id'));

    const url = `/v3/${this.type}/${awardedBadge.awardee.id}/awarded_badges/${awardedBadge.id}`;
    return super.update(awardedBadge.toJSON(), { url, cacheMode });
  }

  /**
   * Update Profile
   *
   * @throws {Clinical6Error}                     - If missing token or missing required parameters
   * @param  {!Object}        param               - The user or profile
   * @param  {String}         [options]           - Modify the nature of the call and response
   * @param  {String}         [options.cacheMode] - Override the caching method for this call
   * @param  {Object}         [options.meta]      - Pass and retrieve through reference meta data
   * @return {Promise<Profile>}              - Promise object that returns one object or hashmap
   *
   * @example
   * import { mobileUserService, Profile, User } from 'clinical6';
   *
   * // You can update a profile using the `saveProfile` method
   *
   * // Update profile using the Current User, `clinical6.user`
   * mobileUserService.saveProfile().then(profile => console.log(profile));
   *
   * // Update profile using a User object
   * const user1 = new User({ ... }); // user must have a related profile, which it does by default
   * mobileUserService.saveProfile(user1).then(profile => console.log(profile));
   *
   * // Update profile using a Profile object
   * const profile = new Profile({ ... }); // profile must have a related user
   * mobileUserService.saveProfile(profile).then(profile => console.log(profile));
   *
   * // Update profile using a Profile object and apply meta data
   * const profile = new Profile({ ... }); // profile must have a related user
   * mobileUserService.saveProfile(profile, { meta: { something: 23 }}).then(profile => console.log(profile));
   *
   * // Updating just one attribute on the logged in user
   * mobileUserService.saveProfile({
   *  profile: { zipCode: 51038 }
   * }).then(profile => console.log(profile));
   *
   * // Updating just one attribute on a different user
   * const user2 = new User({ ... });
   * mobileUserService.saveProfile({
   *  profile: { zipCode: 51038 },
   *  user: user2
   * }).then(profile => console.log(profile));
   *
   * // Additionally, you can do the same thing using `User.saveProfile()`
   * const user3 = new User({ ... }); // user must have a related profile, which it does by default
   * user3.saveProfile().then(profile => console.log(profile));
   */
  async saveProfile(param = undefined, options = {}) {
    let user = {};
    let profile = {};
    if (!param) {
      ({ user } = Client.instance);
      ({ profile } = user);
    } else if (param instanceof User) {
      user = param;
      ({ profile } = user);
    } else if (param instanceof Profile) {
      ({ user } = param);
      profile = param;
    } else if (param.profile) {
      user = param.user || Client.instance.user;
      profile = {
        type: 'profiles',
        attributes: param.profile
      };
    }
    const type = (user && user.type) ? user.type : this.type;
    validate('UserService.saveProfile',
      hasToken(),
      isRequired({ user, profile, type }),
      hasAttribute({ user }, 'id'));
    const url = `/v3/${type}/${user.id}/profile`;
    if (profile.id) {
      return super.update(profile, { url, cacheMode: options.cacheMode, meta: options.meta }).then(p => (user.profile = p));
    }
    return super.insert(profile, { url, cacheMode: options.cacheMode, meta: options.meta }).then(p => (user.profile = p));
  }

  /**
   * Update Schedule
   *
   * @throws {Clinical6Error}                           - If missing token or missing required parameters
   * @param  {!Object}        schedule                  - The mobile user, must have id
   * @param  {!Object}        schedule.attributes       - Hashmap to add for a schedule
   * @param  {!User}          [schedule.mobileUser]     - The mobile user, must have id
   * @param  {!Number}        [schedule.mobileUser.id]  - The mobile user id
   * @param  {String}         [cacheMode]               - Override the caching method for this call
   * @return {Promise<Schedule>}                        - Promise object that returns one object or hashmap
   *
   * @example
   * import { userService, Schedule } from 'clinical6';
   * const data = new Schedule({ updatedAt: '2018-06-02' });
   *
   * // You can update a schedule using the `updateSchedule` method
   * userService.updateSchedule(data).then(schedule => console.log(schedule));
   *
   * // Additionally, you can do the same thing using `User.updateSchedule()`
   * user.updateSchedule(data).then(schedule => console.log(schedule));
   */
  async updateSchedule(schedule, cacheMode = undefined) {
    validate('UserService.updateSchedule',
      hasToken(),
      isRequired({ schedule }),
      hasAttribute({ schedule }, 'id'));
    const user = schedule.mobileUser || Client.instance.user;
    validate('UserService.updateSchedule',
      hasToken(),
      isRequired({ schedule, user }),
      hasAttribute({ schedule, user }, 'id'));

    const url = `/v3/${user.type}/${user.id}/scheduler/personalized_rule_schedules/${schedule.id}`;
    return super.update(schedule, { url, cacheMode });
  }

  /**
   * Updates the profile information for the current, authenticated user.
   *
   * @throws {Clinical6Error} - If missing token or missing required parameters
   * @param  {Object} profile - Profile object that is used to update an existing profile.
   * @param  {Number} id      - The profile ID value of the profile to update.
   * @return {Promise}        - Promise object that returns success or failure
   *
   * @deprecated
   *
   * @example <caption>Update Profile Attribute</caption>
   * import { userService } from 'clinical6';
   * userService.updateProfileAttribute({
   *  "email": "test@test.com"
   * }, 23);
   *
   * @example <caption>Success Response</caption>
   * {
   *   "status": "ok",
   *   "id": 2,
   *   "content": {
   *     "id": 2,
   *     "value": "new_value",
   *     "profile_attribute_id": 20,
   *     "profile_id": 146,
   *     "created_at": "2016-10-07T00:54:38.000Z",
   *     "updated_at": "2016-10-07T00:54:38.175Z",
   *     "recurrent_capture_id": null,
   *     "recurrent_capture_type": null
   *   }
   * }
   *
   * @example <caption>Failure, Not Found</caption>
   * {
   *   "error_detail": {
   *     "ProfileValue": [
   *       "not_found"
   *     ]
   *   },
   *   "friendly_error": "Profile value not found using the value:-1",
   *   "internal_code": 50364,
   *   "more_info": "",
   *   "status": "fail",
   *   "message": "Profile value not found using the value:-1"
   * }
   */
  @deprecate
  async updateProfileAttribute(profile, id) {
    validate('UserService.updateProfileAttribute',
      hasToken(),
      isRequired({
        profile,
        id
      }),
      isA({ id }, 'number'));

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/profile_attributes/${id}`, 'put', profile).then(
        response => resolve(response),
        err => reject(err)
      );
    });
  }
}

export default UserService;