// interface to FreshDesk Solutions (knowledge base) api const fetch = require('node-fetch'); class FreshdeskApi { constructor (baseUrl, apiKey) { this.baseUrl = baseUrl; this._auth = 'Basic ' + new Buffer(`${apiKey}:X`).toString('base64'); this.defaultHeaders = { 'Content-Type': 'application/json', 'Authorization': this._auth }; this.rateLimited = false; } /** * Checks the status of a response. If status is not ok, or the body is not json raise exception * @param {object} res The response object * @returns {object} the response if it is ok */ checkStatus (res) { if (res.ok) { if (res.headers.get('content-type').indexOf('application/json') !== -1) { return res; } throw new Error(`response not json: ${res.headers.get('content-type')}`); } let err = new Error(`response ${res.statusText}`); err.code = res.status; throw err; } listCategories () { return fetch(`${this.baseUrl}/api/v2/solutions/categories`, {headers: this.defaultHeaders}) .then(this.checkStatus) .then(res => res.json()); } listFolders (category) { return fetch( `${this.baseUrl}/api/v2/solutions/categories/${category.id}/folders`, {headers: this.defaultHeaders}) .then(this.checkStatus) .then(res => res.json()); } listArticles (folder) { return fetch( `${this.baseUrl}/api/v2/solutions/folders/${folder.id}/articles`, {headers: this.defaultHeaders}) .then(this.checkStatus) .then(res => res.json()); } updateCategoryTranslation (id, locale, body) { if (this.rateLimited) { process.stdout.write(`Rate limited, skipping id: ${id} for ${locale}\n`); return -1; } return fetch( `${this.baseUrl}/api/v2/solutions/categories/${id}/${locale}`, { method: 'put', body: JSON.stringify(body), headers: this.defaultHeaders }) .then(this.checkStatus) .then(res => res.json()) .catch((err) => { if (err.code === 404) { // not found, try create instead return fetch( `${this.baseUrl}/api/v2/solutions/categories/${id}/${locale}`, { method: 'post', body: JSON.stringify(body), headers: this.defaultHeaders }) .then(this.checkStatus) .then(res => res.json()); } // re-raise the error otherwise throw err; }); } updateFolderTranslation (id, locale, body) { if (this.rateLimited) { process.stdout.write(`Rate limited, skipping id: ${id} for ${locale}\n`); return -1; } return fetch( `${this.baseUrl}/api/v2/solutions/folders/${id}/${locale}`, { method: 'put', body: JSON.stringify(body), headers: this.defaultHeaders }) .then(this.checkStatus) .then(res => res.json()) .catch((err) => { if (err.code === 404) { // not found, try create instead return fetch( `${this.baseUrl}/api/v2/solutions/folders/${id}/${locale}`, { method: 'post', body: JSON.stringify(body), headers: this.defaultHeaders }) .then(this.checkStatus) .then(res => res.json()); } // re-raise the error otherwise throw err; }); } updateArticleTranslation (id, locale, body) { if (this.rateLimited) { process.stdout.write(`Rate limited, skipping id: ${id} for ${locale}\n`); return -1; } return fetch( `${this.baseUrl}/api/v2/solutions/articles/${id}/${locale}`, { method: 'put', body: JSON.stringify(body), headers: this.defaultHeaders }) .then(this.checkStatus) .then(res => res.json()) .catch((err) => { if (err.code === 404) { // not found, try create instead return fetch( `${this.baseUrl}/api/v2/solutions/articles/${id}/${locale}`, { method: 'post', body: JSON.stringify(body), headers: this.defaultHeaders }) .then(this.checkStatus) .then(res => res.json()); } if (err.code === 429) { this.rateLimited = true; } // re-raise the error otherwise throw err; }); } } module.exports = FreshdeskApi;