import { parse } from 'jsonc-parser'; import { readFileSync } from 'fs'; import { parseArgs } from 'util'; import { createInterface } from 'readline/promises'; const helpMsg = ` Синхронизация суммирующих задач * --config=sync-root-issues.jsonc - конфигурационный файл для постановки задачи на выполнение синхронизации * --yes - автоматически отвечать 'y' Пример конфигурационного файла: { "redmineUrl": "http://redmine.my-company.local", "redmineToken": "... ... ...", "eventEmitterUrl": "http://localhost:3000", "rootIds": [ 123001, 130012 ] } `; var args = parseArgs({ options: { config: { type: 'string', }, yes: { type: 'boolean', }, }, }); if (!args?.values?.config) { console.log(helpMsg); process.exit(0); } function readConfig(fileName) { const rawData = readFileSync(fileName, { encoding: 'utf8' }); return parse(rawData); } var config = readConfig(args.values.config); function getAllRootIssueIds(issues, res) { for (let i = 0; i < issues.length; i++) { const issue = issues[i]; if (issue.children && issue.children.length > 0) { res.push(issue.id); getAllRootIssueIds(issue.children, res); } } return res; } async function loadIssueFromRedmine(issueId) { const token = config?.redmineToken || ''; const redmineUrl = config?.redmineUrl || ''; const url = `${redmineUrl}/issues/${issueId}.json?include=children`; const resp = await fetch(url, { headers: { 'X-Redmine-API-Key': token, }, }); const data = await resp.json(); return data?.issue || null; } async function loadAllRootIssueIds() { const rootIssueIds = config?.rootIds || []; const rootIssues = []; for (let i = 0; i < rootIssueIds.length; i++) { const rootIssueId = rootIssueIds[i]; const issue = await loadIssueFromRedmine(rootIssueId); if (issue) { rootIssues.push(issue); } } const allRootIssueIds = getAllRootIssueIds(rootIssues, []); return allRootIssueIds; } async function questionYesOrNo(question, yesMsg, noMsg) { const readline = createInterface({ input: process.stdin, output: process.stdout, }); const answer = await readline.question(`\n${question} (y/n)\n> `); const res = answer == 'y'; readline.close(); console.log(res ? `\n${yesMsg}` : `\n${noMsg}`); return res; } async function main() { console.log( `Конфигурационный файл:\n${JSON.stringify(config, null, ' ')}\n`, ); if (!args.values.yes) { const res = await questionYesOrNo( 'Продолжаем?', 'Продолжаем...', 'Прервано', ); if (!res) process.exit(); } const rootIds = await loadAllRootIssueIds(); console.log(`Задачи для синхронизации - ${JSON.stringify(rootIds)}`); if (!args.values.yes) { const res = await questionYesOrNo( 'Продолжаем?', 'Продолжаем...', 'Прервано', ); if (!res) process.exit(); } console.log(`Отправка задач на синхронизацию...`); await await fetch( `${config.eventEmitterUrl}/redmine-event-emitter/append-issues`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(rootIds), }, ); console.log(`Синхронизация запущена`); } await main();