@ably/cli
Version:
Ably CLI for Pub/Sub, Chat and Spaces
238 lines (237 loc) • 11.3 kB
JavaScript
import { Args, Flags } from "@oclif/core";
import chalk from "chalk";
import { SpacesBaseCommand } from "../../../spaces-base-command.js";
export default class SpacesLocationsGetAll extends SpacesBaseCommand {
static args = {
spaceId: Args.string({
description: "Space ID to get locations from",
required: true,
}),
};
static description = "Get all current locations in a space";
static examples = [
"$ ably spaces locations get-all my-space",
"$ ably spaces locations get-all my-space --json",
"$ ably spaces locations get-all my-space --pretty-json",
];
static flags = {
...SpacesBaseCommand.globalFlags,
format: Flags.string({
char: "f",
default: "text",
description: "Output format",
options: ["text", "json"],
}),
};
realtimeClient = null;
spacesClient = null;
space = null;
async run() {
const { args, flags } = await this.parse(SpacesLocationsGetAll);
const { spaceId } = args;
try {
const setupResult = await this.setupSpacesClient(flags, spaceId);
this.realtimeClient = setupResult.realtimeClient;
this.spacesClient = setupResult.spacesClient;
this.space = setupResult.space;
if (!this.realtimeClient || !this.spacesClient || !this.space) {
this.error("Failed to initialize clients or space");
return;
}
await new Promise((resolve, reject) => {
const checkConnection = () => {
const { state } = this.realtimeClient.connection;
if (state === "connected") {
resolve();
}
else if (state === "failed" ||
state === "closed" ||
state === "suspended") {
reject(new Error(`Connection failed with state: ${state}`));
}
else {
setTimeout(checkConnection, 100);
}
};
checkConnection();
});
this.log(`Connecting to space: ${chalk.cyan(spaceId)}...`);
await this.space.enter();
await new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error("Timed out waiting for space connection"));
}, 5000);
const checkSpaceStatus = () => {
try {
if (this.realtimeClient.connection.state === "connected") {
clearTimeout(timeout);
this.log(`${chalk.green("Connected to space:")} ${chalk.cyan(spaceId)}`);
resolve();
}
else if (this.realtimeClient.connection.state === "failed" ||
this.realtimeClient.connection.state === "closed" ||
this.realtimeClient.connection.state === "suspended") {
clearTimeout(timeout);
reject(new Error(`Space connection failed with connection state: ${this.realtimeClient.connection.state}`));
}
else {
setTimeout(checkSpaceStatus, 100);
}
}
catch (error) {
clearTimeout(timeout);
reject(error);
}
};
checkSpaceStatus();
});
if (!this.shouldOutputJson(flags)) {
this.log(`Fetching locations for space ${chalk.cyan(spaceId)}...`);
}
let locations = [];
try {
const { items: locationsFromSpace } = await this.space.locations.getAll();
if (locationsFromSpace && typeof locationsFromSpace === "object") {
if (Array.isArray(locationsFromSpace)) {
locations = locationsFromSpace;
}
else if (Object.keys(locationsFromSpace).length > 0) {
locations = Object.entries(locationsFromSpace).map(([memberId, locationData]) => ({
location: locationData,
memberId,
}));
}
}
const validLocations = locations.filter((item) => {
if (item === null || item === undefined)
return false;
let locationData;
if (item.location !== undefined) {
locationData = item.location;
}
else if (item.data === undefined) {
const { clientId: _clientId, connectionId: _connectionId, id: _id, member: _member, memberId: _memberId, userId: _userId, ...rest } = item;
if (Object.keys(rest).length === 0)
return false;
locationData = rest;
}
else {
locationData = item.data;
}
if (locationData === null || locationData === undefined)
return false;
if (typeof locationData === "object" &&
Object.keys(locationData).length === 0)
return false;
return true;
});
if (this.shouldOutputJson(flags)) {
this.log(this.formatJsonOutput({
locations: validLocations.map((item) => {
const memberId = item.memberId ||
item.member?.clientId ||
item.clientId ||
item.id ||
item.userId ||
"Unknown";
const locationData = item.location ||
item.data ||
(() => {
const { clientId: _clientId, connectionId: _connectionId, id: _id, member: _member, memberId: _memberId, userId: _userId, ...rest } = item;
return rest;
})();
return {
isCurrentMember: item.member?.isCurrentMember || false,
location: locationData,
memberId,
};
}),
spaceId,
success: true,
timestamp: new Date().toISOString(),
}, flags));
}
else if (!validLocations || validLocations.length === 0) {
this.log(chalk.yellow("No locations are currently set in this space."));
}
else {
const locationsCount = validLocations.length;
this.log(`\n${chalk.cyan("Current locations")} (${chalk.bold(String(locationsCount))}):\n`);
for (const location of validLocations) {
// Check if location has 'current' property with expected structure
if ("current" in location &&
typeof location.current === "object" &&
location.current !== null &&
"member" in location.current) {
const locationWithCurrent = location;
const { member } = locationWithCurrent.current;
this.log(`Member ID: ${chalk.cyan(member.memberId || member.clientId)}`);
try {
const locationData = location.location ||
location.data ||
(() => {
const { clientId: _clientId, connectionId: _connectionId, id: _id, member: _member, memberId: _memberId, userId: _userId, ...rest } = location;
return rest;
})();
this.log(`- ${chalk.blue(member.memberId || member.clientId)}:`);
this.log(` ${chalk.dim("Location:")} ${JSON.stringify(locationData, null, 2)}`);
if (member.isCurrentMember) {
this.log(` ${chalk.green("(Current member)")}`);
}
}
catch (error) {
this.log(`- ${chalk.red("Error displaying location item")}: ${error instanceof Error ? error.message : String(error)}`);
}
}
else {
// Simpler display if location doesn't have expected structure
this.log(`- ${chalk.blue("Member")}:`);
this.log(` ${chalk.dim("Location:")} ${JSON.stringify(location, null, 2)}`);
}
}
}
}
catch (error) {
if (this.shouldOutputJson(flags)) {
this.log(this.formatJsonOutput({
error: error instanceof Error ? error.message : String(error),
spaceId,
status: "error",
success: false,
}, flags));
}
else {
this.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
}
}
try {
await this.space.leave();
this.log(chalk.green("\nSuccessfully disconnected."));
}
catch (error) {
this.log(chalk.yellow(`Error leaving space: ${error instanceof Error ? error.message : String(error)}`));
}
}
catch (error) {
if (error === undefined || error === null) {
this.log(chalk.red("An unknown error occurred (error object is undefined or null)"));
}
else {
const errorMessage = error instanceof Error
? error.message
: String(error || "Unknown error");
this.log(chalk.red(`Error: ${errorMessage}`));
}
}
finally {
try {
if (this.realtimeClient) {
this.realtimeClient.close();
}
}
catch (closeError) {
this.log(chalk.yellow(`Error closing client: ${closeError instanceof Error ? closeError.message : String(closeError)}`));
}
}
}
}