scratch-l10n/scripts/freshdesk-api.js
Chris Garrity 30b1975e94 Scripts to pull translations and update Freshdesk
* Fixed a bug in the push script (tags were not saved as correct structured json, so they weren’t getting put into transifex)
* extended FreshDesk API with functions to update knowledge base. Functions automatically try to create the item if it isn’t found for updating
* tx-pull-help-names: pulls category and folder name translations from transifex and updates them in Freshdesk (Note, since we don’t send people into the knowledge base, these aren’t really public, but the actual folders and categories have to exist to be able to save articles)
* tx-pull-help-articles: pull article translations from Transifex and update the Freshdesk KB.
* help-utils: utility functions for the tx-pull-help-* scripts. Handles limiting the number of things happening in parallel - currently two languages may be processed at the same time.
2020-05-06 14:46:09 -04:00

155 lines
5.3 KiB
JavaScript

// 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;