diff --git a/lib/api/core.dart b/lib/api/core.dart index 8b18349d4f..814f0eac93 100644 --- a/lib/api/core.dart +++ b/lib/api/core.dart @@ -49,14 +49,16 @@ class ApiConnection { bool _isOpen = true; - Future send(http.BaseRequest request) async { + Future> send(http.BaseRequest request) async { assert(_isOpen); addAuth(request); final response = await _client.send(request); if (response.statusCode != 200) { throw Exception("error on ${request.method} ${request.url.path}: status ${response.statusCode}"); } - return utf8.decode(await response.stream.toBytes()); + final bytes = await response.stream.toBytes(); + return jsonDecode(utf8.decode(bytes)); + // TODO(#37): inspect response to throw structured errors } void close() { @@ -65,7 +67,7 @@ class ApiConnection { _isOpen = false; } - Future get(String route, Map? params) async { + Future> get(String route, Map? params) async { final url = realmUrl.replace( path: "/api/v1/$route", queryParameters: encodeParameters(params)); assert(debugLog("GET $url")); @@ -73,7 +75,7 @@ class ApiConnection { return send(request); } - Future post(String route, Map? params) async { + Future> post(String route, Map? params) async { final url = realmUrl.replace(path: "/api/v1/$route"); final request = http.Request('POST', url); if (params != null) { @@ -82,7 +84,7 @@ class ApiConnection { return send(request); } - Future postFileFromStream(String route, Stream> content, int length, { String? filename }) async { + Future> postFileFromStream(String route, Stream> content, int length, { String? filename }) async { http.MultipartRequest request = http.MultipartRequest('POST', Uri.parse("$realmUrl/api/v1/$route")) ..files.add(http.MultipartFile('file', content, length, filename: filename)); return send(request); diff --git a/lib/api/route/account.dart b/lib/api/route/account.dart index d730b198bf..ff536887e2 100644 --- a/lib/api/route/account.dart +++ b/lib/api/route/account.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:json_annotation/json_annotation.dart'; import '../core.dart'; @@ -12,7 +10,7 @@ Future fetchApiKey({ required String username, required String password, }) async { - final String data; + final Map data; // TODO make this function testable by taking ApiConnection from caller final connection = ApiConnection.live(realmUrl: realmUrl); try { @@ -24,8 +22,7 @@ Future fetchApiKey({ connection.close(); } - final json = jsonDecode(data); - return FetchApiKeyResult.fromJson(json); + return FetchApiKeyResult.fromJson(data); } @JsonSerializable(fieldRename: FieldRename.snake) diff --git a/lib/api/route/events.dart b/lib/api/route/events.dart index af961d92ac..556738a599 100644 --- a/lib/api/route/events.dart +++ b/lib/api/route/events.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:json_annotation/json_annotation.dart'; import '../core.dart'; @@ -21,8 +19,7 @@ Future registerQueue(ApiConnection connection) async { 'user_settings_object': true, }, }); - final json = jsonDecode(data); - return InitialSnapshot.fromJson(json); + return InitialSnapshot.fromJson(data); } /// https://zulip.com/api/get-events @@ -34,7 +31,7 @@ Future getEvents(ApiConnection connection, { if (lastEventId != null) 'last_event_id': lastEventId, if (dontBlock != null) 'dont_block': dontBlock, }); - return GetEventsResult.fromJson(jsonDecode(data)); + return GetEventsResult.fromJson(data); } @JsonSerializable(fieldRename: FieldRename.snake) diff --git a/lib/api/route/messages.dart b/lib/api/route/messages.dart index 8411a04c51..1ea639b35f 100644 --- a/lib/api/route/messages.dart +++ b/lib/api/route/messages.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:json_annotation/json_annotation.dart'; import '../core.dart'; @@ -18,7 +16,7 @@ Future getMessages(ApiConnection connection, { 'num_before': numBefore, 'num_after': numAfter, }); - return GetMessagesResult.fromJson(jsonDecode(data)); + return GetMessagesResult.fromJson(data); } @JsonSerializable(fieldRename: FieldRename.snake) @@ -79,7 +77,7 @@ Future sendMessage( 'topic': RawParameter(topic), 'content': RawParameter(content), }); - return SendMessageResult.fromJson(jsonDecode(data)); + return SendMessageResult.fromJson(data); } @JsonSerializable(fieldRename: FieldRename.snake) @@ -106,7 +104,7 @@ Future uploadFile( required String filename, }) async { final data = await connection.postFileFromStream('user_uploads', content, length, filename: filename); - return UploadFileResult.fromJson(jsonDecode(data)); + return UploadFileResult.fromJson(data); } @JsonSerializable(fieldRename: FieldRename.snake) diff --git a/lib/api/route/realm.dart b/lib/api/route/realm.dart index 191ab00643..1f366f5cc5 100644 --- a/lib/api/route/realm.dart +++ b/lib/api/route/realm.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:json_annotation/json_annotation.dart'; import '../core.dart'; @@ -20,7 +18,7 @@ part 'realm.g.dart'; Future getServerSettings({ required Uri realmUrl, }) async { - final String data; + final Map data; // TODO make this function testable by taking ApiConnection from caller final connection = ApiConnection.live(realmUrl: realmUrl); try { @@ -29,8 +27,7 @@ Future getServerSettings({ connection.close(); } - final json = jsonDecode(data); - return GetServerSettingsResult.fromJson(json); + return GetServerSettingsResult.fromJson(data); } @JsonSerializable(fieldRename: FieldRename.snake) diff --git a/lib/api/route/users.dart b/lib/api/route/users.dart index a5a94d93ee..bb560c5418 100644 --- a/lib/api/route/users.dart +++ b/lib/api/route/users.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:json_annotation/json_annotation.dart'; import '../core.dart'; @@ -12,7 +10,7 @@ part 'users.g.dart'; /// as a workaround on old servers. Future getOwnUser(ApiConnection connection) async { final data = await connection.get('users/me', {}); - return GetOwnUserResult.fromJson(jsonDecode(data)); + return GetOwnUserResult.fromJson(data); } @JsonSerializable(fieldRename: FieldRename.snake)