UNPKG

@ably/cli

Version:

Ably CLI for Pub/Sub, Chat and Spaces

141 lines (140 loc) 5.71 kB
import chalk from "chalk"; import { AblyBaseCommand } from "./base-command.js"; import { ControlApi } from "./services/control-api.js"; export class ControlBaseCommand extends AblyBaseCommand { // Add flags specific to control API commands static globalFlags = { ...AblyBaseCommand.globalFlags, // Other Control API specific flags can be added here }; /** * Create a Control API instance for making requests */ createControlApi(flags) { let accessToken = flags["access-token"] || process.env.ABLY_ACCESS_TOKEN; if (!accessToken) { const account = this.configManager.getCurrentAccount(); if (!account) { this.error(`No access token provided. Please specify --access-token or configure an account with "ably accounts login".`); } accessToken = account.accessToken; } if (!accessToken) { this.error(`No access token provided. Please specify --access-token or configure an account with "ably accounts login".`); } return new ControlApi({ accessToken, controlHost: flags["control-host"], }); } formatDate(timestamp) { return new Date(timestamp).toLocaleString(); } /** * Resolves the app ID from the flags, current configuration, or interactive prompt * @param flags The command flags * @returns The app ID */ async resolveAppId(flags) { // If app is provided in flags, use it (it could be ID or name) if (flags.app) { // Try to parse as app ID or name return await this.resolveAppIdFromNameOrId(flags.app); } // Try to get from current app configuration const currentAppId = this.configManager.getCurrentAppId(); if (currentAppId) { return currentAppId; } // No app ID found, try to prompt for it return await this.promptForApp(); } /** * Resolves an app ID from a name or ID * @param appNameOrId The app name or ID to resolve * @returns The app ID */ async resolveAppIdFromNameOrId(appNameOrId) { // If it looks like an app ID (UUID format), just return it if (this.isValidAppId(appNameOrId)) { return appNameOrId; } // Otherwise, need to look it up by name const controlApi = this.createControlApi({}); try { const apps = await controlApi.listApps(); const matchingApp = apps.find((app) => app.name === appNameOrId); if (matchingApp) { return matchingApp.id; } this.error(chalk.red(`App "${appNameOrId}" not found. Please provide a valid app ID or name.`)); } catch (error) { this.error(chalk.red(`Failed to look up app "${appNameOrId}": ${error instanceof Error ? error.message : String(error)}`)); } return appNameOrId; // This will never be reached, but TypeScript needs a return } /** * Prompts the user to select an app * @returns The selected app ID */ async promptForApp() { try { const controlApi = this.createControlApi({}); const apps = await controlApi.listApps(); if (apps.length === 0) { this.error(chalk.red("No apps found in your account. Please create an app first.")); } // Prompt the user to choose an app from the list const app = await this.interactiveHelper.selectApp(controlApi); if (!app) { this.error(chalk.red("No app selected.")); } // Save the selected app ID as the current app this.configManager.setCurrentApp(app.id); return app.id; } catch (error) { this.error(chalk.red(`Failed to get apps: ${error instanceof Error ? error.message : String(error)}`)); } return ""; // This will never be reached, but TypeScript needs a return } /** * Simple validation to check if a string looks like an app ID (UUID) */ isValidAppId(id) { // Basic UUID format check: 8-4-4-4-12 hex digits return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id); } /** * Run the Control API command with standard error handling */ async runControlCommand(flags, apiCall, errorMessage = "Error executing command") { try { // Display account info at start of command this.showAuthInfoIfNeeded(flags); // Create API and execute the command const api = this.createControlApi(flags); return await apiCall(api); } catch (error) { const isJsonMode = this.shouldOutputJson(flags); // Safely get the error message const errorMessageText = `${errorMessage}: ${error instanceof Error ? error.message : String(error)}`; if (isJsonMode) { // Pass the error object itself as details // The `outputJsonError` helper handles stringifying it this.outputJsonError(errorMessageText, error); // Exit explicitly in JSON mode after outputting error to stderr this.exit(1); } else { // Use the standard oclif error for non-JSON modes this.error(errorMessageText); } // This line is technically unreachable due to this.error or this.exit // but needed for TypeScript's control flow analysis return null; } } }