tuuli_app/lib/api/api_client.dart

395 lines
11 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'package:get/get.dart';
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';
import 'package:tuuli_app/api/model/user_model.dart';
class ErrorOrData<T> {
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<T> = Future<ErrorOrData<T>>;
typedef TableItemsData = Map<String, dynamic>;
typedef TableItemsDataList = List<TableItemsData>;
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<AccessTokenModel> 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<TablesListModel> 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<bool> createTable(
String tableName, List<TableField> columns) async {
bool? ignored;
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 {
ignored = true;
}
} else if (response.statusCode == 422) {
error = Exception('Invalid request parameters');
} else {
error = Exception('HTTP ${response.statusCode}');
}
return ErrorOrData(ignored, error);
}
FutureErrorOrData<bool> dropTable(String tableName) async {
bool? ignored;
Exception? error;
final response =
await post('/api/dropTable/${Uri.encodeComponent(tableName)}');
if (response.statusCode == 200) {
final body = json.decode(await response.stream.bytesToString());
if (body['error'] != null) {
error = Exception(body['error']);
} else {
ignored = true;
}
} else if (response.statusCode == 422) {
error = Exception('Invalid request parameters');
} else {
error = Exception('HTTP ${response.statusCode}');
}
return ErrorOrData(ignored, error);
}
FutureErrorOrData<TableItemsDataList> getTableItems(TableModel table) async {
TableItemsDataList? data;
Exception? error;
final response = await post(
'/items/${Uri.encodeComponent(table.tableName)}',
body: {
"fields": ["*"]
},
headers: {
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
final body = json.decode(await response.stream.bytesToString())
as Map<String, dynamic>;
if (body['error'] != null) {
error = Exception(body['error']);
} else if (body['items'] == null) {
error = Exception('Server error');
} else {
data = (body['items'] as List)
.map((e) => e as TableItemsData)
.toList(growable: false);
}
} else if (response.statusCode == 422) {
error = Exception('Invalid request parameters');
} else {
error = Exception('HTTP ${response.statusCode}');
}
return ErrorOrData(data, error);
}
FutureErrorOrData<bool> insertItem(
TableModel table, TableItemsData newItem) async {
bool? ignored;
Exception? error;
final response = await post(
'/items/${Uri.encodeComponent(table.tableName)}/+',
body: newItem.map((key, value) =>
MapEntry(key, value is DateTime ? value.toIso8601String() : value)),
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 {
ignored = true;
}
} else if (response.statusCode == 422) {
error = Exception('Invalid request parameters');
} else {
error = Exception('HTTP ${response.statusCode}');
}
return ErrorOrData(ignored, error);
}
FutureErrorOrData<bool> updateItem(
TableModel table, TableItemsData newItem, TableItemsData oldItem) async {
bool? ignored;
Exception? error;
final response = await post(
'/items/${Uri.encodeComponent(table.tableName)}/*',
body: {
"item": newItem.map((key, value) =>
MapEntry(key, value is DateTime ? value.toIso8601String() : value)),
"oldItem": oldItem.map((key, value) =>
MapEntry(key, value is DateTime ? value.toIso8601String() : value)),
},
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 {
ignored = true;
}
} else if (response.statusCode == 422) {
error = Exception('Invalid request parameters');
} else {
error = Exception('HTTP ${response.statusCode}');
}
return ErrorOrData(ignored, error);
}
FutureErrorOrData<bool> deleteItem(TableModel table, TableItemsData e) async {
bool? ignored;
Exception? error;
TableField? primaryField =
table.columns.firstWhereOrNull((el) => el.isPrimary);
TableField? uniqueField =
table.columns.firstWhereOrNull((el) => el.isUnique);
final response = await post(
'/items/${Uri.encodeComponent(table.tableName)}/-',
body: {
"defs": [
if (primaryField != null)
{
"name": primaryField.fieldName,
"value": e[primaryField.fieldName],
}
else if (uniqueField != null)
{
"name": uniqueField.fieldName,
"value": e[uniqueField.fieldName],
}
else
for (final field in table.columns)
{
"name": field.fieldName,
"value": e[field.fieldName],
}
],
},
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 {
ignored = true;
}
} else if (response.statusCode == 422) {
error = Exception('Invalid request parameters');
} else {
error = Exception('HTTP ${response.statusCode}');
}
return ErrorOrData(ignored, error);
}
FutureErrorOrData<bool> createUser(
TableModel table, String username, String password) async {
bool? ignored;
Exception? error;
final response = await post(
'/api/createUser',
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 {
ignored = true;
}
} else if (response.statusCode == 422) {
error = Exception('Invalid request parameters');
} else {
error = Exception('HTTP ${response.statusCode}');
}
return ErrorOrData(ignored, error);
}
FutureErrorOrData<bool> updateUser(
TableModel table, int userId, String password, String accessToken) async {
bool? ignored;
Exception? error;
final response = await post(
'/api/updateUser',
body: {
"user_id": userId,
"password": password,
"access_token": accessToken,
},
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 {
ignored = true;
}
} else if (response.statusCode == 422) {
error = Exception('Invalid request parameters');
} else {
error = Exception('HTTP ${response.statusCode}');
}
return ErrorOrData(ignored, error);
}
// REGION: HTTP Methods implementation
Future<StreamedResponse> get(
String path, {
Map<String, String>? headers,
}) {
return _request(path, 'GET', headers: headers);
}
Future<StreamedResponse> post(
String path, {
Map<String, String>? headers,
dynamic body,
}) {
return _request(path, 'POST', headers: headers, body: body);
}
Future<StreamedResponse> _request(
String path,
String method, {
Map<String, String>? 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);
}
}