diff --git a/lib/config_props.dart b/lib/config_props.dart index b27202d..648e66c 100644 --- a/lib/config_props.dart +++ b/lib/config_props.dart @@ -3,60 +3,51 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:raygun_cli/environment.dart'; -/// Configuration properties for the Raygun CLI -class ConfigProps { - /// Raygun's application ID - final String appId; - - /// Raygun's access token - final String token; - - ConfigProps._({ - required this.appId, - required this.token, +/// A Config property is a value +/// that can be set via argument +/// or environment variable. +/// TODO: #5 add support for config files (.raygun.yaml or similar) +class ConfigProp { + static const appId = ConfigProp( + name: 'app-id', + envKey: Environment.raygunAppIdKey, + ); + + static const token = ConfigProp( + name: 'token', + envKey: Environment.raygunTokenKey, + ); + + static const apiKey = ConfigProp( + name: 'api-key', + envKey: Environment.raygunApiKeyKey, + ); + + /// The name of the property + final String name; + + /// The environment variable key + final String envKey; + + const ConfigProp({ + required this.name, + required this.envKey, }); - /// Load configuration properties from arguments or environment variables - /// and return a new instance of [ConfigProps] or exit with code 2. - factory ConfigProps.load(ArgResults arguments, {bool verbose = false}) { - String? appId; - String? token; - - // Providing app-id and token via argument takes priority - if (arguments.wasParsed('app-id')) { - appId = arguments['app-id']; - } else { - appId = Environment.instance.raygunAppId; - } - - if (appId == null) { - print('Error: Missing "app-id"'); - print( - ' Please provide "app-id" via argument or environment variable "RAYGUN_APP_ID"'); - exit(2); - } - - if (arguments.wasParsed('token')) { - token = arguments['token']; + /// Load the value of the property from arguments or environment variables + String load(ArgResults arguments) { + String? value; + if (arguments.wasParsed(name)) { + value = arguments[name]; } else { - token = Environment.instance.raygunToken; + value = Environment.instance[envKey]; } - - if (token == null) { - print('Error: Missing "token"'); + if (value == null) { + print('Error: Missing "$name"'); print( - ' Please provide "token" via argument or environment variable "RAYGUN_TOKEN"'); + ' Please provide "$name" via argument or environment variable "$envKey"'); exit(2); } - - if (verbose) { - print('App ID: $appId'); - print('Token: $token'); - } - - return ConfigProps._( - appId: appId, - token: token, - ); + return value; } } diff --git a/lib/deployments/deployments.dart b/lib/deployments/deployments.dart index 924727a..946b82a 100644 --- a/lib/deployments/deployments.dart +++ b/lib/deployments/deployments.dart @@ -7,40 +7,29 @@ import '../config_props.dart'; class Deployments { final ArgResults command; final bool verbose; - late final String appId; - late final String token; Deployments({ required this.command, required this.verbose, - required ConfigProps config, - }) { - appId = config.appId; - token = config.token; - } + }); Future notify() async { - if (!command.wasParsed('api-key')) { - print('Error: Missing "--api-key"'); - print(' Please provide "--api-key" via argument'); - exit(2); - } if (!command.wasParsed('version')) { print('Error: Missing "--version"'); print(' Please provide "--version" via argument'); exit(2); } - final apiKey = command.option('api-key') as String; final version = command.option('version') as String; final ownerName = command.option('owner-name'); final emailAddress = command.option('email-address'); final comment = command.option('comment'); final scmIdentifier = command.option('scm-identifier'); final scmType = command.option('scm-type'); + final apiKey = ConfigProp.apiKey.load(command); + final token = ConfigProp.token.load(command); if (verbose) { - print('app-id: $appId'); print('token: $token'); print('api-key: $apiKey'); print('version: $version'); diff --git a/lib/deployments/deployments_command.dart b/lib/deployments/deployments_command.dart index 7bf9948..6956b91 100644 --- a/lib/deployments/deployments_command.dart +++ b/lib/deployments/deployments_command.dart @@ -3,8 +3,6 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:raygun_cli/deployments/deployments.dart'; -import '../config_props.dart'; - const kDeploymentsCommand = 'deployments'; ArgParser buildParserDeployments() { @@ -15,10 +13,6 @@ ArgParser buildParserDeployments() { negatable: false, help: 'Print deployments usage information', ) - ..addOption( - 'app-id', - help: 'Raygun application ID', - ) ..addOption( 'token', help: 'Raygun access token', @@ -70,11 +64,8 @@ void parseDeploymentsCommand(ArgResults command, bool verbose) { exit(0); } - final configProps = ConfigProps.load(command, verbose: verbose); - Deployments( command: command, verbose: verbose, - config: configProps, ).notify(); } diff --git a/lib/environment.dart b/lib/environment.dart index 2fda981..f2c5e7a 100644 --- a/lib/environment.dart +++ b/lib/environment.dart @@ -3,11 +3,13 @@ import 'dart:io'; /// Wraps access to Environment variables /// Allows faking for testing class Environment { - static String raygunAppIdKey = 'RAYGUN_APP_ID'; - static String raygunTokenKey = 'RAYGUN_TOKEN'; + static const String raygunAppIdKey = 'RAYGUN_APP_ID'; + static const String raygunTokenKey = 'RAYGUN_TOKEN'; + static const String raygunApiKeyKey = 'RAYGUN_API_KEY'; final String? raygunAppId; final String? raygunToken; + final String? raygunApiKey; static Environment? _instance; @@ -27,14 +29,30 @@ class Environment { Environment({ required this.raygunAppId, required this.raygunToken, + required this.raygunApiKey, }); + String? operator [](String key) { + switch (key) { + case raygunAppIdKey: + return raygunAppId; + case raygunTokenKey: + return raygunToken; + case raygunApiKeyKey: + return raygunApiKey; + default: + throw ArgumentError('Unknown environment variable: $key'); + } + } + factory Environment._init() { final raygunAppId = Platform.environment[raygunAppIdKey]; final raygunToken = Platform.environment[raygunTokenKey]; + final raygunApiKey = Platform.environment[raygunApiKeyKey]; return Environment( raygunAppId: raygunAppId, raygunToken: raygunToken, + raygunApiKey: raygunApiKey, ); } } diff --git a/lib/sourcemap/flutter/sourcemap_flutter.dart b/lib/sourcemap/flutter/sourcemap_flutter.dart index 18b5395..d61a53b 100644 --- a/lib/sourcemap/flutter/sourcemap_flutter.dart +++ b/lib/sourcemap/flutter/sourcemap_flutter.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:raygun_cli/config_props.dart'; import 'package:raygun_cli/sourcemap/sourcemap_api.dart'; import 'package:raygun_cli/sourcemap/sourcemap_base.dart'; @@ -7,7 +8,6 @@ class SourcemapFlutter extends SourcemapBase { SourcemapFlutter({ required super.command, required super.verbose, - required super.config, }); @override @@ -19,6 +19,8 @@ class SourcemapFlutter extends SourcemapBase { final uri = command.option('uri') ?? '${command.option('base-uri')}main.dart.js'; final path = command.option('input-map') ?? 'build/web/main.dart.js.map'; + final appId = ConfigProp.appId.load(command); + final token = ConfigProp.token.load(command); if (verbose) { print('app-id: $appId'); print('token: $token'); diff --git a/lib/sourcemap/node/sourcemap_node.dart b/lib/sourcemap/node/sourcemap_node.dart index 6c67478..5ebb10a 100644 --- a/lib/sourcemap/node/sourcemap_node.dart +++ b/lib/sourcemap/node/sourcemap_node.dart @@ -6,7 +6,6 @@ class SourcemapNode extends SourcemapBase { SourcemapNode({ required super.command, required super.verbose, - required super.config, }); @override diff --git a/lib/sourcemap/sourcemap_base.dart b/lib/sourcemap/sourcemap_base.dart index ab1c2fa..9283656 100644 --- a/lib/sourcemap/sourcemap_base.dart +++ b/lib/sourcemap/sourcemap_base.dart @@ -1,21 +1,13 @@ import 'package:args/args.dart'; -import '../config_props.dart'; - abstract class SourcemapBase { SourcemapBase({ required this.command, required this.verbose, - required ConfigProps config, - }) { - appId = config.appId; - token = config.token; - } + }); final ArgResults command; final bool verbose; - late final String appId; - late final String token; Future upload(); } diff --git a/lib/sourcemap/sourcemap_command.dart b/lib/sourcemap/sourcemap_command.dart index f1cc9ff..0cf3907 100644 --- a/lib/sourcemap/sourcemap_command.dart +++ b/lib/sourcemap/sourcemap_command.dart @@ -5,8 +5,6 @@ import 'package:raygun_cli/sourcemap/flutter/sourcemap_flutter.dart'; import 'package:raygun_cli/sourcemap/node/sourcemap_node.dart'; import 'package:raygun_cli/sourcemap/sourcemap_single_file.dart'; -import '../config_props.dart'; - const kSourcemapCommand = 'sourcemap'; ArgParser buildParserSourcemap() { @@ -56,26 +54,22 @@ void parseSourcemapCommand(ArgResults command, bool verbose) { print(buildParserSourcemap().usage); exit(0); } - final configProps = ConfigProps.load(command, verbose: verbose); switch (command.option('platform')) { case null: SourcemapSingleFile( command: command, verbose: verbose, - config: configProps, ).upload(); case 'flutter': SourcemapFlutter( command: command, verbose: verbose, - config: configProps, ).upload(); case 'node': SourcemapNode( command: command, verbose: verbose, - config: configProps, ).upload(); default: print('Unsupported platform'); diff --git a/lib/sourcemap/sourcemap_single_file.dart b/lib/sourcemap/sourcemap_single_file.dart index 9a65f6f..33b6852 100644 --- a/lib/sourcemap/sourcemap_single_file.dart +++ b/lib/sourcemap/sourcemap_single_file.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:raygun_cli/config_props.dart'; import 'package:raygun_cli/sourcemap/sourcemap_api.dart'; import 'package:raygun_cli/sourcemap/sourcemap_base.dart'; @@ -7,7 +8,6 @@ class SourcemapSingleFile extends SourcemapBase { SourcemapSingleFile({ required super.command, required super.verbose, - required super.config, }); @override @@ -24,6 +24,9 @@ class SourcemapSingleFile extends SourcemapBase { } final path = command.option('input-map')!; + final appId = ConfigProp.appId.load(command); + final token = ConfigProp.token.load(command); + if (verbose) { print('app-id: $appId'); print('token: $token'); diff --git a/lib/symbols/flutter_symbols.dart b/lib/symbols/flutter_symbols.dart index 26bc160..a4700c3 100644 --- a/lib/symbols/flutter_symbols.dart +++ b/lib/symbols/flutter_symbols.dart @@ -13,11 +13,10 @@ void parseSymbolsCommand(ArgResults command, bool verbose) { print(buildParserSymbols().usage); exit(0); } - final configProps = ConfigProps.load(command, verbose: verbose); _run( command: command, - appId: configProps.appId, - token: configProps.token, + appId: ConfigProp.appId.load(command), + token: ConfigProp.token.load(command), ).then((result) { if (result) { exit(0); diff --git a/test/config_props_test.dart b/test/config_props_test.dart index 51b8af7..7762e5a 100644 --- a/test/config_props_test.dart +++ b/test/config_props_test.dart @@ -12,9 +12,10 @@ void main() { ..addOption('token'); final results = parser.parse(['--app-id=app-id-parsed', '--token=token-parsed']); - final props = ConfigProps.load(results); - expect(props.appId, 'app-id-parsed'); - expect(props.token, 'token-parsed'); + final token = ConfigProp.token.load(results); + final appId = ConfigProp.appId.load(results); + expect(appId, 'app-id-parsed'); + expect(token, 'token-parsed'); }); test('should parse from env vars', () { @@ -23,6 +24,7 @@ void main() { Environment( raygunAppId: 'app-id-env', raygunToken: 'token-env', + raygunApiKey: 'api-key-env', ), ); @@ -30,15 +32,19 @@ void main() { ArgParser parser = ArgParser() ..addFlag('verbose') ..addOption('app-id') + ..addOption('api-key') ..addOption('token'); // parse nothing final results = parser.parse([]); // load from env vars - final props = ConfigProps.load(results); - expect(props.appId, 'app-id-env'); - expect(props.token, 'token-env'); + final appId = ConfigProp.appId.load(results); + final token = ConfigProp.token.load(results); + final apiKey = ConfigProp.apiKey.load(results); + expect(appId, 'app-id-env'); + expect(token, 'token-env'); + expect(apiKey, 'api-key-env'); }); test('should parse with priority', () { @@ -47,6 +53,7 @@ void main() { Environment( raygunAppId: 'app-id-env', raygunToken: 'token-env', + raygunApiKey: 'api-key-env', ), ); @@ -54,16 +61,23 @@ void main() { ArgParser parser = ArgParser() ..addFlag('verbose') ..addOption('app-id') + ..addOption('api-key') ..addOption('token'); // parse arguments - final results = - parser.parse(['--app-id=app-id-parsed', '--token=token-parsed']); + final results = parser.parse([ + '--app-id=app-id-parsed', + '--token=token-parsed', + '--api-key=api-key-parsed', + ]); // load from parsed even if env vars are set - final props = ConfigProps.load(results); - expect(props.appId, 'app-id-parsed'); - expect(props.token, 'token-parsed'); + final appId = ConfigProp.appId.load(results); + final token = ConfigProp.token.load(results); + final apiKey = ConfigProp.apiKey.load(results); + expect(appId, 'app-id-parsed'); + expect(token, 'token-parsed'); + expect(apiKey, 'api-key-parsed'); }); test('should parse from both', () { @@ -73,6 +87,7 @@ void main() { Environment( raygunAppId: 'app-id-env', raygunToken: null, + raygunApiKey: null, ), ); @@ -86,9 +101,10 @@ void main() { final results = parser.parse(['--token=token-parsed']); // app-id from env, token from argument - final props = ConfigProps.load(results); - expect(props.appId, 'app-id-env'); - expect(props.token, 'token-parsed'); + final appId = ConfigProp.appId.load(results); + final token = ConfigProp.token.load(results); + expect(appId, 'app-id-env'); + expect(token, 'token-parsed'); }); }); }