lunr-elastic-search
Version:
Uses Lunr.js to index and search the knowledge base.
143 lines (122 loc) • 4.95 kB
JavaScript
import * as util from '../util'
import _axios from 'axios'
import flatten from 'array-flatten'
const fields = ['title', 'description_text', 'body_text', 'tags', 'meta_title', 'meta_description', 'meta_keywords'] // the fields that will be indexed
/**
* The lunr.js builder plugin.
* Meant to be used like `builder.use(plugin)`
*
* @memberof plugin.freshdesk
* @return {void}
*/
function plugin () {
this.ref('id')
fields.forEach(f => this.field(f))
}
/**
* Fetches all the solution articles and forum posts from the designated source.
*
* @memberof plugin.freshdesk
* @param {string} domain the domain of the API to make the request to
* @param {string} user the username for the API
* @param {string} pass the password for the API
* @return {object[]} a list of processed solution articles, forum topics, and forum comments
*/
async function fetch (
domain,
user,
pass
) {
const solutions = _axios.create({
baseURL: `https://${domain}.freshdesk.com/api/v2/solutions/`,
auth: {
username: user,
password: pass
}
})
// gets a list of all the articles
const categories = await get(solutions, 'categories')
const folders = await getAllItemsIn(categories, solutions, id => `categories/${id}/folders`)
const articles = await getAllItemsIn(folders, solutions, id => `folders/${id}/articles`)
const processedArticles = await getAllItemsIn(articles, solutions, id => `articles/${id}`).then(arts => arts.map(a => process(a, 'solution'))) // process each article after fetching it
const posts = _axios.create({
baseURL: `https://${domain}.freshdesk.com/api/v2/discussions/`,
auth: {
username: user,
password: pass
}
})
// gets a list of all the articles
const discussionCategories = await get(posts, 'categories')
const forums = await getAllItemsIn(discussionCategories, posts, id => `categories/${id}/forums`).then(ary => ary.filter(t => hasComments(t)))
const topics = await getAllItemsIn(forums, posts, id => `forums/${id}/topics`).then(ary => ary.filter(t => hasComments(t)))
const comments = await getAllItemsIn(topics, posts, id => `topics/${id}/comments`).then(cmts => cmts.map(comment => process(comment, 'forum')))
return [...comments, ...processedArticles, ...topics.map(e => process(e, 'forum'))]
}
function hasComments (topic) {
return topic.comments_count > 0
}
/**
* Helper function to get all the items from a list of parent resources.
*
* @private
* @param {object[]} ary list of parent resources
* @param {axios.Instance} axios the preconfigured axios instance
* @param {Function} buildURL function to build the endpoint URL relative to the baseURL in the axios instance
* @return {object[]} a list of child resources
*/
async function getAllItemsIn (ary, axios, buildURL) {
return flatten.depth(await Promise.all(ary.map(({id}) => get(axios, buildURL(id)))), 1)
}
/**
* Helper function to make axios get request.
*
* @private
* @param {axios.Instance} axios the preconfigured axios instance
* @param {string} url the url to access relative to the baseURL in the axios instance
* @return {object} data of axios response
*/
async function get (axios, url) {
return axios.get(url).then(({data}) => {
let ret
const baseURL = axios.defaults.baseURL
if (data.constructor.name === 'Array') {
ret = data.map(doc => { return {...doc, documentSource: {url: baseURL + url}} })
} else if (data.constructor.name === 'Object') {
ret = {...data, documentSource: {url: baseURL + url}}
}
return ret
})
}
/**
* Modify the documents so that they can be indexed correctly.
*
* @memberof plugin.freshdesk
* @param {object} doc the document to be processed
* @param {?string} source the source of the document
* @return {object} the processed document
*/
function process (doc, source = '') {
const obj = {...doc} // dup the document
obj.tags = obj.tags ? obj.tags.join(' ') : '' // lunr can't index an array of strings so join the tags into 1 string
if (obj.seo_data) Object.keys(obj.seo_data).forEach(k => { obj[k] = obj.seo_data[k] }) // map seo_data to root field so that it can indexed (lunr can't index nested fields)
if (obj.documentSource) obj.documentSource = {...obj.documentSource, type: source}
fields.forEach(f => { obj[f] = obj[f] || '' }) // if any fields are null then it will impact lunr search results
return obj
}
/**
* Checks to see if certain functions are defined.
*
* @return {Boolean}
* @memberOf plugin.freshdesk
*/
function isSupported () {
return util.definedFunction(Object.keys) && util.definedFunction([].forEach)
}
/**
* Contains functions for interacting with the freshdesk API and processing those responses.
*
* @namespace freshdesk
* @memberOf plugin
*/
export {fields, plugin, process, fetch, isSupported}