This repository has been archived on 2022-04-20. You can view files and clone it, but cannot push or open issues or pull requests.
app/lib/api/client.dart

118 lines
3.6 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
class Client {
final JsonDecoder _decoder = new JsonDecoder();
final JsonEncoder _encoder = new JsonEncoder();
final String _token;
final String _base;
int _maxPages;
int get maxPages => _maxPages;
String get base => _base;
Client(this._token, String base)
: _base = base.endsWith('/api/v1') ? base : '$base/api/v1';
bool operator ==(dynamic otherClient) {
return otherClient._token == _token;
}
@override
int get hashCode => _token.hashCode;
get _headers => {
'Authorization': _token != null ? 'Bearer $_token' : '',
'Content-Type': 'application/json'
};
Future<dynamic> get(String url, [Map<String, List<String>> queryParameters]) {
// TODO: This could be moved to a seperate function
var uri = Uri.parse('${this.base}$url');
// Because these are all final values, we can't just add the queryParameters and must instead build a new Uri Object every time this method is called.
var newUri = Uri(
scheme: uri.scheme,
userInfo: uri.userInfo,
host: uri.host,
port: uri.port,
path: uri.path,
query: uri.query,
queryParameters: queryParameters,
// Because dart takes a Map<String, String> here, it is only possible to sort by one parameter while the api supports n parameters.
fragment: uri.fragment);
return http.get(newUri, headers: _headers).then(_handleResponse);
}
Future<dynamic> delete(String url) {
return http
.delete('${this.base}$url', headers: _headers)
.then(_handleResponse);
}
Future<dynamic> post(String url, {dynamic body}) {
return http
.post('${this.base}$url',
headers: _headers, body: _encoder.convert(body))
.then(_handleResponse);
}
Future<dynamic> put(String url, {dynamic body}) {
return http
.put('${this.base}$url',
headers: _headers, body: _encoder.convert(body))
.then(_handleResponse);
}
dynamic _handleResponse(http.Response response) {
if (response.statusCode < 200 ||
response.statusCode >= 400 ||
json == null) {
if (response.statusCode ~/ 100 == 4) {
Map<String, dynamic> error = _decoder.convert(response.body);
throw new InvalidRequestApiException(
response.statusCode,
response.request.url.toString(),
error["message"] ?? "Unknown Error");
}
throw new ApiException(
response.statusCode, response.request.url.toString());
}
// FIXME: This is a workaround for when the client makes another
// unrelated api request in between requesting multiple pages of the same thing.
// To properly fix this, we need a way to pass the max number of pages somewhere else
// and not save them in the global client which everyone uses.
// This workaround only works when the other api requests in between two pages
// are not using pagination.
if (response.headers["x-pagination-total-pages"] != null) {
_maxPages = int.parse(response.headers["x-pagination-total-pages"]);
}
return _decoder.convert(response.body);
}
}
class InvalidRequestApiException extends ApiException {
final String message;
InvalidRequestApiException(int errorCode, String path, this.message)
: super(errorCode, path);
@override
String toString() {
return this.message;
}
}
class ApiException implements Exception {
final int errorCode;
final String path;
ApiException(this.errorCode, this.path);
@override
String toString() {
return "Can't fetch data from server. (Error-Code: $errorCode)";
}
}