Passed
Pull Request — main (#879)
by Juho
07:12 queued 04:02
created

annif.openapi.validation   A

Complexity

Total Complexity 8

Size/Duplication

Total Lines 60
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 37
dl 0
loc 60
rs 10
c 0
b 0
f 0
wmc 8

4 Methods

Rating   Name   Duplication   Size   Complexity  
A CustomRequestBodyValidator._validate() 0 16 4
A CustomRequestBodyValidator.__init__() 0 2 1
A CustomFormDataValidator._validate_params_strictly() 0 11 2
A CustomFormDataValidator.__init__() 0 2 1
1
"""Custom validator for the Annif API."""
2
3
from __future__ import annotations
4
5
import logging
6
from typing import Any
7
8
from connexion.exceptions import BadRequestProblem, ExtraParameterProblem
9
from connexion.json_schema import format_error_with_path
10
from connexion.validators import FormDataValidator, JSONRequestBodyValidator
11
from jsonschema.exceptions import ValidationError
12
13
logger = logging.getLogger("openapi.validation")
14
15
16
class CustomRequestBodyValidator(JSONRequestBodyValidator):
17
    """Custom request body validator that overrides the default error message for the
18
    'maxItems' validator for the 'documents' property to prevent logging request body
19
    with the contents of all documents."""
20
21
    def __init__(self, *args, **kwargs) -> None:
22
        super().__init__(*args, **kwargs)
23
24
    def _validate(self, body: Any) -> dict | None:
25
        try:
26
            return self._validator.validate(body)
27
        except ValidationError as exception:
28
            if exception.validator == "maxItems" and list(exception.schema_path) == [
29
                "properties",
30
                "documents",
31
                "maxItems",
32
            ]:
33
                exception.message = "too many items"
34
            error_path_msg = format_error_with_path(exception=exception)
35
            logger.error(
36
                f"Validation error: {exception.message}{error_path_msg}",
37
                extra={"validator": "body"},
38
            )
39
            raise BadRequestProblem(detail=f"{exception.message}{error_path_msg}")
40
41
42
class CustomFormDataValidator(FormDataValidator):
43
    """Custom request body validator that allows additional metadata fields starting
44
    with 'metadata_' in the request body while rejecting other fields."""
45
46
    def __init__(self, *args, **kwargs) -> None:
47
        super().__init__(*args, **kwargs)
48
49
    def _validate_params_strictly(self, data: dict) -> None:
50
        form_params = data.keys()
51
        spec_params = self._schema.get("properties", {}).keys()
52
53
        reduced_form_params = [
54
            fp for fp in form_params if not fp.startswith("metadata_")
55
        ]
56
57
        errors = set(reduced_form_params).difference(set(spec_params))
58
        if errors:
59
            raise ExtraParameterProblem(param_type="formData", extra_params=errors)
60