alpha-python 0.6.1__py3-none-any.whl → 0.6.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- alpha/__init__.py +2 -1
- alpha/encoder.py +11 -5
- alpha/factories/response_factory.py +11 -4
- alpha/handlers/templates/python-flask/controller.mustache +13 -9
- alpha/infra/__init__.py +2 -1
- alpha/infra/connectors/ldap_connector.py +25 -2
- alpha/infra/models/filter_operators.py +3 -5
- alpha/interfaces/sql_database.py +0 -2
- alpha/utils/response_object.py +90 -23
- {alpha_python-0.6.1.dist-info → alpha_python-0.6.3.dist-info}/METADATA +1 -1
- {alpha_python-0.6.1.dist-info → alpha_python-0.6.3.dist-info}/RECORD +15 -15
- {alpha_python-0.6.1.dist-info → alpha_python-0.6.3.dist-info}/WHEEL +0 -0
- {alpha_python-0.6.1.dist-info → alpha_python-0.6.3.dist-info}/entry_points.txt +0 -0
- {alpha_python-0.6.1.dist-info → alpha_python-0.6.3.dist-info}/licenses/LICENSE +0 -0
- {alpha_python-0.6.1.dist-info → alpha_python-0.6.3.dist-info}/top_level.txt +0 -0
alpha/__init__.py
CHANGED
|
@@ -20,7 +20,7 @@ from alpha.infra.connectors.oidc_connector import (
|
|
|
20
20
|
KeyCloakOIDCConnector,
|
|
21
21
|
)
|
|
22
22
|
from alpha.infra.connectors.sql_alchemy import SqlAlchemyDatabase
|
|
23
|
-
from alpha.infra.models.filter_operators import And, Or
|
|
23
|
+
from alpha.infra.models.filter_operators import And, Or, FilterOperator
|
|
24
24
|
from alpha.infra.models.json_patch import JsonPatch
|
|
25
25
|
from alpha.infra.models.order_by import OrderBy, Order
|
|
26
26
|
from alpha.infra.models.search_filter import SearchFilter, Operator
|
|
@@ -116,6 +116,7 @@ __all__ = [
|
|
|
116
116
|
"SqlAlchemyDatabase",
|
|
117
117
|
"And",
|
|
118
118
|
"Or",
|
|
119
|
+
"FilterOperator",
|
|
119
120
|
"JsonPatch",
|
|
120
121
|
"OrderBy",
|
|
121
122
|
"Order",
|
alpha/encoder.py
CHANGED
|
@@ -4,6 +4,7 @@ complex types into JSON format.
|
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
6
|
from dataclasses import asdict, is_dataclass
|
|
7
|
+
from attrs import asdict as attrs_asdict
|
|
7
8
|
from datetime import date, datetime, time
|
|
8
9
|
from enum import Enum
|
|
9
10
|
from json import encoder
|
|
@@ -14,8 +15,9 @@ import numpy as np # type: ignore[import-untyped]
|
|
|
14
15
|
import pandas as pd # type: ignore[import-untyped]
|
|
15
16
|
import six # type: ignore[import-untyped]
|
|
16
17
|
|
|
17
|
-
|
|
18
18
|
from alpha.interfaces.openapi_model import OpenAPIModel
|
|
19
|
+
from alpha.utils.is_attrs import is_attrs
|
|
20
|
+
from alpha.utils.is_pydantic import is_pydantic
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
class JSONEncoder(encoder.JSONEncoder):
|
|
@@ -72,11 +74,15 @@ class JSONEncoder(encoder.JSONEncoder):
|
|
|
72
74
|
return o.isoformat()
|
|
73
75
|
if isinstance(o, time):
|
|
74
76
|
return o.isoformat()
|
|
77
|
+
if isinstance(o, type):
|
|
78
|
+
cls = getattr(o, "__name__", None)
|
|
79
|
+
return cls if cls is not None else str(o)
|
|
75
80
|
if is_dataclass(o):
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
return asdict(o) # type: ignore
|
|
82
|
+
if is_attrs(o):
|
|
83
|
+
return attrs_asdict(o)
|
|
84
|
+
if is_pydantic(o):
|
|
85
|
+
return o.model_dump()
|
|
80
86
|
|
|
81
87
|
try:
|
|
82
88
|
return json.JSONEncoder.default(self, o)
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
import json
|
|
4
4
|
from dataclasses import MISSING, is_dataclass
|
|
5
5
|
from enum import Enum
|
|
6
|
-
from typing import Any,
|
|
6
|
+
from typing import Any, Sequence, get_args, get_origin
|
|
7
7
|
|
|
8
8
|
from alpha import exceptions
|
|
9
9
|
from alpha.encoder import JSONEncoder
|
|
10
|
+
from alpha.interfaces.attrs_instance import AttrsInstance
|
|
10
11
|
from alpha.interfaces.dataclass_instance import DataclassInstance
|
|
11
12
|
from alpha.interfaces.openapi_model import OpenAPIModel
|
|
13
|
+
from alpha.interfaces.pydantic_instance import PydanticInstance
|
|
12
14
|
from alpha.utils.is_attrs import is_attrs
|
|
13
15
|
from alpha.utils.is_pydantic import is_pydantic
|
|
14
16
|
|
|
@@ -18,8 +20,13 @@ class ResponseFactory:
|
|
|
18
20
|
|
|
19
21
|
def process(
|
|
20
22
|
self,
|
|
21
|
-
response: DataclassInstance
|
|
22
|
-
|
|
23
|
+
response: DataclassInstance
|
|
24
|
+
| Sequence[DataclassInstance]
|
|
25
|
+
| AttrsInstance
|
|
26
|
+
| Sequence[AttrsInstance]
|
|
27
|
+
| PydanticInstance
|
|
28
|
+
| Sequence[PydanticInstance],
|
|
29
|
+
cls: OpenAPIModel | Sequence[OpenAPIModel],
|
|
23
30
|
) -> object:
|
|
24
31
|
"""Mapping a dataclass instance or a collection of instances to an
|
|
25
32
|
OpenAPI model
|
|
@@ -54,7 +61,7 @@ class ResponseFactory:
|
|
|
54
61
|
|
|
55
62
|
# When the source instance and target class are of an iterable type
|
|
56
63
|
if cls_origin in [list, tuple, set]:
|
|
57
|
-
if isinstance(response,
|
|
64
|
+
if isinstance(response, Sequence):
|
|
58
65
|
arg = get_args(cls)[0]
|
|
59
66
|
return [
|
|
60
67
|
self.process(response=obj, cls=arg) for obj in response
|
|
@@ -12,6 +12,7 @@ from typing import List
|
|
|
12
12
|
from alpha.factories.request_factory import RequestFactory
|
|
13
13
|
from alpha.factories.response_factory import ResponseFactory
|
|
14
14
|
from alpha.encoder import JSONEncoder
|
|
15
|
+
from alpha.providers.models.identity import Identity
|
|
15
16
|
from alpha.utils.logging_level_checker import logging_level_checker
|
|
16
17
|
from alpha.utils.response_object import create_response_object
|
|
17
18
|
from alpha.utils.request_headers import Headers
|
|
@@ -43,9 +44,7 @@ from {{packageName}} import models as api_models
|
|
|
43
44
|
|
|
44
45
|
{{#operations}}
|
|
45
46
|
{{#operation}}
|
|
46
|
-
{{#authMethods}}{{#vendorExtensions.x-alpha-service-name}}@inject{{/vendorExtensions.x-alpha-service-name}}{{/authMethods}}
|
|
47
|
-
{{^authMethods}}{{#vendorExtensions.x-alpha-service-name}}@inject{{/vendorExtensions.x-alpha-service-name}}{{/authMethods}}
|
|
48
|
-
{{#authMethods}}{{^vendorExtensions.x-alpha-service-name}}@inject{{/vendorExtensions.x-alpha-service-name}}{{/authMethods}}
|
|
47
|
+
{{#authMethods}}{{#-first}}@inject{{/-first}}{{/authMethods}}{{^authMethods}}{{#vendorExtensions.x-alpha-service-name}}@inject{{/vendorExtensions.x-alpha-service-name}}{{/authMethods}}
|
|
49
48
|
def {{operationId}}(
|
|
50
49
|
{{#allParams}}{{^isBodyParam}}{{paramName}}{{^required}}=None{{/required}},{{/isBodyParam}}{{/allParams}}
|
|
51
50
|
{{#authMethods}}{{#isBasicBearer}}token_factory=Provide[Container.token_factory],{{/isBasicBearer}}{{/authMethods}}
|
|
@@ -229,7 +228,8 @@ def {{operationId}}(
|
|
|
229
228
|
token_factory.validate(auth_token)
|
|
230
229
|
|
|
231
230
|
# Get auth token payload and verify identity
|
|
232
|
-
|
|
231
|
+
payload = token_factory.get_payload(auth_token)
|
|
232
|
+
identity = Identity.from_dict(payload)
|
|
233
233
|
verify_identity(identity,
|
|
234
234
|
roles=roles,
|
|
235
235
|
groups=groups,
|
|
@@ -309,8 +309,9 @@ def {{operationId}}(
|
|
|
309
309
|
status_code={{code}},
|
|
310
310
|
status_message='{{message}}',
|
|
311
311
|
data=result,
|
|
312
|
-
{{#vendorExtensions.x-content-type}}
|
|
313
|
-
{{#vendorExtensions.x-
|
|
312
|
+
accept_header={{#vendorExtensions.x-content-type}}'{{vendorExtensions.x-content-type}}'{{/vendorExtensions.x-content-type}}{{^vendorExtensions.x-content-type}}connexion.request.headers.get("Accept", None){{/vendorExtensions.x-content-type}},
|
|
313
|
+
supported_accept_headers=[{{#produces}}'{{mediaType}}',{{/produces}}{{^produces}}{{#vendorExtensions.x-preferred-produce}}'{{mediaType}}',{{/vendorExtensions.x-preferred-produce}}{{/produces}}],
|
|
314
|
+
{{#vendorExtensions.x-alpha-cookie-support}}response_format='flask'{{/vendorExtensions.x-alpha-cookie-support}}
|
|
314
315
|
)
|
|
315
316
|
{{/returnType}}
|
|
316
317
|
{{^returnType}}
|
|
@@ -318,19 +319,20 @@ def {{operationId}}(
|
|
|
318
319
|
http_codes=http_codes,
|
|
319
320
|
status_code=204,
|
|
320
321
|
status_message='{{message}}',
|
|
321
|
-
{{#vendorExtensions.x-alpha-cookie-support}}data=result,
|
|
322
|
+
{{#vendorExtensions.x-alpha-cookie-support}}data=result, response_format='flask'{{/vendorExtensions.x-alpha-cookie-support}}
|
|
322
323
|
)
|
|
323
324
|
{{/returnType}}
|
|
324
325
|
{{/vendorExtensions.x-alpha-raw-response}}
|
|
325
326
|
{{/is2xx}}
|
|
326
|
-
|
|
327
327
|
{{#is4xx}}
|
|
328
328
|
{{#vendorExtensions.x-alpha-exception}}
|
|
329
329
|
except {{vendorExtensions.x-alpha-exception}} as exc:
|
|
330
330
|
return response_object_function(
|
|
331
331
|
http_codes=http_codes,
|
|
332
332
|
status_code={{code}},
|
|
333
|
-
status_message=f'{exc}'
|
|
333
|
+
status_message=f'{exc}',
|
|
334
|
+
accept_header={{#vendorExtensions.x-content-type}}'{{vendorExtensions.x-content-type}}'{{/vendorExtensions.x-content-type}}{{^vendorExtensions.x-content-type}}connexion.request.headers.get("Accept", None){{/vendorExtensions.x-content-type}},
|
|
335
|
+
supported_accept_headers=[{{#produces}}'{{mediaType}}',{{/produces}}{{^produces}}{{#vendorExtensions.x-preferred-produce}}'{{mediaType}}',{{/vendorExtensions.x-preferred-produce}}{{/produces}}],
|
|
334
336
|
)
|
|
335
337
|
{{/vendorExtensions.x-alpha-exception}}
|
|
336
338
|
{{/is4xx}}
|
|
@@ -341,6 +343,8 @@ def {{operationId}}(
|
|
|
341
343
|
http_codes=http_codes,
|
|
342
344
|
status_code={{code}},
|
|
343
345
|
status_message=f'{exc}'
|
|
346
|
+
accept_header={{#vendorExtensions.x-content-type}}'{{vendorExtensions.x-content-type}}'{{/vendorExtensions.x-content-type}}{{^vendorExtensions.x-content-type}}connexion.request.headers.get("Accept", None){{/vendorExtensions.x-content-type}},
|
|
347
|
+
supported_accept_headers=[{{#produces}}'{{mediaType}}',{{/produces}}{{^produces}}{{#vendorExtensions.x-preferred-produce}}'{{mediaType}}',{{/vendorExtensions.x-preferred-produce}}{{/produces}}],
|
|
344
348
|
)
|
|
345
349
|
{{/vendorExtensions.x-alpha-exception}}
|
|
346
350
|
{{/is5xx}}
|
alpha/infra/__init__.py
CHANGED
|
@@ -3,7 +3,7 @@ from alpha.infra.connectors.oidc_connector import (
|
|
|
3
3
|
KeyCloakOIDCConnector,
|
|
4
4
|
)
|
|
5
5
|
from alpha.infra.connectors.sql_alchemy import SqlAlchemyDatabase
|
|
6
|
-
from alpha.infra.models.filter_operators import And, Or
|
|
6
|
+
from alpha.infra.models.filter_operators import And, Or, FilterOperator
|
|
7
7
|
from alpha.infra.models.json_patch import JsonPatch
|
|
8
8
|
from alpha.infra.models.order_by import OrderBy, Order
|
|
9
9
|
from alpha.infra.models.search_filter import SearchFilter, Operator
|
|
@@ -22,6 +22,7 @@ __all__ = [
|
|
|
22
22
|
"SqlAlchemyDatabase",
|
|
23
23
|
"And",
|
|
24
24
|
"Or",
|
|
25
|
+
"FilterOperator",
|
|
25
26
|
"JsonPatch",
|
|
26
27
|
"OrderBy",
|
|
27
28
|
"Order",
|
|
@@ -31,6 +31,9 @@ class LDAPConnector:
|
|
|
31
31
|
server_port: int = 636,
|
|
32
32
|
use_tls: bool = True,
|
|
33
33
|
client_strategy: ClientStrategyType = SYNC,
|
|
34
|
+
connect_timeout: float | None = 5.0,
|
|
35
|
+
additional_connector_params: dict[str, Any] | None = None,
|
|
36
|
+
additional_server_params: dict[str, Any] | None = None,
|
|
34
37
|
) -> None:
|
|
35
38
|
"""
|
|
36
39
|
Parameters
|
|
@@ -59,12 +62,29 @@ class LDAPConnector:
|
|
|
59
62
|
- 'MOCK_SYNC': Mock synchronous strategy.
|
|
60
63
|
- 'MOCK_ASYNC': Mock asynchronous strategy.
|
|
61
64
|
- 'ASYNC_STREAM': Asynchronous stream strategy.
|
|
65
|
+
connect_timeout
|
|
66
|
+
Maximum number of seconds to wait while opening the socket.
|
|
67
|
+
additional_connector_params
|
|
68
|
+
Additional parameters to pass to the LDAP connection, by default
|
|
69
|
+
{"receive_timeout": 5}
|
|
70
|
+
additional_server_params
|
|
71
|
+
Additional parameters to pass to the LDAP server, by default None
|
|
62
72
|
"""
|
|
63
73
|
self._server_url = server_url
|
|
64
74
|
self._bind_dn = bind_dn
|
|
65
75
|
self._bind_password = bind_password
|
|
66
76
|
self._client_strategy = client_strategy
|
|
67
|
-
|
|
77
|
+
self._connect_timeout = connect_timeout
|
|
78
|
+
self._additional_connector_params: dict[str, Any] = (
|
|
79
|
+
{"receive_timeout": 5}
|
|
80
|
+
if additional_connector_params is None
|
|
81
|
+
else dict(additional_connector_params)
|
|
82
|
+
)
|
|
83
|
+
self._additional_server_params: dict[str, Any] = (
|
|
84
|
+
{}
|
|
85
|
+
if additional_server_params is None
|
|
86
|
+
else dict(additional_server_params)
|
|
87
|
+
)
|
|
68
88
|
tls = None
|
|
69
89
|
if use_tls:
|
|
70
90
|
tls = Tls(
|
|
@@ -78,6 +98,8 @@ class LDAPConnector:
|
|
|
78
98
|
use_ssl=use_tls,
|
|
79
99
|
tls=tls,
|
|
80
100
|
get_info=ALL,
|
|
101
|
+
connect_timeout=self._connect_timeout,
|
|
102
|
+
**self._additional_server_params,
|
|
81
103
|
)
|
|
82
104
|
self._connection: Connection | None = None
|
|
83
105
|
|
|
@@ -97,8 +119,9 @@ class LDAPConnector:
|
|
|
97
119
|
self._server,
|
|
98
120
|
user=self._bind_dn,
|
|
99
121
|
password=self._bind_password,
|
|
100
|
-
auto_bind=True,
|
|
101
122
|
client_strategy=self._client_strategy, # type: ignore
|
|
123
|
+
auto_bind=True,
|
|
124
|
+
**self._additional_connector_params,
|
|
102
125
|
)
|
|
103
126
|
|
|
104
127
|
def disconnect(self) -> None:
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
- Or
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from typing import Any, Callable, Iterable
|
|
7
|
+
from typing import Any, Callable, Iterable, Self
|
|
8
8
|
|
|
9
9
|
from sqlalchemy.orm import Query
|
|
10
10
|
from sqlalchemy.sql.expression import ColumnElement, and_, or_
|
|
@@ -17,13 +17,11 @@ class FilterOperator:
|
|
|
17
17
|
search query
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
-
def __init__(self, *search_filters: SearchFilter):
|
|
20
|
+
def __init__(self, *search_filters: SearchFilter | Self) -> None:
|
|
21
21
|
"""Instantiate the filter operator by storing the search filter
|
|
22
22
|
objects
|
|
23
23
|
"""
|
|
24
|
-
self.search_filters: Iterable[SearchFilter |
|
|
25
|
-
search_filters
|
|
26
|
-
)
|
|
24
|
+
self.search_filters: Iterable[SearchFilter | Self] = search_filters
|
|
27
25
|
|
|
28
26
|
@property
|
|
29
27
|
def filter_operator(
|
alpha/interfaces/sql_database.py
CHANGED
alpha/utils/response_object.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, Any, Literal, overload
|
|
2
1
|
import json
|
|
3
|
-
from
|
|
4
|
-
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Literal, overload
|
|
3
|
+
|
|
5
4
|
from alpha.utils._http_codes import http_codes_en
|
|
5
|
+
from alpha.utils.cookie import Cookie
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from flask.wrappers import Response
|
|
@@ -15,10 +15,12 @@ def create_response_object(
|
|
|
15
15
|
status_code: int,
|
|
16
16
|
status_message: str,
|
|
17
17
|
data: Any | None,
|
|
18
|
-
|
|
18
|
+
accept_header: str,
|
|
19
|
+
supported_accept_headers: list[str] | None,
|
|
19
20
|
http_codes: dict[int, tuple[str, str]],
|
|
20
21
|
json_encoder: type[json.JSONEncoder] | None,
|
|
21
|
-
|
|
22
|
+
response_format: Literal["dict"],
|
|
23
|
+
**kwargs: Any,
|
|
22
24
|
) -> tuple[dict[str, Any], int]: ...
|
|
23
25
|
|
|
24
26
|
|
|
@@ -27,10 +29,12 @@ def create_response_object(
|
|
|
27
29
|
status_code: int,
|
|
28
30
|
status_message: str,
|
|
29
31
|
data: Any | None,
|
|
30
|
-
|
|
32
|
+
accept_header: str,
|
|
33
|
+
supported_accept_headers: list[str] | None,
|
|
31
34
|
http_codes: dict[int, tuple[str, str]],
|
|
32
35
|
json_encoder: type[json.JSONEncoder] | None,
|
|
33
|
-
|
|
36
|
+
response_format: Literal["flask"],
|
|
37
|
+
**kwargs: Any,
|
|
34
38
|
) -> tuple[Response, int]: ...
|
|
35
39
|
|
|
36
40
|
|
|
@@ -39,10 +43,12 @@ def create_response_object(
|
|
|
39
43
|
status_code: int,
|
|
40
44
|
status_message: str,
|
|
41
45
|
data: Any | None,
|
|
42
|
-
|
|
46
|
+
accept_header: str,
|
|
47
|
+
supported_accept_headers: list[str] | None,
|
|
43
48
|
http_codes: dict[int, tuple[str, str]],
|
|
44
49
|
json_encoder: type[json.JSONEncoder] | None,
|
|
45
|
-
|
|
50
|
+
response_format: None = None,
|
|
51
|
+
**kwargs: Any,
|
|
46
52
|
) -> tuple[dict[str, Any], int]: ...
|
|
47
53
|
|
|
48
54
|
|
|
@@ -50,16 +56,20 @@ def create_response_object(
|
|
|
50
56
|
status_code: int,
|
|
51
57
|
status_message: str,
|
|
52
58
|
data: Any | None = None,
|
|
53
|
-
|
|
59
|
+
accept_header: str = "application/json",
|
|
60
|
+
supported_accept_headers: list[str] | None = None,
|
|
54
61
|
http_codes: dict[int, tuple[str, str]] = http_codes_en,
|
|
55
|
-
json_encoder: type[json.JSONEncoder] | None =
|
|
56
|
-
|
|
62
|
+
json_encoder: type[json.JSONEncoder] | None = None,
|
|
63
|
+
response_format: str | None = "dict",
|
|
64
|
+
**kwargs: Any,
|
|
57
65
|
) -> tuple[Response, int] | tuple[dict[str, Any], int]:
|
|
58
66
|
"""Create a HTTP response object.
|
|
59
67
|
|
|
60
68
|
The response object can be either a dictionary or a Flask Response object,
|
|
61
|
-
depending on the value of `
|
|
69
|
+
depending on the value of `response_format`. The response will include the
|
|
62
70
|
status code, a human-readable message, and optionally additional data.
|
|
71
|
+
Only supports JSON responses. For other types, use custom function with
|
|
72
|
+
x-alpha-custom-response-builder in the OpenAPI specification.
|
|
63
73
|
|
|
64
74
|
Parameters
|
|
65
75
|
----------
|
|
@@ -69,15 +79,18 @@ def create_response_object(
|
|
|
69
79
|
Human-readable message describing the status.
|
|
70
80
|
data
|
|
71
81
|
Additional data to include in the response, by default None
|
|
72
|
-
|
|
73
|
-
The
|
|
82
|
+
accept_header
|
|
83
|
+
The value of the Accept header from the request,
|
|
84
|
+
by default "application/json"
|
|
85
|
+
supported_accept_headers
|
|
86
|
+
A list of supported MIME types for the data_type parameter.
|
|
74
87
|
http_codes
|
|
75
88
|
A dictionary mapping HTTP status codes to their descriptions, by
|
|
76
89
|
default http_codes_en
|
|
77
90
|
json_encoder
|
|
78
91
|
A custom JSON encoder class to use when encoding the response data, by
|
|
79
|
-
default
|
|
80
|
-
|
|
92
|
+
default None. If None, the default JSONEncoder will be used.
|
|
93
|
+
response_format
|
|
81
94
|
The type of response to create, either "flask" or "dict", by default
|
|
82
95
|
"dict"
|
|
83
96
|
|
|
@@ -85,13 +98,21 @@ def create_response_object(
|
|
|
85
98
|
-------
|
|
86
99
|
tuple[dict[str, Any], int]
|
|
87
100
|
A tuple containing the response object as a dictionary and the HTTP
|
|
88
|
-
status code. When
|
|
101
|
+
status code. When response_format is "dict".
|
|
89
102
|
tuple[Response, int]
|
|
90
103
|
A tuple containing the flask.Response object and the HTTP status code.
|
|
91
|
-
When
|
|
104
|
+
When response_format is "flask".
|
|
92
105
|
"""
|
|
93
|
-
|
|
94
|
-
|
|
106
|
+
data_type = _resolve_data_type(accept_header, supported_accept_headers)
|
|
107
|
+
|
|
108
|
+
if response_format is None:
|
|
109
|
+
response_format = "dict"
|
|
110
|
+
|
|
111
|
+
if json_encoder is None:
|
|
112
|
+
# Lazily import to avoid circular import during alpha package init.
|
|
113
|
+
from alpha.encoder import JSONEncoder
|
|
114
|
+
|
|
115
|
+
json_encoder = JSONEncoder
|
|
95
116
|
|
|
96
117
|
obj: dict[str, Any] = {
|
|
97
118
|
"detail": status_message,
|
|
@@ -100,7 +121,7 @@ def create_response_object(
|
|
|
100
121
|
"type": data_type or "about:blank",
|
|
101
122
|
}
|
|
102
123
|
|
|
103
|
-
if
|
|
124
|
+
if response_format == "flask":
|
|
104
125
|
# Importing Flask's Response class here lazily to avoid unnecessary
|
|
105
126
|
# dependencies when not using Flask.
|
|
106
127
|
from flask.wrappers import Response
|
|
@@ -138,7 +159,7 @@ def create_response_object(
|
|
|
138
159
|
|
|
139
160
|
return resp, status_code
|
|
140
161
|
|
|
141
|
-
if
|
|
162
|
+
if response_format == "dict":
|
|
142
163
|
if data is not None:
|
|
143
164
|
obj["data"] = data
|
|
144
165
|
return obj, status_code
|
|
@@ -158,6 +179,7 @@ def _split_cookies_from_object(
|
|
|
158
179
|
|
|
159
180
|
Returns
|
|
160
181
|
-------
|
|
182
|
+
tuple[Any | None, list[Cookie]]
|
|
161
183
|
A tuple containing the data and a list of Cookie objects representing
|
|
162
184
|
the cookies.
|
|
163
185
|
"""
|
|
@@ -175,3 +197,48 @@ def _split_cookies_from_object(
|
|
|
175
197
|
data = data[0]
|
|
176
198
|
return data, cookies
|
|
177
199
|
return obj, []
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _resolve_data_type(
|
|
203
|
+
accept_header: str | None,
|
|
204
|
+
supported_accept_headers: list[str] | None,
|
|
205
|
+
default: str = "application/json",
|
|
206
|
+
) -> str:
|
|
207
|
+
"""Resolve the data type for the response by matching against the supported
|
|
208
|
+
types.
|
|
209
|
+
|
|
210
|
+
Match wildcards like "*/*" or "application/*" with first match in supported
|
|
211
|
+
types. If the data type is not supported, resort to default type.
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
accept_header
|
|
216
|
+
The Accept header from the request.
|
|
217
|
+
supported_accept_headers
|
|
218
|
+
A list of supported MIME types. If None, all types are supported.
|
|
219
|
+
default
|
|
220
|
+
The default MIME type to use if the provided accept_header is not
|
|
221
|
+
supported.
|
|
222
|
+
|
|
223
|
+
Returns
|
|
224
|
+
-------
|
|
225
|
+
str
|
|
226
|
+
The resolved MIME type to use for the response.
|
|
227
|
+
"""
|
|
228
|
+
# If no data type provided or no supported types, return default
|
|
229
|
+
if not accept_header or not supported_accept_headers:
|
|
230
|
+
return default
|
|
231
|
+
# Return provided type if it matches supported type
|
|
232
|
+
if accept_header.lower() in supported_accept_headers:
|
|
233
|
+
return accept_header.lower()
|
|
234
|
+
# If MIME type wildcard is provided, return first supported type
|
|
235
|
+
if accept_header.startswith("*/"):
|
|
236
|
+
return supported_accept_headers[0]
|
|
237
|
+
# If MIME subtype is a wildcard, match prefix
|
|
238
|
+
if accept_header.endswith("/*"):
|
|
239
|
+
prefix = accept_header.split("/")[0].lower()
|
|
240
|
+
for supported in supported_accept_headers:
|
|
241
|
+
if supported.startswith(prefix):
|
|
242
|
+
return supported
|
|
243
|
+
# If not matched, return default
|
|
244
|
+
return default
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: alpha-python
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
4
4
|
Summary: Alpha is intended to be the first dependency you need to add to your Python application. It is a Python library which contains standard building blocks that can be used in applications that are used as APIs and/or make use of database interaction.
|
|
5
5
|
Author-email: Bart Reijling <bart@reijling.eu>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
alpha/__init__.py,sha256=
|
|
1
|
+
alpha/__init__.py,sha256=FnXcEv9PSCLWDKE4-9GCqI7HSAvzo3WW_mfcBLPBBxo,6153
|
|
2
2
|
alpha/cli.py,sha256=YTWM7lzmydYazXMJ6LULywvJTMHzvfTO6yNuPrUgHCY,5813
|
|
3
|
-
alpha/encoder.py,sha256=
|
|
3
|
+
alpha/encoder.py,sha256=dPFDfCofX2ehhOZuC7EEoj00l5sclE-BAvq01oQpo4o,2891
|
|
4
4
|
alpha/exceptions.py,sha256=AHoFMPyHvjj6j_1X2TS40dSaFBzCDtgzAmucimSZjfc,5793
|
|
5
5
|
alpha/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
alpha/adapters/__init__.py,sha256=pGpAtLuVs-jOyRShjO1BwSHpB5MyStJ3Hs9wPm49Q2s,201
|
|
@@ -26,7 +26,7 @@ alpha/factories/logging_handler_factory.py,sha256=ZoVkD2S3Pl7NMMfhcFF8k76BlKTsvi
|
|
|
26
26
|
alpha/factories/model_class_factory.py,sha256=VK80vLHGF-XdlW3aln5C9tHSQv81NLxz5ti5jYxdv4w,5862
|
|
27
27
|
alpha/factories/password_factory.py,sha256=uK8X5bNvlyUtxvJXH7MXmYLiaM4jmsS6sZrx7Ew07kM,5031
|
|
28
28
|
alpha/factories/request_factory.py,sha256=vjkvsBkKKUZ7me5a8cEcFYet03qnUZ6f5Qyktfxospw,7159
|
|
29
|
-
alpha/factories/response_factory.py,sha256=
|
|
29
|
+
alpha/factories/response_factory.py,sha256=du2tqJdqzTSIpWeC3z8LmVKKZHqrLZ-1G42TBLbIQG4,6749
|
|
30
30
|
alpha/factories/type_factories.py,sha256=3MCSQ1g1neBVPk0cSbhmGED7O4AQNMmBK8OmTLwuaGM,6478
|
|
31
31
|
alpha/factories/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
32
|
alpha/factories/models/factory_classes.py,sha256=RpmJdPmC5MUNTD4TK6TaqBzd2ezLGSjsHGltl-_6tqo,1131
|
|
@@ -48,7 +48,7 @@ alpha/handlers/templates/python-flask/__init__model.mustache,sha256=JRwyJmOCAD8n
|
|
|
48
48
|
alpha/handlers/templates/python-flask/__init__test.mustache,sha256=wUxDXcyE4rPh3sDp8MvR5m8vqzoU25Fw1bo_xh6InnY,438
|
|
49
49
|
alpha/handlers/templates/python-flask/__main__.mustache,sha256=D11Z_bhka_NsK3EP8iSzV8_Hcc15L-uOntH2IbzrKng,2942
|
|
50
50
|
alpha/handlers/templates/python-flask/base_model.mustache,sha256=B--jl9LmfiHACq9n1kDxm6YTi6tb1oSUFnZya_IKOhU,2184
|
|
51
|
-
alpha/handlers/templates/python-flask/controller.mustache,sha256=
|
|
51
|
+
alpha/handlers/templates/python-flask/controller.mustache,sha256=wDY6EcZ6UiCkBXQKVrzhbOn_zAY6szW3jYNI0LiMohs,18691
|
|
52
52
|
alpha/handlers/templates/python-flask/controller_test.mustache,sha256=2i2cwgQonT1RMJQwdxeq8B3x8-hLjpK7YkI--LkKLOI,2848
|
|
53
53
|
alpha/handlers/templates/python-flask/dockerignore.mustache,sha256=hqlHuqIMLuuwvVqK6xhIcfaezKoChriR3D7TvxtkjGo,906
|
|
54
54
|
alpha/handlers/templates/python-flask/encoder.mustache,sha256=03rsvhN_QdqaWsQC2v3772UvrLEXna9WcvxQsl6mwiU,866
|
|
@@ -65,15 +65,15 @@ alpha/handlers/templates/python-flask/tox.mustache,sha256=iuEc1JquvGZ0UlCCJus2X_
|
|
|
65
65
|
alpha/handlers/templates/python-flask/travis.mustache,sha256=M9UNXQnfg8F6uFvOSsay8FQ0YvPDi8EF6HIiu4UW4-o,319
|
|
66
66
|
alpha/handlers/templates/python-flask/typing_utils.mustache,sha256=mGDTJkj9V9w7NsFSljw33GKB-gWfqoceAot-iHTViNs,809
|
|
67
67
|
alpha/handlers/templates/python-flask/util.mustache,sha256=iQVhsAN_ZCQuQGOxoqATTE90fkIEojKyrfooFqa7Jck,3534
|
|
68
|
-
alpha/infra/__init__.py,sha256=
|
|
68
|
+
alpha/infra/__init__.py,sha256=4eIKsW1N06_vVBrO2u1ESU5iURBb0cGvxcscaPsB4PE,1018
|
|
69
69
|
alpha/infra/connectors/__init__.py,sha256=kEh_mOIIqkWeY4cilislM8ZjKXNFp8zBuMThfbJ1CGc,647
|
|
70
|
-
alpha/infra/connectors/ldap_connector.py,sha256=
|
|
70
|
+
alpha/infra/connectors/ldap_connector.py,sha256=Ce2IspqcrTEkYwlhxoh8QOzEK51-ZcJ9t6RCytk0Ob8,5132
|
|
71
71
|
alpha/infra/connectors/oidc_connector.py,sha256=z9AY1lMVK5nULd80zOvJzo8zPbp6CHJ7fv2smp71eIs,15675
|
|
72
72
|
alpha/infra/connectors/sql_alchemy.py,sha256=8DivUj25_OAVaXmaCsbSnHgOD1w5_ewAXSlZ-cxuMVk,5116
|
|
73
73
|
alpha/infra/databases/__init__.py,sha256=IyA_aB9h_7rOM-aa50QLsA_q4e9W4HnJOqh76aDyrj4,106
|
|
74
74
|
alpha/infra/databases/sql_alchemy.py,sha256=7t17M8UEgW1NMX9LYW6A14EM5UxEJZyp-8a_xLEONAs,235
|
|
75
75
|
alpha/infra/models/__init__.py,sha256=SIakw9bAPXwFmx3I7exCRBs3e2Ug3etOJy6JyIOR1Uc,348
|
|
76
|
-
alpha/infra/models/filter_operators.py,sha256=
|
|
76
|
+
alpha/infra/models/filter_operators.py,sha256=LJw2w8v1FvDhQnEZ8hKq3Rq54_MwolGn7SSDT5t5rT4,2797
|
|
77
77
|
alpha/infra/models/json_patch.py,sha256=w8L59E6w-aSeEknww-JLCrB1kEpFbPXcRlweKP4kU1A,1753
|
|
78
78
|
alpha/infra/models/order_by.py,sha256=QN4ALsbJ-zoh1dx5OHZ2L9pijwfjBM5G0pBbpNrNHnM,4569
|
|
79
79
|
alpha/infra/models/query_clause.py,sha256=xLWxm-cWrODMqpqqVIu1veSVBWLENt97TTHq8CinP7M,2555
|
|
@@ -88,7 +88,7 @@ alpha/interfaces/openapi_model.py,sha256=9QayfwbwTg_3-reFso_ktwNqGI2f2C4AQOAPvPH
|
|
|
88
88
|
alpha/interfaces/patchable.py,sha256=9L42VdrvfDhKKlfjQAMDX6FDA0R-U6QkOWlAHUdxneQ,705
|
|
89
89
|
alpha/interfaces/providers.py,sha256=S0uBThEI_qqRpxyQmwNNSPGMcAVIU2ngXIimflDqUbc,4162
|
|
90
90
|
alpha/interfaces/pydantic_instance.py,sha256=S96jLEWRmDyvYV3VqofSqjiN-lcjfkjiNp7Vpumw49A,245
|
|
91
|
-
alpha/interfaces/sql_database.py,sha256
|
|
91
|
+
alpha/interfaces/sql_database.py,sha256=-YiMBSrGkd2NTS4FEBjXenlPnVQW2t3SvQJMDRyHDjA,919
|
|
92
92
|
alpha/interfaces/sql_mapper.py,sha256=OSGIwbtlQ5-cBHUIiJfLgJlkBU3MgMAbSdxj8X2v-eI,624
|
|
93
93
|
alpha/interfaces/sql_repository.py,sha256=nJCdF_G8YS7_VST_OL14wBA_Ne_DyGZhQQDAta1zqYk,9814
|
|
94
94
|
alpha/interfaces/token_factory.py,sha256=MeSvnyH699vBG0c7Oli4M6RT1wEsCVTNy7V4XQpu31o,1882
|
|
@@ -125,7 +125,7 @@ alpha/utils/is_pydantic.py,sha256=-P4n3n59_YftVqvIIQ_GovxUxQMFIt8htTDd0j2hFis,62
|
|
|
125
125
|
alpha/utils/logging_configurator.py,sha256=wnwcBjI4wzvz2R0oMgEHffoGo9FfgPhf-1ZIlA9Eydo,3817
|
|
126
126
|
alpha/utils/logging_level_checker.py,sha256=elCxkVW07BnVGPhIHf_TQyHCAPpdNY8xp5bszrziRS0,697
|
|
127
127
|
alpha/utils/request_headers.py,sha256=z_0mDT4NoQimG1zPCyM5-7T64BJ91eQXNILuIc08cYI,4310
|
|
128
|
-
alpha/utils/response_object.py,sha256=
|
|
128
|
+
alpha/utils/response_object.py,sha256=UsmL50ziE2rRjGLdzGEdQ99YWTUbIUWjBN7OGO4cneg,7811
|
|
129
129
|
alpha/utils/secret_generator.py,sha256=LjsKb8oDfsfmCCcTKrqtw_duqJ7u3V_o6zGGuG26_XE,393
|
|
130
130
|
alpha/utils/verify_identity.py,sha256=Y1pdcVl8Z_I4ibw2KY7i5QwTQrY29WwpBOJ6j0YOAow,2271
|
|
131
131
|
alpha/utils/version_checker.py,sha256=W8v69hDbrr0VvJ40gpFmXMtVawrDmUz_YGWssKUTmVE,380
|
|
@@ -136,9 +136,9 @@ alpha/utils/openapi_test/models.py,sha256=vAHbjmwc0rVsCj9m8iKqkwPpSkSmkTOT8UQvff
|
|
|
136
136
|
alpha/utils/openapi_test/orm.py,sha256=Py95GV_0e7wI1MYDZR1_EHkvTgev6eNo70SVmnD7kGc,3045
|
|
137
137
|
alpha/utils/openapi_test/response.py,sha256=IxbQ6Nw258LLViHkgfjOpc7zWGTP8ofVOX1zrwDoR50,270
|
|
138
138
|
alpha/utils/openapi_test/service.py,sha256=ycrEUlQmygKthnOhEiir-wtbPFi5cT-bepVclaL9FAk,5003
|
|
139
|
-
alpha_python-0.6.
|
|
140
|
-
alpha_python-0.6.
|
|
141
|
-
alpha_python-0.6.
|
|
142
|
-
alpha_python-0.6.
|
|
143
|
-
alpha_python-0.6.
|
|
144
|
-
alpha_python-0.6.
|
|
139
|
+
alpha_python-0.6.3.dist-info/licenses/LICENSE,sha256=5KwEqC3KUoH4lVXgZ9tGriKOl-LGxHkXBWo16mFmAYM,1070
|
|
140
|
+
alpha_python-0.6.3.dist-info/METADATA,sha256=aXEewzfWErO4dI6yI4a_in5JbWrtMPbEM2sZuicqwS8,8645
|
|
141
|
+
alpha_python-0.6.3.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
142
|
+
alpha_python-0.6.3.dist-info/entry_points.txt,sha256=LBEXdcofOugYYdZ46nz5Dxj_aj1QbRBkumfPGhy-GXI,41
|
|
143
|
+
alpha_python-0.6.3.dist-info/top_level.txt,sha256=tqmNnOmi2RSSiPo99C03fD5Cc3r9za9xTjPAoQC1EGA,6
|
|
144
|
+
alpha_python-0.6.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|