milestone/stable-1-reconcile-auth-3 #176
@ -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