Feature/middlewares #9

Merged
hugo merged 4 commits from Feature/middlewares into master 2022-06-24 14:00:03 +00:00
14 changed files with 1349 additions and 13 deletions
Showing only changes of commit ea4a30ca00 - Show all commits

View File

@ -0,0 +1,386 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:convert';
import 'package:wyatt_http_client/src/authentication/refresh_token_client.dart';
import 'package:wyatt_http_client/src/rest_client.dart';
import 'package:wyatt_http_client/src/utils/protocols.dart';
enum EmailVerificationAction {
signUp,
resetPassword,
changeEmail;
String toSnakeCase() {
return name.splitMapJoin(
RegExp('[A-Z]'),
onMatch: (m) => '_${m[0]?.toLowerCase()}',
onNonMatch: (n) => n,
);
}
factory EmailVerificationAction.fromString(String str) {
return EmailVerificationAction.values.firstWhere(
(EmailVerificationAction element) => element.toSnakeCase() == str,
);
}
}
class VerifyCode {
final String email;
final String verificationCode;
final EmailVerificationAction action;
VerifyCode({
required this.email,
required this.verificationCode,
required this.action,
});
VerifyCode copyWith({
String? email,
String? verificationCode,
EmailVerificationAction? action,
}) {
return VerifyCode(
email: email ?? this.email,
verificationCode: verificationCode ?? this.verificationCode,
action: action ?? this.action,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'email': email,
'verification_code': verificationCode,
'action': action.toSnakeCase(),
};
}
factory VerifyCode.fromMap(Map<String, dynamic> map) {
return VerifyCode(
email: map['email'] as String,
verificationCode: map['verification_code'] as String,
action: EmailVerificationAction.fromString(map['action'] as String),
);
}
String toJson() => json.encode(toMap());
factory VerifyCode.fromJson(String source) =>
VerifyCode.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() =>
'VerifyCode(email: $email, verificationCode: $verificationCode, action: $action)';
}
class Account {
final String email;
final String? sessionId;
Account({
required this.email,
this.sessionId,
});
Account copyWith({
String? email,
String? sessionId,
}) {
return Account(
email: email ?? this.email,
sessionId: sessionId ?? this.sessionId,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'email': email,
'session_id': sessionId,
};
}
factory Account.fromMap(Map<String, dynamic> map) {
return Account(
email: map['email'] as String,
sessionId: map['session_id'] != null ? map['session_id'] as String : null,
);
}
String toJson() => json.encode(toMap());
factory Account.fromJson(String source) =>
Account.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() => 'Account(email: $email, sessionId: $sessionId)';
}
class SignUp {
final String sessionId;
final String password;
SignUp({
required this.sessionId,
required this.password,
});
SignUp copyWith({
String? sessionId,
String? password,
}) {
return SignUp(
sessionId: sessionId ?? this.sessionId,
password: password ?? this.password,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'session_id': sessionId,
'password': password,
};
}
factory SignUp.fromMap(Map<String, dynamic> map) {
return SignUp(
sessionId: map['session_id'] as String,
password: map['password'] as String,
);
}
String toJson() => json.encode(toMap());
factory SignUp.fromJson(String source) =>
SignUp.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() => 'SignUp(sessionId: $sessionId, password: $password)';
}
class TokenSuccess {
final String accessToken;
final String refreshToken;
final Account account;
TokenSuccess({
required this.accessToken,
required this.refreshToken,
required this.account,
});
TokenSuccess copyWith({
String? accessToken,
String? refreshToken,
Account? account,
}) {
return TokenSuccess(
accessToken: accessToken ?? this.accessToken,
refreshToken: refreshToken ?? this.refreshToken,
account: account ?? this.account,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'access_token': accessToken,
'refresh_token': refreshToken,
'account': account.toMap(),
};
}
factory TokenSuccess.fromMap(Map<String, dynamic> map) {
return TokenSuccess(
accessToken: map['access_token'] as String,
refreshToken: map['refresh_token'] as String,
account: Account.fromMap(map['account'] as Map<String, dynamic>),
);
}
String toJson() => json.encode(toMap());
factory TokenSuccess.fromJson(String source) =>
TokenSuccess.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() =>
'TokenSuccess(accessToken: $accessToken, refreshToken: $refreshToken, account: $account)';
}
class Login {
final String email;
final String password;
Login({
required this.email,
required this.password,
});
Login copyWith({
String? email,
String? password,
}) {
return Login(
email: email ?? this.email,
password: password ?? this.password,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'email': email,
'password': password,
};
}
factory Login.fromMap(Map<String, dynamic> map) {
return Login(
email: map['email'] as String,
password: map['password'] as String,
);
}
String toJson() => json.encode(toMap());
factory Login.fromJson(String source) =>
Login.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() => 'Login(email: $email, password: $password)';
}
class FastAPI {
final String baseUrl;
final RefreshTokenClient client;
final int apiVersion;
FastAPI({
this.baseUrl = 'localhost:80',
RefreshTokenClient? client,
this.apiVersion = 1,
}) : client = client ??
RefreshTokenClient(
authorizationEndpoint: '',
tokenEndpoint: '',
accessTokenParser: (body) => body['access_token']! as String,
refreshTokenParser: (body) => body['refresh_token']! as String,
inner: RestClient(
protocol: Protocols.http,
authority: baseUrl,
),
);
String get apiPath => '/api/v$apiVersion';
Future<void> sendSignUpCode(String email) async {
final r = await client.post(
Uri.parse('$apiPath/auth/send-sign-up-code'),
body: <String, String>{
'email': email,
},
);
if (r.statusCode != 201) {
throw Exception('Invalid reponse: ${r.statusCode}');
}
}
Future<Account> verifyCode(VerifyCode verifyCode) async {
final r = await client.post(
Uri.parse('$apiPath/auth/verify-code'),
body: verifyCode.toMap(),
);
if (r.statusCode != 202) {
throw Exception('Invalid reponse: ${r.statusCode}');
} else {
return Account.fromMap(
(jsonDecode(r.body) as Map<String, dynamic>)['account']
as Map<String, dynamic>,
);
}
}
Future<Account> signUp(SignUp signUp) async {
final r = await client.post(
Uri.parse('$apiPath/auth/sign-up'),
body: signUp.toMap(),
);
if (r.statusCode != 201) {
throw Exception('Invalid reponse: ${r.statusCode}');
} else {
return Account.fromJson(r.body);
}
}
Future<TokenSuccess> signInWithPassword(Login login) async {
final r = await client.authorize(login.toMap());
return TokenSuccess.fromJson(r.body);
}
Future<TokenSuccess> refresh() async {
final r = await client.refresh();
return TokenSuccess.fromJson(r?.body ?? '');
}
Future<List<Account>> getAccountList() async {
final r = await client.get(
Uri.parse('$apiPath/account'),
);
if (r.statusCode != 200) {
throw Exception('Invalid reponse: ${r.statusCode}');
} else {
final list = (jsonDecode(r.body) as Map<String, dynamic>)['founds']
as List<Map<String, dynamic>>;
final result = <Account>[];
for (final element in list) {
result.add(Account.fromMap(element));
}
return result;
}
}
}
void main(List<String> args) async {
final api = FastAPI(
client: RefreshTokenClient(
authorizationEndpoint: '/api/v1/auth/sign-in-with-password',
tokenEndpoint: '/api/v1/auth/refresh',
accessTokenParser: (body) => body['access_token']! as String,
refreshTokenParser: (body) => body['refresh_token']! as String,
inner: RestClient(
protocol: Protocols.http,
authority: 'localhost:80',
),
),
);
// await api.sendSignUpCode('git@pcl.ovh');
// final verifiedAccount = await api.verifyCode(
// VerifyCode(
// email: 'git@pcl.ovh',
// verificationCode: '000000000',
// action: EmailVerificationAction.signUp,
// ),
// );
// print(verifiedAccount);
// final registeredAccount = await api.signUp(
// SignUp(sessionId: verifiedAccount.sessionId ?? '', password: 'password'),
// );
// print(registeredAccount);
final signedInAccount = await api.signInWithPassword(
Login(email: 'git@pcl.ovh', password: 'password'),
);
// print(signedInAccount);
final accountList = await api.getAccountList();
print(accountList);
}

View File

@ -0,0 +1,41 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
Future<void> main(List<String> args) async {
// final client = Oauth2Client(
// accessToken: 'test-token',
// inner: RestClient(protocol: Protocols.http, authority: 'localhost:80'),
// );
// final client = RestClient(
// protocol: Protocols.http,
// authority: 'localhost:80',
// inner: Oauth2Client(
// authorizationEndpoint: '/api/v1/account/test',
// tokenEndpoint: '/api/v1/account/test',
// accessToken: 'test-token',
// refreshToken: 'refresh-token',
// ),
// );
// final client = RestClient(protocol: Protocols.http, authority: 'localhost:80');
// final client =AwesomeRestClient(protocol: Protocols.http, authority: 'localhost:80');
// var r = await client.post(
// Uri.parse('/api/v1/account/test'),
// body: <String, String>{
// 'email': 'test@test.fr',
// },
// );
}

View File

@ -0,0 +1,87 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'dart:typed_data';
import 'package:http/http.dart';
import 'package:wyatt_http_client/src/implemented_base_client.dart';
abstract class AuthenticatedClient implements ImplementedBaseClient {
final Client _inner;
AuthenticatedClient({
Client? inner,
}) : _inner = inner ?? Client();
@override
void close() => _inner.close();
@override
Future<Response> head(Uri url, {Map<String, String>? headers}) =>
_inner.head(url, headers: headers);
@override
Future<Response> get(Uri url, {Map<String, String>? headers}) =>
_inner.get(url, headers: headers);
@override
Future<Response> post(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.post(url, headers: headers, body: body, encoding: encoding);
@override
Future<Response> put(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.put(url, headers: headers, body: body, encoding: encoding);
@override
Future<Response> patch(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.patch(url, headers: headers, body: body, encoding: encoding);
@override
Future<Response> delete(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.delete(url, headers: headers, body: body, encoding: encoding);
@override
Future<String> read(Uri url, {Map<String, String>? headers}) =>
_inner.read(url, headers: headers);
@override
Future<Uint8List> readBytes(Uri url, {Map<String, String>? headers}) =>
_inner.readBytes(url, headers: headers);
@override
Future<StreamedResponse> send(BaseRequest request) => _inner.send(request);
}

View File

@ -14,8 +14,161 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'dart:typed_data';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:wyatt_http_client/src/mixins/body_transformer.dart';
import 'package:wyatt_http_client/src/mixins/headers_transformer.dart';
import 'package:wyatt_http_client/src/rest_client.dart'; import 'package:wyatt_http_client/src/rest_client.dart';
import 'package:wyatt_http_client/src/utils/protocols.dart';
import 'package:wyatt_http_client/src/utils/utils.dart';
class AwesomeClient extends BaseClient {
final Client _inner;
AwesomeClient({
Client? inner,
}) : _inner = inner ?? Client();
@override
Future<StreamedResponse> send(BaseRequest request) {
return _inner.send(request);
}
}
class AwesomeRestClient extends AwesomeClient
with HeadersTransformer, BodyTransformer {
final Protocols protocol;
final String? authority;
AwesomeRestClient({
this.protocol = Protocols.https,
this.authority = '',
super.inner,
});
@override
Object? bodyMutator(Object? body) {
print('bodyMutator: Json encoding');
return jsonEncode(body);
}
@override
Map<String, String>? headersMutator(
Object? body,
Map<String, String>? headers,
) {
print('headerMutator: Json encoding');
final mutation = {
'content-type': 'application/json; charset=utf-8',
};
if (headers != null) {
headers.addAll(mutation);
return headers;
} else {
return mutation;
}
}
@override
BaseRequest requestMutator(BaseRequest request) {
print('requestMutator: scheme + authority');
final Uri uri = Uri.parse('${protocol.scheme}$authority${request.url}');
return Utils.copyRequestWith(request, url: uri);
}
}
class AwesomeUrlClient extends AuthenticatedClient {
AwesomeUrlClient(super.inner);
}
class AwesomeOauth2Client extends AuthenticatedClient with HeadersTransformer {
AwesomeOauth2Client(super.inner);
@override
Map<String, String>? headersMutator(
Object? body,
Map<String, String>? headers,
) {
print('headersMutator: Token manager');
final mutation = {
'authorization': 'Bearer TOKEN',
};
if (headers != null) {
headers.addAll(mutation);
return headers;
} else {
return mutation;
}
}
}
abstract class AuthenticatedClient implements Client {
final Client _inner;
Client get inner => _inner;
AuthenticatedClient(BaseClient? inner) : _inner = inner ?? RestClient();
@override
void close() => _inner.close();
@override
Future<Response> head(Uri url, {Map<String, String>? headers}) =>
_inner.head(url, headers: headers);
@override
Future<Response> get(Uri url, {Map<String, String>? headers}) =>
_inner.get(url, headers: headers);
@override
Future<Response> post(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.post(url, headers: headers, body: body, encoding: encoding);
@override
Future<Response> put(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.put(url, headers: headers, body: body, encoding: encoding);
@override
Future<Response> patch(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.patch(url, headers: headers, body: body, encoding: encoding);
@override
Future<Response> delete(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.delete(url, headers: headers, body: body, encoding: encoding);
@override
Future<String> read(Uri url, {Map<String, String>? headers}) =>
_inner.read(url, headers: headers);
@override
Future<Uint8List> readBytes(Uri url, {Map<String, String>? headers}) =>
_inner.readBytes(url, headers: headers);
@override
Future<StreamedResponse> send(BaseRequest request) => _inner.send(request);
}
abstract class AuthenticationClient extends BaseClient { abstract class AuthenticationClient extends BaseClient {
final BaseClient _inner; final BaseClient _inner;
@ -24,6 +177,50 @@ abstract class AuthenticationClient extends BaseClient {
AuthenticationClient(BaseClient? inner) : _inner = inner ?? RestClient(); AuthenticationClient(BaseClient? inner) : _inner = inner ?? RestClient();
@override
Future<Response> head(Uri url, {Map<String, String>? headers}) =>
_inner.head(url, headers: headers);
@override
Future<Response> get(Uri url, {Map<String, String>? headers}) =>
_inner.get(url, headers: headers);
@override
Future<Response> post(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.post(url, headers: headers, body: body, encoding: encoding);
@override
Future<Response> put(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.put(url, headers: headers, body: body, encoding: encoding);
@override
Future<Response> patch(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.patch(url, headers: headers, body: body, encoding: encoding);
@override
Future<Response> delete(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.delete(url, headers: headers, body: body, encoding: encoding);
@override @override
Future<StreamedResponse> send(BaseRequest request) { Future<StreamedResponse> send(BaseRequest request) {
return _inner.send(request); return _inner.send(request);

View File

@ -31,6 +31,7 @@ abstract class HeaderAuthenticationClient extends AuthenticationClient {
final newHeader = modifyHeader(Map.from(request.headers), request); final newHeader = modifyHeader(Map.from(request.headers), request);
request.headers.clear(); request.headers.clear();
request.headers.addAll(newHeader); request.headers.addAll(newHeader);
print(newHeader);
return super.send(request); return super.send(request);
} }
} }

View File

@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:http/http.dart';
import 'package:wyatt_http_client/src/authentication/interfaces/header_authentication_client.dart'; import 'package:wyatt_http_client/src/authentication/interfaces/header_authentication_client.dart';
typedef TokenParser = String Function(Map<String, dynamic>); typedef TokenParser = String Function(Map<String, dynamic>);
@ -21,14 +22,14 @@ typedef TokenParser = String Function(Map<String, dynamic>);
abstract class Oauth2Client extends HeaderAuthenticationClient { abstract class Oauth2Client extends HeaderAuthenticationClient {
Oauth2Client(super.inner); Oauth2Client(super.inner);
Future<void> refresh() { Future<Response?> refresh() {
return Future.value(); return Future.value();
} }
Future<void> authorize( Future<Response> authorize(
Map<String, dynamic> body, { Map<String, dynamic> body, {
Map<String, String>? headers, Map<String, String>? headers,
}) { }) {
return Future.value(); return Future<Response>.value();
} }
} }

View File

@ -23,6 +23,78 @@ import 'package:wyatt_http_client/src/utils/header_keys.dart';
import 'package:wyatt_http_client/src/utils/http_status.dart'; import 'package:wyatt_http_client/src/utils/http_status.dart';
import 'package:wyatt_http_client/src/utils/utils.dart'; import 'package:wyatt_http_client/src/utils/utils.dart';
// class Oauth2Client extends ImplementedBaseClient with RequestTransformer {
// final String authorizationEndpoint;
// final String tokenEndpoint;
// String? accessToken;
// String? refreshToken;
// String? tokenToUse;
// Oauth2Client({
// required this.authorizationEndpoint,
// required this.tokenEndpoint,
// this.accessToken,
// this.refreshToken,
// super.inner,
// }) : tokenToUse = accessToken;
// @override
// BaseRequest requestMutator(BaseRequest request) {
// print('Oauth2Client::requestMutator -> add authorization: $accessToken');
// final headers = request.headers;
// final mutation = {
// 'Authorization': 'Bearer $tokenToUse',
// };
// if (tokenToUse?.isNotEmpty ?? false) {
// headers.addAll(mutation);
// return Utils.copyRequestWith(request, headers: headers);
// }
// return request;
// }
// Future<Response?> refresh() async {
// if (refreshToken?.isNotEmpty ?? false) {
// tokenToUse = refreshToken;
// final response = await get(
// Uri.parse(tokenEndpoint),
// );
// if (response.statusCode == HttpStatus.ok) {
// // final body = json.decode(response.body) as Map<String, dynamic>;
// // accessToken = accessTokenParser(body);
// print('Oauth2Client::refresh -> ok');
// }
// return response;
// }
// return null;
// }
// @override
// Map<String, String>? headersMutator(
// Object? body,
// Map<String, String>? headers,
// ) {
// print(
// 'Oauth2Client::headersMutator -> add authorization: $accessToken',
// );
// final mutation = {
// 'Authorization': 'Bearer $accessToken',
// };
// if (accessToken.isNotEmpty) {
// if (headers != null) {
// headers.addAll(mutation);
// return headers;
// } else {
// return mutation;
// }
// } else {
// return headers;
// }
// }
// }
class RefreshTokenClient extends Oauth2Client { class RefreshTokenClient extends Oauth2Client {
final String authorizationEndpoint; final String authorizationEndpoint;
final String tokenEndpoint; final String tokenEndpoint;
@ -50,6 +122,8 @@ class RefreshTokenClient extends Oauth2Client {
Map<String, String> header, [ Map<String, String> header, [
BaseRequest? request, BaseRequest? request,
]) { ]) {
print('accessToken $accessToken');
print('request $request');
if (accessToken != null && request != null) { if (accessToken != null && request != null) {
header[authenticationHeader] = '$authenticationMethod $accessToken'; header[authenticationHeader] = '$authenticationMethod $accessToken';
return header; return header;
@ -58,13 +132,13 @@ class RefreshTokenClient extends Oauth2Client {
} }
@override @override
Future<void> authorize( Future<Response> authorize(
Map<String, dynamic> body, { Map<String, dynamic> body, {
Map<String, String>? headers, Map<String, String>? headers,
}) async { }) async {
final response = await inner.post( final response = await inner.post(
Uri.parse(authorizationEndpoint), Uri.parse(authorizationEndpoint),
body: jsonEncode(body), body: body,
headers: headers, headers: headers,
); );
@ -80,10 +154,11 @@ class RefreshTokenClient extends Oauth2Client {
this.refreshToken = refreshToken; this.refreshToken = refreshToken;
} }
} }
return response;
} }
@override @override
Future<void> refresh() async { Future<Response?> refresh() async {
if (refreshToken != null) { if (refreshToken != null) {
final Map<String, String> header = { final Map<String, String> header = {
authenticationHeader: '$authenticationHeader $refreshToken', authenticationHeader: '$authenticationHeader $refreshToken',
@ -98,11 +173,16 @@ class RefreshTokenClient extends Oauth2Client {
final body = json.decode(response.body) as Map<String, dynamic>; final body = json.decode(response.body) as Map<String, dynamic>;
accessToken = accessTokenParser(body); accessToken = accessTokenParser(body);
} }
return response;
} }
return null;
} }
@override @override
Future<StreamedResponse> send(BaseRequest request) async { Future<StreamedResponse> send(BaseRequest request) async {
final newHeader = modifyHeader(Map.from(request.headers), request);
request.headers.clear();
request.headers.addAll(newHeader);
final response = await super.send(request); final response = await super.send(request);
if (response.statusCode == HttpStatus.unauthorized) { if (response.statusCode == HttpStatus.unauthorized) {

View File

@ -0,0 +1,31 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:http/http.dart';
class ImplementedBaseClient extends BaseClient {
final Client inner;
ImplementedBaseClient({
Client? inner,
}) : inner = inner ?? Client();
@override
Future<StreamedResponse> send(BaseRequest request) {
return inner.send(request);
}
}

View File

@ -0,0 +1,86 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'dart:typed_data';
import 'package:http/http.dart';
abstract class ImplementedClient extends BaseClient {
final Client _inner;
ImplementedClient({
Client? inner,
}) : _inner = inner ?? Client();
@override
void close() => _inner.close();
@override
Future<Response> head(Uri url, {Map<String, String>? headers}) =>
_inner.head(url, headers: headers);
@override
Future<Response> get(Uri url, {Map<String, String>? headers}) =>
_inner.get(url, headers: headers);
@override
Future<Response> post(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.post(url, headers: headers, body: body, encoding: encoding);
@override
Future<Response> put(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.put(url, headers: headers, body: body, encoding: encoding);
@override
Future<Response> patch(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.patch(url, headers: headers, body: body, encoding: encoding);
@override
Future<Response> delete(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_inner.delete(url, headers: headers, body: body, encoding: encoding);
@override
Future<String> read(Uri url, {Map<String, String>? headers}) =>
_inner.read(url, headers: headers);
@override
Future<Uint8List> readBytes(Uri url, {Map<String, String>? headers}) =>
_inner.readBytes(url, headers: headers);
@override
Future<StreamedResponse> send(BaseRequest request) => _inner.send(request);
}

View File

@ -0,0 +1,83 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:http/http.dart';
mixin BodyTransformer on Client {
Object? bodyMutator(Object? body);
@override
Future<Response> post(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) {
return super.post(
url,
headers: headers,
body: bodyMutator(body),
encoding: encoding,
);
}
@override
Future<Response> put(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) {
return super.put(
url,
headers: headers,
body: bodyMutator(body),
encoding: encoding,
);
}
@override
Future<Response> patch(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) {
return super.patch(
url,
headers: headers,
body: bodyMutator(body),
encoding: encoding,
);
}
@override
Future<Response> delete(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) {
return super.delete(
url,
headers: headers,
body: bodyMutator(body),
encoding: encoding,
);
}
}

View File

@ -0,0 +1,131 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'dart:typed_data';
import 'package:http/http.dart';
mixin HeadersTransformer on Client {
Map<String, String>? headersMutator(
Object? body,
Map<String, String>? headers,
);
@override
Future<Response> head(
Uri url, {
Map<String, String>? headers,
}) {
return super.head(
url,
headers: headersMutator(null, headers),
);
}
@override
Future<Response> get(
Uri url, {
Map<String, String>? headers,
}) {
return super.get(
url,
headers: headersMutator(null, headers),
);
}
@override
Future<Response> post(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) {
return super.post(
url,
headers: headersMutator(body, headers),
body: body,
encoding: encoding,
);
}
@override
Future<Response> put(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) {
return super.put(
url,
headers: headersMutator(body, headers),
body: body,
encoding: encoding,
);
}
@override
Future<Response> patch(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) {
return super.patch(
url,
headers: headersMutator(body, headers),
body: body,
encoding: encoding,
);
}
@override
Future<Response> delete(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) {
return super.delete(
url,
headers: headersMutator(body, headers),
body: body,
encoding: encoding,
);
}
@override
Future<String> read(
Uri url, {
Map<String, String>? headers,
}) {
return super.read(
url,
headers: headersMutator(null, headers),
);
}
@override
Future<Uint8List> readBytes(
Uri url, {
Map<String, String>? headers,
}) {
return super.readBytes(
url,
headers: headersMutator(null, headers),
);
}
}

View File

@ -0,0 +1,33 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:http/http.dart';
import 'package:wyatt_http_client/src/implemented_base_client.dart';
mixin Oauth2Transformer on ImplementedBaseClient {
late final String authorizationEndpoint;
late final String tokenEndpoint;
String? accessToken;
String? refreshToken;
BaseRequest requestAuthenticator(BaseRequest request);
@override
Future<StreamedResponse> send(BaseRequest request) {
final req = requestAuthenticator(request);
return super.send(req);
}
}

View File

@ -0,0 +1,27 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:http/http.dart';
import 'package:wyatt_http_client/src/implemented_base_client.dart';
mixin RequestTransformer on ImplementedBaseClient {
BaseRequest requestMutator(BaseRequest request);
@override
Future<StreamedResponse> send(BaseRequest request) {
return super.send(requestMutator(request));
}
}

View File

@ -14,25 +14,177 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:wyatt_http_client/src/implemented_base_client.dart';
import 'package:wyatt_http_client/src/mixins/body_transformer.dart';
import 'package:wyatt_http_client/src/mixins/headers_transformer.dart';
import 'package:wyatt_http_client/src/mixins/request_transformer.dart';
import 'package:wyatt_http_client/src/utils/protocols.dart'; import 'package:wyatt_http_client/src/utils/protocols.dart';
import 'package:wyatt_http_client/src/utils/utils.dart'; import 'package:wyatt_http_client/src/utils/utils.dart';
class RestClient extends BaseClient { class RestClient extends ImplementedBaseClient
with BodyTransformer, HeadersTransformer, RequestTransformer {
final Protocols protocol; final Protocols protocol;
final String? authority; final String? authority;
final Client _inner;
RestClient({ RestClient({
this.protocol = Protocols.https, this.protocol = Protocols.https,
this.authority = '', this.authority = '',
Client? inner, super.inner,
}) : _inner = inner ?? Client(); });
@override @override
Future<StreamedResponse> send(BaseRequest request) { Object? bodyMutator(Object? body) {
print(
'RestClient::bodyMutator -> encode in json if body is Map: ${body is Map}',
);
if (body is Map) {
return jsonEncode(body);
}
return body;
}
@override
Map<String, String>? headersMutator(
Object? body,
Map<String, String>? headers,
) {
print(
'RestClient::headersMutator -> add json content-type if body is Map: ${body is Map}',
);
final mutation = {
'content-type': 'application/json; charset=utf-8',
};
if (body is Map) {
if (headers != null) {
headers.addAll(mutation);
return headers;
} else {
return mutation;
}
}
return headers;
}
@override
BaseRequest requestMutator(BaseRequest request) {
print(
'RestClient::requestMutator -> add prefix path: ${protocol.scheme}$authority',
);
final Uri uri = Uri.parse('${protocol.scheme}$authority${request.url}'); final Uri uri = Uri.parse('${protocol.scheme}$authority${request.url}');
return _inner.send(Utils.copyRequestWith(request, url: uri)); return Utils.copyRequestWith(request, url: uri);
} }
} }
// class RestClient extends BaseClient {
// final Protocols protocol;
// final String? authority;
// final Client _inner;
// RestClient({
// this.protocol = Protocols.https,
// this.authority = '',
// Client? inner,
// }) : _inner = inner ?? Client();
// String? forceJson(Object? body) {
// String? b;
// if (body != null && body is Map) {
// b = jsonEncode(body);
// }
// return b;
// }
// Map<String, String>? forceJsonHeader(
// Object? body, Map<String, String>? headers,) {
// final Map<String, String> h = headers ?? {};
// if (body != null && body is Map) {
// h['Content-Type'] = 'application/json';
// }
// return h;
// }
// // @override
// // Future<Response> post(
// // Uri url, {
// // Map<String, String>? headers,
// // Object? body,
// // Encoding? encoding,
// // }) {
// // final b = forceJson(body) ?? body;
// // final h = forceJsonHeader(body, headers) ?? headers;
// // print(b);
// // print(h);
// // return super.post(
// // url,
// // headers: h,
// // body: b,
// // encoding: encoding,
// // );
// // }
// @override
// Future<Response> put(
// Uri url, {
// Map<String, String>? headers,
// Object? body,
// Encoding? encoding,
// }) {
// final b = forceJson(body) ?? body;
// final h = forceJsonHeader(body, headers) ?? headers;
// return super.put(
// url,
// headers: h,
// body: b,
// encoding: encoding,
// );
// }
// @override
// Future<Response> patch(
// Uri url, {
// Map<String, String>? headers,
// Object? body,
// Encoding? encoding,
// }) {
// final b = forceJson(body) ?? body;
// final h = forceJsonHeader(body, headers) ?? headers;
// return super.patch(
// url,
// headers: h,
// body: b,
// encoding: encoding,
// );
// }
// @override
// Future<Response> delete(
// Uri url, {
// Map<String, String>? headers,
// Object? body,
// Encoding? encoding,
// }) {
// final b = forceJson(body) ?? body;
// final h = forceJsonHeader(body, headers) ?? headers;
// return super.delete(
// url,
// headers: h,
// body: b,
// encoding: encoding,
// );
// }
// @override
// Future<StreamedResponse> send(BaseRequest request) {
// final Uri uri = Uri.parse('${protocol.scheme}$authority${request.url}');
// return _inner.send(
// Utils.copyRequestWith(
// request,
// url: uri,
// ),
// );
// }
// }