-
Notifications
You must be signed in to change notification settings - Fork 0
[DX-1285] Implement get-message to retrieve latest version by serial
#385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a58034e
2b3a6ee
6c7cfc1
b296802
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| import { Args, Flags } from "@oclif/core"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I think there's a confusing part here. If I publish a message on a non-mutable channel, I see a serial. If I then We should think about how we best handle this
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added error hint, now shows proper error message
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| import * as Ably from "ably"; | ||
|
|
||
| import { AblyBaseCommand } from "../../base-command.js"; | ||
| import { CommandError } from "../../errors/command-error.js"; | ||
| import { productApiFlags } from "../../flags.js"; | ||
| import { | ||
| formatMessageTimestamp, | ||
| formatMessagesOutput, | ||
| formatResource, | ||
| } from "../../utils/output.js"; | ||
| import type { MessageDisplayFields } from "../../utils/output.js"; | ||
|
|
||
| const MUTABLE_MESSAGES_HINT = | ||
| "The channel may not have mutableMessages enabled — without this rule, individual messages cannot be retrieved by serial. Please check the same using 'ably apps rules list'. If the 'Mutable Messages' rule is enabled, then make sure to enter correct message serial."; | ||
|
|
||
| export default class ChannelsGetMessage extends AblyBaseCommand { | ||
| static override args = { | ||
| channelName: Args.string({ | ||
| description: "The channel name", | ||
| required: true, | ||
| }), | ||
| messageSerial: Args.string({ | ||
| description: "The serial of the message to retrieve", | ||
| required: true, | ||
| }), | ||
| }; | ||
|
|
||
| static override description = | ||
| "Get the latest version of a message on an Ably channel. Requires `mutableMessages` enabled on the channel rule."; | ||
|
|
||
| static override examples = [ | ||
| '$ ably channels get-message my-channel "01234567890:0"', | ||
| '$ ably channels get-message my-channel "01234567890:0" --json', | ||
| '$ ably channels get-message my-channel "01234567890:0" --pretty-json', | ||
| '$ ably channels get-message my-channel "01234567890:0" --cipher YOUR_CIPHER_KEY', | ||
| ]; | ||
|
|
||
| static override flags = { | ||
| ...productApiFlags, | ||
| cipher: Flags.string({ | ||
| description: | ||
| "Decryption key for encrypted messages (base64-encoded or hex-encoded, supports AES-128-CBC and AES-256-CBC)", | ||
| }), | ||
| }; | ||
|
|
||
| async run(): Promise<void> { | ||
| const { args, flags } = await this.parse(ChannelsGetMessage); | ||
| const channelName = args.channelName; | ||
| const serial = args.messageSerial; | ||
|
|
||
| try { | ||
| const rest = await this.createAblyRestClient(flags); | ||
| if (!rest) return; | ||
|
|
||
| const channelOptions: Ably.ChannelOptions = {}; | ||
| if (flags.cipher) { | ||
| channelOptions.cipher = { key: flags.cipher }; | ||
| } | ||
|
|
||
| const channel = rest.channels.get(channelName, channelOptions); | ||
|
|
||
| this.logProgress( | ||
| `Fetching message ${formatResource(serial)} on channel ${formatResource(channelName)}`, | ||
| flags, | ||
| ); | ||
|
|
||
| const message = await channel.getMessage(serial); | ||
|
|
||
| const tracePayload = { | ||
| id: message.id, | ||
| timestamp: formatMessageTimestamp(message.timestamp), | ||
| channel: channelName, | ||
| event: message.name || undefined, | ||
| clientId: message.clientId, | ||
| connectionId: message.connectionId, | ||
| data: message.data as unknown, | ||
| encoding: message.encoding, | ||
| extras: message.extras as unknown, | ||
| action: | ||
| message.action === undefined ? undefined : String(message.action), | ||
| serial: message.serial, | ||
| version: message.version, | ||
| annotations: message.annotations, | ||
| }; | ||
| this.logCliEvent( | ||
| flags, | ||
| "channelGetMessage", | ||
| "messageRetrieved", | ||
| `Retrieved message ${message.serial ?? serial} on channel ${channelName}`, | ||
| tracePayload, | ||
| ); | ||
|
|
||
| if (this.shouldOutputJson(flags)) { | ||
| this.logJsonResult( | ||
| { | ||
| message: { | ||
| ...message, | ||
| // Stringify action for predictable JSON typing across commands | ||
| // (matches `channels subscribe`'s explicit normalisation). | ||
| action: | ||
| message.action === undefined | ||
| ? undefined | ||
| : String(message.action), | ||
| // Nullish-aware: a legitimate epoch-zero timestamp must not be | ||
| // dropped to undefined. | ||
| timestamp: | ||
| message.timestamp == null | ||
| ? undefined | ||
| : new Date(message.timestamp).toISOString(), | ||
| }, | ||
| }, | ||
| flags, | ||
| ); | ||
|
sacOO7 marked this conversation as resolved.
sacOO7 marked this conversation as resolved.
|
||
| } else { | ||
| const display: MessageDisplayFields = { | ||
| action: | ||
| message.action === undefined ? undefined : String(message.action), | ||
| channel: channelName, | ||
| clientId: message.clientId, | ||
| data: message.data, | ||
| event: message.name || undefined, | ||
| id: message.id, | ||
| serial: message.serial, | ||
| timestamp: message.timestamp ?? Date.now(), | ||
| version: message.version, | ||
| annotations: message.annotations, | ||
| }; | ||
| this.log(formatMessagesOutput([display])); | ||
| } | ||
| } catch (error) { | ||
| const cmdError = CommandError.from(error); | ||
| const enriched = | ||
| cmdError.code === 40400 | ||
| ? new CommandError(`${cmdError.message}\n${MUTABLE_MESSAGES_HINT}`, { | ||
| code: cmdError.code, | ||
| statusCode: cmdError.statusCode, | ||
| context: cmdError.context, | ||
| }) | ||
| : error; | ||
| this.fail(enriched, flags, "channelGetMessage", { | ||
| channel: channelName, | ||
| serial, | ||
| }); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me, the parenthesis on the end is a bit of a smell. Can we not describe it accurately enough in a single sentence?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
b296802