From 5d426bcf949a0a69249da7e76d00e2b9b6170bc4 Mon Sep 17 00:00:00 2001 From: mrjvs Date: Tue, 23 Sep 2025 22:31:32 +0200 Subject: [PATCH] chore: refactor how outputs are created in CLI --- src/cli/apps.cmd.ts | 27 ++++++++++++++++-------- src/cli/files.cmd.ts | 24 ++++++++++++---------- src/cli/output.ts | 49 +++++++++++++++++++++++++++++++++----------- src/cli/tasks.cmd.ts | 16 ++++++++++----- 4 files changed, 80 insertions(+), 36 deletions(-) diff --git a/src/cli/apps.cmd.ts b/src/cli/apps.cmd.ts index 59f3b16..dd26b6b 100644 --- a/src/cli/apps.cmd.ts +++ b/src/cli/apps.cmd.ts @@ -7,13 +7,21 @@ const listCmd = new Command('ls') .action(commandHandler<[]>(async (cmd): Promise => { const ctx = getCliContext(); const { apps } = await ctx.grpc.listKnownBOSSApps({}); - logOutputList(cmd.format, apps.map(v => ({ - name: prettyTrunc(v.name, 20) - })), { - bossAppId: 'App ID', - name: 'Name', - titleId: 'Title ID', - titleRegion: 'Title region' + logOutputList(apps, { + format: cmd.format, + onlyIncludeKeys: ['bossAppId', 'name', 'tasks', 'titleRegion'], + mapping: { + bossAppId: 'App ID', + name: 'Name', + titleId: 'Title ID', + titleRegion: 'Title region' + }, + prettify(key, value) { + if (key === 'name') { + return prettyTrunc(value, 20); + } + return value; + } }); })); @@ -30,12 +38,15 @@ const viewCmd = new Command('view') return; } - logOutputObject(cmd.format, { + const obj = { appId: app.bossAppId, name: app.name, titleId: app.titleId, titleRegion: app.titleRegion, knownTasks: app.tasks + }; + logOutputObject(obj, { + format: cmd.format }); })); diff --git a/src/cli/files.cmd.ts b/src/cli/files.cmd.ts index c995130..5e51b0f 100644 --- a/src/cli/files.cmd.ts +++ b/src/cli/files.cmd.ts @@ -18,16 +18,16 @@ const listCmd = new Command('ls') bossAppId: appId, taskId: taskId }); - logOutputList(cmd.format, files.map(v => ({ - ...v, - size: v.size, - dataId: v.dataId - }), { - dataId: 'Data ID', - name: 'Name', - type: 'Type', - size: 'Size (bytes)' - })); + logOutputList(files, { + format: cmd.format, + onlyIncludeKeys: ['dataId', 'name', 'type', 'size'], + mapping: { + dataId: 'Data ID', + name: 'Name', + type: 'Type', + size: 'Size (bytes)' + } + }); })); const viewCmd = new Command('view') @@ -47,7 +47,7 @@ const viewCmd = new Command('view') console.log(`Could not find task file with data ID ${dataId} in task ${taskId}`); return; } - logOutputObject(cmd.format, { + logOutputObject({ dataId: file.dataId, name: file.name, type: file.type, @@ -62,6 +62,8 @@ const viewCmd = new Command('view') }, createdAt: new Date(Number(file.createdTimestamp)), updatedAt: new Date(Number(file.updatedTimestamp)) + }, { + format: cmd.format }); })); diff --git a/src/cli/output.ts b/src/cli/output.ts index 7b64c5e..0342157 100644 --- a/src/cli/output.ts +++ b/src/cli/output.ts @@ -1,11 +1,25 @@ export type FormattableObject = Record; -export type FieldMapping = Record; export type FormatOption = 'json' | 'pretty'; -function mapOutputObject(obj: FormattableObject, mapping: FieldMapping): FormattableObject { +function preprocessObject(obj: FormattableObject, ops: FormattedOutputOptions): FormattableObject { + const onlyIncludeKeys = ops.onlyIncludeKeys; + if (!onlyIncludeKeys) { + return obj; + } + const entries = Object.entries(obj); - const mappedEntries = entries.map((v) => { - const newKey = mapping[v[0]] ?? v[0]; + const filteredEntries = entries.filter(v => onlyIncludeKeys.includes(v[0])); + return Object.fromEntries(filteredEntries); +} + +function makePrettyOutputObject(obj: FormattableObject, ops: FormattedOutputOptions): FormattableObject { + const entries = Object.entries(obj); + const prettifiedEntries = entries.map((v) => { + const value = ops.prettify ? ops.prettify(v[0], v[1]) : v[1]; + return [v[0], value]; + }); + const mappedEntries = prettifiedEntries.map((v) => { + const newKey = ops.mapping?.[v[0]] ?? v[0]; return [newKey, v[1]] as const; }); return Object.fromEntries(mappedEntries); @@ -18,19 +32,30 @@ function jsonReplacer(key: string, value: any): any { return value; } -export function logOutputList(format: FormatOption, items: T[], mapping: FieldMapping = {}): void { - if (format === 'json') { - console.log(JSON.stringify(items, jsonReplacer, 2)); +export type FormattedOutputOptions = { + format: FormatOption; + onlyIncludeKeys?: Array; + mapping?: Partial>; + prettify?: (key: string, value: any) => any; +}; + +export function logOutputList(items: T[], ops: FormattedOutputOptions): void { + const processedItems = items.map(v => preprocessObject(v, ops)); + if (ops.format === 'json') { + console.log(JSON.stringify(processedItems, jsonReplacer, 2)); return; } - const mappedItems = items.map(item => mapOutputObject(item, mapping)); + + const mappedItems = processedItems.map(item => makePrettyOutputObject(item, ops)); console.table(mappedItems); } -export function logOutputObject(format: FormatOption, obj: T, mapping: FieldMapping = {}): void { - if (format === 'json') { - console.log(JSON.stringify(obj, jsonReplacer, 2)); +export function logOutputObject(obj: T, ops: FormattedOutputOptions): void { + const processedObj = preprocessObject(obj, ops); + if (ops.format === 'json') { + console.log(JSON.stringify(processedObj, jsonReplacer, 2)); return; } - console.log(mapOutputObject(obj, mapping)); + + console.log(makePrettyOutputObject(processedObj, ops)); } diff --git a/src/cli/tasks.cmd.ts b/src/cli/tasks.cmd.ts index 432344d..ef41894 100644 --- a/src/cli/tasks.cmd.ts +++ b/src/cli/tasks.cmd.ts @@ -10,10 +10,14 @@ const listCmd = new Command('ls') const ctx = getCliContext(); const { tasks } = await ctx.grpc.listTasks({}); const filteredTasks = tasks.filter(v => v.bossAppId === appId); - logOutputList(cmd.format, filteredTasks, { - id: 'Task ID', - description: 'Description', - status: 'Status' + logOutputList(filteredTasks, { + format: cmd.format, + mapping: { + id: 'Task ID', + description: 'Description', + status: 'Status' + }, + onlyIncludeKeys: ['id', 'description', 'status'] }); })); @@ -30,7 +34,7 @@ const viewCmd = new Command('view') console.log(`Could not find task with ID ${taskId} in app ${appId}`); return; } - logOutputObject(cmd.format, { + logOutputObject({ taskId: task.id, inGameId: task.inGameId, description: task.description, @@ -40,6 +44,8 @@ const viewCmd = new Command('view') status: task.status, createdAt: new Date(Number(task.createdTimestamp)), updatedAt: new Date(Number(task.updatedTimestamp)) + }, { + format: cmd.format }); }));