Manual Reference Source Test

src/services/ContentService.js

import Client from '../Client';
import Content from '../helpers/content/Content';
import Query from '../helpers/Query';
import VuforiaTarget from '../helpers/VuforiaTarget';
import JsonApiService from './JsonApiService';
import {
  hasAttribute,
  hasToken,
  isA,
  isRequired,
  isValid,
  validate,
} from '../utilities/ValidationUtility';

/**
 * Service handling Content calls with specific endpoints.
 *
 * Our SDK allows users to access and filter through your Clinical6 content, be it text, images,
 * videos, or other media.
 *
 * For instance, when a user selects an option from a mobile menu, you can provide the user with
 * content related to that option. The user's selection could direct them to a website (using the
 * SDK's in-app browser), in which case you would want to return a URL. Another menu option might
 * direct the user to a series of pictures with captions and links. In this case, your dynamic
 * content request would return URLs, images[<sup>1</sup>](#fn1), and text.
 *
 * Possible values for the `content_type` that can be found in dynamic content JSON responses
 * include:
 *
 * Content Type | JavaScript Attribute Name
 * --- | ---
 * Dates Category | `shared_category`
 * Events Category | `event_category`
 * Country | `country`
 * Date | `date`
 * Date with Time | `date_time`
 * Location | `location`
 * Email | `email`
 * Number | `number`
 * Text | `string`
 * Phone Number | `telephone`
 * Text Area | `text`
 * Rich Text (HTML) | `richtext`
 * Time | `time`
 * Time Zone | `time_zone`
 * URL | `url`
 * Files | `files`
 * Action | `action`
 * Boolean | `boolean`
 *
 */
class ContentService extends JsonApiService {
  /**
   * Update type to be dynamic_content__contents
   */
  constructor() {
    super('dynamic_content__contents',
      {
        title: 'ContentService',
        obj: 'content',
        tokenRequired: ['delete', 'get', 'insert', 'update']
      });
  }

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

  /**
   * Get Content
   *
   * @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  {String} [cacheMode]       - Override the caching method for this call
   * @return {Promise<Content[] | Content>}
   *                                    - Promise object that returns one object or hashmap
   *
   * @example
   * import { contentService } from 'clinical6';
   *
   * // You will be able to access these entry groups using the `get` method.
   * contentService.get({ id: 15 }).then(templates => console.log(templates));
   *
   * // get all cars
   * contentService.get('cars');
   */
  get(params, cacheMode = undefined) {
    validate('ContentService.get',
      hasToken());
    const _params = (typeof params !== 'string') ? params : {
      filters: {
        content_type: {
          permanent_link: params
        }
      }
    };
    return super.get(_params, { cacheMode });
  }

  /**
   * Get Types
   *
   * @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  {String} [cacheMode]       - Override the caching method for this call
   * @return {Promise<ContentType[] | ContentType>}
   *                                    - Promise object that returns one object or hashmap
   *
   * @example
   * import { contentService } from 'clinical6';
   *
   * // You will be able to access these entry groups using the `getType` method.
   * contentService.getTypes().then(types => console.log(types));
   */
  getTypes(params = undefined, cacheMode = undefined) {
    validate('ContentService.getTypes',
      hasToken());

    const types = {
      type: 'dynamic_content__content_types'
    };

    if (params && params.id) {
      types.id = params.id;
    }

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

  /**
   * Retrieves a single dynamic content type given the id.
   *
   * @throws {Clinical6Error}       - If missing token or missing required parameters
   * @param  {!(String|Number)} id  - The unique indicator to retrieve content.
   * @return {Promise<Content>}     - Promise object that returns Content
   *
   * @example
   * import { contentService } from 'clinical6';
   *
   * contentService.getById(32).then(content => console.log(content));
   */
  async getById(id) {
    validate('ContentService.getById',
      hasToken(),
      isRequired({ id }));
    return this.get({ id });
  }

  /**
   * Retrieves a list of dynamic content based on the permanent link given
   *
   * @throws {Clinical6Error}       - If missing token or missing required parameters
   * @param  {!String} resource     - The permanent link for the requested content_type.
   *                                  Note: this param is part of the URL
   * @return {Promise<Content[]>}   - Promise object that returns a list of Content
   *
   * @example
   * import { contentService } from 'clinical6';
   *
   * contentService.getContent('acronyms').then(acronyms => console.log(acronyms));
   */
  async getContent(resource) {
    validate('ContentService.getContent',
      hasToken(),
      isRequired({ resource }),
      isA({ resource }, 'string'));
    return this.get(resource);
  }

  /**
   * Retrieves a list of favorite dynamic content based on the permanent link given
   *
   * @throws {Clinical6Error}       - If missing token or missing required parameters
   * @param  {!String} resource     - The permanent link for the requested content_type.
   *                                  Note: this param is part of the URL
   * @param  {?Number} [page=0]     - The page number of the results given.
   * @param  {?Number} [perPage=0]  - The number of content to return per page.
   * @return {Promise<Content[]>}   - Promise object that returns a list of Content
   *
   * @example
   * import { contentService } from 'clinical6';
   *
   * // All
   * contentService.getContentFavorites('acronyms').then(acronyms => console.log(acronyms));
   *
   * // The first 10
   * contentService.getContentFavorites('acronyms', 1, 10).then(acronyms => console.log(acronyms));
   */
  async getContentFavorites(resource, page = 0, perPage = 0) {
    validate('ContentService.getContentFavorites',
      hasToken(),
      isRequired({ resource }),
      isA({ resource }, 'string'));
    return new Promise((resolve, reject) => {
      const httpQuery = `?page=${page}&per_page=${perPage}`;
      Client.instance.fetch(`/api/dynamic/${resource}/favorites${httpQuery}`).then(
        response => resolve(response.content.map(obj => new Content(obj.content_type, obj))),
        err => reject(err)
      );
    });
  }

  /**
   * Makes a request based on permanent link and query given
   *
   * @throws {Clinical6Error}         - If missing token or missing required parameters
   * @param  {!Query}  query          - a Query object initiated by the Query class
   * @param  {!String} query.resource - the parmanent link of the content the query is based on
   * @return {Promise<Content[]>}     - Promise object that returns a list of Content
   *
   * @example
   * import { contentService, Query } from 'clinical6';
   *
   * const acronymsQ = new Query('acronyms');
   *
   * // Calls `contentService.filter`
   * contentService.find(acronymsQ).then(acronyms => console.log(acronyms));
   *
   * // usually called through Query.
   * acronymsQ.find();
   */
  find(query) {
    validate('ContentService.find',
      hasToken(),
      isRequired({ query }),
      isValid((query && (query instanceof Query)), 'query is not a type of Query'));

    return this.filter(query);
  }

  /**
   * Filters and returns a list of the contents for the given content_type
   *
   * @throws {Clinical6Error}         - If missing token or missing required parameters
   * @param  {!Query}  query          - a Query object initiated by the Query class
   * @param  {!String} query.resource - the parmanent link of the content the query is based on
   * @return {Promise<Content[]>}     - Promise object that returns a list of Content
   *
   * @example
   * import { contentService, Query } from 'clinical6';
   *
   * const acronymsQ = new Query('acronyms');
   * contentService.filter(acronymsQ).then(acronyms => console.log(acronyms));
   */
  async filter(query) {
    validate('ContentService.filter',
      hasToken(),
      isRequired({ query }),
      isValid((query && (query instanceof Query)), 'query is not a type of Query'),
      hasAttribute({ query }, '_resource'));
    const { resource } = query;

    query.validate();

    // Create 'filters' that are used for searches from the Query attributes
    const { filters } = query;

    // Validate query attributes
    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/dynamic/${resource}/filter`, 'post', {
        filters,
        order_by: query.orderBy,
        minimal: query.minimal,
        brand_relative: query.brandRelative,
        reference_module: query.reference_module,
        page: query.page,
        per_page: query.perPage,
      }).then(
        response => resolve(response.content.map(obj => new Content(obj.content_type, obj))),
        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<{ [id: String] : MobileUser } | MobileUser>}
  //  *                              - Promise object that returns one MobileUser or a key:MobileUser
  //  *                                hashtable
  //  *
  //  * @example
  //  * import { contentService } from 'clinical6';
  //  *
  //  * // You will be able to access these mobile users using the `get` method.
  //  * contentService.get().then(mobileUsers => console.log(mobileUsers));
  //  *
  //  * // 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.
  //  * contentService.get({id: 23}).then(mobileUser => console.log(mobileUser));
  //  */
  // get(params, filters = undefined, cacheMode = undefined) {
  //   validate('MobileUserService.get',
  //     hasToken()
  //   );
  //   let url = `/v3/dynamic_content/contents`;
  //   if (params.contentType || filters) {
  //     const f = { }
  //     const filter = Object.keys(filters).map(k => `filters[${k}]=${filters[k]}`).join('&');
  //     url = `${url}?${filter}`;
  //   }

  //   return super.get(params, { cacheMode });
  // }

  /**
   * Retrieves a random dynamic content based on the permanent link given
   *
   * @throws {Clinical6Error}   - If missing token or missing required parameters
   * @param  {!String} resource - The permanent link for the requested content_type.
   *                              Note: this param is part of the URL
   * @return {Promise<Content>} - Promise object that returns a random Content
   *
   * @example
   * import { contentService } from 'clinical6';
   *
   * contentService.getRandom('acronyms').then(content => console.log(content));
   */
  async getRandom(resource) {
    validate('ContentService.getRandom',
      hasToken(),
      isRequired({ resource }),
      isA({ resource }, 'string'));
    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/dynamic/${resource}/random`).then(
        response => resolve(new Content(response.content.content_type, response.content)),
        err => reject(err)
      );
    });
  }

  /**
   * @deprecated
   * Retrieves the list of vuforia targets associated with the content.
   *
   * @throws {Clinical6Error}         - If missing token or missing required parameters
   * @param  {!Number} id             - The id of the content whose vuforia targets will be
   *                                    returned.
   * @return {Promise<VuforiaTarget>} - Promise object that returns success or failure
   *
   * @example
   * import { contentService } from 'clinical6';
   *
   * contentService.getVuforiaDynamicContent(23).then(vuforia => console.log(vuforia));
   */
  async getVuforiaDynamicContent(id) {
    validate('ContentService.getVuforiaDynamicContent',
      hasToken(),
      isRequired({ id }));

    return new Promise((resolve, reject) => {
      Client.instance.fetch(`/api/dynamic/${id}/vuforia_target`).then(
        (response) => {
          let result = response;
          result = new VuforiaTarget(response);
          resolve(result);
        },
        err => reject(err)
      );
    });
  }

  /**
   * Insert dynamic content information.  Use the {@link Content#save} method when possible.
   *
   * @throws {Clinical6Error}         - If missing token or missing required parameters
   * @param  {!Content} content       - A content subclass containing information to insert
   * @param  {?String}  content.title - Required title of the content
   * @param  {String}   [cacheMode]   - Override the caching method for this call
   * @return {Promise}                - Promise object that returns success or failure
   *
   * @example
   * import { contentService, Content } from 'clinical6';
   *
   * let car = new Content('car');
   * car.set('title', 'My Car');
   * car.set('make', 'Toyota')
   * car.set('model', 'Corolla')
   * car.set('year', 2006)
   *
   * contentService.insert(car);
   *
   * // Frankly, we just call the `Content.save` method.
   * car.save();
   */
  async insert(content, cacheMode = undefined) {
    validate('ContentService.insert',
      hasToken(),
      isRequired({ content }),
      isValid((content && (content instanceof Content)), 'content is not a type of Content'));

    // return content.syncRelationships().then(() => super.insert(content, { cacheMode }));
    // return super.insert(content, { cacheMode });
    return new Promise((resolve, reject) => {
      content.syncRelationships().then(
        () => resolve(),
        reason => reject(reason)
      );
    }).then(() => super.insert(content, { cacheMode }));
  }

  /**
   * Update dynamic content information.  Use the {@link Content#save} method when possible.
   *
   * @throws {Clinical6Error}       - If missing token or missing required parameters
   * @param  {!Content} content     - Some dynamic content object that is used to update an
   *                                  existing content.
   * @param  {String}   [cacheMode] - Override the caching method for this call
   * @return {Promise}              - Promise object that returns success or failure
   *
   * @example
   * import { contentService, Content } from 'clinical6';
   *
   * let car = new Content();
   * car.set('content_type', 'car');
   * car.set('title', 'My Car');
   * car.set('make', 'Toyota')
   * car.set('model', 'Corolla')
   * car.set('year', 2006)
   *
   * // inserts
   * car.save().then(() => {
   *   // car now has id
   *
   *   // We can do this, but it's easier to just call the save method
   *   contentService.update(car);
   *
   *   // Car has id from insert, so this last save is an update
   *   return car.save();
   * });
   */
  async update(content, cacheMode = undefined) {
    validate('ContentService.update',
      hasToken(),
      isRequired({ content }),
      hasAttribute({ content }, 'id'),
      isValid((content && (content instanceof Content)), 'content is not a type of Content'));
    return content.syncRelationships().then(() => super.update(content, { cacheMode }));
  }
}

export default ContentService;