`openapi-generator`の`dart-dio-next`で生成したコードでAuthorizationヘッダーにBearerトークンを指定する

# 書くこと
* `openapi-generator`の`dart-dio-next`で生成したコードでAuthorizationヘッダーにBearerトークンを指定する方法
* 自動生成されるBearerトークンを指定処理のソースコードの解説

# 書かないこと
* APIのパスを定義した完全なOpenAPI定義について
* `openapi-generator`の実行環境について
* OpenAPIについて

## openapi-generatorについて
OpenAPI v3の 定義からソースコードを自動生成するツール。
今回使うgenerateテンプレートは`dart-dio-next`という`dio` パッケージを使った`Dart`のソースコード

https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/dart-dio-next.md

## OAS定義

公式リファレンスに従いOASを定義していきます。

```yaml:openapi.yml
security:
  - bearerAuth: []

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
```

https://swagger.io/docs/specification/authentication/bearer-authentication/

## `openapi-generator`実行
細かいパラメーターは各環境に合わせてください

```bash
$ openapi-generator generate -i openapi.yml -g dart-dio-next
```

# API呼び出しとトークン設定

自動生成したパッケージのクラスに対して`setBearerAuth`を呼び出し第一引数にOASの`securitySchemes`で指定したスキーマの名前`'bearerAuth'`を、第二引数にトークンの値を設定します。

```dart
final client = HttpClient();
client.setBearerAuth('bearerAuth', 'abc');
```

## 詳細
出力されるdart パッケージの`lib/api.dart`に以下のようにBearer tokenを設定する関数が定義されています。

```dart:lib/api.dart
  void setBearerAuth(String name, String token) {
    if (this.dio.interceptors.any((i) => i is BearerAuthInterceptor)) {
      (this.dio.interceptors.firstWhere((i) => i is BearerAuthInterceptor) as BearerAuthInterceptor).tokens[name] = token;
    }
  }
```

この関数では`BearerAuthInterceptor`の`tokens`メンバに値を代入します。
`setBearerAuth('bearerAuth', 'abc')`としたので`'bearerAuth'` keyに`'abc'` valueを代入しています。
実際にリクエストを行う際`BearerAuthInterceptor`の`onRequest`でインターセプト処理が行われます。
`lib/auth/bearer_auth.dart`は以下のように定義されています。

```dart:lib/auth/bearer_auth.dart
class BearerAuthInterceptor extends AuthInterceptor {
  final Map<String, String> tokens = {};

  @override
  void onRequest(
    RequestOptions options,
    RequestInterceptorHandler handler,
  ) {
    final authInfo = getAuthInfo(options, (secure) => secure['type'] == 'http' && secure['scheme'] == 'bearer');
    for (final info in authInfo) {
      final token = tokens[info['name']];
      if (token != null) {
        options.headers['Authorization'] = 'Bearer ${token}';
        break;
      }
    }
    super.onRequest(options, handler);
  }
}
```

`getAuthInfo`関数を呼び出してその結果をループし、`tokens`に格納されている前述したBearer tokenを 取り出してヘッダーに設定しています
`getAuthInfo`の 引数に定義されている以下の関数はOASで定義した`securitySchemes`を取得する関数です。

```dart
(secure) => secure['type'] == 'http' && secure['scheme'] == 'bearer'`
```

そのため、**前述したOASで`securitySchemes`を定義していないとこのメソッドは意図した通りに動きません。**

```yaml
securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
```

`getAuthInfo`関数は`lib/auth/auth.dart`は以下のように定義されています。

```dart:auth.dart
abstract class AuthInterceptor extends Interceptor {
  /// Get auth information on given route for the given type.
  /// Can return an empty list if type is not present on auth data or
  /// if route doesn't need authentication.
  List<Map<String, String>> getAuthInfo(RequestOptions route, bool Function(Map<String, String> secure) handles) {
    if (route.extra.containsKey('secure')) {
      final auth = route.extra['secure'] as List<Map<String, String>>;
      return auth.where((secure) => handles(secure)).toList();
    }
    return [];
  }
}
```

OASの`securitySchemes`で指定した値をリストで取得します。`bearerFormat`を扱う機能は提供されていないようでリストから除外されています。

```dart
[{type: http, scheme: bearer, name: bearerAuth}]
```

# 環境
* openapi-generator-cli v5.3.1
* Dart SDK version: 2.16.0