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;