api
base
ApiData
Base class to describe input/output data format on Endpoints as APIs.
Subclasses of this class can be used an argument of data_format
decorator for callbacks on Endpoints. If input/output parameters of
the data_format
are specified, the decorator validates if raw data,
typically a bytes
object, has expected data format.
Subclasses of this class can define their own data formats in __init__
methods. Developers should implement __init__
methods of the subclasses
such that each objects has data with expected formats by implementors.
Note
This class is an abstract class. So, don't initilize it and specify it
as an argument of the data_format
decorator.
Subclasses of this class should validate if raw data given to
__init__
methods has expected formats. If the validation failes,
the classes MUST raise ValidataionFailedError
to announce the
failure to the data_format
decorator.
__validate__(raw, content_type)
classmethod
special
Parameters:
Name | Type | Description | Default |
---|---|---|---|
raw |
bytes |
Raw data to be validated. |
required |
content_type |
ContentType |
Values of |
required |
Source code in bamboo/api/base.py
@classmethod
@abstractmethod
def __validate__(cls, raw: bytes, content_type: ContentType) -> ApiData:
"""
Args:
raw : Raw data to be validated.
content_type : Values of `Content-Type` header.
"""
pass
ApiValidationFailedError
Raised if type validation of data failes.
BinaryApiData
API data with no format.
This class can be used to describe raw data with no data format. So, any received data from clients is acceptable on the class.
Examples:
class MockEndpoint(Endpoint):
@data_format(input=BinaryApiData, output=None)
def do_GET(self, rec_body: BinaryApiData) -> None:
# get raw data of request body
raw_data = rec_body.raw
raw: bytes
property
readonly
Raw data of input binary.
__init__(self, data)
special
Parameters:
Name | Type | Description | Default |
---|---|---|---|
data |
bytes |
Binary data. |
required |
Source code in bamboo/api/base.py
def __init__(self, data: bytes) -> None:
"""
Args:
data: Binary data.
"""
self._data = data
__validate__(raw, content_type)
classmethod
special
Parameters:
Name | Type | Description | Default |
---|---|---|---|
raw |
bytes |
Raw data to be validated. |
required |
content_type |
ContentType |
Values of |
required |
Returns:
Type | Description |
---|---|
BinaryApiData |
The BinaryApiData object validated succcessfully. |
Note
In objects of this class, content_type
is not used even if any
content_type
is specified.
Source code in bamboo/api/base.py
@classmethod
def __validate__(cls, raw: bytes, content_type: ContentType) -> BinaryApiData:
"""
Args:
raw: Raw data to be validated.
content_type: Values of `Content-Type` header.
Returns:
The BinaryApiData object validated succcessfully.
Note:
In objects of this class, `content_type` is not used even if any
`content_type` is specified.
"""
if not isinstance(raw, bytes):
raise ApiValidationFailedError(f"'raw' must be a 'bytes'.")
return cls(raw)
form
FormApiData
API data with x-www-form-urlencoded
This class can be used to describe data with x-www-form-urlencoded
format. This class should be inheritted and its several
class-attributes should be defined in the subclass. Developer must
define type hints of the class-attributes, which are used to validate if
raw data has format the type hints define. In this class, the type hints
must be only str
. Otherwise, TypeError
will be raised.
Examples:
-
Defining subclass of this class
class UserCredentials(FormApiData): user_id: str password: str
-
Validating received data
class MockEndpoint(Endpoint): @data_format(input=UserCredentials, output=None) def do_GET(self, rec_body: UserCredentials) -> None: # Do something... # Example authenticate(rec_body.user_id, rec_body.password)
__init__(self, **data)
special
Parameters:
Name | Type | Description | Default |
---|---|---|---|
raw |
|
Raw data to be validated. |
required |
content_type |
|
Values of |
required |
Source code in bamboo/api/form.py
def __init__(self, **data: str) -> None:
"""
Args:
raw: Raw data to be validated.
content_type: Values of `Content-Type` header.
"""
mapped = _build_dict(self.__class__, **data)
self.__dict__.update(mapped.__dict__)
__init_subclass__()
classmethod
special
This method is called when a class is subclassed.
The default implementation does nothing. It may be overridden to extend subclasses.
Source code in bamboo/api/form.py
def __init_subclass__(cls) -> None:
_has_valid_annotations(cls)
__validate__(raw, content_type)
classmethod
special
Parameters:
Name | Type | Description | Default |
---|---|---|---|
raw |
bytes |
Raw data to be validated. |
required |
content_type |
ContentType |
Values of |
required |
Returns:
Type | Description |
---|---|
FormApiData |
The FormApiData object validated successfully. |
Source code in bamboo/api/form.py
@classmethod
def __validate__(
cls,
raw: bytes,
content_type: ContentType,
) -> FormApiData:
"""
Args:
raw: Raw data to be validated.
content_type: Values of `Content-Type` header.
Returns:
The FormApiData object validated successfully.
"""
if content_type.charset is None:
content_type.charset = "UTF-8"
if not cls.verify_content_type(content_type):
raise ApiValidationFailedError(
"Media type of 'Content-Type' header is not "
f"{MediaTypes.x_www_form_urlencoded}, "
f"but {content_type.media_type}."
)
try:
raw = raw.decode(encoding=content_type.charset)
except UnicodeDecodeError as e:
raise ApiValidationFailedError(
"Decoding raw data failed. The encoding was expected "
f"{content_type.charset}, but not corresponded."
) from e
return _build_form_api(cls, raw)
json
JsonApiData
API data with JSON format.
This class can be used to describe data with JSON format. This class should be inheritted and its several class-attributes should be defined in the subclass. Developers must define type hints of the class-attribtues, which are used to validate if raw data has format the type hints define.
Examples:
-
Defining subclass of this class
class User(JsonApiData): name: str email: str age: int class MockApiData(JsonApiData): users: List[User]
-
Validating received data
class MockEndpoint(Endpoint): @data_format(input=MockApiData, output=None) def do_GET(self, rec_body: MockApiData) -> None: # Do something... # Example for user in rec_body.users: print(f"user name : {user.name}")
__init_subclass__()
classmethod
special
This method is called when a class is subclassed.
The default implementation does nothing. It may be overridden to extend subclasses.
Source code in bamboo/api/json.py
def __init_subclass__(cls) -> None:
_has_valid_annotations(cls)
__validate__(raw, content_type)
classmethod
special
Parameters:
Name | Type | Description | Default |
---|---|---|---|
raw |
bytes |
Raw data to be validated. |
required |
content_type |
ContentType |
Values of |
required |
Source code in bamboo/api/json.py
@classmethod
def __validate__(
cls,
raw: bytes,
content_type: ContentType
) -> JsonApiData:
"""
Args:
raw: Raw data to be validated.
content_type: Values of `Content-Type` header.
"""
if content_type.charset is None:
content_type.charset = "UTF-8"
if not cls.verify_content_type(content_type):
raise ApiValidationFailedError(
"Media type of 'Content-Type' header was not "
f"{MediaTypes.json}, but {content_type.media_type}."
)
try:
raw = raw.decode(encoding=content_type.charset)
data = json.loads(raw)
except UnicodeDecodeError:
raise ApiValidationFailedError(
"Decoding raw data failed. The encoding was expected "
f"{content_type.charset}, but not corresponded."
)
except json.decoder.JSONDecodeError:
raise ApiValidationFailedError(
"Decoding raw data failed."
"The raw data had invalid JSON format."
)
return cls(**data)