UNPKG

bananareporter

Version:

Easily generate a report from multiple sources

205 lines (204 loc) 8.88 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const core_1 = require("@oclif/core"); const yaml = require("js-yaml"); const node_fs_1 = require("node:fs"); const path = require("node:path"); const dayjs = require("dayjs"); const core_2 = require("../../core"); const base_1 = require("../../integrations/base"); const gitlab_1 = require("../../integrations/gitlab"); const papaparse_1 = require("papaparse"); const lodash_1 = require("lodash"); const logger_1 = require("../../util/logger"); const github_1 = require("../../integrations/github"); const todotxt_1 = require("../../integrations/todotxt"); const zod_1 = require("zod"); const node_path_1 = require("node:path"); const git_cli_1 = require("../../integrations/git-cli"); class Run extends core_1.Command { static dateStringValidation(input) { if (!dayjs(input).isValid()) { throw new Error(`provided date "${input}" is not valid, make sure it's valid ISO8601 string`); } return input; } async run() { // this.log('🍌 Banana Reporter') const { flags } = await this.parse(Run); logger_1.logger.debug('run CLI flags', flags); const configFileLocation = flags.config || path.join(this.config.configDir, 'config.yaml'); logger_1.logger.debug(`run configFileLocation: ${configFileLocation}`); const configContent = yaml.load((0, node_fs_1.readFileSync)(configFileLocation).toString()); logger_1.logger.debug('run configContent', configContent); const validatedConfig = (0, core_2.loadAndValidateConfig)({ ...configContent, configFileLocation, ...(0, lodash_1.omit)(flags, ['include-raw-object', 'config']), includeRawObject: flags['include-raw-object'], }); logger_1.logger.debug('run validatedConfig', validatedConfig); // TODO the sources with different hosts / types can be called in parallel let reportList = []; for (const [i, s] of validatedConfig.sources.entries()) { logger_1.logger.debug(`run source loop ${i}`, s); this.log(`working on ${s.type} (${i + 1}) source`); let res = []; switch (s.type) { case base_1.SourceType.Enum.gitlab: { const integrationLoaded = new gitlab_1.GitlabIntegration(s, validatedConfig); // eslint-disable-next-line no-await-in-loop res = await integrationLoaded.fetchData(); break; } case base_1.SourceType.Enum.github: { const integrationLoaded = new github_1.GithubIntegration(s, validatedConfig); // eslint-disable-next-line no-await-in-loop res = await integrationLoaded.fetchData(); break; } case base_1.SourceType.Enum['git-cli']: { const integrationLoaded = new git_cli_1.GitCliIntegration(s, validatedConfig); // eslint-disable-next-line no-await-in-loop res = await integrationLoaded.fetchData(); break; } case base_1.SourceType.Enum['todo.txt']: { const integrationLoaded = new todotxt_1.TodoTxtIntegration(s, validatedConfig); // eslint-disable-next-line no-await-in-loop res = await integrationLoaded.fetchData(); break; } default: { this.error(`unknown source provided ${s.type}`); } } logger_1.logger.debug(`run source loop integration res length ${res.length}`); // eslint-disable-next-line unicorn/prefer-spread reportList = reportList.concat(res); } // remove any duplicates (based on "id") reportList = [...new Map(reportList.map(v => [v.id, v])).values()]; if (reportList.length === 0) { this.warn('no entries found, make sure the date range provided and the sources are correct'); return; } // re order by date ASC reportList.sort((a, b) => { return dayjs(a.date).diff(b.date); }); // output let outputStr = ''; // eslint-disable-next-line unicorn/prefer-switch if (validatedConfig.format === core_2.BananaOutputFileFormat.Enum.csv) { const fieldsColumns = ['date', 'type', 'username', 'projectName', 'description']; logger_1.logger.debug('run converting json to csv'); const csvOutput = (0, papaparse_1.unparse)({ fields: fieldsColumns, data: reportList, }); outputStr = csvOutput; } else if (validatedConfig.format === core_2.BananaOutputFileFormat.Enum.json) { outputStr = JSON.stringify(reportList, null, 2); } else if (validatedConfig.format === core_2.BananaOutputFileFormat.Enum.jsonl) { logger_1.logger.debug('run converting json to jsonl'); outputStr = reportList.map(r => JSON.stringify(r)).join('\n'); } const fileExt = `.${validatedConfig.format}`; let outFile = validatedConfig.out; logger_1.logger.debug(`Run.flags.out.default ${Run.flags.out.default}`); logger_1.logger.debug(`fileExt ${fileExt}`); logger_1.logger.debug(`outFile ${outFile}`); // if --out was provided and it's a directory // use the default file name try { if ((0, node_fs_1.statSync)(outFile).isDirectory()) { outFile = (0, node_path_1.resolve)(outFile, Run.flags.out.default); logger_1.logger.debug(`outFile is a directory, updated to ${outFile}`); } } catch (error) { logger_1.logger.debug('statSync error', error); } // add file extension if (!outFile.endsWith(fileExt)) { if (outFile === Run.flags.out.default) { outFile = outFile .replace(`.${Run.flags.format.default}`, fileExt); } else { outFile += fileExt; } } if (outFile.includes('$FROM') || outFile.includes('$TO')) { outFile = outFile .replace('$FROM', validatedConfig.from) .replace('$TO', validatedConfig.to); } logger_1.logger.debug(`run saving to ${outFile} with file content of size ${outputStr.length}`); (0, node_fs_1.writeFileSync)(outFile, outputStr); this.log(`report with ${reportList.length} entries saved to ${outFile}`); // this.log(`run! ${flags.config} ${JSON.stringify(configContent)}`) } async catch(error) { if (error instanceof zod_1.z.ZodError) { const errMsg = error.issues.map(i => `${i.message}: ${i.path.join(', ')}`).join('\n'); throw new TypeError(`validation failed, ${errMsg}`); } throw error; } } exports.default = Run; Run.description = 'Run report'; Run.examples = [ `$ banana-reporter run --from 2023-01-01 --to 2023-01-31 report with 138 entries saved to ./bananareporter.json `, ]; Run.flags = { config: core_1.Flags.string({ char: 'c', description: 'config file location, by default ~/.config/bananareporter/config.yaml', required: false, env: 'BANANAREPORTER_CONFIG_PATH', }), from: core_1.Flags.string({ description: 'from date (ISO8601)', helpValue: `${dayjs().startOf('month').format('YYYY-MM-DD')}`, required: false, }), to: core_1.Flags.string({ description: 'to date (ISO8601)', helpValue: `${dayjs().endOf('month').format('YYYY-MM-DD')}`, required: false, dependsOn: ['from'], }), out: core_1.Flags.file({ char: 'o', aliases: ['output'], description: 'file path or directory to save the output', required: true, default: './bananareporter_$FROM__$TO.json', }), format: core_1.Flags.string({ description: 'output file format', options: Object.values(core_2.BananaOutputFileFormat.Values), required: true, default: core_2.BananaOutputFileFormat.Enum.json, }), delay: core_1.Flags.integer({ description: 'global delay in millisecons between http requests', default: 300, }), 'include-raw-object': core_1.Flags.boolean({ description: 'include raw object in json/jsonl reporter output', default: false, }), 'disable-welcome-emoji': core_1.Flags.boolean({ description: 'disable banana welcome emoji', default: false, hidden: true, }), };