Skip to content

Commit

Permalink
feat(aft): Generate AWS config (#3424)
Browse files Browse the repository at this point in the history
* feat(aft): Generate AWS config

Generates the list of AWS services directly via the `aft generate sdk` command. Later, this will be expanded to include AWS regions and other common configuration values.

* chore(aft): Add generated code comment
  • Loading branch information
dnys1 authored Jul 17, 2023
1 parent 42b4e89 commit 50b1dbe
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 5 deletions.
111 changes: 111 additions & 0 deletions packages/aft/lib/src/commands/generate/generate_sdk_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import 'dart:io';
import 'package:aft/aft.dart';
import 'package:aft/src/options/glob_options.dart';
import 'package:async/async.dart';
import 'package:aws_common/aws_common.dart';
import 'package:checked_yaml/checked_yaml.dart';
import 'package:code_builder/code_builder.dart';
import 'package:collection/collection.dart';
import 'package:git/git.dart';
import 'package:path/path.dart' as p;
Expand Down Expand Up @@ -146,6 +148,9 @@ class GenerateSdkCommand extends AmplifyCommand with GlobOptions {

/// Generates the SDK for [package].
Future<void> _generateForPackage(PackageInfo package) async {
if (package.name == 'aws_common') {
return _generateAwsConfig(package);
}
final configFile = File(p.join(package.path, 'sdk.yaml'));
if (!await configFile.exists()) {
return;
Expand Down Expand Up @@ -275,6 +280,112 @@ class GenerateSdkCommand extends AmplifyCommand with GlobOptions {
}
}

/// Generates common AWS configuration files, e.g. `aws_service.dart`.
Future<void> _generateAwsConfig(PackageInfo awsCommon) async {
logger.info('Generating AWS configuration files...');
const classDocs = '''
/// The enumeration of AWS services.
///
/// This enumeration is used to configure the SigV4 signer. To use a service
/// that is not listed here, call [AWSService.new] directly.''';
final builder = LibraryBuilder();
final awsService = ClassBuilder()
..name = 'AWSService'
..docs.add(classDocs)
..constructors.add(
Constructor(
(c) => c
..constant = true
..docs.add(
'/// Creates a new [AWSService] instance which can be passed to a SigV4 signer.',
)
..requiredParameters.addAll([
Parameter(
(p) => p
..toThis = true
..name = 'service',
)
]),
),
)
..fields.addAll([
Field(
(f) => f
..modifier = FieldModifier.final$
..docs.add('/// The SigV4 service name, used in signing.')
..name = 'service'
..type = refer('String'),
)
]);

final modelsDir = await _modelsDirForRef('master');
final models = modelsDir.list().whereType<File>();
final services = <Field>[];
await for (final model in models) {
final astJson = await model.readAsString();
final ast = parseAstJson(astJson);
final serviceShape = ast.shapes.values.whereType<ServiceShape>().single;
final sigV4Trait = serviceShape.getTrait<SigV4Trait>();
final serviceTrait = serviceShape.expectTrait<ServiceTrait>();
final serviceName = serviceTrait.sdkId.camelCase;
final sigV4Service = sigV4Trait?.name;
if (sigV4Service == null) {
logger.warn('Skipping $serviceName (no SigV4 service name)');
continue;
}
final title = serviceShape.getTrait<TitleTrait>()?.value;
logger.debug('Found AWS service "$serviceName"');
services.add(
Field(
(f) => f
..static = true
..modifier = FieldModifier.constant
..docs.addAll([if (title != null) '/// $title'])
..name = serviceName
..assignment = refer('AWSService').constInstance([
literalString(sigV4Service),
]).code,
),
);
}

awsService.fields.addAll(
services..sort((a, b) => a.name.compareTo(b.name)),
);
builder.body.add(awsService.build());

final library = builder.build();
final emitter = DartEmitter(
allocator: Allocator(),
orderDirectives: true,
useNullSafetySyntax: true,
);
final code = '''
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Generated with `aft generate sdk`. Do not modify by hand.
${library.accept(emitter)}
''';

final output = File(
p.join(
awsCommon.path,
'lib',
'src',
'config',
'aws_service.dart',
),
);
await output.writeAsString(code);
await Process.run(
'dart',
['format', output.path],
workingDirectory: awsCommon.path,
);
}

@override
Future<void> run() async {
await super.run();
Expand Down
1 change: 1 addition & 0 deletions packages/aft/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencies:
built_value: ">=8.6.0 <8.7.0"
checked_yaml: ^2.0.0
cli_util: ^0.3.5
code_builder: 4.5.0
collection: ^1.16.0
file: ^7.0.0
git: ^2.0.0
Expand Down
16 changes: 11 additions & 5 deletions packages/aws_common/lib/src/config/aws_service.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

/// {@template aws_common.aws_service}
/// Enumeration of AWS services.
/// {@endtemplate}
final class AWSService {
/// {@macro aws_common.aws_service}
// Generated with `aft generate sdk`. Do not modify by hand.

/// The enumeration of AWS services.
///
/// This enumeration is used to configure the SigV4 signer. To use a service
/// that is not listed here, call [AWSService.new] directly.
class AWSService {
/// Creates a new [AWSService] instance which can be passed to a SigV4 signer.
const AWSService(this.service);

/// The SigV4 service name, used in signing.
Expand Down Expand Up @@ -53,6 +56,9 @@ final class AWSService {
/// AWS AppConfig Data
static const appConfigData = AWSService('appconfig');

/// AppFabric
static const appFabric = AWSService('appfabric');

/// Amazon AppIntegrations Service
static const appIntegrations = AWSService('app-integrations');

Expand Down

0 comments on commit 50b1dbe

Please sign in to comment.