import 'dart:async'; import 'dart:convert'; import 'package:tuuli_app/api/model/access_token_model.dart'; import 'package:http/browser_client.dart'; import 'package:http/http.dart'; import 'package:tuuli_app/api/model/table_field_model.dart'; import 'package:tuuli_app/api/model/tables_list_model.dart'; class ErrorOrData { final T? data; final Exception? error; ErrorOrData(this.data, this.error); void unfold( void Function(T data) onData, void Function(Exception error) onError) { if (data != null) { onData(data as T); } else { onError(error!); } } } typedef FutureErrorOrData = Future>; class ApiClient { final BrowserClient _client = BrowserClient(); var _accessToken = ''; final Uri baseUrl; ApiClient(this.baseUrl); ApiClient.fromString(String baseUrl) : this(Uri.parse(baseUrl)); void setAccessToken(String accessToken) { _accessToken = accessToken; } FutureErrorOrData login( String username, String password, ) async { AccessTokenModel? data; Exception? error; final response = await post('/api/getAccessToken', body: { 'username': username, 'password': password, }, headers: { 'Content-Type': 'application/json', }); if (response.statusCode == 200) { final body = json.decode(await response.stream.bytesToString()); if (body['error'] != null) { error = Exception(body['error']); } else if (body['access_token'] == null) { error = Exception('No access token'); } else { data = AccessTokenModel(accessToken: body['access_token']); } } else if (response.statusCode == 422) { error = Exception('Invalid request parameters'); } else { error = Exception('HTTP ${response.statusCode}'); } return ErrorOrData(data, error); } FutureErrorOrData tablesList() async { TablesListModel? data; Exception? error; final response = await get('/api/listTables'); if (response.statusCode == 200) { final body = json.decode(await response.stream.bytesToString()); if (body['error'] != null) { error = Exception(body['error']); } else if (body['tables'] == null) { error = Exception('Server error'); } else { data = TablesListModel.fromJson(body); } } else if (response.statusCode == 422) { error = Exception('Invalid request parameters'); } else { error = Exception('HTTP ${response.statusCode}'); } return ErrorOrData(data, error); } FutureErrorOrData createTable( String tableName, List columns) async { Exception? error; final response = await post('/api/createTable/${Uri.encodeComponent(tableName)}', body: { 'columns': columns.map((e) => e.toColumnDefinition()).toList(growable: false), }, headers: { 'Content-Type': 'application/json', }); if (response.statusCode == 200) { final body = json.decode(await response.stream.bytesToString()); if (body['error'] != null) { error = Exception(body['error']); } } else if (response.statusCode == 422) { error = Exception('Invalid request parameters'); } else { error = Exception('HTTP ${response.statusCode}'); } return ErrorOrData(null, error); } Future get( String path, { Map? headers, }) { return _request(path, 'GET', headers: headers); } Future post( String path, { Map? headers, dynamic body, }) { return _request(path, 'POST', headers: headers, body: body); } Future _request( String path, String method, { Map? headers, dynamic body, }) async { final uri = baseUrl.resolve(path); final request = Request(method, uri); if (headers != null) { request.headers.addAll(headers); } if (_accessToken.isNotEmpty) { request.headers["Access-Token"] = _accessToken; } if (body != null) { request.body = json.encode(body); } return _client.send(request); } }