WIP - Redesigned login flow

This commit is contained in:
Anton Stubenbord
2023-01-05 01:38:00 +01:00
parent 2445c97d44
commit 738ef99bc5
60 changed files with 1159 additions and 755 deletions

View File

@@ -15,7 +15,7 @@ class PaperlessServerException implements Exception {
@override
String toString() {
return "ErrorMessage(code: $code${stackTrace != null ? ', stackTrace: ${stackTrace.toString()}' : ''}${httpStatusCode != null ? ', httpStatusCode: $httpStatusCode' : ''})";
return "PaperlessServerException(code: $code${stackTrace != null ? ', stackTrace: ${stackTrace.toString()}' : ''}${httpStatusCode != null ? ', httpStatusCode: $httpStatusCode' : ''})";
}
}

View File

@@ -24,11 +24,8 @@ class PaperlessAuthenticationApiImpl implements PaperlessAuthenticationApi {
},
);
} on DioError catch (error) {
if (error.error is ErrorCode) {
throw PaperlessServerException(
error.error,
httpStatusCode: error.response?.statusCode,
);
if (error.error is PaperlessServerException) {
throw error.error;
} else {
log(error.message);
throw PaperlessServerException(

View File

@@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:developer';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
@@ -42,58 +43,74 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
for (final tag in tags) {
formData.fields.add(MapEntry('tags', tag.toString()));
}
try {
final response =
await client.post('/api/documents/post_document/', data: formData);
final response =
await client.post('/api/documents/post_document/', data: formData);
if (response.statusCode != 200) {
throw PaperlessServerException(
ErrorCode.documentUploadFailed,
httpStatusCode: response.statusCode,
);
if (response.statusCode != 200) {
throw PaperlessServerException(
ErrorCode.documentUploadFailed,
httpStatusCode: response.statusCode,
);
}
} on DioError catch (err) {
throw err.error;
}
}
@override
Future<DocumentModel> update(DocumentModel doc) async {
final response = await client.put(
"/api/documents/${doc.id}/",
data: doc.toJson(),
);
if (response.statusCode == 200) {
return DocumentModel.fromJson(response.data);
} else {
throw const PaperlessServerException(ErrorCode.documentUpdateFailed);
try {
final response = await client.put(
"/api/documents/${doc.id}/",
data: doc.toJson(),
);
if (response.statusCode == 200) {
return DocumentModel.fromJson(response.data);
} else {
throw const PaperlessServerException(ErrorCode.documentUpdateFailed);
}
} on DioError catch (err) {
throw err.error;
}
}
@override
Future<PagedSearchResult<DocumentModel>> find(DocumentFilter filter) async {
final filterParams = filter.toQueryParameters();
final response = await client.get(
"/api/documents/",
queryParameters: filterParams,
);
if (response.statusCode == 200) {
return compute(
PagedSearchResult.fromJson,
PagedSearchResultJsonSerializer<DocumentModel>(
response.data,
DocumentModel.fromJson,
),
try {
final response = await client.get(
"/api/documents/",
queryParameters: filterParams,
);
} else {
throw const PaperlessServerException(ErrorCode.documentLoadFailed);
if (response.statusCode == 200) {
return compute(
PagedSearchResult.fromJson,
PagedSearchResultJsonSerializer<DocumentModel>(
response.data,
DocumentModel.fromJson,
),
);
} else {
throw const PaperlessServerException(ErrorCode.documentLoadFailed);
}
} on DioError catch (err) {
throw err.error;
}
}
@override
Future<int> delete(DocumentModel doc) async {
final response = await client.delete("/api/documents/${doc.id}/");
try {
final response = await client.delete("/api/documents/${doc.id}/");
if (response.statusCode == 204) {
return Future.value(doc.id);
if (response.statusCode == 204) {
return Future.value(doc.id);
}
throw const PaperlessServerException(ErrorCode.documentDeleteFailed);
} on DioError catch (err) {
throw err.error;
}
throw const PaperlessServerException(ErrorCode.documentDeleteFailed);
}
@override
@@ -107,16 +124,20 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
@override
Future<Uint8List> getPreview(int documentId) async {
final response = await client.get(
getPreviewUrl(documentId),
options: Options(
responseType:
ResponseType.bytes), //TODO: Check if bytes or stream is required
);
if (response.statusCode == 200) {
return response.data;
try {
final response = await client.get(
getPreviewUrl(documentId),
options: Options(
responseType: ResponseType
.bytes), //TODO: Check if bytes or stream is required
);
if (response.statusCode == 200) {
return response.data;
}
throw const PaperlessServerException(ErrorCode.documentPreviewFailed);
} on DioError catch (err) {
throw err.error;
}
throw const PaperlessServerException(ErrorCode.documentPreviewFailed);
}
@override
@@ -134,21 +155,29 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
.map((e) => e.archiveSerialNumber)
.firstWhere((asn) => asn != null, orElse: () => 0)! +
1;
} on PaperlessServerException catch (_) {
} on PaperlessServerException {
throw const PaperlessServerException(ErrorCode.documentAsnQueryFailed);
} on DioError catch (err) {
throw err.error;
}
}
@override
Future<Iterable<int>> bulkAction(BulkAction action) async {
final response = await client.post(
"/api/documents/bulk_edit/",
data: action.toJson(),
);
if (response.statusCode == 200) {
return action.documentIds;
} else {
throw const PaperlessServerException(ErrorCode.documentBulkActionFailed);
try {
final response = await client.post(
"/api/documents/bulk_edit/",
data: action.toJson(),
);
if (response.statusCode == 200) {
return action.documentIds;
} else {
throw const PaperlessServerException(
ErrorCode.documentBulkActionFailed,
);
}
} on DioError catch (err) {
throw err.error;
}
}
@@ -174,53 +203,68 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
@override
Future<Uint8List> download(DocumentModel document) async {
//TODO: Add missing error handling
final response = await client.get(
"/api/documents/${document.id}/download/",
options: Options(responseType: ResponseType.bytes),
);
return response.data;
try {
final response = await client.get(
"/api/documents/${document.id}/download/",
options: Options(responseType: ResponseType.bytes),
);
return response.data;
} on DioError catch (err) {
throw err.error;
}
}
@override
Future<DocumentMetaData> getMetaData(DocumentModel document) async {
final response =
await client.get("/api/documents/${document.id}/metadata/");
return compute(
DocumentMetaData.fromJson,
response.data as Map<String, dynamic>,
);
try {
final response =
await client.get("/api/documents/${document.id}/metadata/");
return compute(
DocumentMetaData.fromJson,
response.data as Map<String, dynamic>,
);
} on DioError catch (err) {
throw err.error;
}
}
@override
Future<List<String>> autocomplete(String query, [int limit = 10]) async {
final response = await client.get(
'/api/search/autocomplete/',
queryParameters: {
'query': query,
'limit': limit,
},
);
if (response.statusCode == 200) {
return response.data as List<String>;
try {
final response = await client.get(
'/api/search/autocomplete/',
queryParameters: {
'query': query,
'limit': limit,
},
);
if (response.statusCode == 200) {
return response.data as List<String>;
}
throw const PaperlessServerException(ErrorCode.autocompleteQueryError);
} on DioError catch (err) {
throw err.error;
}
throw const PaperlessServerException(ErrorCode.autocompleteQueryError);
}
@override
Future<List<SimilarDocumentModel>> findSimilar(int docId) async {
final response =
await client.get("/api/documents/?more_like=$docId&pageSize=10");
if (response.statusCode == 200) {
return (await compute(
PagedSearchResult<SimilarDocumentModel>.fromJson,
PagedSearchResultJsonSerializer(
response.data,
SimilarDocumentModel.fromJson,
),
))
.results;
try {
final response =
await client.get("/api/documents/?more_like=$docId&pageSize=10");
if (response.statusCode == 200) {
return (await compute(
PagedSearchResult<SimilarDocumentModel>.fromJson,
PagedSearchResultJsonSerializer(
response.data,
SimilarDocumentModel.fromJson,
),
))
.results;
}
throw const PaperlessServerException(ErrorCode.similarQueryError);
} on DioError catch (err) {
throw err.error;
}
throw const PaperlessServerException(ErrorCode.similarQueryError);
}
}

View File

@@ -13,16 +13,16 @@ import 'package:paperless_api/src/request_utils.dart';
//Notes:
// Removed content type json header
class PaperlessLabelApiImpl implements PaperlessLabelsApi {
final Dio client;
final Dio _client;
PaperlessLabelApiImpl(this.client);
PaperlessLabelApiImpl(this._client);
@override
Future<Correspondent?> getCorrespondent(int id) async {
return getSingleResult(
"/api/correspondents/$id/",
Correspondent.fromJson,
ErrorCode.correspondentLoadFailed,
client: client,
client: _client,
);
}
@@ -32,7 +32,7 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
"/api/tags/$id/",
Tag.fromJson,
ErrorCode.tagLoadFailed,
client: client,
client: _client,
);
}
@@ -42,7 +42,7 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
"/api/tags/?page=1&page_size=100000",
Tag.fromJson,
ErrorCode.tagLoadFailed,
client: client,
client: _client,
minRequiredApiVersion: 2,
);
return results
@@ -56,7 +56,7 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
"/api/document_types/$id/",
DocumentType.fromJson,
ErrorCode.documentTypeLoadFailed,
client: client,
client: _client,
);
}
@@ -66,7 +66,7 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
"/api/correspondents/?page=1&page_size=100000",
Correspondent.fromJson,
ErrorCode.correspondentLoadFailed,
client: client,
client: _client,
);
return results
@@ -80,7 +80,7 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
"/api/document_types/?page=1&page_size=100000",
DocumentType.fromJson,
ErrorCode.documentTypeLoadFailed,
client: client,
client: _client,
);
return results
@@ -90,151 +90,191 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
@override
Future<Correspondent> saveCorrespondent(Correspondent correspondent) async {
final response = await client.post(
'/api/correspondents/',
data: correspondent.toJson(),
);
if (response.statusCode == HttpStatus.created) {
return Correspondent.fromJson(response.data);
try {
final response = await _client.post(
'/api/correspondents/',
data: correspondent.toJson(),
);
if (response.statusCode == HttpStatus.created) {
return Correspondent.fromJson(response.data);
}
throw PaperlessServerException(
ErrorCode.correspondentCreateFailed,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
ErrorCode.correspondentCreateFailed,
httpStatusCode: response.statusCode,
);
}
@override
Future<DocumentType> saveDocumentType(DocumentType type) async {
final response = await client.post(
'/api/document_types/',
data: type.toJson(),
);
if (response.statusCode == HttpStatus.created) {
return DocumentType.fromJson(response.data);
try {
final response = await _client.post(
'/api/document_types/',
data: type.toJson(),
);
if (response.statusCode == HttpStatus.created) {
return DocumentType.fromJson(response.data);
}
throw PaperlessServerException(
ErrorCode.documentTypeCreateFailed,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
ErrorCode.documentTypeCreateFailed,
httpStatusCode: response.statusCode,
);
}
@override
Future<Tag> saveTag(Tag tag) async {
final response = await client.post(
'/api/tags/',
data: tag.toJson(),
options: Options(headers: {"Accept": "application/json; version=2"}),
);
if (response.statusCode == HttpStatus.created) {
return Tag.fromJson(response.data);
try {
final response = await _client.post(
'/api/tags/',
data: tag.toJson(),
options: Options(headers: {"Accept": "application/json; version=2"}),
);
if (response.statusCode == HttpStatus.created) {
return Tag.fromJson(response.data);
}
throw PaperlessServerException(
ErrorCode.tagCreateFailed,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
ErrorCode.tagCreateFailed,
httpStatusCode: response.statusCode,
);
}
@override
Future<int> deleteCorrespondent(Correspondent correspondent) async {
assert(correspondent.id != null);
final response =
await client.delete('/api/correspondents/${correspondent.id}/');
if (response.statusCode == HttpStatus.noContent) {
return correspondent.id!;
try {
final response =
await _client.delete('/api/correspondents/${correspondent.id}/');
if (response.statusCode == HttpStatus.noContent) {
return correspondent.id!;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
}
@override
Future<int> deleteDocumentType(DocumentType documentType) async {
assert(documentType.id != null);
final response =
await client.delete('/api/document_types/${documentType.id}/');
if (response.statusCode == HttpStatus.noContent) {
return documentType.id!;
try {
final response =
await _client.delete('/api/document_types/${documentType.id}/');
if (response.statusCode == HttpStatus.noContent) {
return documentType.id!;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
}
@override
Future<int> deleteTag(Tag tag) async {
assert(tag.id != null);
final response = await client.delete('/api/tags/${tag.id}/');
if (response.statusCode == HttpStatus.noContent) {
return tag.id!;
try {
final response = await _client.delete('/api/tags/${tag.id}/');
if (response.statusCode == HttpStatus.noContent) {
return tag.id!;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
}
@override
Future<Correspondent> updateCorrespondent(Correspondent correspondent) async {
assert(correspondent.id != null);
final response = await client.put(
'/api/correspondents/${correspondent.id}/',
data: json.encode(correspondent.toJson()),
);
if (response.statusCode == HttpStatus.ok) {
return Correspondent.fromJson(response.data);
try {
final response = await _client.put(
'/api/correspondents/${correspondent.id}/',
data: json.encode(correspondent.toJson()),
);
if (response.statusCode == HttpStatus.ok) {
return Correspondent.fromJson(response.data);
}
throw PaperlessServerException(
ErrorCode.unknown, //TODO: Add correct error code mapping.
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
ErrorCode.unknown, //TODO: Add correct error code mapping.
httpStatusCode: response.statusCode,
);
}
@override
Future<DocumentType> updateDocumentType(DocumentType documentType) async {
assert(documentType.id != null);
final response = await client.put(
'/api/document_types/${documentType.id}/',
data: documentType.toJson(),
);
if (response.statusCode == HttpStatus.ok) {
return DocumentType.fromJson(response.data);
try {
final response = await _client.put(
'/api/document_types/${documentType.id}/',
data: documentType.toJson(),
);
if (response.statusCode == HttpStatus.ok) {
return DocumentType.fromJson(response.data);
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
}
@override
Future<Tag> updateTag(Tag tag) async {
assert(tag.id != null);
final response = await client.put(
'/api/tags/${tag.id}/',
options: Options(headers: {"Accept": "application/json; version=2"}),
data: tag.toJson(),
);
if (response.statusCode == HttpStatus.ok) {
return Tag.fromJson(response.data);
try {
final response = await _client.put(
'/api/tags/${tag.id}/',
options: Options(headers: {"Accept": "application/json; version=2"}),
data: tag.toJson(),
);
if (response.statusCode == HttpStatus.ok) {
return Tag.fromJson(response.data);
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
}
@override
Future<int> deleteStoragePath(StoragePath path) async {
assert(path.id != null);
final response = await client.delete('/api/storage_paths/${path.id}/');
if (response.statusCode == HttpStatus.noContent) {
return path.id!;
try {
final response = await _client.delete('/api/storage_paths/${path.id}/');
if (response.statusCode == HttpStatus.noContent) {
return path.id!;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
ErrorCode.unknown,
httpStatusCode: response.statusCode,
);
}
@override
@@ -243,7 +283,7 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
"/api/storage_paths/$id/",
StoragePath.fromJson,
ErrorCode.storagePathLoadFailed,
client: client,
client: _client,
);
}
@@ -253,7 +293,7 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
"/api/storage_paths/?page=1&page_size=100000",
StoragePath.fromJson,
ErrorCode.storagePathLoadFailed,
client: client,
client: _client,
);
return results
@@ -263,27 +303,37 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
@override
Future<StoragePath> saveStoragePath(StoragePath path) async {
final response = await client.post(
'/api/storage_paths/',
data: path.toJson(),
);
if (response.statusCode == HttpStatus.created) {
return StoragePath.fromJson(response.data);
try {
final response = await _client.post(
'/api/storage_paths/',
data: path.toJson(),
);
if (response.statusCode == HttpStatus.created) {
return StoragePath.fromJson(response.data);
}
throw PaperlessServerException(
ErrorCode.storagePathCreateFailed,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(ErrorCode.storagePathCreateFailed,
httpStatusCode: response.statusCode);
}
@override
Future<StoragePath> updateStoragePath(StoragePath path) async {
assert(path.id != null);
final response = await client.put(
'/api/storage_paths/${path.id}/',
data: path.toJson(),
);
if (response.statusCode == HttpStatus.ok) {
return StoragePath.fromJson(response.data);
try {
final response = await _client.put(
'/api/storage_paths/${path.id}/',
data: path.toJson(),
);
if (response.statusCode == HttpStatus.ok) {
return StoragePath.fromJson(response.data);
}
throw const PaperlessServerException(ErrorCode.unknown);
} on DioError catch (err) {
throw err.error;
}
throw const PaperlessServerException(ErrorCode.unknown);
}
}

View File

@@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
@@ -9,9 +8,9 @@ import 'package:paperless_api/src/request_utils.dart';
import 'paperless_saved_views_api.dart';
class PaperlessSavedViewsApiImpl implements PaperlessSavedViewsApi {
final Dio client;
final Dio _client;
PaperlessSavedViewsApiImpl(this.client);
PaperlessSavedViewsApiImpl(this._client);
@override
Future<Iterable<SavedView>> findAll([Iterable<int>? ids]) async {
@@ -19,7 +18,7 @@ class PaperlessSavedViewsApiImpl implements PaperlessSavedViewsApi {
"/api/saved_views/",
SavedView.fromJson,
ErrorCode.loadSavedViewsError,
client: client,
client: _client,
);
return result.where((view) => ids?.contains(view.id!) ?? true);
@@ -27,29 +26,37 @@ class PaperlessSavedViewsApiImpl implements PaperlessSavedViewsApi {
@override
Future<SavedView> save(SavedView view) async {
final response = await client.post(
"/api/saved_views/",
data: view.toJson(),
);
if (response.statusCode == HttpStatus.created) {
return SavedView.fromJson(response.data);
try {
final response = await _client.post(
"/api/saved_views/",
data: view.toJson(),
);
if (response.statusCode == HttpStatus.created) {
return SavedView.fromJson(response.data);
}
throw PaperlessServerException(
ErrorCode.createSavedViewError,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
ErrorCode.createSavedViewError,
httpStatusCode: response.statusCode,
);
}
@override
Future<int> delete(SavedView view) async {
final response = await client.delete("/api/saved_views/${view.id}/");
if (response.statusCode == HttpStatus.noContent) {
return view.id!;
try {
final response = await _client.delete("/api/saved_views/${view.id}/");
if (response.statusCode == HttpStatus.noContent) {
return view.id!;
}
throw PaperlessServerException(
ErrorCode.deleteSavedViewError,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
ErrorCode.deleteSavedViewError,
httpStatusCode: response.statusCode,
);
}
@override
@@ -58,7 +65,7 @@ class PaperlessSavedViewsApiImpl implements PaperlessSavedViewsApi {
"/api/saved_views/$id/",
SavedView.fromJson,
ErrorCode.loadSavedViewsError,
client: client,
client: _client,
);
}
}

View File

@@ -11,22 +11,26 @@ Future<T> getSingleResult<T>(
required Dio client,
int minRequiredApiVersion = 1,
}) async {
final response = await client.get(
url,
options: Options(
headers: {'accept': 'application/json; version=$minRequiredApiVersion'},
),
);
if (response.statusCode == HttpStatus.ok) {
return compute(
fromJson,
response.data as Map<String, dynamic>,
try {
final response = await client.get(
url,
options: Options(
headers: {'accept': 'application/json; version=$minRequiredApiVersion'},
),
);
if (response.statusCode == HttpStatus.ok) {
return compute(
fromJson,
response.data as Map<String, dynamic>,
);
}
throw PaperlessServerException(
errorCode,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
errorCode,
httpStatusCode: response.statusCode,
);
}
Future<List<T>> getCollection<T>(
@@ -36,30 +40,34 @@ Future<List<T>> getCollection<T>(
required Dio client,
int minRequiredApiVersion = 1,
}) async {
final response = await client.get(
url,
options: Options(headers: {
'accept': 'application/json; version=$minRequiredApiVersion'
}),
);
if (response.statusCode == HttpStatus.ok) {
final Map<String, dynamic> body = response.data;
if (body.containsKey('count')) {
if (body['count'] == 0) {
return <T>[];
} else {
return compute(
_collectionFromJson,
_CollectionFromJsonSerializationParams(
fromJson, (body['results'] as List).cast<Map<String, dynamic>>()),
);
try {
final response = await client.get(
url,
options: Options(headers: {
'accept': 'application/json; version=$minRequiredApiVersion'
}),
);
if (response.statusCode == HttpStatus.ok) {
final Map<String, dynamic> body = response.data;
if (body.containsKey('count')) {
if (body['count'] == 0) {
return <T>[];
} else {
return compute(
_collectionFromJson,
_CollectionFromJsonSerializationParams(fromJson,
(body['results'] as List).cast<Map<String, dynamic>>()),
);
}
}
}
throw PaperlessServerException(
errorCode,
httpStatusCode: response.statusCode,
);
} on DioError catch (err) {
throw err.error;
}
throw PaperlessServerException(
errorCode,
httpStatusCode: response.statusCode,
);
}
List<T> _collectionFromJson<T>(