From 574a9cea753e9b9be7ae6f0cf4158e03a505792d Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Thu, 25 Feb 2021 23:26:12 -0800 Subject: [PATCH 1/5] inital nullsafety changes --- .travis.yml | 2 +- lib/src/configuration.dart | 18 +- lib/src/default_configurations.dart | 34 +- lib/src/mirror_property.dart | 39 +- lib/src/runtime.dart | 9 +- pubspec.yaml | 9 +- test/config_test.dart | 488 +++++++++++++++++--------- test/nested_test.dart | 14 +- test/no_default_constructor_test.dart | 8 +- tool/runtime_test_runner.dart | 18 +- 10 files changed, 392 insertions(+), 247 deletions(-) diff --git a/.travis.yml b/.travis.yml index 91c2168..419ddc4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: dart dart: - - stable + - beta sudo: required env: diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index 8b0eb08..609ff34 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -18,7 +18,7 @@ abstract class Configuration { /// [contents] must be YAML. Configuration.fromString(String contents) { - final yamlMap = loadYaml(contents) as Map; + final yamlMap = loadYaml(contents); final map = yamlMap.map((k, v) => MapEntry(k.toString(), v)); decode(map); @@ -41,7 +41,7 @@ abstract class Configuration { this, "input is not an object (is a '${value.runtimeType}')"); } - _runtime.decode(this, value as Map); + _runtime.decode(this, value); validate(); } @@ -79,7 +79,7 @@ abstract class ConfigurationRuntime { return decode(); } on ConfigurationException catch (e) { throw ConfigurationException(configuration, e.message, - keyPath: [name]..addAll(e.keyPath)); + keyPath: [name]..addAll(e.keyPath)); } on IntermediateException catch (e) { final underlying = e.underlying; if (underlying is ConfigurationException) { @@ -89,17 +89,17 @@ abstract class ConfigurationRuntime { underlying.keyPath ].expand((i) => i).toList(); throw ConfigurationException(configuration, underlying.message, - keyPath: keyPaths); - } else if (underlying is CastError) { + keyPath: keyPaths); + } else if (underlying is TypeError) { throw ConfigurationException(configuration, "input is wrong type", - keyPath: [name]..addAll(e.keyPath)); + keyPath: [name]..addAll(e.keyPath)); } throw ConfigurationException(configuration, underlying.toString(), - keyPath: [name]..addAll(e.keyPath)); + keyPath: [name]..addAll(e.keyPath)); } catch (e) { throw ConfigurationException(configuration, e.toString(), - keyPath: [name]); + keyPath: [name]); } } } @@ -153,7 +153,7 @@ class ConfigurationException { @override String toString() { - if (keyPath?.isEmpty ?? true) { + if (keyPath.isEmpty) { return "Failed to read '${configuration.runtimeType}'\n\t-> $message"; } diff --git a/lib/src/default_configurations.dart b/lib/src/default_configurations.dart index 6d22872..c4ea6d2 100644 --- a/lib/src/default_configurations.dart +++ b/lib/src/default_configurations.dart @@ -24,29 +24,29 @@ class DatabaseConfiguration extends Configuration { /// The host of the database to connect to. /// /// This property is required. - String host; + String? host; /// The port of the database to connect to. /// /// This property is required. - int port; + int? port; /// The name of the database to connect to. /// /// This property is required. - String databaseName; + String? databaseName; /// A username for authenticating to the database. /// /// This property is optional. @optionalConfiguration - String username; + String? username; /// A password for authenticating to the database. /// /// This property is optional. @optionalConfiguration - String password; + String? password; /// A flag to represent permanence. /// @@ -54,7 +54,7 @@ class DatabaseConfiguration extends Configuration { /// dropping it after the tests are complete. /// This property is optional. @optionalConfiguration - bool isTemporary; + bool? isTemporary; @override void decode(dynamic value) { @@ -68,26 +68,24 @@ class DatabaseConfiguration extends Configuration { "'${value.runtimeType}' is not assignable; must be a object or string"); } - var uri = Uri.parse(value as String); + var uri = Uri.parse(value); host = uri.host; port = uri.port; if (uri.pathSegments.length == 1) { databaseName = uri.pathSegments.first; } - if (uri.userInfo == null || uri.userInfo == '') { + if (uri.userInfo == '') { validate(); return; } var authority = uri.userInfo.split(":"); - if (authority != null) { - if (authority.isNotEmpty) { - username = Uri.decodeComponent(authority.first); - } - if (authority.length > 1) { - password = Uri.decodeComponent(authority.last); - } + if (authority.isNotEmpty) { + username = Uri.decodeComponent(authority.first); + } + if (authority.length > 1) { + password = Uri.decodeComponent(authority.last); } validate(); @@ -108,17 +106,17 @@ class APIConfiguration extends Configuration { /// /// This property is required. /// Example: https://external.api.com:80/resources - String baseURL; + String? baseURL; /// The client ID. /// /// This property is optional. @optionalConfiguration - String clientID; + String? clientID; /// The client secret. /// /// This property is optional. @optionalConfiguration - String clientSecret; + String? clientSecret; } diff --git a/lib/src/mirror_property.dart b/lib/src/mirror_property.dart index 88ddd3a..c9b9ee8 100644 --- a/lib/src/mirror_property.dart +++ b/lib/src/mirror_property.dart @@ -9,14 +9,14 @@ class MirrorTypeCodec { final klass = type as ClassMirror; final classHasDefaultConstructor = klass.declarations.values.any((dm) { return dm is MethodMirror && - dm.isConstructor && - dm.constructorName == const Symbol('') && - dm.parameters.every((p) => p.isOptional == true); + dm.isConstructor && + dm.constructorName == const Symbol('') && + dm.parameters.every((p) => p.isOptional == true); }); if (!classHasDefaultConstructor) { throw StateError( - "Failed to compile '${type.reflectedType}'\n\t-> 'Configuration' subclasses MUST declare an unnammed constructor (i.e. '${type.reflectedType}();') if they are nested."); + "Failed to compile '${type.reflectedType}'\n\t-> 'Configuration' subclasses MUST declare an unnammed constructor (i.e. '${type.reflectedType}();') if they are nested."); } } } @@ -57,7 +57,7 @@ class MirrorTypeCodec { Configuration _decodeConfig(dynamic object) { final item = (type as ClassMirror) - .newInstance(const Symbol(""), []).reflectee as Configuration; + .newInstance(const Symbol(""), []).reflectee as Configuration; item.decode(object); @@ -65,7 +65,8 @@ class MirrorTypeCodec { } List _decodeList(List value) { - final out = (type as ClassMirror).newInstance(const Symbol(''), []).reflectee as List; + final out = (type as ClassMirror) + .newInstance(const Symbol(''), []).reflectee as List; final innerDecoder = MirrorTypeCodec(type.typeArguments.first); for (var i = 0; i < value.length; i++) { try { @@ -83,7 +84,7 @@ class MirrorTypeCodec { Map _decodeMap(Map value) { final map = (type as ClassMirror) - .newInstance(const Symbol(""), []).reflectee as Map; + .newInstance(const Symbol(""), []).reflectee as Map; final innerDecoder = MirrorTypeCodec(type.typeArguments.last); value.forEach((key, val) { @@ -173,7 +174,6 @@ return map; } String get _decodeConfigSource { - return """ final item = ${expectedType}(); @@ -181,7 +181,6 @@ return map; return item; """; - } String get _decodeIntSource { @@ -206,7 +205,8 @@ return map; } class MirrorConfigurationProperty { - MirrorConfigurationProperty(this.property) : codec = MirrorTypeCodec(property.type); + MirrorConfigurationProperty(this.property) + : codec = MirrorTypeCodec(property.type); final VariableMirror property; final MirrorTypeCodec codec; @@ -217,15 +217,16 @@ class MirrorConfigurationProperty { String get source => codec.source; static bool _isVariableRequired(VariableMirror m) { - final attribute = m.metadata - .firstWhere( - (im) => - im.type.isSubtypeOf(reflectType(ConfigurationItemAttribute)), - orElse: () => null) - ?.reflectee as ConfigurationItemAttribute; - - return attribute == null || - attribute.type == ConfigurationItemAttributeType.required; + try { + final attribute = m.metadata + .firstWhere((im) => + im.type.isSubtypeOf(reflectType(ConfigurationItemAttribute))) + .reflectee as ConfigurationItemAttribute; + + return attribute.type == ConfigurationItemAttributeType.required; + } catch (_) { + return true; + } } dynamic decode(dynamic input) { diff --git a/lib/src/runtime.dart b/lib/src/runtime.dart index b807097..3eefcaf 100644 --- a/lib/src/runtime.dart +++ b/lib/src/runtime.dart @@ -13,7 +13,7 @@ class ConfigurationRuntimeImpl extends ConfigurationRuntime final ClassMirror type; - Map properties; + late Map properties; @override void decode(Configuration configuration, Map input) { @@ -48,7 +48,8 @@ class ConfigurationRuntimeImpl extends ConfigurationRuntime buf.writeln("final valuesCopy = Map.from(input);"); properties.forEach((k, v) { buf.writeln("{"); - buf.writeln("final v = Configuration.getEnvironmentOrValue(valuesCopy.remove('$k'));"); + buf.writeln( + "final v = Configuration.getEnvironmentOrValue(valuesCopy.remove('$k'));"); buf.writeln("if (v != null) {"); buf.writeln( " final decodedValue = tryDecode(configuration, '$k', () { ${v.source} });"); @@ -94,7 +95,7 @@ class ConfigurationRuntimeImpl extends ConfigurationRuntime declarations.addAll(ptr.declarations.values .whereType() .where((vm) => !vm.isStatic && !vm.isPrivate)); - ptr = ptr.superclass; + ptr = ptr.superclass!; } final m = {}; @@ -128,7 +129,7 @@ class ConfigurationRuntimeImpl extends ConfigurationRuntime @override String compile(BuildContext ctx) { final directives = ctx.getImportDirectives( - uri: type.originalDeclaration.location.sourceUri, + uri: type.originalDeclaration.location!.sourceUri, alsoImportOriginalFile: true) ..add("import 'package:safe_config/src/intermediate_exception.dart';"); diff --git a/pubspec.yaml b/pubspec.yaml index 22145bb..b96313a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,11 +6,12 @@ homepage: http://stablekernel.com documentation: environment: - sdk: ">=2.0.0 <3.0.0" + sdk: ">=2.12.0-0 <3.0.0" dependencies: - yaml: '>=2.1.8 <3.0.0' - runtime: ^1.0.0-5 + yaml: ^3.1.0 + runtime: + git: https://github.com/j4qfrost/dart-runtime.git dev_dependencies: - test: ^1.3.0 \ No newline at end of file + test: ^1.16.4 \ No newline at end of file diff --git a/test/config_test.dart b/test/config_test.dart index e7a0f78..8f72267 100644 --- a/test/config_test.dart +++ b/test/config_test.dart @@ -17,11 +17,11 @@ void main() { var t = TopLevelConfiguration.fromString(yamlString); expect(t.port, 80); expect(t.name, "foobar"); - expect(t.database.host, "stablekernel.com"); - expect(t.database.username, "bob"); - expect(t.database.password, "fred"); - expect(t.database.databaseName, "dbname"); - expect(t.database.port, 5000); + expect(t.database!.host, "stablekernel.com"); + expect(t.database!.username, "bob"); + expect(t.database!.password, "fred"); + expect(t.database!.databaseName, "dbname"); + expect(t.database!.port, 5000); var asMap = { "port": 80, @@ -37,11 +37,11 @@ void main() { t = TopLevelConfiguration.fromMap(asMap); expect(t.port, 80); expect(t.name, "foobar"); - expect(t.database.host, "stablekernel.com"); - expect(t.database.username, "bob"); - expect(t.database.password, "fred"); - expect(t.database.databaseName, "dbname"); - expect(t.database.port, 5000); + expect(t.database!.host, "stablekernel.com"); + expect(t.database!.username, "bob"); + expect(t.database!.password, "fred"); + expect(t.database!.databaseName, "dbname"); + expect(t.database!.port, 5000); }); test("Configuration subclasses success case", () { @@ -58,12 +58,12 @@ void main() { var t = ConfigurationSubclass.fromString(yamlString); expect(t.port, 80); expect(t.extraValue, 2); - expect(t.database.host, "stablekernel.com"); - expect(t.database.username, "bob"); - expect(t.database.password, "fred"); - expect(t.database.databaseName, "dbname"); - expect(t.database.port, 5000); - expect(t.database.extraDatabaseValue, 3); + expect(t.database!.host, "stablekernel.com"); + expect(t.database!.username, "bob"); + expect(t.database!.password, "fred"); + expect(t.database!.databaseName, "dbname"); + expect(t.database!.port, 5000); + expect(t.database!.extraDatabaseValue, 3); var asMap = { "port": 80, @@ -80,12 +80,12 @@ void main() { t = ConfigurationSubclass.fromMap(asMap); expect(t.port, 80); expect(t.extraValue, 2); - expect(t.database.host, "stablekernel.com"); - expect(t.database.username, "bob"); - expect(t.database.password, "fred"); - expect(t.database.databaseName, "dbname"); - expect(t.database.port, 5000); - expect(t.database.extraDatabaseValue, 3); + expect(t.database!.host, "stablekernel.com"); + expect(t.database!.username, "bob"); + expect(t.database!.password, "fred"); + expect(t.database!.databaseName, "dbname"); + expect(t.database!.port, 5000); + expect(t.database!.extraDatabaseValue, 3); }); test("Extra property", () { @@ -103,7 +103,13 @@ void main() { var _ = TopLevelConfiguration.fromString(yamlString); fail('unreachable'); } on ConfigurationException catch (e) { - expect(e.toString(), allOf([contains("TopLevelConfiguration"), contains("unexpected keys found"), contains("'extraKey'")])); + expect( + e.toString(), + allOf([ + contains("TopLevelConfiguration"), + contains("unexpected keys found"), + contains("'extraKey'") + ])); } try { @@ -123,7 +129,13 @@ void main() { fail('unreachable'); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("TopLevelConfiguration"), contains("unexpected keys found"), contains("'extraKey'")])); + expect( + e.toString(), + allOf([ + contains("TopLevelConfiguration"), + contains("unexpected keys found"), + contains("'extraKey'") + ])); } }); @@ -141,7 +153,13 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing required"), contains("TopLevelConfiguration"), contains("'port'")])); + expect( + e.toString(), + allOf([ + contains("missing required"), + contains("TopLevelConfiguration"), + contains("'port'") + ])); } try { @@ -159,7 +177,13 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing required"), contains("TopLevelConfiguration"), contains("'port'")])); + expect( + e.toString(), + allOf([ + contains("missing required"), + contains("TopLevelConfiguration"), + contains("'port'") + ])); } }); @@ -171,7 +195,13 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing required"), contains("TopLevelConfiguration"), contains("'database'")])); + expect( + e.toString(), + allOf([ + contains("missing required"), + contains("TopLevelConfiguration"), + contains("'database'") + ])); } try { @@ -180,7 +210,13 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing required"), contains("TopLevelConfiguration"), contains("'database'")])); + expect( + e.toString(), + allOf([ + contains("missing required"), + contains("TopLevelConfiguration"), + contains("'database'") + ])); } }); @@ -226,7 +262,13 @@ void main() { fail("unreachable"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing required"), contains("ConfigurationSubclass"), contains("'port'")])); + expect( + e.toString(), + allOf([ + contains("missing required"), + contains("ConfigurationSubclass"), + contains("'port'") + ])); } try { @@ -246,7 +288,13 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing required"), contains("ConfigurationSubclass"), contains("'port'")])); + expect( + e.toString(), + allOf([ + contains("missing required"), + contains("ConfigurationSubclass"), + contains("'port'") + ])); } }); @@ -266,7 +314,13 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing required"), contains("ConfigurationSubclass"), contains("'extraValue'")])); + expect( + e.toString(), + allOf([ + contains("missing required"), + contains("ConfigurationSubclass"), + contains("'extraValue'") + ])); } try { @@ -286,7 +340,13 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing required"), contains("ConfigurationSubclass"), contains("'extraValue'")])); + expect( + e.toString(), + allOf([ + contains("missing required"), + contains("ConfigurationSubclass"), + contains("'extraValue'") + ])); } }); @@ -306,7 +366,13 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing required"), contains("ConfigurationSubclass"), contains("'port'")])); + expect( + e.toString(), + allOf([ + contains("missing required"), + contains("ConfigurationSubclass"), + contains("'port'") + ])); } try { @@ -326,7 +392,13 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing required"), contains("ConfigurationSubclass"), contains("'port'")])); + expect( + e.toString(), + allOf([ + contains("missing required"), + contains("ConfigurationSubclass"), + contains("'port'") + ])); } }); @@ -346,7 +418,13 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing required"), contains("ConfigurationSubclass"), contains("'extraDatabaseValue'")])); + expect( + e.toString(), + allOf([ + contains("missing required"), + contains("ConfigurationSubclass"), + contains("'extraDatabaseValue'") + ])); } try { @@ -366,7 +444,13 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing required"), contains("ConfigurationSubclass"), contains("'extraDatabaseValue'")])); + expect( + e.toString(), + allOf([ + contains("missing required"), + contains("ConfigurationSubclass"), + contains("'extraDatabaseValue'") + ])); } }); @@ -385,7 +469,12 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("ConfigurationSubclassWithValidation"), contains("not a host.com")])); + expect( + e.toString(), + allOf([ + contains("ConfigurationSubclassWithValidation"), + contains("not a host.com") + ])); } try { @@ -404,7 +493,12 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("ConfigurationSubclassWithValidation"), contains("not a host.com")])); + expect( + e.toString(), + allOf([ + contains("ConfigurationSubclassWithValidation"), + contains("not a host.com") + ])); } }); @@ -420,11 +514,11 @@ void main() { var t = TopLevelConfiguration.fromString(yamlString); expect(t.port, 80); expect(t.name, isNull); - expect(t.database.host, "stablekernel.com"); - expect(t.database.username, "bob"); - expect(t.database.password, "fred"); - expect(t.database.databaseName, "dbname"); - expect(t.database.port, 5000); + expect(t.database!.host, "stablekernel.com"); + expect(t.database!.username, "bob"); + expect(t.database!.password, "fred"); + expect(t.database!.databaseName, "dbname"); + expect(t.database!.port, 5000); var asMap = { "port": 80, @@ -439,11 +533,11 @@ void main() { t = TopLevelConfiguration.fromMap(asMap); expect(t.port, 80); expect(t.name, isNull); - expect(t.database.host, "stablekernel.com"); - expect(t.database.username, "bob"); - expect(t.database.password, "fred"); - expect(t.database.databaseName, "dbname"); - expect(t.database.port, 5000); + expect(t.database!.host, "stablekernel.com"); + expect(t.database!.username, "bob"); + expect(t.database!.password, "fred"); + expect(t.database!.databaseName, "dbname"); + expect(t.database!.port, 5000); }); test("Nested optional can be missing", () { @@ -458,25 +552,30 @@ void main() { var t = TopLevelConfiguration.fromString(yamlString); expect(t.port, 80); expect(t.name, "foobar"); - expect(t.database.host, "stablekernel.com"); - expect(t.database.username, isNull); - expect(t.database.password, "fred"); - expect(t.database.databaseName, "dbname"); - expect(t.database.port, 5000); + expect(t.database!.host, "stablekernel.com"); + expect(t.database!.username, isNull); + expect(t.database!.password, "fred"); + expect(t.database!.databaseName, "dbname"); + expect(t.database!.port, 5000); var asMap = { "port": 80, "name": "foobar", - "database": {"host": "stablekernel.com", "password": "fred", "databaseName": "dbname", "port": 5000} + "database": { + "host": "stablekernel.com", + "password": "fred", + "databaseName": "dbname", + "port": 5000 + } }; t = TopLevelConfiguration.fromMap(asMap); expect(t.port, 80); expect(t.name, "foobar"); - expect(t.database.host, "stablekernel.com"); - expect(t.database.username, isNull); - expect(t.database.password, "fred"); - expect(t.database.databaseName, "dbname"); - expect(t.database.port, 5000); + expect(t.database!.host, "stablekernel.com"); + expect(t.database!.username, isNull); + expect(t.database!.password, "fred"); + expect(t.database!.databaseName, "dbname"); + expect(t.database!.port, 5000); }); test("Nested required cannot be missing", () { @@ -492,20 +591,36 @@ void main() { fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing"), contains("TopLevelConfiguration"), contains("'databaseName'")])); + expect( + e.toString(), + allOf([ + contains("missing"), + contains("TopLevelConfiguration"), + contains("'databaseName'") + ])); } try { var asMap = { "port": 80, "name": "foobar", - "database": {"host": "stablekernel.com", "password": "fred", "port": 5000} + "database": { + "host": "stablekernel.com", + "password": "fred", + "port": 5000 + } }; var _ = TopLevelConfiguration.fromMap(asMap); fail("Should not succeed"); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing"), contains("TopLevelConfiguration"), contains("'databaseName'")])); + expect( + e.toString(), + allOf([ + contains("missing"), + contains("TopLevelConfiguration"), + contains("'databaseName'") + ])); } }); @@ -537,24 +652,24 @@ void main() { var special = SpecialInfo.fromString(yamlString); expect(special.strings, ["abcd", "efgh"]); - expect(special.databaseRecords.first.host, "stablekernel.com"); - expect(special.databaseRecords.first.databaseName, "db1"); - expect(special.databaseRecords.first.port, 1000); - - expect(special.databaseRecords.last.username, "bob"); - expect(special.databaseRecords.last.databaseName, "db2"); - expect(special.databaseRecords.last.port, 2000); - expect(special.databaseRecords.last.host, "stablekernel.com"); - - expect(special.integers["first"], 1); - expect(special.integers["second"], 2); - expect(special.databaseMap["db1"].databaseName, "db1"); - expect(special.databaseMap["db1"].host, "stablekernel.com"); - expect(special.databaseMap["db1"].port, 1000); - expect(special.databaseMap["db2"].username, "bob"); - expect(special.databaseMap["db2"].databaseName, "db2"); - expect(special.databaseMap["db2"].port, 2000); - expect(special.databaseMap["db2"].host, "stablekernel.com"); + expect(special.databaseRecords!.first.host, "stablekernel.com"); + expect(special.databaseRecords!.first.databaseName, "db1"); + expect(special.databaseRecords!.first.port, 1000); + + expect(special.databaseRecords!.last.username, "bob"); + expect(special.databaseRecords!.last.databaseName, "db2"); + expect(special.databaseRecords!.last.port, 2000); + expect(special.databaseRecords!.last.host, "stablekernel.com"); + + expect(special.integers!["first"], 1); + expect(special.integers!["second"], 2); + expect(special.databaseMap!["db1"]!.databaseName, "db1"); + expect(special.databaseMap!["db1"]!.host, "stablekernel.com"); + expect(special.databaseMap!["db1"]!.port, 1000); + expect(special.databaseMap!["db2"]!.username, "bob"); + expect(special.databaseMap!["db2"]!.databaseName, "db2"); + expect(special.databaseMap!["db2"]!.port, 2000); + expect(special.databaseMap!["db2"]!.host, "stablekernel.com"); }); test("From file works the same", () { @@ -573,11 +688,11 @@ void main() { var t = TopLevelConfiguration.fromFile(File("tmp.yaml")); expect(t.port, 80); expect(t.name, "foobar"); - expect(t.database.host, "stablekernel.com"); - expect(t.database.username, "bob"); - expect(t.database.password, "fred"); - expect(t.database.databaseName, "dbname"); - expect(t.database.port, 5000); + expect(t.database!.host, "stablekernel.com"); + expect(t.database!.username, "bob"); + expect(t.database!.password, "fred"); + expect(t.database!.databaseName, "dbname"); + expect(t.database!.port, 5000); file.deleteSync(); }); @@ -597,9 +712,9 @@ void main() { config = OptionalEmbeddedContainer.fromString(yamlString); expect(config.port, 80); - expect(config.database.host, "here"); - expect(config.database.port, 90); - expect(config.database.databaseName, "db"); + expect(config.database!.host, "here"); + expect(config.database!.port, 90); + expect(config.database!.databaseName, "db"); }); test("Optional nested ConfigurationItem obeys required items", () { @@ -623,34 +738,38 @@ void main() { var values = OptionalEmbeddedContainer.fromString(yamlString); expect(values.port, 80); - expect(values.database.username, "dart"); - expect(values.database.password, "pw"); - expect(values.database.port, 5432); - expect(values.database.databaseName, "dbname"); + expect(values.database!.username, "dart"); + expect(values.database!.password, "pw"); + expect(values.database!.port, 5432); + expect(values.database!.databaseName, "dbname"); }); - test("Database configuration as a string can contain an URL-encoded authority", () { + test( + "Database configuration as a string can contain an URL-encoded authority", + () { var yamlString = "port: 80\n" "database: \"postgres://dart%40google.com:pass%23word@host:5432/dbname\"\n"; var values = OptionalEmbeddedContainer.fromString(yamlString); - expect(values.database.username, "dart@google.com"); - expect(values.database.password, "pass#word"); + expect(values.database!.username, "dart@google.com"); + expect(values.database!.password, "pass#word"); }); - test("Omitting optional values in a 'decoded' config still returns succees", () { + test("Omitting optional values in a 'decoded' config still returns succees", + () { var yamlString = "port: 80\n" "database: \"postgres://host:5432/dbname\"\n"; var values = OptionalEmbeddedContainer.fromString(yamlString); expect(values.port, 80); - expect(values.database.username, isNull); - expect(values.database.password, isNull); - expect(values.database.port, 5432); - expect(values.database.databaseName, "dbname"); + expect(values.database!.username, isNull); + expect(values.database!.password, isNull); + expect(values.database!.port, 5432); + expect(values.database!.databaseName, "dbname"); }); - test("Not including required values in a 'decoded' config still yields error", () { + test("Not including required values in a 'decoded' config still yields error", + () { var yamlString = "port: 80\n" "database: \"postgres://dart:pw@host:5432\"\n"; @@ -659,17 +778,25 @@ void main() { expect(true, false); } on ConfigurationException catch (e) { print("$e"); - expect(e.toString(), allOf([contains("missing"), contains("OptionalEmbeddedContainer"), contains("'databaseName'")])); + expect( + e.toString(), + allOf([ + contains("missing"), + contains("OptionalEmbeddedContainer"), + contains("'databaseName'") + ])); } }); test("Environment variable escape values read from Environment", () { - print("This test must be run with environment variables of TEST_VALUE=1 and TEST_BOOL=true"); + print( + "This test must be run with environment variables of TEST_VALUE=1 and TEST_BOOL=true"); - var yamlString = "path: \$PATH\noptionalDooDad: \$XYZ123\ntestValue: \$TEST_VALUE\ntestBoolean: \$TEST_BOOL"; + var yamlString = + "path: \$PATH\noptionalDooDad: \$XYZ123\ntestValue: \$TEST_VALUE\ntestBoolean: \$TEST_BOOL"; var values = EnvironmentConfiguration.fromString(yamlString); expect(values.path, Platform.environment["PATH"]); - expect(values.testValue, int.parse(Platform.environment["TEST_VALUE"])); + expect(values.testValue, int.parse(Platform.environment["TEST_VALUE"]!)); expect(values.testBoolean, true); expect(values.optionalDooDad, isNull); }); @@ -703,22 +830,24 @@ void main() { "This test must be run with environment variables of TEST_DB_ENV_VAR=postgres://user:password@host:5432/dbname"); const yamlString = "port: 80\ndatabase: \$TEST_DB_ENV_VAR"; final config = TopLevelConfiguration.fromString(yamlString); - expect(config.database.username, "user"); - expect(config.database.password, "password"); - expect(config.database.host, "host"); - expect(config.database.port, 5432); - expect(config.database.databaseName, "dbname"); + expect(config.database!.username, "user"); + expect(config.database!.password, "password"); + expect(config.database!.host, "host"); + expect(config.database!.port, 5432); + expect(config.database!.databaseName, "dbname"); }); - - test("Assigning value of incorrect type to parsed integer emits error and field name", () { + + test( + "Assigning value of incorrect type to parsed integer emits error and field name", + () { var yamlString = "port: foobar\n" - "name: foobar\n" - "database:\n" - " host: stablekernel.com\n" - " username: bob\n" - " password: fred\n" - " databaseName: dbname\n" - " port: 5000"; + "name: foobar\n" + "database:\n" + " host: stablekernel.com\n" + " username: bob\n" + " password: fred\n" + " databaseName: dbname\n" + " port: 5000"; try { TopLevelConfiguration.fromString(yamlString); @@ -731,16 +860,18 @@ void main() { } }); - test("Assigning value of incorrect type to nested field emits error and field name", () { + test( + "Assigning value of incorrect type to nested field emits error and field name", + () { var yamlString = "port: 1000\n" - "name: foobar\n" - "database:\n" - " host: stablekernel.com\n" - " username:\n" - " - item\n" - " password: password\n" - " databaseName: dbname\n" - " port: 5000"; + "name: foobar\n" + "database:\n" + " host: stablekernel.com\n" + " username:\n" + " - item\n" + " password: password\n" + " databaseName: dbname\n" + " port: 5000"; try { TopLevelConfiguration.fromString(yamlString); @@ -780,94 +911,102 @@ void main() { class TopLevelConfiguration extends Configuration { TopLevelConfiguration(); - TopLevelConfiguration.fromString(String contents) : super.fromString(contents); + TopLevelConfiguration.fromString(String contents) + : super.fromString(contents); TopLevelConfiguration.fromFile(File file) : super.fromFile(file); TopLevelConfiguration.fromMap(Map map) : super.fromMap(map); @requiredConfiguration - int port; + int? port; @optionalConfiguration - String name; + String? name; - DatabaseConfiguration database; + DatabaseConfiguration? database; } class TopLevelConfigurationWithValidation extends Configuration { TopLevelConfigurationWithValidation(); - TopLevelConfigurationWithValidation.fromString(String contents) : super.fromString(contents); + TopLevelConfigurationWithValidation.fromString(String contents) + : super.fromString(contents); - TopLevelConfigurationWithValidation.fromFile(File file) : super.fromFile(file); + TopLevelConfigurationWithValidation.fromFile(File file) + : super.fromFile(file); TopLevelConfigurationWithValidation.fromMap(Map map) : super.fromMap(map); @requiredConfiguration - int port; + int? port; @override void validate() { super.validate(); - if (port < 0 || port > 65535) { + if (port! < 0 || port! > 65535) { throw ConfigurationException(this, "$port", keyPath: ["port"]); } } @optionalConfiguration - String name; + String? name; } class DatabaseConfigurationSubclass extends DatabaseConfiguration { DatabaseConfigurationSubclass(); - int extraDatabaseValue; + int? extraDatabaseValue; } class ConfigurationSuperclass extends Configuration { ConfigurationSuperclass(); - ConfigurationSuperclass.fromString(String contents) : super.fromString(contents); + ConfigurationSuperclass.fromString(String contents) + : super.fromString(contents); ConfigurationSuperclass.fromFile(File file) : super.fromFile(file); ConfigurationSuperclass.fromMap(Map map) : super.fromMap(map); @requiredConfiguration - int port; + int? port; @optionalConfiguration - String name; + String? name; } class ConfigurationSubclass extends ConfigurationSuperclass { ConfigurationSubclass(); - ConfigurationSubclass.fromString(String contents) : super.fromString(contents); + ConfigurationSubclass.fromString(String contents) + : super.fromString(contents); ConfigurationSubclass.fromFile(File file) : super.fromFile(file); ConfigurationSubclass.fromMap(Map map) : super.fromMap(map); - int extraValue; + int? extraValue; - DatabaseConfigurationSubclass database; + DatabaseConfigurationSubclass? database; } class ConfigurationSubclassWithValidation extends ConfigurationSuperclass { ConfigurationSubclassWithValidation(); - ConfigurationSubclassWithValidation.fromString(String contents) : super.fromString(contents); + ConfigurationSubclassWithValidation.fromString(String contents) + : super.fromString(contents); - ConfigurationSubclassWithValidation.fromFile(File file) : super.fromFile(file); + ConfigurationSubclassWithValidation.fromFile(File file) + : super.fromFile(file); ConfigurationSubclassWithValidation.fromMap(Map map) : super.fromMap(map); - DatabaseConfigurationSubclassWithValidation database; + DatabaseConfigurationSubclassWithValidation? database; } -class DatabaseConfigurationSubclassWithValidation extends DatabaseConfiguration { +class DatabaseConfigurationSubclassWithValidation + extends DatabaseConfiguration { DatabaseConfigurationSubclassWithValidation(); @override @@ -875,8 +1014,8 @@ class DatabaseConfigurationSubclassWithValidation extends DatabaseConfiguration super.validate(); RegExp validHost = RegExp( r"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$"); - if (!validHost.hasMatch(host)) { - throw ConfigurationException(this, host, keyPath: ["host"]); + if (!validHost.hasMatch(host!)) { + throw ConfigurationException(this, host!, keyPath: ["host"]); } } } @@ -886,54 +1025,57 @@ class SpecialInfo extends Configuration { SpecialInfo.fromString(String contents) : super.fromString(contents); - List strings; - List databaseRecords; - Map integers; - Map databaseMap; + List? strings; + List? databaseRecords; + Map? integers; + Map? databaseMap; } class OptionalEmbeddedContainer extends Configuration { OptionalEmbeddedContainer(); - OptionalEmbeddedContainer.fromString(String contents) : super.fromString(contents); + OptionalEmbeddedContainer.fromString(String contents) + : super.fromString(contents); - int port; + int? port; @optionalConfiguration - DatabaseConfiguration database; + DatabaseConfiguration? database; } class EnvironmentConfiguration extends Configuration { EnvironmentConfiguration(); - EnvironmentConfiguration.fromString(String contents) : super.fromString(contents); + EnvironmentConfiguration.fromString(String contents) + : super.fromString(contents); - String path; - int testValue; - bool testBoolean; + String? path; + int? testValue; + bool? testBoolean; @optionalConfiguration - String optionalDooDad; + String? optionalDooDad; } class StaticVariableConfiguration extends Configuration { - StaticVariableConfiguration(); - StaticVariableConfiguration.fromString(String contents) : super.fromString(contents); + StaticVariableConfiguration.fromString(String contents) + : super.fromString(contents); - static String staticVariable; + static String? staticVariable; - int value; + int? value; } class PrivateVariableConfiguration extends Configuration { PrivateVariableConfiguration(); - PrivateVariableConfiguration.fromString(String contents) : super.fromString(contents); + PrivateVariableConfiguration.fromString(String contents) + : super.fromString(contents); - String _privateVariable; - int value; + String? _privateVariable; + int? value; } class EnvFail extends Configuration { @@ -941,22 +1083,22 @@ class EnvFail extends Configuration { EnvFail.fromString(String contents) : super.fromString(contents); - String value; + String? value; } class BoolConfig extends Configuration { BoolConfig(); BoolConfig.fromString(String contents) : super.fromString(contents); - bool value; + bool? value; } class DefaultValConfig extends Configuration { DefaultValConfig(); DefaultValConfig.fromString(String contents) : super.fromString(contents); - String required; + String? required; @optionalConfiguration String value = "default"; -} \ No newline at end of file +} diff --git a/test/nested_test.dart b/test/nested_test.dart index 529cbe6..a313e8e 100644 --- a/test/nested_test.dart +++ b/test/nested_test.dart @@ -178,26 +178,26 @@ class Parent extends Configuration { Parent.fromMap(Map m) : super.fromMap(m); - String id; + String? id; @optionalConfiguration - List> listOfListOfParents; + List>? listOfListOfParents; @optionalConfiguration - List peers; + List? peers; @optionalConfiguration - Map namedChildren; + Map? namedChildren; } class Child extends Configuration { - String id; + String? id; @optionalConfiguration - Parent parent; + Parent? parent; @optionalConfiguration - List peers; + List? peers; } String getMessage(Map object) { diff --git a/test/no_default_constructor_test.dart b/test/no_default_constructor_test.dart index e464ab4..964ea5a 100644 --- a/test/no_default_constructor_test.dart +++ b/test/no_default_constructor_test.dart @@ -3,7 +3,9 @@ import 'package:safe_config/safe_config.dart'; import 'package:test/test.dart'; void main() { - test("Nested configuration without unnamed constructor is an error at compile time", () { + test( + "Nested configuration without unnamed constructor is an error at compile time", + () { try { RuntimeContext.current; fail('unreachable'); @@ -14,10 +16,10 @@ void main() { } class ParentConfig extends Configuration { - BadConfig badConfig; + BadConfig? badConfig; } class BadConfig extends Configuration { BadConfig.from(this.id); String id; -} \ No newline at end of file +} diff --git a/tool/runtime_test_runner.dart b/tool/runtime_test_runner.dart index ee65b78..794e6ab 100644 --- a/tool/runtime_test_runner.dart +++ b/tool/runtime_test_runner.dart @@ -33,15 +33,15 @@ Future main(List args) async { print("${makePrompt()} Running tests derived from ${f.path}..."); print("${makePrompt()} Running tests derived from ${f.path}..."); final result = await Process.start("dart", ["test/main_test.dart"], - workingDirectory: - ctx.buildDirectoryUri.toFilePath(windows: Platform.isWindows), - environment: { - "TEST_BOOL": "true", - "TEST_DB_ENV_VAR": "postgres://user:password@host:5432/dbname", - "TEST_VALUE": "1" - }); - stdout.addStream(result.stdout); - stderr.addStream(result.stderr); + workingDirectory: + ctx.buildDirectoryUri.toFilePath(windows: Platform.isWindows), + environment: { + "TEST_BOOL": "true", + "TEST_DB_ENV_VAR": "postgres://user:password@host:5432/dbname", + "TEST_VALUE": "1" + }); + await stdout.addStream(result.stdout); + await stderr.addStream(result.stderr); if (await result.exitCode != 0) { exitCode = -1; From bc037ec1f2a3521c422af91f3964e40f1c44d952 Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 15 Mar 2021 02:27:24 -0700 Subject: [PATCH 2/5] oh god im so dumb --- lib/safe_config.dart | 2 +- lib/src/compiler.dart | 4 ++-- lib/src/configuration.dart | 3 ++- lib/src/runtime.dart | 3 +-- pubspec.yaml | 2 +- tool/runtime_test_runner.dart | 7 ++++--- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/safe_config.dart b/lib/safe_config.dart index d0843a7..11b077d 100644 --- a/lib/safe_config.dart +++ b/lib/safe_config.dart @@ -3,4 +3,4 @@ library safe_config; export 'src/compiler.dart'; export 'src/configuration.dart'; -export 'src/default_configurations.dart'; \ No newline at end of file +export 'src/default_configurations.dart'; diff --git a/lib/src/compiler.dart b/lib/src/compiler.dart index 7299d21..837f40b 100644 --- a/lib/src/compiler.dart +++ b/lib/src/compiler.dart @@ -3,8 +3,8 @@ import 'dart:mirrors'; import 'package:runtime/runtime.dart'; -import 'configuration.dart'; -import 'runtime.dart'; +import 'package:safe_config/src/configuration.dart'; +import 'package:safe_config/src/runtime.dart'; class ConfigurationCompiler extends Compiler { @override diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index 609ff34..9d5f9ec 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -74,7 +74,8 @@ abstract class ConfigurationRuntime { void decode(Configuration configuration, Map input); void validate(Configuration configuration); - dynamic tryDecode(Configuration configuration, String name, void decode()) { + dynamic tryDecode( + Configuration configuration, String name, dynamic decode()) { try { return decode(); } on ConfigurationException catch (e) { diff --git a/lib/src/runtime.dart b/lib/src/runtime.dart index 3eefcaf..2f3a333 100644 --- a/lib/src/runtime.dart +++ b/lib/src/runtime.dart @@ -3,7 +3,7 @@ import 'dart:mirrors'; import 'package:runtime/runtime.dart'; import 'package:safe_config/src/configuration.dart'; -import 'mirror_property.dart'; +import 'package:safe_config/src/mirror_property.dart'; class ConfigurationRuntimeImpl extends ConfigurationRuntime implements SourceCompiler { @@ -132,7 +132,6 @@ class ConfigurationRuntimeImpl extends ConfigurationRuntime uri: type.originalDeclaration.location!.sourceUri, alsoImportOriginalFile: true) ..add("import 'package:safe_config/src/intermediate_exception.dart';"); - return """${directives.join("\n")} final instance = ConfigurationRuntimeImpl(); class ConfigurationRuntimeImpl extends ConfigurationRuntime { diff --git a/pubspec.yaml b/pubspec.yaml index b96313a..e61bfb7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,4 +14,4 @@ dependencies: git: https://github.com/j4qfrost/dart-runtime.git dev_dependencies: - test: ^1.16.4 \ No newline at end of file + test: ^1.16.5 \ No newline at end of file diff --git a/tool/runtime_test_runner.dart b/tool/runtime_test_runner.dart index 794e6ab..4389d7d 100644 --- a/tool/runtime_test_runner.dart +++ b/tool/runtime_test_runner.dart @@ -30,7 +30,6 @@ Future main(List args) async { final bm = BuildManager(ctx); await bm.build(); - print("${makePrompt()} Running tests derived from ${f.path}..."); print("${makePrompt()} Running tests derived from ${f.path}..."); final result = await Process.start("dart", ["test/main_test.dart"], workingDirectory: @@ -40,8 +39,10 @@ Future main(List args) async { "TEST_DB_ENV_VAR": "postgres://user:password@host:5432/dbname", "TEST_VALUE": "1" }); - await stdout.addStream(result.stdout); - await stderr.addStream(result.stderr); + // ignore: unawaited_futures + stdout.addStream(result.stdout); + // ignore: unawaited_futures + stderr.addStream(result.stderr); if (await result.exitCode != 0) { exitCode = -1; From 4c1effdb4cec7ad46f7893c6ed44f2a295e2e274 Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 15 Mar 2021 03:24:51 -0700 Subject: [PATCH 3/5] remove .git --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index e61bfb7..aeacce4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ environment: dependencies: yaml: ^3.1.0 runtime: - git: https://github.com/j4qfrost/dart-runtime.git + git: https://github.com/j4qfrost/dart-runtime dev_dependencies: test: ^1.16.5 \ No newline at end of file From 6bcebdbb9ba21d5c42ee9d6a05430771542f773b Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Sat, 20 Mar 2021 20:11:53 -0700 Subject: [PATCH 4/5] null check yamlmap --- lib/src/configuration.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index 9d5f9ec..a38eccf 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -20,7 +20,7 @@ abstract class Configuration { Configuration.fromString(String contents) { final yamlMap = loadYaml(contents); final map = - yamlMap.map((k, v) => MapEntry(k.toString(), v)); + yamlMap?.map((k, v) => MapEntry(k.toString(), v)); decode(map); } From 508fc7f511cd45cd4a814d92a9ffeff9db6814bc Mon Sep 17 00:00:00 2001 From: j4qfrost Date: Mon, 29 Mar 2021 12:56:00 -0500 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: Reductions --- lib/src/configuration.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index a38eccf..e519737 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -18,7 +18,7 @@ abstract class Configuration { /// [contents] must be YAML. Configuration.fromString(String contents) { - final yamlMap = loadYaml(contents); + final yamlMap = loadYaml(contents) as Map?; final map = yamlMap?.map((k, v) => MapEntry(k.toString(), v)); decode(map); @@ -75,7 +75,10 @@ abstract class ConfigurationRuntime { void validate(Configuration configuration); dynamic tryDecode( - Configuration configuration, String name, dynamic decode()) { + Configuration configuration, + String name, + dynamic decode(), +) { try { return decode(); } on ConfigurationException catch (e) {