Skip to content

Commit

Permalink
update to 3.20.0
Browse files Browse the repository at this point in the history
  • Loading branch information
jonataslaw committed Nov 28, 2020
1 parent ecdd4e5 commit a910c7f
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 30 deletions.
25 changes: 21 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
## [3.20.0]
- Added GetConnect.
-

## [3.20.0] - Big update
* Added GetConnect.
- GetConnect is an easy way to communicate from your back to your front. With it you can:
- Communicate through websockets
- Send messages and events via websockets.
- Listen to messages and events via websockets.
- Make http requests (GET, PUT, POST, DELETE).
- Add request modifiers (like attaching a token to each request made).
- Add answer modifiers (how to change a value field whenever the answer arrives)
- Add an authenticator, if the answer is 401, you can configure the renewal of your JWT, for example, and then it will again make the http request.
- Set the number of attempts for the authenticator
- Define a baseUrl for all requests
- Define a standard encoder for your Model.
- Note1: You will never need to use jsonEncoder. It will always be called automatically with each request. If you define an encoder for your model, it will return the instance of your model class ALREADY FILLED with server data.
- Note2: all requests are safety, you do not need to insert try / catch in requests. It will always return a response. In case of an error code, Response.hasError will return true. The error code will always be returned, unless the error was a connection error, which will be returned Response.hasError, but with error code null.
- These are relatively new features, and also inserted in separate containers. You don't have to use it if you don't want to. As it is relatively new, some functions, such as specific http methods, may be missing.
* Translation to Korean (@rws08)
* Fix Overlays state (@eduardoflorence)
* Update chinese docs (@jonahzheng)
* Added context.isDarkMode to context extensions


## [3.17.1]
- Allow list.assignAll, map.assignAll and set.assignAll operate with null values
Expand Down
84 changes: 82 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)

_Languages: English (this file), [Chinese](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README.ru.md), [Polish](README.pl.md), [Korean](README.ko-kr.md)._
**Languages: English (this file), [Chinese](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README.ru.md), [Polish](README.pl.md), [Korean](README.ko-kr.md).**

[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
[![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
Expand Down Expand Up @@ -35,6 +35,9 @@ _Languages: English (this file), [Chinese](README.zh-cn.md), [Brazilian Portugue
- [Change locale](#change-locale)
- [System locale](#system-locale)
- [Change Theme](#change-theme)
- [GetConnect](#getconnect)
- [Default configuration](#default-configuration)
- [Custom configuration](#custom-configuration)
- [Other Advanced APIs](#other-advanced-apis)
- [Optional Global Settings and Manual configurations](#optional-global-settings-and-manual-configurations)
- [Local State Widgets](#local-state-widgets)
Expand Down Expand Up @@ -380,6 +383,83 @@ Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());

When `.darkmode` is activated, it will switch to the _light theme_, and when the _light theme_ becomes active, it will change to _dark theme_.

## GetConnect
GetConnect is an easy way to communicate from your back to your front with http or websockets

### Default configuration
You can simply extend GetConnect and use the GET/POST/PUT/DELETE/SOCKET methods to communicate with your Rest API or websockets.

```dart
class UserProvider extends GetConnect {
// Get request
Future<Response> getUser(int id) => get('http://youapi/users/$id');
// Post request
Future<Response> postUser(Map data) => post('http://youapi/users', body: data);
// Post request with File
Future<Response<CasesModel>> postCases(List<int> image) {
final form = FormData({
'file': MultipartFile(image, filename: 'avatar.png'),
'otherFile': MultipartFile(image, filename: 'cover.png'),
});
return post('http://youapi/users/upload', form);
}
GetSocket userMessages() {
return socket('https://yourapi/users/socket');
}
}
```
### Custom configuration
GetConnect is highly customizable You can define base Url, as answer modifiers, as Requests modifiers, define an authenticator, and even the number of attempts in which it will try to authenticate itself, in addition to giving the possibility to define a standard decoder that will transform all your requests into your Models without any additional configuration.

```dart
class HomeProvider extends GetConnect {
@override
void onInit() {
@override
void onInit() {
// All request will pass to jsonEncode so CasesModel.fromJson()
httpClient.defaultDecoder = CasesModel.fromJson;
httpClient.baseUrl = 'https://api.covid19api.com';
// baseUrl = 'https://api.covid19api.com'; // It define baseUrl to
// Http and websockets if used with no [httpClient] instance
// It's will attach 'apikey' property on header from all requests
httpClient.addRequestModifier((request) {
request.headers['apikey'] = '12345678';
return request;
});
// Even if the server sends data from the country "Brazil",
// it will never be displayed to users, because you remove
// that data from the response, even before the response is delivered
httpClient.addResponseModifier<CasesModel>((request, response) {
CasesModel model = response.body;
if (model.countries.contains('Brazil')) {
model.countries.remove('Brazilll');
}
});
httpClient.addAuthenticator((request) async {
final response = await get("http://yourapi/token");
final token = response.body['token'];
// Set the header
request.headers['Authorization'] = "$token";
return request;
});
//Autenticator will be called 3 times if HttpStatus is
//HttpStatus.unauthorized
httpClient.maxAuthRetries = 3;
}
}
@override
Future<Response<CasesModel>> getCases(String path) => get(path);
}
```


## Other Advanced APIs

```dart
Expand Down Expand Up @@ -734,7 +814,7 @@ Is a `const Stateless` Widget that has a getter `controller` for a registered `C
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
child: Text( controller.title ), // just call `controller.something`
child: Text(controller.title), // just call `controller.something`
);
}
}
Expand Down
76 changes: 66 additions & 10 deletions lib/get_connect/http/src/http.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'dart:async';
import 'dart:convert';

import 'package:meta/meta.dart';
import 'package:flutter/foundation.dart';

import '../src/certificates/certificates.dart';
import '../src/exceptions/exceptions.dart';
Expand Down Expand Up @@ -31,9 +31,11 @@ class GetHttpClient {

Duration timeout;

bool errorSafety = true;

final HttpRequestBase _httpClient;

final GetModifier _interceptor;
final GetModifier _modifier;

GetHttpClient({
this.userAgent = 'getx-client',
Expand All @@ -48,7 +50,27 @@ class GetHttpClient {
allowAutoSignedCert: allowAutoSignedCert,
trustedCertificates: trustedCertificates,
),
_interceptor = GetModifier();
_modifier = GetModifier();

void addAuthenticator<T>(RequestModifier<T> auth) {
_modifier.authenticator = auth as RequestModifier;
}

void addRequestModifier<T>(RequestModifier<T> interceptor) {
_modifier.addRequestModifier<T>(interceptor);
}

void removeRequestModifier<T>(RequestModifier<T> interceptor) {
_modifier.removeRequestModifier(interceptor);
}

void addResponseModifier<T>(ResponseModifier<T> interceptor) {
_modifier.addResponseModifier(interceptor);
}

void removeResponseModifier<T>(ResponseModifier<T> interceptor) {
_modifier.removeResponseModifier<T>(interceptor);
}

Uri _createUri(String url, Map<String, dynamic> query) {
if (baseUrl != null) {
Expand Down Expand Up @@ -91,7 +113,9 @@ class GetHttpClient {

bodyBytes = utf8.encode(jsonString);
} on Exception catch (err) {
throw UnexpectedFormat(err.toString());
if (!errorSafety) {
throw UnexpectedFormat(err.toString());
} else {}
}
}

Expand Down Expand Up @@ -155,15 +179,15 @@ class GetHttpClient {
request.headers[key] = value;
});

if (authenticate) await _interceptor.authenticator(request);
await _interceptor.modifyRequest(request);
if (authenticate) await _modifier.authenticator(request);
await _modifier.modifyRequest(request);

var response = await _httpClient.send<T>(request);

await _interceptor.modifyResponse(request, response);
await _modifier.modifyResponse(request, response);

if (HttpStatus.unauthorized == response.statusCode &&
_interceptor.authenticator != null &&
_modifier.authenticator != null &&
requestNumber <= maxAuthRetries) {
return _performRequest(
handler,
Expand All @@ -172,12 +196,32 @@ class GetHttpClient {
headers: request.headers,
);
} else if (HttpStatus.unauthorized == response.statusCode) {
throw UnauthorizedException();
if (!errorSafety) {
throw UnauthorizedException();
} else {
return Response<T>(
request: request,
headers: response.headers,
statusCode: response.statusCode,
body: response.body,
statusText: response.statusText,
);
}
}

return response;
} on Exception catch (err) {
throw GetHttpException(err.toString());
if (!errorSafety) {
throw GetHttpException(err.toString());
} else {
return Response<T>(
request: null,
headers: null,
statusCode: null,
body: null,
statusText: "$err",
);
}
}
}

Expand Down Expand Up @@ -267,6 +311,9 @@ class GetHttpClient {
);
return response;
} on Exception catch (e) {
if (!errorSafety) {
throw GetHttpException(e.toString());
}
return Future.value(Response<T>(
request: null,
statusCode: null,
Expand Down Expand Up @@ -297,6 +344,9 @@ class GetHttpClient {
);
return response;
} on Exception catch (e) {
if (!errorSafety) {
throw GetHttpException(e.toString());
}
return Future.value(Response<T>(
request: null,
statusCode: null,
Expand All @@ -320,6 +370,9 @@ class GetHttpClient {
);
return response;
} on Exception catch (e) {
if (!errorSafety) {
throw GetHttpException(e.toString());
}
return Future.value(Response<T>(
request: null,
statusCode: null,
Expand All @@ -343,6 +396,9 @@ class GetHttpClient {
);
return response;
} on Exception catch (e) {
if (!errorSafety) {
throw GetHttpException(e.toString());
}
return Future.value(Response<T>(
request: null,
statusCode: null,
Expand Down
22 changes: 12 additions & 10 deletions lib/get_connect/http/src/interceptors/get_modifiers.dart
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
import 'dart:async';

import '../request/request.dart';
import '../response/response.dart';

typedef RequestModifier = Future<Request> Function(Request request);
typedef RequestModifier<T> = FutureOr<Request<T>> Function(Request<T> request);

typedef ResponseModifier = Future<Null> Function(
Request request, Response response);
typedef ResponseModifier<T> = FutureOr Function(
Request<T> request, Response<T> response);

typedef HandlerExecute<T> = Future<Request<T>> Function();

class GetModifier {
class GetModifier<T> {
final _requestModifiers = <RequestModifier>[];
final _responseModifiers = <ResponseModifier>[];
RequestModifier authenticator;

void addRequestModifier(RequestModifier interceptor) {
_requestModifiers.add(interceptor);
void addRequestModifier<T>(RequestModifier<T> interceptor) {
_requestModifiers.add(interceptor as RequestModifier);
}

void removeRequestModifier(RequestModifier interceptor) {
void removeRequestModifier<T>(RequestModifier<T> interceptor) {
_requestModifiers.remove(interceptor);
}

void addResponseModifier(ResponseModifier interceptor) {
_responseModifiers.add(interceptor);
void addResponseModifier<T>(ResponseModifier<T> interceptor) {
_responseModifiers.add(interceptor as ResponseModifier);
}

void removeResponseModifier(ResponseModifier interceptor) {
void removeResponseModifier<T>(ResponseModifier<T> interceptor) {
_requestModifiers.remove(interceptor);
}

Expand Down
4 changes: 3 additions & 1 deletion lib/get_connect/http/src/multipart/multipart_file.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'package:flutter/foundation.dart';

import '../request/request.dart';

class MultipartFile {
MultipartFile(
List<int> bytes, {
this.filename,
@required this.filename,
this.contentType = 'application/octet-stream',
}) : length = bytes.length,
stream = BodyBytes.fromBytes(bytes);
Expand Down
2 changes: 1 addition & 1 deletion lib/get_connect/http/src/request/request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';

import 'package:meta/meta.dart';
import 'package:flutter/foundation.dart';

import '../http.dart';
import '../multipart/form_data.dart';
Expand Down
1 change: 0 additions & 1 deletion lib/get_connect/http/src/response/response.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'dart:collection';
import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:meta/meta.dart';

import '../request/request.dart';
import '../status/http_status.dart';
Expand Down
21 changes: 21 additions & 0 deletions lib/get_utils/src/extensions/event_loop_extensions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'dart:async';
import '../../../get_core/src/get_interface.dart';

extension LoopEventsExt on GetInterface {
Future<T> toEnd<T>(FutureOr<T> computation()) async {
await Future.delayed(Duration.zero);
final val = computation();
return val;
}

FutureOr<T> asap<T>(T computation(), {bool Function() condition}) async {
T val;
if (condition == null || !condition()) {
await Future.delayed(Duration.zero);
val = computation();
} else {
val = computation();
}
return val;
}
}
Loading

0 comments on commit a910c7f

Please sign in to comment.