UNPKG

fluro

Version:

Promise based HTTP Fluro client for the browser and node.js

878 lines (634 loc) 27.3 kB
import _ from 'lodash'; import axios from 'axios'; const CancelToken = axios.CancelToken; /////////////////////////////////////////////////// /** * Creates a new FluroContent instance. * This module provides a number of helper functions for Creating, Reading, Updating and Deleting content via the Fluro API * * @constructor * @param {FluroCore} fluro A reference to the parent instance of the FluroCore module. This module is usually created by a FluroCore instance that passes itself in as the first argument. */ var FluroContent = function(fluro) { if (!fluro.api) { throw new Error(`Can't Instantiate FluroContent before FluroAPI exists`); } //Keep track of any refresh requests var inflightRefreshRequest; /////////////////////////////////////////////////// var service = {} /////////////////////////////////////////////////// /** * Runs a search from the Fluro server and returns the results * @alias FluroContent.search * @param {String} terms The keywords to search for * @param {Object} options Extra Configuration and options for how to search the database and how to render the results * @param {Object} options.limit How many results should be returned. Defaults to 10 * @param {Array} options.types Specify types or definition names for which items should be searched for * @param {Boolean} options.showQuery If true will return the query used to search instead of the search results themselves * @return {Array} An array of content items that match the search, if options.types is specified will be a nested array of results for each type * * @example * fluro.content.search('Wonder', {limit:5, types:['song', 'album', 'tag']}).then(function(results) { * //Will return a nested array with up to 5 results for each type * //[{_type:'Song', results:[{title:"Wonder"...}]}, {_type:'Album', results:[{title:"Wonder"...}]}] * }) * * fluro.content.search('Wonder', {limit:5}).then(function(results) { * //Will return an array of up to 5 items the user has access to view that match the search terms * //[{title:"Wonder", _type:'article', definition:'song'...}, {title:"Wonder", _type:'article', definition:'album'...}] * }) */ ///////////////////////////////////////////////// var currentSearch; ///////////////////////////////////////////////// service.search = function(terms, params, config) { if (!params) { params = {}; } if (!params.limit) { params.limit = 10; } ///////////////////////////////////////////////// if (currentSearch) { // cancel the request (the message parameter is optional) currentSearch.cancel('Operation canceled by the user.'); } ///////////////////////////////////////////////// currentSearch = CancelToken.source(); ///////////////////////////////////////////////// return new Promise(function(resolve, reject) { if (!terms || !terms.length) { return resolve([]); } if (!config) { config = {}; } config.params = params; config.cancelToken = currentSearch.token; // var requestOptions = { // params: options, // cancelToken: currentSearch.token, // } ///////////////////////////////////////////// //Retrieve the query results fluro.api.get(`/content/search/${terms}`, config).then(function(res) { resolve(res.data); }).catch(function(thrown) { if (axios.isCancel(thrown)) { // console.log('Request canceled', thrown.message); } else { // handle error } }); }); } /////////////////////////////////////////////////// /** * Retrieves a specific definition or data type * @alias FluroContent.type * @param {String} definitionName The defined type or definition name to retrieve * @param {Object} options Extra Configuration and options for how to search the database and how to render the results * @return {Promise} A promise that will resolve with the definition * * @example * fluro.content.type('song', options, config).then(function(definition) { * //Will return the definition * }) */ var typePromise; var typeCacheable = true; ///////////////////////////////////////////////// service.type = function(definitionName, params) { if (!params) { params = {}; } if (!definitionName) { throw Error('No definition name was provided'); } ///////////////////////////////////////////////// //If we are already requesting this definition if (!typePromise || !typeCacheable) { //Create a new promise typePromise = new Promise(function(resolve, reject) { // if (!config) { // config = {}; // } // config.params = params; // var requestOptions = { // params: options, // cancelToken: currentMentionSearch.token, // } ///////////////////////////////////////////// //Retrieve the definition from the server and send it back to //the user fluro.api.get(`/defined/${definitionName}`) .then(function(res) { resolve(res.data); typeCacheable = true; }).catch(function(err) { reject(err); typeCacheable = false; }); }); } ///////////////////////////////////////////////// return typePromise; } /////////////////////////////////////////////////// /** * Runs a search from the Fluro server for a specific mentionable user * @alias FluroContent.mention * @param {String} mentionID the Name or Mention ID of the persona to search for * @param {Object} options Extra Configuration and options for how to search the database and how to render the results * @param {Object} config Optional HTTP Request Configuration * @param {Integer} options.limit Extra Configuration and options for how to search the database and how to render the results * @param {Integer} options.managed Search for managed personas instead of Global Fluro user personas * @return {Array} An array of personas who can be mentioned * * @example * fluro.content.mention('john.smith', {limit:5}, config).then(function(results) { * //Will return a nested array with up to 5 personas * }) */ ///////////////////////////////////////////////// var currentMentionSearch; ///////////////////////////////////////////////// service.mention = function(terms, params, config) { if (!params) { params = {}; } if (!params.limit) { params.limit = 5; } ///////////////////////////////////////////////// if (currentMentionSearch) { // cancel the request (the message parameter is optional) currentMentionSearch.cancel('Operation canceled by the user.'); } ///////////////////////////////////////////////// currentMentionSearch = CancelToken.source(); ///////////////////////////////////////////////// return new Promise(function(resolve, reject) { if (!terms || !terms.length) { return resolve([]); } if (!config) { config = {}; } config.params = params; config.cancelToken = currentMentionSearch.token; // var requestOptions = { // params: options, // cancelToken: currentMentionSearch.token, // } ///////////////////////////////////////////// //Retrieve the query results fluro.api.get(`/mention/${terms}`, config).then(function(res) { resolve(res.data); }).catch(function(thrown) { if (axios.isCancel(thrown)) { // console.log('Request canceled', thrown.message); } else { // handle error } }); }); } /////////////////////////////////////////////////// /** * A helper function for retrieving the results of a specified query * @alias FluroContent.query * @param {String} queryID The id of the query you want to run * @param {Object} options The options for the query * @param {Object} options.params The query string parameters for the query that will be mapped ?one=value&two=value * @param {Object} options.variables Any query variables you wish to inject each key will be mapped ?variables[key]=value * @return {Promise} A promise that will be resolved with the results or an error */ service.query = function(queryID, options) { //Get as just a query queryID = fluro.utils.getStringID(queryID); if (!options) { options = {} } return new Promise(function(resolve, reject) { var requestOptions = { params: {} } //If there are query string parameters if (options.params) { requestOptions.params = options.params; } ///////////////////////////////////////////// if (options.variables) { var variableParams = _.reduce(options.variables, function(set, value, key) { set[`variables[${key}]`] = value; return set; }, {}) //Add it to our parameters _.assign(requestOptions.params, variableParams); } ///////////////////////////////////////////// //Retrieve the query results fluro.api.get(`/content/_query/${queryID}`, requestOptions).then(function(res) { resolve(res.data); }, reject); }) } /////////////////////////////////////////////////// /** * This function returns a single populated item by providing its _id * @alias FluroContent.get * @param {String} id The item's _id * @param {Object} params Extra query string parameters for the request * @return {Promise} A promise that will be resolved with the item or an error * @example * * //Retrieve just the title for item '5be504eabf33991239599d63' * fluro.content.get('5be504eabf33991239599d63', {select:'title'}) */ service.get = function(id, params) { //Ensure it's a simple single ID id = fluro.utils.getStringID(id); ///////////////////////////////////// if (!params) { params = {} } ///////////////////////////////////// return new Promise(function(resolve, reject) { var requestOptions = { params: {} } //If there are query string parameters if (params) { requestOptions.params = params; } ///////////////////////////////////////////// //Retrieve the query results fluro.api.get(`/content/get/${id}`, requestOptions).then(function(res) { resolve(res.data); }, reject); }) } /////////////////////////////////////////////////// /////////////////////////////////////////////////// /** * This function returns a single populated item by providing its _external id * @alias FluroContent.external * @param {String} externalID The item's _external id property * @param {Object} params Extra query string parameters for the request * @return {Promise} A promise that will be resolved with the item or an error * @example * * //Retrieve just the title for item with external id that matches '5be504-eabf33991-239599-d63' * fluro.content.external('5be504-eabf33991-239599-d63', {select:'title'}) */ service.external = function(id, params) { if (!params) { params = {} } return new Promise(function(resolve, reject) { var requestOptions = { params: {} } //If there are query string parameters if (params) { requestOptions.params = params; } ///////////////////////////////////////////// //Retrieve the query results fluro.api.get(`/content/external/${id}`, requestOptions).then(function(res) { resolve(res.data); }, reject); }) } /////////////////////////////////////////////////// /** * This function returns a single populated item by providing its slug * @alias FluroContent.slug * @param {String} slug The item's slug value * @param {Object} params Extra query string parameters for the request * @return {Promise} A promise that will be resolved with the item or an error * @example * * //Retrieve just the title for item with the slug 'my-article' * fluro.content.slug('my-article', {select:'title'}) */ service.slug = function(id, params) { if (!params) { params = {} } return new Promise(function(resolve, reject) { var requestOptions = { params: {} } //If there are query string parameters if (params) { requestOptions.params = params; } ///////////////////////////////////////////// //Retrieve the query results fluro.api.get(`/content/slug/${id}`, requestOptions).then(function(res) { resolve(res.data); }, reject); }) } /////////////////////////////////////////////////// /** * A helper function for retrieving the results of a dynamic query * @alias FluroContent.retrieve * @param {Object} criteria The query criteria * @param {Object} options Extra options and parameters * @return {Promise} A promise that will be resolved with the results or an error * @example * * //Find all events that have a status of active or archived where the endDate is greater than or equal to now and return the titles * fluro.content.retrieve({_type:'event', status:{$in:['active', 'archived']}, endDate:{$gte:"date('now')"}}}, {select:'title'}) */ service.retrieve = function(criteria, options) { if (!options) { options = {} } return new Promise(function(resolve, reject) { var requestOptions = { params: {} } //If there are query string parameters if (options) { requestOptions.params = options; } ///////////////////////////////////////////// //Retrieve the query results fluro.api.post(`/content/_query`, criteria, requestOptions).then(function(res) { resolve(res.data); }, reject); }) } /////////////////////////////////////////////////// /** * This function returns a list of related items * That either reference the specified item or are referenced by the provided item * @alias FluroContent.related * @param {String} id The item to find related content for * @param {Object} params Extra query string parameters for the request * @return {Promise} A promise that will be resolved with an array of related items * @example * * //Retrieve some related items for '5be504eabf33991239599d63' * fluro.content.related('5be504eabf33991239599d63', {select:'title'}) */ service.related = function(id, params) { id = fluro.utils.getStringID(id); if (!id) { throw Error(`No id specified ${id}`); } if (!params) { params = {} } return new Promise(function(resolve, reject) { var requestOptions = { params: {} } //If there are query string parameters if (params) { requestOptions.params = params; } var criteria = { _references: id, } // service.retrieve(criteria, requestOptions).then(resolve, reject); // ///////////////////////////////////////////// //Retrieve the query results fluro.api.get(`/content/related/${id}`, requestOptions).then(function(res) { resolve(res.data); }, reject); }) } /////////////////////////////////////////////////// /** * This function returns an interaction definition via the public 'form' API endpoint * This will only result successfully if the definition requested has the definition of 'form' and has the status of 'active' * @alias FluroContent.form * @param {String} id The id of the form to retrieve * @param {Object} options Extra options for the request * @param {Object} options.testing Whether to load the form in testing mode or not * @return {Promise} A promise that will be resolved with the form or an error * @example * * //Retrieve a form ('58dca23c21428d2d045a1cf7') in testing mode * fluro.content.form('58dca23c21428d2d045a1cf7', {testing:true}) */ service.form = function(id, options) { id = fluro.utils.getStringID(id); if (!id) { throw Error(`No id specified ${id}`); } if (!options) { options = {} } return new Promise(function(resolve, reject) { var requestOptions = { // params: {} } //If there are query string parameters // if (params) { // requestOptions.params = params; // } // service.retrieve(criteria, requestOptions).then(resolve, reject); // ///////////////////////////////////////////// //Retrieve the query results fluro.api.get(`/form/${id}`, requestOptions).then(function(res) { console.log('RESOLVE!', res.data); resolve(res.data); }, reject); }) } /////////////////////////////////////////////////// /** * This function makes it easy to submit form interactions via the Fluro API * @alias FluroContent.submitInteraction * @param {String} definitionName the definition of the form you want to submit eg. 'supportRequest' or 'contactUs'... * @param {Object} data The interaction data to submit * @param {Object} options Extra options for the request * @param {Object} options.reply The id of the post to reply to (If threaded conversation) * @return {Promise} A promise that will be resolved with an array of related items * @example * * //Retrieve some related items for '5be504eabf33991239599d63' * fluro.content.submitInteraction('5be504eabf33991239599d63', 'comment', {data:{customField:'My message'}}, {reply:'5be504eabf33991239599d63'}) */ service.submitInteraction = function(type, submission, options) { if (!options) { options = {} } return new Promise(function(resolve, reject) { var requestOptions = options; ///////////////////////////////////////////// //Retrieve the query results fluro.api.post(`/interact/${type}`, submission, requestOptions) .then(function(res) { resolve(res.data); },reject); }) } /////////////////////////////////////////////////// /** * This function makes it easy to create and attach a post to a specified piece of fluro content * @alias FluroContent.submitPost * @param {String} target The ID of the item to attach this post to * @param {String} definitionName the definition type of the post you want to create eg. 'note' or 'comment'... * @param {Object} data The post content to create * @param {Object} options Extra options for the request * @param {Object} options.reply The id of the post to reply to (If threaded conversation) * @return {Promise} A promise that will be resolved with an array of related items * @example * * //Retrieve some related items for '5be504eabf33991239599d63' * fluro.content.submitPost('5be504eabf33991239599d63', 'comment', {data:{customField:'My message'}}, {reply:'5be504eabf33991239599d63'}) */ service.submitPost = function(id, type, body, options) { id = fluro.utils.getStringID(id); if (!id) { throw Error(`No target specified ${id}`); } if (!options) { options = {} } return new Promise(function(resolve, reject) { var requestOptions = { // params: {} } // ///////////////////////////////////////////// //Retrieve the query results fluro.api.post(`/post/${id}/${type}`, body, requestOptions) .then(function(res) { resolve(res.data); },reject); }) } /////////////////////////////////////////////////// /** * This function makes it easy to retrieve the current thread of posts attached to a specific * item * @alias FluroContent.thread * @param {String} target The ID of the item to attach this post to * @param {String} definitionName the definition type of the post you want to create eg. 'note' or 'comment'... * @param {Object} data The post content to create * @param {Object} options Extra options for the request * @param {Object} options.reply The id of the post to reply to (If threaded conversation) * @return {Promise} A promise that will be resolved with an array of related items * @example * * //Retrieve the current post thread of all 'comments' attached to a specific content * fluro.content.thread('5be504eabf33991239599d63', 'comment', {data:{customField:'My message'}}, {reply:'5be504eabf33991239599d63'}) */ service.thread = function(id, type, options) { id = fluro.utils.getStringID(id); if (!id) { throw Error(`No target specified ${id}`); } if (!options) { options = {} } return new Promise(function(resolve, reject) { var requestOptions = { // params: {} } // ///////////////////////////////////////////// //Retrieve the query results fluro.api.get(`/post/${id}/${type}`, requestOptions) .then(function(res) { resolve(res.data); },reject); }) } /////////////////////////////////////////////////// /** * This function makes it easy to retrieve all distinct values for a specified field key * for a specified subset of items from the server, for instance if you wanted to retrieve all possible 'firstName' values from * a selection of content ids * @alias FluroContent.values * @param {Array} ids The ids you want to retrieve values for * @param {String} key the key of the field you want to retrieve the values for * @return {Promise} A promise that will be resolved with an array of possible values * @example * * * fluro.content.values(['5be504eabf33991239599d63'], 'firstName').then(function(values) { * //Would return ['Frank', 'Lucy', 'Marissa'] * }) */ service.values = function(ids, key, options) { ids = fluro.utils.arrayIDs(ids); // if (!ids | !ids.length ) { // throw Error(`No ids specified ${ids}`); // } if (!key | !key.length ) { throw Error(`No key specified `); } if (!options) { options = {} } return new Promise(function(resolve, reject) { var payload = { ids, key, } ///////////////////////////////////////////// var url =`/content/distinct/values`; if(options.type) { url =`/content/${options.type}/distinct/values`; } ///////////////////////////////////////////// //Retrieve the query results return fluro.api.post(url, payload, options) .then(function(res) { resolve(_.orderBy(res.data, function(entry) { return entry.title || entry; })); },reject); }) } /////////////////////////////////////////////////// /** * This function makes it easy to retrieve all distinct keys for a specified selection of ids * @alias FluroContent.keys * @param {Array} ids The ids you want to retrieve keys for * @param {Object} options extra options and query parameters for the http request * @return {Promise} A promise that will be resolved with an array of possible keys * @example * * * fluro.content.keys(['5be504eabf33991239599d63']).then(function(values) { * //Would return ['firstName', 'lastName', 'title', 'tags', 'realms'] * }) */ service.keys = function(ids, options) { ids = fluro.utils.arrayIDs(ids); if (!ids | !ids.length ) { throw Error(`No ids specified ${ids}`); } if (!options) { options = {} } return new Promise(function(resolve, reject) { var payload = { ids, } ///////////////////////////////////////////// var url =`/content/distinct/keys`; if(options.type) { url =`/content/${options.type}/distinct/keys`; } ///////////////////////////////////////////// //Retrieve the query results return fluro.api.post(url, payload, options) .then(function(res) { resolve(res.data); },reject); }) } /////////////////////////////////////////////////// return service; } export default FluroContent;