Skip to content

Commit 6e9c5a2

Browse files
authored
Deserialize and serialize unknown fields (#2153)
1 parent 2e1e4ae commit 6e9c5a2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+864
-257
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
);
1919
```
2020

21+
### Improvements
22+
23+
- Deserialize and serialize unknown fields ([#2153](https://github.com/getsentry/sentry-dart/pull/2153))
24+
2125
## 8.6.0
2226

2327
### Improvements
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import 'dart:collection';
2+
3+
import 'package:meta/meta.dart';
4+
5+
@internal
6+
class AccessAwareMap<String, V> extends MapBase<String, V> {
7+
AccessAwareMap(this._map);
8+
9+
final Map<String, V> _map;
10+
final Set<String> _accessedKeysWithValues = {};
11+
12+
Set<String> get accessedKeysWithValues => _accessedKeysWithValues;
13+
14+
@override
15+
V? operator [](Object? key) {
16+
if (key is String && _map.containsKey(key)) {
17+
_accessedKeysWithValues.add(key);
18+
}
19+
return _map[key];
20+
}
21+
22+
@override
23+
void operator []=(String key, V value) {
24+
_map[key] = value;
25+
}
26+
27+
@override
28+
void clear() {
29+
_map.clear();
30+
_accessedKeysWithValues.clear();
31+
}
32+
33+
@override
34+
Iterable<String> get keys => _map.keys;
35+
36+
@override
37+
V? remove(Object? key) {
38+
return _map.remove(key);
39+
}
40+
41+
Map<String, dynamic>? notAccessed() {
42+
if (_accessedKeysWithValues.length == _map.length) {
43+
return null;
44+
}
45+
Map<String, dynamic> unknown = _map.keys
46+
.where((key) => !_accessedKeysWithValues.contains(key))
47+
.fold<Map<String, dynamic>>({}, (map, key) {
48+
map[key] = _map[key];
49+
return map;
50+
});
51+
return unknown.isNotEmpty ? unknown : null;
52+
}
53+
}

dart/lib/src/protocol/breadcrumb.dart

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:meta/meta.dart';
22

33
import '../utils.dart';
44
import '../protocol.dart';
5+
import 'access_aware_map.dart';
56

67
/// Structured data to describe more information prior to the event captured.
78
/// See `Sentry.captureEvent()`.
@@ -30,6 +31,7 @@ class Breadcrumb {
3031
this.data,
3132
SentryLevel? level,
3233
this.type,
34+
this.unknown,
3335
}) : timestamp = timestamp ?? getUtcDateTime(),
3436
level = level ?? SentryLevel.info;
3537

@@ -156,30 +158,36 @@ class Breadcrumb {
156158
/// The value is submitted to Sentry with second precision.
157159
final DateTime timestamp;
158160

161+
@internal
162+
final Map<String, dynamic>? unknown;
163+
159164
/// Deserializes a [Breadcrumb] from JSON [Map].
160-
factory Breadcrumb.fromJson(Map<String, dynamic> json) {
165+
factory Breadcrumb.fromJson(Map<String, dynamic> jsonData) {
166+
final json = AccessAwareMap(jsonData);
167+
161168
final levelName = json['level'];
162169
final timestamp = json['timestamp'];
163170

164171
var data = json['data'];
165172
if (data != null) {
166173
data = Map<String, dynamic>.from(data as Map);
167174
}
168-
169175
return Breadcrumb(
170176
timestamp: timestamp != null ? DateTime.tryParse(timestamp) : null,
171177
message: json['message'],
172178
category: json['category'],
173179
data: data,
174180
level: levelName != null ? SentryLevel.fromName(levelName) : null,
175181
type: json['type'],
182+
unknown: json.notAccessed(),
176183
);
177184
}
178185

179186
/// Converts this breadcrumb to a map that can be serialized to JSON according
180187
/// to the Sentry protocol.
181188
Map<String, dynamic> toJson() {
182189
return {
190+
...?unknown,
183191
'timestamp': formatDateAsIso8601WithMillisPrecision(timestamp),
184192
if (message != null) 'message': message,
185193
if (category != null) 'category': category,
@@ -204,5 +212,6 @@ class Breadcrumb {
204212
level: level ?? this.level,
205213
type: type ?? this.type,
206214
timestamp: timestamp ?? this.timestamp,
215+
unknown: unknown,
207216
);
208217
}

dart/lib/src/protocol/debug_image.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import 'package:meta/meta.dart';
22

3+
import 'access_aware_map.dart';
4+
35
/// The list of debug images contains all dynamic libraries loaded into
46
/// the process and their memory addresses.
57
/// Instruction addresses in the Stack Trace are mapped into the list of debug
@@ -51,6 +53,9 @@ class DebugImage {
5153
/// MachO CPU type identifier.
5254
final int? cpuType;
5355

56+
@internal
57+
final Map<String, dynamic>? unknown;
58+
5459
const DebugImage({
5560
required this.type,
5661
this.name,
@@ -65,10 +70,12 @@ class DebugImage {
6570
this.codeId,
6671
this.cpuType,
6772
this.cpuSubtype,
73+
this.unknown,
6874
});
6975

7076
/// Deserializes a [DebugImage] from JSON [Map].
71-
factory DebugImage.fromJson(Map<String, dynamic> json) {
77+
factory DebugImage.fromJson(Map<String, dynamic> data) {
78+
final json = AccessAwareMap(data);
7279
return DebugImage(
7380
type: json['type'],
7481
name: json['name'],
@@ -83,12 +90,14 @@ class DebugImage {
8390
codeId: json['code_id'],
8491
cpuType: json['cpu_type'],
8592
cpuSubtype: json['cpu_subtype'],
93+
unknown: json.notAccessed(),
8694
);
8795
}
8896

8997
/// Produces a [Map] that can be serialized to JSON.
9098
Map<String, dynamic> toJson() {
9199
return {
100+
...?unknown,
92101
'type': type,
93102
if (uuid != null) 'uuid': uuid,
94103
if (debugId != null) 'debug_id': debugId,
@@ -134,5 +143,6 @@ class DebugImage {
134143
codeId: codeId ?? this.codeId,
135144
cpuType: cpuType ?? this.cpuType,
136145
cpuSubtype: cpuSubtype ?? this.cpuSubtype,
146+
unknown: unknown,
137147
);
138148
}

dart/lib/src/protocol/debug_meta.dart

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:meta/meta.dart';
22

33
import '../protocol.dart';
4+
import 'access_aware_map.dart';
45

56
/// The debug meta interface carries debug information for processing errors and crash reports.
67
@immutable
@@ -16,10 +17,15 @@ class DebugMeta {
1617
/// images in order to retrieve debug files for symbolication.
1718
List<DebugImage> get images => List.unmodifiable(_images ?? const []);
1819

19-
DebugMeta({this.sdk, List<DebugImage>? images}) : _images = images;
20+
DebugMeta({this.sdk, List<DebugImage>? images, this.unknown})
21+
: _images = images;
22+
23+
@internal
24+
final Map<String, dynamic>? unknown;
2025

2126
/// Deserializes a [DebugMeta] from JSON [Map].
22-
factory DebugMeta.fromJson(Map<String, dynamic> json) {
27+
factory DebugMeta.fromJson(Map<String, dynamic> data) {
28+
final json = AccessAwareMap(data);
2329
final sdkInfoJson = json['sdk_info'];
2430
final debugImagesJson = json['images'] as List<dynamic>?;
2531
return DebugMeta(
@@ -28,19 +34,21 @@ class DebugMeta {
2834
?.map((debugImageJson) =>
2935
DebugImage.fromJson(debugImageJson as Map<String, dynamic>))
3036
.toList(),
37+
unknown: json.notAccessed(),
3138
);
3239
}
3340

3441
/// Produces a [Map] that can be serialized to JSON.
3542
Map<String, dynamic> toJson() {
3643
final sdkInfo = sdk?.toJson();
3744
return {
45+
...?unknown,
3846
if (sdkInfo?.isNotEmpty ?? false) 'sdk_info': sdkInfo,
3947
if (_images?.isNotEmpty ?? false)
4048
'images': _images!
4149
.map((e) => e.toJson())
4250
.where((element) => element.isNotEmpty)
43-
.toList(growable: false)
51+
.toList(growable: false),
4452
};
4553
}
4654

@@ -51,5 +59,6 @@ class DebugMeta {
5159
DebugMeta(
5260
sdk: sdk ?? this.sdk,
5361
images: images ?? _images,
62+
unknown: unknown,
5463
);
5564
}

dart/lib/src/protocol/mechanism.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import 'package:meta/meta.dart';
22

3+
import 'access_aware_map.dart';
4+
35
/// Sentry Exception Mechanism
46
/// The exception mechanism is an optional field residing
57
/// in the Exception Interface. It carries additional information about
@@ -76,6 +78,9 @@ class Mechanism {
7678
/// (the last to be listed in the exception values).
7779
final int? parentId;
7880

81+
@internal
82+
final Map<String, dynamic>? unknown;
83+
7984
Mechanism({
8085
required this.type,
8186
this.description,
@@ -88,6 +93,7 @@ class Mechanism {
8893
this.source,
8994
this.exceptionId,
9095
this.parentId,
96+
this.unknown,
9197
}) : _meta = meta != null ? Map.from(meta) : null,
9298
_data = data != null ? Map.from(data) : null;
9399

@@ -116,10 +122,12 @@ class Mechanism {
116122
source: source ?? this.source,
117123
exceptionId: exceptionId ?? this.exceptionId,
118124
parentId: parentId ?? this.parentId,
125+
unknown: unknown,
119126
);
120127

121128
/// Deserializes a [Mechanism] from JSON [Map].
122-
factory Mechanism.fromJson(Map<String, dynamic> json) {
129+
factory Mechanism.fromJson(Map<String, dynamic> jsonData) {
130+
final json = AccessAwareMap(jsonData);
123131
var data = json['data'];
124132
if (data != null) {
125133
data = Map<String, dynamic>.from(data as Map);
@@ -142,12 +150,14 @@ class Mechanism {
142150
source: json['source'],
143151
exceptionId: json['exception_id'],
144152
parentId: json['parent_id'],
153+
unknown: json.notAccessed(),
145154
);
146155
}
147156

148157
/// Produces a [Map] that can be serialized to JSON.
149158
Map<String, dynamic> toJson() {
150159
return {
160+
...?unknown,
151161
'type': type,
152162
if (description != null) 'description': description,
153163
if (helpLink != null) 'help_link': helpLink,

dart/lib/src/protocol/metric_summary.dart

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import 'package:meta/meta.dart';
2+
13
import '../metrics/metric.dart';
4+
import 'access_aware_map.dart';
25

36
class MetricSummary {
47
final num min;
@@ -7,7 +10,10 @@ class MetricSummary {
710
final int count;
811
final Map<String, String>? tags;
912

10-
MetricSummary.fromGauge(GaugeMetric gauge)
13+
@internal
14+
final Map<String, dynamic>? unknown;
15+
16+
MetricSummary.fromGauge(GaugeMetric gauge, {this.unknown})
1117
: min = gauge.minimum,
1218
max = gauge.maximum,
1319
sum = gauge.sum,
@@ -19,20 +25,26 @@ class MetricSummary {
1925
required this.max,
2026
required this.sum,
2127
required this.count,
22-
required this.tags});
28+
required this.tags,
29+
this.unknown});
2330

2431
/// Deserializes a [MetricSummary] from JSON [Map].
25-
factory MetricSummary.fromJson(Map<String, dynamic> data) => MetricSummary(
26-
min: data['min'],
27-
max: data['max'],
28-
count: data['count'],
29-
sum: data['sum'],
30-
tags: data['tags']?.cast<String, String>(),
31-
);
32+
factory MetricSummary.fromJson(Map<String, dynamic> data) {
33+
final json = AccessAwareMap(data);
34+
return MetricSummary(
35+
min: json['min'],
36+
max: json['max'],
37+
count: json['count'],
38+
sum: json['sum'],
39+
tags: json['tags']?.cast<String, String>(),
40+
unknown: json.notAccessed(),
41+
);
42+
}
3243

3344
/// Produces a [Map] that can be serialized to JSON.
3445
Map<String, dynamic> toJson() {
35-
return <String, dynamic>{
46+
return {
47+
...?unknown,
3648
'min': min,
3749
'max': max,
3850
'count': count,

dart/lib/src/protocol/sdk_info.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import 'package:meta/meta.dart';
22

3+
import 'access_aware_map.dart';
4+
35
/// An object describing the system SDK.
46
@immutable
57
class SdkInfo {
@@ -8,26 +10,33 @@ class SdkInfo {
810
final int? versionMinor;
911
final int? versionPatchlevel;
1012

13+
@internal
14+
final Map<String, dynamic>? unknown;
15+
1116
const SdkInfo({
1217
this.sdkName,
1318
this.versionMajor,
1419
this.versionMinor,
1520
this.versionPatchlevel,
21+
this.unknown,
1622
});
1723

1824
/// Deserializes a [SdkInfo] from JSON [Map].
19-
factory SdkInfo.fromJson(Map<String, dynamic> json) {
25+
factory SdkInfo.fromJson(Map<String, dynamic> data) {
26+
final json = AccessAwareMap(data);
2027
return SdkInfo(
2128
sdkName: json['sdk_name'],
2229
versionMajor: json['version_major'],
2330
versionMinor: json['version_minor'],
2431
versionPatchlevel: json['version_patchlevel'],
32+
unknown: json.notAccessed(),
2533
);
2634
}
2735

2836
/// Produces a [Map] that can be serialized to JSON.
2937
Map<String, dynamic> toJson() {
3038
return {
39+
...?unknown,
3140
if (sdkName != null) 'sdk_name': sdkName,
3241
if (versionMajor != null) 'version_major': versionMajor,
3342
if (versionMinor != null) 'version_minor': versionMinor,
@@ -46,5 +55,6 @@ class SdkInfo {
4655
versionMajor: versionMajor ?? this.versionMajor,
4756
versionMinor: versionMinor ?? this.versionMinor,
4857
versionPatchlevel: versionPatchlevel ?? this.versionPatchlevel,
58+
unknown: unknown,
4959
);
5060
}

0 commit comments

Comments
 (0)