Skip to content

Commit

Permalink
Deserialize and serialize unknown fields (#2153)
Browse files Browse the repository at this point in the history
  • Loading branch information
denrase authored Aug 6, 2024
1 parent 2e1e4ae commit 6e9c5a2
Show file tree
Hide file tree
Showing 55 changed files with 864 additions and 257 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
);
```

### Improvements

- Deserialize and serialize unknown fields ([#2153](https://github.com/getsentry/sentry-dart/pull/2153))

## 8.6.0

### Improvements
Expand Down
53 changes: 53 additions & 0 deletions dart/lib/src/protocol/access_aware_map.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import 'dart:collection';

import 'package:meta/meta.dart';

@internal
class AccessAwareMap<String, V> extends MapBase<String, V> {
AccessAwareMap(this._map);

final Map<String, V> _map;
final Set<String> _accessedKeysWithValues = {};

Set<String> get accessedKeysWithValues => _accessedKeysWithValues;

@override
V? operator [](Object? key) {
if (key is String && _map.containsKey(key)) {
_accessedKeysWithValues.add(key);
}
return _map[key];
}

@override
void operator []=(String key, V value) {
_map[key] = value;
}

@override
void clear() {
_map.clear();
_accessedKeysWithValues.clear();
}

@override
Iterable<String> get keys => _map.keys;

@override
V? remove(Object? key) {
return _map.remove(key);
}

Map<String, dynamic>? notAccessed() {
if (_accessedKeysWithValues.length == _map.length) {
return null;
}
Map<String, dynamic> unknown = _map.keys
.where((key) => !_accessedKeysWithValues.contains(key))
.fold<Map<String, dynamic>>({}, (map, key) {
map[key] = _map[key];
return map;
});
return unknown.isNotEmpty ? unknown : null;
}
}
13 changes: 11 additions & 2 deletions dart/lib/src/protocol/breadcrumb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:meta/meta.dart';

import '../utils.dart';
import '../protocol.dart';
import 'access_aware_map.dart';

/// Structured data to describe more information prior to the event captured.
/// See `Sentry.captureEvent()`.
Expand Down Expand Up @@ -30,6 +31,7 @@ class Breadcrumb {
this.data,
SentryLevel? level,
this.type,
this.unknown,
}) : timestamp = timestamp ?? getUtcDateTime(),
level = level ?? SentryLevel.info;

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

@internal
final Map<String, dynamic>? unknown;

/// Deserializes a [Breadcrumb] from JSON [Map].
factory Breadcrumb.fromJson(Map<String, dynamic> json) {
factory Breadcrumb.fromJson(Map<String, dynamic> jsonData) {
final json = AccessAwareMap(jsonData);

final levelName = json['level'];
final timestamp = json['timestamp'];

var data = json['data'];
if (data != null) {
data = Map<String, dynamic>.from(data as Map);
}

return Breadcrumb(
timestamp: timestamp != null ? DateTime.tryParse(timestamp) : null,
message: json['message'],
category: json['category'],
data: data,
level: levelName != null ? SentryLevel.fromName(levelName) : null,
type: json['type'],
unknown: json.notAccessed(),
);
}

/// Converts this breadcrumb to a map that can be serialized to JSON according
/// to the Sentry protocol.
Map<String, dynamic> toJson() {
return {
...?unknown,
'timestamp': formatDateAsIso8601WithMillisPrecision(timestamp),
if (message != null) 'message': message,
if (category != null) 'category': category,
Expand All @@ -204,5 +212,6 @@ class Breadcrumb {
level: level ?? this.level,
type: type ?? this.type,
timestamp: timestamp ?? this.timestamp,
unknown: unknown,
);
}
12 changes: 11 additions & 1 deletion dart/lib/src/protocol/debug_image.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:meta/meta.dart';

import 'access_aware_map.dart';

/// The list of debug images contains all dynamic libraries loaded into
/// the process and their memory addresses.
/// Instruction addresses in the Stack Trace are mapped into the list of debug
Expand Down Expand Up @@ -51,6 +53,9 @@ class DebugImage {
/// MachO CPU type identifier.
final int? cpuType;

@internal
final Map<String, dynamic>? unknown;

const DebugImage({
required this.type,
this.name,
Expand All @@ -65,10 +70,12 @@ class DebugImage {
this.codeId,
this.cpuType,
this.cpuSubtype,
this.unknown,
});

/// Deserializes a [DebugImage] from JSON [Map].
factory DebugImage.fromJson(Map<String, dynamic> json) {
factory DebugImage.fromJson(Map<String, dynamic> data) {
final json = AccessAwareMap(data);
return DebugImage(
type: json['type'],
name: json['name'],
Expand All @@ -83,12 +90,14 @@ class DebugImage {
codeId: json['code_id'],
cpuType: json['cpu_type'],
cpuSubtype: json['cpu_subtype'],
unknown: json.notAccessed(),
);
}

/// Produces a [Map] that can be serialized to JSON.
Map<String, dynamic> toJson() {
return {
...?unknown,
'type': type,
if (uuid != null) 'uuid': uuid,
if (debugId != null) 'debug_id': debugId,
Expand Down Expand Up @@ -134,5 +143,6 @@ class DebugImage {
codeId: codeId ?? this.codeId,
cpuType: cpuType ?? this.cpuType,
cpuSubtype: cpuSubtype ?? this.cpuSubtype,
unknown: unknown,
);
}
15 changes: 12 additions & 3 deletions dart/lib/src/protocol/debug_meta.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:meta/meta.dart';

import '../protocol.dart';
import 'access_aware_map.dart';

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

DebugMeta({this.sdk, List<DebugImage>? images}) : _images = images;
DebugMeta({this.sdk, List<DebugImage>? images, this.unknown})
: _images = images;

@internal
final Map<String, dynamic>? unknown;

/// Deserializes a [DebugMeta] from JSON [Map].
factory DebugMeta.fromJson(Map<String, dynamic> json) {
factory DebugMeta.fromJson(Map<String, dynamic> data) {
final json = AccessAwareMap(data);
final sdkInfoJson = json['sdk_info'];
final debugImagesJson = json['images'] as List<dynamic>?;
return DebugMeta(
Expand All @@ -28,19 +34,21 @@ class DebugMeta {
?.map((debugImageJson) =>
DebugImage.fromJson(debugImageJson as Map<String, dynamic>))
.toList(),
unknown: json.notAccessed(),
);
}

/// Produces a [Map] that can be serialized to JSON.
Map<String, dynamic> toJson() {
final sdkInfo = sdk?.toJson();
return {
...?unknown,
if (sdkInfo?.isNotEmpty ?? false) 'sdk_info': sdkInfo,
if (_images?.isNotEmpty ?? false)
'images': _images!
.map((e) => e.toJson())
.where((element) => element.isNotEmpty)
.toList(growable: false)
.toList(growable: false),
};
}

Expand All @@ -51,5 +59,6 @@ class DebugMeta {
DebugMeta(
sdk: sdk ?? this.sdk,
images: images ?? _images,
unknown: unknown,
);
}
12 changes: 11 additions & 1 deletion dart/lib/src/protocol/mechanism.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:meta/meta.dart';

import 'access_aware_map.dart';

/// Sentry Exception Mechanism
/// The exception mechanism is an optional field residing
/// in the Exception Interface. It carries additional information about
Expand Down Expand Up @@ -76,6 +78,9 @@ class Mechanism {
/// (the last to be listed in the exception values).
final int? parentId;

@internal
final Map<String, dynamic>? unknown;

Mechanism({
required this.type,
this.description,
Expand All @@ -88,6 +93,7 @@ class Mechanism {
this.source,
this.exceptionId,
this.parentId,
this.unknown,
}) : _meta = meta != null ? Map.from(meta) : null,
_data = data != null ? Map.from(data) : null;

Expand Down Expand Up @@ -116,10 +122,12 @@ class Mechanism {
source: source ?? this.source,
exceptionId: exceptionId ?? this.exceptionId,
parentId: parentId ?? this.parentId,
unknown: unknown,
);

/// Deserializes a [Mechanism] from JSON [Map].
factory Mechanism.fromJson(Map<String, dynamic> json) {
factory Mechanism.fromJson(Map<String, dynamic> jsonData) {
final json = AccessAwareMap(jsonData);
var data = json['data'];
if (data != null) {
data = Map<String, dynamic>.from(data as Map);
Expand All @@ -142,12 +150,14 @@ class Mechanism {
source: json['source'],
exceptionId: json['exception_id'],
parentId: json['parent_id'],
unknown: json.notAccessed(),
);
}

/// Produces a [Map] that can be serialized to JSON.
Map<String, dynamic> toJson() {
return {
...?unknown,
'type': type,
if (description != null) 'description': description,
if (helpLink != null) 'help_link': helpLink,
Expand Down
32 changes: 22 additions & 10 deletions dart/lib/src/protocol/metric_summary.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import 'package:meta/meta.dart';

import '../metrics/metric.dart';
import 'access_aware_map.dart';

class MetricSummary {
final num min;
Expand All @@ -7,7 +10,10 @@ class MetricSummary {
final int count;
final Map<String, String>? tags;

MetricSummary.fromGauge(GaugeMetric gauge)
@internal
final Map<String, dynamic>? unknown;

MetricSummary.fromGauge(GaugeMetric gauge, {this.unknown})
: min = gauge.minimum,
max = gauge.maximum,
sum = gauge.sum,
Expand All @@ -19,20 +25,26 @@ class MetricSummary {
required this.max,
required this.sum,
required this.count,
required this.tags});
required this.tags,
this.unknown});

/// Deserializes a [MetricSummary] from JSON [Map].
factory MetricSummary.fromJson(Map<String, dynamic> data) => MetricSummary(
min: data['min'],
max: data['max'],
count: data['count'],
sum: data['sum'],
tags: data['tags']?.cast<String, String>(),
);
factory MetricSummary.fromJson(Map<String, dynamic> data) {
final json = AccessAwareMap(data);
return MetricSummary(
min: json['min'],
max: json['max'],
count: json['count'],
sum: json['sum'],
tags: json['tags']?.cast<String, String>(),
unknown: json.notAccessed(),
);
}

/// Produces a [Map] that can be serialized to JSON.
Map<String, dynamic> toJson() {
return <String, dynamic>{
return {
...?unknown,
'min': min,
'max': max,
'count': count,
Expand Down
12 changes: 11 additions & 1 deletion dart/lib/src/protocol/sdk_info.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:meta/meta.dart';

import 'access_aware_map.dart';

/// An object describing the system SDK.
@immutable
class SdkInfo {
Expand All @@ -8,26 +10,33 @@ class SdkInfo {
final int? versionMinor;
final int? versionPatchlevel;

@internal
final Map<String, dynamic>? unknown;

const SdkInfo({
this.sdkName,
this.versionMajor,
this.versionMinor,
this.versionPatchlevel,
this.unknown,
});

/// Deserializes a [SdkInfo] from JSON [Map].
factory SdkInfo.fromJson(Map<String, dynamic> json) {
factory SdkInfo.fromJson(Map<String, dynamic> data) {
final json = AccessAwareMap(data);
return SdkInfo(
sdkName: json['sdk_name'],
versionMajor: json['version_major'],
versionMinor: json['version_minor'],
versionPatchlevel: json['version_patchlevel'],
unknown: json.notAccessed(),
);
}

/// Produces a [Map] that can be serialized to JSON.
Map<String, dynamic> toJson() {
return {
...?unknown,
if (sdkName != null) 'sdk_name': sdkName,
if (versionMajor != null) 'version_major': versionMajor,
if (versionMinor != null) 'version_minor': versionMinor,
Expand All @@ -46,5 +55,6 @@ class SdkInfo {
versionMajor: versionMajor ?? this.versionMajor,
versionMinor: versionMinor ?? this.versionMinor,
versionPatchlevel: versionPatchlevel ?? this.versionPatchlevel,
unknown: unknown,
);
}
Loading

0 comments on commit 6e9c5a2

Please sign in to comment.