<!--
 * 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/>.
-->

# 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>
  <img src="https://img.shields.io/badge/SDK-Dart%20%7C%20Flutter-blue?style=flat-square" alt="SDK: Dart & Flutter" />
</p>

HTTP Client for Dart with Middlewares !

## Getting started

Simply add wyatt_http_client in pubspec.yaml, then

```dart
import 'package:wyatt_http_client/wyatt_http_client.dart';
```

## Usage

Firstly you have to understand **Middleware** and **Pipeline** concepts.

In `wyatt_http_client` a middleware is an object where requests and responses 
pass through. And a pipeline is basicaly a list of middlewares.

In a pipeline with middlewares A and B, if request pass through A, then B, 
the response will pass through B then A.

> You can `print(pipeline)` to get full process order of a pipeline.

For example, if you want to log every request, and simplify an url you can use provided `SimpleLogger` and `UriPrefix` .

```dart
// Create the Pipeline
final Pipeline pipeline = Pipeline()
    ..addMiddleware(
        const UriPrefixMiddleware(
            protocol: Protocols.http,
            authority: 'localhost:80',
        ),
    )
    ..addMiddleware(const SimpleLoggerMiddleware());
```

Then if you print the pipeline, 

```
[Req] -> UriPrefix -> SimpleLogger
[Res] -> SimpleLogger
```

> The `response` doesn't pass through `UriPrefix` because it's an `OnRequestMiddleware` only.

And you can create a client.

```dart
final client = MiddlewareClient(pipeline: pipeline);
```

At this point you can use `client` like every Client from `package:http/http.dart` .

## Recipes

### Rest API with URL Authentication

Let's build a client for a REST API where the (bad) authentication is through the URL.
We need some middlewares:

* SimpleLogger, to log every request and response (useful for debug).
* BodyToJson, to automaticaly transform Map object to JSON.
* UriPrefix, to simplify the build of an API Object (save protocol and API prefix).
* UnsafeAuth, to use url based authentication.

Let's start by creating the Pipeline:

```dart
final Pipeline pipeline = Pipeline()
    ..addMiddleware(
        const UriPrefixMiddleware(
            protocol: Protocols.http,
            authority: 'localhost:80',
        ),
    )
    ..addMiddleware(const BodyToJsonMiddleware())
    ..addMiddleware(
        const UnsafeAuthMiddleware(
            username: 'wyatt',
            password: 'motdepasse',
        ),
    )
    ..addMiddleware(SimpleLoggerMiddleware());
```

Then simply create a client and make a call.

```dart
final client = MiddlewareClient(pipeline: pipeline);

await client.get(Uri.parse('/protected'));
```

> Here it make a `GET` call on `http://localhost:80/protected?username=wyatt&password=motdepasse`

And voilà.

### Rest API with Oauth2

So now we want a real authentication.

```dart
final Pipeline pipeline = Pipeline()
    ..addMiddleware(
        const UriPrefixMiddleware(
            protocol: Protocols.http,
            authority: 'localhost:80',
        ),
    )
    ..addMiddleware(const BodyToJsonMiddleware())
    ..addMiddleware(
        RefreshTokenAuthMiddleware(
            authorizationEndpoint: '/auth/sign-in',
            tokenEndpoint: '/auth/refresh',
            accessTokenParser: (body) => body['access_token']! as String,
            refreshTokenParser: (body) => body['refresh_token']! as String,
            unauthorized: HttpStatus.forbidden,
        ),
    )
    ..addMiddleware(const SimpleLoggerMiddleware());
```

> Here we just change `UnsafeAuthMiddleware` by `RefreshTokenAuthMiddleware` and the whole app while adapt to a new authentication system.

### Create a new Middleware

You can create your own middleware by implementing `Middleware` class, and use mixins to add `OnRequest` and/or `OnResponse` methods.

```dart
class SimpleLoggerMiddleware 
    with OnRequestMiddleware, OnResponseMiddleware 
    implements Middleware {
    
  const SimpleLoggerMiddleware();
  
  @override
  String getName() => 'SimpleLogger';

  @override
  Future<MiddlewareRequest> onRequest(
    MiddlewareContext context,
    MiddlewareRequest request,
  ) async {
    print('${getName()}::OnRequest');
    return request;
  }

  @override
  Future<MiddlewareResponse> onResponse(
    MiddlewareContext context,
    MiddlewareResponse response,
  ) async {
    print('${getName()}::OnResponse');
    return response;
  }
}
```
