Skip to main content

Example Project Structure

Startup File​

from arconq_sdk import AppBuilder, Dependency, arconq_app
from containers.container import Container
from message_handlers import example_message_handler
from routers import examples_router
from scheduled_job import example_scheduled_job
from background_jobs import example_background_job


@arconq_app
def main(
container: Container,
app_builder: AppBuilder = Dependency(Container.app_builder),
) -> None:
app_builder.configure_arconq(container)

# registrations of a router, message handler, a scheduled job and a background job (parts of the example app)
app_builder.add_web_api_router(container, examples_router, examples_router.router, "/router")
app_builder.add_scheduled_job(container, example_scheduled_job, example_scheduled_job.job)
app_builder.add_background_job(container, example_background_job, example_background_job.job)
app_builder.add_message_handler(container, example_message_handler, example_message_handler.handler)

app = app_builder.build_app()

app.run()


if __name__ == "__main__":
# usage of the Container class (more information below)
container = Container()
container.wire(modules=[__name__])
main(container)

Container Class​

Registrations of services and other classes that can be injected to message handlers, scheduled jobs and routers should be put here. It's going to make possible injecting them via dependency injection mechanism. Each registered service need to get passed the parameters defined in their constructors.

from dependency_injector import providers
from arconq_sdk.dependency_injection.base_container import BaseContainer
from services.hello_world_service import HelloWorldService
from services.random_int_service import RandomIntService
from services.messaging_service import MessagingService

class Container(BaseContainer):
random_int_service = providers.Factory(RandomIntService, BaseContainer.logger)
hello_world_service = providers.Factory(HelloWorldService, random_int_service)
messaging_service = providers.Factory(MessagingService, BaseContainer.logger, BaseContainer.message_bus)

Example Router​

You need to create a Router object and use it to decorate a function. In the highlighted lines two injections are happening:

  • Logger - part of the ArconQ SDK
  • HelloWorldService - part of the example app

Since it's a function and not a class receiving the injections, there's a need to use the Dependency function and refer to the injected class from the Container.

from arconq_sdk import Dependency, Logger
from arconq_sdk.web_api.router import Router
from containers.container import Container
from services.hello_world_service import HelloWorldService

router = Router()
tag = "Examples"

@router.http_get("", response_model=dict[str, str], tags=[tag])
async def get_examples_async(
logger: Logger = Dependency(Container.logger),
hello_world_service: HelloWorldService = Dependency(Container.hello_world_service),
):
greeting_phrase = hello_world_service.generate_greeting_phrase()
logger.info(greeting_phrase)
return {"greeting_phrase": greeting_phrase}

Example Services​

Here is the HelloWorldService definition. It also is getting injected another service called RandomIntService. Notice a different syntax. When injecting to a class constructor, there's no need to use the Dependency function. Putting the parameter in constructor is enough. Remember the service registration in the Container class.

from services.random_int_service import RandomIntService

class HelloWorldService:
def __init__(self, random_int_service: RandomIntService) -> None:
self.random_int_service = random_int_service

def generate_greeting_phrase(self) -> str:
return f"Hello World! {self.random_int_service.generate_random_int()}"

RandomIntService definition:

import random
from arconq_sdk import Logger

class RandomIntService:
def __init__(self, logger: Logger) -> None:
self.logger = logger

def generate_random_int(self) -> int:
self.logger.info("Generating new random integer")
return random.choice([1, 2, 3])

Example Message​

This class represents payload used in the examples below the Scheduled Job sends it and the Message Handler receives it.

from arconq_sdk.contracts.message import Message


class ExampleMessage(Message):
Id: int
Description: str
Number: int

Example Scheduled Job​

You need to define a ScheduledJob object and use it to decorate a function. In the highlighted lines two injections are happening:

  • Logger - part of the ArconQ SDK
  • MessagingService - part of the example app

For more information about scheduling check this article.

from arconq_sdk import Dependency, Logger, ScheduledJob
from containers.container import Container
from services.messaging_service import MessagingService

job = ScheduledJob()

@job.schedule("/example-job", cron_schedule="0/10 * * ? * * *")
async def example_scheduled_job(
logger: Logger = Dependency(Container.logger),
messaging_service: MessagingService = Dependency(Container.messaging_service)
):
logger.info("Scheduled job was executed")
await messaging_service.send_message()

Below you can find the MessagingService definition. It uses the MessageBus (part of ArconQ SDK) to publish a message.
Remember the service registration in the Container class.

For more information about sending messages please check this article.

from arconq_sdk import Logger, MessageBus
import random
from contracts.example_message import ExampleMessage

class MessagingService:
def __init__(self, logger: Logger, message_bus: MessageBus) -> None:
self.logger = logger
self.message_bus = message_bus

async def send_message(self) -> None:
message = ExampleMessage(
Id=1,
Description="message sent by a Python app",
Number=random.randint(1, 100),
)
self.logger.info("Sending a message: {Message}", Message=message)
await self.message_bus.publish_async(message)

Example Background Job​

You need to define a BackgroundJob object and use it to decorate a function. In the highlighted lines two injections are happening:

  • Logger - part of the ArconQ SDK
  • MessagingService - part of the example app

For more information about background jobs check this article.

import asyncio

from arconq_sdk import Dependency, Logger, BackgroundJob
from containers.container import Container
from services.messaging_service import MessagingService

job = BackgroundJob()


@job.register(name="example background job")
async def example_background_job(
logger: Logger = Dependency(Container.logger),
messaging_service: MessagingService = Dependency(Container.messaging_service)
):
while True:
await messaging_service.send_message()
logger.info("Message has been sent")
await asyncio.sleep(10.0)

Example Message Handler​

You need to create a MessageHandler object and use it to decorate a function. Type of message that will be handled needs to be passed in the decorator parameters message_type=ExampleMessage. The method parameters:

  • message - the message that the handler will receive
  • logger - part of the ArconQ SDK, injected via dependency injection.

For more information about receiving messages please check this article

from arconq_sdk import Dependency, Logger, MessageHandler
from containers.container import Container
from contracts.example_message import ExampleMessage

handler = MessageHandler()

@handler.handle("/example-handler", message_type=ExampleMessage)
async def example_message_handler(
message: ExampleMessage,
logger: Logger = Dependency(Container.logger),
):
logger.info("Handler received a message of content: {Message}", Message=message)