alpha-python 0.6.1__tar.gz → 0.6.3__tar.gz

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.
Files changed (151) hide show
  1. {alpha_python-0.6.1 → alpha_python-0.6.3}/PKG-INFO +1 -1
  2. {alpha_python-0.6.1 → alpha_python-0.6.3}/pyproject.toml +1 -1
  3. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/__init__.py +2 -1
  4. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/encoder.py +11 -5
  5. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/response_factory.py +11 -4
  6. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/controller.mustache +13 -9
  7. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/infra/__init__.py +2 -1
  8. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/infra/connectors/ldap_connector.py +25 -2
  9. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/infra/models/filter_operators.py +3 -5
  10. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/sql_database.py +0 -2
  11. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/response_object.py +90 -23
  12. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha_python.egg-info/PKG-INFO +1 -1
  13. {alpha_python-0.6.1 → alpha_python-0.6.3}/tests/test_encoder.py +43 -2
  14. {alpha_python-0.6.1 → alpha_python-0.6.3}/LICENSE +0 -0
  15. {alpha_python-0.6.1 → alpha_python-0.6.3}/README.md +0 -0
  16. {alpha_python-0.6.1 → alpha_python-0.6.3}/setup.cfg +0 -0
  17. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/adapters/__init__.py +0 -0
  18. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/adapters/rest_api_unit_of_work.py +0 -0
  19. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/adapters/sqla_unit_of_work.py +0 -0
  20. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/cli.py +0 -0
  21. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/containers/__init__.py +0 -0
  22. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/containers/container.py +0 -0
  23. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/domain/__init__.py +0 -0
  24. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/domain/models/__init__.py +0 -0
  25. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/domain/models/base_model.py +0 -0
  26. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/domain/models/group.py +0 -0
  27. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/domain/models/life_cycle_base.py +0 -0
  28. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/domain/models/role.py +0 -0
  29. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/domain/models/user.py +0 -0
  30. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/exceptions.py +0 -0
  31. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/__init__.py +0 -0
  32. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/_type_conversion_matrix.py +0 -0
  33. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/_type_mapping.py +0 -0
  34. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/class_factories.py +0 -0
  35. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/default_field_factory.py +0 -0
  36. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/field_iterator.py +0 -0
  37. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/jwt_factory.py +0 -0
  38. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/logging_handler_factory.py +0 -0
  39. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/model_class_factory.py +0 -0
  40. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/models/__init__.py +0 -0
  41. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/models/factory_classes.py +0 -0
  42. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/password_factory.py +0 -0
  43. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/request_factory.py +0 -0
  44. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/factories/type_factories.py +0 -0
  45. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/__init__.py +0 -0
  46. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/api_generate_handler.py +0 -0
  47. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/api_run_handler.py +0 -0
  48. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/base_handler.py +0 -0
  49. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/gen-code.sh +0 -0
  50. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/models/__init__.py +0 -0
  51. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/models/argument.py +0 -0
  52. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/models/command.py +0 -0
  53. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/models/section.py +0 -0
  54. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/models/subparser.py +0 -0
  55. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/run-api.sh +0 -0
  56. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/__init__.py +0 -0
  57. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/Dockerfile.mustache +0 -0
  58. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/README.mustache +0 -0
  59. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/__init__model.mustache +0 -0
  60. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/__init__test.mustache +0 -0
  61. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/__main__.mustache +0 -0
  62. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/base_model.mustache +0 -0
  63. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/controller_test.mustache +0 -0
  64. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/dockerignore.mustache +0 -0
  65. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/encoder.mustache +0 -0
  66. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/git_push.sh.mustache +0 -0
  67. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/gitignore.mustache +0 -0
  68. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/model.mustache +0 -0
  69. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/openapi.mustache +0 -0
  70. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/param_type.mustache +0 -0
  71. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/requirements.mustache +0 -0
  72. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/security_controller_.mustache +0 -0
  73. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/setup.mustache +0 -0
  74. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/test-requirements.mustache +0 -0
  75. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/tox.mustache +0 -0
  76. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/travis.mustache +0 -0
  77. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/typing_utils.mustache +0 -0
  78. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/handlers/templates/python-flask/util.mustache +0 -0
  79. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/infra/connectors/__init__.py +0 -0
  80. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/infra/connectors/oidc_connector.py +0 -0
  81. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/infra/connectors/sql_alchemy.py +0 -0
  82. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/infra/databases/__init__.py +0 -0
  83. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/infra/databases/sql_alchemy.py +0 -0
  84. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/infra/models/__init__.py +0 -0
  85. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/infra/models/json_patch.py +0 -0
  86. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/infra/models/order_by.py +0 -0
  87. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/infra/models/query_clause.py +0 -0
  88. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/infra/models/search_filter.py +0 -0
  89. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/__init__.py +0 -0
  90. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/api_repository.py +0 -0
  91. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/attrs_instance.py +0 -0
  92. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/dataclass_instance.py +0 -0
  93. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/factories.py +0 -0
  94. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/handler.py +0 -0
  95. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/openapi_model.py +0 -0
  96. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/patchable.py +0 -0
  97. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/providers.py +0 -0
  98. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/pydantic_instance.py +0 -0
  99. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/sql_mapper.py +0 -0
  100. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/sql_repository.py +0 -0
  101. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/token_factory.py +0 -0
  102. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/unit_of_work.py +0 -0
  103. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/interfaces/updatable.py +0 -0
  104. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/mixins/__init__.py +0 -0
  105. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/mixins/group_lifecycle.py +0 -0
  106. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/mixins/jwt_provider.py +0 -0
  107. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/mixins/user_lifecycle.py +0 -0
  108. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/providers/__init__.py +0 -0
  109. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/providers/api_key_provider.py +0 -0
  110. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/providers/database_provider.py +0 -0
  111. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/providers/ldap_provider.py +0 -0
  112. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/providers/models/__init__.py +0 -0
  113. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/providers/models/credentials.py +0 -0
  114. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/providers/models/identity.py +0 -0
  115. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/providers/models/token.py +0 -0
  116. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/providers/oidc_provider.py +0 -0
  117. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/py.typed +0 -0
  118. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/repositories/__init__.py +0 -0
  119. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/repositories/models/__init__.py +0 -0
  120. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/repositories/models/repository_model.py +0 -0
  121. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/repositories/rest_api_repository.py +0 -0
  122. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/repositories/sql_alchemy_repository.py +0 -0
  123. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/services/__init__.py +0 -0
  124. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/services/authentication_service.py +0 -0
  125. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/services/models/__init__.py +0 -0
  126. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/services/models/cookie.py +0 -0
  127. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/services/user_lifecycle_management.py +0 -0
  128. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/__init__.py +0 -0
  129. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/_http_codes.py +0 -0
  130. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/cookie.py +0 -0
  131. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/is_attrs.py +0 -0
  132. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/is_pydantic.py +0 -0
  133. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/logging_configurator.py +0 -0
  134. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/logging_level_checker.py +0 -0
  135. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/openapi_test/__init__.py +0 -0
  136. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/openapi_test/container.py +0 -0
  137. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/openapi_test/exceptions.py +0 -0
  138. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/openapi_test/models.py +0 -0
  139. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/openapi_test/orm.py +0 -0
  140. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/openapi_test/response.py +0 -0
  141. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/openapi_test/service.py +0 -0
  142. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/request_headers.py +0 -0
  143. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/secret_generator.py +0 -0
  144. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/verify_identity.py +0 -0
  145. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha/utils/version_checker.py +0 -0
  146. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha_python.egg-info/SOURCES.txt +0 -0
  147. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha_python.egg-info/dependency_links.txt +0 -0
  148. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha_python.egg-info/entry_points.txt +0 -0
  149. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha_python.egg-info/requires.txt +0 -0
  150. {alpha_python-0.6.1 → alpha_python-0.6.3}/src/alpha_python.egg-info/top_level.txt +0 -0
  151. {alpha_python-0.6.1 → alpha_python-0.6.3}/tests/test_cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpha-python
3
- Version: 0.6.1
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
1
  [project]
2
2
  name = "alpha-python"
3
- version = "0.6.1"
3
+ version = "0.6.3"
4
4
  description = "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
  readme = "README.md"
6
6
  authors = [
@@ -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",
@@ -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
- if isinstance(o, type):
77
- cls = getattr(o, "__class__")
78
- return cls.__name__
79
- return asdict(o)
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, Iterable, get_args, get_origin
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 | Iterable[DataclassInstance],
22
- cls: OpenAPIModel | Iterable[OpenAPIModel],
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, Iterable):
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
- identity = token_factory.get_payload(auth_token)
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}}data_type='{{vendorExtensions.x-content-type}}'{{/vendorExtensions.x-content-type}}
313
- {{#vendorExtensions.x-alpha-cookie-support}}response_type='flask'{{/vendorExtensions.x-alpha-cookie-support}}
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, response_type='flask'{{/vendorExtensions.x-alpha-cookie-support}}
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}}
@@ -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 | FilterOperator] = (
25
- search_filters
26
- )
24
+ self.search_filters: Iterable[SearchFilter | Self] = search_filters
27
25
 
28
26
  @property
29
27
  def filter_operator(
@@ -32,5 +32,3 @@ class SqlDatabase(Protocol):
32
32
  def drop_tables(
33
33
  self, metadata: sa.MetaData, tables: list[sa.Table] | None = None
34
34
  ) -> None: ...
35
-
36
- def _create_schema(self, engine: Engine, schema_name: str) -> None: ...
@@ -1,8 +1,8 @@
1
- from typing import TYPE_CHECKING, Any, Literal, overload
2
1
  import json
3
- from alpha.encoder import JSONEncoder
4
- from alpha.utils.cookie import Cookie
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
- data_type: str,
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
- response_type: Literal["dict"],
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
- data_type: str,
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
- response_type: Literal["flask"],
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
- data_type: str,
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
- response_type: None = None,
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
- data_type: str = "application/json",
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 = JSONEncoder,
56
- response_type: str | None = "dict",
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 `response_type`. The response will include the
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
- data_type
73
- The MIME type of the response, by default "application/json"
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 alpha.encoder.JSONEncoder
80
- response_type
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 response_type is "dict".
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 response_type is "flask".
104
+ When response_format is "flask".
92
105
  """
93
- if response_type is None:
94
- response_type = "dict"
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 response_type == "flask":
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 response_type == "dict":
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.1
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
@@ -3,11 +3,14 @@ from datetime import date, datetime, time, timezone
3
3
  from enum import Enum
4
4
  from typing import Any, Type, TypeVar
5
5
  from uuid import UUID
6
+ from attrs import define
7
+ from pydantic import BaseModel
8
+ from dataclasses import dataclass
6
9
 
7
10
  import numpy as np
8
11
  import pandas as pd
9
12
  import pytest
10
- from alpha.encoder import JSONEncoder
13
+ import alpha
11
14
 
12
15
  T = TypeVar("T")
13
16
 
@@ -15,7 +18,7 @@ T = TypeVar("T")
15
18
  @pytest.fixture
16
19
  def encoder_factory():
17
20
  def run_encoder(obj: Any, key: str):
18
- json_ = json.dumps(obj, cls=JSONEncoder)
21
+ json_ = json.dumps(obj, cls=alpha.JSONEncoder)
19
22
  dict_ = json.loads(json_)
20
23
  return dict_[key]
21
24
 
@@ -75,6 +78,20 @@ class FakeModelToList:
75
78
  return [1, 2, 3]
76
79
 
77
80
 
81
+ @dataclass
82
+ class FakeDataclassModel:
83
+ dataclass: str
84
+
85
+
86
+ @define
87
+ class FakeAttrsModel:
88
+ attrs: str
89
+
90
+
91
+ class FakePydanticModel(BaseModel):
92
+ pydantic: str
93
+
94
+
78
95
  @pytest.fixture
79
96
  def uuid_str():
80
97
  return "af6b2857-567c-490b-81e6-db21dbeba69b"
@@ -168,3 +185,27 @@ def test_open_api_model(encoder_factory, obj):
168
185
  def test_open_api_model_list(encoder_factory, obj):
169
186
  value = encoder_factory(obj, "open_api_model_list")
170
187
  assert value == [{"value": "abc"}]
188
+
189
+
190
+ def test_dataclass_model_type(encoder_factory):
191
+ model = FakeDataclassModel
192
+ obj = json.dumps(model, cls=alpha.JSONEncoder)
193
+ assert obj == '"FakeDataclassModel"'
194
+
195
+
196
+ def test_dataclass_model(encoder_factory):
197
+ model = FakeDataclassModel(dataclass="abc")
198
+ obj = encoder_factory(model, "dataclass")
199
+ assert obj == "abc"
200
+
201
+
202
+ def test_attrs_model(encoder_factory):
203
+ model = FakeAttrsModel(attrs="abc")
204
+ obj = encoder_factory(model, "attrs")
205
+ assert obj == "abc"
206
+
207
+
208
+ def test_pydantic_model(encoder_factory):
209
+ model = FakePydanticModel(pydantic="abc")
210
+ obj = encoder_factory(model, "pydantic")
211
+ assert obj == "abc"
File without changes
File without changes
File without changes