Manual Reference Source Test

src/Clinical6.js

import Client from './Client';

import Device from './helpers/Device';
import Storage from './helpers/Storage';
import UserService from './services/UserService';
import SystemService from './services/SystemService';
import { isEmail } from './utilities/FormatUtility';
import { serviceFactory } from './utilities/factories/ServiceFactory';
import { validate, isRequired, hasAttribute } from './utilities/ValidationUtility';

/**
 * Client to the Clinical6 instance
 *
 * @example
 * Clinical6 client = new Clinical6("https://mysite.captivereach.com");
 * await client.guestSignIn('thisismydeviceid', 'ios', 'thisismypushid');
 * console.log(client.authToken);
 */
class Clinical6 {
  /**
   * Constructs the class with baseUrl provided
   *
   * @param {String} [apiBaseUrl = 'http://localhost'] - Base URL the (sub)domain of the Captive Reach server. Example: 'https://dhsadobe.captivereach.com'
   */
  constructor() {
    this.apiBaseUrl = 'http://localhost';

    /** @type {Class} */
    this.Client = Client.instance;

    // Storage
    /** @type {StorageUtility} */
    // this.storageUtility = Client.instance.storageUtility;

    /** @type {Storage} */
    this.storage = new Storage();
  }

  /**
   * Gets the apiBaseUrl
   *
   * @type {String}
   * @return {String} Current apiBaseUrl
   */
  get apiBaseUrl() {
    /** @type {String} */
    this._apiBaseUrl = Client.instance.apiBaseUrl;
    return this._apiBaseUrl;
  }

  /**
   * Sets the apiBaseUrl
   *
   * @type {String}
   * @return {String} Current apiBaseUrl
   */
  set apiBaseUrl(apiBaseUrl) {
    this._apiBaseUrl = apiBaseUrl;
    Client.instance.apiBaseUrl = this._apiBaseUrl;
  }

  /**
   * Gets the authToken from Client.instance.authToken
   *
   * @type {String}
   * @param {String} authToken
   */
  get authToken() {
    /** @type {String} */
    this._authToken = Client.instance.authToken;
    return this._authToken;
  }

  /**
   * Sets the authToken for Client.instance.authToken
   *
   * @type {String}
   * @param {String} authToken
   */
  set authToken(authToken) {
    this._authToken = authToken;
    Client.instance.authToken = this._authToken;
  }

  /**
   * Gets the configuration information
   *
   * @type {Object}
   * @return {Object} data - the information in a key value object
   */
  get config() {
    return Client.instance.config;
  }

  /**
   * Sets the configuration information
   *
   * @type {Object}
   * @param {Object} data - the information in a key value object
   */
  set config(data) {
    Client.instance.config = data;
  }

  /**
   * Gets the device from Client.instance.device
   *
   * @type {Device}
   */
  get device() {
    return Client.instance.device;
  }

  /**
   * Sets the device for Client.instance.device
   *
   * @type {Device}
   * @param {Device} device
   */
  set device(device) {
    Client.instance.device = device;
  }

  /**
   * Gets the mobileApplicationKey from Client.instance.mobileApplicationKey
   *
   * @type {String}
   */
  set mobileApplicationKey(mobileApplicationKey) {
    Client.instance.mobileApplicationKey = mobileApplicationKey;
  }

  /**
   * Sets the mobileApplicationKey for Client.instance.mobileApplicationKey
   *
   * @type {String}
   * @param {String} device
   */
  get mobileApplicationKey() {
    return Client.instance.mobileApplicationKey;
  }

  /**
   * Gets the user from Client.instance.user
   *
   * @type {User}
   */
  get user() {
    return Client.instance.user;
  }

  /**
   * Sets the user for Client.instance.user
   *
   * @type {User}
   * @param {MobileUser} user
   */
  set user(user) {
    Client.instance.user = user;
  }

  /**
   * Deletes an object
   * @param  {Object} [obj]       - Object used for this action
   * @param  {String} [cacheMode] - Override the caching method for this call
   * @return {Promise}            - Returns a promise via ajax call.
   *
   * @example
   * import { clinical6, Site, Client } from 'clinical6';
   *
   * // Removes obj from server and local storage
   * const obj = new Site({...}); // Site for example
   * clinical6.delete(obj);
   *
   * // No longer in storage
   * Client.instance.storageUtility.has('sites', { id: obj.id });
   *
   * // Can also be done using a json description
   * clinical6.delete({ id: 5, type: '23', options: { cascading: true }});
   */
  async delete(obj, cacheMode = 'networkOnly') {
    validate('Clinical6.delete',
      isRequired({ obj }),
      hasAttribute({ obj }, 'type'));
    const service = serviceFactory.get(obj.type);
    return service.delete(obj, { cacheMode });
  }

  /**
   * Prepare and Fetch HTTP request to API
   *
   * @param {String} url - Path to the endpoint starting with '/'
   * @param {String} [method] - HTTP Method (DELETE|GET|POST|PUT)
   * @param {Object} [data] - JSON payload, required on POST and PUT calls.
   * @return {Promise} - Resolves on HTTP 200. Rejects on all else.
   */
  fetch(url, method = 'get', data) {
    return Client.instance.fetch(url, method, data);
  }

  /**
   * Updates an object
   * @throws {Clinical6Error}             - If missing token or missing required parameters
   * @param  {Object} [params]            - Parameters used to get information from server
   * @param  {Number} [params.id]         - Id to get data from the server
   * @param  {String} [params.type]       - Type to be used for storage
   * @param  {Object} [params.filters]    - Filters to be used for get
   * @param  {Object} [options]           - Options to modify or change behavior/results of the call
   * @param  {String} [options.cacheMode] - Override the caching method for this call
   * @param  {String} [options.meta]      - Pass through and update by reference meta data
   * @return {Promise}                    - Returns a promise via ajax call.
   *
   * @example
   * import { clinical6, Setting, Site, Client } from 'clinical6';
   *
   * // You can get sites using an existing instantiated object
   * const obj = new Site({...}); // Site for example
   * clinical6.get(obj).then(sites => console.log(sites)); // will get all sites
   *
   * // If id exists, it will return only one site
   * obj.id = 5;
   * clinical6.get(obj).then(site => console.log(site)); // will get site where id = 5
   *
   * // Using the string of the type
   * clinical6.get('mobile_users').then(users => console.log(users)); // will return all mobile users
   *
   * // Using the class name directly
   * clinical6.get(Setting).then(settings => console.log(settings)); // will return all settings
   *
   * // Adding filters and cache mode
   * clinical6.get({ type: 'some_object_type', filters: {}}, 'networkOnly').then(obj => console.log(obj));
   */
  async get(params, options = { cacheMode: undefined, meta: {} }) {
    let type = '';
    if (typeof params === 'string') {
      type = params;
    } else if (params && params.type) {
      ({ type } = params);
    }
    const service = serviceFactory.get(type);
    let _params = { type };
    if (typeof params === 'object') {
      _params = Object.assign({}, _params, params);
    }
    if (typeof options === 'string') {
      options.cacheMode = options;
    }
    return service.get(_params, options);
  }

  /**
   * Updates an object
   * @throws {Clinical6Error}             - If missing token or missing required parameters
   * @param  {Object} parent              - Parameters used to get information from server
   * @param  {Number} parent.id           - Id to get data from the server
   * @param  {String} parent.type         - Type to be used for storage
   * @param  {Object} [child]             - Parameters used to get information from server
   * @param  {Number} [child.id]          - Id to get data from the server
   * @param  {String} [child.type]        - Type to be used for storage
   * @param  {Object} [child.filters]     - Filters to be used for get
   * @param  {Object} [options]           - Options to modify or change behavior/results of the call
   * @param  {String} [options.cacheMode] - Override the caching method for this call
   * @param  {String} [options.meta]      - Pass through and update by reference meta data
   * @return {Promise}                    - Returns a promise via ajax call.
   *
   * @example
   * import { clinical6, SiteMember } from 'clinical6';
   *
   * clinical6.getChildren(site, { type: 'trials/site_members' });
   * clinical6.getChildren(site, new SiteMember());
   * clinical6.getChildren(user, { type: 'ediary/entries' });  // generates /v3/mobile_users/:id/ediary/entries
   */
  async getChildren(parent, child = {}, options = { cacheMode: undefined, meta: {} }) {
    const type = (parent && parent.type) ? parent.type : '';
    const service = serviceFactory.get(type);
    return service.getChildren(parent, child, options);
  }

  /**
   * Retrieves the profile of the current, authenticated mobile user.
   *
   * @return {Promise} Promise object that returns success or failure
   */
  getProfile() {
    return new UserService().getProfile();
  }

  /**
   * Inserts an object
   * @param  {Object} [obj]       - Object used for this action
   * @param  {String} [cacheMode] - Override the caching method for this call
   * @return {Promise}            - Returns a promise via ajax call.
   *
   * @example
   * import { clinical6, Site, Client } from 'clinical6';
   *
   * // Removes obj from server and local storage
   * const obj = new Site({...}); // Site for example
   * clinical6.insert(obj);
   *
   * // Now in storage
   * Client.instance.storageUtility.has('sites', { id: obj.id });
   */
  async insert(obj, cacheMode = 'networkOnly') {
    validate('Clinical6.insert',
      isRequired({ obj }),
      hasAttribute({ obj }, 'type'));
    const service = serviceFactory.get(obj.type);
    return service.insert(obj, { cacheMode });
  }

  /**
   * Tracks a new event in the log.
   *
   * @param {Object} [logEntry] - The new log entry to track. (optional)
   * @param {String} [logEntry.message] - The message of the log entry. (optional)
   * @param {String} [logEntry.tag] - The tag value of the log entry. (optional)
   * @param {String} logEntry.level - Indicates the level of importance of the log entry. (required)
   * @param {Object} [requestInformation] - The request information of the API call. (optional)
   * @param {String} [requestInformation.method] - The method of the request. (optional)
   * @param {String} [requestInformation.params] - The body or parameters that were sent on
   * the failing/debugging request. (optional)
   * @param {String} [requestInformationtry.status_code] - The response of the request. (optional)
   * @param {String} [requestInformationtry.url] - The URL of the request. (optional)
   * @param {Object} [deviceInformation] - The device information of the device that
   *  made the request. (optional)
   * @param {String} [deviceInformation.app_version] - The version of the app that
   * was running on the device. (optional)
   * @param {String} [deviceInformation.brand] - The brand of the device. (optional)
   * @param {String} [deviceInformation.carrier] - The carrier of the device . (optional)
   * @param {String} [deviceInformation.gps_status] - The gps status of the device. (optional)
   * @param {Number} [deviceInformation.latitude] - The latitude value where the device was.
   * (optional)
   * @param {Number} [deviceInformation.longitude] - The longitude value where the device was.
   * (optional)
   * @param {String} [deviceInformation.manufacturer] - The manufacturer of the device. (optional)
   * @param {String} [deviceInformation.network_status] - The network status of the device.
   *  (optional)
   * @param {String} [deviceInformation.os_version] - The OS version of the device. (optional)
   * @param {String} [deviceInformation.push_id] - The push ID value of the device. (optional)
   * @param {String} [deviceInformation.token] - The token value of the device. (optional)
   * @param {String} [deviceInformation.udid] - The UDID value of the device. (optional)
   * @param {String} [deviceInformation.technology] - The technology of the device. (optional)
   *
   * @return {Promise} Promise object that returns success or failure
   *
   * @example <caption>Log</caption>
   * import { systemService } from 'Clinical6';
   * systemService.log({
   *  "message": "MyText",
   *  "tag": "MyString",
   *  "level": "Error"
   * }, {
   *   "url": "MyString",
   *   "method": "MyString",
   *   "params": "MyText",
   *   "status_code": "MyString"
   * }, {
   *   "udid": "MyString",
   *   "token": "MyString",
   *   "push_id": "MyString",
   *   "app_version": "MyString",
   *   "os_version": "MyString",
   *   "network_status": "MyString",
   *   "gps_status": "MyString",
   *   "latitude": "1.5",
   *   "longitude": "1.5",
   *   "brand": "MyString",
   *   "manufacturer": "MyString",
   *   "carrier": "MyString"
   * });
   *
   * @example <caption>Success Response</caption>
   * {
   *   "status": "ok",
   *   "message": "Debug Log was successfully created"
   * }
   */
  log(logEntry, requestInformation, deviceInformation) {
    return new SystemService().log(logEntry, requestInformation, deviceInformation);
  }

  /**
   * Register a device for guest, a username, or email
   *
   * @param  {String} [account]     - MobileUser object in which to insert
   * @param  {String} [password]    - Password must exist if guest is false
   * @param  {Device} [device]      - Device with id to register
   * @return {Promise<MobileUser>}  - Registered Mobile User
   *
   * @example
   * import { clinical6 } from 'clinical6';
   * const mobileUser = new MobileUser({...});
   *
   * // you can register a guest using the `register` method.
   * clinical6.register()
   *  .then(mobileUser => console.log(mobileUser));
   *
   * // Register using an account name
   * clinical6.register('abc', 'asdf', device)
   *  .then(mobileUser => console.log(mobileUser));
   *
   * // Register using an email address
   * clinical6.register('test@test.com', 'asdf', device)
   *  .then(mobileUser => console.log(mobileUser));
   */
  async register(account = undefined, password = undefined, device = undefined) {
    const attributes = {};
    if (!account && !password) {
      attributes.guest = true;
    } else {
      attributes.password = password;
      if (isEmail(account)) {
        attributes.email = account;
      } else {
        attributes.account_name = account;
      }
    }

    let d = device || Client.instance.device;
    if (d) {
      if (d.id && d.select) {
        // if exists, just use device information
        d.select();
      } else {
        // if doesn't exist, insert new one
        d = await d.save();
      }
      d.select();
    }
    return new UserService().register(attributes, Client.instance.device);

    // return prepareDevice.then(() => new UserService().register(attributes));
  }

  /**
   * Starts the reset password process and sends an email to the user with instructions.
   *
   * @throws {Clinical6Error}   - If missing required parameters
   * @param  {String}  account  - Either the account name or email address
   * @return {Promise}          - Promise object that returns success or failure
   *
   * @example
   * import { clinical6 } from 'clinical6';
   * clinical6.requestPasswordReset('test@test.com'); // email
   * clinical6.requestPasswordReset('jsmith'); // account
   */
  requestPasswordReset(account) {
    const attributes = {};
    if (isEmail(account)) {
      attributes.email = account;
    } else {
      attributes.account_name = account;
    }
    return new UserService().requestPasswordReset(attributes);
  }

  /**
   * Sends user's account name via email. This method does not require an access token.
   *
   * @param {String} token    - Token from email.
   * @param {String} password - New password for the account.
   * @param {Device} [device] - Reset with this device (Default to saved device).
   *
   * @return {Promise} - Promise object that returns success or failure
   */
  resetPassword(token, password, device = undefined) {
    return new UserService().resetPassword(token, password, device || Client.instance.device);
  }

  /**
   * Sign in with device info
   *
   * @param  {String} account   - The username associated with the user account
   * @param  {String} password  - The password for the user
   * @return {Promise}          - Promise object that returns success or failure
   */
  signIn(account = undefined, password = undefined) {
    const attributes = {};
    if (!account && !password) {
      attributes.guest = true;
    } else {
      attributes.password = password;
      if (isEmail(account)) {
        attributes.email = account;
      } else {
        attributes.account_name = account;
      }
    }

    let promise;
    let prepareDevice;
    switch (this.user.type) {
      case 'users':
        promise = (new UserService()).signIn(attributes);
        break;
      case 'mobile_users':
      default:
        prepareDevice = new Promise((resolve) => {
          const d = Client.instance.device;
          if (d.id && d.select) {
            resolve(d.select());
          } else {
            d.save().then(_d => resolve(_d.select()));
          }
        });
        promise = prepareDevice.then(() => new UserService().signIn(attributes));
        break;
    }

    return promise;
  }

  /**
   * Sign in with device info
   *
   * @return {Promise} Promise object that returns success or failure
   */
  async signInGuest() {
    const deviceJson = Client.instance.device.toJSON();
    if (Client.instance.authToken) {
      await Client.instance.device.delete();
    }
    deviceJson.id = undefined;
    deviceJson.accessToken = undefined;
    Client.instance.device = new Device(deviceJson);
    const d = await Client.instance.device.save();
    d.select();
    return new UserService().register({ guest: true }, Client.instance.device);
  }

  /**
   * 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.
   *
   * This method does not require access token.
   *
   * @return {Promise} Promise object that returns success or failure
   */
  signOut() {
    return new UserService().signOut();
  }

  /**
   * Updates an object
   * @param  {Object} [obj]               - Object used for this action
   * @param  {Object} [options]           - Options to override the call
   * @param  {String} [options.cacheMode] - Override the caching method for this call
   * @param  {Object} [options.meta]      - Apply meta data to be passed to this call
   * @return {Promise}                    - Returns a promise via ajax call.
   *
   * @example
   * import { clinical6, Site, Client } from 'clinical6';
   *
   * // Updates a site
   * const obj = new Site({...}); // Site for example
   * obj.data = `Updated Data`;
   * clinical6.update(obj);
   *
   * // Updates a site and applies meta data
   * const obj = new Site({...}); // Site for example
   * obj.data = `Updated Data`;
   * clinical6.update(obj, { cacheMode: 'networkOnly', meta: { something: 23 }});
   */
  async update(obj, options = { cacheMode: 'networkOnly', meta: undefined }) {
    validate('Clinical6.update',
      isRequired({ obj }),
      hasAttribute({ obj }, 'type'));
    const service = serviceFactory.get(obj.type);
    return service.update(obj, options);
  }

  /**
   * Submits the provided JSON to the server.
   *
   * @param {Object} json - The JSON value with the attributes to validate.
   *
   * @return {Promise} Promise object that returns success or failure
   */
  xAuth(json) {
    return new SystemService().xAuth(json);
  }
}

export default Clinical6;