bedrock.endpoints
This module contains the Endpoint class, which is the base class for all endpoints and any decorators that are applied to endpoints or their methods.
Example:
A simple endpoint at the root path
Let's make an endpoint for CRUDing a Country entity.
We'll call it Countries and it will be accessible at /countries/.
from bedrock.endpoints.endpoint import Endpoint
from bedrock.endpoints.decorators import protected, with_query_param, filter_on_columns
from model.country import Country
class Countries(Endpoint):
def __init__(self):
super().__init__("/countries/", related_model=Country)
@with_query_param("sort_order", str)
@with_query_param("sort_column", str)
@with_query_param("offset", int)
@with_query_param("limit", int)
@filter_on_columns()
def get_global(self, event, sort_order=None, sort_column=None, offset=None, limit=None, filters={}):
return self.get_global_generic(event, Country, filters, order_by=sort_column, order=sort_order, offset=offset, limit=limit)
def get_single(self, event):
return self.get_single_generic(event, Country)
@protected(scopes=["global:admin", "country:admin", "country:write"])
def post_global(self, event):
return self.post_global_generic(event, Country)
@protected(scopes=["global:admin", "country:admin", "country:write"])
def put_single(self, event):
# Let's make sure the user doesn't try to change the uuid
body = json.loads(event['body'])
body["uuid"] = event["pathParameters"][self.param_key]
return self.put_single_generic(event, Country, altered_body=body)
@protected(scopes=["global:admin", "country:admin", "country:delete"])
def delete_single(self, event):
return self.delete_single_generic(event, Country, event["pathParameters"][self.param_key])
Now let's break down what's happening above:
Countriesis a subclass ofEndpoint, which is the base class for all endpoints.Countriesconstructor defines how the endpoint is accessed. In this case, it's at/countries/and it's related to theCountrymodel.- The
related_modelparameter is used by the generic helpers but also to help generate the OpenAPI spec.
- The
get_globalis a method that will be called when aGETrequest is made to/countries/.- It's decorated with a few
@with_query_paramand@filter_on_columnsto allow for sorting, pagination and filtering. - It calls the generic
get_global_genericmethod, which will fetch allCountryentities and apply the filters, sorting and pagination.
- It's decorated with a few
get_singleis a method that will be called when aGETrequest is made to/countries/{uuid}.- It calls the generic
get_single_genericmethod, which will fetch a singleCountryentity.
- It calls the generic
post_globalis a method that will be called when aPOSTrequest is made to/countries/.- It's decorated by the
@protecteddecorator, which will ensure that the user has sufficient access. - It calls the generic
post_global_genericmethod, which will create a newCountryentity.
- It's decorated by the
put_singleis a method that will be called when aPUTrequest is made to/countries/{uuid}.- It's decorated by the
@protecteddecorator, which will ensure that the user has sufficient access. - It calls the generic
put_single_genericmethod, which will update a singleCountryentity. - It also has some logic to ensure that the
uuidin the body matches the one in the URL.
- It's decorated by the
delete_singleis a method that will be called when aDELETErequest is made to/countries/{uuid}.- It's decorated by the
@protecteddecorator, which will ensure that the user has sufficient access. - It calls the generic
delete_single_genericmethod, which will delete a singleCountryentity.
- It's decorated by the
A simple endpoint at a nested path
Let's make an endpoint for CRUDing a Region entity.
We'll call it Regions and it will be accessible at /countries/{countryUuid}/regions/.
from bedrock.endpoints.endpoint import Endpoint
from bedrock.endpoints.decorators import protected, with_query_param, filter_on_columns
from model.region import Region
class Regions(Endpoint):
def __init__(self):
super().__init__("/regions/", "/countries/{countryUuid}", related_model=Region)
@with_query_param("sort_order", str)
@with_query_param("sort_column", str)
@with_query_param("offset", int)
@with_query_param("limit", int)
@filter_on_columns()
def get_global(self, event, countryUuid, sort_order=None, sort_column=None, offset=None, limit=None, filters={}):
# Make sure we have a countryUuid, this should be handled by API Gateway but just in case
try:
country_uuid = event["pathParameters"]["countryUuid"]
except Exception as e:
raise NotFoundException("country uuid not provided in url") from e
# Then fetch all Regions and automatically add a filter on country_uuid
return self.get_global_generic(event, Region, {
**filters,
add_default_equality_operator("country_uuid"): countryUuid
}, order_by=sort_column, order=sort_order, offset=offset, limit=limit)
def get_single(self, event, countryUuid):
# Make sure we have a countryUuid, this should be handled by API Gateway but just in case
try:
country_uuid = event["pathParameters"]["countryUuid"]
except Exception as e:
raise NotFoundException("country uuid not provided in url") from e
# Then fetch the single Region and automatically add a filter on country_uuid (so we get nothing if the countryUuid is wrong)
return self.get_single_generic(event, Region, {
add_default_equality_operator("country_uuid"): countryUuid
})
@protected(scopes=["global:admin", "region:admin", "region:write"])
def post_global(self, event):
# Lets make it easier for the user and automatically add the countryUuid to the body (while also replacing any value it could have)
body = json.loads(event['body'])
body["countryUuid"] = event["pathParameters"]["countryUuid"]
# Then we can just call the generic post_global with the altered body
return self.post_global_generic(event, Region, altered_body=body)
@protected(scopes=["global:admin", "region:admin", "region:write"])
def put_single(self, event):
# Lets make sure the user doesn't try to change the countryUuid or this region's uuid
body = json.loads(event['body'])
body["countryUuid"] = event["pathParameters"]["countryUuid"]
body["uuid"] = event["pathParameters"][self.param_key]
# Then we can just call the generic put_single with the altered body (and the countryUuid filter)
return self.put_single_generic(event, Region, altered_body=body, belongs_to={
add_default_equality_operator("country_uuid"): countryUuid
})
@protected(scopes=["global:admin", "region:admin", "region:delete"])
def delete_single(self, event):
return self.delete_single_generic(event, Region, event["pathParameters"][self.param_key], belongs_to={
add_default_equality_operator("country_uuid"): countryUuid
})
Again, let's break down what's happening above:
Regionsis a subclass ofEndpoint, which is the base class for all endpoints.Regionsconstructor defines how the endpoint is accessed. In this case, it's at/countries/{countryUuid}/regions/and it's related to theRegionmodel.get_globalis a method that will be called when aGETrequest is made to/countries/{countryUuid}/regions/.- It's decorated with a few
@with_query_paramand@filter_on_columnsto allow for sorting, pagination and filtering. - It calls the generic
get_global_genericmethod, which will fetch allRegionentities and apply the filters, sorting and pagination. - It also adds a filter on
country_uuidto ensure that we only getRegionentities that belong to theCountryentity with the providedcountryUuid.
- It's decorated with a few
get_singleis a method that will be called when aGETrequest is made to/countries/{countryUuid}/regions/{uuid}.- It calls the generic
get_single_genericmethod, which will fetch a singleRegionentity. - It also adds a filter on
country_uuidto ensure that we only get aRegionentity that belongs to theCountryentity with the providedcountryUuid.
- It calls the generic
post_globalis a method that will be called when aPOSTrequest is made to/countries/{countryUuid}/regions/.- It's decorated by the
@protecteddecorator, which will ensure that the user has sufficient access. - It calls the generic
post_global_genericmethod, which will create a newRegionentity. - It also adds the
countryUuidto the body to ensure that the newRegionentity belongs to theCountryentity with the providedcountryUuid.
- It's decorated by the
put_singleis a method that will be called when aPUTrequest is made to/countries/{countryUuid}/regions/{uuid}.- It's decorated by the
@protecteddecorator, which will ensure that the user has sufficient access. - It calls the generic
put_single_genericmethod, which will update a singleRegionentity. - It also adds the
countryUuidto the body to ensure that the updatedRegionentity belongs to theCountryentity with the providedcountryUuid. - It also has some logic to ensure that the
uuidin the body matches the one in the URL.
- It's decorated by the
delete_singleis a method that will be called when aDELETErequest is made to/countries/{countryUuid}/regions/{uuid}.- It's decorated by the
@protecteddecorator, which will ensure that the user has sufficient access. - It calls the generic
delete_single_genericmethod, which will delete a singleRegionentity. - It also adds a filter on
country_uuidto ensure that we only delete aRegionentity that belongs to theCountryentity with the providedcountryUuid.
- It's decorated by the
1""" 2This module contains the [`Endpoint`](endpoints/endpoint.html#Endpoint) class, which is the base class for all endpoints and any [decorators](endpoints/decorators.html) that are applied to endpoints or their methods. 3 4# Example: 5 6## A simple endpoint at the root path 7Let's make an endpoint for CRUDing a `Country` entity. 8 9We'll call it `Countries` and it will be accessible at `/countries/`. 10 11```python 12from bedrock.endpoints.endpoint import Endpoint 13from bedrock.endpoints.decorators import protected, with_query_param, filter_on_columns 14from model.country import Country 15 16class Countries(Endpoint): 17 def __init__(self): 18 super().__init__("/countries/", related_model=Country) 19 20 @with_query_param("sort_order", str) 21 @with_query_param("sort_column", str) 22 @with_query_param("offset", int) 23 @with_query_param("limit", int) 24 @filter_on_columns() 25 def get_global(self, event, sort_order=None, sort_column=None, offset=None, limit=None, filters={}): 26 return self.get_global_generic(event, Country, filters, order_by=sort_column, order=sort_order, offset=offset, limit=limit) 27 28 def get_single(self, event): 29 return self.get_single_generic(event, Country) 30 31 @protected(scopes=["global:admin", "country:admin", "country:write"]) 32 def post_global(self, event): 33 return self.post_global_generic(event, Country) 34 35 @protected(scopes=["global:admin", "country:admin", "country:write"]) 36 def put_single(self, event): 37 # Let's make sure the user doesn't try to change the uuid 38 body = json.loads(event['body']) 39 body["uuid"] = event["pathParameters"][self.param_key] 40 return self.put_single_generic(event, Country, altered_body=body) 41 42 @protected(scopes=["global:admin", "country:admin", "country:delete"]) 43 def delete_single(self, event): 44 return self.delete_single_generic(event, Country, event["pathParameters"][self.param_key]) 45``` 46 47Now let's break down what's happening above: 48* `Countries` is a subclass of `Endpoint`, which is the base class for all endpoints. 49* `Countries` constructor defines how the endpoint is accessed. In this case, it's at `/countries/` and it's related to the `Country` model. 50 * The `related_model` parameter is used by the generic helpers but also to help generate the OpenAPI spec. 51* `get_global` is a method that will be called when a `GET` request is made to `/countries/`. 52 * It's decorated with a few [`@with_query_param`](endpoints/decorators/with_query_param.html) and [`@filter_on_columns`](endpoints/decorators/filter_on_columns.html) to allow for sorting, pagination and filtering. 53 * It calls the generic `get_global_generic` method, which will fetch all `Country` entities and apply the filters, sorting and pagination. 54* `get_single` is a method that will be called when a `GET` request is made to `/countries/{uuid}`. 55 * It calls the generic `get_single_generic` method, which will fetch a single `Country` entity. 56* `post_global` is a method that will be called when a `POST` request is made to `/countries/`. 57 * It's decorated by the [`@protected`](endpoints/decorators/protected.html) decorator, which will ensure that the user has sufficient access. 58 * It calls the generic `post_global_generic` method, which will create a new `Country` entity. 59* `put_single` is a method that will be called when a `PUT` request is made to `/countries/{uuid}`. 60 * It's decorated by the [`@protected`](endpoints/decorators/protected.html) decorator, which will ensure that the user has sufficient access. 61 * It calls the generic `put_single_generic` method, which will update a single `Country` entity. 62 * It also has some logic to ensure that the `uuid` in the body matches the one in the URL. 63* `delete_single` is a method that will be called when a `DELETE` request is made to `/countries/{uuid}`. 64 * It's decorated by the [`@protected`](endpoints/decorators/protected.html) decorator, which will ensure that the user has sufficient access. 65 * It calls the generic `delete_single_generic` method, which will delete a single `Country` entity. 66 67## A simple endpoint at a nested path 68 69Let's make an endpoint for CRUDing a `Region` entity. 70 71We'll call it `Regions` and it will be accessible at `/countries/{countryUuid}/regions/`. 72 73```python 74from bedrock.endpoints.endpoint import Endpoint 75from bedrock.endpoints.decorators import protected, with_query_param, filter_on_columns 76from model.region import Region 77 78class Regions(Endpoint): 79 def __init__(self): 80 super().__init__("/regions/", "/countries/{countryUuid}", related_model=Region) 81 82 @with_query_param("sort_order", str) 83 @with_query_param("sort_column", str) 84 @with_query_param("offset", int) 85 @with_query_param("limit", int) 86 @filter_on_columns() 87 def get_global(self, event, countryUuid, sort_order=None, sort_column=None, offset=None, limit=None, filters={}): 88 # Make sure we have a countryUuid, this should be handled by API Gateway but just in case 89 try: 90 country_uuid = event["pathParameters"]["countryUuid"] 91 except Exception as e: 92 raise NotFoundException("country uuid not provided in url") from e 93 94 # Then fetch all Regions and automatically add a filter on country_uuid 95 return self.get_global_generic(event, Region, { 96 **filters, 97 add_default_equality_operator("country_uuid"): countryUuid 98 }, order_by=sort_column, order=sort_order, offset=offset, limit=limit) 99 100 def get_single(self, event, countryUuid): 101 # Make sure we have a countryUuid, this should be handled by API Gateway but just in case 102 try: 103 country_uuid = event["pathParameters"]["countryUuid"] 104 except Exception as e: 105 raise NotFoundException("country uuid not provided in url") from e 106 # Then fetch the single Region and automatically add a filter on country_uuid (so we get nothing if the countryUuid is wrong) 107 return self.get_single_generic(event, Region, { 108 add_default_equality_operator("country_uuid"): countryUuid 109 }) 110 111 @protected(scopes=["global:admin", "region:admin", "region:write"]) 112 def post_global(self, event): 113 # Lets make it easier for the user and automatically add the countryUuid to the body (while also replacing any value it could have) 114 body = json.loads(event['body']) 115 body["countryUuid"] = event["pathParameters"]["countryUuid"] 116 # Then we can just call the generic post_global with the altered body 117 return self.post_global_generic(event, Region, altered_body=body) 118 119 @protected(scopes=["global:admin", "region:admin", "region:write"]) 120 def put_single(self, event): 121 # Lets make sure the user doesn't try to change the countryUuid or this region's uuid 122 body = json.loads(event['body']) 123 body["countryUuid"] = event["pathParameters"]["countryUuid"] 124 body["uuid"] = event["pathParameters"][self.param_key] 125 # Then we can just call the generic put_single with the altered body (and the countryUuid filter) 126 return self.put_single_generic(event, Region, altered_body=body, belongs_to={ 127 add_default_equality_operator("country_uuid"): countryUuid 128 }) 129 130 @protected(scopes=["global:admin", "region:admin", "region:delete"]) 131 def delete_single(self, event): 132 return self.delete_single_generic(event, Region, event["pathParameters"][self.param_key], belongs_to={ 133 add_default_equality_operator("country_uuid"): countryUuid 134 }) 135``` 136 137Again, let's break down what's happening above: 138* `Regions` is a subclass of `Endpoint`, which is the base class for all endpoints. 139* `Regions` constructor defines how the endpoint is accessed. In this case, it's at `/countries/{countryUuid}/regions/` and it's related to the `Region` model. 140* `get_global` is a method that will be called when a `GET` request is made to `/countries/{countryUuid}/regions/`. 141 * It's decorated with a few [`@with_query_param`](endpoints/decorators/with_query_param.html) and [`@filter_on_columns`](endpoints/decorators/filter_on_columns.html) to allow for sorting, pagination and filtering. 142 * It calls the generic `get_global_generic` method, which will fetch all `Region` entities and apply the filters, sorting and pagination. 143 * It also adds a filter on `country_uuid` to ensure that we only get `Region` entities that belong to the `Country` entity with the provided `countryUuid`. 144* `get_single` is a method that will be called when a `GET` request is made to `/countries/{countryUuid}/regions/{uuid}`. 145 * It calls the generic `get_single_generic` method, which will fetch a single `Region` entity. 146 * It also adds a filter on `country_uuid` to ensure that we only get a `Region` entity that belongs to the `Country` entity with the provided `countryUuid`. 147* `post_global` is a method that will be called when a `POST` request is made to `/countries/{countryUuid}/regions/`. 148 * It's decorated by the [`@protected`](endpoints/decorators/protected.html) decorator, which will ensure that the user has sufficient access. 149 * It calls the generic `post_global_generic` method, which will create a new `Region` entity. 150 * It also adds the `countryUuid` to the body to ensure that the new `Region` entity belongs to the `Country` entity with the provided `countryUuid`. 151* `put_single` is a method that will be called when a `PUT` request is made to `/countries/{countryUuid}/regions/{uuid}`. 152 * It's decorated by the [`@protected`](endpoints/decorators/protected.html) decorator, which will ensure that the user has sufficient access. 153 * It calls the generic `put_single_generic` method, which will update a single `Region` entity. 154 * It also adds the `countryUuid` to the body to ensure that the updated `Region` entity belongs to the `Country` entity with the provided `countryUuid`. 155 * It also has some logic to ensure that the `uuid` in the body matches the one in the URL. 156* `delete_single` is a method that will be called when a `DELETE` request is made to `/countries/{countryUuid}/regions/{uuid}`. 157 * It's decorated by the [`@protected`](endpoints/decorators/protected.html) decorator, which will ensure that the user has sufficient access. 158 * It calls the generic `delete_single_generic` method, which will delete a single `Region` entity. 159 * It also adds a filter on `country_uuid` to ensure that we only delete a `Region` entity that belongs to the `Country` entity with the provided `countryUuid`. 160 161"""