| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143 |
1×
4×
28×
4×
4×
8×
8×
8×
4×
4×
16×
8×
16×
8×
24×
44×
52×
52×
52×
72×
8×
8×
52×
36×
36×
36×
36×
252×
36×
1×
| 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 Eif (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}
|