import 'dart:convert'; import 'package:flutter/widgets.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:http/http.dart'; import 'package:seshat/config/constants.dart'; import 'package:seshat/domain/models/bal.dart'; import 'package:seshat/domain/models/book.dart'; import 'package:seshat/domain/models/book_instance.dart'; import 'package:seshat/domain/models/owner.dart'; import 'package:seshat/domain/models/search_result.dart'; import 'package:seshat/utils/command.dart'; import 'package:seshat/utils/result.dart'; typedef AuthHeaderProvider = String? Function(); class ApiClient { ApiClient({String? host, int? port}); late final Command0 load; String? token; bool isReady = false; FlutterSecureStorage? _secureStorage; Future _initStore() async { _secureStorage ??= const FlutterSecureStorage( aOptions: AndroidOptions(encryptedSharedPreferences: true), ); } Future> _getHeaders([ Map? additionalHeaders, ]) async { await _initStore(); final token = await _secureStorage!.read(key: "token"); final headers = {"Authorization": "Bearer $token", ...?additionalHeaders}; return headers; } /* * ================= * =====[ BAL ]===== * ================= */ Future> stopBal(int id) async { final client = Client(); try { final headers = await _getHeaders(); final response = await client.post( Uri.parse("https://$apiBasePath/bal/${id.toString()}/stop"), headers: headers, ); if (response.statusCode == 200) { final json = jsonDecode(response.body); return Result.ok(Bal.fromJSON(json)); } else if (response.statusCode == 403) { throw "You don't own the specified BAL"; } else if (response.statusCode == 404) { throw "No BAL with specified ID found"; } else if (response.statusCode == 409) { throw "Selected BAL was not on ongoing state"; } else { throw "Unknown error"; } } catch (e) { return Result.error(Exception(e)); } finally { client.close(); } } Future> startBal(int id) async { final client = Client(); try { final headers = await _getHeaders(); final response = await client.post( Uri.parse("https://$apiBasePath/bal/${id.toString()}/start"), headers: headers, ); if (response.statusCode == 200) { final json = jsonDecode(response.body); return Result.ok(Bal.fromJSON(json)); } else if (response.statusCode == 403) { throw "You don't own the specified BAL"; } else if (response.statusCode == 404) { throw "No BAL with specified ID found"; } else if (response.statusCode == 409) { throw "Cannot have multiple BAl ongoing at the same time!"; } else { throw "Unknown error"; } } catch (e) { return Result.error(Exception(e)); } finally { client.close(); } } Future> editBal( int id, String name, DateTime start, DateTime end, ) async { final client = Client(); try { final headers = await _getHeaders({"Content-Type": "application/json"}); final body = { "name": name, "start_timestamp": (start.millisecondsSinceEpoch / 1000).round(), "end_timestamp": (end.millisecondsSinceEpoch / 1000).round(), }; final response = await client.patch( Uri.parse("https://$apiBasePath/bal/${id.toString()}"), headers: headers, body: jsonEncode(body), ); if (response.statusCode == 200) { final json = jsonDecode(response.body); return Result.ok(Bal.fromJSON(json)); } else { throw Exception("Something went wrong"); } } catch (e) { return Result.error(Exception(e)); } finally { client.close(); } } Future> getBalById(int id) async { final client = Client(); try { final headers = await _getHeaders(); final response = await client.get( Uri.parse("https://$apiBasePath/bal/${id.toString()}"), headers: headers, ); if (response.statusCode == 200) { final json = jsonDecode(response.body); return Result.ok(Bal.fromJSON(json)); } else if (response.statusCode == 403) { throw Exception("You don't own the specified bal"); } else { return Result.error( Exception("No bal wirth this id exists the database"), ); } } catch (e) { return Result.error(Exception(e)); } finally { client.close(); } } Future> addBal(String name, DateTime start, DateTime end) async { final client = Client(); try { final headers = await _getHeaders({"Content-Type": "application/json"}); final body = { "name": name, "start_timestamp": (start.millisecondsSinceEpoch / 1000).round(), "end_timestamp": (end.millisecondsSinceEpoch / 1000).round(), }; final response = await client.post( Uri.parse("https://$apiBasePath/bal"), headers: headers, body: jsonEncode(body), ); if (response.statusCode == 201) { final json = jsonDecode(response.body); return Result.ok(Bal.fromJSON(json)); } else { throw Exception("Something went wrong"); } } catch (e) { return Result.error(Exception(e)); } finally { client.close(); } } Future>> getBals() async { final client = Client(); try { final headers = await _getHeaders(); final response = await client.get( Uri.parse("https://$apiBasePath/bals"), headers: headers, ); if (response.statusCode == 200) { final json = jsonDecode(response.body) as List; debugPrint("\n\n\n\nRECEIVED : $json\n\n\n\n"); debugPrint( "\n\n\n\nFORMATTED : ${json.map((element) => Bal.fromJSON(element)).toList()}\n\n\n\n", ); return Result.ok(json.map((element) => Bal.fromJSON(element)).toList()); } else { throw Exception("Something wrong happened"); } } catch (e) { debugPrint("ERROR: ${e.toString()}"); return Result.error(Exception(e)); } finally { client.close(); } } Future> getCurrentBal() async { final client = Client(); try { final headers = await _getHeaders(); final response = await client.get( Uri.parse("https://$apiBasePath/bal/current"), headers: headers, ); if (response.statusCode == 200) { final json = jsonDecode(response.body); return Result.ok(Bal.fromJSON(json)); } else if (response.statusCode == 404) { return Result.ok(null); } else { throw Exception("Something went wrong"); } } catch (e) { return Result.error(Exception(e)); } finally { client.close(); } } /* * =================== * =====[ BOOKS ]===== * =================== */ Future> getBookById(int id) async { final client = Client(); try { final headers = await _getHeaders(); final response = await client.get( Uri.parse("https://$apiBasePath/book/id/${id.toString()}"), headers: headers, ); if (response.statusCode == 200) { final json = jsonDecode(response.body); return Result.ok(Book.fromJSON(json)); } else { throw Exception("The book was not found"); } } catch (e) { return Result.error(Exception("API $e")); } finally { client.close(); } } Future> getBookByEAN(String ean) async { final client = Client(); try { final headers = await _getHeaders(); final response = await client.get( Uri.parse("https://$apiBasePath/book/ean/$ean"), headers: headers, ); if (response.statusCode == 200) { final json = jsonDecode(response.body); return Result.ok(Book.fromJSON(json)); } else { throw Exception("The book was not found"); } } catch (e) { return Result.error(Exception("API $e")); } finally { client.close(); } } /* * ============================= * =====[ BOOKS INSTANCES ]===== * ============================= */ Future>> getBookInstanceBySearch( int balId, String title, String author, ) async { final client = Client(); try { final headers = await _getHeaders({"Content-Type": "application/json"}); final body = jsonEncode({"title": title, "author": author}); debugPrint("\n\n\n\n$body\n\n\n\n"); final response = await client.post( Uri.parse("https://$apiBasePath/bal/${balId.toString()}/search"), headers: headers, body: body, ); if (response.statusCode == 200) { final json = jsonDecode(response.body) as List; debugPrint("\n\n\n\nJSON : $json\n\n\n\n"); return Result.ok(json.map((el) => SearchResult.fromJSON(el)).toList()); } else { throw "Unknown Error"; } } catch (e) { debugPrint("\n\n\n\nERROR: ${e.toString()}\n\n\n\n"); return Result.error(Exception("API $e")); } finally { client.close(); } } Future>> getBookInstanceByEAN( int balId, int ean, ) async { final client = Client(); try { final headers = await _getHeaders(); final response = await client.get( Uri.parse( "https://$apiBasePath/bal/${balId.toString()}/ean/${ean.toString()}/book_instances", ), headers: headers, ); if (response.statusCode == 200) { final json = jsonDecode(response.body) as List; return Result.ok(json.map((el) => BookInstance.fromJSON(el)).toList()); } else { throw "Unknown Error"; } } catch (e) { return Result.error(Exception("API $e")); } finally { client.close(); } } Future> sellBooks(Map books) async { final client = Client(); try { final headers = await _getHeaders({"Content-Type": "application/json"}); debugPrint("\n\n\n\nMAP: $books\n\n\n\n"); final body = jsonEncode(books); debugPrint("\n\n\n\nSENT: $body\n\n\n\n"); final response = await client.post( Uri.parse("https://$apiBasePath/book_instance/sell/bulk"), headers: headers, body: body, ); if (response.statusCode == 200) { return Result.ok(response.statusCode); } else { throw "Unknown error"; } } catch (e) { debugPrint("\n\n\n\nERROR : ${e.toString()}\n\n\n\n"); return Result.error(Exception(e)); } finally { client.close(); } } Future> sendBook( Book book, Owner owner, Bal bal, double price, ) async { final client = Client(); try { final headers = await _getHeaders({"Content-Type": "application/json"}); final body = jsonEncode({ "bal_id": bal.id, "book_id": book.id, "owner_id": owner.id, "price": price, }); final response = await client.post( Uri.parse("https://$apiBasePath/book_instance"), headers: headers, body: body, ); if (response.statusCode == 201) { final json = jsonDecode(response.body); return Result.ok(BookInstance.fromJSON(json)); } else if (response.statusCode == 403) { throw Exception("You don't own that book instance"); } else { throw Exception("Something wrong happened"); } } catch (e) { return Result.error(Exception(e)); } finally { client.close(); } } /* * ==================== * =====[ OWNERS ]===== * ==================== */ Future> getOwnerById(int id) async { final client = Client(); try { final headers = await _getHeaders(); final response = await client.get( Uri.parse("https://$apiBasePath/owner/${id.toString()}"), headers: headers, ); if (response.statusCode == 200) { final json = jsonDecode(response.body); return Result.ok(Owner.fromJSON(json)); } else { throw Exception("The owner was not found"); } } catch (e) { return Result.error(Exception("API $e")); } finally { client.close(); } } Future> getSectionOwner() async { final client = Client(); try { final headers = await _getHeaders(); final response = await client.get( Uri.parse("https://$apiBasePath/owner/self"), headers: headers, ); if (response.statusCode == 200) { final json = jsonDecode(response.body); return Result.ok(Owner.fromJSON(json)); } else { throw "Unknown error"; } } catch (e) { return Result.error(Exception(e)); } finally { client.close(); } } /// Call on `/owners` to get a list of all [Owner]s Future>> getOwners() async { final client = Client(); try { final headers = await _getHeaders(); final response = await client.get( Uri.parse("https://$apiBasePath/owners"), headers: headers, ); if (response.statusCode == 200) { final json = jsonDecode(response.body) as List; return Result.ok( json.map((element) => Owner.fromJSON(element)).toList(), ); } else { throw Exception("Invalid request"); } } on Exception catch (error) { return Result.error(error); } finally { client.close(); } } /// Adds an owner to the database Future> addOwner( String firstName, String lastName, String contact, ) async { final client = Client(); try { final headers = await _getHeaders({"Content-Type": "application/json"}); final body = { "first_name": firstName, "last_name": lastName, "contact": contact, }; final response = await client.post( Uri.parse("https://$apiBasePath/owner"), headers: headers, body: jsonEncode(body), ); if (response.statusCode == 201) { final json = jsonDecode(response.body); return Result.ok(Owner.fromJSON(json)); } else { throw Exception("Invalid request"); } } on Exception catch (error) { return Result.error(error); } finally { client.close(); } } }