chapps.rest.routers.common module
Route factories and other reusable code
Factories for utility functions and API routes are defined in this module along with some FastAPI dependencies.
The route factories perform the repetitive grunt work required to set up the
typical ‘create’, ‘read’, ‘update’, ‘delete’ and ‘list’ functions needed for
basic object management. In order to avoid extra levels of metaprogramming,
the parameter name for the record ID of the main object involved in a
factory-generated API call is item_id
, since it is clear, brief and
generic. Apologies to future subclassors who want an ‘items’ table.
These route factories are used to create all the routes for
users
, emails
,
domains
, and quotas
.
- async chapps.rest.routers.common.list_query_params(skip=0, limit=1000, q='%')[source]
FastAPI dependency for list queries
- chapps.rest.routers.common.model_name(cls)[source]
Convenience function to get the lowercase name of a model
- Return type
- chapps.rest.routers.common.load_model_with_assoc(cls, assoc, engine=Engine(mysql://chapps:***@localhost:3306/chapps))[source]
Create a closure which loads an object along with arbitrary associations
This isn’t meant to create an API route on its own, but it may be used in API routes. It is mainly used in
live
routes, which are currently all one-offs, not created by factories. In order to return a closure which can work in any context, it does not return a coroutine but a standard synchronous closure.- Parameters
cls (CHAPPSModel) – a data model class
assoc (
List
[JoinAssoc
]) – a list of associations (asJoinAssoc
objects)engine (Optional[Engine]) – defaults to
chapps.dbsession.sql_engine
if not specified
- Return type
callable
- Returns
a closure as follows:
- chapps.rest.routers.common.load_models_with_assoc(cls, *, assoc, engine=Engine(mysql://chapps:***@localhost:3306/chapps))[source]
Build a map of source name => associated object id
- Parameters
cls (
CHAPPSModel
) – source modelassoc (
JoinAssoc
) – a join association representing the associated modelengine – override the SQLA engine if desired
- Return type
callable
- Returns
a mapper function which accepts a list of IDs of the source model and returns a list of dicts with
<source_model>_name
and<assoc_model>_id
fields, mapping the source objects onto the IDs of their associated objects of the configured type.
- chapps.rest.routers.common.db_wrapper(*, cls, engine=Engine(mysql://chapps:***@localhost:3306/chapps), exception_message='{route_name}:{model}', empty_set_message='Unable to find a matching {model}')[source]
Decorator for database interactions
- Parameters
cls (CHAPPSModel) – the data model class
engine (Engine) – an
SQLAlchemy
engine, which defaults to the package-wide one declared indbsession
exception_message (str) – a message to include if any untrapped exception occurs; defaults to
{route_name}:{model}
. Only those two symbols are available for expansion. All arguments are appended.empty_set_message (str) – included if a SELECT results in an empty set; defaults to
Unable to find a matching {model}
and supports both substitutions thatexception_message
does
- Returns
a decorator closure, which will be called with the function to be decorated as its argument. This is a regular callable decorator.
- Return type
callable which wraps and returns a function
The decorator sets up some global symbols for use inside the DB access function:
- session
a
Session
instance created in a context containing the execution of the wrapped coroutine, suitable for performing database interactions, and which will be automatically closed after the coroutine completes- model_name
a string containing the lowercase name of the model
- chapps.rest.routers.common.db_interaction(*, cls, engine=Engine(mysql://chapps:***@localhost:3306/chapps), exception_message='{route_name}:{model}', empty_set_message='Unable to find a matching {model}')[source]
Decorator for database interactions
- Parameters
cls (CHAPPSModel) – the data model class
engine (Engine) – an
SQLAlchemy
engine, which defaults to the package-wide one declared indbsession
exception_message (str) – a message to include if any untrapped exception occurs; defaults to
{route_name}:{model}
. Only those two symbols are available for expansion. All arguments are appended.empty_set_message (str) – included if a SELECT results in an empty set; defaults to
Unable to find a matching {model}
and supports both substitutions thatexception_message
does
- Returns
a decorator closure, which will be called with the function to be decorated as its argument. In this case, the function is expected to be a coroutine which is being manufactured for use in the API, and so the decorator closure returned by this routine defines a coroutine to wrap and await its argument, which is ultimately returned and used as the API route.
- Return type
callable which wraps and returns a coroutine
The decorator sets up some global symbols for use inside the API route coroutines:
- session
a
Session
instance created in a context containing the execution of the wrapped coroutine, suitable for performing database interactions, and which will be automatically closed after the coroutine completes- model_name
a string containing the lowercase name of the model
- chapps.rest.routers.common.get_item_by_id(cls, *, response_model, engine=Engine(mysql://chapps:***@localhost:3306/chapps), assoc=None)[source]
Build a route coroutine to get an item by ID
- Parameters
cls (CHAPPSModel) – the main data model for the request
response_model (CHAPPSResponse) – the response model
engine (Engine) – defaults to
sql_engine
assoc (List[JoinAssoc]) – if included, these associations will be included as optional keys in the response
At present there is no provision for dealing with extremely long association lists. Even if there were 500 elements, the response would not be extremely large.
Note
An alternate closure factory for creating routes which specifically list associations does provide pagination, etc. See
list_associated()
The factory produces a coroutine decorated with the
db_interaction()
decorator, as do all the route factories. Its signature is:async def get_i(item_id: int) -> response_model
The factory sets the final closure’s name and doc metadata properly to ensure that the automatic documentation is coherent and accurate. All the route factories do this to a greater or lesser extent.
- chapps.rest.routers.common.list_items(cls, *, response_model, engine=Engine(mysql://chapps:***@localhost:3306/chapps))[source]
Build a route coroutine to list items
- Parameters
cls (CHAPPSModel) – the main data model for the request
response_model (CHAPPSResponse) – the response model
engine (Engine) – defaults to
sql_engine
The returned closure expects to receive the query parameters as a dict, since that is what the dependency will return. Its signature is
async def list_i(qparams: dict = Depends(list_query_params))
The closure’s name and document metadata are updated to ensure coherence and accuracy of the automatic API documentation.
For an example of using this factory, see Listing Domains.
- chapps.rest.routers.common.list_associated(cls, *, assoc, response_model, engine=Engine(mysql://chapps:***@localhost:3306/chapps))[source]
Build a route to list associated items with pagination
- Parameters
cls (
CHAPPSModel
) – the main data modelassoc (
JoinAssoc
) – the association to listresponse_model (
CHAPPSResponse
) – the response modelengine (sqlalchemy.engine.Engine) – defaults to
sql_engine
The returned coroutine will paginate a list of the associated objects, given the ID of a main (source) object to use to select associations. The
qparams
parameter is a bundle of standard listing query parameters defined bylist_query_params()
via thefastapi.Depends
mechanism.async def assoc_list(item_id: int, qparams: dict) -> response_model
It returns in the
response
key of its output a list of the associated object, goverened by the search and window parameters inqparams
.
- chapps.rest.routers.common.delete_item(cls, *, response_model=<class 'chapps.models.DeleteResp'>, engine=Engine(mysql://chapps:***@localhost:3306/chapps))[source]
Build a route coroutine to delete an item by ID
- Parameters
cls (CHAPPSModel) – the data model to manage
response_model (CHAPPSResponse) – defaults to
DeleteResp
engine (Engine) – defaults to
sql_engine
The returned coroutine accepts a list of record IDs for the specified object type and delete them. Its signature is:
async def delete_i(item_ids: List[int]) -> DeleteResp
- chapps.rest.routers.common.adjust_associations(cls, *, assoc, assoc_op, params=None, response_model=<class 'chapps.models.TextResp'>, engine=Engine(mysql://chapps:***@localhost:3306/chapps))[source]
Build a route to add or subtract association lists, or set unitary ones
- Parameters
cls (CHAPPSModel) – a data model class
assoc (List[JoinAssoc]) – list of associations to operate on
assoc_op (AssocOperation) – operation to perform on the association
response_model (CHAPPSResponse) – the response model to send
engine (Engine) – defaults to
sql_engine
The returned coroutine provides logic for a route which adds or subtracts elements to or from those already associated with the main object. Its exact signature is dependent on what associations are listed. After
item_id
, which is an ID to use to look up the main object, it will expect further arguments named as the association (assoc_name
) which are of the specified type (assoc_type
).If only one association is adjusted by the route, there will be just the one list (or scalar) as a body argument, which doesn’t get a label, making the API call very easy to format and looking very clean in the API docs. If more than one are specified,
FastAPI
will expect a JSON object in the body with keys named as the ID columns and values which are lists of IDs.It all seems quite complicated when stated this way, but when viewed in the API documentation, it makes much more sense.
For an example of using this factory, see Handling Associations
- chapps.rest.routers.common.update_item(cls, *, response_model, assoc=None, engine=Engine(mysql://chapps:***@localhost:3306/chapps))[source]
Build a route to update items.
- Parameters
cls (CHAPPSModel) – the main data model
response_model (CHAPPSResponse) – the response model
assoc (JoinAssoc) – the association to list
engine (Engine) – defaults to
sql_engine
The returned coroutine implements an API route for updating an item by ID, optionally including any associations included when the route coroutine is built. If association data is provided to the route, it will completely replace any existing associations to that type of record with the new list of IDs.
Its signature is determined by the contents of the
JoinAssoc
passed to it. The factory constructsParameter
elements and uses them to create a correctSignature
for the new, decorated closure. It also sets the__doc__
and__name__
metadata so that FastAPI will be able to find all the required data to create an API route with good documentation.For an example of how to use this factory, see Updating Domains
- chapps.rest.routers.common.create_item(cls, *, response_model, params=None, defaults=None, assoc=None, engine=Engine(mysql://chapps:***@localhost:3306/chapps))[source]
Build a route coroutine to create new item records
- Parameters
cls (CHAPPSModel) – the main data model
response_model (CHAPPSResponse) – the response model
params (dict) – defaults to
dict(name=str)
; specify to provide additional column names and types, and be sure to includename
, as all models currently are expected to have aname
column, which is not allowed to be null.assoc (JoinAssoc) – the associations to attach, if any
engine (Engine) – defaults to
sql_engine
The returned coroutine implements an API route for creating an item, setting all its elements (other than ID) to whatever values are provided. Currently all values must be provided. If desired, associations may also be provided to the factory, and they will be accommodated by the coroutine.
For an example invocation of this factory, see Creating Users