// 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 . // 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_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() { return name.splitMapJoin( RegExp('[A-Z]'), onMatch: (m) => '_${m[0]?.toLowerCase()}', onNonMatch: (n) => n, ); } factory EmailVerificationAction.fromString(String str) { return EmailVerificationAction.values.firstWhere( (EmailVerificationAction element) => element.toSnakeCase() == str, ); } } class VerifyCode { final String email; final String verificationCode; final EmailVerificationAction action; VerifyCode({ required this.email, required this.verificationCode, required this.action, }); VerifyCode copyWith({ String? email, String? verificationCode, EmailVerificationAction? action, }) { return VerifyCode( email: email ?? this.email, verificationCode: verificationCode ?? this.verificationCode, action: action ?? this.action, ); } Map toMap() { return { 'email': email, 'verification_code': verificationCode, 'action': action.toSnakeCase(), }; } factory VerifyCode.fromMap(Map map) { return VerifyCode( email: map['email'] as String, verificationCode: map['verification_code'] as String, action: EmailVerificationAction.fromString(map['action'] as String), ); } String toJson() => json.encode(toMap()); factory VerifyCode.fromJson(String source) => VerifyCode.fromMap(json.decode(source) as Map); @override String toString() => 'VerifyCode(email: $email, verificationCode: $verificationCode, action: $action)'; } class Account { final String email; final String? sessionId; Account({ required this.email, this.sessionId, }); Account copyWith({ String? email, String? sessionId, }) { return Account( email: email ?? this.email, sessionId: sessionId ?? this.sessionId, ); } Map toMap() { return { 'email': email, 'session_id': sessionId, }; } factory Account.fromMap(Map map) { return Account( email: map['email'] as String, sessionId: map['session_id'] != null ? map['session_id'] as String : null, ); } String toJson() => json.encode(toMap()); factory Account.fromJson(String source) => Account.fromMap(json.decode(source) as Map); @override String toString() => 'Account(email: $email, sessionId: $sessionId)'; } class SignUp { final String sessionId; final String password; SignUp({ required this.sessionId, required this.password, }); SignUp copyWith({ String? sessionId, String? password, }) { return SignUp( sessionId: sessionId ?? this.sessionId, password: password ?? this.password, ); } Map toMap() { return { 'session_id': sessionId, 'password': password, }; } factory SignUp.fromMap(Map map) { return SignUp( sessionId: map['session_id'] as String, password: map['password'] as String, ); } String toJson() => json.encode(toMap()); factory SignUp.fromJson(String source) => SignUp.fromMap(json.decode(source) as Map); @override String toString() => 'SignUp(sessionId: $sessionId, password: $password)'; } class TokenSuccess { final String accessToken; final String refreshToken; final Account account; TokenSuccess({ required this.accessToken, required this.refreshToken, required this.account, }); TokenSuccess copyWith({ String? accessToken, String? refreshToken, Account? account, }) { return TokenSuccess( accessToken: accessToken ?? this.accessToken, refreshToken: refreshToken ?? this.refreshToken, account: account ?? this.account, ); } Map toMap() { return { 'access_token': accessToken, 'refresh_token': refreshToken, 'account': account.toMap(), }; } factory TokenSuccess.fromMap(Map map) { return TokenSuccess( accessToken: map['access_token'] as String, refreshToken: map['refresh_token'] as String, account: Account.fromMap(map['account'] as Map), ); } String toJson() => json.encode(toMap()); factory TokenSuccess.fromJson(String source) => TokenSuccess.fromMap(json.decode(source) as Map); @override String toString() => 'TokenSuccess(accessToken: $accessToken, refreshToken: $refreshToken, account: $account)'; } class Login { final String email; final String password; Login({ required this.email, required this.password, }); Login copyWith({ String? email, String? password, }) { return Login( email: email ?? this.email, password: password ?? this.password, ); } Map toMap() { return { 'email': email, 'password': password, }; } factory Login.fromMap(Map map) { return Login( email: map['email'] as String, password: map['password'] as String, ); } String toJson() => json.encode(toMap()); factory Login.fromJson(String source) => Login.fromMap(json.decode(source) as Map); @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 sendSignUpCode(String email) async { final r = await client.post( Uri.parse('$apiPath/auth/send-sign-up-code'), body: { 'email': email, }, ); if (r.statusCode != 201) { throw Exception('Invalid reponse: ${r.statusCode}'); } } Future 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)['account'] as Map, ); } } Future 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 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 refresh() async { // final r = await client.refresh(); // return TokenSuccess.fromJson(r?.body ?? ''); // } Future> 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)['founds'] as List>; final result = []; for (final element in list) { result.add(Account.fromMap(element)); } return result; } } } void main(List args) async { final Pipeline pipeline = Pipeline() .addMiddleware(SimpleLoggerMiddleware()) .addMiddleware( UriPrefixMiddleware( protocol: Protocols.http, authority: 'localhost:80', ), ) .addMiddleware(BodyToJsonMiddleware()) .addMiddleware( RefreshTokenMiddleware( 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, ), ); print(pipeline.getLogic()); 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, // ), // ); // print(verifiedAccount); // final registeredAccount = await api.signUp( // SignUp(sessionId: verifiedAccount.sessionId ?? '', password: 'password'), // ); // print(registeredAccount); final signedInAccount = await api.signInWithPassword( Login(email: 'git@pcl.ovh', password: 'password'), ); // print(signedInAccount); final accountList = await api.getAccountList(); print(accountList); }