Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

## 4.0.0-alpha.2

-
- feat: add contexts to scope

## 4.0.0-alpha.1

Expand Down
12 changes: 12 additions & 0 deletions dart/lib/src/protocol/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
/// As opposed to the runtime, this is the actual application that was
/// running and carries metadata about the current session.
class App {
static const type = 'app';

const App({
this.name,
this.version,
Expand Down Expand Up @@ -68,4 +70,14 @@ class App {

return json;
}

App clone() => App(
name: name,
version: version,
identifier: identifier,
build: build,
buildType: buildType,
startTime: startTime,
deviceAppHash: deviceAppHash,
);
}
4 changes: 4 additions & 0 deletions dart/lib/src/protocol/browser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
/// This can either be the browser this event ocurred in, or the user
/// agent of a web request that triggered the event.
class Browser {
static const type = 'browser';

/// Creates an instance of [Browser].
const Browser({this.name, this.version});

Expand All @@ -26,4 +28,6 @@ class Browser {

return json;
}

Browser clone() => Browser(name: name, version: version);
}
202 changes: 133 additions & 69 deletions dart/lib/src/protocol/contexts.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:collection';

import '../protocol.dart';
import 'app.dart';
import 'browser.dart';
Expand All @@ -11,107 +13,169 @@ import 'runtime.dart';
/// the current HTTP request.
///
/// See also: https://docs.sentry.io/development/sdk-dev/event-payloads/contexts/.
class Contexts {
const Contexts({
this.device,
this.operatingSystem,
this.runtimes,
this.app,
this.browser,
this.gpu,
});
class Contexts extends MapView<String, dynamic> {
Contexts({
Device device,
OperatingSystem operatingSystem,
List<Runtime> runtimes,
App app,
Browser browser,
Gpu gpu,
}) : super({
Device.type: device,
OperatingSystem.type: operatingSystem,
Runtime.listType: runtimes ?? [],
App.type: app,
Browser.type: browser,
Gpu.type: gpu,
});

/// This describes the device that caused the event.
final Device device;
Device get device => this[Device.type];

set device(Device device) => this[Device.type] = device;

/// Describes the operating system on which the event was created.
///
/// In web contexts, this is the operating system of the browse
/// (normally pulled from the User-Agent string).
final OperatingSystem operatingSystem;
OperatingSystem get operatingSystem => this[OperatingSystem.type];

/// Describes a runtime in more detail.
///
/// Typically this context is used multiple times if multiple runtimes
/// are involved (for instance if you have a JavaScript application running
/// on top of JVM).
final List<Runtime> runtimes;
set operatingSystem(OperatingSystem operatingSystem) =>
this[OperatingSystem.type] = operatingSystem;

/// Describes a list of runtimes in more detail
/// (for instance if you have a Flutter application running
/// on top of Android).
List<Runtime> get runtimes => List.unmodifiable(this[Runtime.listType]);

void addRuntime(Runtime runtime) => this[Runtime.listType].add(runtime);

void removeRuntime(Runtime runtime) => this[Runtime.listType].remove(runtime);

/// App context describes the application.
///
/// As opposed to the runtime, this is the actual application that was
/// running and carries metadata about the current session.
final App app;
App get app => this[App.type];

set app(App app) => this[App.type] = app;

/// Carries information about the browser or user agent for web-related
/// errors.
///
/// This can either be the browser this event ocurred in, or the user
/// agent of a web request that triggered the event.
final Browser browser;
Browser get browser => this[Browser.type];

set browser(Browser browser) => this[Browser.type] = browser;

/// GPU context describes the GPU of the device.
final Gpu gpu;
Gpu get gpu => this[Gpu.type];

// TODO: contexts should accept arbitrary values
set gpu(Gpu gpu) => this[Gpu.type] = gpu;

/// Produces a [Map] that can be serialized to JSON.
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};

Map<String, dynamic> deviceMap;
if (device != null && (deviceMap = device.toJson()).isNotEmpty) {
json['device'] = deviceMap;
}

Map<String, dynamic> osMap;
if (operatingSystem != null &&
(osMap = operatingSystem.toJson()).isNotEmpty) {
json['os'] = osMap;
}

Map<String, dynamic> appMap;
if (app != null && (appMap = app.toJson()).isNotEmpty) {
json['app'] = appMap;
}

Map<String, dynamic> browserMap;
if (browser != null && (browserMap = browser.toJson()).isNotEmpty) {
json['browser'] = browserMap;
}

Map<String, dynamic> gpuMap;
if (gpu != null && (gpuMap = gpu.toJson()).isNotEmpty) {
json['gpu'] = gpuMap;
}

if (runtimes != null) {
if (runtimes.length == 1) {
final runtime = runtimes[0];
if (runtime != null) {
final key = runtime.key ?? 'runtime';
json[key] = runtime.toJson();
}
} else if (runtimes.length > 1) {
for (final runtime in runtimes) {
if (runtime != null) {
var key = runtime.key ?? runtime.name.toLowerCase();

if (json.containsKey(key)) {
var k = 0;
while (json.containsKey(key)) {
key = '$key$k';
k++;
forEach((key, value) {
if (value == null) return;
switch (key) {
case Device.type:
Map<String, dynamic> deviceMap;
if ((deviceMap = device.toJson()).isNotEmpty) {
json[Device.type] = deviceMap;
}
break;
case OperatingSystem.type:
Map<String, dynamic> osMap;
if ((osMap = operatingSystem.toJson()).isNotEmpty) {
json[OperatingSystem.type] = osMap;
}
break;

case App.type:
Map<String, dynamic> appMap;
if ((appMap = app.toJson()).isNotEmpty) {
json[App.type] = appMap;
}
break;

case Browser.type:
Map<String, dynamic> browserMap;
if ((browserMap = browser.toJson()).isNotEmpty) {
json[Browser.type] = browserMap;
}
break;

case Gpu.type:
Map<String, dynamic> gpuMap;
if ((gpuMap = gpu.toJson()).isNotEmpty) {
json[Gpu.type] = gpuMap;
}
break;

case Runtime.listType:
if (runtimes != null) {
if (runtimes.length == 1) {
final runtime = runtimes[0];
if (runtime != null) {
final key = runtime.key ?? Runtime.type;
json[key] = runtime.toJson();
}
} else if (runtimes.length > 1) {
for (final runtime in runtimes) {
if (runtime != null) {
var key = runtime.key ?? runtime.name.toLowerCase();

if (json.containsKey(key)) {
var k = 0;
while (json.containsKey(key)) {
key = '$key$k';
k++;
}
}

json[key] = runtime.toJson()
..addAll(<String, String>{'type': Runtime.type});
}
}
}

json[key] = runtime.toJson()
..addAll(<String, String>{'type': 'runtime'});
}
}

break;

default:
json[key] = value;
}
}
});

return json;
}

Contexts clone() {
final copy = Contexts(
device: device?.clone(),
operatingSystem: operatingSystem?.clone(),
app: app?.clone(),
browser: browser?.clone(),
gpu: gpu?.clone(),
runtimes: runtimes.map((runtime) => runtime.clone()).toList(),
)..addEntries(
entries.where((element) => !_defaultFields.contains(element.key)),
);

return copy;
}

static const _defaultFields = [
App.type,
Device.type,
OperatingSystem.type,
Runtime.listType,
Runtime.type,
Gpu.type,
Browser.type,
];
}
30 changes: 30 additions & 0 deletions dart/lib/src/protocol/device.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ enum Orientation { portrait, landscape }

/// This describes the device that caused the event.
class Device {
static const type = 'device';

const Device({
this.name,
this.family,
Expand Down Expand Up @@ -230,4 +232,32 @@ class Device {

return json;
}

Device clone() => Device(
name: name,
family: family,
model: model,
modelId: modelId,
arch: arch,
batteryLevel: batteryLevel,
orientation: orientation,
manufacturer: manufacturer,
brand: brand,
screenResolution: screenResolution,
screenDensity: screenDensity,
screenDpi: screenDpi,
online: online,
charging: charging,
lowMemory: lowMemory,
simulator: simulator,
memorySize: memorySize,
freeMemory: freeMemory,
usableMemory: usableMemory,
storageSize: storageSize,
freeStorage: freeStorage,
externalStorageSize: externalStorageSize,
externalFreeStorage: externalFreeStorage,
bootTime: bootTime,
timezone: timezone,
);
}
14 changes: 14 additions & 0 deletions dart/lib/src/protocol/gpu.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// }

class Gpu {
static const type = 'gpu';

/// The name of the graphics device.
final String name;

Expand Down Expand Up @@ -50,6 +52,18 @@ class Gpu {
this.npotSupport,
});

Gpu clone() => Gpu(
name: name,
id: id,
vendorId: vendorId,
vendorName: vendorName,
memorySize: memorySize,
apiType: apiType,
multiThreadedRendering: multiThreadedRendering,
version: version,
npotSupport: npotSupport,
);

/// Produces a [Map] that can be serialized to JSON.
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
Expand Down
10 changes: 10 additions & 0 deletions dart/lib/src/protocol/runtime.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// are involved (for instance if you have a JavaScript application running
/// on top of JVM).
class Runtime {
static const listType = 'runtimes';
static const type = 'runtime';

const Runtime({this.key, this.name, this.version, this.rawDescription})
: assert(key == null || key.length >= 1);

Expand Down Expand Up @@ -43,4 +46,11 @@ class Runtime {

return json;
}

Runtime clone() => Runtime(
key: key,
name: name,
version: version,
rawDescription: rawDescription,
);
}
5 changes: 3 additions & 2 deletions dart/lib/src/protocol/sentry_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ class SentryEvent {
this.extra,
this.fingerprint,
this.user,
this.contexts,
Contexts contexts,
this.breadcrumbs,
}) : eventId = eventId ?? SentryId.newId(),
timestamp = timestamp ?? getUtcDateTime();
timestamp = timestamp ?? getUtcDateTime(),
contexts = contexts ?? Contexts();

/// Refers to the default fingerprinting algorithm.
///
Expand Down
Loading