chapps.rest.routers.users module

User record management implemented by factories

This module defines API routes for managing User records, and defines the JoinAssoc data to describe the relationship between User records and other record types: Domain, Email, and Quota.

As such, this is the only router where the factories are used to create or update records with multiple associations. It seems a good opportunity for another example.

chapps.rest.routers.users.api = <fastapi.routing.APIRouter object>

The API router for User record maintenance

This router is once again full of calls to the factories in the common module. The User model is the most connected to other things, however, and so seems like a good spot for examples related to associations.

Creating Users

When creating a User record, it seems likely that the caller might like to automatically associate the new User with an existing Quota record, and at least one Domain record, perhaps even an Email record. The factory will provide a coroutine which will optionally accept ID lists for these associations. That is to say, the coroutine will treat them as optional arguments and do nothing if they are not provided.

All of the logic and magic behind making this go smoothly is hidden within the JoinAssoc class. We simply provide a list of these associations to the factory and it handles the rest:

api.post(
    "/",
    status_code=201,
    response_model=UserResp,
    responses={status.HTTP_409_CONFLICT: {"description": "Unique key error."}},
)(create_item(User, response_model=UserResp, assoc=user_join_assoc))

In the above example, the FastAPI code for specifying details about the POST route takes up more space than the factory call to obtain the actual User creation coroutine.

The definition of user_join_assoc may be found below. It is a list containing references to all three JoinAssoc instances, relating to a Quota and lists of Domain and Email records.

Handling Associations

Sometimes there is a need to remove a specific association from a list, or add one or a handful. It would be helpful if it were not necessary to obtain or manufacture a list of IDs in order to use a replacement-type edit such as the basic model update route. The User model has a number of different associations to manage, so here is an example of adding domains:

api.put("/{item_id}/domains/", response_model=TextResp)(
    adjust_associations(
        User, assoc=[user_domains_assoc], assoc_op=AssocOperation.add
    )
)

I chose to use PUT because it is associated with partial updates. Within the API router wrapper, we use a call to the adjust_associations() route factory, which returns a coroutine which will take a User ID and a list of Domain IDs as arguments. When invoked via the API, that coroutine will ensure that all the existing Domain records listed are associated to the User. IntegrityError is ignored during the process, so any attempts to add an existing association or to add a nonexistent Domain will not raise errors – all existing Domain records identified by ID will be associated to the User, and other associations to that User will be preserved.

Parameters
Return type

None

async chapps.rest.routers.users.map_usernames_to_quota_ids(user_ids)[source]

Map User identfiers onto Quota ids

If a display requires a large matrix of users with their quota settings, this routine may be helpful. The Quota records may be fetched before or after, just once for each kind of quota, and then cross-referenced much more efficiently than requesting each separately.

The response contains a list of JSON objects (hashes or dictionaries), with the keys user_name and quota_id. Only existing users are returned, possibly with quota_id set to None if the user has no quota policy assigned. They are sorted by the user’s ID value.

Parameters

user_ids (List[int]) –

async chapps.rest.routers.users.map_usernames_to_domain_ids(user_ids)[source]

Map User identfiers onto Domain id lists

If a display requires a large matrix of users with their domain authorizations, this routine may be helpful. The Domain records may be fetched before or after, just once for each domain, and then cross-referenced much more efficiently than requesting each separately.

The response contains a list of JSON objects (hashes or dictionaries), with the keys user_name and domain_ids. Only existing users are returned, possibly with domain_ids set to None if the user has no domain authorizations. They are sorted by the user’s ID value.

Parameters

user_ids (List[int]) –

async chapps.rest.routers.users.map_usernames_to_email_ids(user_ids)[source]

Map User identfiers onto Email id lists

If a display requires a large matrix of users with their email authorizations, this routine may be helpful. The Email records may be fetched before or after, just once for each email, and then cross-referenced much more efficiently than requesting each separately.

The response contains a list of JSON objects (hashes or dictionaries), with the keys user_name and email_ids. Only existing users are returned, possibly with email_ids set to None if the user has no email authorizations. They are sorted by the user’s ID value.

Parameters

user_ids (List[int]) –