run method

  1. @override
Future<void> run()
override

Runs this command.

The return value is wrapped in a Future if necessary and returned by CommandRunner.runCommand.

Implementation

@override
Future<void> run() async {
  if (argResults!.rest.isEmpty) {
    AppLogger.error('Usage: appflight keys revoke <label-or-prefix>');
    exit(ExitCodes.usage);
  }
  final typed = argResults!.rest.first;
  final force = argResults!['force'] as bool;
  final skipConfirm = argResults!['yes'] as bool;

  // Checked before any credentials lookup or HTTP call — no terminal and
  // no --yes can never be confirmed, so there is nothing to resolve for.
  // Silently skipping the prompt on no-TTY would be both untestable and
  // unsafe in CI; refusing is what makes this reachable from a plain
  // subprocess with no fake server behind it.
  if (!skipConfirm && !stdin.hasTerminal) {
    AppLogger.error(
      'Refusing to revoke without confirmation (no terminal). Pass '
      '--yes to confirm.',
    );
    exit(ExitCodes.usage);
  }

  final creds = requireCredentials();
  final dio = ApiClient.instance.forKey(creds.apiKey);

  try {
    final keys = await KeysService.ins.list(apiDio: dio);
    final matches = resolveLabelOrPrefix(keys, typed);
    final currentKeyId = KeysCommand.currentKeyId(creds, keys);
    final resolution = RevokeResolution.resolve(
      matches: matches,
      currentKeyId: currentKeyId,
      force: force,
    );

    switch (resolution.decision) {
      case RevokeDecision.noMatch:
        AppLogger.error(KeysCommand.noMatchMessage(typed));
        exit(ExitCodes.usage);
      case RevokeDecision.ambiguous:
        AppLogger.error(
          KeysCommand.ambiguousMessage(typed, resolution.matches!),
        );
        exit(ExitCodes.usage);
      case RevokeDecision.inUse:
        AppLogger.error(
          KeysCommand.inUseRefusalMessage(
            resolution.match!['name'] as String,
          ),
        );
        exit(ExitCodes.usage);
      case RevokeDecision.proceed:
        break;
    }

    final match = resolution.match!;
    if (!skipConfirm && !_confirm(match)) {
      AppLogger.info('Cancelled.');
      return;
    }

    await KeysService.ins.revoke(apiDio: dio, keyId: match['id'] as String);
    AppLogger.success('Revoked "${match['name']}" (${match['prefix']}).');
  } catch (e, st) {
    handleCommandFailure(e, st);
  }
}