331 lines
9.3 KiB
Dart
331 lines
9.3 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';
|
|
|
|
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);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|