Test Failed
Pull Request — master (#375)
by Vinicius
05:01
created

kytos.core.rest_api   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 130
Duplicated Lines 33.85 %

Importance

Changes 0
Metric Value
eloc 80
dl 44
loc 130
rs 10
c 0
b 0
f 0
wmc 17

6 Functions

Rating   Name   Duplication   Size   Complexity  
A get_body() 0 4 1
A get_json_or_400() 0 6 2
A content_type_json_or_415() 0 8 2
A get_json() 0 6 1
A _json_serializer() 0 4 2
A aget_json_or_400() 0 6 2

5 Methods

Rating   Name   Duplication   Size   Complexity  
A StarletteOpenAPIRequest.body() 6 6 2
A JSONResponse.render() 0 9 1
A AStarletteOpenAPIRequest.__init__() 12 12 1
A AStarletteOpenAPIRequest.body() 6 6 2
A StarletteOpenAPIRequest.__init__() 12 12 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
"""Rest API utilities module."""
2
# pylint: disable=self-assigning-variable
3
import json
4
from datetime import datetime
5
from typing import Any, Optional
6
7
from asgiref.sync import async_to_sync
8
from openapi_core.contrib.starlette import \
9
    StarletteOpenAPIRequest as _StarletteOpenAPIRequest
10
from openapi_core.validation.request.datatypes import RequestParameters
11
from starlette.exceptions import HTTPException
12
from starlette.requests import Request
13
from starlette.responses import JSONResponse as StarletteJSONResponse
14
from starlette.responses import Response
15
16
Request = Request
17
Response = Response
18
HTTPException = HTTPException
19
20
21
def _json_serializer(obj):
22
    if isinstance(obj, datetime):
23
        return obj.isoformat()
24
    raise TypeError(f"Type {type(obj)} not serializable")
25
26
27
def get_body(request: Request) -> bytes:
28
    """Try to get request.body form a sync @rest route."""
29
    body = async_to_sync(request.body)
30
    return body()
31
32
33
def get_json(request: Request) -> Any:
34
    """Try to get request.json from a sync @rest route.
35
    It might raise json.decoder.JSONDecodeError.
36
    """
37
    json_body = async_to_sync(request.json)
38
    return json_body()
39
40
41
def get_json_or_400(request: Request) -> Any:
42
    """Try to get request.json from a sync @rest route or HTTPException 400."""
43
    try:
44
        return get_json(request)
45
    except (json.decoder.JSONDecodeError, TypeError) as exc:
46
        raise HTTPException(400, detail=f"Invalid json: {str(exc)}")
47
48
49
async def aget_json_or_400(request: Request) -> Any:
50
    """Try to get request.json from async @rest route or HTTPException 400."""
51
    try:
52
        return await request.json()
53
    except (json.decoder.JSONDecodeError, TypeError) as exc:
54
        raise HTTPException(400, detail=f"Invalid json: {str(exc)}")
55
56
57
def content_type_json_or_415(request: Request) -> Optional[str]:
58
    """Ensures request Content-Type is application/json or raises 415."""
59
    content_type = request.headers.get("Content-Type")
60
    if content_type != "application/json":
61
        err = "Expected Content-Type: application/json, " \
62
              f"got: {content_type}"
63
        raise HTTPException(415, detail=err)
64
    return content_type
65
66
67
class JSONResponse(StarletteJSONResponse):
68
    """JSONResponse with custom default serializer that supports datetime."""
69
    media_type = "application/json"
70
71
    def render(self, content) -> bytes:
72
        return json.dumps(
73
            content,
74
            ensure_ascii=False,
75
            allow_nan=False,
76
            indent=None,
77
            separators=(",", ":"),
78
            default=_json_serializer,
79
        ).encode("utf-8")
80
81
82
# pylint: disable=super-init-not-called
83 View Code Duplication
class AStarletteOpenAPIRequest(_StarletteOpenAPIRequest):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
84
    """Async StarletteOpenAPIRequest."""
85
86
    def __init__(self, request: Request, body: bytes) -> None:
87
        """Constructor of AsycnStarletteOpenAPIRequest.
88
89
        This constructor doesn't call super().__init__() to keep it async
90
        """
91
        self.request = request
92
        self.parameters = RequestParameters(
93
            query=self.request.query_params,
94
            header=self.request.headers,
95
            cookie=self.request.cookies,
96
        )
97
        self._body = body
98
99
    @property
100
    def body(self) -> Optional[str]:
101
        body = self._body
102
        if body is None:
103
            return None
104
        return body.decode("utf-8")
105
106
107
# pylint: disable=super-init-not-called
108 View Code Duplication
class StarletteOpenAPIRequest(_StarletteOpenAPIRequest):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
109
    """Sync StarletteOpenAPIRequest."""
110
111
    def __init__(self, request: Request, body: bytes) -> None:
112
        """Constructor of AsycnStarletteOpenAPIRequest.
113
114
        This constructor doesn't call super().__init__() to keep it async
115
        """
116
        self.request = request
117
        self.parameters = RequestParameters(
118
            query=self.request.query_params,
119
            header=self.request.headers,
120
            cookie=self.request.cookies,
121
        )
122
        self._body = body
123
124
    @property
125
    def body(self) -> Optional[str]:
126
        body = self._body
127
        if body is None:
128
            return None
129
        return body.decode("utf-8")
130