Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inject dependencies in handlers #571

Open
OrangeTux opened this issue Jan 15, 2024 · 0 comments
Open

Inject dependencies in handlers #571

OrangeTux opened this issue Jan 15, 2024 · 0 comments
Labels
enhancement New feature or request

Comments

@OrangeTux
Copy link
Collaborator

OrangeTux commented Jan 15, 2024

The current routing API provides very little flexibility. The current approach with the @on() decorator has a few limitations.

Issues

  1. The interface of a handlers is limited to receive primitive types like str, int, list or dict. It would be great if the framework allow handlers taking composite types. Compare the first 'old' style of routing with the second example that uses composite types.
class ChargePoint:
  @on("TransactionEvent")
  async def on_transaction_event(
    self, 
    event_type: str,
    datetime: str,
    meter_value: Dict,
    **kwargs
):
    ...
from datetime import datetime
from ocpp.v201.enums import EventType
from ocpp.v201.datatypes import Metervalue

class ChargePoint:
  @on("TransactionEvent")
  async def on_transaction_event(
    self, 
    event_type: EventType
    datetime: datetime,
    meter_value: MeterValue,
    **kwargs
):
   ...
  1. Handlers only receive data that's available in the Call's payload. It would be great if also other dependencies could be injected into a handler. For example, the unique id of message. See Add call unique id to handler kwargs #545 . Or maybe a database connection. Or the IP address of the charger.

  2. Handlers are tightly coupled an instance of ocpp.v16.ChargePoint or ocpp.v201.ChargePoint. You can't register plain function as handlers. E.g., this is not possible:

@on("Heartbeat")
async fn on_heartbeat(**kwargs):
   ...

In my experience, binding the handlers to a a class leads to large and complex classes. Where each handler mutates a piece of state within the class.

Possible solution

I'm wondering if we can implement a dependency mechanism that allows users a lot more flexibility. FastAPI has an interesting Dependency Injection mechanism. And I'm keen to figure out if we can implement something similar.

FastAPI allows handlers to receive any type, as long as that type can be constructed from an HTTP request. This library could implement a similar approach. Below are three examples of how dependency injection could look like:

class ChargePoint():
   @on("TransactionEvent")
   async def on_transaction_event(
      self,
      event_type: EventType = Depends(EventType),
      datetime: datetime = Depends(datetime),
      meter_value: MeterValue = Depends(MeterValue),
  ):
    ...
# Filter the unique_id from a call
def unique_id(call: Call) -> str:
   ...

class ChargePoint():
   @on("heartbeat")
   async def on_heartbeat(
      self,
      unique_id: str = Depends(unique_id),
  ):
    ...
# Returns the EVSE ID of a connection
def evse_id(...) -> str:
   ...

# Returns a connection to a database
def get_db() -> DbConnection:
   ...

@on("BootNotification")
async def on_boot_notification(
   evse_id: str = Depends(evse_id),
   db: DbConnection = Depends(get_db),
):
   """ Look up EVSE ID in database and to verify if it is allowed to connect.  """
  ... 
   
@OrangeTux OrangeTux added the enhancement New feature or request label Jan 15, 2024
@OrangeTux OrangeTux changed the title Dependencie injection Dependency injection Jan 15, 2024
@OrangeTux OrangeTux changed the title Dependency injection Inject dependencies in handlers Jan 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Development

No branches or pull requests

1 participant