|
| 1 | +import { Args, Flags } from "@oclif/core"; |
| 2 | + |
| 3 | +import { AblyBaseCommand } from "../../../base-command.js"; |
| 4 | +import { productApiFlags } from "../../../flags.js"; |
| 5 | +import { |
| 6 | + formatClientId, |
| 7 | + formatCountLabel, |
| 8 | + formatHeading, |
| 9 | + formatIndex, |
| 10 | + formatLabel, |
| 11 | + formatLimitWarning, |
| 12 | + formatMessageTimestamp, |
| 13 | + formatProgress, |
| 14 | + formatResource, |
| 15 | + formatWarning, |
| 16 | +} from "../../../utils/output.js"; |
| 17 | +import { |
| 18 | + buildPaginationNext, |
| 19 | + collectPaginatedResults, |
| 20 | + formatPaginationWarning, |
| 21 | +} from "../../../utils/pagination.js"; |
| 22 | + |
| 23 | +// Chat SDK maps room presence to the underlying channel: roomName::$chat |
| 24 | +const chatChannelName = (roomName: string) => `${roomName}::$chat`; |
| 25 | + |
| 26 | +export default class RoomsPresenceGetAll extends AblyBaseCommand { |
| 27 | + static override args = { |
| 28 | + room: Args.string({ |
| 29 | + description: "Room to get presence members for", |
| 30 | + required: true, |
| 31 | + }), |
| 32 | + }; |
| 33 | + |
| 34 | + static override description = |
| 35 | + "Get all current presence members in a chat room"; |
| 36 | + |
| 37 | + static override examples = [ |
| 38 | + "$ ably rooms presence get-all my-room", |
| 39 | + "$ ably rooms presence get-all my-room --limit 50", |
| 40 | + "$ ably rooms presence get-all my-room --json", |
| 41 | + "$ ably rooms presence get-all my-room --pretty-json", |
| 42 | + ]; |
| 43 | + |
| 44 | + static override flags = { |
| 45 | + ...productApiFlags, |
| 46 | + limit: Flags.integer({ |
| 47 | + default: 100, |
| 48 | + description: "Maximum number of results to return", |
| 49 | + min: 1, |
| 50 | + }), |
| 51 | + }; |
| 52 | + |
| 53 | + async run(): Promise<void> { |
| 54 | + const { args, flags } = await this.parse(RoomsPresenceGetAll); |
| 55 | + |
| 56 | + try { |
| 57 | + const client = await this.createAblyRestClient(flags); |
| 58 | + if (!client) return; |
| 59 | + |
| 60 | + const { room: roomName } = args; |
| 61 | + const channelName = chatChannelName(roomName); |
| 62 | + |
| 63 | + if (!this.shouldOutputJson(flags)) { |
| 64 | + this.log( |
| 65 | + formatProgress( |
| 66 | + `Fetching presence members for room: ${formatResource(roomName)}`, |
| 67 | + ), |
| 68 | + ); |
| 69 | + } |
| 70 | + |
| 71 | + this.logCliEvent( |
| 72 | + flags, |
| 73 | + "presence", |
| 74 | + "fetching", |
| 75 | + `Fetching presence members for room ${roomName}`, |
| 76 | + { room: roomName }, |
| 77 | + ); |
| 78 | + |
| 79 | + const firstPage = await client.channels |
| 80 | + .get(channelName) |
| 81 | + .presence.get({ limit: flags.limit }); |
| 82 | + |
| 83 | + const { items, hasMore, pagesConsumed } = await collectPaginatedResults( |
| 84 | + firstPage, |
| 85 | + flags.limit, |
| 86 | + ); |
| 87 | + |
| 88 | + this.logCliEvent( |
| 89 | + flags, |
| 90 | + "presence", |
| 91 | + "fetched", |
| 92 | + `Fetched ${items.length} presence members`, |
| 93 | + { room: roomName, count: items.length }, |
| 94 | + ); |
| 95 | + |
| 96 | + // Show pagination warning early (before main output) |
| 97 | + const paginationWarning = formatPaginationWarning( |
| 98 | + pagesConsumed, |
| 99 | + items.length, |
| 100 | + ); |
| 101 | + if (paginationWarning && !this.shouldOutputJson(flags)) { |
| 102 | + this.log(paginationWarning); |
| 103 | + } |
| 104 | + |
| 105 | + if (this.shouldOutputJson(flags)) { |
| 106 | + const presenceMembers = items.map((member) => ({ |
| 107 | + clientId: member.clientId, |
| 108 | + connectionId: member.connectionId, |
| 109 | + data: member.data ?? null, |
| 110 | + extras: member.extras ?? null, |
| 111 | + updatedAt: formatMessageTimestamp(member.timestamp), |
| 112 | + })); |
| 113 | + const next = buildPaginationNext(hasMore); |
| 114 | + this.logJsonResult( |
| 115 | + { |
| 116 | + presenceMembers, |
| 117 | + hasMore, |
| 118 | + ...(next && { next }), |
| 119 | + total: items.length, |
| 120 | + }, |
| 121 | + flags, |
| 122 | + ); |
| 123 | + } else if (items.length === 0) { |
| 124 | + this.logToStderr( |
| 125 | + formatWarning("No members currently present in this room."), |
| 126 | + ); |
| 127 | + } else { |
| 128 | + this.log( |
| 129 | + `\n${formatHeading(`Presence members in room: ${roomName}`)} (${formatCountLabel(items.length, "member")}):\n`, |
| 130 | + ); |
| 131 | + |
| 132 | + for (let i = 0; i < items.length; i++) { |
| 133 | + const member = items[i]; |
| 134 | + this.log(`${formatIndex(i + 1)}`); |
| 135 | + this.log( |
| 136 | + ` ${formatLabel("Client ID")} ${formatClientId(member.clientId)}`, |
| 137 | + ); |
| 138 | + this.log(` ${formatLabel("Connection ID")} ${member.connectionId}`); |
| 139 | + if (member.data !== null && member.data !== undefined) { |
| 140 | + this.log(` ${formatLabel("Data")} ${JSON.stringify(member.data)}`); |
| 141 | + } |
| 142 | + if ( |
| 143 | + member.extras !== null && |
| 144 | + member.extras !== undefined && |
| 145 | + typeof member.extras === "object" && |
| 146 | + Object.keys(member.extras).length > 0 |
| 147 | + ) { |
| 148 | + this.log( |
| 149 | + ` ${formatLabel("Extras")} ${JSON.stringify(member.extras)}`, |
| 150 | + ); |
| 151 | + } |
| 152 | + this.log( |
| 153 | + ` ${formatLabel("Updated At")} ${formatMessageTimestamp(member.timestamp)}`, |
| 154 | + ); |
| 155 | + this.log(""); |
| 156 | + } |
| 157 | + |
| 158 | + if (hasMore) { |
| 159 | + const warning = formatLimitWarning( |
| 160 | + items.length, |
| 161 | + flags.limit, |
| 162 | + "members", |
| 163 | + ); |
| 164 | + if (warning) this.log(warning); |
| 165 | + } |
| 166 | + } |
| 167 | + } catch (error) { |
| 168 | + this.fail(error, flags, "roomPresenceGetAll", { |
| 169 | + room: args.room, |
| 170 | + }); |
| 171 | + } |
| 172 | + } |
| 173 | +} |
0 commit comments