-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #349 from weni-ai/feature/eda-projects-consumers
Create TemplateType and Project consumers and handle queues
- Loading branch information
Showing
20 changed files
with
511 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .app_configuration import AppConfigurationUseCase |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from typing import TYPE_CHECKING | ||
|
||
|
||
if TYPE_CHECKING: # pragma: no cover | ||
from django.contrib.auth import get_user_model | ||
|
||
from marketplace.core.types import AppType | ||
from ..models import App | ||
|
||
User = get_user_model() | ||
|
||
|
||
class AppConfigurationUseCase: # pragma: no cover | ||
def __init__(self, channel_client, channel_token_client): | ||
self.__channel_client = channel_client | ||
self.__channel_token_client = channel_token_client | ||
|
||
def configure_app(self, app: "App", apptype: "AppType", user: "User") -> None: | ||
apptype.configure_app(app, user, self.__channel_client, self.__channel_token_client) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
from amqp.channel import Channel | ||
|
||
from marketplace.projects.handle import handle_consumers as project_handle_consumers | ||
|
||
|
||
def handle_consumers(channel: Channel) -> None: | ||
pass | ||
project_handle_consumers(channel) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from .project_consumer import ProjectConsumer | ||
from .template_type_consumer import TemplateTypeConsumer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import amqp | ||
from sentry_sdk import capture_exception | ||
|
||
from ..usecases import ( | ||
TemplateTypeIntegrationUseCase, | ||
ProjectCreationUseCase, | ||
AppSetupHandlerUseCase, | ||
ProjectCreationDTO, | ||
) | ||
from marketplace.applications.usecases import AppConfigurationUseCase | ||
from marketplace.connect.client import ConnectProjectClient, WPPRouterChannelClient | ||
from marketplace.event_driven.parsers import JSONParser | ||
from marketplace.event_driven.consumers import EDAConsumer | ||
|
||
|
||
class ProjectConsumer(EDAConsumer): # pragma: no cover | ||
def consume(self, message: amqp.Message): | ||
print(f"[ProjectConsumer] - Consuming a message. Body: {message.body}") | ||
|
||
try: | ||
body = JSONParser.parse(message.body) | ||
|
||
project_dto = ProjectCreationDTO( | ||
uuid=body.get("uuid"), | ||
name=body.get("name"), | ||
is_template=body.get("is_template"), | ||
date_format=body.get("date_format"), | ||
template_type_uuid=body.get("template_type_uuid"), | ||
timezone=body.get("timezone"), | ||
) | ||
|
||
connect_client = ConnectProjectClient() | ||
wpp_router_client = WPPRouterChannelClient() | ||
app_configuration = AppConfigurationUseCase(connect_client, wpp_router_client) | ||
|
||
app_setup_handler = AppSetupHandlerUseCase(app_configuration) | ||
template_type_integration = TemplateTypeIntegrationUseCase(app_setup_handler) | ||
|
||
project_creation = ProjectCreationUseCase(template_type_integration) | ||
project_creation.create_project(project_dto, body.get("user_email")) | ||
|
||
message.channel.basic_ack(message.delivery_tag) | ||
|
||
except Exception as exception: | ||
capture_exception(exception) | ||
message.channel.basic_reject(message.delivery_tag, requeue=False) | ||
print(f"[ProjectConsumer] - Message rejected by: {exception}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import amqp | ||
|
||
from marketplace.event_driven.parsers import JSONParser | ||
from marketplace.event_driven.consumers import EDAConsumer | ||
from ..usecases import create_template_type | ||
|
||
|
||
class TemplateTypeConsumer(EDAConsumer): # pragma: no cover | ||
def consume(self, message: amqp.Message): | ||
body = JSONParser.parse(message.body) | ||
|
||
print(f"[TemplateTypeConsumer] - Consuming a message. Body: {body}") | ||
|
||
create_template_type(uuid=body.get("uuid"), project_uuid=body.get("project_uuid"), name=body.get("name")) | ||
|
||
message.channel.basic_ack(message.delivery_tag) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from amqp.channel import Channel | ||
|
||
from .consumers import TemplateTypeConsumer, ProjectConsumer | ||
|
||
|
||
def handle_consumers(channel: Channel) -> None: | ||
channel.basic_consume("integrations.template-types", callback=TemplateTypeConsumer().handle) | ||
channel.basic_consume("integrations.projects", callback=ProjectConsumer().handle) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from .exceptions import InvalidProjectData | ||
from .template_type_creation import create_template_type | ||
from .project_creation import ProjectCreationUseCase, ProjectCreationDTO | ||
from .app_setup_handler import AppSetupHandlerUseCase | ||
from .template_type_integration import TemplateTypeIntegrationUseCase |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from django.contrib.auth import get_user_model | ||
|
||
from ..models import Project, TemplateType | ||
from .exceptions import InvalidTemplateTypeData | ||
from marketplace.core.types import APPTYPES | ||
|
||
|
||
User = get_user_model() | ||
|
||
|
||
class AppSetupHandlerUseCase: | ||
def __init__(self, app_configuration): | ||
self.__app_configuration = app_configuration | ||
|
||
def setup_apps_in_project(self, project: Project, template_type: TemplateType, user: User): | ||
setup = template_type.setup | ||
|
||
if setup == {}: | ||
raise InvalidTemplateTypeData(f"The `setup` of TemplateType {template_type.uuid} is empty!") | ||
|
||
for setup_app in setup.get("apps"): | ||
code = setup_app.get("code") | ||
|
||
if not code: | ||
raise InvalidTemplateTypeData(f"The TemplateType {template_type.uuid} has an invalid setup!") | ||
|
||
try: | ||
apptype = APPTYPES.get(code) | ||
except KeyError: | ||
raise InvalidTemplateTypeData(f"TemplateType {template_type.uuid} has invalid app code!") | ||
|
||
app = apptype.create_app(project_uuid=str(project.uuid), created_by=user) | ||
self.__app_configuration.configure_app(app, apptype, user) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
class InvalidProjectData(Exception): | ||
pass | ||
|
||
|
||
class InvalidTemplateTypeData(Exception): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from abc import ABC, abstractmethod | ||
from typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: # pragma: no cover | ||
from django.contrib.auth import get_user_model | ||
|
||
from ..models import Project | ||
|
||
User = get_user_model() | ||
|
||
|
||
class TemplateTypeIntegrationInterface(ABC): # pragma: no cover | ||
@abstractmethod | ||
def integrate_template_type_in_project(self, project: "Project", template_type_uuid: str, user: "User"): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
from dataclasses import dataclass | ||
from django.contrib.auth import get_user_model | ||
|
||
from ..models import Project | ||
from .interfaces import TemplateTypeIntegrationInterface | ||
|
||
|
||
User = get_user_model() | ||
|
||
|
||
@dataclass | ||
class ProjectCreationDTO: | ||
uuid: str | ||
name: str | ||
is_template: bool | ||
date_format: str | ||
timezone: str | ||
template_type_uuid: str | ||
|
||
|
||
class ProjectCreationUseCase: | ||
def __init__(self, template_type_integration: TemplateTypeIntegrationInterface): | ||
self.__template_type_integration = template_type_integration | ||
|
||
def get_or_create_user_by_email(self, email: str) -> tuple: | ||
return User.objects.get_or_create(email=email) | ||
|
||
def get_or_create_project(self, project_dto: ProjectCreationDTO, user: User) -> tuple: | ||
return Project.objects.get_or_create( | ||
uuid=project_dto.uuid, | ||
defaults=dict( | ||
name=project_dto.name, | ||
date_format=project_dto.date_format, | ||
timezone=project_dto.timezone, | ||
created_by=user, | ||
is_template=project_dto.is_template, | ||
), | ||
) | ||
|
||
def create_project(self, project_dto: ProjectCreationDTO, user_email: str) -> None: | ||
user, _ = self.get_or_create_user_by_email(user_email) | ||
project, _ = self.get_or_create_project(project_dto, user) | ||
|
||
if project_dto.is_template: | ||
self.__template_type_integration.integrate_template_type_in_project( | ||
project, project_dto.template_type_uuid, user | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from marketplace.applications.models import App | ||
from ..models import TemplateType, Project | ||
|
||
|
||
def create_template_type(uuid: str, project_uuid: Project, name: str) -> TemplateType: | ||
setup = {"apps": []} | ||
|
||
for app in App.objects.filter(project_uuid=project_uuid): | ||
try: | ||
setup["apps"].append(app.apptype.template_type_setup()) | ||
except NotImplementedError as error: | ||
print(error) | ||
pass | ||
|
||
template_type, created = TemplateType.objects.get_or_create(uuid=uuid, defaults=dict(name=name, setup=setup)) | ||
|
||
if not created: | ||
template_type.name = name | ||
template_type.setup = setup | ||
template_type.save() | ||
|
||
return template_type |
33 changes: 33 additions & 0 deletions
33
marketplace/projects/usecases/template_type_integration.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from typing import TYPE_CHECKING | ||
|
||
from .interfaces import TemplateTypeIntegrationInterface | ||
from .exceptions import InvalidTemplateTypeData | ||
from ..models import TemplateType | ||
|
||
|
||
if TYPE_CHECKING: # pragma: no cover | ||
from ..models import Project | ||
from django.contrib.auth import get_user_model | ||
|
||
User = get_user_model() | ||
|
||
|
||
class TemplateTypeIntegrationUseCase(TemplateTypeIntegrationInterface): | ||
def __init__(self, app_setup_handler): | ||
self.__app_setup_handler = app_setup_handler | ||
|
||
def integrate_template_type_in_project(self, project: "Project", template_type_uuid: str, user: "User") -> None: | ||
if project.template_type is not None: | ||
raise InvalidTemplateTypeData(f"The project `{project.uuid}` already has an integrated template!") | ||
|
||
if template_type_uuid is None: | ||
raise InvalidTemplateTypeData("'template_type_uuid' cannot be empty when 'is_template' is True!") | ||
|
||
try: | ||
template_type = TemplateType.objects.get(uuid=template_type_uuid) | ||
except TemplateType.DoesNotExist: | ||
raise InvalidTemplateTypeData(f"Template Type with uuid `{template_type_uuid}` does not exists!") | ||
|
||
self.__app_setup_handler.setup_apps_in_project(project, template_type, user) | ||
project.template_type = template_type | ||
project.save() |
Empty file.
52 changes: 52 additions & 0 deletions
52
marketplace/projects/usecases/tests/test_app_setup_handler.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import uuid | ||
from unittest.mock import MagicMock | ||
|
||
from django.test import TestCase | ||
from django.contrib.auth import get_user_model | ||
|
||
from ..app_setup_handler import AppSetupHandlerUseCase | ||
from ...models import Project, TemplateType | ||
from ..exceptions import InvalidTemplateTypeData | ||
|
||
|
||
User = get_user_model() | ||
|
||
|
||
class AppSetupHandlerTestCase(TestCase): | ||
def setUp(self): | ||
self.app_configuration = MagicMock() | ||
self.uescase = AppSetupHandlerUseCase(self.app_configuration) | ||
|
||
self.user = User.objects.create_superuser(email="user@marketplace.ai") | ||
self.project = Project.objects.create(uuid=uuid.uuid4(), name="Test Project", created_by=self.user) | ||
|
||
def test_empty_setup_raises_invalid_template_type_data(self): | ||
template_type = TemplateType.objects.create(uuid=uuid.uuid4(), name="Fake TT", setup={}) | ||
|
||
error_message = f"The `setup` of TemplateType {template_type.uuid} is empty!" | ||
with self.assertRaisesMessage(InvalidTemplateTypeData, error_message): | ||
self.uescase.setup_apps_in_project(self.project, template_type, self.user) | ||
|
||
def test_setup_without_code_raises_invalid_template_type_data(self): | ||
template_type = TemplateType.objects.create(uuid=uuid.uuid4(), name="Fake TT", setup={"apps": [{}]}) | ||
|
||
error_message = f"The TemplateType {template_type.uuid} has an invalid setup!" | ||
with self.assertRaisesMessage(InvalidTemplateTypeData, error_message): | ||
self.uescase.setup_apps_in_project(self.project, template_type, self.user) | ||
|
||
def test_setup_without_invalid_code_raises_invalid_template_type_data(self): | ||
template_type = TemplateType.objects.create( | ||
uuid=uuid.uuid4(), name="Fake TT", setup={"apps": [{"code": "fake-code"}]} | ||
) | ||
|
||
error_message = f"TemplateType {template_type.uuid} has invalid app code!" | ||
with self.assertRaisesMessage(InvalidTemplateTypeData, error_message): | ||
self.uescase.setup_apps_in_project(self.project, template_type, self.user) | ||
|
||
def test_configure_app_called_once_with_valid_template_type(self): | ||
template_type = TemplateType.objects.create( | ||
uuid=uuid.uuid4(), name="Fake TT", setup={"apps": [{"code": "wpp-demo"}]} | ||
) | ||
|
||
self.uescase.setup_apps_in_project(self.project, template_type, self.user) | ||
self.app_configuration.configure_app.assert_called_once() |
Oops, something went wrong.