import 'package:http/http.dart'; import 'package:logger/logger.dart'; import 'dart:convert'; final log = Logger(); const defaultRoutes = { "login": "/api/login", "logout": "/api/logout", "checkin": "/api/checkin", "checkout": "/api/checkout", "movements": "/api/movements", "status": "/api/status", "ping": "/api/ping", }; class Connection { // Connection constructor. Connection(this.username, this.password, this.baseAddress, [this.routes = defaultRoutes]); // baseAddress contains the base address to the BotZ server final Uri baseAddress; // Username is the username to autenticate against the foreign // service. final String username; // Password is the password to autenticate against the foreign // service. final String password; final Map routes; // _stateCookie is private. It contains the HTTP state cookie // assigned by the server. String _cookies = ""; bool _loggedIn = false; bool _checkedIn = false; void updateState(String cookies, bool loggedIn) { this._cookies = loggedIn ? cookies : ""; this._loggedIn = loggedIn; } void updateMovement(bool checkedIn) => this._checkedIn = checkedIn; bool isLoggedIn() => this._loggedIn; bool isCheckedIn() => this._checkedIn; // Perform the login and obtain the cookie. void login() async { var targetUri = this.baseAddress.toString() + this.routes['login']; var payload = jsonEncode({ "username": this.username, "password": this.password, }); var resp = await post( targetUri, headers: {"Content-Type": "application/json"}, body: payload, ); log.d("[login] Post sent to: $targetUri"); handleResponse(resp); var body = jsonDecode(resp.body); log.d("[login] Response: $body"); this.updateState(resp.headers['cookies'], body['logged_in']); } // Perform the logout. void logout() async { var targetUri = this.baseAddress.toString() + this.routes['logout']; var resp = await post( targetUri, headers: {'cookies': this._cookies}, ); log.d("[logout] Post sent to: $targetUri"); handleResponse(resp); var body = jsonDecode(resp.body); log.d("[logout] Response: $body"); this.updateState(this._cookies, body['logged_in']); } // Perform the checkin if possible. void checkin() async { if (!this._loggedIn) { throw UnacceptableInvocationException("Not logged in"); } if (this._checkedIn) { throw UnacceptableInvocationException("Yet checked in"); } var targetUri = this.baseAddress.toString() + this.routes['checkin']; var resp = await post( targetUri, headers: {'cookies': this._cookies}, ); handleResponse(resp); var body = jsonDecode(resp.body); log.d("[checkin] Response: $body"); this.updateMovement(body['checked_in']); } // Perform the checkout if possible. void checkout() async { if (!this._loggedIn) { throw UnacceptableInvocationException("Not logged in"); } if (!this._checkedIn) { throw UnacceptableInvocationException("Not yet checked in"); } var targetUri = this.baseAddress.toString() + this.routes['checkout']; var resp = await post( targetUri, headers: {'cookies': this._cookies}, ); handleResponse(resp); var body = jsonDecode(resp.body); log.d("[checkout] Response: $body"); this.updateMovement(body['checked_in']); } // Query the server for the list of movements. Future> getMovements() async { if (!this._loggedIn) { throw UnacceptableInvocationException("Not logged in"); } if (!this._checkedIn) { throw UnacceptableInvocationException("Not yet checked in"); } var targetUri = this.baseAddress.toString() + this.routes['movements']; var resp = await get( targetUri, headers: {'cookies': this._cookies}, ); try { handleResponse(resp); } on NotFoundException { return {}; } var body = jsonDecode(resp.body); Map movements; for (var values in body['movements']) { movements[values['time']] = values['type']; } return movements; } } class UnauthorizedException implements ClientException { UnauthorizedException(this.uri); final String message = "Unauthorized"; final Uri uri; } class NotFoundException implements ClientException { NotFoundException(this.uri); final String message = "Resource not found"; final Uri uri; } class UnacceptableInvocationException implements Exception { UnacceptableInvocationException(this.message); final String message; } Response handleResponse(Response resp) { final c = resp.statusCode; if (c >= 200 && c < 400) { } else if (c == 401) { throw UnauthorizedException(resp.request.url); } else if (c == 404) { throw NotFoundException(resp.request.url); }else if (c >=400 || c < 500) { throw ClientException("Unexpected return code: $c", resp.request.url); } else if (c >= 500) { throw ClientException("Server error: $c", resp.request.url); } return resp; }