Skip to content

Commit

Permalink
Make "SafeCqrs" the default CQRS client (#148)
Browse files Browse the repository at this point in the history
* Add cqrs_wrapper package

* Add cqrs dependency

* Add CqrsWrapper class

* Add CqrsError classes

* Add equatable dependency

* Update readme

* Add CqrsResult classes

* Implement CqrsResult sealed classes

* Add isInvalid check to CqrsResult

* Differentiate command and query results

* Implement noThrowGet and noThrowRun

* Make cqrs results more generic

* Rewrite Cqrs errors

* Rewrite CqrsResult

* Simplify Cqrs errors to just enums

* Add missing error handlers to CqrsWrapper

* Add isFailure getter to CqrsCommandResult

* Implement missing cases for error handling in CqrsWrapper

* Add initial library docs

* Move CqrsWrapper to cqrs library

* Add initial docs for CqrsWrapper and make logger optional

* Add docs for CqrsWrapper.noThrowGet

* Add headers argument to noThrowGet and noThrowRun

* Add docs for CqrsWrapper

* Add initial docs for CqrsResult

* Make error argument in CqrsCommandResult.nonValidationError non nullable

* Refactor CqrsResult

* Refactor CqrsWrapper

* Make CqrsWrapper implement Cqrs

* Add docs for CqrsResult

* Add CqrsCommandResultValidationErrorExtension

* Add connectivity_plus to deps

* Reimplement Cqrs.get

* Specify error differentiation

* Remove old files

* Add cqrs exports

* Add cqrs result classes

* Reimplement Cqrs.run

* Fix docs for CqrsError

* Update docs for Cqrs.run

* Add missing docs for Cqrs

* Add middleware mechanism to Cqrs

* Add default value of headers argument in Cqrs.run

* Add tests for Cqrs.get

* Remove connectivity_plus from deps

* Update main docs

* Organize file of CqrsError

* Add default middleware method bodies

* Add license docs to result classes

* Organize imports

* Add missing authentication and forbiddenAccess error cases in Cqrs.run

* Make ValidationError extend Equatable

* Add Cqrs.run tests

* Add CQRS operation result

* Reimplement Cqrs.perform

* Add default implementation of CqrsMiddleware.handleOperationResult

* Update Cqrs tests for methods get, run and perform

* Revert entry doc change

* Add extension with isInvalid getter for CqrsCommandResult<CqrsError> specifically

* Fix Cqrs logging strings

* Add tests for Cqrs result classes

* Revert entry do change v2

* Remove cqrs_exception_test.dart

* Add CqrsCommandResult tests

* Revert unwanted change in docs

* Update Cqrs.addMiddleware docs

* Add tests ensuring Cqrs middlewares handled porperly

* Add tests for CqrsMiddleware

* Fix tests for CqrsMiddleware.handleOperationResult

* Implement Equatable correctly

* Update CqrsCommandSuccess field tests

* Remove CqrsException

* Remove CqrsException from exports

* Make error not generic in Cqrs result classes

* Fix props override in CqrsCommandSuccess

* Rename CqrsQueryResult to QResult

* Rename CqrsCommandResult to CResult

* Rename CqrsOperationResult to OResult

* Update main docs

* Add missing tests for CSuccess

* Simplify result classes getters

* Update result classes tests

* Update CqrsError docs

* Fix typo in docs

* Exhaust case with Operation()

* Exhaust all the rest _ResultType in case explicitly

* Return early from _log

* Rename CommandResult to CommandResponse

* Reimplement cqrs result classes

* Make base result classes private

* Refactor result classes

* Simplify CqrsMiddleware.handleResult

* Remove CqrsError

* Refactor Cqrs._log

* Add ValidationError.props test

* Update test for ValidationError.props

* Fix docs for result classes

* Revert making result classes a typedef of one base result class

* Move ValidationError to separate file and make CommandResponse package private

* Rename command_result.dart to command_response.dart

* Rename OResult to OperationResult

* Rename CResult to CommandResult

* Rename QResult to QueryResult

* Fix tests of CommandResponse

* Add null check on logger in Cqrs._log method

* Split CqrsError into Query Command and Operation Error

* Make CqrsMethod a sealed class

* Fix docs typos from code review

Co-authored-by: Marcin Wojnarowski <xmarcinmarcin@gmail.com>

* Update CHANGELOG.md

* Remove blank space in CHANGELOG.md

* Update README.md

* Update versions of dependencies in CHANGELOG.md

Co-authored-by: Marcin Wojnarowski <xmarcinmarcin@gmail.com>

---------

Co-authored-by: Marcin Wojnarowski <xmarcinmarcin@gmail.com>
  • Loading branch information
jtarkowski27 and shilangyu committed Sep 26, 2023
1 parent 2ec3726 commit 2b3fb1c
Show file tree
Hide file tree
Showing 18 changed files with 1,311 additions and 295 deletions.
12 changes: 12 additions & 0 deletions packages/cqrs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# 10.0.0

- **Breaking:** Fundamentaly change overall `Cqrs` API making it no-throw guarantee.
- **Breaking:** Make `Cqrs.get`, `Cqrs.run` and `Cqrs.perform` return result data in form of `QueryResult`, `CommandResult` and `OperationResult` respectively.
- Add `logger` parameter (of type `Logger` from `logging` package) to `Cqrs` default constructor. If provided, the `logger` will be used as a debug logging interface in execution of CQRS methods.
- Add middleware mechanism in form of `CqrsMiddleware` intended to use in processing result from queries, commands and operations.
- **Breaking:** Remove `CqrsException`.
- **Breaking:** Rename previous `CommandResult` to `CommandResponse` and make it package private.
- Mark the `CqrsMethod` as `sealed`.
- **Breaking:** Make `ValidationError` extend `Equatable` from `equatable` package.
- **Breaking:** Add `equatable` (`^2.0.5`) and `logging` (`^1.2.0`) dependencies.

# 9.0.0

- Bumped `http` dependency to `1.0.0`. (#105)
Expand Down
12 changes: 4 additions & 8 deletions packages/cqrs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ class AddFlower implements Command {
Map<String, dynamic> toJson() => {'Name': name, 'Pretty': pretty};
}
abstract class AddFlowerErrorCodes {
static const alreadyExists = 1;
}
// Firstly you need an Uri to which the requests will be sent.
// Remember about the trailing slash as otherwise resolved paths
// may be invalid.
Expand All @@ -67,11 +63,11 @@ final flowers = await cqrs.get(AllFlowers(page: 1));
final result = await cqrs.run(AddFlower(name: 'Daisy', pretty: true));
// You can check the command result for its status, whether it successfully ran.
if (result.success) {
if (result case CommandSuccess()) {
print('Added a daisy successfully!');
} else if (result.hasError(AddFlowerErrorCodes.alreadyExists)) {
// Or check for errors in `result.errors`. You can use a `hasError` helper.
print('Daisy already exists!');
} else if (result case CommandFailure(isInvalid: true, :final validationErrors)) {
print('Validation errors occured!');
handleValidationErrors(validationErrors);
} else {
print('Error occured');
}
Expand Down
22 changes: 19 additions & 3 deletions packages/cqrs/lib/cqrs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@
/// // Fetching first page of flowers
/// final flowers = await cqrs.get(AllFlowers(page: 1));
///
/// // Handling query result
/// if (flowers case QuerySuccess(:final data)) {
/// print(data);
/// } else if (flowers case QueryFailure(:final error)) {
/// print('Something failed with error $error');
/// }
///
/// // Adding a new flower
/// final result = await cqrs.run(
/// AddFlower(
Expand All @@ -36,7 +43,15 @@
/// ),
/// );
///
/// print(result.success); // true
/// // Handling command result
/// if (result case CommandSuccess()) {
/// print('Flower added succefully');
/// } else if (result case CommandFailure(isInvalid: true, :final validationErrors)) {
/// print('Validation errors occured');
/// handleValidationErrors(validationErrors);
/// } else if (result case CommandFailure(:final error)) {
/// print('Something failed with error ${error}');
/// }
/// ```
///
/// See also:
Expand All @@ -49,7 +64,8 @@
/// code contract generator.
library;

export 'src/command_result.dart';
export 'src/cqrs.dart';
export 'src/cqrs_exception.dart';
export 'src/cqrs_error.dart';
export 'src/cqrs_result.dart';
export 'src/transport_types.dart';
export 'src/validation_error.dart';
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,21 @@
// limitations under the License.

import 'transport_types.dart';
import 'validation_error.dart';

/// The result of running a [Command].
class CommandResult {
/// Creates a [CommandResult] with [errors];
const CommandResult(this.errors);
class CommandResponse {
/// Creates a [CommandResponse] with [errors];
const CommandResponse(this.errors);

/// Creates a success [CommandResult] without any errors.
const CommandResult.success() : errors = const [];
/// Creates a success [CommandResponse] without any errors.
const CommandResponse.success() : errors = const [];

/// Creates a failed [CommandResult] and ensures it has errors.
CommandResult.failed(this.errors) : assert(errors.isNotEmpty);
/// Creates a failed [CommandResponse] and ensures it has errors.
CommandResponse.failed(this.errors) : assert(errors.isNotEmpty);

/// Creates a [CommandResult] from JSON.
CommandResult.fromJson(Map<String, dynamic> json)
/// Creates a [CommandResponse] from JSON.
CommandResponse.fromJson(Map<String, dynamic> json)
: errors = (json['ValidationErrors'] as List)
.map(
(dynamic error) =>
Expand All @@ -43,16 +44,16 @@ class CommandResult {
/// Validation errors related to the data carried by the [Command].
final List<ValidationError> errors;

/// Checks whether this [CommandResult] contains a provided error `code` in
/// Checks whether this [CommandResponse] contains a provided error `code` in
/// its validation errors.
bool hasError(int code) => errors.any((error) => error.code == code);

/// Checks whether this [CommandResult] contains a provided error `code` in
/// Checks whether this [CommandResponse] contains a provided error `code` in
/// its validation errors related to the `propertyName`.
bool hasErrorForProperty(int code, String propertyName) => errors
.any((error) => error.code == code && error.propertyName == propertyName);

/// Serializes this [CommandResult] to JSON.
/// Serializes this [CommandResponse] to JSON.
Map<String, dynamic> toJson() => <String, dynamic>{
'WasSuccessful': success,
'ValidationErrors': errors.map((error) => error.toJson()).toList(),
Expand All @@ -61,34 +62,3 @@ class CommandResult {
@override
String toString() => 'CommandResult($errors)';
}

/// A validation error.
class ValidationError {
/// Creates a [ValidationError] from [code], [message], and [propertyName].
const ValidationError(this.code, this.message, this.propertyName);

/// Creates a [ValidationError] from JSON.
ValidationError.fromJson(Map<String, dynamic> json)
: code = json['ErrorCode'] as int,
message = json['ErrorMessage'] as String,
propertyName = json['PropertyName'] as String;

/// Code of the validation error.
final int code;

/// Message describing the validation error.
final String message;

/// Path to the property which caused the error.
final String propertyName;

/// Serializes this [ValidationError] to JSON.
Map<String, dynamic> toJson() => <String, dynamic>{
'ErrorCode': code,
'ErrorMessage': message,
'PropertyName': propertyName
};

@override
String toString() => '[$propertyName] $code: $message';
}
Loading

0 comments on commit 2b3fb1c

Please sign in to comment.