Ejemplo n.º 1
0
class DbPerson(BaseEntity):
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50)
    age = Integer(default=21)

    class Meta:
        schema_name = "pepes"
Ejemplo n.º 2
0
class PersonSQLite(BaseAggregate):
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50, required=True)
    age = Integer(default=21)

    class Meta:
        provider = "sqlite"
Ejemplo n.º 3
0
class OrderedPerson(BaseEntity):
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50)
    age = Integer(default=21)

    class Meta:
        order_by = "first_name"
Ejemplo n.º 4
0
class Person(BaseAggregate):
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50, required=True)
    age = Integer(default=21)

    @classmethod
    def add_newcomer(cls, person_dict):
        """Factory method to add a new Person to the system"""
        newcomer = Person(
            first_name=person_dict["first_name"],
            last_name=person_dict["last_name"],
            age=person_dict["age"],
        )

        # Publish Event via the domain
        current_domain.publish(
            PersonAdded(
                id=newcomer.id,
                first_name=newcomer.first_name,
                last_name=newcomer.last_name,
                age=newcomer.age,
            )
        )

        return newcomer
Ejemplo n.º 5
0
class PostMeta(BaseEntity):
    likes = Integer(default=0)

    post = Reference(Post)

    class Meta:
        aggregate_cls = Post
Ejemplo n.º 6
0
class PersonAdded(BaseEvent):
    id = Auto(identifier=True)
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50, required=True)
    age = Integer(default=21)

    class Meta:
        aggregate_cls = Person
Ejemplo n.º 7
0
class DbPerson(BaseView):
    person_id = Identifier(identifier=True)
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50)
    age = Integer(default=21)

    class Meta:
        schema_name = "peoples"
Ejemplo n.º 8
0
class OrderedPerson(BaseView):
    person_id = Identifier(identifier=True)
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50)
    age = Integer(default=21)

    class Meta:
        order_by = "first_name"
Ejemplo n.º 9
0
    def test_choice(self):
        """Test choices validations for the Integer field"""
        class StatusChoices(enum.Enum):
            """Set of choices for the status"""

            PENDING = (0, "Pending")
            SUCCESS = (1, "Success")
            ERROR = (2, "Error")

        status = Integer(choices=StatusChoices)
        assert status is not None

        # Test loading of values to the status field
        assert status._load(0) == 0
        with pytest.raises(ValidationError) as e_info:
            status._load(4)
        assert e_info.value.messages == {
            "unlinked":
            ["Value `4` is not a valid choice. "
             "Must be among [0, 1, 2]"]
        }
Ejemplo n.º 10
0
class BaseAggregate(EventedMixin, BaseEntity):
    """The Base class for Protean-Compliant Domain Aggregates.

    Provides helper methods to custom define aggregate attributes, and query attribute names
    during runtime.

    Basic Usage::

        @domain.aggregate
        class Dog:
            id = field.Integer(identifier=True)
            name = field.String(required=True, max_length=50)
            age = field.Integer(default=5)
            owner = field.String(required=True, max_length=15)

    (or)

        class Dog(BaseAggregate):
            id = field.Integer(identifier=True)
            name = field.String(required=True, max_length=50)
            age = field.Integer(default=5)
            owner = field.String(required=True, max_length=15)

        domain.register_element(Dog)

    During persistence, the model associated with this entity is retrieved dynamically from
            the repository factory. Model is usually initialized with a live DB connection.
    """

    element_type = DomainObjects.AGGREGATE

    def __new__(cls, *args, **kwargs):
        if cls is BaseAggregate:
            raise TypeError("BaseAggregate cannot be instantiated")
        return super().__new__(cls)

    # Track current version of Aggregate
    _version = Integer(default=-1)

    class Meta:
        abstract = True

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    @classmethod
    def _default_options(cls):
        return [
            ("provider", "default"),
            ("model", None),
            ("stream_name", inflection.underscore(cls.__name__)),
            ("schema_name", inflection.underscore(cls.__name__)),
        ]
Ejemplo n.º 11
0
class Building(BaseValueObject):
    name = String(max_length=50)
    floors = Integer()
    status = String(choices=BuildingStatus)

    def defaults(self):
        if not self.status:
            if self.floors == 4:
                self.status = BuildingStatus.DONE.value
            else:
                self.status = BuildingStatus.WIP.value

    def clean(self):
        errors = defaultdict(list)
        if self.floors >= 4 and self.status != BuildingStatus.DONE.value:
            errors["status"].append("should be DONE")
        return errors
Ejemplo n.º 12
0
    def test_min_value(self):
        """Test minimum value validation for the integer field"""

        with pytest.raises(ValidationError):
            age = Integer(min_value=5)
            age._load(3)
Ejemplo n.º 13
0
class PostMeta(BaseEntity):
    likes = Integer(default=0)

    post = Reference(Post)
Ejemplo n.º 14
0
class NotAPerson(BaseView):
    identifier = Identifier(identifier=True)
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50)
    age = Integer(default=21)
Ejemplo n.º 15
0
class PersonExplicitID(BaseView):
    ssn = String(max_length=36, identifier=True)
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50)
    age = Integer(default=21)
Ejemplo n.º 16
0
class PersonAutoSSN(BaseView):
    ssn = Auto(identifier=True)
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50)
    age = Integer(default=21)
Ejemplo n.º 17
0
class Receiver(BaseAggregate):
    name = String()
    age = Integer()
Ejemplo n.º 18
0
class PersonAdded(BaseEvent):
    id = Identifier(required=True)
    email = String(max_length=255, required=True)
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50, required=True)
    age = Integer(default=21)
Ejemplo n.º 19
0
class NotAPerson(BaseEntity):
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50)
    age = Integer(default=21)
Ejemplo n.º 20
0
class PersonCommand(BaseCommand):
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50, required=True)
    age = Integer(default=21)
Ejemplo n.º 21
0
class Relative(BaseEntity):
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50)
    age = Integer(default=21)
    relative_of = HasOne(Person)
 class Person:
     name = String()
     age = Integer()
Ejemplo n.º 23
0
class BaseEventSourcedAggregate(IdentityMixin, EventedMixin, OptionsMixin,
                                BaseContainer):
    """Base Event Sourced Aggregate class that all EventSourced Aggregates should inherit from.

    The order of inheritance is important. We want BaseContainer to be initialised first followed by
    OptionsMixin (so that `meta_` is in place) before inheriting other mixins."""

    element_type = DomainObjects.EVENT_SOURCED_AGGREGATE

    # Track current version of Aggregate
    _version = Integer(default=-1)

    class Meta:
        abstract = True

    @classmethod
    def _default_options(cls):
        return [
            ("stream_name", inflection.underscore(cls.__name__)),
        ]

    def __init_subclass__(subclass) -> None:
        super().__init_subclass__()

        if not subclass.meta_.abstract:
            subclass.__validate_id_field()

        # Associate a `_projections` map with subclasses.
        #   It needs to be initialized here because if it
        #   were initialized in __init__, the same collection object
        #   would be made available across all subclasses,
        #   defeating its purpose.
        setattr(subclass, "_projections", defaultdict(set))

        # Store associated events
        setattr(subclass, "_events_cls_map", {})

    @classmethod
    def __validate_id_field(subclass):
        """Lookup the id field for this view and assign"""
        # FIXME What does it mean when there are no declared fields?
        #   Does it translate to an abstract view?
        if has_fields(subclass):
            try:
                id_field = next(
                    field for _, field in declared_fields(subclass).items()
                    if isinstance(field, (Field)) and field.identifier)

                setattr(subclass, _ID_FIELD_NAME, id_field.field_name)

            except StopIteration:
                raise IncorrectUsageError({
                    "_entity": [
                        f"Event Sourced Aggregate `{subclass.__name__}` needs to have at least one identifier"
                    ]
                })

    def __eq__(self, other):
        """Equivalence check to be based only on Identity"""

        # FIXME Enhanced Equality Checks
        #   * Ensure IDs have values and both of them are not null
        #   * Ensure that the ID is of the right type
        #   * Ensure that Objects belong to the same `type`
        #   * Check Reference equality

        # FIXME Check if `==` and `in` operator work with __eq__

        if type(other) is type(self):
            self_id = getattr(self, id_field(self).field_name)
            other_id = getattr(other, id_field(other).field_name)

            return self_id == other_id

        return False

    def __hash__(self):
        """Overrides the default implementation and bases hashing on identity"""

        # FIXME Add Object Class Type to hash
        return hash(getattr(self, id_field(self).field_name))

    def _apply(self, event_dict: Dict) -> None:
        """Apply the event onto the aggregate by calling the appropriate projection.

        Args:
            event (BaseEvent): Event object to apply
        """
        # FIXME Handle case of missing projection
        for fn in self._projections[event_dict["type"]]:
            # Reconstruct Event object
            event_cls = self._events_cls_map[event_dict["type"]]
            event = event_cls(**event_dict["data"])

            # Call event handler method
            fn(self, event)
Ejemplo n.º 24
0
class Person(BaseAggregate):
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50, required=True)
    age = Integer(default=21)
    created_at = DateTime(default=datetime.now())
Ejemplo n.º 25
0
class Person(BaseAggregate):
    first_name = String(max_length=50, required=True)
    last_name = String(max_length=50, required=True)
    age = Integer(default=21)
Ejemplo n.º 26
0
        class PersonSchema:
            name = String(required=True)
            age = Integer(required=True)

            class Meta:
                aggregate_cls = User
Ejemplo n.º 27
0
        class PostMeta(BaseEntity):
            likes = Integer(default=0)

            class Meta:
                aggregate_cls = Post
Ejemplo n.º 28
0
class AbstractPerson(BaseView):
    age = Integer(default=5)

    class Meta:
        abstract = True
class User(BaseEventSourcedAggregate):
    name = String()
    age = Integer()
Ejemplo n.º 30
0
class Provider(BaseAggregate):
    name = String()
    age = Integer()