mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2026-02-01 14:25:04 -06:00
Externalized API and models as own package
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
abstract class PaperlessAuthenticationApi {
|
||||
Future<String> login({
|
||||
required String username,
|
||||
required String password,
|
||||
required String serverUrl,
|
||||
});
|
||||
}
|
||||
+53
@@ -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]);
|
||||
}
|
||||
+282
@@ -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);
|
||||
}
|
||||
+54
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
+7
@@ -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();
|
||||
}
|
||||
+53
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user