bedrock.endpoints.decorators.verify_body_key_value_pairs

 1import functools  # pragma: unit
 2import json  # pragma: unit
 3
 4from bedrock.exceptions import BadRequestException, BedrockException  # pragma: unit
 5
 6
 7def verify_body_key_value_pairs(expected: dict, fail_on_missing: bool = True):  # pragma: unit
 8    """
 9    Verify that the body of the request contains the expected key-value pairs.
10
11    :param expected: A dictionary of expected key-value pairs. If the value is a list, the verification passes when the body value is contained in the list. Values can be `{{placeholders}}` for arguments in the function's kwargs.
12    :param fail_on_missing: Whether to raise an exception if the body is missing a key. Defaults to `True`.
13
14    Example usage: for a `POST` to `/countries/` where the body must include a key `country_code` with the values of
15    either `"GB"` or `"PT"`:
16    ```python
17    class Countries(Endpoint):
18        # ...
19        @verify_body_key_value_pairs({"country_code": ["GB", "PT"]})
20        def post_global(self, event):
21            # ...
22    ```
23
24    Example usage: for a `POST` to `/countries/` where the body must include the same value as a protected entity's field:
25    ```python
26    class Countries(Endpoint):
27        # ...
28        @protected("country_code")
29        @verify_body_key_value_pairs({"country_code": "{{country_code}}"})
30        def post_global(self, event, country_code):
31            # ...
32    ```
33    """
34
35    def decorator(func):
36        @functools.wraps(func)
37        def wrapper(*args, **kwargs):
38            event = args[1]
39            body = json.loads(event["body"])
40            for key, _value in expected.items():
41                if key not in body:
42                    if not fail_on_missing:
43                        continue
44                    else:
45                        raise BadRequestException(f"Missing key {key}")
46                if isinstance(_value, str) and _value.startswith("{{"):
47                    value_placeholder_name = _value.replace("{{", "").replace("}}", "")
48                    try:
49                        value = kwargs[value_placeholder_name]
50                    except KeyError:
51                        raise BedrockException(f"Missing value for placeholder {value_placeholder_name}")
52                else:
53                    value = _value
54                if not _matches(value, body[key]):
55                    raise BadRequestException(f"Invalid value for key - {key}: {value}")
56
57            return func(*args, **kwargs)
58
59        return wrapper
60
61    return decorator
62
63
64def _matches(matches, value_to_match):  # pragma: unit
65    if isinstance(matches, list):
66        return value_to_match in matches
67    return value_to_match == matches
def verify_body_key_value_pairs(expected: dict, fail_on_missing: bool = True):
 8def verify_body_key_value_pairs(expected: dict, fail_on_missing: bool = True):  # pragma: unit
 9    """
10    Verify that the body of the request contains the expected key-value pairs.
11
12    :param expected: A dictionary of expected key-value pairs. If the value is a list, the verification passes when the body value is contained in the list. Values can be `{{placeholders}}` for arguments in the function's kwargs.
13    :param fail_on_missing: Whether to raise an exception if the body is missing a key. Defaults to `True`.
14
15    Example usage: for a `POST` to `/countries/` where the body must include a key `country_code` with the values of
16    either `"GB"` or `"PT"`:
17    ```python
18    class Countries(Endpoint):
19        # ...
20        @verify_body_key_value_pairs({"country_code": ["GB", "PT"]})
21        def post_global(self, event):
22            # ...
23    ```
24
25    Example usage: for a `POST` to `/countries/` where the body must include the same value as a protected entity's field:
26    ```python
27    class Countries(Endpoint):
28        # ...
29        @protected("country_code")
30        @verify_body_key_value_pairs({"country_code": "{{country_code}}"})
31        def post_global(self, event, country_code):
32            # ...
33    ```
34    """
35
36    def decorator(func):
37        @functools.wraps(func)
38        def wrapper(*args, **kwargs):
39            event = args[1]
40            body = json.loads(event["body"])
41            for key, _value in expected.items():
42                if key not in body:
43                    if not fail_on_missing:
44                        continue
45                    else:
46                        raise BadRequestException(f"Missing key {key}")
47                if isinstance(_value, str) and _value.startswith("{{"):
48                    value_placeholder_name = _value.replace("{{", "").replace("}}", "")
49                    try:
50                        value = kwargs[value_placeholder_name]
51                    except KeyError:
52                        raise BedrockException(f"Missing value for placeholder {value_placeholder_name}")
53                else:
54                    value = _value
55                if not _matches(value, body[key]):
56                    raise BadRequestException(f"Invalid value for key - {key}: {value}")
57
58            return func(*args, **kwargs)
59
60        return wrapper
61
62    return decorator

Verify that the body of the request contains the expected key-value pairs.

Parameters
  • expected: A dictionary of expected key-value pairs. If the value is a list, the verification passes when the body value is contained in the list. Values can be {{placeholders}} for arguments in the function's kwargs.
  • fail_on_missing: Whether to raise an exception if the body is missing a key. Defaults to True.

Example usage: for a POST to /countries/ where the body must include a key country_code with the values of either "GB" or "PT":

class Countries(Endpoint):
    # ...
    @verify_body_key_value_pairs({"country_code": ["GB", "PT"]})
    def post_global(self, event):
        # ...

Example usage: for a POST to /countries/ where the body must include the same value as a protected entity's field:

class Countries(Endpoint):
    # ...
    @protected("country_code")
    @verify_body_key_value_pairs({"country_code": "{{country_code}}"})
    def post_global(self, event, country_code):
        # ...