Skip to content

Commit 999c651

Browse files
authored
checked_yaml: improve handling of missing keys (#818)
If a CheckedFromJsonException exception is caught for a key that is missing in the source map, include that information in the message Also correctly handle the case where CheckedFromJsonException.message is `null` Fixes #816
1 parent 0dd731c commit 999c651

File tree

9 files changed

+95
-22
lines changed

9 files changed

+95
-22
lines changed

checked_yaml/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 2.0.1
2+
3+
- If `CheckedFromJsonException` is caught for a key missing in the source map,
4+
include those details in the thrown `ParsedYamlException`.
5+
6+
- Correctly handle the case where `CheckedFromJsonException.message` is `null`.
7+
18
## 2.0.0
29

310
- *BREAKING* `checkedYamlDecode` `sourceUrl` parameter is now a `Uri`.

checked_yaml/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ for the class annotation.
1818
class Configuration {
1919
@JsonKey(required: true)
2020
final String name;
21-
@JsonKey(required: true)
2221
final int count;
2322
2423
Configuration({required this.name, required this.count}) {

checked_yaml/example/example.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ part 'example.g.dart';
1717
class Configuration {
1818
@JsonKey(required: true)
1919
final String name;
20-
@JsonKey(required: true)
2120
final int count;
2221

2322
Configuration({required this.name, required this.count}) {

checked_yaml/example/example.g.dart

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

checked_yaml/lib/checked_yaml.dart

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,18 @@ ParsedYamlException toParsedYamlException(
7575
innerError: exception,
7676
);
7777
} else {
78-
final yamlValue = yamlMap.nodes[exception.key];
79-
80-
if (yamlValue == null) {
81-
// TODO: test this case!
78+
if (exception.key == null) {
79+
return ParsedYamlException(
80+
exception.message ?? 'There was an error parsing the map.',
81+
yamlMap,
82+
innerError: exception,
83+
);
84+
} else if (!yamlMap.containsKey(exception.key)) {
8285
return ParsedYamlException(
83-
exception.message!,
86+
[
87+
'Missing key "${exception.key}".',
88+
if (exception.message != null) exception.message!,
89+
].join(' '),
8490
yamlMap,
8591
innerError: exception,
8692
);
@@ -91,7 +97,7 @@ ParsedYamlException toParsedYamlException(
9197
}
9298
return ParsedYamlException(
9399
message,
94-
yamlValue,
100+
yamlMap.nodes[exception.key] ?? yamlMap,
95101
innerError: exception,
96102
);
97103
}

checked_yaml/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: checked_yaml
2-
version: 2.0.0
2+
version: 2.0.1
33

44
description: >-
55
Generate more helpful exceptions when decoding YAML documents using
@@ -19,7 +19,7 @@ dev_dependencies:
1919
json_serializable: ^4.0.0
2020
path: ^1.0.0
2121
test: ^1.16.0
22-
test_process: ^1.0.1
22+
test_process: ^2.0.0
2323

2424
dependency_overrides:
2525
# Need to update dependencies on these packages
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import 'package:checked_yaml/checked_yaml.dart';
2+
import 'package:json_annotation/json_annotation.dart';
3+
import 'package:test/test.dart';
4+
import 'package:yaml/yaml.dart';
5+
6+
void main() {
7+
test('bob', () {
8+
expect(
9+
() => checkedYamlDecode(
10+
'{"innerMap": {}}',
11+
(m) {
12+
throw CheckedFromJsonException(
13+
m!['innerMap'] as YamlMap,
14+
null,
15+
'nothing',
16+
null,
17+
);
18+
},
19+
),
20+
throwsA(
21+
isA<ParsedYamlException>()
22+
.having(
23+
(e) => e.message,
24+
'message',
25+
'There was an error parsing the map.',
26+
)
27+
.having((e) => e.yamlNode, 'yamlNode', isA<YamlMap>())
28+
.having(
29+
(e) => e.innerError,
30+
'innerError',
31+
isA<CheckedFromJsonException>(),
32+
)
33+
.having((e) => e.formattedMessage, 'formattedMessage', '''
34+
line 1, column 14: There was an error parsing the map.
35+
36+
1 │ {"innerMap": {}}
37+
│ ^^
38+
╵'''),
39+
),
40+
);
41+
});
42+
}

checked_yaml/test/example_test.dart

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,26 @@ Configuration: {name: bob, count: 42}
2121
_expectThrows(
2222
'{}',
2323
r'''
24-
line 1, column 1: Required keys are missing: name, count.
24+
line 1, column 1: Required keys are missing: name.
2525
2626
1 │ {}
2727
│ ^^
2828
╵''',
2929
);
3030
});
3131

32+
test('missing count', () {
33+
_expectThrows(
34+
'{"name":"something"}',
35+
r'''
36+
line 1, column 1: Missing key "count". type 'Null' is not a subtype of type 'int' in type cast
37+
38+
1 │ {"name":"something"}
39+
│ ^^^^^^^^^^^^^^^^^^^^
40+
╵''',
41+
);
42+
});
43+
3244
test('not a map', () {
3345
_expectThrows(
3446
'42',
@@ -103,10 +115,17 @@ line 1, column 10: Unsupported value for "name". Cannot be empty.
103115
}
104116

105117
void _expectThrows(String yamlContent, matcher) => expect(
106-
() => _run(yamlContent),
107-
throwsA(isA<ParsedYamlException>().having((e) {
108-
printOnFailure("r'''\n${e.formattedMessage}'''");
109-
return e.formattedMessage;
110-
}, 'formattedMessage', matcher)));
118+
() => _run(yamlContent),
119+
throwsA(
120+
isA<ParsedYamlException>().having(
121+
(e) {
122+
printOnFailure("r'''\n${e.formattedMessage}'''");
123+
return e.formattedMessage;
124+
},
125+
'formattedMessage',
126+
matcher,
127+
),
128+
),
129+
);
111130

112131
void _run(String yamlContent) => example.main([yamlContent]);

checked_yaml/test/readme_test.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
// @dart=2.9
6-
75
import 'dart:convert';
86
import 'dart:io';
97

@@ -61,9 +59,13 @@ $errorContent
6159
```'''));
6260

6361
final proc = await TestProcess.start(
64-
Platform.resolvedExecutable, [_examplePath, inputContent]);
62+
Platform.resolvedExecutable,
63+
[_examplePath, inputContent],
64+
);
6565
await expectLater(
66-
proc.stderr, emitsInOrder(LineSplitter.split(errorContent)));
66+
proc.stderr,
67+
emitsInOrder(LineSplitter.split(errorContent)),
68+
);
6769

6870
await proc.shouldExit(isNot(0));
6971
});

0 commit comments

Comments
 (0)