Skip to content

Commit 6e01d28

Browse files
authored
Move ParseHTTPClient to Dio (#459)
* Merged manually from branch dio * fixing nullpointer * Fix batch operations Maybe dio changes the toString behaviour. * use mine * keep content-type * fix file upload issue * fixed file uppload on web * performance improvement in parse_file_web * fix for issue #456 (#457) Added @BaranMichal25 fix for #456. #456 (comment) * fix flutter test * fix crash when debug=true * fix dart test * add ProgressCallback in README * added progressCallback example
1 parent 74c4845 commit 6e01d28

29 files changed

+296
-152
lines changed

packages/dart/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ These classes are used by default to represent files, but you can also build you
782782

783783
Have a look at the example application for a small (non web) example.
784784

785-
785+
When uploading or downloading a file, you can use the `progressCallback`-parameter to track the progress of the http request.
786786
```dart
787787
//A short example for showing an image from a ParseFileBase
788788
Widget buildImage(ParseFileBase image){
@@ -820,7 +820,10 @@ someParseObject.set("image", parseFile);
820820
//This saves the ParseObject as well as all of its children, and the ParseFileBase is such a child.
821821
await someParseObject.save();
822822
```
823-
823+
```dart
824+
//progressCallback example
825+
file.upload(progressCallback: (int count, int total) => print("$count of $total"));
826+
```
824827
## Other Features of this library
825828
Main:
826829
* Installation (View the example application)

packages/dart/lib/parse_server_sdk.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import 'dart:io';
66
import 'dart:math';
77
import 'dart:typed_data';
88

9-
import 'package:http/http.dart';
10-
import 'package:http/io_client.dart';
9+
import 'package:dio/dio.dart' hide Options;
10+
import 'package:dio/dio.dart' as dio show Options;
1111
import 'package:meta/meta.dart';
12+
import 'package:mime_type/mime_type.dart';
13+
import 'package:parse_server_sdk/src/network/http_client_adapter.dart';
1214
import 'package:parse_server_sdk/src/network/parse_websocket.dart'
1315
as parse_web_socket;
1416
import 'package:path/path.dart' as path;
@@ -23,6 +25,7 @@ part 'src/base/parse_constants.dart';
2325
part 'src/data/parse_core_data.dart';
2426
part 'src/data/parse_subclass_handler.dart';
2527
part 'src/enums/parse_enum_api_rq.dart';
28+
part 'src/network/dio-options.dart';
2629
part 'src/network/parse_connectivity.dart';
2730
part 'src/network/parse_http_client.dart';
2831
part 'src/network/parse_live_query.dart';
@@ -150,8 +153,8 @@ class Parse {
150153
const ParseApiRQ type = ParseApiRQ.healthCheck;
151154

152155
try {
153-
final Response response =
154-
await _client.get('${ParseCoreData().serverUrl}$keyEndPointHealth');
156+
final Response<String> response = await _client
157+
.get<String>('${ParseCoreData().serverUrl}$keyEndPointHealth');
155158
parseResponse =
156159
handleResponse<Parse>(null, response, type, _debug, className);
157160
} on Exception catch (e) {

packages/dart/lib/src/base/parse_constants.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const String keyHeaderSessionToken = 'X-Parse-Session-Token';
4646
const String keyHeaderRevocableSession = 'X-Parse-Revocable-Session';
4747
const String keyHeaderUserAgent = 'user-agent';
4848
const String keyHeaderApplicationId = 'X-Parse-Application-Id';
49-
const String keyHeaderContentType = 'Content-Type';
49+
const String keyHeaderContentType = Headers.contentTypeHeader;
5050
const String keyHeaderContentTypeJson = 'application/json';
5151
const String keyHeaderMasterKey = 'X-Parse-Master-Key';
5252
const String keyHeaderClientKey = 'X-Parse-Client-Key';
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
part of flutter_parse_sdk;
2+
3+
class Options extends dio.Options {
4+
Options({
5+
String method,
6+
int sendTimeout,
7+
int receiveTimeout,
8+
Map<String, dynamic> extra,
9+
Map<String, dynamic> headers,
10+
ResponseType responseType,
11+
String contentType,
12+
ValidateStatus validateStatus,
13+
bool receiveDataWhenStatusError,
14+
bool followRedirects,
15+
int maxRedirects,
16+
RequestEncoder requestEncoder,
17+
ResponseDecoder responseDecoder,
18+
}) : super(
19+
method: method,
20+
sendTimeout: sendTimeout,
21+
receiveTimeout: receiveTimeout,
22+
extra: extra,
23+
headers: headers,
24+
responseType: responseType,
25+
contentType: contentType ??
26+
(headers ?? <String, dynamic>{})[Headers.contentTypeHeader],
27+
validateStatus: validateStatus,
28+
receiveDataWhenStatusError: receiveDataWhenStatusError,
29+
followRedirects: followRedirects,
30+
maxRedirects: maxRedirects,
31+
requestEncoder: requestEncoder,
32+
responseDecoder: responseDecoder,
33+
);
34+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export 'http_client_adapter_native.dart'
2+
if (dart.library.js) 'http_client_adapter_web.dart';
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import 'dart:io';
2+
3+
import 'package:dio/adapter.dart';
4+
import 'package:dio/dio.dart';
5+
6+
HttpClientAdapter createHttpClientAdapter(SecurityContext securityContext) {
7+
final DefaultHttpClientAdapter defaultHttpClientAdapter =
8+
DefaultHttpClientAdapter();
9+
10+
if (securityContext != null)
11+
defaultHttpClientAdapter.onHttpClientCreate =
12+
(HttpClient client) => HttpClient(context: securityContext);
13+
return defaultHttpClientAdapter;
14+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import 'dart:io';
2+
3+
import 'package:dio/adapter_browser.dart';
4+
import 'package:dio/dio.dart';
5+
6+
HttpClientAdapter createHttpClientAdapter(SecurityContext securityContext) {
7+
final BrowserHttpClientAdapter browserHttpClientAdapter =
8+
BrowserHttpClientAdapter();
9+
return browserHttpClientAdapter;
10+
}
Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,62 @@
11
part of flutter_parse_sdk;
22

33
/// Creates a custom version of HTTP Client that has Parse Data Preset
4-
class ParseHTTPClient extends BaseClient {
4+
class ParseHTTPClient with DioMixin implements Dio {
55
ParseHTTPClient({bool sendSessionId = false, SecurityContext securityContext})
6-
: _sendSessionId = sendSessionId,
7-
_client = securityContext != null
8-
? IOClient(HttpClient(context: securityContext))
9-
: Client();
6+
: _sendSessionId = sendSessionId {
7+
options = BaseOptions();
8+
httpClientAdapter = createHttpClientAdapter(securityContext);
9+
}
1010

11-
final Client _client;
1211
final bool _sendSessionId;
1312
final String _userAgent = '$keyLibraryName $keySdkVersion';
1413
ParseCoreData data = ParseCoreData();
1514
Map<String, String> additionalHeaders;
1615

1716
/// Overrides the call method for HTTP Client and adds custom headers
1817
@override
19-
Future<StreamedResponse> send(BaseRequest request) {
18+
Future<Response<T>> request<T>(
19+
String path, {
20+
dynamic data,
21+
Map<String, dynamic> queryParameters,
22+
CancelToken cancelToken,
23+
dio.Options options,
24+
ProgressCallback onSendProgress,
25+
ProgressCallback onReceiveProgress,
26+
}) {
27+
options ??= Options();
2028
if (!identical(0, 0.0)) {
21-
request.headers[keyHeaderUserAgent] = _userAgent;
29+
options.headers[keyHeaderUserAgent] = _userAgent;
2230
}
23-
request.headers[keyHeaderApplicationId] = data.applicationId;
31+
options.headers[keyHeaderApplicationId] = this.data.applicationId;
2432
if ((_sendSessionId == true) &&
25-
(data.sessionId != null) &&
26-
(request.headers[keyHeaderSessionToken] == null))
27-
request.headers[keyHeaderSessionToken] = data.sessionId;
33+
(this.data.sessionId != null) &&
34+
(options.headers[keyHeaderSessionToken] == null))
35+
options.headers[keyHeaderSessionToken] = this.data.sessionId;
2836

29-
if (data.clientKey != null)
30-
request.headers[keyHeaderClientKey] = data.clientKey;
31-
if (data.masterKey != null)
32-
request.headers[keyHeaderMasterKey] = data.masterKey;
37+
if (this.data.clientKey != null)
38+
options.headers[keyHeaderClientKey] = this.data.clientKey;
39+
if (this.data.masterKey != null)
40+
options.headers[keyHeaderMasterKey] = this.data.masterKey;
3341

3442
/// If developer wants to add custom headers, extend this class and add headers needed.
3543
if (additionalHeaders != null && additionalHeaders.isNotEmpty) {
3644
additionalHeaders
37-
.forEach((String key, String value) => request.headers[key] = value);
45+
.forEach((String key, String value) => options.headers[key] = value);
3846
}
3947

40-
if (data.debug) {
41-
logCUrl(request);
48+
if (this.data.debug) {
49+
logCUrl(options, data, path);
4250
}
4351

44-
return _client.send(request);
52+
return super.request(
53+
path,
54+
data: data,
55+
queryParameters: queryParameters,
56+
cancelToken: cancelToken,
57+
options: options,
58+
onSendProgress: onSendProgress,
59+
onReceiveProgress: onReceiveProgress,
60+
);
4561
}
4662
}

packages/dart/lib/src/network/parse_query.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,12 @@ class QueryBuilder<T extends ParseObject> {
343343
/// Finishes the query and calls the server
344344
///
345345
/// Make sure to call this after defining your queries
346-
Future<ParseResponse> query<T extends ParseObject>() async {
347-
return object.query<T>(buildQuery());
346+
Future<ParseResponse> query<T extends ParseObject>(
347+
{ProgressCallback progressCallback}) async {
348+
return object.query<T>(
349+
buildQuery(),
350+
progressCallback: progressCallback,
351+
);
348352
}
349353

350354
Future<ParseResponse> distinct<T extends ParseObject>(

packages/dart/lib/src/objects/parse_config.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class ParseConfig extends ParseObject {
1616
Future<ParseResponse> getConfigs() async {
1717
try {
1818
final String uri = '${ParseCoreData().serverUrl}/config';
19-
final Response result = await _client.get(uri);
19+
final Response<String> result = await _client.get<String>(uri);
2020
return handleResponse<ParseConfig>(
2121
this, result, ParseApiRQ.getConfigs, _debug, parseClassName);
2222
} on Exception catch (e) {
@@ -29,7 +29,7 @@ class ParseConfig extends ParseObject {
2929
try {
3030
final String uri = '${ParseCoreData().serverUrl}/config';
3131
final String body = '{\"params\":{\"$key\": \"${parseEncode(value)}\"}}';
32-
final Response result = await _client.put(uri, body: body);
32+
final Response<String> result = await _client.put<String>(uri, data: body);
3333
return handleResponse<ParseConfig>(
3434
this, result, ParseApiRQ.addConfig, _debug, parseClassName);
3535
} on Exception catch (e) {

packages/dart/lib/src/objects/parse_file.dart

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,26 @@ class ParseFile extends ParseFileBase {
4040
}
4141

4242
@override
43-
Future<ParseFile> download() async {
43+
Future<ParseFile> download({ProgressCallback progressCallback}) async {
4444
if (url == null) {
4545
return this;
4646
}
4747

4848
file = File('${ParseCoreData().fileDirectory}/$name');
4949
await file.create();
50-
final Response response = await _client.get(url);
51-
await file.writeAsBytes(response.bodyBytes);
50+
final Response<List<int>> response = await _client.get<List<int>>(
51+
url,
52+
options: Options(responseType: ResponseType.bytes),
53+
onReceiveProgress: progressCallback,
54+
);
55+
await file.writeAsBytes(response.data);
5256

5357
return this;
5458
}
5559

5660
/// Uploads a file to Parse Server
5761
@override
58-
Future<ParseResponse> upload() async {
62+
Future<ParseResponse> upload({ProgressCallback progressCallback}) async {
5963
if (saved) {
6064
//Creates a Fake Response to return the correct result
6165
final Map<String, String> response = <String, String>{
@@ -64,23 +68,25 @@ class ParseFile extends ParseFileBase {
6468
};
6569
return handleResponse<ParseFile>(
6670
this,
67-
Response(json.encode(response), 201),
71+
Response<String>(data: json.encode(response), statusCode: 201),
6872
ParseApiRQ.upload,
6973
_debug,
7074
parseClassName);
7175
}
7276

73-
final String ext = path.extension(file.path).replaceAll('.', '');
7477
final Map<String, String> headers = <String, String>{
75-
HttpHeaders.contentTypeHeader: getContentType(ext)
78+
HttpHeaders.contentTypeHeader: mime(file.path) ?? 'application/octet-stream',
7679
};
7780
try {
7881
final String uri = _client.data.serverUrl + '$_path';
79-
final List<int> body = await file.readAsBytes();
80-
final Response response =
81-
await _client.post(uri, headers: headers, body: body);
82+
final Response<String> response = await _client.post<String>(
83+
uri,
84+
options: Options(headers: headers),
85+
data: file.openRead(),
86+
onSendProgress: progressCallback,
87+
);
8288
if (response.statusCode == 201) {
83-
final Map<String, dynamic> map = json.decode(response.body);
89+
final Map<String, dynamic> map = json.decode(response.data);
8490
url = map['url'].toString();
8591
name = map['name'].toString();
8692
}

packages/dart/lib/src/objects/parse_file_base.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ abstract class ParseFileBase extends ParseObject {
4141
}
4242

4343
/// Uploads a file to Parse Server
44-
Future<ParseResponse> upload();
44+
Future<ParseResponse> upload({ProgressCallback progressCallback});
4545

46-
Future<ParseFileBase> download();
46+
Future<ParseFileBase> download({ProgressCallback progressCallback});
4747
}

packages/dart/lib/src/objects/parse_file_web.dart

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,23 @@ class ParseWebFile extends ParseFileBase {
1818
Uint8List file;
1919

2020
@override
21-
Future<ParseWebFile> download() async {
21+
Future<ParseWebFile> download({ProgressCallback progressCallback}) async {
2222
if (url == null) {
2323
return this;
2424
}
2525

26-
final Response response = await _client.get(url);
27-
file = response.bodyBytes;
26+
final Response<List<int>> response = await _client.get<List<int>>(
27+
url,
28+
options: Options(responseType: ResponseType.bytes),
29+
onReceiveProgress: progressCallback,
30+
);
31+
file = response.data;
2832

2933
return this;
3034
}
3135

3236
@override
33-
Future<ParseResponse> upload() async {
37+
Future<ParseResponse> upload({ProgressCallback progressCallback}) async {
3438
if (saved) {
3539
//Creates a Fake Response to return the correct result
3640
final Map<String, String> response = <String, String>{
@@ -39,23 +43,26 @@ class ParseWebFile extends ParseFileBase {
3943
};
4044
return handleResponse<ParseWebFile>(
4145
this,
42-
Response(json.encode(response), 201),
46+
Response<String>(data: json.encode(response), statusCode: 201),
4347
ParseApiRQ.upload,
4448
_debug,
4549
parseClassName);
4650
}
4751

4852
final Map<String, String> headers = <String, String>{
49-
HttpHeaders.contentTypeHeader: url ?? name != null
50-
? getContentType(path.extension(url ?? name))
51-
: 'text/plain'
53+
HttpHeaders.contentTypeHeader:
54+
mime(url ?? name) ?? 'application/octet-stream',
5255
};
5356
try {
5457
final String uri = _client.data.serverUrl + '$_path';
55-
final Response response =
56-
await _client.post(uri, headers: headers, body: file);
58+
final Response<String> response = await _client.post<String>(
59+
uri,
60+
options: Options(headers: headers),
61+
data: Stream<List<int>>.fromIterable(<List<int>>[file]),
62+
onSendProgress: progressCallback,
63+
);
5764
if (response.statusCode == 201) {
58-
final Map<String, dynamic> map = json.decode(response.body);
65+
final Map<String, dynamic> map = json.decode(response.data);
5966
url = map['url'].toString();
6067
name = map['name'].toString();
6168
}

0 commit comments

Comments
 (0)