Skip to content

Commit 8ec202f

Browse files
committed
Fix parsing errors
1 parent 1e351bf commit 8ec202f

File tree

2 files changed

+76
-13
lines changed

2 files changed

+76
-13
lines changed

packages/powersync_core/lib/src/exceptions.dart

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@ class SyncResponseException implements Exception {
6161
static SyncResponseException _fromResponseBody(
6262
http.BaseResponse response, String body) {
6363
final decoded = convert.jsonDecode(body);
64-
final details = _stringOrFirst(decoded['error']?['details']) ?? body;
64+
final details = switch (decoded['error']) {
65+
final Map<String, Object?> details => _errorDescription(details),
66+
_ => null,
67+
} ??
68+
body;
69+
6570
final message = '${response.reasonPhrase ?? "Request failed"}: $details';
6671
return SyncResponseException(response.statusCode, message);
6772
}
@@ -73,6 +78,37 @@ class SyncResponseException implements Exception {
7378
);
7479
}
7580

81+
/// Extracts an error description from an error resonse looking like
82+
/// `{"code":"PSYNC_S2106","status":401,"description":"Authentication required","name":"AuthorizationError"}`.
83+
static String? _errorDescription(Map<String, Object?> raw) {
84+
final code = raw['code']; // Required, string
85+
final description = raw['description']; // Required, string
86+
87+
final name = raw['name']; // Optional, string
88+
final details = raw['details']; // Optional, string
89+
90+
if (code is! String || description is! String) {
91+
return null;
92+
}
93+
94+
final fullDescription = StringBuffer(code);
95+
if (name is String) {
96+
fullDescription.write('($name)');
97+
}
98+
99+
fullDescription
100+
..write(': ')
101+
..write(description);
102+
103+
if (details is String) {
104+
fullDescription
105+
..write(' ')
106+
..write(details);
107+
}
108+
109+
return fullDescription.toString();
110+
}
111+
76112
int statusCode;
77113
String description;
78114

@@ -84,18 +120,6 @@ class SyncResponseException implements Exception {
84120
}
85121
}
86122

87-
String? _stringOrFirst(Object? details) {
88-
if (details == null) {
89-
return null;
90-
} else if (details is String) {
91-
return details;
92-
} else if (details case [final String first, ...]) {
93-
return first;
94-
} else {
95-
return null;
96-
}
97-
}
98-
99123
class PowersyncNotReadyException implements Exception {
100124
/// @nodoc
101125
PowersyncNotReadyException(this.message);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import 'dart:convert';
2+
3+
import 'package:http/http.dart';
4+
import 'package:powersync_core/src/exceptions.dart';
5+
import 'package:test/test.dart';
6+
7+
void main() {
8+
group('SyncResponseException', () {
9+
const errorResponse =
10+
'{"error":{"code":"PSYNC_S2106","status":401,"description":"Authentication required","name":"AuthorizationError"}}';
11+
12+
test('fromStreamedResponse', () async {
13+
final exc = await SyncResponseException.fromStreamedResponse(
14+
StreamedResponse(Stream.value(utf8.encode(errorResponse)), 401));
15+
16+
expect(exc.statusCode, 401);
17+
expect(exc.description,
18+
'Request failed: PSYNC_S2106(AuthorizationError): Authentication required');
19+
});
20+
21+
test('fromResponse', () {
22+
final exc =
23+
SyncResponseException.fromResponse(Response(errorResponse, 401));
24+
expect(exc.statusCode, 401);
25+
expect(exc.description,
26+
'Request failed: PSYNC_S2106(AuthorizationError): Authentication required');
27+
});
28+
29+
test('malformed', () {
30+
const malformed =
31+
'{"message":"Route GET:/foo/bar not found","error":"Not Found","statusCode":404}';
32+
33+
final exc = SyncResponseException.fromResponse(Response(malformed, 401));
34+
expect(exc.statusCode, 401);
35+
expect(exc.description,
36+
'Request failed: {"message":"Route GET:/foo/bar not found","error":"Not Found","statusCode":404}');
37+
});
38+
});
39+
}

0 commit comments

Comments
 (0)