@ably/cli
Version:
Ably CLI for Pub/Sub, Chat and Spaces
141 lines (140 loc) • 5.47 kB
JavaScript
import { Args } from "@oclif/core";
import { ChatBaseCommand } from "../../../chat-base-command.js";
export default class RoomsOccupancyGet extends ChatBaseCommand {
static args = {
roomId: Args.string({
description: "Room ID to get occupancy for",
required: true,
}),
};
static description = "Get current occupancy metrics for a room";
static examples = [
"$ ably rooms occupancy get my-room",
'$ ably rooms occupancy get --api-key "YOUR_API_KEY" my-room',
"$ ably rooms occupancy get my-room --json",
"$ ably rooms occupancy get my-room --pretty-json",
];
static flags = {
...ChatBaseCommand.globalFlags,
};
ablyClient = null;
chatClient = null;
room = null;
async forceCloseConnections() {
try {
// First try to release the room
if (this.room) {
await Promise.race([
this.room.detach(),
new Promise(resolve => setTimeout(resolve, 1000)) // 1s timeout
]);
}
}
catch {
// Ignore detach errors
}
try {
// Release room from chat client
if (this.chatClient && this.room) {
await Promise.race([
this.chatClient.rooms.release(this.room.name),
new Promise(resolve => setTimeout(resolve, 1000)) // 1s timeout
]);
}
}
catch {
// Ignore release errors
}
try {
// Force close the Ably client
if (this.ablyClient) {
await Promise.race([
new Promise((resolve) => {
if (this.ablyClient.connection.state === 'closed') {
resolve();
return;
}
const onClosed = () => {
resolve();
};
// Listen for closed and failed states
this.ablyClient.connection.once('closed', onClosed);
this.ablyClient.connection.once('failed', onClosed);
this.ablyClient.close();
// Cleanup listeners after 2 seconds
setTimeout(() => {
this.ablyClient.connection.off('closed', onClosed);
this.ablyClient.connection.off('failed', onClosed);
resolve();
}, 2000);
}),
new Promise(resolve => setTimeout(resolve, 2000)) // 2s timeout
]);
}
}
catch {
// Ignore close errors
}
}
async run() {
const { args, flags } = await this.parse(RoomsOccupancyGet);
try {
// Create Chat client
this.chatClient = await this.createChatClient(flags);
// Get the underlying Ably client for cleanup
this.ablyClient = this._chatRealtimeClient;
if (!this.chatClient) {
this.error("Failed to create Chat client");
return;
}
const { roomId } = args;
// Get the room with occupancy enabled
this.room = await this.chatClient.rooms.get(roomId, {});
// Attach to the room to access occupancy with timeout
await Promise.race([
this.room.attach(),
new Promise((_, reject) => setTimeout(() => reject(new Error('Room attach timeout')), 10000))
]);
// Get occupancy metrics using the Chat SDK's occupancy API
const occupancyMetrics = await Promise.race([
this.room.occupancy.get(),
new Promise((_, reject) => setTimeout(() => reject(new Error("Occupancy get timeout")), 5000)),
]);
// Output the occupancy metrics based on format
if (this.shouldOutputJson(flags)) {
this.log(this.formatJsonOutput({
metrics: occupancyMetrics,
roomId,
success: true,
}, flags));
}
else {
this.log(`Occupancy metrics for room '${roomId}':\n`);
this.log(`Connections: ${occupancyMetrics.connections ?? 0}`);
this.log(`Presence Members: ${occupancyMetrics.presenceMembers ?? 0}`);
}
}
catch (error) {
if (this.shouldOutputJson(flags)) {
this.log(this.formatJsonOutput({
error: error instanceof Error ? error.message : String(error),
roomId: args.roomId,
success: false,
}, flags));
}
else {
this.error(`Error fetching room occupancy: ${error instanceof Error ? error.message : String(error)}`);
}
}
finally {
// Force cleanup with timeouts to ensure the command exits
await this.forceCloseConnections();
// Force exit after cleanup
setTimeout(() => {
if (process.env.NODE_ENV !== 'test') {
process.exit(0);
}
}, 100);
}
}
}