diff --git a/lib/core/model/info_message_exception.dart b/lib/core/model/info_message_exception.dart index 817954e..36ddafa 100644 --- a/lib/core/model/info_message_exception.dart +++ b/lib/core/model/info_message_exception.dart @@ -9,4 +9,9 @@ class InfoMessageException implements Exception { this.message, this.stackTrace, }); + + @override + String toString() { + return 'InfoMessageException(code: $code, message: $message, stackTrace: $stackTrace)'; + } } diff --git a/lib/core/security/session_manager.dart b/lib/core/security/session_manager.dart index 2244d34..7e50889 100644 --- a/lib/core/security/session_manager.dart +++ b/lib/core/security/session_manager.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:dio/io.dart'; import 'package:flutter/material.dart'; -import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.dart'; +import 'package:paperless_api/src/interceptor/dio_http_error_interceptor.dart'; import 'package:paperless_mobile/core/interceptor/dio_offline_interceptor.dart'; import 'package:paperless_mobile/core/interceptor/dio_unauthorized_interceptor.dart'; import 'package:paperless_mobile/core/interceptor/retry_on_connection_change_interceptor.dart'; diff --git a/lib/core/translation/error_code_localization_mapper.dart b/lib/core/translation/error_code_localization_mapper.dart index 499c08e..2fc69f4 100644 --- a/lib/core/translation/error_code_localization_mapper.dart +++ b/lib/core/translation/error_code_localization_mapper.dart @@ -82,5 +82,7 @@ String translateError(BuildContext context, ErrorCode code) { 'Could not load custom field.', //TODO: INTL ErrorCode.customFieldDeleteFailed => 'Could not delete custom field, please try again.', //TODO: INTL + ErrorCode.deleteNoteFailed => 'Could not delete note, please try again.', + ErrorCode.addNoteFailed => 'Could not create note, please try again.', }; } diff --git a/lib/features/document_details/cubit/document_details_cubit.dart b/lib/features/document_details/cubit/document_details_cubit.dart index b010b83..3c17cad 100644 --- a/lib/features/document_details/cubit/document_details_cubit.dart +++ b/lib/features/document_details/cubit/document_details_cubit.dart @@ -311,4 +311,17 @@ class DocumentDetailsCubit extends Cubit { _notifier.removeListener(this); await super.close(); } + + Future addNote(String text) async { + assert(state.status == LoadingStatus.loaded); + try { + final updatedDocument = await _api.addNote( + document: state.document!, + text: text, + ); + _notifier.notifyUpdated(updatedDocument); + } on PaperlessApiException catch (err) { + addError(TransientPaperlessApiError(code: err.code)); + } + } } diff --git a/lib/features/document_details/view/widgets/add_note_page.dart b/lib/features/document_details/view/widgets/add_note_page.dart deleted file mode 100644 index 0de1544..0000000 --- a/lib/features/document_details/view/widgets/add_note_page.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -import 'package:paperless_api/paperless_api.dart'; -import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; - -class AddNotePage extends StatefulWidget { - final DocumentModel document; - - const AddNotePage({super.key, required this.document}); - - @override - State createState() => _AddNotePageState(); -} - -class _AddNotePageState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(S.of(context)!.addNote), - ), - body: Column( - children: [ - TextField( - decoration: InputDecoration( - labelText: S.of(context)!.content, - ), - ), - ElevatedButton( - onPressed: () {}, - child: Text(S.of(context)!.save), - ), - ], - ), - ); - } -} diff --git a/lib/features/document_details/view/widgets/document_notes_widget.dart b/lib/features/document_details/view/widgets/document_notes_widget.dart index 5d472f1..189e373 100644 --- a/lib/features/document_details/view/widgets/document_notes_widget.dart +++ b/lib/features/document_details/view/widgets/document_notes_widget.dart @@ -8,18 +8,65 @@ import 'package:paperless_mobile/features/document_details/cubit/document_detail import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/helpers/message_helpers.dart'; -class DocumentNotesWidget extends StatelessWidget { +class DocumentNotesWidget extends StatefulWidget { final DocumentModel document; const DocumentNotesWidget({super.key, required this.document}); + @override + State createState() => _DocumentNotesWidgetState(); +} + +class _DocumentNotesWidgetState extends State { + final _noteContentController = TextEditingController(); + final _formKey = GlobalKey(); + @override Widget build(BuildContext context) { return SliverMainAxisGroup( slivers: [ + SliverToBoxAdapter( + child: Form( + key: _formKey, + child: Column( + children: [ + TextFormField( + controller: _noteContentController, + maxLines: null, + validator: (value) { + if (value?.isEmpty ?? true) { + return S.of(context)!.thisFieldIsRequired; + } + return null; + }, + decoration: InputDecoration( + hintText: 'Your note here...', + labelText: 'New note', + floatingLabelBehavior: FloatingLabelBehavior.always, + ), + ).padded(), + Align( + alignment: Alignment.centerRight, + child: FilledButton.icon( + icon: Icon(Icons.note_add_outlined), + label: Text("Add note"), + onPressed: () { + _formKey.currentState?.save(); + if (_formKey.currentState?.validate() ?? false) { + context + .read() + .addNote(_noteContentController.text); + } + }, + ).padded(), + ), + ], + ).padded(), + ), + ), SliverList.separated( separatorBuilder: (context, index) => const SizedBox(height: 16), itemBuilder: (context, index) { - final note = document.notes.elementAt(index); + final note = widget.document.notes.elementAt(index); return Card( // borderRadius: BorderRadius.circular(8), // elevation: 1, @@ -51,13 +98,6 @@ class DocumentNotesWidget extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - Spacer(), - IconButton( - icon: Icon(Icons.edit), - onPressed: () { - // Push edit page - }, - ), IconButton( icon: Icon(Icons.delete), onPressed: () { @@ -74,7 +114,7 @@ class DocumentNotesWidget extends StatelessWidget { ).padded(16), ); }, - itemCount: document.notes.length, + itemCount: widget.document.notes.length, ), ], ); diff --git a/lib/features/document_details/view/widgets/edit_note_page.dart b/lib/features/document_details/view/widgets/edit_note_page.dart deleted file mode 100644 index 226600f..0000000 --- a/lib/features/document_details/view/widgets/edit_note_page.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; - -class EditNotePage extends StatefulWidget { - const EditNotePage({super.key}); - - @override - State createState() => _EditNotePageState(); -} - -class _EditNotePageState extends State { - @override - Widget build(BuildContext context) { - return const Placeholder(); - } -} \ No newline at end of file diff --git a/lib/features/document_scan/cubit/document_scanner_cubit.dart b/lib/features/document_scan/cubit/document_scanner_cubit.dart index 7cf0c52..dce5108 100644 --- a/lib/features/document_scan/cubit/document_scanner_cubit.dart +++ b/lib/features/document_scan/cubit/document_scanner_cubit.dart @@ -54,7 +54,9 @@ class DocumentScannerCubit extends Cubit { Future removeScan(File file) async { try { - await file.delete(); + if (await file.exists()) { + await file.delete(); + } } catch (error, stackTrace) { throw InfoMessageException( code: ErrorCode.scanRemoveFailed, diff --git a/lib/features/document_scan/view/scanner_page.dart b/lib/features/document_scan/view/scanner_page.dart index 2bf183f..6d63430 100644 --- a/lib/features/document_scan/view/scanner_page.dart +++ b/lib/features/document_scan/view/scanner_page.dart @@ -14,6 +14,7 @@ import 'package:paperless_mobile/core/bloc/loading_status.dart'; import 'package:paperless_mobile/core/database/hive/hive_config.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/global/constants.dart'; +import 'package:paperless_mobile/core/model/info_message_exception.dart'; import 'package:paperless_mobile/core/service/file_service.dart'; import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart'; import 'package:paperless_mobile/features/document_scan/cubit/document_scanner_cubit.dart'; @@ -326,6 +327,8 @@ class _ScannerPageState extends State .removeScan(scans[index]); } on PaperlessApiException catch (error, stackTrace) { showErrorMessage(context, error, stackTrace); + } on InfoMessageException catch (error, stackTrace) { + showInfoMessage(context, error, stackTrace); } }, index: index, diff --git a/lib/routing/routes/documents_route.dart b/lib/routing/routes/documents_route.dart index 16ecc98..c26dde7 100644 --- a/lib/routing/routes/documents_route.dart +++ b/lib/routing/routes/documents_route.dart @@ -9,7 +9,6 @@ import 'package:paperless_mobile/features/document_bulk_action/view/widgets/full import 'package:paperless_mobile/features/document_bulk_action/view/widgets/fullscreen_bulk_edit_tags_widget.dart'; import 'package:paperless_mobile/features/document_details/cubit/document_details_cubit.dart'; import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart'; -import 'package:paperless_mobile/features/document_details/view/widgets/add_note_page.dart'; import 'package:paperless_mobile/features/document_edit/cubit/document_edit_cubit.dart'; import 'package:paperless_mobile/features/document_edit/view/document_edit_page.dart'; import 'package:paperless_mobile/features/documents/view/pages/document_view.dart'; @@ -211,16 +210,3 @@ class BulkEditDocumentsRoute extends GoRouteData { ); } } - -class AddNoteRoute extends GoRouteData { - final DocumentModel $extra; - - AddNoteRoute({required this.$extra}); - - @override - Widget build(BuildContext context, GoRouterState state) { - return AddNotePage( - document: $extra, - ); - } -} diff --git a/lib/routing/routes/shells/authenticated_route.dart b/lib/routing/routes/shells/authenticated_route.dart index 92eac37..9c0f585 100644 --- a/lib/routing/routes/shells/authenticated_route.dart +++ b/lib/routing/routes/shells/authenticated_route.dart @@ -85,10 +85,6 @@ part 'authenticated_route.g.dart'; path: 'preview', name: R.documentPreview, ), - TypedGoRoute( - path: 'add-note', - name: R.addNote, - ), ], ) ], diff --git a/packages/paperless_api/lib/paperless_api.dart b/packages/paperless_api/lib/paperless_api.dart index 444403f..8552f8c 100644 --- a/packages/paperless_api/lib/paperless_api.dart +++ b/packages/paperless_api/lib/paperless_api.dart @@ -4,3 +4,4 @@ export 'src/models/models.dart'; export 'src/modules/modules.dart'; export 'src/converters/converters.dart'; export 'config/hive/hive_type_ids.dart'; +export 'src/interceptor/dio_http_error_interceptor.dart'; diff --git a/lib/core/interceptor/dio_http_error_interceptor.dart b/packages/paperless_api/lib/src/interceptor/dio_http_error_interceptor.dart similarity index 100% rename from lib/core/interceptor/dio_http_error_interceptor.dart rename to packages/paperless_api/lib/src/interceptor/dio_http_error_interceptor.dart diff --git a/packages/paperless_api/lib/src/models/document_filter.dart b/packages/paperless_api/lib/src/models/document_filter.dart index fb9d8a9..a2dace3 100644 --- a/packages/paperless_api/lib/src/models/document_filter.dart +++ b/packages/paperless_api/lib/src/models/document_filter.dart @@ -125,8 +125,8 @@ class DocumentFilter extends Equatable { return queryParams; } - @override - String toString() => toQueryParameters().toString(); + // @override + // String toString() => toQueryParameters().toString(); DocumentFilter copyWith({ int? pageSize, @@ -249,9 +249,4 @@ class DocumentFilter extends Equatable { moreLike, selectedView, ]; - - // factory DocumentFilter.fromJson(Map json) => - // _$DocumentFilterFromJson(json); - - // Map toJson() => _$DocumentFilterToJson(this); } diff --git a/packages/paperless_api/lib/src/models/filter_rule_model.dart b/packages/paperless_api/lib/src/models/filter_rule_model.dart index f974351..1f84e55 100644 --- a/packages/paperless_api/lib/src/models/filter_rule_model.dart +++ b/packages/paperless_api/lib/src/models/filter_rule_model.dart @@ -82,7 +82,6 @@ class FilterRule with EquatableMixin { assert(filter.tags is IdsTagsQuery); return filter.copyWith( tags: switch (filter.tags) { - // TODO: Handle this case. IdsTagsQuery(include: var i, exclude: var e) => IdsTagsQuery( include: [...i, int.parse(value!)], exclude: e, diff --git a/packages/paperless_api/lib/src/models/models.dart b/packages/paperless_api/lib/src/models/models.dart index 77f4147..fbb67f8 100644 --- a/packages/paperless_api/lib/src/models/models.dart +++ b/packages/paperless_api/lib/src/models/models.dart @@ -28,4 +28,4 @@ export 'task/task.dart'; export 'task/task_status.dart'; export 'user_model.dart'; export 'exception/exceptions.dart'; -export 'note_model.dart'; +export 'note_model.dart' show NoteModel; diff --git a/packages/paperless_api/lib/src/models/note_model.dart b/packages/paperless_api/lib/src/models/note_model.dart index 62fda21..e5ebc6e 100644 --- a/packages/paperless_api/lib/src/models/note_model.dart +++ b/packages/paperless_api/lib/src/models/note_model.dart @@ -1,3 +1,5 @@ +// ignore_for_file: invalid_annotation_target + import 'package:freezed_annotation/freezed_annotation.dart'; part 'note_model.freezed.dart'; part 'note_model.g.dart'; @@ -9,9 +11,19 @@ class NoteModel with _$NoteModel { required String? note, required DateTime? created, required int? document, - required int? user, + @JsonKey(fromJson: parseNoteUserFromJson) required int? user, }) = _NoteModel; factory NoteModel.fromJson(Map json) => _$NoteModelFromJson(json); } + +int? parseNoteUserFromJson(dynamic json) { + if (json == null) return null; + if (json is Map) { + return json['id']; + } else if (json is int) { + return json; + } + return null; +} diff --git a/packages/paperless_api/lib/src/models/paperless_api_exception.dart b/packages/paperless_api/lib/src/models/paperless_api_exception.dart index ad60cd0..fde8e10 100644 --- a/packages/paperless_api/lib/src/models/paperless_api_exception.dart +++ b/packages/paperless_api/lib/src/models/paperless_api_exception.dart @@ -11,7 +11,16 @@ class PaperlessApiException implements Exception { this.httpStatusCode, }); - const PaperlessApiException.unknown() : this(ErrorCode.unknown); + const PaperlessApiException.unknown({ + String? details, + StackTrace? stackTrace, + int? httpStatusCode, + }) : this( + ErrorCode.unknown, + details: details, + stackTrace: stackTrace, + httpStatusCode: httpStatusCode, + ); @override String toString() { @@ -71,5 +80,7 @@ enum ErrorCode { updateSavedViewError, customFieldCreateFailed, customFieldLoadFailed, - customFieldDeleteFailed; + customFieldDeleteFailed, + deleteNoteFailed, + addNoteFailed; } diff --git a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/date_range_query.dart b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/date_range_query.dart index 3396aa2..d232aba 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/date_range_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/date_range_query.dart @@ -11,7 +11,7 @@ import 'date_range_unit.dart'; part 'date_range_query.g.dart'; -sealed class DateRangeQuery { +sealed class DateRangeQuery with EquatableMixin { const DateRangeQuery(); Map toQueryParameter(DateRangeQueryField field); @@ -28,10 +28,13 @@ class UnsetDateRangeQuery extends DateRangeQuery { @override bool matches(DateTime dt) => true; + + @override + List get props => []; } @HiveType(typeId: PaperlessApiHiveTypeIds.relativeDateRangeQuery) -class RelativeDateRangeQuery extends DateRangeQuery with EquatableMixin { +class RelativeDateRangeQuery extends DateRangeQuery { @HiveField(0) final int offset; @HiveField(1) @@ -84,7 +87,7 @@ class RelativeDateRangeQuery extends DateRangeQuery with EquatableMixin { @JsonSerializable() @HiveType(typeId: PaperlessApiHiveTypeIds.absoluteDateRangeQuery) -class AbsoluteDateRangeQuery extends DateRangeQuery with EquatableMixin { +class AbsoluteDateRangeQuery extends DateRangeQuery { @LocalDateTimeJsonConverter() @HiveField(0) final DateTime? after; diff --git a/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.dart b/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.dart index 6db3cd5..4b0a694 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.dart @@ -4,7 +4,7 @@ import 'package:paperless_api/config/hive/hive_type_ids.dart'; part 'id_query_parameter.g.dart'; -sealed class IdQueryParameter { +sealed class IdQueryParameter with EquatableMixin { const IdQueryParameter(); Map toQueryParameter(String field); bool matches(int? id); @@ -23,6 +23,9 @@ class UnsetIdQueryParameter extends IdQueryParameter { @override bool matches(int? id) => true; + + @override + List get props => []; } // @HiveType(typeId: PaperlessApiHiveTypeIds.notAssignedIdQueryParameter) @@ -36,6 +39,8 @@ class NotAssignedIdQueryParameter extends IdQueryParameter { @override bool matches(int? id) => id == null; + @override + List get props => []; } // @HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedIdQueryParameter) @@ -48,6 +53,8 @@ class AnyAssignedIdQueryParameter extends IdQueryParameter { @override bool matches(int? id) => id != null; + @override + List get props => []; } @HiveType(typeId: PaperlessApiHiveTypeIds.setIdQueryParameter) diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart index cfa0187..438c999 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart @@ -4,7 +4,7 @@ import 'package:paperless_api/config/hive/hive_type_ids.dart'; part 'tags_query.g.dart'; -sealed class TagsQuery { +sealed class TagsQuery with EquatableMixin { const TagsQuery(); Map toQueryParameter(); bool matches(Iterable ids); @@ -20,10 +20,13 @@ class NotAssignedTagsQuery extends TagsQuery { @override bool matches(Iterable ids) => ids.isEmpty; + + @override + List get props => []; } @HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedTagsQuery) -class AnyAssignedTagsQuery extends TagsQuery with EquatableMixin { +class AnyAssignedTagsQuery extends TagsQuery { @HiveField(0) final List tagIds; const AnyAssignedTagsQuery({ @@ -54,7 +57,7 @@ class AnyAssignedTagsQuery extends TagsQuery with EquatableMixin { } @HiveType(typeId: PaperlessApiHiveTypeIds.idsTagsQuery) -class IdsTagsQuery extends TagsQuery with EquatableMixin { +class IdsTagsQuery extends TagsQuery { @HiveField(0) final List include; @HiveField(1) diff --git a/packages/paperless_api/lib/src/models/query_parameters/text_query.dart b/packages/paperless_api/lib/src/models/query_parameters/text_query.dart index 7c31e68..b5716f1 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/text_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/text_query.dart @@ -1,3 +1,4 @@ +import 'package:equatable/equatable.dart'; import 'package:hive/hive.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_api/config/hive/hive_type_ids.dart'; @@ -91,6 +92,11 @@ class TextQuery { return other.queryText == queryText && other.queryType == queryType; } + @override + String toString() { + return "TextQuery($queryText, $queryType)"; + } + @override int get hashCode => Object.hash(queryText, queryType); } diff --git a/packages/paperless_api/lib/src/modules/authentication_api/authentication_api.dart b/packages/paperless_api/lib/src/modules/authentication_api/authentication_api.dart index 7735979..f1d685e 100644 --- a/packages/paperless_api/lib/src/modules/authentication_api/authentication_api.dart +++ b/packages/paperless_api/lib/src/modules/authentication_api/authentication_api.dart @@ -1,9 +1,4 @@ -import 'package:paperless_api/src/models/exception/exceptions.dart'; - abstract class PaperlessAuthenticationApi { - /// - /// @throws [PaperlessUnauthorizedException] - /// Future login({ required String username, required String password, diff --git a/packages/paperless_api/lib/src/modules/authentication_api/authentication_api_impl.dart b/packages/paperless_api/lib/src/modules/authentication_api/authentication_api_impl.dart index 3ca527a..ecccb06 100644 --- a/packages/paperless_api/lib/src/modules/authentication_api/authentication_api_impl.dart +++ b/packages/paperless_api/lib/src/modules/authentication_api/authentication_api_impl.dart @@ -37,6 +37,11 @@ class PaperlessAuthenticationApiImpl implements PaperlessAuthenticationApi { // return AuthenticationTemporaryRedirect(redirectUrl!); } on DioException catch (exception) { throw exception.unravel(); + } catch (error, stackTrace) { + throw PaperlessApiException.unknown( + details: error.toString(), + stackTrace: stackTrace, + ); } } } diff --git a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api.dart b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api.dart index 2bbb9dc..37a228d 100644 --- a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api.dart +++ b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api.dart @@ -36,4 +36,7 @@ abstract class PaperlessDocumentsApi { Future findSuggestions(DocumentModel document); Future> autocomplete(String query, [int limit = 10]); + + Future addNote( + {required DocumentModel document, required String text}); } diff --git a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart index 36cb080..5b746cc 100644 --- a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart +++ b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart @@ -337,7 +337,30 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi { return document.copyWith(notes: notes); } on DioException catch (exception) { throw exception.unravel( - orElse: const PaperlessApiException(ErrorCode.documentDeleteFailed), + orElse: const PaperlessApiException(ErrorCode.deleteNoteFailed), + ); + } + } + + @override + Future addNote({ + required DocumentModel document, + required String text, + }) async { + try { + final response = await client.post( + "/api/documents/${document.id}/notes/", + options: Options(validateStatus: (status) => status == 200), + data: {'note': text}, + ); + + final notes = + (response.data as List).map((e) => NoteModel.fromJson(e)).toList(); + + return document.copyWith(notes: notes); + } on DioException catch (exception) { + throw exception.unravel( + orElse: const PaperlessApiException(ErrorCode.addNoteFailed), ); } } diff --git a/packages/paperless_api/pubspec.yaml b/packages/paperless_api/pubspec.yaml index 2ae45da..259568d 100644 --- a/packages/paperless_api/pubspec.yaml +++ b/packages/paperless_api/pubspec.yaml @@ -22,6 +22,8 @@ dependencies: jiffy: ^5.0.0 freezed_annotation: ^2.4.1 hive: ^2.2.3 + mockito: ^5.4.4 + http_mock_adapter: ^0.6.1 dev_dependencies: flutter_test: diff --git a/packages/paperless_api/test/api/authentication/login_test.dart b/packages/paperless_api/test/api/authentication/login_test.dart new file mode 100644 index 0000000..776c7bf --- /dev/null +++ b/packages/paperless_api/test/api/authentication/login_test.dart @@ -0,0 +1,90 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:http_mock_adapter/http_mock_adapter.dart'; +import 'package:mockito/mockito.dart'; +import 'package:paperless_api/paperless_api.dart'; + +void main() { + group('AuthenticationApi with DioHttpErrorIncerceptor', () { + late PaperlessAuthenticationApi authenticationApi; + late DioAdapter mockAdapter; + const token = "abcde"; + const invalidCredentialsServerMessage = + "Unable to log in with provided credentials."; + + setUp(() { + final dio = Dio()..interceptors.add(DioHttpErrorInterceptor()); + authenticationApi = PaperlessAuthenticationApiImpl(dio); + mockAdapter = DioAdapter(dio: dio); + // Valid credentials + mockAdapter.onPost( + "/api/token/", + data: { + "username": "username", + "password": "password", + }, + (server) => server.reply(200, {"token": token}), + ); + // Invalid credentials + mockAdapter.onPost( + "/api/token/", + data: { + "username": "wrongUsername", + "password": "wrongPassword", + }, + (server) => server.reply(400, { + "non_field_errors": [invalidCredentialsServerMessage] + }), + ); + }); + + // tearDown(() {}); + test( + 'should return a valid token when logging in with valid credentials', + () { + expect( + authenticationApi.login( + username: "username", + password: "password", + ), + completion(token), + ); + }, + ); + + test( + 'should throw a PaperlessFormValidationException containing a reason ' + 'when logging in with invalid credentials', + () { + expect( + authenticationApi.login( + username: "wrongUsername", + password: "wrongPassword", + ), + throwsA(isA().having( + (e) => e.unspecificErrorMessage(), + "non-field specific error message", + equals(invalidCredentialsServerMessage), + )), + ); + }, + ); + + test( + 'should return an error when logging in with invalid credentials', + () { + expect( + authenticationApi.login( + username: "wrongUsername", + password: "wrongPassword", + ), + throwsA(isA().having( + (e) => e.unspecificErrorMessage(), + "non-field specific error message", + equals(invalidCredentialsServerMessage), + )), + ); + }, + ); + }); +} diff --git a/packages/paperless_api/test/saved_view_test.dart b/packages/paperless_api/test/parsing/saved_view_test.dart similarity index 94% rename from packages/paperless_api/test/saved_view_test.dart rename to packages/paperless_api/test/parsing/saved_view_test.dart index 0e1abf0..63a0c39 100644 --- a/packages/paperless_api/test/saved_view_test.dart +++ b/packages/paperless_api/test/parsing/saved_view_test.dart @@ -2,7 +2,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:paperless_api/paperless_api.dart'; void main() { - group('Validate parsing logic from [SavedView] to [DocumentFilter]:', () { + group('Parsing [SavedView] to [DocumentFilter]:', () { test('Values are correctly parsed if set.', () { expect( SavedView.fromJson({ @@ -64,7 +64,7 @@ void main() { ] }).toDocumentFilter(), equals( - DocumentFilter.initial.copyWith( + DocumentFilter( correspondent: const SetIdQueryParameter(id: 42), documentType: const SetIdQueryParameter(id: 69), storagePath: const SetIdQueryParameter(id: 14), @@ -83,6 +83,7 @@ void main() { sortField: SortField.created, sortOrder: SortOrder.descending, query: const TextQuery.extended("Never gonna give you up"), + selectedView: 1, ), ), ); @@ -99,7 +100,11 @@ void main() { "sort_reverse": true, "filter_rules": [], }).toDocumentFilter(), - equals(DocumentFilter.initial), + equals( + const DocumentFilter( + selectedView: 1, + ), + ), ); }); @@ -130,11 +135,12 @@ void main() { }, ], }).toDocumentFilter(); - final expected = DocumentFilter.initial.copyWith( - correspondent: const NotAssignedIdQueryParameter(), - documentType: const NotAssignedIdQueryParameter(), - storagePath: const NotAssignedIdQueryParameter(), - tags: const NotAssignedTagsQuery(), + const expected = DocumentFilter( + correspondent: NotAssignedIdQueryParameter(), + documentType: NotAssignedIdQueryParameter(), + storagePath: NotAssignedIdQueryParameter(), + tags: NotAssignedTagsQuery(), + selectedView: 1, ); expect( actual, @@ -148,6 +154,7 @@ void main() { expect( SavedView.fromDocumentFilter( DocumentFilter( + selectedView: 1, correspondent: const SetIdQueryParameter(id: 1), documentType: const SetIdQueryParameter(id: 2), storagePath: const SetIdQueryParameter(id: 3), @@ -173,6 +180,7 @@ void main() { ), equals( SavedView( + id: 1, name: "test_name", showOnDashboard: false, showInSidebar: false,