bananareporter
Version:
Easily generate a report from multiple sources
205 lines (204 loc) • 8.88 kB
JavaScript
;
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,
}),
};