def prep_class(cls, element_cls, **kwargs): if issubclass(element_cls, BaseDomainEvent): new_element_cls = element_cls else: try: new_dict = element_cls.__dict__.copy() new_dict.pop('__dict__', None) # Remove __dict__ to prevent recursion new_element_cls = type(element_cls.__name__, (BaseDomainEvent, ), new_dict) except BaseException as exc: logger.debug("Error during Element registration:", repr(exc)) raise IncorrectUsageError( "Invalid class {element_cls.__name__} for type {element_type.value}" " (Error: {exc})", ) if hasattr(new_element_cls, 'meta_'): if not (hasattr(new_element_cls.meta_, 'aggregate_cls') and new_element_cls.meta_.aggregate_cls): new_element_cls.meta_.aggregate_cls = kwargs.pop('aggregate_cls', None) new_element_cls.meta_.bounded_context = kwargs.pop('bounded_context', None) if not new_element_cls.meta_.aggregate_cls: raise IncorrectUsageError("Domain Events need to be associated with an Aggregate") return new_element_cls
def prep_class(cls, element_cls, **kwargs): if issubclass(element_cls, BaseAggregate): new_element_cls = element_cls else: try: new_dict = element_cls.__dict__.copy() new_dict.pop('__dict__', None) # Remove __dict__ to prevent recursion new_element_cls = type(element_cls.__name__, (BaseAggregate, ), new_dict) except BaseException as exc: logger.debug("Error during Element registration:", repr(exc)) raise IncorrectUsageError( "Invalid class {element_cls.__name__} for type {element_type.value}" " (Error: {exc})", ) cls._validate_aggregate_class(new_element_cls) new_element_cls.meta_.provider = (kwargs.pop('provider', None) or (hasattr(new_element_cls, 'meta_') and new_element_cls.meta_.provider) or 'default') new_element_cls.meta_.model = (kwargs.pop('model', None) or (hasattr(new_element_cls, 'meta_') and new_element_cls.meta_.model) or None) new_element_cls.meta_.bounded_context = ( kwargs.pop('bounded_context', None) or (hasattr(new_element_cls, 'meta_') and new_element_cls.meta_.bounded_context)) return new_element_cls
def entity_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseEntity) if element_cls.meta_.abstract is True: raise NotSupportedError( f"{element_cls.__name__} class has been marked abstract" f" and cannot be instantiated") element_cls.meta_.provider = (kwargs.pop("provider", None) or (hasattr(element_cls, "meta_") and element_cls.meta_.provider) or "default") element_cls.meta_.model = (kwargs.pop("model", None) or (hasattr(element_cls, "meta_") and element_cls.meta_.model) or None) element_cls.meta_.aggregate_cls = (kwargs.pop("aggregate_cls", None) or (hasattr(element_cls, "meta_") and element_cls.meta_.aggregate_cls) or None) if not element_cls.meta_.aggregate_cls: raise IncorrectUsageError( f"Entity `{element_cls.__name__}` needs to be associated with an Aggregate" ) return element_cls
def prep_class(cls, element_cls, **kwargs): if issubclass(element_cls, BaseSubscriber): new_element_cls = element_cls else: try: new_dict = element_cls.__dict__.copy() new_dict.pop('__dict__', None) # Remove __dict__ to prevent recursion new_element_cls = type(element_cls.__name__, (BaseSubscriber, ), new_dict) except BaseException as exc: logger.debug("Error during Element registration:", repr(exc)) raise IncorrectUsageError( "Invalid class {element_cls.__name__} for type {element_type.value}" " (Error: {exc})", ) cls._validate_subscriber_class(new_element_cls) new_element_cls.meta_.domain_event = (kwargs.pop( 'domain_event', None) or (hasattr(new_element_cls, 'meta_') and new_element_cls.meta_.domain_event) or None) new_element_cls.meta_.broker = (kwargs.pop('broker', None) or (hasattr(new_element_cls, 'meta_') and new_element_cls.meta_.broker) or 'default') new_element_cls.meta_.bounded_context = ( kwargs.pop('bounded_context', None) or (hasattr(new_element_cls, 'meta_') and new_element_cls.meta_.bounded_context)) new_element_cls.meta_.aggregate_cls = (kwargs.pop( 'aggregate_cls', None) or (hasattr(new_element_cls, 'meta_') and new_element_cls.meta_.aggregate_cls) or None) if not new_element_cls.meta_.domain_event: raise IncorrectUsageError( f"Subscriber `{new_element_cls.__name__}` needs to be associated with a Domain Event" ) return new_element_cls
def value_object_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseValueObject) if not (hasattr(element_cls.meta_, "aggregate_cls") and element_cls.meta_.aggregate_cls): element_cls.meta_.aggregate_cls = kwargs.pop("aggregate_cls", None) if not element_cls.meta_.aggregate_cls: raise IncorrectUsageError( "Value Objects need to be associated with an Aggregate") return element_cls
def subscriber_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseSubscriber) element_cls.meta_.domain_event = (kwargs.pop("domain_event", None) or (hasattr(element_cls, "meta_") and element_cls.meta_.domain_event) or None) element_cls.meta_.broker = (kwargs.pop("broker", None) or (hasattr(element_cls, "meta_") and element_cls.meta_.broker) or "default") if not element_cls.meta_.domain_event: raise IncorrectUsageError( f"Subscriber `{element_cls.__name__}` needs to be associated with a Domain Event" ) if not element_cls.meta_.broker: raise IncorrectUsageError( f"Subscriber `{element_cls.__name__}` needs to be associated with a Broker" ) return element_cls
def derive_element_class(element_cls, base_cls): if not issubclass(element_cls, base_cls): try: new_dict = element_cls.__dict__.copy() new_dict.pop("__dict__", None) # Remove __dict__ to prevent recursion element_cls = type(element_cls.__name__, (base_cls, ), new_dict) except BaseException as exc: logger.debug("Error during Element registration:", repr(exc)) raise IncorrectUsageError( "Invalid class {element_cls.__name__} for type {element_type.value}" " (Error: {exc})", ) return element_cls
def repository_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseRepository) if not (hasattr(element_cls.meta_, "aggregate_cls") and element_cls.meta_.aggregate_cls): element_cls.meta_.aggregate_cls = kwargs.pop("aggregate_cls", None) if not (hasattr(element_cls.meta_, "database") and element_cls.meta_.database): element_cls.meta_.database = kwargs.pop("database", "ALL") if not element_cls.meta_.aggregate_cls: raise IncorrectUsageError( "Repositories need to be associated with an Aggregate") return element_cls
def factory_for(self, domain_object_type): # Protean from protean.core.aggregate import aggregate_factory from protean.core.application_service import application_service_factory from protean.core.command import command_factory from protean.core.command_handler import command_handler_factory from protean.core.domain_event import domain_event_factory from protean.core.domain_service import domain_service_factory from protean.core.email import email_factory from protean.core.entity import entity_factory from protean.core.model import model_factory from protean.core.repository import repository_factory from protean.core.serializer import serializer_factory from protean.core.subscriber import subscriber_factory from protean.core.value_object import value_object_factory from protean.core.view import view_factory factories = { DomainObjects.AGGREGATE.value: aggregate_factory, DomainObjects.APPLICATION_SERVICE.value: application_service_factory, DomainObjects.COMMAND.value: command_factory, DomainObjects.COMMAND_HANDLER.value: command_handler_factory, DomainObjects.DOMAIN_EVENT.value: domain_event_factory, DomainObjects.DOMAIN_SERVICE.value: domain_service_factory, DomainObjects.EMAIL.value: email_factory, DomainObjects.ENTITY.value: entity_factory, DomainObjects.MODEL.value: model_factory, DomainObjects.REPOSITORY.value: repository_factory, DomainObjects.SUBSCRIBER.value: subscriber_factory, DomainObjects.SERIALIZER.value: serializer_factory, DomainObjects.VALUE_OBJECT.value: value_object_factory, DomainObjects.VIEW.value: view_factory, } if domain_object_type.value not in factories: raise IncorrectUsageError( "Unknown Element Type {element_type.value} for class {element_cls.__name__} " " (Error: {exc})", ) return factories[domain_object_type.value]
def model_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseModel) if hasattr(element_cls, "Meta"): element_cls.meta_ = ModelMeta(element_cls.Meta) else: element_cls.meta_ = ModelMeta() if not (hasattr(element_cls.meta_, "entity_cls") and element_cls.meta_.entity_cls): element_cls.meta_.entity_cls = kwargs.pop("entity_cls", None) if not (hasattr(element_cls.meta_, "schema") and element_cls.meta_.schema): element_cls.meta_.schema = kwargs.pop("schema", None) if not (hasattr(element_cls.meta_, "database") and element_cls.meta_.database): element_cls.meta_.database = kwargs.pop("database", None) if not element_cls.meta_.entity_cls: raise IncorrectUsageError( "Models need to be associated with an Entity or Aggregate") return element_cls
def _register_element(self, element_type, element_cls, **kwargs): # noqa: C901 """Register class into the domain""" # Check if `element_cls` is already a subclass of the Element Type # which would be the case in an explicit declaration like `class Account(BaseEntity):` # # We will need to construct a class derived from the right base class # if the Element was specified through annotation, like so: # # ``` # @Entity # class Account: # ``` if element_type == DomainObjects.VALUE_OBJECT: from protean.core.value_object import ValueObjectFactory new_cls = ValueObjectFactory.prep_class(element_cls, **kwargs) elif element_type == DomainObjects.REPOSITORY: from protean.core.repository.base import RepositoryFactory new_cls = RepositoryFactory.prep_class(element_cls, **kwargs) elif element_type == DomainObjects.AGGREGATE: from protean.core.aggregate import AggregateFactory new_cls = AggregateFactory.prep_class(element_cls, **kwargs) elif element_type == DomainObjects.ENTITY: from protean.core.entity import EntityFactory new_cls = EntityFactory.prep_class(element_cls, **kwargs) elif element_type == DomainObjects.SUBSCRIBER: from protean.core.broker.subscriber import SubscriberFactory new_cls = SubscriberFactory.prep_class(element_cls, **kwargs) elif element_type == DomainObjects.DOMAIN_EVENT: from protean.core.domain_event import DomainEventFactory new_cls = DomainEventFactory.prep_class(element_cls, **kwargs) else: try: if not issubclass(element_cls, self.base_class_mapping[element_type.value]): new_dict = element_cls.__dict__.copy() new_dict.pop('__dict__', None) # Remove __dict__ to prevent recursion # Hack to switch between `marshmallow.Schema` and `BaseSerializer` # while creating the derived class for Serializers # # This becomes necessary because we need to derive the undecorated class # from `BaseSerializer`, but once derived, the base hierarchy only reflects # `marshmallow.Schema` (This is a metaclass, so it disrupts hierarchy). if element_type == DomainObjects.SERIALIZER: from protean.core.serializer import BaseSerializer base_cls = BaseSerializer else: base_cls = self.base_class_mapping[element_type.value] new_cls = type(element_cls.__name__, (base_cls, ), new_dict) else: new_cls = element_cls # Element was already subclassed properly except BaseException as exc: logger.debug("Error during Element registration:", repr(exc)) raise IncorrectUsageError( "Invalid class {element_cls.__name__} for type {element_type.value}" " (Error: {exc})", ) aggregate_cls = None if (element_type == DomainObjects.MODEL and self._validate_model_class(new_cls)): # Associate aggregate/entity class with model if `entity_cls` was supplied as an explicit parameter from protean.core.repository.model import ModelMeta if hasattr(new_cls, 'Meta'): new_cls.meta_ = ModelMeta(new_cls.Meta) else: new_cls.meta_ = ModelMeta() entity_cls = new_cls.meta_.entity_cls or kwargs.pop( 'entity_cls', None) if not entity_cls: raise IncorrectUsageError( "Models need to be associated with an Entity or Aggregate" ) if not new_cls.meta_.entity_cls: new_cls.meta_.entity_cls = entity_cls # Remember model association with aggregate/entity class, for easy fetching self._models[fully_qualified_name(entity_cls)] = new_cls if element_type == DomainObjects.COMMAND_HANDLER and self._validate_command_handler_class( new_cls): command_cls = new_cls.meta_.command_cls or kwargs.pop( 'command', None) broker_name = new_cls.meta_.broker or 'default' if not command_cls: raise IncorrectUsageError( "Command Handlers need to be associated with a Command" ) new_cls.meta_.command_cls = command_cls new_cls.meta_.broker = broker_name if element_type == DomainObjects.EMAIL and self._validate_email_class( new_cls): provider_name = new_cls.meta_.provider or 'default' new_cls.meta_.provider = provider_name # Enrich element with domain information if hasattr(new_cls, 'meta_'): new_cls.meta_.aggregate_cls = aggregate_cls or kwargs.pop( 'aggregate_cls', None) new_cls.meta_.bounded_context = kwargs.pop( 'bounded_context', None) # Register element with domain self._domain_registry.register_element(new_cls) return new_cls