Skip to content

Commit 5ee19c1

Browse files
authored
Add fragment to HTTP Client errors and fix user sync (#1102)
1 parent 76410c3 commit 5ee19c1

File tree

6 files changed

+51
-10
lines changed

6 files changed

+51
-10
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## Unreleased
44

5+
### Fixes
6+
7+
- Add missing `fragment` for HTTP Client Errors ([#1102](https://github.com/getsentry/sentry-dart/pull/1102))
8+
- Sync user name and geo for Android ([#1102](https://github.com/getsentry/sentry-dart/pull/1102))
9+
510
### Dependencies
611

712
- Bump Android SDK from v6.6.0 to v6.7.0 ([#1105](https://github.com/getsentry/sentry-dart/pull/1105))

dart/lib/src/http_client/failed_request_client.dart

+7-1
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,14 @@ class FailedRequestClient extends BaseClient {
157157
}) async {
158158
// As far as I can tell there's no way to get the uri without the query part
159159
// so we replace it with an empty string.
160-
final urlWithoutQuery = request.url.replace(query: '').toString();
160+
final urlWithoutQuery = request.url
161+
.replace(query: '', fragment: '')
162+
.toString()
163+
.replaceAll('?', '')
164+
.replaceAll('#', '');
161165

162166
final query = request.url.query.isEmpty ? null : request.url.query;
167+
final fragment = request.url.fragment.isEmpty ? null : request.url.fragment;
163168

164169
final sentryRequest = SentryRequest(
165170
method: request.method,
@@ -172,6 +177,7 @@ class FailedRequestClient extends BaseClient {
172177
'content_length': request.contentLength.toString(),
173178
'duration': requestDuration.toString(),
174179
},
180+
fragment: fragment,
175181
);
176182

177183
final mechanism = Mechanism(

dart/test/http_client/failed_request_client_test.dart

+5-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import '../mocks.dart';
99
import '../mocks/mock_hub.dart';
1010
import '../mocks/mock_transport.dart';
1111

12-
final requestUri = Uri.parse('https://example.com?foo=bar');
12+
final requestUri = Uri.parse('https://example.com?foo=bar#myFragment');
1313

1414
void main() {
1515
group(FailedRequestClient, () {
@@ -53,8 +53,9 @@ void main() {
5353
final request = eventCall.request;
5454
expect(request, isNotNull);
5555
expect(request?.method, 'GET');
56-
expect(request?.url, 'https://example.com?');
56+
expect(request?.url, 'https://example.com');
5757
expect(request?.queryString, 'foo=bar');
58+
expect(request?.fragment, 'myFragment');
5859
expect(request?.cookies, 'foo=bar');
5960
expect(request?.headers, {'Cookie': 'foo=bar'});
6061
// ignore: deprecated_member_use_from_same_package
@@ -112,8 +113,9 @@ void main() {
112113
final request = eventCall.request;
113114
expect(request, isNotNull);
114115
expect(request?.method, 'GET');
115-
expect(request?.url, 'https://example.com?');
116+
expect(request?.url, 'https://example.com');
116117
expect(request?.queryString, 'foo=bar');
118+
expect(request?.fragment, 'myFragment');
117119
expect(request?.cookies, 'foo=bar');
118120
expect(request?.headers, {'Cookie': 'foo=bar'});
119121
// ignore: deprecated_member_use_from_same_package

dio/lib/src/dio_event_processor.dart

+10-1
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,18 @@ class DioEventProcessor implements EventProcessor {
119119
final options = dioError.requestOptions;
120120
// As far as I can tell there's no way to get the uri without the query part
121121
// so we replace it with an empty string.
122-
final urlWithoutQuery = options.uri.replace(query: '').toString();
122+
final urlWithoutQuery = options.uri
123+
.replace(query: '', fragment: '')
124+
.toString()
125+
.replaceAll('?', '')
126+
.replaceAll('#', '');
123127

124128
final query = options.uri.query.isEmpty ? null : options.uri.query;
125129

130+
// future proof, Dio does not support it yet and even if passing in the path,
131+
// the parsing of the uri returns empty.
132+
final fragment = options.uri.fragment.isEmpty ? null : options.uri.fragment;
133+
126134
final headers = options.headers
127135
.map((key, dynamic value) => MapEntry(key, value?.toString() ?? ''));
128136

@@ -132,6 +140,7 @@ class DioEventProcessor implements EventProcessor {
132140
url: urlWithoutQuery,
133141
queryString: query,
134142
data: _getRequestData(dioError.requestOptions.data),
143+
fragment: fragment,
135144
);
136145
}
137146

flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt

+20-5
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
262262
}
263263
}
264264

265+
@Suppress("ComplexMethod")
265266
private fun setUser(user: Map<String, Any?>?, result: Result) {
266267
if (user == null) {
267268
Sentry.setUser(null)
@@ -270,30 +271,44 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
270271
}
271272

272273
val userInstance = User()
274+
val userData = mutableMapOf<String, String>()
275+
val unknown = mutableMapOf<String, Any>()
273276

274277
(user["email"] as? String)?.let { userInstance.email = it }
275278
(user["id"] as? String)?.let { userInstance.id = it }
276279
(user["username"] as? String)?.let { userInstance.username = it }
277280
(user["ip_address"] as? String)?.let { userInstance.ipAddress = it }
278281
(user["segment"] as? String)?.let { userInstance.segment = it }
282+
283+
// Not mapped on Android yet, added to the unknown databag that gets serialized correctly anyway
284+
// Should be solved by https://github.com/getsentry/team-mobile/issues/59
285+
// or https://github.com/getsentry/team-mobile/issues/56
286+
(user["name"] as? String)?.let { unknown["name"] = it }
287+
(user["geo"] as? Map<String, Any?>)?.let { unknown["geo"] = it }
288+
279289
(user["extras"] as? Map<String, Any?>)?.let { extras ->
280-
val others = mutableMapOf<String, String>()
281290
for ((key, value) in extras.entries) {
282291
if (value != null) {
283-
others[key] = value.toString()
292+
userData[key] = value.toString()
284293
}
285294
}
286-
userInstance.others = others
287295
}
288296
(user["data"] as? Map<String, Any?>)?.let { data ->
289-
val others = mutableMapOf<String, String>()
290297
for ((key, value) in data.entries) {
291298
if (value != null) {
292-
others[key] = value.toString()
299+
// data has precedence over extras
300+
userData[key] = value.toString()
293301
}
294302
}
295303
}
296304

305+
if (userData.isNotEmpty()) {
306+
userInstance.data = userData
307+
}
308+
if (unknown.isNotEmpty()) {
309+
userInstance.unknown = unknown
310+
}
311+
297312
Sentry.setUser(userInstance)
298313

299314
result.success("")

flutter/ios/Classes/SentryFlutterPluginApple.swift

+4
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,10 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin {
527527
}
528528
}
529529

530+
// missing name and geo
531+
// Should be solved by https://github.com/getsentry/team-mobile/issues/59
532+
// or https://github.com/getsentry/team-mobile/issues/56
533+
530534
SentrySDK.setUser(userInstance)
531535
} else {
532536
SentrySDK.setUser(nil)

0 commit comments

Comments
 (0)