Skip to content

Commit 41a47c8

Browse files
committed
Implement publish product workflow
1 parent c0ddb99 commit 41a47c8

7 files changed

Lines changed: 394 additions & 3 deletions

File tree

api/src/api/.env.Local

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
LoggingLevel="INFO"
1+
LoggingLevel="INFO"
2+
CosmosDbNoSqlUrl="http://localhost:8081"
3+
CosmosDbNoSqlKey="C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
4+
CosmosDbNoSqlDatabase="mydatabase"

api/src/api/application_settings.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from pathlib import Path
22

33
from azure.identity import DefaultAzureCredential
4+
from pydantic import SecretStr
45
from pydantic.alias_generators import to_pascal
56
from pydantic_settings import (
67
AzureKeyVaultSettingsSource,
@@ -18,6 +19,9 @@ class ApplicationSettings(BaseSettings):
1819
model_config = SettingsConfigDict(alias_generator=to_pascal, extra="ignore")
1920

2021
logging_level: str
22+
cosmos_db_no_sql_url: str
23+
cosmos_db_no_sql_key: SecretStr
24+
cosmos_db_no_sql_database: str
2125

2226
@classmethod
2327
def settings_customise_sources(
Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,48 @@
1+
from azure.cosmos import CosmosClient, PartitionKey
2+
13
from api.application_settings import ApplicationSettings
24
from api.workflows.products.publish_product.publish_product_workflow import (
35
PublishProductWorkflow,
46
)
7+
from common.application_environment import ApplicationEnvironment
8+
from domain.entities.product import Product
59

610

711
class DependencyContainer:
812
@classmethod
913
def initialize(cls) -> None:
1014
cls.initialize_application_settings()
15+
cls.initialize_local_environment()
1116

1217
@classmethod
1318
def initialize_application_settings(cls) -> None:
1419
cls.application_settings = ApplicationSettings() # type: ignore[reportCallIssue]
1520

21+
@classmethod
22+
def initialize_local_environment(cls) -> None:
23+
if ApplicationEnvironment.get_current() != ApplicationEnvironment.LOCAL:
24+
return
25+
26+
with CosmosClient(
27+
cls.get_application_settings().cosmos_db_no_sql_url,
28+
cls.get_application_settings().cosmos_db_no_sql_key.get_secret_value(),
29+
) as cosmosdb_client:
30+
cosmosdb_client.create_database_if_not_exists(
31+
id=cls.get_application_settings().cosmos_db_no_sql_database
32+
)
33+
cosmosdb_database = cosmosdb_client.get_database_client(
34+
cls.get_application_settings().cosmos_db_no_sql_database
35+
)
36+
cosmosdb_database.create_container_if_not_exists(
37+
id=Product.__name__, partition_key=PartitionKey("/id")
38+
)
39+
1640
@classmethod
1741
def get_application_settings(cls) -> ApplicationSettings:
1842
return cls.application_settings
1943

2044
@classmethod
2145
def get_publish_product_workflow(cls) -> PublishProductWorkflow:
22-
return PublishProductWorkflow()
46+
return PublishProductWorkflow(
47+
application_settings=cls.get_application_settings()
48+
)
Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
1+
from azure.cosmos.aio import CosmosClient
2+
3+
from api.application_settings import ApplicationSettings
14
from api.workflows.products.publish_product.publish_product_request import (
25
PublishProductRequest,
36
)
47
from api.workflows.products.publish_product.publish_product_response import (
58
PublishProductResponse,
69
)
10+
from domain.entities.product import Product
711

812

913
class PublishProductWorkflow:
14+
def __init__(self, application_settings: ApplicationSettings) -> None:
15+
self.application_settings = application_settings
16+
1017
async def execute(self, request: PublishProductRequest) -> PublishProductResponse:
11-
return PublishProductResponse(id=request.name)
18+
async with CosmosClient(
19+
self.application_settings.cosmos_db_no_sql_url,
20+
self.application_settings.cosmos_db_no_sql_key.get_secret_value(),
21+
) as cosmosdb_client:
22+
cosmosdb_database = cosmosdb_client.get_database_client(
23+
self.application_settings.cosmos_db_no_sql_database
24+
)
25+
product_container = cosmosdb_database.get_container_client(Product.__name__)
26+
product = Product.publish(
27+
name=request.name,
28+
description=request.description,
29+
price=request.price,
30+
)
31+
await product_container.create_item(product.model_dump())
32+
return PublishProductResponse(id=product.id)

domain/src/domain/entities/product.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ class Product(Entity):
1313
is_discontinued: bool
1414
discontinuation_reason: str | None = None
1515

16+
@staticmethod
17+
def publish(name: str, price: Decimal, description: str | None = None) -> "Product":
18+
return Product(
19+
name=name, price=price, description=description, is_discontinued=False
20+
)
21+
1622
def discontinue(self, discontinuation_reason: str | None = None) -> None:
1723
if self.is_discontinued:
1824
error_message = "The product is already discontinued"

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ name = "python-architecture"
33
version = "0.1.0"
44
requires-python = ">=3.12"
55
dependencies = [
6+
"aiohttp==3.11.18",
7+
"azure-cosmos==4.9.0",
68
"common",
79
"domain",
810
"fastapi[standard]==0.115.12",

0 commit comments

Comments
 (0)