client: Update account API

This commit is contained in:
Elias Projahn 2020-05-13 13:43:06 +02:00
parent 0847dde610
commit ea1469c0dd

View file

@ -5,28 +5,28 @@ import 'package:http/http.dart' as http;
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:musicus_database/musicus_database.dart'; import 'package:musicus_database/musicus_database.dart';
/// A user of the Musicus API. /// Credentials for a Musicus account.
class User { class MusicusAccountCredentials {
/// Username. /// The user's username.
final String name; final String username;
/// An optional email address.
final String email;
/// The user's password. /// The user's password.
final String password; final String password;
User({ MusicusAccountCredentials({
this.name, this.username,
this.email,
this.password, this.password,
}); });
}
Map<String, dynamic> toJson() => { /// Additional information on a Musicus account.
'name': name, class MusicusAccountDetails {
'email': email, /// An optional email address.
'password': password, final String email;
};
MusicusAccountDetails({
this.email,
});
} }
/// A simple http client for the Musicus server. /// A simple http client for the Musicus server.
@ -47,18 +47,22 @@ class MusicusClient {
/// Base path to the root location of the Musicus API. /// Base path to the root location of the Musicus API.
final String basePath; final String basePath;
User _user; MusicusAccountCredentials _credentials;
/// The user to login. /// Account credentials for login.
User get user => _user; ///
set user(User user) { /// If this is null, unauthorized requests will fail.
_user = user; MusicusAccountCredentials get credentials => _credentials;
set credentials(MusicusAccountCredentials credentials) {
_credentials = credentials;
_token = null; _token = null;
} }
final _client = http.Client(); final _client = http.Client();
/// The last retrieved access token. /// The last retrieved access token.
///
/// If this is null, a new token should be retrieved using [login] if needed.
String _token; String _token;
MusicusClient({ MusicusClient({
@ -66,11 +70,11 @@ class MusicusClient {
@required this.host, @required this.host,
this.port = 443, this.port = 443,
this.basePath, this.basePath,
User user, MusicusAccountCredentials credentials,
}) : assert(scheme != null), }) : assert(scheme != null),
assert(port != null), assert(port != null),
assert(host != null), assert(host != null),
_user = user; _credentials = credentials;
/// Create an URI using member variables and parameters. /// Create an URI using member variables and parameters.
Uri createUri({ Uri createUri({
@ -86,39 +90,126 @@ class MusicusClient {
); );
} }
/// Register a new user. /// Create a new Musicus account.
/// ///
/// This will return true, if the action was successful. Subsequent requests /// The email address is optional. This will return true, if the action was
/// will automatically be made as the new user. /// successful. In that case, the new credentials will automatically be
Future<bool> register(User newUser) async { /// stored as under [credentials] and used for subsequent requests.
Future<bool> registerAccount({
@required String username,
@required String password,
String email,
}) async {
final response = await _client.post( final response = await _client.post(
createUri( createUri(
path: '/register', path: '/account/register',
), ),
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: jsonEncode(newUser.toJson()), body: jsonEncode({
'username': username,
'password': password,
'email': email,
}),
); );
if (response.statusCode == HttpStatus.ok) { if (response.statusCode == HttpStatus.ok) {
_user = newUser; _credentials = MusicusAccountCredentials(
username: username,
password: password,
);
_token = null; _token = null;
return true; return true;
} else { } else {
return false; return false;
} }
} }
/// Retrieve an access token for [user]. /// Get the current account details.
Future<MusicusAccountDetails> getAccountDetails() async {
assert(_credentials != null);
final response = await _authorized(
'GET',
createUri(path: '/account/details'),
);
if (response.statusCode == HttpStatus.ok) {
final json = jsonDecode(response.body);
return MusicusAccountDetails(
email: json['email'],
);
} else {
return null;
}
}
/// Change the account details for the currently used user account.
/// ///
/// The token will land in [_token]. If the login failed, a /// This will throw a [MusicusLoginFailedException] if the account doesn't
/// [MusicusLoginFailedException] will be thrown. /// exist or the old password was wrong.
Future<void> _login() async { Future<void> updateAccount(String newEmail, String newPassword) async {
assert(_credentials != null);
final response = await _client.post(
createUri(path: '/account/details'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'username': _credentials.username,
'password': _credentials.password,
'newEmail': newEmail,
'newPassword': newPassword,
}),
);
if (response.statusCode != HttpStatus.ok) {
throw MusicusLoginFailedException();
}
}
/// Delete the currently used Musicus account.
///
/// This will throw a [MusicusLoginFailedException] if the user doesn't exist
/// or the password was wrong.
Future<void> deleteAccount() async {
assert(_credentials != null);
final response = await _client.post(
createUri(path: '/account/delete'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'username': _credentials.username,
'password': _credentials.password,
}),
);
if (response.statusCode == HttpStatus.ok) {
_credentials = null;
_token = null;
} else {
throw MusicusLoginFailedException();
}
}
/// Retrieve an access token for the current user.
///
/// This will be called automatically, when the client calls a method that
/// requires it. If the login failed, a [MusicusLoginFailedException] will be
/// thrown.
Future<void> login() async {
assert(_credentials != null);
final response = await _client.post( final response = await _client.post(
createUri( createUri(
path: '/login', path: '/account/login',
), ),
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: jsonEncode(user.toJson()), body: jsonEncode({
'username': _credentials.username,
'password': _credentials.password,
}),
); );
if (response.statusCode == HttpStatus.ok) { if (response.statusCode == HttpStatus.ok) {
@ -138,12 +229,19 @@ class MusicusClient {
/// a [MusicusNotAuthorizedException]. /// a [MusicusNotAuthorizedException].
Future<http.Response> _authorized(String method, Uri uri, Future<http.Response> _authorized(String method, Uri uri,
{Map<String, String> headers, String body}) async { {Map<String, String> headers, String body}) async {
if (_user != null) { if (_credentials != null) {
Future<http.Response> _request() async { Future<http.Response> _request() async {
final request = http.Request(method, uri); final request = http.Request(method, uri);
request.headers.addAll(headers);
if (headers != null) {
request.headers.addAll(headers);
}
request.headers['Authorization'] = 'Bearer $_token'; request.headers['Authorization'] = 'Bearer $_token';
request.body = body;
if (body != null) {
request.body = body;
}
return await http.Response.fromStream(await _client.send(request)); return await http.Response.fromStream(await _client.send(request));
} }
@ -153,11 +251,11 @@ class MusicusClient {
if (_token != null) { if (_token != null) {
response = await _request(); response = await _request();
if (response.statusCode == HttpStatus.unauthorized) { if (response.statusCode == HttpStatus.unauthorized) {
await _login(); await login();
response = await _request(); response = await _request();
} }
} else { } else {
await _login(); await login();
response = await _request(); response = await _request();
} }