Externalized API and models as own package

This commit is contained in:
Anton Stubenbord
2022-12-02 01:48:13 +01:00
parent 60d1a2e62a
commit ec7707e4a4
143 changed files with 1496 additions and 1339 deletions
@@ -0,0 +1,7 @@
abstract class PaperlessAuthenticationApi {
Future<String> login({
required String username,
required String password,
required String serverUrl,
});
}
@@ -0,0 +1,53 @@
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart';
import 'package:paperless_api/src/models/paperless_server_exception.dart';
import 'package:paperless_api/src/modules/authentication_api/authentication_api.dart';
class PaperlessAuthenticationApiImpl implements PaperlessAuthenticationApi {
final BaseClient client;
PaperlessAuthenticationApiImpl(this.client);
@override
Future<String> login({
required String username,
required String password,
required String serverUrl,
}) async {
late Response response;
try {
response = await client.post(
Uri.parse("/api/token/"),
body: {"username": username, "password": password},
);
} on FormatException catch (e) {
final source = e.source;
if (source is String &&
source.contains("400 No required SSL certificate was sent")) {
throw PaperlessServerException(
ErrorCode.missingClientCertificate,
httpStatusCode: response.statusCode,
);
}
}
if (response.statusCode == HttpStatus.ok) {
final data = jsonDecode(utf8.decode(response.bodyBytes));
return data['token'];
} else if (response.statusCode == HttpStatus.badRequest &&
response.body
.toLowerCase()
.contains("no required certificate was sent")) {
throw PaperlessServerException(
ErrorCode.invalidClientCertificateConfiguration,
httpStatusCode: response.statusCode,
);
} else {
throw PaperlessServerException(
ErrorCode.authenticationFailed,
httpStatusCode: response.statusCode,
);
}
}
}
@@ -0,0 +1,36 @@
import 'dart:typed_data';
import 'package:paperless_api/src/models/bulk_edit_model.dart';
import 'package:paperless_api/src/models/document_filter.dart';
import 'package:paperless_api/src/models/document_meta_data_model.dart';
import 'package:paperless_api/src/models/document_model.dart';
import 'package:paperless_api/src/models/paged_search_result.dart';
import 'package:paperless_api/src/models/similar_document_model.dart';
abstract class PaperlessDocumentsApi {
Future<void> create(
Uint8List documentBytes, {
required String filename,
required String title,
required String authToken,
required String serverUrl,
int? documentType,
int? correspondent,
Iterable<int> tags = const [],
DateTime? createdAt,
});
Future<DocumentModel> update(DocumentModel doc);
Future<int> findNextAsn();
Future<PagedSearchResult<DocumentModel>> find(DocumentFilter filter);
Future<List<SimilarDocumentModel>> findSimilar(int docId);
Future<int> delete(DocumentModel doc);
Future<DocumentMetaData> getMetaData(DocumentModel document);
Future<Iterable<int>> bulkAction(BulkAction action);
Future<Uint8List> getPreview(int docId);
String getThumbnailUrl(int docId);
Future<DocumentModel> waitForConsumptionFinished(
String filename, String title);
Future<Uint8List> download(DocumentModel document);
Future<List<String>> autocomplete(String query, [int limit = 10]);
}
@@ -0,0 +1,282 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:http/src/boundary_characters.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:paperless_api/src/constants.dart';
import 'package:paperless_api/src/models/bulk_edit_model.dart';
import 'package:paperless_api/src/models/document_filter.dart';
import 'package:paperless_api/src/models/document_meta_data_model.dart';
import 'package:paperless_api/src/models/document_model.dart';
import 'package:paperless_api/src/models/paged_search_result.dart';
import 'package:paperless_api/src/models/paperless_server_exception.dart';
import 'package:paperless_api/src/models/query_parameters/asn_query.dart';
import 'package:paperless_api/src/models/query_parameters/sort_field.dart';
import 'package:paperless_api/src/models/query_parameters/sort_order.dart';
import 'package:paperless_api/src/models/similar_document_model.dart';
import 'paperless_documents_api.dart';
class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
final BaseClient baseClient;
final HttpClient httpClient;
PaperlessDocumentsApiImpl(this.baseClient, this.httpClient);
@override
Future<void> create(
Uint8List documentBytes, {
required String filename,
required String title,
required String authToken,
required String serverUrl,
int? documentType,
int? correspondent,
Iterable<int> tags = const [],
DateTime? createdAt,
}) async {
// The multipart request has to be generated from scratch as the http library does
// not allow the same key (tags) to be added multiple times. However, this is what the
// paperless api expects, i.e. one block for each tag.
final request = await httpClient.postUrl(
Uri.parse("$serverUrl/api/documents/post_document/"),
);
final boundary = _boundaryString();
StringBuffer bodyBuffer = StringBuffer();
var fields = <String, String>{};
fields.putIfAbsent('title', () => title);
if (createdAt != null) {
fields.putIfAbsent('created', () => apiDateFormat.format(createdAt));
}
if (correspondent != null) {
fields.putIfAbsent('correspondent', () => jsonEncode(correspondent));
}
if (documentType != null) {
fields.putIfAbsent('document_type', () => jsonEncode(documentType));
}
for (final key in fields.keys) {
bodyBuffer.write(_buildMultipartField(key, fields[key]!, boundary));
}
for (final tag in tags) {
bodyBuffer.write(_buildMultipartField('tags', tag.toString(), boundary));
}
bodyBuffer.write("--$boundary"
'\r\nContent-Disposition: form-data; name="document"; filename="$filename"'
"\r\nContent-type: application/octet-stream"
"\r\n\r\n");
final closing = "\r\n--$boundary--\r\n";
// Set headers
request.headers.set(HttpHeaders.contentTypeHeader,
"multipart/form-data; boundary=$boundary");
request.headers.set(HttpHeaders.contentLengthHeader,
"${bodyBuffer.length + closing.length + documentBytes.lengthInBytes}");
request.headers.set(HttpHeaders.authorizationHeader, "Token $authToken");
//Write fields to request
request.write(bodyBuffer.toString());
//Stream file
await request.addStream(Stream.fromIterable(documentBytes.map((e) => [e])));
// Write closing boundary to request
request.write(closing);
final response = await request.close();
if (response.statusCode != 200) {
throw PaperlessServerException(
ErrorCode.documentUploadFailed,
httpStatusCode: response.statusCode,
);
}
}
String _buildMultipartField(String fieldName, String value, String boundary) {
// ignore: prefer_interpolation_to_compose_strings
return '--$boundary'
'\r\nContent-Disposition: form-data; name="$fieldName"'
'\r\nContent-type: text/plain'
'\r\n\r\n' +
value +
'\r\n';
}
String _boundaryString() {
Random _random = Random();
var prefix = 'dart-http-boundary-';
var list = List<int>.generate(
70 - prefix.length,
(index) => boundaryCharacters[_random.nextInt(boundaryCharacters.length)],
growable: false,
);
return '$prefix${String.fromCharCodes(list)}';
}
@override
Future<DocumentModel> update(DocumentModel doc) async {
final response = await baseClient.put(
Uri.parse("/api/documents/${doc.id}/"),
body: json.encode(doc.toJson()),
headers: {"Content-Type": "application/json"},
);
if (response.statusCode == 200) {
return DocumentModel.fromJson(
jsonDecode(utf8.decode(response.bodyBytes)) as Map<String, dynamic>,
);
} else {
throw const PaperlessServerException(ErrorCode.documentUpdateFailed);
}
}
@override
Future<PagedSearchResult<DocumentModel>> find(DocumentFilter filter) async {
final filterParams = filter.toQueryString();
final response = await baseClient.get(
Uri.parse("/api/documents/?$filterParams"),
);
if (response.statusCode == 200) {
return compute(
PagedSearchResult.fromJson,
PagedSearchResultJsonSerializer<DocumentModel>(
jsonDecode(utf8.decode(response.bodyBytes)),
DocumentModel.fromJson,
),
);
} else {
throw const PaperlessServerException(ErrorCode.documentLoadFailed);
}
}
@override
Future<int> delete(DocumentModel doc) async {
final response =
await baseClient.delete(Uri.parse("/api/documents/${doc.id}/"));
if (response.statusCode == 204) {
return Future.value(doc.id);
}
throw const PaperlessServerException(ErrorCode.documentDeleteFailed);
}
@override
String getThumbnailUrl(int documentId) {
return "/api/documents/$documentId/thumb/";
}
String getPreviewUrl(int documentId) {
return "/api/documents/$documentId/preview/";
}
@override
Future<Uint8List> getPreview(int documentId) async {
final response = await baseClient.get(Uri.parse(getPreviewUrl(documentId)));
if (response.statusCode == 200) {
return response.bodyBytes;
}
throw const PaperlessServerException(ErrorCode.documentPreviewFailed);
}
@override
Future<int> findNextAsn() async {
const DocumentFilter asnQueryFilter = DocumentFilter(
sortField: SortField.archiveSerialNumber,
sortOrder: SortOrder.descending,
asn: AsnQuery.anyAssigned(),
page: 1,
pageSize: 1,
);
try {
final result = await find(asnQueryFilter);
return result.results
.map((e) => e.archiveSerialNumber)
.firstWhere((asn) => asn != null, orElse: () => 0)! +
1;
} on PaperlessServerException catch (_) {
throw const PaperlessServerException(ErrorCode.documentAsnQueryFailed);
}
}
@override
Future<Iterable<int>> bulkAction(BulkAction action) async {
final response = await baseClient.post(
Uri.parse("/api/documents/bulk_edit/"),
body: json.encode(action.toJson()),
headers: {'Content-Type': 'application/json'},
);
if (response.statusCode == 200) {
return action.documentIds;
} else {
throw const PaperlessServerException(ErrorCode.documentBulkActionFailed);
}
}
@override
Future<DocumentModel> waitForConsumptionFinished(
String fileName, String title) async {
PagedSearchResult<DocumentModel> results =
await find(DocumentFilter.latestDocument);
while ((results.results.isEmpty ||
(results.results[0].originalFileName != fileName &&
results.results[0].title != title))) {
//TODO: maybe implement more intelligent retry logic or find workaround for websocket authentication...
await Future.delayed(const Duration(seconds: 2));
results = await find(DocumentFilter.latestDocument);
}
try {
return results.results.first;
} on StateError {
throw const PaperlessServerException(ErrorCode.documentUploadFailed);
}
}
@override
Future<Uint8List> download(DocumentModel document) async {
final response = await baseClient
.get(Uri.parse("/api/documents/${document.id}/download/"));
return response.bodyBytes;
}
@override
Future<DocumentMetaData> getMetaData(DocumentModel document) async {
final response = await baseClient
.get(Uri.parse("/api/documents/${document.id}/metadata/"));
return compute(
DocumentMetaData.fromJson,
jsonDecode(utf8.decode(response.bodyBytes)) as Map<String, dynamic>,
);
}
@override
Future<List<String>> autocomplete(String query, [int limit = 10]) async {
final response = await baseClient
.get(Uri.parse("/api/search/autocomplete/?query=$query&limit=$limit}"));
if (response.statusCode == 200) {
return jsonDecode(utf8.decode(response.bodyBytes)) as List<String>;
}
throw const PaperlessServerException(ErrorCode.autocompleteQueryError);
}
@override
Future<List<SimilarDocumentModel>> findSimilar(int docId) async {
final response = await baseClient
.get(Uri.parse("/api/documents/?more_like=$docId&pageSize=10"));
if (response.statusCode == 200) {
return (await compute(
PagedSearchResult<SimilarDocumentModel>.fromJson,
PagedSearchResultJsonSerializer(
jsonDecode(utf8.decode(response.bodyBytes)),
SimilarDocumentModel.fromJson,
),
))
.results;
}
throw const PaperlessServerException(ErrorCode.similarQueryError);
}
}
@@ -0,0 +1,39 @@
import 'package:paperless_api/src/models/labels/correspondent_model.dart';
import 'package:paperless_api/src/models/labels/document_type_model.dart';
import 'package:paperless_api/src/models/labels/storage_path_model.dart';
import 'package:paperless_api/src/models/labels/tag_model.dart';
///
/// Provides basic CRUD operations for labels, including:
/// <ul>
/// <li>Correspondents</li>
/// <li>Document Types</li>
/// <li>Tags</li>
/// <li>Storage Paths</li>
/// </ul>
///
abstract class PaperlessLabelsApi {
Future<Correspondent?> getCorrespondent(int id);
Future<List<Correspondent>> getCorrespondents();
Future<Correspondent> saveCorrespondent(Correspondent correspondent);
Future<Correspondent> updateCorrespondent(Correspondent correspondent);
Future<int> deleteCorrespondent(Correspondent correspondent);
Future<Tag?> getTag(int id);
Future<List<Tag>> getTags({List<int>? ids});
Future<Tag> saveTag(Tag tag);
Future<Tag> updateTag(Tag tag);
Future<int> deleteTag(Tag tag);
Future<DocumentType?> getDocumentType(int id);
Future<List<DocumentType>> getDocumentTypes();
Future<DocumentType> saveDocumentType(DocumentType type);
Future<DocumentType> updateDocumentType(DocumentType documentType);
Future<int> deleteDocumentType(DocumentType documentType);
Future<StoragePath?> getStoragePath(int id);
Future<List<StoragePath>> getStoragePaths();
Future<StoragePath> saveStoragePath(StoragePath path);
Future<StoragePath> updateStoragePath(StoragePath path);
Future<int> deleteStoragePath(StoragePath path);
}
@@ -0,0 +1,300 @@
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart';
import 'package:paperless_api/src/models/labels/correspondent_model.dart';
import 'package:paperless_api/src/models/labels/document_type_model.dart';
import 'package:paperless_api/src/models/labels/storage_path_model.dart';
import 'package:paperless_api/src/models/labels/tag_model.dart';
import 'package:paperless_api/src/models/paperless_server_exception.dart';
import 'package:paperless_api/src/modules/labels_api/paperless_labels_api.dart';
import 'package:paperless_api/src/utils.dart';
class PaperlessLabelApiImpl implements PaperlessLabelsApi {
final BaseClient client;
PaperlessLabelApiImpl(this.client);
@override
Future<Correspondent?> getCorrespondent(int id) async {
return getSingleResult(
"/api/correspondents/$id/",
Correspondent.fromJson,
ErrorCode.correspondentLoadFailed,
client: client,
);
}
@override
Future<Tag?> getTag(int id) async {
return getSingleResult(
"/api/tags/$id/",
Tag.fromJson,
ErrorCode.tagLoadFailed,
client: client,
);
}
@override
Future<List<Tag>> getTags({List<int>? ids}) async {
final results = await getCollection(
"/api/tags/?page=1&page_size=100000",
Tag.fromJson,
ErrorCode.tagLoadFailed,
client: client,
minRequiredApiVersion: 2,
);
return results
.where((element) => ids?.contains(element.id) ?? true)
.toList();
}
@override
Future<DocumentType?> getDocumentType(int id) async {
return getSingleResult(
"/api/document_types/$id/",
DocumentType.fromJson,
ErrorCode.documentTypeLoadFailed,
client: client,
);
}
@override
Future<List<Correspondent>> getCorrespondents() {
return getCollection(
"/api/correspondents/?page=1&page_size=100000",
Correspondent.fromJson,
ErrorCode.correspondentLoadFailed,
client: client,
);
}
@override
Future<List<DocumentType>> getDocumentTypes() {
return getCollection(
"/api/document_types/?page=1&page_size=100000",
DocumentType.fromJson,
ErrorCode.documentTypeLoadFailed,
client: client,
);
}
@override
Future<Correspondent> saveCorrespondent(Correspondent correspondent) async {
final response = await client.post(
Uri.parse('/api/correspondents/'),
body: jsonEncode(correspondent.toJson()),
headers: {"Content-Type": "application/json"},
encoding: Encoding.getByName("utf-8"),
);
if (response.statusCode == HttpStatus.created) {
return Correspondent.fromJson(
jsonDecode(utf8.decode(response.bodyBytes)),
);
}
throw PaperlessServerException(
ErrorCode.correspondentCreateFailed,
httpStatusCode: response.statusCode,
);
}
@override
Future<DocumentType> saveDocumentType(DocumentType type) async {
final response = await client.post(
Uri.parse('/api/document_types/'),
body: json.encode(type.toJson()),
headers: {"Content-Type": "application/json"},
encoding: Encoding.getByName("utf-8"),
);
if (response.statusCode == HttpStatus.created) {
return DocumentType.fromJson(
jsonDecode(utf8.decode(response.bodyBytes)),
);
}
throw PaperlessServerException(
ErrorCode.documentTypeCreateFailed,
httpStatusCode: response.statusCode,
);
}
@override
Future<Tag> saveTag(Tag tag) async {
final body = json.encode(tag.toJson());
final response = await client.post(
Uri.parse('/api/tags/'),
body: body,
headers: {
"Content-Type": "application/json",
"Accept": "application/json; version=2",
},
encoding: Encoding.getByName("utf-8"),
);
if (response.statusCode == HttpStatus.created) {
return Tag.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
}
throw PaperlessServerException(
ErrorCode.tagCreateFailed,
httpStatusCode: response.statusCode,
);
}
@override
Future<int> deleteCorrespondent(Correspondent correspondent) async {
assert(correspondent.id != null);
final response = await client
.delete(Uri.parse('/api/correspondents/${correspondent.id}/'));
if (response.statusCode == HttpStatus.noContent) {
return correspondent.id!;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
}
@override
Future<int> deleteDocumentType(DocumentType documentType) async {
assert(documentType.id != null);
final response = await client
.delete(Uri.parse('/api/document_types/${documentType.id}/'));
if (response.statusCode == HttpStatus.noContent) {
return documentType.id!;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
}
@override
Future<int> deleteTag(Tag tag) async {
assert(tag.id != null);
final response = await client.delete(Uri.parse('/api/tags/${tag.id}/'));
if (response.statusCode == HttpStatus.noContent) {
return tag.id!;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
}
@override
Future<Correspondent> updateCorrespondent(Correspondent correspondent) async {
assert(correspondent.id != null);
final response = await client.put(
Uri.parse('/api/correspondents/${correspondent.id}/'),
headers: {"Content-Type": "application/json"},
body: json.encode(correspondent.toJson()),
encoding: Encoding.getByName("utf-8"),
);
if (response.statusCode == HttpStatus.ok) {
return Correspondent.fromJson(
jsonDecode(utf8.decode(response.bodyBytes)));
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
}
@override
Future<DocumentType> updateDocumentType(DocumentType documentType) async {
assert(documentType.id != null);
final response = await client.put(
Uri.parse('/api/document_types/${documentType.id}/'),
headers: {"Content-Type": "application/json"},
body: json.encode(documentType.toJson()),
encoding: Encoding.getByName("utf-8"),
);
if (response.statusCode == HttpStatus.ok) {
return DocumentType.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
}
@override
Future<Tag> updateTag(Tag tag) async {
assert(tag.id != null);
final response = await client.put(
Uri.parse('/api/tags/${tag.id}/'),
headers: {
"Accept": "application/json; version=2",
"Content-Type": "application/json",
},
body: json.encode(tag.toJson()),
encoding: Encoding.getByName("utf-8"),
);
if (response.statusCode == HttpStatus.ok) {
return Tag.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
}
@override
Future<int> deleteStoragePath(StoragePath path) async {
assert(path.id != null);
final response =
await client.delete(Uri.parse('/api/storage_paths/${path.id}/'));
if (response.statusCode == HttpStatus.noContent) {
return path.id!;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
}
@override
Future<StoragePath?> getStoragePath(int id) {
return getSingleResult(
"/api/storage_paths/?page=1&page_size=100000",
StoragePath.fromJson,
ErrorCode.storagePathLoadFailed,
client: client,
);
}
@override
Future<List<StoragePath>> getStoragePaths() {
return getCollection(
"/api/storage_paths/?page=1&page_size=100000",
StoragePath.fromJson,
ErrorCode.storagePathLoadFailed,
client: client,
);
}
@override
Future<StoragePath> saveStoragePath(StoragePath path) async {
final response = await client.post(
Uri.parse('/api/storage_paths/'),
body: json.encode(path.toJson()),
headers: {"Content-Type": "application/json"},
);
if (response.statusCode == HttpStatus.created) {
return StoragePath.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
}
throw PaperlessServerException(ErrorCode.storagePathCreateFailed,
httpStatusCode: response.statusCode);
}
@override
Future<StoragePath> updateStoragePath(StoragePath path) async {
assert(path.id != null);
final response = await client.put(
Uri.parse('/api/storage_paths/${path.id}/'),
headers: {"Content-Type": "application/json"},
body: json.encode(path.toJson()),
);
if (response.statusCode == HttpStatus.ok) {
return StoragePath.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
}
throw const PaperlessServerException(ErrorCode.unknown);
}
}
@@ -0,0 +1,11 @@
export 'authentication_api/authentication_api.dart';
export 'authentication_api/authentication_api_impl.dart';
export 'labels_api/paperless_labels_api.dart';
export 'labels_api/paperless_labels_api_impl.dart';
export 'documents_api/paperless_documents_api.dart';
export 'documents_api/paperless_documents_api_impl.dart';
export 'saved_views_api/paperless_saved_views_api.dart';
export 'saved_views_api/paperless_saved_views_api_impl.dart';
export 'server_stats_api/paperless_server_stats_api.dart';
export 'server_stats_api/paperless_server_stats_api_impl.dart';
@@ -0,0 +1,8 @@
import 'package:paperless_api/src/models/saved_view_model.dart';
abstract class PaperlessSavedViewsApi {
Future<List<SavedView>> getAll();
Future<SavedView> save(SavedView view);
Future<int> delete(SavedView view);
}
@@ -0,0 +1,54 @@
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart';
import 'package:paperless_api/src/models/paperless_server_exception.dart';
import 'package:paperless_api/src/models/saved_view_model.dart';
import 'package:paperless_api/src/utils.dart';
import 'paperless_saved_views_api.dart';
class PaperlessSavedViewsApiImpl implements PaperlessSavedViewsApi {
final BaseClient client;
PaperlessSavedViewsApiImpl(this.client);
@override
Future<List<SavedView>> getAll() {
return getCollection(
"/api/saved_views/",
SavedView.fromJson,
ErrorCode.loadSavedViewsError,
client: client,
);
}
@override
Future<SavedView> save(SavedView view) async {
final response = await client.post(
Uri.parse("/api/saved_views/"),
body: jsonEncode(view.toJson()),
headers: {'Content-Type': 'application/json'},
);
if (response.statusCode == HttpStatus.created) {
return SavedView.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
}
throw PaperlessServerException(
ErrorCode.createSavedViewError,
httpStatusCode: response.statusCode,
);
}
@override
Future<int> delete(SavedView view) async {
final response =
await client.delete(Uri.parse("/api/saved_views/${view.id}/"));
if (response.statusCode == HttpStatus.noContent) {
return view.id!;
}
throw PaperlessServerException(
ErrorCode.deleteSavedViewError,
httpStatusCode: response.statusCode,
);
}
}
@@ -0,0 +1,7 @@
import 'package:paperless_api/src/models/paperless_server_information_model.dart';
import 'package:paperless_api/src/models/paperless_server_statistics_model.dart';
abstract class PaperlessServerStatsApi {
Future<PaperlessServerInformationModel> getServerInformation();
Future<PaperlessServerStatisticsModel> getServerStatistics();
}
@@ -0,0 +1,53 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:paperless_api/src/models/paperless_server_exception.dart';
import 'package:paperless_api/src/models/paperless_server_information_model.dart';
import 'package:paperless_api/src/models/paperless_server_statistics_model.dart';
import 'paperless_server_stats_api.dart';
///
/// API for retrieving information about paperless server state,
/// such as version number, and statistics including documents in
/// inbox and total number of documents.
///
class PaperlessServerStatsApiImpl implements PaperlessServerStatsApi {
final BaseClient client;
PaperlessServerStatsApiImpl(this.client);
@override
Future<PaperlessServerInformationModel> getServerInformation() async {
final response = await client.get(Uri.parse("/api/ui_settings/"));
final version =
response.headers[PaperlessServerInformationModel.versionHeader] ??
'unknown';
final apiVersion = int.tryParse(
response.headers[PaperlessServerInformationModel.apiVersionHeader] ??
'1');
final String username =
jsonDecode(utf8.decode(response.bodyBytes))['username'];
final String host = response
.headers[PaperlessServerInformationModel.hostHeader] ??
response.request?.headers[PaperlessServerInformationModel.hostHeader] ??
('${response.request?.url.host}:${response.request?.url.port}');
return PaperlessServerInformationModel(
username: username,
version: version,
apiVersion: apiVersion,
host: host,
);
}
@override
Future<PaperlessServerStatisticsModel> getServerStatistics() async {
final response = await client.get(Uri.parse('/api/statistics/'));
if (response.statusCode == 200) {
return PaperlessServerStatisticsModel.fromJson(
jsonDecode(utf8.decode(response.bodyBytes)) as Map<String, dynamic>,
);
}
throw const PaperlessServerException.unknown();
}
}