Skip to content

Commit

Permalink
add module-level depencency containers, refactor logger
Browse files Browse the repository at this point in the history
  • Loading branch information
pgorecki committed Sep 7, 2021
1 parent 6134dd2 commit b54a53f
Show file tree
Hide file tree
Showing 20 changed files with 824 additions and 196 deletions.
52 changes: 0 additions & 52 deletions src/api/container.py

This file was deleted.

76 changes: 23 additions & 53 deletions src/api/main.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
import logging
from os import getenv
from fastapi import FastAPI, Depends
from seedwork.infrastructure.request_context import request_context
from starlette.middleware import Middleware
from starlette_context import context, plugins
from starlette_context.middleware import ContextMiddleware


from seedwork.domain.value_objects import UUID
from seedwork.infrastructure.repository import InMemoryRepository
from seedwork.infrastructure.logging import logger, LoggerFactory
from modules.catalog.domain.entities import Listing
from modules.catalog.application.commands import CreateListingDraftCommand
from modules.catalog.application.command_handlers import create_listing_draft

from api.routers import catalog, users
from api.middleware import DependencyInjecionPlugin
from api.container import Container, RequestContainer

from api.middleware import RequestContextPlugin
from config.api_config import ApiConfig
from config.container import Container


# configure logger prior to first usage
LoggerFactory.configure(logger_name="cli")

# dependency injection container
container = Container()
Expand All @@ -27,11 +31,7 @@
middleware = [
Middleware(
ContextMiddleware,
plugins=(
plugins.RequestIdPlugin(),
plugins.CorrelationIdPlugin(),
DependencyInjecionPlugin(container),
),
plugins=(RequestContextPlugin(container),),
)
]

Expand All @@ -41,7 +41,7 @@
app.include_router(users.router)
app.container = container

logger = container.logger()

logger.info("using db engine %s" % str(container.engine()))


Expand All @@ -50,49 +50,19 @@ async def root():
return {"info": "Online auctions API. See /docs for documentation"}


def request_container():
return RequestContainer.create(app.container)


def request_logger(container=Depends(request_container)):
return container.request_logger()


@app.get("/test")
async def test(container=Depends(request_container), logger=Depends(request_logger)):
logger.debug("test")
async def test():
import asyncio

logger.debug("test1")
await asyncio.sleep(3)
logger.debug("test2")
await asyncio.sleep(3)
logger.debug("test3")
await asyncio.sleep(3)
logger.debug("test4")
service = container.dummy_service()
return {"service response": service.serve(), "request_id": container.request_id()}


# ####
# from fastapi import Depends


# def common_parameters(x: str, y: int):
# print(args, kwargs)
# return None


# def common_parameters2(z: int = 10):
# print(args, kwargs)
# return None


# def get_context():
# context = object()
# context.correlation_id = "1234"
# return context


# @app.get("/dependency")
# async def root(
# # foo=Depends(common_parameters),
# # bar=Depends(common_parameters2),
# context=Depends(get_context),
# ):
# # print(request)
# return {
# "info": "Online auctions API. See /docs for documentation "
# + context.correlation_id
# }
return {
"service response": service.serve(),
"correlation_id": request_context.correlation_id,
}
12 changes: 9 additions & 3 deletions src/api/middleware.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
from os import umask


import uuid
from starlette_context.plugins import Plugin


class DependencyInjecionPlugin(Plugin):
key = "container"
class RequestContextPlugin(Plugin):
key = "request_context"

def __init__(self, container) -> None:
super().__init__()
self.container = container

async def process_request(self, request):
return self.container
"""When processing a request, we set up a new request context"""
context = self.container.request_context()
context._correlation_id.set(uuid.uuid4())
6 changes: 6 additions & 0 deletions src/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from pathlib import Path
import sys

# add base project path to PYTHONPATH
BASE_DIR = Path(__file__).resolve().parent.parent
sys.path.append(str(BASE_DIR))
38 changes: 38 additions & 0 deletions src/cli/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from uuid import uuid4
from modules.catalog import catalog_container
from modules.catalog.domain.repositories import SellerRepository
from modules.catalog.application.queries import (
GetAllListingsQuery,
GetListingsOfSellerQuery,
)
from seedwork.infrastructure.request_context import request_context
from seedwork.infrastructure.logging import logger, LoggerFactory


# a sample command line script to print all listings
# run with "cd src && python -m cli"

# configure logger prior to first usage
LoggerFactory.configure(logger_name="cli")

# configure catalog module
catalog_container.config.from_dict(dict())

# instantiate catalog module
catalog_module = catalog_container.module()

logger.info("Application configured")

# interact with a catalog module by issuing queries and commands
# use request context if you want to logically separate queries/commands
# from each other in the logs
with request_context:
query_result = catalog_module.execute_query(GetAllListingsQuery())
logger.info("All listings: %s", query_result.data)

with request_context:
seller_id = SellerRepository.next_id()
query_result = catalog_module.execute_query(
GetListingsOfSellerQuery(seller_id=seller_id)
)
logger.info(f"Listings of seller {seller_id} %s", query_result.data)
Empty file added src/cli/dependencies.py
Empty file.
34 changes: 34 additions & 0 deletions src/config/container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import uuid
import logging
from sqlalchemy import create_engine
from dependency_injector import containers, providers

from modules.catalog.infrastructure.persistence import MongoListingRepository
from seedwork.infrastructure.request_context import RequestContext


class DummyService:
def __init__(self, config) -> None:
self.config = config

def serve(self):
return f"serving with config {self.config}"


class Container(containers.DeclarativeContainer):
"""Dependency Injection Container
see https://github.com/ets-labs/python-dependency-injector for more details
"""

config = providers.Configuration()
engine = providers.Singleton(create_engine, config.DATABASE_URL, echo=config.DEBUG)
dummy_service = providers.Factory(DummyService, config)
dummy_singleton = providers.Singleton(DummyService, config)

request_context = providers.Singleton(RequestContext)

correlation_id = providers.Factory(
lambda request_context: request_context.correlation_id.get(), request_context
)
listing_repository = providers.Factory(MongoListingRepository)
7 changes: 7 additions & 0 deletions src/modules/catalog/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from modules.catalog.container import CatalogModuleContainer

"""
catalog_container is a composition root for a catalog module
Use `catalog_container.module()` to create an instance of CatalogModule
"""
catalog_container = CatalogModuleContainer()
5 changes: 5 additions & 0 deletions src/modules/catalog/application/queries.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from seedwork.domain.value_objects import UUID
from seedwork.application.queries import Query


class GetAllListingsQuery(Query):
...


class GetListingsOfSellerQuery(Query):
seller_id: UUID
14 changes: 10 additions & 4 deletions src/modules/catalog/application/query_handlers.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
from seedwork.application.queries import Query
from seedwork.application.query_handlers import QueryResult
from seedwork.application.query_handlers import QueryHandler, QueryResult
from seedwork.application.decorators import query_handler

from ..application.queries import GetAllListingsQuery
from ..application.queries import GetAllListingsQuery, GetListingsOfSellerQuery
from ..domain.repositories import ListingRepository


@query_handler
def get_all_listings(query: GetAllListingsQuery, sql_connection) -> QueryResult:
def get_all_listings(query: GetAllListingsQuery) -> QueryResult:
sql = "SELECT * FROM listings"
data = sql_connection.execute(sql)
data = ["foo", "bar"]
return QueryResult.ok(data)


@query_handler
def get_listings_of_seller(query: GetListingsOfSellerQuery) -> QueryHandler:
data = ["foo"]
return QueryResult.ok(data)
20 changes: 20 additions & 0 deletions src/modules/catalog/container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""
Each module has it's own DI composition root
"""

from dependency_injector import containers, providers
from sqlalchemy import create_engine
from modules.catalog.module import CatalogModule
from modules.catalog.infrastructure.persistence import MongoListingRepository


class CatalogModuleContainer(containers.DeclarativeContainer):
"""Dependency Injection Container
see https://github.com/ets-labs/python-dependency-injector for more details
"""

config = providers.Configuration()
engine = providers.Singleton(create_engine, config.DATABASE_URL, echo=config.DEBUG)
listing_repository = providers.Factory(MongoListingRepository)
module = providers.Singleton(CatalogModule)
1 change: 1 addition & 0 deletions src/modules/catalog/domain/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def publish(self):


class Seller(Entity):
id: UUID
is_new: bool = True
currently_published_listings_count: int = 0

Expand Down
21 changes: 21 additions & 0 deletions src/modules/catalog/infrastructure/persistence.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from seedwork.domain.aggregates import Aggregate
from seedwork.domain.value_objects import UUID

from modules.catalog.domain.repositories import ListingRepository


class MongoListingRepository(ListingRepository):
def get_by_id(self, id: UUID) -> Aggregate:
...

def get_by_id(self, id: UUID) -> Aggregate:
...

def insert(self, entity: Aggregate):
...

def update(self, entity: Aggregate):
...

def delete(self, entity_id: UUID):
...
Loading

0 comments on commit b54a53f

Please sign in to comment.