Skip to content

Commit f27bde1

Browse files
committed
Updated history and subscribe message fields with missing data fields
1 parent 7c78e27 commit f27bde1

3 files changed

Lines changed: 130 additions & 67 deletions

File tree

src/commands/channels/history.ts

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
import { Args, Flags } from "@oclif/core";
22
import * as Ably from "ably";
3-
import chalk from "chalk";
43

54
import { AblyBaseCommand } from "../../base-command.js";
65
import { productApiFlags, timeRangeFlags } from "../../flags.js";
7-
import { formatMessageData } from "../../utils/json-formatter.js";
86
import { buildHistoryParams } from "../../utils/history.js";
97
import { errorMessage } from "../../utils/errors.js";
108
import {
11-
countLabel,
9+
formatMessagesOutput,
1210
formatTimestamp,
1311
formatMessageTimestamp,
1412
limitWarning,
15-
resource,
13+
toMessageJson,
1614
} from "../../utils/output.js";
15+
import type { MessageDisplayFields } from "../../utils/output.js";
1716

1817
export default class ChannelsHistory extends AblyBaseCommand {
1918
static override args = {
@@ -85,41 +84,30 @@ export default class ChannelsHistory extends AblyBaseCommand {
8584
const history = await channel.history(historyParams);
8685
const messages = history.items;
8786

87+
// Build display fields from history results
88+
const displayMessages: MessageDisplayFields[] = messages.map(
89+
(message, index) => ({
90+
channel: channelName,
91+
clientId: message.clientId,
92+
data: message.data,
93+
event: message.name || "(none)",
94+
id: message.id,
95+
indexPrefix: `[${index + 1}] ${formatTimestamp(formatMessageTimestamp(message.timestamp))}`,
96+
serial: message.serial,
97+
timestamp: message.timestamp ?? Date.now(),
98+
}),
99+
);
100+
88101
// Display results based on format
89102
if (this.shouldOutputJson(flags)) {
90-
this.log(this.formatJsonOutput({ messages }, flags));
91-
} else {
92-
if (messages.length === 0) {
93-
this.log("No messages found in the channel history.");
94-
return;
95-
}
96-
97103
this.log(
98-
`Found ${countLabel(messages.length, "message")} in the history of channel: ${resource(channelName)}`,
104+
this.formatJsonOutput(
105+
displayMessages.map((msg) => toMessageJson(msg)),
106+
flags,
107+
),
99108
);
100-
this.log("");
101-
102-
for (const [index, message] of messages.entries()) {
103-
const timestampDisplay = message.timestamp
104-
? formatTimestamp(formatMessageTimestamp(message.timestamp))
105-
: chalk.dim("[Unknown timestamp]");
106-
107-
this.log(`${chalk.dim(`[${index + 1}]`)} ${timestampDisplay}`);
108-
this.log(
109-
`${chalk.dim("Event:")} ${chalk.yellow(message.name || "(none)")}`,
110-
);
111-
112-
if (message.clientId) {
113-
this.log(
114-
`${chalk.dim("Client ID:")} ${chalk.blue(message.clientId)}`,
115-
);
116-
}
117-
118-
this.log(chalk.dim("Data:"));
119-
this.log(formatMessageData(message.data));
120-
121-
this.log("");
122-
}
109+
} else {
110+
this.log(formatMessagesOutput(displayMessages));
123111

124112
const warning = limitWarning(messages.length, flags.limit, "messages");
125113
if (warning) this.log(warning);

src/commands/channels/subscribe.ts

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ import {
99
productApiFlags,
1010
rewindFlag,
1111
} from "../../flags.js";
12-
import { formatMessageData } from "../../utils/json-formatter.js";
1312
import {
13+
formatMessagesOutput,
1414
listening,
1515
progress,
1616
resource,
1717
success,
18-
formatTimestamp,
19-
formatMessageTimestamp,
18+
toMessageJson,
2019
} from "../../utils/output.js";
20+
import type { MessageDisplayFields } from "../../utils/output.js";
2121

2222
export default class ChannelsSubscribe extends AblyBaseCommand {
2323
static override args = {
@@ -195,46 +195,30 @@ export default class ChannelsSubscribe extends AblyBaseCommand {
195195

196196
channel.subscribe((message: Ably.Message) => {
197197
this.sequenceCounter++;
198-
const timestamp = formatMessageTimestamp(message.timestamp);
199-
const messageEvent = {
198+
199+
const msgFields: MessageDisplayFields = {
200200
channel: channel.name,
201201
clientId: message.clientId,
202-
connectionId: message.connectionId,
203202
data: message.data,
204-
encoding: message.encoding,
205203
event: message.name || "(none)",
206204
id: message.id,
207-
timestamp,
205+
serial: message.serial,
206+
timestamp: message.timestamp ?? Date.now(),
208207
...(flags["sequence-numbers"]
209-
? { sequence: this.sequenceCounter }
208+
? { sequencePrefix: `${chalk.dim(`[${this.sequenceCounter}]`)} ` }
210209
: {}),
211210
};
212-
this.logCliEvent(
213-
flags,
214-
"subscribe",
215-
"messageReceived",
216-
`Received message on channel ${channel.name}`,
217-
messageEvent,
218-
);
219211

220212
if (this.shouldOutputJson(flags)) {
221-
this.log(this.formatJsonOutput(messageEvent, flags));
222-
} else {
223-
const name = message.name || "(none)";
224-
const sequencePrefix = flags["sequence-numbers"]
225-
? `${chalk.dim(`[${this.sequenceCounter}]`)}`
226-
: "";
227-
228-
// Message header with timestamp and channel info
229-
this.log(
230-
`${formatTimestamp(timestamp)}${sequencePrefix} ${chalk.cyan(`Channel: ${channel.name}`)} | ${chalk.yellow(`Event: ${name}`)}`,
231-
);
232-
233-
// Message data with consistent formatting
234-
this.log(chalk.dim("Data:"));
235-
this.log(formatMessageData(message.data));
213+
const jsonMsg = toMessageJson(msgFields);
214+
if (flags["sequence-numbers"]) {
215+
jsonMsg.sequence = this.sequenceCounter;
216+
}
236217

237-
this.log(""); // Empty line for better readability
218+
this.log(this.formatJsonOutput(jsonMsg, flags));
219+
} else {
220+
this.log(formatMessagesOutput([msgFields]));
221+
this.log(""); // Empty line for better readability between messages
238222
}
239223
});
240224
}
@@ -258,6 +242,7 @@ export default class ChannelsSubscribe extends AblyBaseCommand {
258242
}
259243

260244
this.log(listening("Listening for messages."));
245+
this.log("");
261246
}
262247

263248
this.logCliEvent(

src/utils/output.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import chalk, { type ChalkInstance } from "chalk";
2+
import { formatMessageData, isJsonData } from "./json-formatter.js";
23

34
export function progress(message: string): string {
45
return `${message}...`;
@@ -63,6 +64,95 @@ export function limitWarning(
6364
return null;
6465
}
6566

67+
/**
68+
* Fields for consistent message display across subscribe and history commands.
69+
* All fields use the same names and format for both human-readable and JSON output.
70+
* Timestamp is raw milliseconds (Unix epoch) — not converted to ISO string.
71+
*/
72+
export interface MessageDisplayFields {
73+
channel: string;
74+
clientId?: string;
75+
data: unknown;
76+
event: string;
77+
id?: string;
78+
indexPrefix?: string;
79+
sequencePrefix?: string;
80+
serial?: string;
81+
timestamp: number;
82+
}
83+
84+
/**
85+
* Format an array of messages for human-readable console output.
86+
* Each message shows all fields on separate lines, messages separated by blank lines.
87+
* Returns "No messages found." for empty arrays.
88+
*/
89+
export function formatMessagesOutput(messages: MessageDisplayFields[]): string {
90+
if (messages.length === 0) {
91+
return "No messages found.";
92+
}
93+
94+
const formatted = messages.map((msg) => {
95+
const lines: string[] = [];
96+
97+
if (msg.indexPrefix) {
98+
lines.push(chalk.dim(msg.indexPrefix));
99+
}
100+
101+
const timestampLine = `${chalk.dim("Timestamp:")} ${msg.timestamp}`;
102+
lines.push(
103+
msg.sequencePrefix
104+
? `${msg.sequencePrefix}${timestampLine}`
105+
: timestampLine,
106+
`${chalk.dim("Channel:")} ${resource(msg.channel)}`,
107+
`${chalk.dim("Event:")} ${chalk.yellow(msg.event)}`,
108+
);
109+
110+
if (msg.id) {
111+
lines.push(`${chalk.dim("ID:")} ${msg.id}`);
112+
}
113+
114+
if (msg.clientId) {
115+
lines.push(`${chalk.dim("Client ID:")} ${chalk.blue(msg.clientId)}`);
116+
}
117+
118+
if (msg.serial) {
119+
lines.push(`${chalk.dim("Serial:")} ${msg.serial}`);
120+
}
121+
122+
if (isJsonData(msg.data)) {
123+
lines.push(`${chalk.dim("Data:")}\n${formatMessageData(msg.data)}`);
124+
} else {
125+
lines.push(`${chalk.dim("Data:")} ${String(msg.data)}`);
126+
}
127+
128+
return lines.join("\n");
129+
});
130+
131+
return formatted.join("\n\n");
132+
}
133+
134+
/**
135+
* Convert a single MessageDisplayFields to a plain object for JSON output.
136+
* Includes all required fields, omits undefined optional fields.
137+
*
138+
* Usage:
139+
* Single message (subscribe): toMessageJson(msg)
140+
* Array of messages (history): messages.map(toMessageJson)
141+
*/
142+
export function toMessageJson(
143+
msg: MessageDisplayFields,
144+
): Record<string, unknown> {
145+
return {
146+
timestamp: msg.timestamp,
147+
channel: msg.channel,
148+
event: msg.event,
149+
...(msg.id ? { id: msg.id } : {}),
150+
...(msg.clientId ? { clientId: msg.clientId } : {}),
151+
...(msg.serial ? { serial: msg.serial } : {}),
152+
data: msg.data,
153+
};
154+
}
155+
66156
export function formatPresenceAction(action: string): {
67157
symbol: string;
68158
color: ChalkInstance;

0 commit comments

Comments
 (0)