Skip to content

Commit 9828733

Browse files
authored
Feat: Add culture context (#491)
* add culture context * Update CHANGELOG.md * Update dart/lib/src/protocol/sentry_culture.dart * fix formatting * fix to json
1 parent 2bf8577 commit 9828733

File tree

6 files changed

+159
-0
lines changed

6 files changed

+159
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased
22

3+
* Feat: Add Culture Context (#491)
34
* Remove `Sentry.currentHub` (#490)
45

56
# 5.1.0

dart/lib/src/protocol.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ export 'protocol/sentry_runtime.dart';
2222
export 'protocol/sentry_stack_frame.dart';
2323
export 'protocol/sentry_stack_trace.dart';
2424
export 'protocol/sentry_user.dart';
25+
export 'protocol/sentry_culture.dart';

dart/lib/src/protocol/contexts.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ class Contexts extends MapView<String, dynamic> {
1616
SentryApp? app,
1717
SentryBrowser? browser,
1818
SentryGpu? gpu,
19+
SentryCulture? culture,
1920
}) : super({
2021
SentryDevice.type: device,
2122
SentryOperatingSystem.type: operatingSystem,
2223
SentryRuntime.listType: runtimes ?? [],
2324
SentryApp.type: app,
2425
SentryBrowser.type: browser,
2526
SentryGpu.type: gpu,
27+
SentryCulture.type: culture,
2628
});
2729

2830
factory Contexts.fromJson(Map<String, dynamic> data) {
@@ -40,6 +42,9 @@ class Contexts extends MapView<String, dynamic> {
4042
browser: data[SentryBrowser.type] != null
4143
? SentryBrowser.fromJson(Map.from(data[SentryBrowser.type]))
4244
: null,
45+
culture: data[SentryCulture.type] != null
46+
? SentryCulture.fromJson(Map.from(data[SentryCulture.type]))
47+
: null,
4348
gpu: data[SentryGpu.type] != null
4449
? SentryGpu.fromJson(Map.from(data[SentryGpu.type]))
4550
: null,
@@ -99,6 +104,12 @@ class Contexts extends MapView<String, dynamic> {
99104

100105
set browser(SentryBrowser? browser) => this[SentryBrowser.type] = browser;
101106

107+
/// Culture Context describes certain properties of the culture in which the
108+
/// software is used.
109+
SentryCulture? get culture => this[SentryCulture.type];
110+
111+
set culture(SentryCulture? culture) => this[SentryCulture.type] = culture;
112+
102113
/// GPU context describes the GPU of the device.
103114
SentryGpu? get gpu => this[SentryGpu.type];
104115

@@ -138,6 +149,13 @@ class Contexts extends MapView<String, dynamic> {
138149
}
139150
break;
140151

152+
case SentryCulture.type:
153+
final cultureMap = culture?.toJson();
154+
if (cultureMap?.isNotEmpty ?? false) {
155+
json[SentryCulture.type] = cultureMap;
156+
}
157+
break;
158+
141159
case SentryGpu.type:
142160
final gpuMap = gpu?.toJson();
143161
if (gpuMap?.isNotEmpty ?? false) {
@@ -191,6 +209,7 @@ class Contexts extends MapView<String, dynamic> {
191209
operatingSystem: operatingSystem?.clone(),
192210
app: app?.clone(),
193211
browser: browser?.clone(),
212+
culture: culture?.clone(),
194213
gpu: gpu?.clone(),
195214
runtimes: runtimes.map((runtime) => runtime.clone()).toList(),
196215
)..addEntries(
@@ -206,6 +225,7 @@ class Contexts extends MapView<String, dynamic> {
206225
List<SentryRuntime>? runtimes,
207226
SentryApp? app,
208227
SentryBrowser? browser,
228+
SentryCulture? culture,
209229
SentryGpu? gpu,
210230
}) =>
211231
Contexts(
@@ -215,6 +235,7 @@ class Contexts extends MapView<String, dynamic> {
215235
app: app ?? this.app,
216236
browser: browser ?? this.browser,
217237
gpu: gpu ?? this.gpu,
238+
culture: culture ?? this.culture,
218239
)..addEntries(
219240
entries.where((element) => !_defaultFields.contains(element.key)),
220241
);
@@ -227,5 +248,6 @@ class Contexts extends MapView<String, dynamic> {
227248
SentryRuntime.type,
228249
SentryGpu.type,
229250
SentryBrowser.type,
251+
SentryCulture.type,
230252
];
231253
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import 'package:meta/meta.dart';
2+
3+
/// Culture Context describes certain properties of the culture in which the
4+
/// software is used.
5+
@immutable
6+
class SentryCulture {
7+
static const type = 'culture';
8+
9+
const SentryCulture({
10+
this.calendar,
11+
this.displayName,
12+
this.locale,
13+
this.is24HourFormat,
14+
this.timezone,
15+
});
16+
17+
factory SentryCulture.fromJson(Map<String, dynamic> data) => SentryCulture(
18+
calendar: data['calendar'],
19+
displayName: data['display_name'],
20+
locale: data['locale'],
21+
is24HourFormat: data['is_24_hour_format'],
22+
timezone: data['timezone'],
23+
);
24+
25+
/// Optional: For example `GregorianCalendar`. Free form string.
26+
final String? calendar;
27+
28+
/// Optional: Human readable name of the culture.
29+
/// For example `English (United States)`
30+
final String? displayName;
31+
32+
/// Optional. The name identifier, usually following the RFC 4646.
33+
/// For example `en-US` or `pt-BR`.
34+
final String? locale;
35+
36+
/// Optional. boolean, either true or false.
37+
final bool? is24HourFormat;
38+
39+
/// Optional. The timezone of the locale. For example, `Europe/Vienna`.
40+
final String? timezone;
41+
42+
/// Produces a [Map] that can be serialized to JSON.
43+
Map<String, dynamic> toJson() {
44+
return <String, dynamic>{
45+
if (calendar != null) 'calendar': calendar!,
46+
if (displayName != null) 'display_name': displayName!,
47+
if (locale != null) 'locale': locale!,
48+
if (is24HourFormat != null) 'is_24_hour_format': is24HourFormat!,
49+
if (timezone != null) 'timezone': timezone!,
50+
};
51+
}
52+
53+
SentryCulture clone() => SentryCulture(
54+
calendar: calendar,
55+
displayName: displayName,
56+
locale: locale,
57+
is24HourFormat: is24HourFormat,
58+
timezone: timezone,
59+
);
60+
61+
SentryCulture copyWith({
62+
String? calendar,
63+
String? displayName,
64+
String? locale,
65+
bool? is24HourFormat,
66+
String? timezone,
67+
}) =>
68+
SentryCulture(
69+
calendar: calendar ?? this.calendar,
70+
displayName: displayName ?? this.displayName,
71+
locale: locale ?? this.locale,
72+
is24HourFormat: is24HourFormat ?? this.is24HourFormat,
73+
timezone: timezone ?? this.timezone,
74+
);
75+
}

dart/test/protocol/contexts_test.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ void main() {
2222
final app = SentryApp(name: 'name1');
2323
final browser = SentryBrowser(name: 'name1');
2424
final gpu = SentryGpu(id: 2);
25+
final culture = SentryCulture(locale: 'foo-bar');
2526

2627
final copy = data.copyWith(
2728
device: device,
@@ -30,6 +31,7 @@ void main() {
3031
app: app,
3132
browser: browser,
3233
gpu: gpu,
34+
culture: culture,
3335
);
3436

3537
expect(device.toJson(), copy.device!.toJson());
@@ -40,6 +42,7 @@ void main() {
4042
);
4143
expect(app.toJson(), copy.app!.toJson());
4244
expect(browser.toJson(), copy.browser!.toJson());
45+
expect(culture.toJson(), copy.culture!.toJson());
4346
expect(gpu.toJson(), copy.gpu!.toJson());
4447
expect('value', copy['extra']);
4548
});
@@ -52,4 +55,5 @@ Contexts _generate() => Contexts(
5255
app: SentryApp(name: 'name'),
5356
browser: SentryBrowser(name: 'name'),
5457
gpu: SentryGpu(id: 1),
58+
culture: SentryCulture(locale: 'foo-bar'),
5559
);

dart/test/protocol/culture_test.dart

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import 'package:collection/collection.dart';
2+
import 'package:sentry/sentry.dart';
3+
import 'package:test/test.dart';
4+
5+
void main() {
6+
group(SentryCulture, () {
7+
test('copyWith keeps unchanged', () {
8+
final data = _generate();
9+
10+
final copy = data.copyWith();
11+
12+
expect(
13+
MapEquality().equals(data.toJson(), copy.toJson()),
14+
true,
15+
);
16+
});
17+
18+
test('copyWith takes new values', () {
19+
final data = _generate();
20+
21+
final copy = data.copyWith(
22+
calendar: 'calendar',
23+
displayName: 'displayName',
24+
is24HourFormat: false, // opposite of the value from _generate
25+
locale: 'locale',
26+
timezone: 'timezone',
27+
);
28+
29+
expect('calendar', copy.calendar);
30+
expect('displayName', copy.displayName);
31+
expect(false, copy.is24HourFormat);
32+
expect('locale', copy.locale);
33+
expect('timezone', copy.timezone);
34+
});
35+
36+
test('toJson', () {
37+
final data = _generate();
38+
39+
expect(data.toJson(), <String, dynamic>{
40+
'calendar': 'FooCalendar',
41+
'display_name': 'FooLanguage',
42+
'is_24_hour_format': true,
43+
'locale': 'fo-ba',
44+
'timezone': 'best-timezone',
45+
});
46+
});
47+
});
48+
}
49+
50+
SentryCulture _generate() => SentryCulture(
51+
calendar: 'FooCalendar',
52+
displayName: 'FooLanguage',
53+
is24HourFormat: true,
54+
locale: 'fo-ba',
55+
timezone: 'best-timezone',
56+
);

0 commit comments

Comments
 (0)