Skip to content

Commit

Permalink
implement token create command
Browse files Browse the repository at this point in the history
  • Loading branch information
codekeyz committed Feb 12, 2024
1 parent 32291b2 commit fe75025
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import 'dart:async';

import 'package:mason_logger/mason_logger.dart';

import '../../command.dart';
import '../../utils/api.dart';
import '../../utils/prompts.dart';

class TokenCreateCommand extends BaseGlobeCommand {
TokenCreateCommand() {
Expand Down Expand Up @@ -32,6 +36,38 @@ class TokenCreateCommand extends BaseGlobeCommand {
FutureOr<int> run() async {
requireAuth();

return 0;
final validated = await scope.validate();

final projects = await selectProjects(
validated.organization,
logger: logger,
api: api,
scope: scope,
);
if (projects.isEmpty) {
return ExitCode.software.code;
}

final projectNames = projects.map((e) => cyan.wrap(e.slug)).join(', ');
final createTokenProgress =
logger.progress('Creating Token for $projectNames');

try {
final token = await api.createToken(
orgId: validated.organization.slug,
name: 'New Token',
projectUuids: projects.map((e) => e.id).toList(),
expiresAt: DateTime(2024, 12),
);
createTokenProgress.complete("Here's your token: ${cyan.wrap(token)}");
return ExitCode.success.code;
} on ApiException catch (e) {
createTokenProgress.fail('✗ Failed to create token: ${e.message}');
return ExitCode.software.code;
} catch (e, s) {
createTokenProgress.fail('✗ Failed to create token: $e');
logger.detail(s.toString());
return ExitCode.software.code;
}
}
}
73 changes: 73 additions & 0 deletions packages/globe_cli/lib/src/utils/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,41 @@ class GlobeApi {

return Deployment.fromJson(response);
}

Future<String> createToken({
required String orgId,
required String name,
required List<String> projectUuids,
required DateTime expiresAt,
}) async {
requireAuth();
logger.detail('API Request: POST /orgs/$orgId/api-tokens');

final body = json.encode({
'name': name,
'projectUuids': projectUuids,
'expiresAt': expiresAt.toIso8601String()
});

Check notice on line 236 in packages/globe_cli/lib/src/utils/api.dart

View workflow job for this annotation

GitHub Actions / analyze

Missing a required trailing comma.

See https://dart.dev/lints/require_trailing_commas to learn more about this problem.

// create token
var response = _handleResponse(
await http.post(
_buildUri('/orgs/$orgId/api-tokens'),
headers: headers,
body: body,
),
)! as Map<String, Object?>;
final token = Token.fromJson(response);

// get token value
response = _handleResponse(
await http.get(
_buildUri('/orgs/$orgId/api-tokens/${token.uuid}/generate'),
headers: headers,
),
)! as Map<String, Object?>;
return response['token'].toString();
}
}

class Settings {
Expand Down Expand Up @@ -564,3 +599,41 @@ enum OrganizationType {
}
}
}

class Token {
final String uuid;
final String name;
final String organizationUuid;
final DateTime expiresAt;
final List<String> cliTokenClaimProject;

const Token._({
required this.uuid,
required this.name,
required this.organizationUuid,
required this.expiresAt,
required this.cliTokenClaimProject,
});

factory Token.fromJson(Map<String, dynamic> json) {
return switch (json) {
{
'uuid': final String uuid,
'name': final String name,
'organizationUuid': final String organizationUuid,
'expiresAt': final String expiresAt,
'cliTokenClaimProject': final List<dynamic> cliTokenClaimProject,
} =>
Token._(
uuid: uuid,
name: name,
organizationUuid: organizationUuid,
expiresAt: DateTime.parse(expiresAt),
cliTokenClaimProject: cliTokenClaimProject
.map((e) => (e as Map)['projectUuid'].toString())
.toList(),
),
_ => throw const FormatException('Token'),
};
}
}
44 changes: 44 additions & 0 deletions packages/globe_cli/lib/src/utils/prompts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,47 @@ Future<Project> selectProject(

return projects.firstWhere((p) => p.id == selectedProject);
}

Future<List<Project>> selectProjects(
Organization organization, {
required Logger logger,
required GlobeApi api,
required GlobeScope scope,
}) async {
logger.detail('Fetching organization projects');
final projects = await api.getProjects(org: organization.id);
logger.detail('Found ${projects.length} projects');

if (projects.isEmpty) {
logger.detail(
'No projects found, you need to create a new project first.',
);
logger.err('No projects found.');
exitOverride(1);
}

/// If there's only one, automatically select it.
if (projects.length == 1) {
final project = projects.first;
logger.detail('Automatically selecting ${project.slug}.');
return projects;
}

final projectsMap = projects
.fold<Map<String, Project>>({}, (prev, curr) => prev..[curr.slug] = curr);

/// Ask user to choose zero or more options.
final selections = logger.chooseAny(
'Select projects to associate token with:',
choices: projectsMap.keys.toList(),
);

if (selections.isEmpty) {
logger.detail(
'No projects selected, you need to select atleast one project.',
);
logger.err('No projects selected.');
}

return selections.map((e) => projectsMap[e]!).toList();
}

0 comments on commit fe75025

Please sign in to comment.