mirror of
https://github.com/scratchfoundation/scratch-l10n.git
synced 2024-12-24 06:32:33 -05:00
145 lines
4.9 KiB
JavaScript
Executable file
145 lines
4.9 KiB
JavaScript
Executable file
#!/usr/bin/env node
|
|
|
|
/**
|
|
* @fileoverview
|
|
* Script get Knowledge base articles from Freshdesk and push them to transifex.
|
|
*/
|
|
|
|
import {txPush, txCreateResource} from '../lib/transifex.js';
|
|
|
|
const args = process.argv.slice(2);
|
|
|
|
const usage = `
|
|
Pull knowledge base articles from Freshdesk and push to scratch-help project on transifex. Usage:
|
|
node tx-push-help.js
|
|
NOTE:
|
|
FRESHDESK_TOKEN environment variable needs to be set to a FreshDesk API key with
|
|
access to the Knowledge Base.
|
|
TX_TOKEN environment variable needs to be set with a Transifex API token. See
|
|
the Localization page on the GUI wiki for information about setting up Transifex.
|
|
`;
|
|
// Fail immediately if the API tokens are not defined, or there any argument
|
|
if (!process.env.TX_TOKEN || !process.env.FRESHDESK_TOKEN || args.length > 0) {
|
|
process.stdout.write(usage);
|
|
process.exit(1);
|
|
}
|
|
|
|
import FreshdeskApi from './freshdesk-api.js';
|
|
|
|
const FD = new FreshdeskApi('https://mitscratch.freshdesk.com', process.env.FRESHDESK_TOKEN);
|
|
const TX_PROJECT = 'scratch-help';
|
|
|
|
const categoryNames = {};
|
|
const folderNames = {};
|
|
|
|
/**
|
|
* Generate a transifex id from the name and id field of an objects. Remove spaces and '/'
|
|
* from the name and append '.<id>' Transifex ids (slugs) have a max length of 50. Use at most
|
|
* 30 characters of the name to allow for Freshdesk id, and a suffix like '_json'
|
|
* @param {object} item data from Freshdesk that includes the name and id of a category or folder
|
|
* @return {string} generated transifex id
|
|
*/
|
|
const makeTxId = item => {
|
|
return `${item.name.replace(/[ /]/g, '').slice(0, 30)}_${item.id}`;
|
|
};
|
|
|
|
const txPushResource = async (name, articles, type) => {
|
|
const resourceData = {
|
|
slug: name,
|
|
name: name,
|
|
i18n_type: type,
|
|
priority: 0, // default to normal priority
|
|
content: articles
|
|
};
|
|
|
|
try {
|
|
await txPush(TX_PROJECT, name, articles);
|
|
} catch (err) {
|
|
if (err.statusCode !== 404) {
|
|
process.stdout.write(`Transifex Error: ${err.message}\n`);
|
|
process.stdout.write(
|
|
`Transifex Error ${err.response.statusCode.toString()}: ${err.response.body}\n`);
|
|
process.exitCode = 1;
|
|
return;
|
|
}
|
|
|
|
// file not found - create it, but also give message
|
|
process.stdout.write(`Transifex Resource not found, creating: ${name}\n`);
|
|
if (err.statusCode === 404) {
|
|
await txCreateResource(TX_PROJECT, resourceData);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* get a flattened list of folders associated with the specified categories
|
|
* @param {object[]} categories array of categories the folders belong to
|
|
* @return {Promise<object[]>} flattened list of folders from all requested categories
|
|
*/
|
|
const getFolders = async (categories) => {
|
|
const categoryFolders = await Promise.all(
|
|
categories.map(category => FD.listFolders(category))
|
|
);
|
|
return [].concat(...categoryFolders);
|
|
};
|
|
|
|
const PUBLISHED = 2; // in Freshdesk, draft status = 1, and published = 2
|
|
|
|
/**
|
|
* Save articles in a particular folder
|
|
* @param {object} folder The folder object
|
|
*/
|
|
const saveArticles = async folder => {
|
|
await FD.listArticles(folder)
|
|
.then(json => {
|
|
const txArticles = json.reduce((strings, current) => {
|
|
if (current.status === PUBLISHED) {
|
|
strings[`${current.id}`] = {
|
|
title: {
|
|
string: current.title
|
|
},
|
|
description: {
|
|
string: current.description
|
|
}
|
|
};
|
|
if (current.tags.length > 0) {
|
|
strings[`${current.id}`].tags = {string: current.tags.toString()};
|
|
}
|
|
}
|
|
return strings;
|
|
}, {});
|
|
process.stdout.write(`Push ${folder.name} articles to Transifex\n`);
|
|
txPushResource(`${makeTxId(folder)}_json`, txArticles, 'STRUCTURED_JSON');
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @param {object[]} folders Array of folders containing articles to be saved
|
|
*/
|
|
const saveArticleFolders = async (folders) => {
|
|
await Promise.all(folders.map(folder => saveArticles(folder)));
|
|
};
|
|
|
|
const syncSources = async () => {
|
|
await FD.listCategories()
|
|
.then(json => {
|
|
// save category names for translation
|
|
for (let cat of json.values()) {
|
|
categoryNames[`${makeTxId(cat)}`] = cat.name;
|
|
}
|
|
return json;
|
|
})
|
|
.then(getFolders)
|
|
.then(data => {
|
|
data.forEach(item => {
|
|
folderNames[`${makeTxId(item)}`] = item.name;
|
|
});
|
|
process.stdout.write('Push category and folder names to Transifex\n');
|
|
txPushResource('categoryNames_json', categoryNames, 'KEYVALUEJSON');
|
|
txPushResource('folderNames_json', folderNames, 'KEYVALUEJSON');
|
|
return data;
|
|
})
|
|
.then(saveArticleFolders);
|
|
};
|
|
|
|
syncSources();
|