refactor(http)!: fix cascade dart good practices + docs
This commit is contained in:
		
							parent
							
								
									216a6c2aae
								
							
						
					
					
						commit
						07c2f4aeff
					
				| @ -52,13 +52,13 @@ abstract class GetItInitializer { | ||||
|       ) | ||||
|       ..registerLazySingleton<MiddlewareClient>(() { | ||||
|         final Pipeline pipeline = Pipeline() | ||||
|             .addMiddleware( | ||||
|               UriPrefixMiddleware( | ||||
|                 protocol: Protocols.https, | ||||
|                 authority: 'jsonplaceholder.typicode.com', | ||||
|               ), | ||||
|             ) | ||||
|             .addMiddleware(BodyToJsonMiddleware()); | ||||
|           ..addMiddleware( | ||||
|             const UriPrefixMiddleware( | ||||
|               protocol: Protocols.https, | ||||
|               authority: 'jsonplaceholder.typicode.com', | ||||
|             ), | ||||
|           ) | ||||
|           ..addMiddleware(const BodyToJsonMiddleware()); | ||||
|         return MiddlewareClient(pipeline: pipeline); | ||||
|       }) | ||||
|       ..registerLazySingleton<PhotoRemoteDataSource>( | ||||
|  | ||||
| @ -16,12 +16,10 @@ | ||||
|  * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| --> | ||||
| 
 | ||||
| # Dart - HTTP Client | ||||
| # HTTP Client | ||||
| 
 | ||||
| <p align="left"> | ||||
|   <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"> | ||||
|     <img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /> | ||||
|   </a> | ||||
|   <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"><img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /></a> | ||||
|   <img src="https://img.shields.io/badge/SDK-Dart%20%7C%20Flutter-blue?style=flat-square" alt="SDK: Dart & Flutter" /> | ||||
| </p> | ||||
| 
 | ||||
| @ -52,13 +50,13 @@ For example, if you want to log every request, and simplify an url you can use p | ||||
| ```dart | ||||
| // Create the Pipeline | ||||
| final Pipeline pipeline = Pipeline() | ||||
|     .addMiddleware( | ||||
|         UriPrefixMiddleware( | ||||
|     ..addMiddleware( | ||||
|         const UriPrefixMiddleware( | ||||
|             protocol: Protocols.http, | ||||
|             authority: 'localhost:80', | ||||
|         ), | ||||
|     ) | ||||
|     .addMiddleware(SimpleLoggerMiddleware()); | ||||
|     ..addMiddleware(const SimpleLoggerMiddleware()); | ||||
| ``` | ||||
| 
 | ||||
| Then if you print the pipeline,  | ||||
| @ -94,20 +92,20 @@ Let's start by creating the Pipeline: | ||||
| 
 | ||||
| ```dart | ||||
| final Pipeline pipeline = Pipeline() | ||||
|     .addMiddleware( | ||||
|         UriPrefixMiddleware( | ||||
|     ..addMiddleware( | ||||
|         const UriPrefixMiddleware( | ||||
|             protocol: Protocols.http, | ||||
|             authority: 'localhost:80', | ||||
|         ), | ||||
|     ) | ||||
|     .addMiddleware(BodyToJsonMiddleware()) | ||||
|     .addMiddleware( | ||||
|         UnsafeAuthMiddleware( | ||||
|     ..addMiddleware(const BodyToJsonMiddleware()) | ||||
|     ..addMiddleware( | ||||
|         const UnsafeAuthMiddleware( | ||||
|             username: 'wyatt', | ||||
|             password: 'motdepasse', | ||||
|         ), | ||||
|     ) | ||||
|     .addMiddleware(SimpleLoggerMiddleware()); | ||||
|     ..addMiddleware(SimpleLoggerMiddleware()); | ||||
| ``` | ||||
| 
 | ||||
| Then simply create a client and make a call. | ||||
| @ -128,14 +126,14 @@ So now we want a real authentication. | ||||
| 
 | ||||
| ```dart | ||||
| final Pipeline pipeline = Pipeline() | ||||
|     .addMiddleware( | ||||
|         UriPrefixMiddleware( | ||||
|     ..addMiddleware( | ||||
|         const UriPrefixMiddleware( | ||||
|             protocol: Protocols.http, | ||||
|             authority: 'localhost:80', | ||||
|         ), | ||||
|     ) | ||||
|     .addMiddleware(BodyToJsonMiddleware()) | ||||
|     .addMiddleware( | ||||
|     ..addMiddleware(const BodyToJsonMiddleware()) | ||||
|     ..addMiddleware( | ||||
|         RefreshTokenAuthMiddleware( | ||||
|             authorizationEndpoint: '/auth/sign-in', | ||||
|             tokenEndpoint: '/auth/refresh', | ||||
| @ -144,7 +142,7 @@ final Pipeline pipeline = Pipeline() | ||||
|             unauthorized: HttpStatus.forbidden, | ||||
|         ), | ||||
|     ) | ||||
|     .addMiddleware(SimpleLoggerMiddleware()); | ||||
|     ..addMiddleware(const SimpleLoggerMiddleware()); | ||||
| ``` | ||||
| 
 | ||||
| > Here we just change `UnsafeAuthMiddleware` by `RefreshTokenAuthMiddleware` and the whole app while adapt to a new authentication system. | ||||
| @ -157,6 +155,8 @@ You can create your own middleware by implementing `Middleware` class, and use m | ||||
| class SimpleLoggerMiddleware  | ||||
|     with OnRequestMiddleware, OnResponseMiddleware  | ||||
|     implements Middleware { | ||||
|      | ||||
|   const SimpleLoggerMiddleware(); | ||||
|    | ||||
|   @override | ||||
|   String getName() => 'SimpleLogger'; | ||||
|  | ||||
| @ -1,294 +0,0 @@ | ||||
| // 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:async'; | ||||
| import 'dart:io'; | ||||
| 
 | ||||
| import 'package:wyatt_http_client/wyatt_http_client.dart'; | ||||
| 
 | ||||
| String lastToken = ''; | ||||
| int token = 0; | ||||
| 
 | ||||
| void printAuth(HttpRequest req) { | ||||
|   print( | ||||
|     'Authorization => ' | ||||
|     "${req.headers.value('Authorization') ?? 'no authorization header'}", | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| Future<void> handleBasic(HttpRequest req) async { | ||||
|   printAuth(req); | ||||
| } | ||||
| 
 | ||||
| Future<void> handleBasicNegotiate(HttpRequest req) async { | ||||
|   if (req.headers.value('Authorization') == null) { | ||||
|     req.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||
|     req.response.headers.set(HeaderKeys.wwwAuthenticate, 'Basic realm="Wyatt"'); | ||||
|     print(req.response.headers.value('WWW-Authenticate')); | ||||
|     return req.response.close(); | ||||
|   } | ||||
|   printAuth(req); | ||||
| } | ||||
| 
 | ||||
| Future<void> handleBearer(HttpRequest req) async { | ||||
|   printAuth(req); | ||||
| } | ||||
| 
 | ||||
| Future<void> handleDigest(HttpRequest req) async { | ||||
|   if (req.headers.value('Authorization') == null) { | ||||
|     req.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||
|     req.response.headers.set( | ||||
|       'WWW-Authenticate', | ||||
|       'Digest realm="Wyatt", ' | ||||
|           'qop="auth,auth-int", ' | ||||
|           'nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", ' | ||||
|           'opaque="5ccc069c403ebaf9f0171e9517f40e41"', | ||||
|     ); | ||||
|     print(req.response.headers.value('WWW-Authenticate')); | ||||
|     return req.response.close(); | ||||
|   } | ||||
|   printAuth(req); | ||||
| } | ||||
| 
 | ||||
| Future<void> handleUnsafe(HttpRequest req) async { | ||||
|   print( | ||||
|     'Query parameters => ' | ||||
|     '${req.uri.queryParameters}', | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| Future<void> handleOauth2RefreshToken(HttpRequest req) async { | ||||
|   final action = req.uri.queryParameters['action']; | ||||
|   if (action == null) { | ||||
|     printAuth(req); | ||||
|   } | ||||
| 
 | ||||
|   switch (action) { | ||||
|     case 'login': | ||||
|       if (req.method == 'POST') { | ||||
|         token++; | ||||
|         req.response.write( | ||||
|           '{"accessToken": "access-token-awesome$token", ' | ||||
|           '"refreshToken": "refresh-token-awesome$token"}', | ||||
|         ); | ||||
|       } | ||||
|       break; | ||||
|     case 'refresh': | ||||
|       printAuth(req); | ||||
|       if (req.method == 'GET') { | ||||
|         token++; | ||||
|         req.response.write('{"accessToken": "access-token-refreshed$token"}'); | ||||
|       } | ||||
|       break; | ||||
|     case 'access-denied': | ||||
|       final String receivedToken = req.headers.value('Authorization') ?? ''; | ||||
|       if (receivedToken != '' && | ||||
|           lastToken != '' && | ||||
|           receivedToken != lastToken) { | ||||
|         lastToken = receivedToken; | ||||
|         printAuth(req); | ||||
|         return req.response.close(); | ||||
|       } else { | ||||
|         lastToken = receivedToken; | ||||
|         req.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||
|         return req.response.close(); | ||||
|       } | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| Future<void> server() async { | ||||
|   final server = await HttpServer.bind(InternetAddress.anyIPv6, 8080); | ||||
|   var error = 0; | ||||
|   var token = 0; | ||||
|   await server.forEach((request) { | ||||
|     print('[${request.method}] ${request.uri}'); | ||||
|     switch (request.uri.path) { | ||||
|       case '/test/basic-test': | ||||
|         handleBasic(request); | ||||
|         break; | ||||
|       case '/test/basic-test-with-negotiate': | ||||
|         handleBasicNegotiate(request); | ||||
|         break; | ||||
|       case '/test/digest-test': | ||||
|         handleDigest(request); | ||||
|         break; | ||||
|       case '/test/apikey-test': | ||||
|         handleBearer(request); | ||||
|         break; | ||||
|       case '/test/bearer-test': | ||||
|         handleBearer(request); | ||||
|         break; | ||||
|       case '/test/unsafe-test': | ||||
|         handleUnsafe(request); | ||||
|         break; | ||||
|       case '/test/oauth2-test': | ||||
|         handleOauth2RefreshToken(request); | ||||
|         break; | ||||
| 
 | ||||
|       case '/test/bearer-login': | ||||
|         if (request.method == 'POST') { | ||||
|           request.response.write('{"token": "access-token-test"}'); | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|       case '/test/oauth2-test-error': | ||||
|         error++; | ||||
|         print('Error $error'); | ||||
|         if (error >= 3) { | ||||
|           print('Authorized'); | ||||
|           error = 0; | ||||
|         } else { | ||||
|           request.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||
|         } | ||||
|         break; | ||||
|       case '/test/oauth2-test-timeout': | ||||
|         error++; | ||||
|         print('Error $error'); | ||||
|         request.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||
|         break; | ||||
|       case '/test/oauth2-login': | ||||
|         if (request.method == 'POST') { | ||||
|           token++; | ||||
|           request.response.write( | ||||
|             '{"accessToken": "access-token-awesome$token", ' | ||||
|             '"refreshToken": "refresh-token-awesome$token"}', | ||||
|           ); | ||||
|         } | ||||
|         break; | ||||
|       case '/test/oauth2-refresh': | ||||
|         print( | ||||
|           'Authorization => ' | ||||
|           "${request.headers.value('Authorization') ?? 'no refresh token'}", | ||||
|         ); | ||||
|         if (request.method == 'GET') { | ||||
|           token++; | ||||
|           request.response | ||||
|               .write('{"accessToken": "access-token-refreshed$token"}'); | ||||
|         } | ||||
|         break; | ||||
|       case '/test/oauth2-refresh-error': | ||||
|         request.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||
|         break; | ||||
| 
 | ||||
|       default: | ||||
|         print(' => Unknown path or method'); | ||||
|         request.response.statusCode = HttpStatus.notFound.statusCode; | ||||
|     } | ||||
|     request.response.close(); | ||||
|     print('===================='); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| Future<void> main() async { | ||||
|   unawaited(server()); | ||||
|   const base = 'localhost:8080'; | ||||
|   final uriPrefix = UriPrefixMiddleware( | ||||
|     protocol: Protocols.http, | ||||
|     authority: base, | ||||
|   ); | ||||
|   final jsonEncoder = BodyToJsonMiddleware(); | ||||
|   final logger = SimpleLoggerMiddleware(); | ||||
| 
 | ||||
|   // Basic | ||||
|   final basicAuth = BasicAuthMiddleware( | ||||
|     username: 'username', | ||||
|     password: 'password', | ||||
|   ); | ||||
|   final basic = MiddlewareClient( | ||||
|     pipeline: Pipeline.fromIterable([ | ||||
|       uriPrefix, | ||||
|       basicAuth, | ||||
|       logger, | ||||
|     ]), | ||||
|   ); | ||||
|   await basic.get(Uri.parse('/test/basic-test')); | ||||
| 
 | ||||
|   // Digest | ||||
|   final digestAuth = DigestAuthMiddleware( | ||||
|     username: 'Mufasa', | ||||
|     password: 'Circle Of Life', | ||||
|   ); | ||||
|   final digest = MiddlewareClient( | ||||
|     pipeline: Pipeline.fromIterable([ | ||||
|       uriPrefix, | ||||
|       digestAuth, | ||||
|       logger, | ||||
|     ]), | ||||
|   ); | ||||
|   await digest.get(Uri.parse('/test/digest-test')); | ||||
| 
 | ||||
|   // // Bearer | ||||
|   // final bearer = BearerAuthenticationClient( | ||||
|   //   token: 'access-token-test', | ||||
|   //   inner: restClient, | ||||
|   // ); | ||||
|   // await bearer.get(Uri.parse('/test/bearer-test')); | ||||
| 
 | ||||
|   // // API Key | ||||
|   // final apiKey = BearerAuthenticationClient( | ||||
|   //   token: 'awesome-api-key', | ||||
|   //   authenticationMethod: 'ApiKey', | ||||
|   //   inner: restClient, | ||||
|   // ); | ||||
|   // await apiKey.get(Uri.parse('/test/apikey-test')); | ||||
| 
 | ||||
|   // Unsafe URL | ||||
|   final unsafeAuth = UnsafeAuthMiddleware( | ||||
|     username: 'Mufasa', | ||||
|     password: 'Circle Of Life', | ||||
|   ); | ||||
|   final unsafe = MiddlewareClient( | ||||
|     pipeline: Pipeline.fromIterable([ | ||||
|       uriPrefix, | ||||
|       unsafeAuth, | ||||
|       logger, | ||||
|     ]), | ||||
|   ); | ||||
|   await unsafe.get(Uri.parse('/test/unsafe-test')); | ||||
| 
 | ||||
|   // OAuth2 | ||||
|   final refreshTokenAuth = RefreshTokenAuthMiddleware( | ||||
|     authorizationEndpoint: '/test/oauth2-test?action=login', | ||||
|     tokenEndpoint: '/test/oauth2-test?action=refresh', | ||||
|     accessTokenParser: (body) => body['accessToken']! as String, | ||||
|     refreshTokenParser: (body) => body['refreshToken']! as String, | ||||
|   ); | ||||
|   final refreshToken = MiddlewareClient( | ||||
|     pipeline: Pipeline.fromIterable([ | ||||
|       uriPrefix, | ||||
|       jsonEncoder, | ||||
|       refreshTokenAuth, | ||||
|       logger, | ||||
|     ]), | ||||
|   ); | ||||
|   await refreshToken.get(Uri.parse('/test/oauth2-test')); | ||||
|   // Login | ||||
|   await refreshToken.post( | ||||
|     Uri.parse('/test/oauth2-test'), | ||||
|     body: <String, String>{ | ||||
|       'username': 'username', | ||||
|       'password': 'password', | ||||
|     }, | ||||
|   ); | ||||
|   await refreshToken.get(Uri.parse('/test/oauth2-test')); | ||||
|   // await refreshToken.refresh(); | ||||
|   // await refreshToken.get(Uri.parse('/test/oauth2-test')); | ||||
|   // await refreshToken.get(Uri.parse('/test/oauth2-test?action=access-denied')); | ||||
| 
 | ||||
|   exit(0); | ||||
| } | ||||
| @ -1,371 +0,0 @@ | ||||
| // 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/middleware_client.dart'; | ||||
| import 'package:wyatt_http_client/src/middlewares/body_to_json_middleware.dart'; | ||||
| import 'package:wyatt_http_client/src/middlewares/refresh_token_auth_middleware.dart'; | ||||
| import 'package:wyatt_http_client/src/middlewares/simple_logger_middleware.dart'; | ||||
| import 'package:wyatt_http_client/src/middlewares/uri_prefix_middleware.dart'; | ||||
| import 'package:wyatt_http_client/src/pipeline.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/http_status.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/protocols.dart'; | ||||
| 
 | ||||
| enum EmailVerificationAction { | ||||
|   signUp, | ||||
|   resetPassword, | ||||
|   changeEmail; | ||||
| 
 | ||||
|   String toSnakeCase() => name.splitMapJoin( | ||||
|         RegExp('[A-Z]'), | ||||
|         onMatch: (m) => '_${m[0]?.toLowerCase()}', | ||||
|         onNonMatch: (n) => n, | ||||
|       ); | ||||
| 
 | ||||
|   factory EmailVerificationAction.fromString(String str) => | ||||
|       EmailVerificationAction.values.firstWhere( | ||||
|         (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, | ||||
|   }) => | ||||
|       VerifyCode( | ||||
|         email: email ?? this.email, | ||||
|         verificationCode: verificationCode ?? this.verificationCode, | ||||
|         action: action ?? this.action, | ||||
|       ); | ||||
| 
 | ||||
|   Map<String, dynamic> toMap() => <String, dynamic>{ | ||||
|         'email': email, | ||||
|         'verification_code': verificationCode, | ||||
|         'action': action.toSnakeCase(), | ||||
|       }; | ||||
| 
 | ||||
|   factory VerifyCode.fromMap(Map<String, dynamic> map) => 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, | ||||
|   }) => | ||||
|       Account( | ||||
|         email: email ?? this.email, | ||||
|         sessionId: sessionId ?? this.sessionId, | ||||
|       ); | ||||
| 
 | ||||
|   Map<String, dynamic> toMap() => <String, dynamic>{ | ||||
|         'email': email, | ||||
|         'session_id': sessionId, | ||||
|       }; | ||||
| 
 | ||||
|   factory Account.fromMap(Map<String, dynamic> map) => 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, | ||||
|   }) => | ||||
|       SignUp( | ||||
|         sessionId: sessionId ?? this.sessionId, | ||||
|         password: password ?? this.password, | ||||
|       ); | ||||
| 
 | ||||
|   Map<String, dynamic> toMap() => <String, dynamic>{ | ||||
|         'session_id': sessionId, | ||||
|         'password': password, | ||||
|       }; | ||||
| 
 | ||||
|   factory SignUp.fromMap(Map<String, dynamic> map) => 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, | ||||
|   }) => | ||||
|       TokenSuccess( | ||||
|         accessToken: accessToken ?? this.accessToken, | ||||
|         refreshToken: refreshToken ?? this.refreshToken, | ||||
|         account: account ?? this.account, | ||||
|       ); | ||||
| 
 | ||||
|   Map<String, dynamic> toMap() => <String, dynamic>{ | ||||
|         'access_token': accessToken, | ||||
|         'refresh_token': refreshToken, | ||||
|         'account': account.toMap(), | ||||
|       }; | ||||
| 
 | ||||
|   factory TokenSuccess.fromMap(Map<String, dynamic> map) => 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, | ||||
|   }) => | ||||
|       Login( | ||||
|         email: email ?? this.email, | ||||
|         password: password ?? this.password, | ||||
|       ); | ||||
| 
 | ||||
|   Map<String, dynamic> toMap() => <String, dynamic>{ | ||||
|         'email': email, | ||||
|         'password': password, | ||||
|       }; | ||||
| 
 | ||||
|   factory Login.fromMap(Map<String, dynamic> map) => 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 MiddlewareClient client; | ||||
|   final int apiVersion; | ||||
| 
 | ||||
|   FastAPI({ | ||||
|     this.baseUrl = 'localhost:80', | ||||
|     MiddlewareClient? client, | ||||
|     this.apiVersion = 1, | ||||
|   }) : client = client ?? MiddlewareClient(); | ||||
| 
 | ||||
|   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.post( | ||||
|       Uri.parse('$apiPath/auth/sign-in-with-password'), | ||||
|       body: login.toMap(), | ||||
|     ); | ||||
|     if (r.statusCode != 200) { | ||||
|       throw Exception('Invalid reponse: ${r.statusCode}'); | ||||
|     } else { | ||||
|       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 Pipeline pipeline = Pipeline() | ||||
|       .addMiddleware( | ||||
|         UriPrefixMiddleware( | ||||
|           protocol: Protocols.http, | ||||
|           authority: 'localhost:80', | ||||
|         ), | ||||
|       ) | ||||
|       .addMiddleware(BodyToJsonMiddleware()) | ||||
|       .addMiddleware( | ||||
|         RefreshTokenAuthMiddleware( | ||||
|           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, | ||||
|           unauthorized: HttpStatus.forbidden, | ||||
|         ), | ||||
|       ) | ||||
|       .addMiddleware(SimpleLoggerMiddleware()); | ||||
| 
 | ||||
|   print(pipeline); | ||||
|   final client = MiddlewareClient(pipeline: pipeline); | ||||
| 
 | ||||
|   final api = FastAPI( | ||||
|     client: client, | ||||
|   ); | ||||
| 
 | ||||
|   await api.sendSignUpCode('git@pcl.ovh'); | ||||
|   // final verifiedAccount = await api.verifyCode( | ||||
|   //   VerifyCode( | ||||
|   //     email: 'git@pcl.ovh', | ||||
|   //     verificationCode: '000000000', | ||||
|   //     action: EmailVerificationAction.signUp, | ||||
|   //   ), | ||||
|   // ); | ||||
|   // final registeredAccount = await api.signUp( | ||||
|   //   SignUp(sessionId: verifiedAccount.sessionId ?? '', password: 'password'), | ||||
|   // ); | ||||
|   // final signedInAccount = await api.signInWithPassword( | ||||
|   //   Login(email: 'git@pcl.ovh', password: 'password'), | ||||
|   // ); | ||||
|   final accountList = await api.getAccountList(); | ||||
|   print(accountList); | ||||
| } | ||||
| @ -1,164 +0,0 @@ | ||||
| // ignore_for_file: public_member_api_docs, sort_constructors_first | ||||
| // 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:wyatt_http_client/src/middleware_client.dart'; | ||||
| import 'package:wyatt_http_client/src/middlewares/body_to_json_middleware.dart'; | ||||
| import 'package:wyatt_http_client/src/middlewares/simple_logger_middleware.dart'; | ||||
| import 'package:wyatt_http_client/src/middlewares/unsafe_auth_middleware.dart'; | ||||
| import 'package:wyatt_http_client/src/middlewares/uri_prefix_middleware.dart'; | ||||
| import 'package:wyatt_http_client/src/pipeline.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/protocols.dart'; | ||||
| 
 | ||||
| // class RequestMutatorMiddleware implements Middleware { | ||||
| //   @override | ||||
| //   Middleware? parent; | ||||
| 
 | ||||
| //   @override | ||||
| //   Middleware? child; | ||||
| 
 | ||||
| //   RequestMutatorMiddleware({ | ||||
| //     this.parent, | ||||
| //     this.child, | ||||
| //   }); | ||||
| 
 | ||||
| //   @override | ||||
| //   BaseRequest onRequest(BaseRequest request) { | ||||
| //     print('RequestMutator::OnRequest: ${request.method} -> ${request.url}'); | ||||
| //     return child?.onRequest(request) ?? request; | ||||
| //   } | ||||
| 
 | ||||
| //   @override | ||||
| //   BaseResponse onResponse(BaseResponse response) { | ||||
| //     final res = child?.onResponse(response) ?? response; | ||||
| //     print( | ||||
| //       'RequestMutator::OnResponse: ${res.statusCode} -> ${res.contentLength} | ||||
| // bytes', | ||||
| //     ); | ||||
| //     return res; | ||||
| //   } | ||||
| // } | ||||
| 
 | ||||
| // typedef Middleware = Handler Function(Handler innerHandler); | ||||
| 
 | ||||
| // Middleware createMiddleware({ | ||||
| //   FutureOr<Response?> Function(Request)? requestHandler, | ||||
| //   FutureOr<Response> Function(Response)? responseHandler, | ||||
| //   FutureOr<Response> Function(Object error, StackTrace)? errorHandler, | ||||
| // }) { | ||||
| //   requestHandler ??= (request) => null; | ||||
| //   responseHandler ??= (response) => response; | ||||
| 
 | ||||
| //   FutureOr<Response> Function(Object, StackTrace)? onError; | ||||
| //   if (errorHandler != null) { | ||||
| //     onError = (error, stackTrace) { | ||||
| //       if (error is Exception) throw error; | ||||
| //       return errorHandler(error, stackTrace); | ||||
| //     }; | ||||
| //   } | ||||
| 
 | ||||
| //   return (Handler innerHandler) { | ||||
| //     return (request) { | ||||
| //       return Future.sync(() => requestHandler!(request)).then((response) { | ||||
| //         if (response != null) return response; | ||||
| 
 | ||||
| //         return Future.sync(() => innerHandler(request)) | ||||
| //             .then((response) => responseHandler!(response), onError: | ||||
| // onError); | ||||
| //       }); | ||||
| //     }; | ||||
| //   }; | ||||
| // } | ||||
| 
 | ||||
| // extension MiddlewareX on Middleware { | ||||
| //   Middleware addMiddleware(Middleware other) => | ||||
| //       (Handler handler) => this(other(handler)); | ||||
| //   Handler addHandler(Handler handler) => this(handler); | ||||
| // } | ||||
| 
 | ||||
| // typedef Handler = FutureOr<Response> Function(Request request); | ||||
| 
 | ||||
| // final headerMutator = createMiddleware( | ||||
| //   responseHandler: (response) { | ||||
| //     print(response.headers); | ||||
| //     return response; | ||||
| //   },); | ||||
| 
 | ||||
| // class Pipeline { | ||||
| //   const Pipeline(); | ||||
| 
 | ||||
| //   Pipeline addMiddleware(Middleware middleware) => | ||||
| //       _Pipeline(middleware, addHandler); | ||||
| 
 | ||||
| //   Handler addHandler(Handler handler) => handler; | ||||
| 
 | ||||
| //   Middleware get middleware => addHandler; | ||||
| // } | ||||
| 
 | ||||
| // class _Pipeline extends Pipeline { | ||||
| //   final Middleware _middleware; | ||||
| //   final Middleware _parent; | ||||
| 
 | ||||
| //   _Pipeline(this._middleware, this._parent); | ||||
| 
 | ||||
| //   @override | ||||
| //   Handler addHandler(Handler handler) => _parent(_middleware(handler)); | ||||
| // } | ||||
| 
 | ||||
| Future<void> main(List<String> args) async { | ||||
|   final UnsafeAuthMiddleware auth = UnsafeAuthMiddleware(); | ||||
|   final Pipeline pipeline = Pipeline() | ||||
|       .addMiddleware( | ||||
|         UriPrefixMiddleware( | ||||
|           protocol: Protocols.http, | ||||
|           authority: 'localhost:80', | ||||
|         ), | ||||
|       ) | ||||
|       .addMiddleware(BodyToJsonMiddleware()) | ||||
|       .addMiddleware( | ||||
|         UnsafeAuthMiddleware( | ||||
|           username: 'wyatt', | ||||
|           password: 'motdepasse', | ||||
|         ), | ||||
|       ) | ||||
|       .addMiddleware(SimpleLoggerMiddleware()); | ||||
|   // .addMiddleware( | ||||
|   //   RefreshTokenMiddleware( | ||||
|   //     authorizationEndpoint: '/api/v1/account/test?action=authorize', | ||||
|   //     tokenEndpoint: '/api/v1/account/test?action=refresh', | ||||
|   //     accessTokenParser: (body) => body['access_token']! as String, | ||||
|   //     refreshTokenParser: (body) => body['refresh_token']! as String, | ||||
|   //   ), | ||||
|   // ); | ||||
| 
 | ||||
|   print(pipeline); | ||||
|   final client = MiddlewareClient(pipeline: pipeline); | ||||
|   await client.post( | ||||
|     Uri.parse('/api/v1/account/test'), | ||||
|     body: <String, String>{ | ||||
|       'email': 'test@test.fr', | ||||
|     }, | ||||
|   ); | ||||
|   auth | ||||
|     ..username = 'username' | ||||
|     ..password = 'password'; | ||||
|   await client.post( | ||||
|     Uri.parse('/api/v1/account/test'), | ||||
|     body: <String, String>{ | ||||
|       'email': 'test@test.fr', | ||||
|     }, | ||||
|   ); | ||||
| } | ||||
| @ -18,12 +18,21 @@ import 'package:wyatt_http_client/src/models/middleware_context.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_response.dart'; | ||||
| 
 | ||||
| /// {@template middleware} | ||||
| /// A middleware is a class that can intercept requests and responses | ||||
| /// and modify them before they are sent to the server or before they | ||||
| /// are returned to the client. | ||||
| /// {@endtemplate} | ||||
| abstract class Middleware { | ||||
|   Middleware(); | ||||
|   /// {@macro middleware} | ||||
|   const Middleware(); | ||||
| 
 | ||||
|   /// The name of the middleware. | ||||
|   String getName(); | ||||
| } | ||||
| 
 | ||||
| mixin OnRequestMiddleware { | ||||
|   /// Performs an action before the request is sent to the server. | ||||
|   Future<MiddlewareRequest> onRequest( | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareRequest request, | ||||
| @ -31,6 +40,7 @@ mixin OnRequestMiddleware { | ||||
| } | ||||
| 
 | ||||
| mixin OnResponseMiddleware { | ||||
|   /// Performs an action before the response is returned to the client. | ||||
|   Future<MiddlewareResponse> onResponse( | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareResponse response, | ||||
|  | ||||
| @ -24,15 +24,23 @@ import 'package:wyatt_http_client/src/models/unfreezed_request.dart'; | ||||
| import 'package:wyatt_http_client/src/pipeline.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/http_methods.dart'; | ||||
| 
 | ||||
| /// {@template middleware_client} | ||||
| /// A custom [Client] implementation that allows you to intercept requests | ||||
| /// and responses and modify them before they are sent to the server or | ||||
| /// before they are returned to the client. | ||||
| /// {@endtemplate} | ||||
| class MiddlewareClient extends BaseClient { | ||||
|   /// {@macro middleware_client} | ||||
|   MiddlewareClient({ | ||||
|     Pipeline? pipeline, | ||||
|     Client? inner, | ||||
|   })  : pipeline = pipeline ?? Pipeline(), | ||||
|         inner = inner ?? Client() { | ||||
|     print('Using Pipeline:\n$pipeline'); | ||||
|   } | ||||
|         inner = inner ?? Client(); | ||||
| 
 | ||||
|   /// The [Client] that will be used to send requests. | ||||
|   final Client inner; | ||||
| 
 | ||||
|   /// The [Pipeline] that will be used to intercept requests and responses. | ||||
|   final Pipeline pipeline; | ||||
| 
 | ||||
|   @override | ||||
|  | ||||
| @ -1,15 +0,0 @@ | ||||
| // 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/>. | ||||
| @ -22,14 +22,24 @@ import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/authentication_methods.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/header_keys.dart'; | ||||
| 
 | ||||
| /// {@template basic_auth_middleware} | ||||
| /// A middleware that adds basic authentication to the request. | ||||
| /// {@endtemplate} | ||||
| class BasicAuthMiddleware with OnRequestMiddleware implements Middleware { | ||||
|   BasicAuthMiddleware({ | ||||
|   /// {@macro basic_auth_middleware} | ||||
|   const BasicAuthMiddleware({ | ||||
|     this.username, | ||||
|     this.password, | ||||
|     this.authenticationHeader = HeaderKeys.authorization, | ||||
|   }); | ||||
|   String? username; | ||||
|   String? password; | ||||
| 
 | ||||
|   /// The username to use for authentication. | ||||
|   final String? username; | ||||
| 
 | ||||
|   /// The password to use for authentication. | ||||
|   final String? password; | ||||
| 
 | ||||
|   /// The header to use for authentication. | ||||
|   final String authenticationHeader; | ||||
| 
 | ||||
|   @override | ||||
| @ -43,10 +53,7 @@ class BasicAuthMiddleware with OnRequestMiddleware implements Middleware { | ||||
|     if (username == null || password == null) { | ||||
|       return request; | ||||
|     } | ||||
|     print( | ||||
|       '${getName()}::OnRequest\n' | ||||
|       '>> Basic: ${base64Encode(utf8.encode('$username:$password'))}', | ||||
|     ); | ||||
| 
 | ||||
|     final mutation = { | ||||
|       authenticationHeader: '${AuthenticationMethods.basic} ' | ||||
|           '${base64Encode(utf8.encode('$username:$password'))}', | ||||
|  | ||||
| @ -20,7 +20,13 @@ import 'package:wyatt_http_client/src/middleware.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_context.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||
| 
 | ||||
| /// {@template body_to_json_middleware} | ||||
| /// A middleware that transforms the body in json if it's a [Map]. | ||||
| /// {@endtemplate} | ||||
| class BodyToJsonMiddleware with OnRequestMiddleware implements Middleware { | ||||
|   /// {@macro body_to_json_middleware} | ||||
|   const BodyToJsonMiddleware(); | ||||
| 
 | ||||
|   @override | ||||
|   String getName() => 'BodyToJson'; | ||||
| 
 | ||||
| @ -29,11 +35,6 @@ class BodyToJsonMiddleware with OnRequestMiddleware implements Middleware { | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareRequest request, | ||||
|   ) async { | ||||
|     print( | ||||
|       '${getName()}::OnRequest\n' | ||||
|       '>> Transforms body in json if Map then update ' | ||||
|       'headers with right content-type', | ||||
|     ); | ||||
|     final mutation = { | ||||
|       'content-type': 'application/json; charset=utf-8', | ||||
|     }; | ||||
|  | ||||
| @ -16,7 +16,13 @@ | ||||
| 
 | ||||
| import 'package:wyatt_http_client/src/middleware.dart'; | ||||
| 
 | ||||
| /// {@template default_middleware} | ||||
| /// A default middleware that does nothing. | ||||
| /// {@endtemplate} | ||||
| class DefaultMiddleware implements Middleware { | ||||
|   /// {@macro default_middleware} | ||||
|   const DefaultMiddleware(); | ||||
| 
 | ||||
|   @override | ||||
|   String getName() => 'DefaultMiddleware'; | ||||
| } | ||||
|  | ||||
| @ -22,9 +22,13 @@ import 'package:wyatt_http_client/src/utils/digest_auth.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/header_keys.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/http_status.dart'; | ||||
| 
 | ||||
| /// {@template digest_auth_middleware} | ||||
| /// A middleware that handles digest authentication. | ||||
| /// {@endtemplate} | ||||
| class DigestAuthMiddleware | ||||
|     with OnRequestMiddleware, OnResponseMiddleware | ||||
|     implements Middleware { | ||||
|   /// {@macro digest_auth_middleware} | ||||
|   DigestAuthMiddleware({ | ||||
|     required this.username, | ||||
|     required this.password, | ||||
| @ -47,10 +51,6 @@ class DigestAuthMiddleware | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareRequest request, | ||||
|   ) async { | ||||
|     print( | ||||
|       '${getName()}::OnRequest\n' | ||||
|       '>> Digest ready: ${_digestAuth.isReady()}', | ||||
|     ); | ||||
|     if (_digestAuth.isReady()) { | ||||
|       final mutation = { | ||||
|         authenticationHeader: _digestAuth.getAuthString( | ||||
| @ -82,10 +82,6 @@ class DigestAuthMiddleware | ||||
|         return MiddlewareResponse(httpResponse: newResponse); | ||||
|       } | ||||
|     } | ||||
|     print( | ||||
|       '${getName()}::OnResponse\n' | ||||
|       '>> Digest ready: ${_digestAuth.isReady()}', | ||||
|     ); | ||||
|     return response; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -14,7 +14,8 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| export 'access_token_auth_middleware.dart'; | ||||
| // All built-in middlewares | ||||
| 
 | ||||
| export 'basic_auth_middleware.dart'; | ||||
| export 'body_to_json_middleware.dart'; | ||||
| export 'default_middleware.dart'; | ||||
|  | ||||
| @ -28,9 +28,14 @@ import 'package:wyatt_http_client/src/utils/http_status.dart'; | ||||
| 
 | ||||
| typedef TokenParser = String Function(Map<String, dynamic>); | ||||
| 
 | ||||
| /// {@template refresh_token_auth_middleware} | ||||
| /// A middleware that refreshes the access token when it expires. | ||||
| /// This middleware is useful for OAuth2. | ||||
| /// {@endtemplate} | ||||
| class RefreshTokenAuthMiddleware | ||||
|     with OnRequestMiddleware, OnResponseMiddleware | ||||
|     implements Middleware { | ||||
|   /// {@macro refresh_token_auth_middleware} | ||||
|   RefreshTokenAuthMiddleware({ | ||||
|     required this.authorizationEndpoint, | ||||
|     required this.tokenEndpoint, | ||||
| @ -113,11 +118,6 @@ class RefreshTokenAuthMiddleware | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareRequest request, | ||||
|   ) async { | ||||
|     print( | ||||
|       '${getName()}::OnRequest\n' | ||||
|       '>> accessToken: $accessToken\n' | ||||
|       '>> refreshToken: $refreshToken', | ||||
|     ); | ||||
|     // Check if it is authorization | ||||
|     if (context.originalRequest?.url == Uri.parse(authorizationEndpoint)) { | ||||
|       return request; | ||||
| @ -168,12 +168,6 @@ class RefreshTokenAuthMiddleware | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     print( | ||||
|       '${getName()}::OnResponse\n' | ||||
|       '>> accessToken: $accessToken\n' | ||||
|       '>> refreshToken: $refreshToken', | ||||
|     ); | ||||
| 
 | ||||
|     if (response.status == unauthorized) { | ||||
|       // Refresh | ||||
|       MiddlewareRequest? newRequest = await refresh(context); | ||||
|  | ||||
| @ -19,9 +19,15 @@ import 'package:wyatt_http_client/src/models/middleware_context.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_response.dart'; | ||||
| 
 | ||||
| /// {@template simple_logger_middleware} | ||||
| /// A simple logger middleware that logs the request and response. | ||||
| /// {@endtemplate} | ||||
| class SimpleLoggerMiddleware | ||||
|     with OnRequestMiddleware, OnResponseMiddleware | ||||
|     implements Middleware { | ||||
|   /// {@macro simple_logger_middleware} | ||||
|   const SimpleLoggerMiddleware(); | ||||
| 
 | ||||
|   @override | ||||
|   String getName() => 'SimpleLogger'; | ||||
| 
 | ||||
| @ -30,11 +36,22 @@ class SimpleLoggerMiddleware | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareRequest request, | ||||
|   ) async { | ||||
|     print( | ||||
|       '${getName()}::OnRequest\n' | ||||
|       '>> ${request.method} ${request.url}\n' | ||||
|       '>> Headers: ${request.headers}\n>> Body: ${request.encodedBody}', | ||||
|     ); | ||||
|     final log = StringBuffer() | ||||
|       ..writeln('${getName()}::OnRequest') | ||||
|       ..writeln('>> ${request.method} ${request.url}'); | ||||
|     if (request.headers.isNotEmpty) { | ||||
|       log.writeln('>> Headers:'); | ||||
|       request.headers.forEach((key, value) { | ||||
|         log.writeln('>>   $key: $value'); | ||||
|       }); | ||||
|     } | ||||
|     if (request.encodedBody.isNotEmpty) { | ||||
|       log | ||||
|         ..writeln('>> Body:') | ||||
|         ..writeln(request.encodedBody); | ||||
|     } | ||||
|     print(log); | ||||
| 
 | ||||
|     return request; | ||||
|   } | ||||
| 
 | ||||
| @ -43,12 +60,13 @@ class SimpleLoggerMiddleware | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareResponse response, | ||||
|   ) async { | ||||
|     print( | ||||
|       '${getName()}::OnResponse\n' | ||||
|       '>> Status: ${response.status.name.toUpperCase()}\n' | ||||
|       '>> Length: ${response.contentLength ?? '0'} bytes', | ||||
|       // '>> Body: ${response.body}', | ||||
|     ); | ||||
|     final log = StringBuffer() | ||||
|       ..writeln('${getName()}::OnResponse') | ||||
|       ..writeln('>> Status: ${response.status.name.toUpperCase()}') | ||||
|       ..writeln('>> Length: ${response.contentLength ?? '0'} bytes'); | ||||
| 
 | ||||
|     print(log); | ||||
| 
 | ||||
|     return response; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -19,15 +19,20 @@ import 'package:wyatt_http_client/src/models/middleware_context.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/convert.dart'; | ||||
| 
 | ||||
| /// {@template unsafe_auth_middleware} | ||||
| /// A middleware that appends the username and password to the URL. | ||||
| /// | ||||
| /// This is not recommended to use in production. | ||||
| /// {@endtemplate} | ||||
| class UnsafeAuthMiddleware with OnRequestMiddleware implements Middleware { | ||||
|   UnsafeAuthMiddleware({ | ||||
|   const UnsafeAuthMiddleware({ | ||||
|     this.username, | ||||
|     this.password, | ||||
|     this.usernameField = 'username', | ||||
|     this.passwordField = 'password', | ||||
|   }); | ||||
|   String? username; | ||||
|   String? password; | ||||
|   final String? username; | ||||
|   final String? password; | ||||
| 
 | ||||
|   final String usernameField; | ||||
|   final String passwordField; | ||||
| @ -45,10 +50,6 @@ class UnsafeAuthMiddleware with OnRequestMiddleware implements Middleware { | ||||
|     } | ||||
|     final Uri uri = | ||||
|         request.url + '?$usernameField=$username&$passwordField=$password'; | ||||
|     print( | ||||
|       '${getName()}::OnRequest\n' | ||||
|       '>> Append: ?$usernameField=$username&$passwordField=$password', | ||||
|     ); | ||||
|     request.modifyRequest(request.unfreezedRequest.copyWith(url: uri)); | ||||
|     return request; | ||||
|   } | ||||
|  | ||||
| @ -19,12 +19,20 @@ import 'package:wyatt_http_client/src/models/middleware_context.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/protocols.dart'; | ||||
| 
 | ||||
| /// {@template uri_prefix_middleware} | ||||
| /// A middleware that adds a prefix to the request's URI. | ||||
| /// {@endtemplate} | ||||
| class UriPrefixMiddleware with OnRequestMiddleware implements Middleware { | ||||
|   UriPrefixMiddleware({ | ||||
|   /// {@macro uri_prefix_middleware} | ||||
|   const UriPrefixMiddleware({ | ||||
|     required this.protocol, | ||||
|     required this.authority, | ||||
|   }); | ||||
| 
 | ||||
|   /// The protocol of the prefix. | ||||
|   final Protocols protocol; | ||||
| 
 | ||||
|   /// The authority of the prefix. | ||||
|   final String? authority; | ||||
| 
 | ||||
|   @override | ||||
| @ -36,11 +44,6 @@ class UriPrefixMiddleware with OnRequestMiddleware implements Middleware { | ||||
|     MiddlewareRequest request, | ||||
|   ) async { | ||||
|     final Uri uri = Uri.parse('${protocol.scheme}$authority${request.url}'); | ||||
|     print( | ||||
|       '${getName()}::OnRequest\n' | ||||
|       '>> From: ${request.url}\n' | ||||
|       '>> To: $uri', | ||||
|     ); | ||||
|     request.modifyRequest(request.unfreezedRequest.copyWith(url: uri)); | ||||
|     return request; | ||||
|   } | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| // ignore_for_file: public_member_api_docs, sort_constructors_first | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| @ -20,15 +19,12 @@ import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_response.dart'; | ||||
| import 'package:wyatt_http_client/src/pipeline.dart'; | ||||
| 
 | ||||
| /// {@template middleware_context} | ||||
| /// A class that contains the context of the middleware. | ||||
| /// {@endtemplate} | ||||
| class MiddlewareContext { | ||||
|   Pipeline pipeline; | ||||
|   MiddlewareClient client; | ||||
|   MiddlewareRequest? originalRequest; | ||||
|   MiddlewareRequest? lastRequest; | ||||
|   MiddlewareResponse? originalResponse; | ||||
|   MiddlewareResponse? lastResponse; | ||||
| 
 | ||||
|   MiddlewareContext({ | ||||
|   /// {@macro middleware_context} | ||||
|   const MiddlewareContext({ | ||||
|     required this.pipeline, | ||||
|     required this.client, | ||||
|     this.originalRequest, | ||||
| @ -37,6 +33,26 @@ class MiddlewareContext { | ||||
|     this.lastResponse, | ||||
|   }); | ||||
| 
 | ||||
|   /// The pipeline that the middleware is in. | ||||
|   final Pipeline pipeline; | ||||
| 
 | ||||
|   /// The client that the middleware is in. | ||||
|   final MiddlewareClient client; | ||||
| 
 | ||||
|   /// The original request that the middleware is in. | ||||
|   final MiddlewareRequest? originalRequest; | ||||
| 
 | ||||
|   /// The last request that the middleware is in. | ||||
|   final MiddlewareRequest? lastRequest; | ||||
| 
 | ||||
|   /// The original response that the middleware is in. | ||||
|   final MiddlewareResponse? originalResponse; | ||||
| 
 | ||||
|   /// The last response that the middleware is in. | ||||
|   final MiddlewareResponse? lastResponse; | ||||
| 
 | ||||
|   /// Create a copy of this [MiddlewareContext] with the given fields replaced | ||||
|   /// with the new values. | ||||
|   MiddlewareContext copyWith({ | ||||
|     Pipeline? pipeline, | ||||
|     MiddlewareClient? client, | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| // ignore_for_file: public_member_api_docs, sort_constructors_first | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| @ -22,24 +21,43 @@ import 'package:wyatt_http_client/src/models/unfreezed_request.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/convert.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/request_utils.dart'; | ||||
| 
 | ||||
| /// {@template middleware_request} | ||||
| /// A class that represents a middleware request. | ||||
| /// {@endtemplate} | ||||
| class MiddlewareRequest { | ||||
|   UnfreezedRequest unfreezedRequest; | ||||
|   Request _httpRequest; | ||||
| 
 | ||||
|   Request get request => _httpRequest; | ||||
| 
 | ||||
|   // Proxy | ||||
|   String get method => _httpRequest.method; | ||||
|   Uri get url => _httpRequest.url; | ||||
|   Map<String, String> get headers => _httpRequest.headers; | ||||
|   Encoding get encoding => _httpRequest.encoding; | ||||
|   String get encodedBody => _httpRequest.body; | ||||
|   Object? get body => unfreezedRequest.body; | ||||
| 
 | ||||
|   /// {@macro middleware_request} | ||||
|   MiddlewareRequest({ | ||||
|     required this.unfreezedRequest, | ||||
|   }) : _httpRequest = Request(unfreezedRequest.method, unfreezedRequest.url); | ||||
| 
 | ||||
|   /// The unfreezed request. | ||||
|   UnfreezedRequest unfreezedRequest; | ||||
| 
 | ||||
|   Request _httpRequest; | ||||
| 
 | ||||
|   /// The http request. (Read-only) | ||||
|   Request get request => _httpRequest; | ||||
| 
 | ||||
|   /// The request method (proxy, read-only). | ||||
|   String get method => _httpRequest.method; | ||||
| 
 | ||||
|   /// The request url (proxy, read-only). | ||||
|   Uri get url => _httpRequest.url; | ||||
| 
 | ||||
|   /// The request headers (proxy, read-only). | ||||
|   Map<String, String> get headers => _httpRequest.headers; | ||||
| 
 | ||||
|   /// The request body (proxy, read-only). | ||||
|   Encoding get encoding => _httpRequest.encoding; | ||||
| 
 | ||||
|   /// The request body (proxy, read-only). | ||||
|   String get encodedBody => _httpRequest.body; | ||||
| 
 | ||||
|   /// The request body (proxy, read-only). | ||||
|   Object? get body => unfreezedRequest.body; | ||||
| 
 | ||||
|   /// Copies this request and returns a new request with the given | ||||
|   /// [unfreezedRequest]. | ||||
|   MiddlewareRequest copyWith({ | ||||
|     UnfreezedRequest? unfreezedRequest, | ||||
|   }) => | ||||
| @ -47,6 +65,7 @@ class MiddlewareRequest { | ||||
|         unfreezedRequest: unfreezedRequest ?? this.unfreezedRequest, | ||||
|       ); | ||||
| 
 | ||||
|   /// Modifies the request with the given [unfreezedRequest]. | ||||
|   void modifyRequest(UnfreezedRequest unfreezedRequest) { | ||||
|     String? body; | ||||
|     if (unfreezedRequest.body != null) { | ||||
| @ -72,6 +91,8 @@ class MiddlewareRequest { | ||||
|     this.unfreezedRequest = unfreezedRequest; | ||||
|   } | ||||
| 
 | ||||
|   /// Applies the changes made to the request by modifying it with the | ||||
|   /// [unfreezedRequest]. | ||||
|   void apply() { | ||||
|     modifyRequest(unfreezedRequest); | ||||
|   } | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| // ignore_for_file: public_member_api_docs, sort_constructors_first | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| @ -18,12 +17,25 @@ | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/http_status.dart'; | ||||
| 
 | ||||
| /// {@template middleware_response} | ||||
| /// A class that represents a middleware response. | ||||
| /// {@endtemplate} | ||||
| class MiddlewareResponse { | ||||
|   BaseResponse httpResponse; | ||||
|   /// {@macro middleware_response} | ||||
|   const MiddlewareResponse({ | ||||
|     required this.httpResponse, | ||||
|   }); | ||||
| 
 | ||||
|   // Proxy | ||||
|   /// {@macro middleware_response} | ||||
|   final BaseResponse httpResponse; | ||||
| 
 | ||||
|   /// The status code of the response. (proxy) | ||||
|   int get statusCode => httpResponse.statusCode; | ||||
| 
 | ||||
|   /// The status of the response. (proxy) | ||||
|   HttpStatus get status => HttpStatus.from(statusCode); | ||||
| 
 | ||||
|   /// The body of the response. (proxy or empty string) | ||||
|   String get body { | ||||
|     if (httpResponse is Response) { | ||||
|       return (httpResponse as Response).body; | ||||
| @ -32,13 +44,13 @@ class MiddlewareResponse { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /// The content length of the response. (proxy) | ||||
|   int? get contentLength => httpResponse.contentLength; | ||||
| 
 | ||||
|   /// The headers of the response. (proxy) | ||||
|   Map<String, String> get headers => httpResponse.headers; | ||||
| 
 | ||||
|   MiddlewareResponse({ | ||||
|     required this.httpResponse, | ||||
|   }); | ||||
| 
 | ||||
|   /// Returns a copy of this response with the given [httpResponse]. | ||||
|   MiddlewareResponse copyWith({ | ||||
|     BaseResponse? httpResponse, | ||||
|   }) => | ||||
|  | ||||
| @ -16,20 +16,38 @@ | ||||
| 
 | ||||
| import 'dart:convert'; | ||||
| 
 | ||||
| /// {@template unfreezed_request} | ||||
| /// A class that represents an unfreezed request. | ||||
| /// It is used to unfreeze a Request object, and allows you to | ||||
| /// modify the request before sending it. | ||||
| /// {@endtemplate} | ||||
| class UnfreezedRequest { | ||||
|   UnfreezedRequest({ | ||||
|   /// {@macro unfreezed_request} | ||||
|   const UnfreezedRequest({ | ||||
|     required this.method, | ||||
|     required this.url, | ||||
|     this.headers, | ||||
|     this.body, | ||||
|     this.encoding, | ||||
|   }); | ||||
| 
 | ||||
|   /// The request method. | ||||
|   final String method; | ||||
| 
 | ||||
|   /// The request url. | ||||
|   final Uri url; | ||||
| 
 | ||||
|   /// The request headers. | ||||
|   final Map<String, String>? headers; | ||||
| 
 | ||||
|   /// The request body. | ||||
|   final Object? body; | ||||
| 
 | ||||
|   /// The request encoding. | ||||
|   final Encoding? encoding; | ||||
| 
 | ||||
|   /// Copies this request and returns a new request with the given [method], | ||||
|   /// [url], [headers], [body] and [encoding]. | ||||
|   UnfreezedRequest copyWith({ | ||||
|     String? method, | ||||
|     Uri? url, | ||||
|  | ||||
| @ -19,20 +19,27 @@ import 'package:wyatt_http_client/src/models/middleware_context.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_response.dart'; | ||||
| 
 | ||||
| /// {@template pipeline} | ||||
| /// A [Pipeline] is a list of [Middleware]s that are executed in order. | ||||
| /// {@endtemplate} | ||||
| class Pipeline { | ||||
|   /// {@macro pipeline} | ||||
|   Pipeline() : _middlewares = <Middleware>[]; | ||||
| 
 | ||||
|   /// {@macro pipeline} | ||||
|   Pipeline.fromIterable(Iterable<Middleware> middlewares) | ||||
|       : _middlewares = middlewares.toList(); | ||||
|    | ||||
|   final List<Middleware> _middlewares; | ||||
| 
 | ||||
|   /// The length of the [Pipeline]. | ||||
|   /// | ||||
|   /// This is the number of [Middleware]s in the [Pipeline]. | ||||
|   int get length => _middlewares.length; | ||||
| 
 | ||||
|   /// Add a [Middleware] to this [Pipeline] | ||||
|   Pipeline addMiddleware(Middleware middleware) { | ||||
|   void addMiddleware(Middleware middleware) { | ||||
|     _middlewares.add(middleware); | ||||
|     // TODO(hpcl): use Dart cascades instead of returning this | ||||
|     // ignore: avoid_returning_this | ||||
|     return this; | ||||
|   } | ||||
| 
 | ||||
|   /// Create new [Pipeline] from the start or end to a specified [Middleware]. | ||||
| @ -57,11 +64,15 @@ class Pipeline { | ||||
|     return Pipeline.fromIterable(fromEnd ? nodes.reversed : nodes); | ||||
|   } | ||||
| 
 | ||||
|   /// Call the [onRequest] method of all [OnRequestMiddleware]s in the | ||||
|   /// [Pipeline]. | ||||
|   /// | ||||
|   /// The [MiddlewareRequest] returned by the last [OnRequestMiddleware] is | ||||
|   /// returned. | ||||
|   Future<MiddlewareRequest> onRequest( | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareRequest request, | ||||
|   ) async { | ||||
|     print('\n\nNEW REQUEST\n'); | ||||
|     MiddlewareRequest req = request..apply(); | ||||
|     MiddlewareContext ctx = context.copyWith(lastRequest: req); | ||||
|     for (final middleware in _middlewares) { | ||||
| @ -73,11 +84,15 @@ class Pipeline { | ||||
|     return req; | ||||
|   } | ||||
| 
 | ||||
|   /// Call the [onResponse] method of all [OnResponseMiddleware]s in the | ||||
|   /// [Pipeline]. | ||||
|   /// | ||||
|   /// The [MiddlewareResponse] returned by the last [OnResponseMiddleware] is | ||||
|   /// returned. | ||||
|   Future<MiddlewareResponse> onResponse( | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareResponse response, | ||||
|   ) async { | ||||
|     print('\n\nNEW RESPONSE\n'); | ||||
|     MiddlewareResponse res = response; | ||||
|     MiddlewareContext ctx = context.copyWith(lastResponse: res); | ||||
|     for (final middleware in _middlewares.reversed) { | ||||
|  | ||||
| @ -14,8 +14,14 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| /// Defines some authentication methods | ||||
| abstract class AuthenticationMethods { | ||||
|   /// The `Basic` authentication method. | ||||
|   static const String basic = 'Basic'; | ||||
| 
 | ||||
|   /// The `Bearer` authentication method. | ||||
|   static const String bearer = 'Bearer'; | ||||
| 
 | ||||
|   /// The `Digest` authentication method. | ||||
|   static const String digest = 'Digest'; | ||||
| } | ||||
|  | ||||
| @ -16,7 +16,11 @@ | ||||
| 
 | ||||
| import 'dart:convert'; | ||||
| 
 | ||||
| class Convert { | ||||
| /// Defines some convert functions. | ||||
| abstract class Convert { | ||||
|   /// Converts a list of bytes to a hex string. | ||||
|   /// | ||||
|   /// If [upperCase] is `true`, the hex string will be in uppercase. | ||||
|   static String toHex(List<int> bytes, {bool upperCase = false}) { | ||||
|     final buffer = StringBuffer(); | ||||
|     for (final int part in bytes) { | ||||
| @ -32,6 +36,11 @@ class Convert { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /// Converts a map to a query string. | ||||
|   /// | ||||
|   /// If [encoding] is `null`, the default encoding is `utf8`. | ||||
|   /// | ||||
|   /// For example, the map `{a: 1, b: 2}` will be converted to `a=1&b=2`. | ||||
|   static String mapToQuery(Map<String, String> map, {Encoding? encoding}) { | ||||
|     final pairs = <List<String>>[]; | ||||
|     map.forEach( | ||||
| @ -45,6 +54,7 @@ class Convert { | ||||
| } | ||||
| 
 | ||||
| extension UriX on Uri { | ||||
|   /// Returns a new [Uri] by appending the given [path] to this [Uri]. | ||||
|   Uri operator +(String path) { | ||||
|     final thisPath = toString(); | ||||
|     return Uri.parse(thisPath + path); | ||||
|  | ||||
| @ -18,7 +18,8 @@ import 'dart:convert'; | ||||
| 
 | ||||
| import 'package:crypto/crypto.dart'; | ||||
| 
 | ||||
| class Crypto { | ||||
| /// Defines some crypto functions. | ||||
| abstract class Crypto { | ||||
|   /// Hash a string using MD5 | ||||
|   static String md5Hash(String data) { | ||||
|     final content = const Utf8Encoder().convert(data); | ||||
|  | ||||
| @ -17,7 +17,9 @@ | ||||
| import 'dart:core'; | ||||
| import 'dart:math'; | ||||
| 
 | ||||
| /// Defines some delay functions. | ||||
| abstract class Delay { | ||||
|   /// Returns a delay based on the [attempt]. | ||||
|   static Duration getRetryDelay(int attempt) { | ||||
|     assert(attempt >= 0, 'attempt cannot be negative'); | ||||
|     if (attempt <= 0) { | ||||
|  | ||||
| @ -19,12 +19,13 @@ import 'dart:math'; | ||||
| import 'package:wyatt_http_client/src/utils/convert.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/crypto.dart'; | ||||
| 
 | ||||
| /// A class for digest authentication. | ||||
| class DigestAuth { | ||||
|   // request counter | ||||
| 
 | ||||
|   DigestAuth(this.username, this.password); | ||||
|   String username; | ||||
|   String password; | ||||
|   final String username; | ||||
|   final String password; | ||||
| 
 | ||||
|   // must get from first response | ||||
|   String? _algorithm; | ||||
|  | ||||
| @ -14,8 +14,14 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| /// Defines some header keys. | ||||
| abstract class HeaderKeys { | ||||
|   /// The `Authorization` header key. | ||||
|   static const String authorization = 'Authorization'; | ||||
| 
 | ||||
|   /// The `WWW-Authenticate` header key. | ||||
|   static const String wwwAuthenticate = 'WWW-Authenticate'; | ||||
| 
 | ||||
|   /// The `Content-Type` header key. | ||||
|   static const String contentType = 'Content-Type'; | ||||
| } | ||||
|  | ||||
| @ -14,6 +14,7 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| /// Defines http verb methods. | ||||
| enum HttpMethods { | ||||
|   head('HEAD'), | ||||
|   get('GET'), | ||||
| @ -24,5 +25,8 @@ enum HttpMethods { | ||||
| 
 | ||||
|   const HttpMethods(this.method); | ||||
| 
 | ||||
|   /// Returns the method of the http verb. | ||||
|   /// | ||||
|   /// For example, the method of [HttpMethods.get] is `GET`. | ||||
|   final String method; | ||||
| } | ||||
|  | ||||
| @ -83,6 +83,7 @@ enum HttpStatus { | ||||
| 
 | ||||
|   const HttpStatus(this.statusCode); | ||||
| 
 | ||||
|   /// Returns the [HttpStatus] with the given [statusCode]. | ||||
|   factory HttpStatus.from(int status) => | ||||
|       HttpStatus.values.firstWhere((element) => element.statusCode == status); | ||||
| 
 | ||||
| @ -98,13 +99,18 @@ enum HttpStatus { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   /// Checks if the status code is in the range of 100-199. | ||||
|   bool isInfo() => statusCode >= 100 && statusCode < 200; | ||||
| 
 | ||||
|   /// Checks if the status code is in the range of 200-299. | ||||
|   bool isSuccess() => statusCode >= 200 && statusCode < 300; | ||||
| 
 | ||||
|   /// Checks if the status code is in the range of 300-399. | ||||
|   bool isRedirection() => statusCode >= 300 && statusCode < 400; | ||||
| 
 | ||||
|   /// Checks if the status code is in the range of 400-499. | ||||
|   bool isClientError() => statusCode >= 400 && statusCode < 500; | ||||
| 
 | ||||
|   /// Checks if the status code is in the range of 500-599. | ||||
|   bool isServerError() => statusCode >= 500 && statusCode < 600; | ||||
| } | ||||
|  | ||||
| @ -14,9 +14,13 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| /// Defines few protocols | ||||
| enum Protocols { | ||||
|   http, | ||||
|   https; | ||||
| 
 | ||||
|   /// Returns the scheme of the protocol. | ||||
|   /// | ||||
|   /// For example, the scheme of [Protocols.http] is `http://`. | ||||
|   String get scheme => '$name://'; | ||||
| } | ||||
|  | ||||
| @ -16,6 +16,7 @@ | ||||
| 
 | ||||
| import 'package:http/http.dart'; | ||||
| 
 | ||||
| /// Defines some request utils. | ||||
| abstract class RequestUtils { | ||||
|   static Request _copyNormalRequestWith( | ||||
|     Request original, { | ||||
| @ -38,6 +39,9 @@ abstract class RequestUtils { | ||||
|     return request; | ||||
|   } | ||||
| 
 | ||||
|   /// Copies the given [original] request and returns a new request with the | ||||
|   /// given [method], [url], [headers], [maxRedirects], [followRedirects], | ||||
|   /// [persistentConnection] and [body]. | ||||
|   static BaseRequest copyRequestWith( | ||||
|     BaseRequest original, { | ||||
|     String? method, | ||||
| @ -77,6 +81,8 @@ abstract class RequestUtils { | ||||
|     return request; | ||||
|   } | ||||
| 
 | ||||
|   /// Copies the given [original] request and returns a new request. | ||||
|   /// This method is useful when you want to modify the request | ||||
|   static BaseRequest copyRequest(BaseRequest original) { | ||||
|     if (original is Request) { | ||||
|       return _copyNormalRequest(original); | ||||
|  | ||||
| @ -3,6 +3,8 @@ description: A Dart client for RESTful APIs with authentication. | ||||
| repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_http_client | ||||
| version: 1.2.0 | ||||
| 
 | ||||
| publish_to: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub | ||||
| 
 | ||||
| environment: | ||||
|   sdk: '>=2.17.0 <3.0.0' | ||||
| 
 | ||||
| @ -12,7 +14,5 @@ dependencies: | ||||
| 
 | ||||
| dev_dependencies: | ||||
|   wyatt_analysis: | ||||
|     git: | ||||
|       url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages | ||||
|       ref: wyatt_analysis-v2.4.1 | ||||
|       path: packages/wyatt_analysis | ||||
|     hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub | ||||
|     version: ^2.4.1 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user