Skip to content

Commit

Permalink
Simplify IncorrectUsageError exception
Browse files Browse the repository at this point in the history
  • Loading branch information
subhashb committed Jul 27, 2024
1 parent da4a68b commit c91cbb8
Show file tree
Hide file tree
Showing 47 changed files with 160 additions and 362 deletions.
5 changes: 1 addition & 4 deletions docs/guides/change-state/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,6 @@ Out[3]: <PublishArticle: PublishArticle object ({'article_id': '1', 'published_a

In [4]: publish_article_command.published_at = datetime.now() - timedelta(hours=24)
...
IncorrectUsageError: {
'_command': [
'Command Objects are immutable and cannot be modified once created'
]
IncorrectUsageError: 'Command Objects are immutable and cannot be modified once created'
}
```
4 changes: 2 additions & 2 deletions docs/guides/domain-definition/entities.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ specified while defining the identity, you will see an `IncorrectUsageError`:
... class Comment:
... content = String(max_length=500)
...
IncorrectUsageError: {'_entity': ['Entity `Comment` needs to be associated with an Aggregate']}
IncorrectUsageError: 'Entity `Comment` needs to be associated with an Aggregate'
```

An Entity cannot enclose another Entity (or Aggregate). Trying to do so will
Expand All @@ -44,7 +44,7 @@ throw `IncorrectUsageError`.
... class SubComment:
... parent = Comment()
...
IncorrectUsageError: {'_entity': ['Entity `Comment` needs to be associated with an Aggregate']}
IncorrectUsageError: 'Entity `Comment` needs to be associated with an Aggregate'
```
<!-- FIXME Ensure entities cannot enclose other entities. When entities
enclose something other than permitted fields, through an error-->
Expand Down
6 changes: 1 addition & 5 deletions docs/guides/domain-definition/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,5 @@ In [2]: renamed = UserRenamed(user_id=user.id, name="John Doe Jr.")

In [3]: renamed.name = "John Doe Sr."
...
IncorrectUsageError: {
'_message': [
'Event/Command Objects are immutable and cannot be modified once created'
]
}
IncorrectUsageError: 'Event/Command Objects are immutable and cannot be modified once created'
```
6 changes: 3 additions & 3 deletions docs/guides/domain-definition/value-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ In [3]: class Balance(BaseValueObject):
...: currency = String(max_length=3, unique=True)
...: amount = Float()
...
IncorrectUsageError: {'_value_object': ["Value Objects cannot contain fields marked 'unique' (field 'currency')"]}
IncorrectUsageError: "Value Objects cannot contain fields marked 'unique' (field 'currency')"
```

Same case if you try to find a Value Object's `id_field`:
Expand All @@ -238,7 +238,7 @@ In [4]: from protean.reflection import id_field

In [5]: id_field(Balance)
...
IncorrectUsageError: {"identity": ["<class '__main__.Balance'> does not have identity fields"]}
IncorrectUsageError: "<class '__main__.Balance'> does not have identity fields"
```

## Immutability
Expand All @@ -250,5 +250,5 @@ In [1]: bal1 = Balance(currency='USD', amount=100.0)

In [2]: bal1.currency = "CAD"
...
IncorrectUsageError: {'_value_object': ["Value Objects are immutable and cannot be modified once created"]}
IncorrectUsageError: "Value Objects are immutable and cannot be modified once created"
```
12 changes: 2 additions & 10 deletions src/protean/core/aggregate.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,7 @@ def apply(fn):

if len(typing.get_type_hints(fn)) > 2:
raise IncorrectUsageError(
{
"_entity": [
f"Handler method `{fn.__name__}` has incorrect number of arguments"
]
}
f"Handler method `{fn.__name__}` has incorrect number of arguments"
)

try:
Expand All @@ -276,11 +272,7 @@ def apply(fn):
)
except StopIteration:
raise IncorrectUsageError(
{
"_entity": [
f"Apply method `{fn.__name__}` should accept an argument annotated with the Event class"
]
}
f"Apply method `{fn.__name__}` should accept an argument annotated with the Event class"
)

@functools.wraps(fn)
Expand Down
6 changes: 1 addition & 5 deletions src/protean/core/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,7 @@ def command_factory(element_cls, domain, **opts):

if not element_cls.meta_.part_of and not element_cls.meta_.abstract:
raise IncorrectUsageError(
{
"_command": [
f"Command `{element_cls.__name__}` needs to be associated with an aggregate or a stream"
]
}
f"Command `{element_cls.__name__}` needs to be associated with an aggregate or a stream"
)

return element_cls
6 changes: 1 addition & 5 deletions src/protean/core/command_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ def command_handler_factory(element_cls, domain, **opts):

if not element_cls.meta_.part_of:
raise IncorrectUsageError(
{
"_entity": [
f"Command Handler `{element_cls.__name__}` needs to be associated with an Aggregate"
]
}
f"Command Handler `{element_cls.__name__}` needs to be associated with an Aggregate"
)

return element_cls
6 changes: 1 addition & 5 deletions src/protean/core/domain_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,7 @@ def domain_service_factory(element_cls, domain, **opts):

if not element_cls.meta_.part_of or len(element_cls.meta_.part_of) < 2:
raise IncorrectUsageError(
{
"_entity": [
f"Domain Service `{element_cls.__name__}` needs to be associated with two or more Aggregates"
]
}
f"Domain Service `{element_cls.__name__}` needs to be associated with two or more Aggregates"
)

# Iterate through methods marked as `@invariant` and record them for later use
Expand Down
6 changes: 1 addition & 5 deletions src/protean/core/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -621,11 +621,7 @@ def entity_factory(element_cls, domain, **opts):

if not element_cls.meta_.part_of:
raise IncorrectUsageError(
{
"_entity": [
f"Entity `{element_cls.__name__}` needs to be associated with an Aggregate"
]
}
f"Entity `{element_cls.__name__}` needs to be associated with an Aggregate"
)

# Set up reference fields
Expand Down
6 changes: 1 addition & 5 deletions src/protean/core/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,7 @@ def domain_event_factory(element_cls, domain, **opts):

if not element_cls.meta_.part_of and not element_cls.meta_.abstract:
raise IncorrectUsageError(
{
"_event": [
f"Event `{element_cls.__name__}` needs to be associated with an aggregate or a stream"
]
}
f"Event `{element_cls.__name__}` needs to be associated with an aggregate or a stream"
)

return element_cls
6 changes: 1 addition & 5 deletions src/protean/core/event_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,7 @@ def event_handler_factory(element_cls, domain, **opts):

if not (element_cls.meta_.part_of or element_cls.meta_.stream_category):
raise IncorrectUsageError(
{
"_entity": [
f"Event Handler `{element_cls.__name__}` needs to be associated with an aggregate or a stream"
]
}
f"Event Handler `{element_cls.__name__}` needs to be associated with an aggregate or a stream"
)

return element_cls
16 changes: 3 additions & 13 deletions src/protean/core/event_sourced_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ def __init__(self, domain) -> None:

def add(self, aggregate: BaseAggregate) -> None:
if aggregate is None:
raise IncorrectUsageError(
{"_entity": ["Aggregate object to persist is invalid"]}
)
raise IncorrectUsageError("Aggregate object to persist is invalid")

# Proceed only if aggregate has events
if len(aggregate._events) > 0:
Expand Down Expand Up @@ -105,20 +103,12 @@ def event_sourced_repository_factory(element_cls, domain, **opts):

if not element_cls.meta_.part_of:
raise IncorrectUsageError(
{
"_entity": [
f"Repository `{element_cls.__name__}` should be associated with an Aggregate"
]
}
f"Repository `{element_cls.__name__}` should be associated with an Aggregate"
)

if not element_cls.meta_.part_of.meta_.is_event_sourced:
raise IncorrectUsageError(
{
"_entity": [
f"Repository `{element_cls.__name__}` can only be associated with an Event Sourced Aggregate"
]
}
f"Repository `{element_cls.__name__}` can only be associated with an Event Sourced Aggregate"
)

return element_cls
6 changes: 1 addition & 5 deletions src/protean/core/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,7 @@ def model_factory(element_cls, domain, **opts):

if not element_cls.meta_.part_of:
raise IncorrectUsageError(
{
"_entity": [
f"Model `{element_cls.__name__}` should be associated with an Entity or Aggregate"
]
}
f"Model `{element_cls.__name__}` should be associated with an Entity or Aggregate"
)

return element_cls
12 changes: 2 additions & 10 deletions src/protean/core/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,7 @@ def repository_factory(element_cls, domain, **opts):

if not element_cls.meta_.part_of:
raise IncorrectUsageError(
{
"_entity": [
f"Repository `{element_cls.__name__}` should be associated with an Aggregate"
]
}
f"Repository `{element_cls.__name__}` should be associated with an Aggregate"
)

# FIXME Uncomment
Expand All @@ -284,11 +280,7 @@ def repository_factory(element_cls, domain, **opts):
database.value for database in Database
]:
raise IncorrectUsageError(
{
"_entity": [
f"Repository `{element_cls.__name__}` should be associated with a valid Database"
]
}
f"Repository `{element_cls.__name__}` should be associated with a valid Database"
)

return element_cls
12 changes: 2 additions & 10 deletions src/protean/core/subscriber.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,12 @@ def subscriber_factory(element_cls, domain, **opts):

if not element_cls.meta_.event:
raise IncorrectUsageError(
{
"_entity": [
f"Subscriber `{element_cls.__name__}` needs to be associated with an Event"
]
}
f"Subscriber `{element_cls.__name__}` needs to be associated with an Event"
)

if not element_cls.meta_.broker:
raise IncorrectUsageError(
{
"_entity": [
f"Subscriber `{element_cls.__name__}` needs to be associated with a Broker"
]
}
f"Subscriber `{element_cls.__name__}` needs to be associated with a Broker"
)

return element_cls
26 changes: 5 additions & 21 deletions src/protean/core/value_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,36 +44,24 @@ def __validate_for_basic_field_types(subclass):
# Value objects can hold all kinds of fields, except associations
if isinstance(field_obj, (Reference, Association)):
raise IncorrectUsageError(
{
"_value_object": [
f"Value Objects cannot have associations. "
f"Remove {field_name} ({field_obj.__class__.__name__}) from class {subclass.__name__}"
]
}
f"Value Objects cannot have associations. "
f"Remove {field_name} ({field_obj.__class__.__name__}) from class {subclass.__name__}"
)

@classmethod
def __validate_for_non_identifier_fields(subclass):
for field_name, field_obj in fields(subclass).items():
if field_obj.identifier:
raise IncorrectUsageError(
{
"_value_object": [
f"Value Objects cannot contain fields marked 'identifier' (field '{field_name}')"
]
}
f"Value Objects cannot contain fields marked 'identifier' (field '{field_name}')"
)

@classmethod
def __validate_for_non_unique_fields(subclass):
for field_name, field_obj in fields(subclass).items():
if field_obj.unique:
raise IncorrectUsageError(
{
"_value_object": [
f"Value Objects cannot contain fields marked 'unique' (field '{field_name}')"
]
}
f"Value Objects cannot contain fields marked 'unique' (field '{field_name}')"
)

def __init__(self, *template, **kwargs): # noqa: C901
Expand Down Expand Up @@ -176,11 +164,7 @@ def __setattr__(self, name, value):
return super().__setattr__(name, value)
else:
raise IncorrectUsageError(
{
"_value_object": [
"Value Objects are immutable and cannot be modified once created"
]
}
"Value Objects are immutable and cannot be modified once created"
)

def _postcheck(self, return_errors=False):
Expand Down
14 changes: 3 additions & 11 deletions src/protean/core/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,8 @@ def __validate_for_basic_field_types(subclass):
for field_name, field_obj in declared_fields(subclass).items():
if isinstance(field_obj, (Reference, Association, ValueObject)):
raise IncorrectUsageError(
{
"_entity": [
f"Views can only contain basic field types. "
f"Remove {field_name} ({field_obj.__class__.__name__}) from class {subclass.__name__}"
]
}
f"Views can only contain basic field types. "
f"Remove {field_name} ({field_obj.__class__.__name__}) from class {subclass.__name__}"
)

def __init__(self, *template, **kwargs):
Expand Down Expand Up @@ -110,11 +106,7 @@ def view_factory(element_cls, domain, **opts):

if not element_cls.meta_.abstract and not hasattr(element_cls, _ID_FIELD_NAME):
raise IncorrectUsageError(
{
"_entity": [
f"View `{element_cls.__name__}` needs to have at least one identifier"
]
}
f"View `{element_cls.__name__}` needs to have at least one identifier"
)

element_cls.meta_.provider = (
Expand Down
Loading

0 comments on commit c91cbb8

Please sign in to comment.