alpha-python 0.4.0__tar.gz → 0.5.1__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 (144) hide show
  1. {alpha_python-0.4.0 → alpha_python-0.5.1}/LICENSE +1 -1
  2. {alpha_python-0.4.0/src/alpha_python.egg-info → alpha_python-0.5.1}/PKG-INFO +58 -2
  3. alpha_python-0.5.1/README.md +58 -0
  4. {alpha_python-0.4.0 → alpha_python-0.5.1}/pyproject.toml +10 -2
  5. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/__init__.py +26 -8
  6. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/adapters/__init__.py +2 -0
  7. alpha_python-0.5.1/src/alpha/adapters/rest_api_unit_of_work.py +105 -0
  8. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/adapters/sqla_unit_of_work.py +9 -9
  9. alpha_python-0.5.1/src/alpha/domain/models/group.py +35 -0
  10. alpha_python-0.5.1/src/alpha/domain/models/user.py +130 -0
  11. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/exceptions.py +12 -0
  12. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/jwt_factory.py +88 -25
  13. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/password_factory.py +1 -0
  14. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/controller.mustache +37 -28
  15. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/__init__.py +5 -1
  16. alpha_python-0.5.1/src/alpha/interfaces/api_repository.py +610 -0
  17. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/providers.py +1 -1
  18. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/sql_repository.py +0 -11
  19. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/token_factory.py +6 -1
  20. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/mixins/jwt_provider.py +7 -7
  21. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/providers/database_provider.py +2 -2
  22. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/providers/ldap_provider.py +9 -9
  23. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/providers/models/identity.py +56 -22
  24. alpha_python-0.5.1/src/alpha/providers/models/token.py +55 -0
  25. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/providers/oidc_provider.py +7 -5
  26. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/repositories/__init__.py +2 -0
  27. alpha_python-0.5.1/src/alpha/repositories/models/repository_model.py +34 -0
  28. alpha_python-0.5.1/src/alpha/repositories/rest_api_repository.py +1199 -0
  29. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/services/__init__.py +2 -0
  30. alpha_python-0.5.1/src/alpha/services/authentication_service.py +835 -0
  31. alpha_python-0.5.1/src/alpha/services/models/cookie.py +51 -0
  32. alpha_python-0.5.1/src/alpha/services/user_lifecycle_management.py +331 -0
  33. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/utils/__init__.py +2 -0
  34. alpha_python-0.5.1/src/alpha/utils/request_headers.py +108 -0
  35. alpha_python-0.5.1/src/alpha/utils/response_object.py +166 -0
  36. alpha_python-0.5.1/src/alpha/utils/secret_generator.py +19 -0
  37. {alpha_python-0.4.0 → alpha_python-0.5.1/src/alpha_python.egg-info}/PKG-INFO +58 -2
  38. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha_python.egg-info/SOURCES.txt +9 -1
  39. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha_python.egg-info/requires.txt +1 -0
  40. alpha_python-0.4.0/README.md +0 -3
  41. alpha_python-0.4.0/src/alpha/domain/models/user.py +0 -66
  42. alpha_python-0.4.0/src/alpha/providers/models/token.py +0 -7
  43. alpha_python-0.4.0/src/alpha/repositories/models/repository_model.py +0 -16
  44. alpha_python-0.4.0/src/alpha/services/authentication_service.py +0 -200
  45. alpha_python-0.4.0/src/alpha/utils/response_object.py +0 -26
  46. {alpha_python-0.4.0 → alpha_python-0.5.1}/setup.cfg +0 -0
  47. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/cli.py +0 -0
  48. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/containers/__init__.py +0 -0
  49. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/containers/container.py +0 -0
  50. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/domain/__init__.py +0 -0
  51. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/domain/models/__init__.py +0 -0
  52. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/domain/models/base_model.py +0 -0
  53. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/domain/models/life_cycle_base.py +0 -0
  54. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/encoder.py +0 -0
  55. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/__init__.py +0 -0
  56. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/_type_conversion_matrix.py +0 -0
  57. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/_type_mapping.py +0 -0
  58. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/class_factories.py +0 -0
  59. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/default_field_factory.py +0 -0
  60. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/field_iterator.py +0 -0
  61. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/logging_handler_factory.py +0 -0
  62. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/model_class_factory.py +0 -0
  63. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/models/__init__.py +0 -0
  64. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/models/factory_classes.py +0 -0
  65. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/request_factory.py +0 -0
  66. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/response_factory.py +0 -0
  67. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/factories/type_factories.py +0 -0
  68. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/__init__.py +0 -0
  69. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/api_generate_handler.py +0 -0
  70. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/api_run_handler.py +0 -0
  71. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/base_handler.py +0 -0
  72. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/gen-code.sh +0 -0
  73. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/models/__init__.py +0 -0
  74. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/models/argument.py +0 -0
  75. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/models/command.py +0 -0
  76. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/models/section.py +0 -0
  77. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/models/subparser.py +0 -0
  78. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/run-api.sh +0 -0
  79. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/__init__.py +0 -0
  80. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/Dockerfile.mustache +0 -0
  81. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/README.mustache +0 -0
  82. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/__init__model.mustache +0 -0
  83. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/__init__test.mustache +0 -0
  84. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/__main__.mustache +0 -0
  85. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/base_model.mustache +0 -0
  86. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/controller_test.mustache +0 -0
  87. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/dockerignore.mustache +0 -0
  88. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/encoder.mustache +0 -0
  89. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/git_push.sh.mustache +0 -0
  90. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/gitignore.mustache +0 -0
  91. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/model.mustache +0 -0
  92. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/openapi.mustache +0 -0
  93. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/param_type.mustache +0 -0
  94. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/requirements.mustache +0 -0
  95. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/security_controller_.mustache +0 -0
  96. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/setup.mustache +0 -0
  97. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/test-requirements.mustache +0 -0
  98. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/tox.mustache +0 -0
  99. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/travis.mustache +0 -0
  100. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/typing_utils.mustache +0 -0
  101. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/handlers/templates/python-flask/util.mustache +0 -0
  102. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/infra/__init__.py +0 -0
  103. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/infra/connectors/__init__.py +0 -0
  104. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/infra/connectors/ldap_connector.py +0 -0
  105. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/infra/connectors/oidc_connector.py +0 -0
  106. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/infra/databases/__init__.py +0 -0
  107. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/infra/databases/sql_alchemy.py +0 -0
  108. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/infra/models/__init__.py +0 -0
  109. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/infra/models/filter_operators.py +0 -0
  110. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/infra/models/json_patch.py +0 -0
  111. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/infra/models/order_by.py +0 -0
  112. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/infra/models/query_clause.py +0 -0
  113. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/infra/models/search_filter.py +0 -0
  114. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/attrs_instance.py +0 -0
  115. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/dataclass_instance.py +0 -0
  116. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/factories.py +0 -0
  117. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/handler.py +0 -0
  118. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/openapi_model.py +0 -0
  119. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/patchable.py +0 -0
  120. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/pydantic_instance.py +0 -0
  121. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/sql_database.py +0 -0
  122. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/sql_mapper.py +0 -0
  123. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/unit_of_work.py +0 -0
  124. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/interfaces/updateable.py +0 -0
  125. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/mixins/__init__.py +0 -0
  126. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/providers/__init__.py +0 -0
  127. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/providers/api_key_provider.py +0 -0
  128. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/providers/models/__init__.py +0 -0
  129. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/providers/models/credentials.py +0 -0
  130. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/py.typed +0 -0
  131. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/repositories/models/__init__.py +0 -0
  132. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/repositories/sql_alchemy_repository.py +0 -0
  133. /alpha_python-0.4.0/src/alpha/providers/local_provider.py → /alpha_python-0.5.1/src/alpha/services/models/__init__.py +0 -0
  134. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/utils/_http_codes.py +0 -0
  135. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/utils/is_attrs.py +0 -0
  136. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/utils/is_pydantic.py +0 -0
  137. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/utils/logging_configurator.py +0 -0
  138. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/utils/logging_level_checker.py +0 -0
  139. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/utils/verify_identity.py +0 -0
  140. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha/utils/version_checker.py +0 -0
  141. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha_python.egg-info/dependency_links.txt +0 -0
  142. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha_python.egg-info/entry_points.txt +0 -0
  143. {alpha_python-0.4.0 → alpha_python-0.5.1}/src/alpha_python.egg-info/top_level.txt +0 -0
  144. {alpha_python-0.4.0 → alpha_python-0.5.1}/tests/test_encoder.py +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 breijling
3
+ Copyright (c) 2026 Bart Reijling
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpha-python
3
- Version: 0.4.0
3
+ Version: 0.5.1
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
  Requires-Python: >=3.11
@@ -29,6 +29,7 @@ Requires-Dist: itsdangerous==1.1.0; extra == "flask"
29
29
  Requires-Dist: MarkupSafe<2.0.2; extra == "flask"
30
30
  Requires-Dist: Jinja2==2.11.2; extra == "flask"
31
31
  Requires-Dist: Flask<2,>=1.1.2; extra == "flask"
32
+ Requires-Dist: Werkzeug<2.1.0,>=1.0.1; extra == "flask"
32
33
  Requires-Dist: flask-cors>=3.0.10; extra == "flask"
33
34
  Requires-Dist: Flask-Compress>=1.13; extra == "flask"
34
35
  Provides-Extra: mysql
@@ -41,4 +42,59 @@ Dynamic: license-file
41
42
 
42
43
  # alpha
43
44
 
44
- 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.
45
+ 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.
46
+
47
+ ## Badges
48
+
49
+ [![PyPI version](https://badge.fury.io/py/alpha-python.svg)](https://badge.fury.io/py/alpha-python)
50
+ [![Build Status](https://travis-ci.com/breijling/alpha.svg?branch=main)](https://travis-ci.com/breijling/alpha)
51
+ [![Coverage Status](https://coveralls.io/repos/github/breijling/alpha/badge.svg?branch=main)](https://coveralls.io/github/breijling/alpha?branch=main)
52
+ [![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit/)
53
+
54
+ ## Documentation
55
+
56
+ TODO: Add documentation link when available.
57
+
58
+ ## Installation
59
+
60
+ The library is still in development, but you can already install it using pip:
61
+
62
+ ```shell
63
+ pip install alpha-python
64
+ ```
65
+
66
+ If you want to use the alpha cli for generating API code, you can install it using pip as well:
67
+
68
+ ```shell
69
+ pip install alpha-python[api-generator]
70
+ ```
71
+
72
+ If you want to add the library to your API project, you can add it to your pyproject.toml file:
73
+
74
+ ```shell
75
+ # Poetry example
76
+ poetry add alpha-python --extras "flask, postgresql"
77
+ poetry add --dev alpha-python --extras "api-generator"
78
+
79
+ # UV example
80
+ uv add alpha-python --extra flask --extra postgresql
81
+ uv add --dev alpha-python --extra api-generator
82
+ ```
83
+
84
+ ## Usage
85
+
86
+ The library contains a lot of different components, but the most important ones are:
87
+
88
+ - `alpha.encoder.JSONEncoder`: A JSON encoder that can be used to serialize complex objects to JSON.
89
+
90
+ ## Features
91
+
92
+
93
+
94
+ ## Contributing
95
+
96
+ If you want to contribute to the development of this library, you can fork the repository and create a pull request with your changes.
97
+
98
+ ## License
99
+
100
+ This library is licensed under the MIT License. See the LICENSE file for more information.
@@ -0,0 +1,58 @@
1
+ # alpha
2
+
3
+ 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.
4
+
5
+ ## Badges
6
+
7
+ [![PyPI version](https://badge.fury.io/py/alpha-python.svg)](https://badge.fury.io/py/alpha-python)
8
+ [![Build Status](https://travis-ci.com/breijling/alpha.svg?branch=main)](https://travis-ci.com/breijling/alpha)
9
+ [![Coverage Status](https://coveralls.io/repos/github/breijling/alpha/badge.svg?branch=main)](https://coveralls.io/github/breijling/alpha?branch=main)
10
+ [![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit/)
11
+
12
+ ## Documentation
13
+
14
+ TODO: Add documentation link when available.
15
+
16
+ ## Installation
17
+
18
+ The library is still in development, but you can already install it using pip:
19
+
20
+ ```shell
21
+ pip install alpha-python
22
+ ```
23
+
24
+ If you want to use the alpha cli for generating API code, you can install it using pip as well:
25
+
26
+ ```shell
27
+ pip install alpha-python[api-generator]
28
+ ```
29
+
30
+ If you want to add the library to your API project, you can add it to your pyproject.toml file:
31
+
32
+ ```shell
33
+ # Poetry example
34
+ poetry add alpha-python --extras "flask, postgresql"
35
+ poetry add --dev alpha-python --extras "api-generator"
36
+
37
+ # UV example
38
+ uv add alpha-python --extra flask --extra postgresql
39
+ uv add --dev alpha-python --extra api-generator
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ The library contains a lot of different components, but the most important ones are:
45
+
46
+ - `alpha.encoder.JSONEncoder`: A JSON encoder that can be used to serialize complex objects to JSON.
47
+
48
+ ## Features
49
+
50
+
51
+
52
+ ## Contributing
53
+
54
+ If you want to contribute to the development of this library, you can fork the repository and create a pull request with your changes.
55
+
56
+ ## License
57
+
58
+ This library is licensed under the MIT License. See the LICENSE file for more information.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "alpha-python"
3
- version = "0.4.0"
3
+ version = "0.5.1"
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 = [
@@ -56,7 +56,10 @@ dev = [
56
56
  "psycopg2-binary>=2.9.11",
57
57
  "pymysql>=1.1.2",
58
58
  "ldap3>=2.9.1",
59
- "cryptography>=46.0.3"
59
+ "cryptography>=46.0.3",
60
+ "ipykernel>=7.2.0",
61
+ "flask<2",
62
+ "httpx>=0.28.1",
60
63
  ]
61
64
 
62
65
  [project.optional-dependencies]
@@ -72,6 +75,7 @@ flask = [
72
75
  "MarkupSafe<2.0.2",
73
76
  "Jinja2==2.11.2",
74
77
  "Flask>=1.1.2,<2",
78
+ "Werkzeug>=1.0.1,<2.1.0",
75
79
  "flask-cors>=3.0.10",
76
80
  "Flask-Compress>=1.13",
77
81
  ]
@@ -116,5 +120,9 @@ force_grid_wrap = 0
116
120
  python_version = "3.11"
117
121
  exclude = ['^tests/']
118
122
 
123
+ [[tool.mypy.overrides]]
124
+ module = ["flask", "flask.*"]
125
+ ignore_missing_imports = true
126
+
119
127
  [tool.ruff]
120
128
  line-length = 79
@@ -1,3 +1,4 @@
1
+ from alpha.adapters.rest_api_unit_of_work import RestApiUnitOfWork
1
2
  from alpha.adapters.sqla_unit_of_work import SqlAlchemyUnitOfWork
2
3
  from alpha.factories.jwt_factory import JWTFactory
3
4
  from alpha.factories.logging_handler_factory import LoggingHandlerFactory
@@ -20,6 +21,7 @@ from alpha.interfaces.pydantic_instance import PydanticInstance
20
21
  from alpha.interfaces.openapi_model import OpenAPIModel
21
22
  from alpha.interfaces.updateable import Updateable
22
23
  from alpha.interfaces.patchable import Patchable
24
+ from alpha.interfaces.api_repository import ApiRepository
23
25
  from alpha.interfaces.sql_repository import SqlRepository
24
26
  from alpha.interfaces.sql_mapper import SqlMapper
25
27
  from alpha.interfaces.sql_database import SqlDatabase
@@ -44,8 +46,10 @@ from alpha.providers.models.credentials import PasswordCredentials
44
46
  from alpha.providers.models.token import Token
45
47
  from alpha.providers.oidc_provider import OIDCProvider, KeyCloakProvider
46
48
  from alpha.repositories.models.repository_model import RepositoryModel
49
+ from alpha.repositories.rest_api_repository import RestApiRepository
47
50
  from alpha.repositories.sql_alchemy_repository import SqlAlchemyRepository
48
51
  from alpha.services.authentication_service import AuthenticationService
52
+ from alpha.services.user_lifecycle_management import UserLifecycleManagement
49
53
  from alpha.utils.is_attrs import is_attrs
50
54
  from alpha.utils.is_pydantic import is_pydantic
51
55
  from alpha.utils.logging_configurator import (
@@ -53,6 +57,7 @@ from alpha.utils.logging_configurator import (
53
57
  GunicornLogger,
54
58
  )
55
59
  from alpha.utils.logging_level_checker import logging_level_checker
60
+ from alpha.utils.request_headers import Headers
56
61
  from alpha.utils.response_object import create_response_object
57
62
  from alpha.utils.verify_identity import verify_identity
58
63
  from alpha.utils.version_checker import minor_version_gte
@@ -60,13 +65,20 @@ from alpha.encoder import JSONEncoder
60
65
 
61
66
  # Optional LDAP support - only import if ldap3 is available
62
67
  try:
63
- from alpha.infra.connectors.ldap_connector import LDAPConnector # noqa: F401
64
- from alpha.providers.ldap_provider import LDAPProvider, ADProvider # noqa: F401
68
+ from alpha.infra.connectors.ldap_connector import (
69
+ LDAPConnector, # noqa: F401
70
+ )
71
+ from alpha.providers.ldap_provider import (
72
+ LDAPProvider, # noqa: F401
73
+ ADProvider, # noqa: F401
74
+ )
75
+
65
76
  _LDAP_AVAILABLE = True
66
77
  except ImportError:
67
- _LDAP_AVAILABLE = False # type: ignore
78
+ _LDAP_AVAILABLE = False # type: ignore
68
79
 
69
80
  __all__ = [
81
+ "RestApiUnitOfWork",
70
82
  "SqlAlchemyUnitOfWork",
71
83
  "JWTFactory",
72
84
  "LoggingHandlerFactory",
@@ -91,6 +103,7 @@ __all__ = [
91
103
  "OpenAPIModel",
92
104
  "Updateable",
93
105
  "Patchable",
106
+ "ApiRepository",
94
107
  "SqlRepository",
95
108
  "SqlMapper",
96
109
  "SqlDatabase",
@@ -112,13 +125,16 @@ __all__ = [
112
125
  "OIDCProvider",
113
126
  "KeyCloakProvider",
114
127
  "RepositoryModel",
128
+ "RestApiRepository",
115
129
  "SqlAlchemyRepository",
116
130
  "AuthenticationService",
131
+ "UserLifecycleManagement",
117
132
  "is_attrs",
118
133
  "is_pydantic",
119
134
  "LoggingConfigurator",
120
135
  "GunicornLogger",
121
136
  "logging_level_checker",
137
+ "Headers",
122
138
  "create_response_object",
123
139
  "verify_identity",
124
140
  "minor_version_gte",
@@ -128,8 +144,10 @@ __all__ = [
128
144
 
129
145
  # Conditionally add LDAP-related exports if available
130
146
  if _LDAP_AVAILABLE:
131
- __all__.extend([
132
- "LDAPConnector",
133
- "LDAPProvider",
134
- "ADProvider",
135
- ])
147
+ __all__.extend(
148
+ [
149
+ "LDAPConnector",
150
+ "LDAPProvider",
151
+ "ADProvider",
152
+ ]
153
+ )
@@ -1,5 +1,7 @@
1
+ from alpha.adapters.rest_api_unit_of_work import RestApiUnitOfWork
1
2
  from alpha.adapters.sqla_unit_of_work import SqlAlchemyUnitOfWork
2
3
 
3
4
  __all__ = [
5
+ "RestApiUnitOfWork",
4
6
  "SqlAlchemyUnitOfWork",
5
7
  ]
@@ -0,0 +1,105 @@
1
+ """Contains the REST API Unit of Work implementation."""
2
+
3
+ from typing import Any, TypeVar
4
+
5
+ import requests
6
+
7
+ from alpha.repositories.models.repository_model import RepositoryModel
8
+
9
+
10
+ UOW = TypeVar("UOW", bound="RestApiUnitOfWork")
11
+
12
+
13
+ class RestApiUnitOfWork:
14
+ """Unit of Work implementation for REST API interactions."""
15
+
16
+ def __init__(
17
+ self,
18
+ repos: list[RepositoryModel[Any]],
19
+ session: requests.sessions.Session | None = None,
20
+ ) -> None:
21
+ """Initialize the Unit of Work with repositories.
22
+
23
+ Parameters
24
+ ----------
25
+ repos : list[RepositoryModel]
26
+ The list of repository models to use.
27
+ session : requests.sessions.Session | None
28
+ The requests session (or compatible HTTP client, e.g., httpx) to
29
+ use for context management, by default None
30
+
31
+ Raises
32
+ ------
33
+ TypeError
34
+ If any repository does not implement its specified interface.
35
+ """
36
+ self._repositories = repos
37
+ self._session = session
38
+
39
+ def __enter__(self: UOW) -> UOW:
40
+ """Enter the REST API Unit of Work context.
41
+ Initializes a :class:`requests.sessions.Session` if one was not
42
+ provided and attaches the configured repositories as attributes on the
43
+ unit of work instance. Each repository is constructed using the shared
44
+ session and its associated configuration, and optionally validated
45
+ against a declared interface.
46
+
47
+ Returns
48
+ -------
49
+ UOW
50
+ The configured :class:`RestApiUnitOfWork` instance to be used
51
+ within the context manager.
52
+ """
53
+ self._session = self._session or requests.sessions.Session()
54
+
55
+ for repo in self._repositories:
56
+ name: str = repo.name
57
+ interface: Any = repo.interface
58
+ additional_config: dict[str, Any] = dict(
59
+ repo.additional_config or {}
60
+ )
61
+
62
+ self.__setattr__(
63
+ name,
64
+ repo.repository(
65
+ session=self._session,
66
+ default_model=repo.default_model,
67
+ **additional_config,
68
+ ),
69
+ )
70
+
71
+ if interface:
72
+ if not isinstance(getattr(self, name), interface):
73
+ raise TypeError(f"Repository for {name} has no interface")
74
+
75
+ return self
76
+
77
+ def __exit__(self, *args: Any) -> None:
78
+ """Finalize the Unit of Work context."""
79
+ if self._session:
80
+ self._session.close()
81
+
82
+ def commit(self) -> None:
83
+ raise NotImplementedError("RestApiUnitOfWork does not support commit")
84
+
85
+ def flush(self) -> None:
86
+ raise NotImplementedError("RestApiUnitOfWork does not support flush")
87
+
88
+ def rollback(self) -> None:
89
+ raise NotImplementedError(
90
+ "RestApiUnitOfWork does not support rollback"
91
+ )
92
+
93
+ def refresh(self, obj: object) -> None:
94
+ raise NotImplementedError("RestApiUnitOfWork does not support refresh")
95
+
96
+ @property
97
+ def session(self) -> requests.sessions.Session | None:
98
+ """Get the current session.
99
+
100
+ Returns
101
+ -------
102
+ requests.sessions.Session | None
103
+ The current session used for API interactions.
104
+ """
105
+ return self._session
@@ -14,7 +14,11 @@ UOW = TypeVar("UOW", bound="SqlAlchemyUnitOfWork")
14
14
  class SqlAlchemyUnitOfWork:
15
15
  """Unit of Work implementation for SQLAlchemy databases."""
16
16
 
17
- def __init__(self, db: SqlDatabase, repos: list[RepositoryModel]) -> None:
17
+ def __init__(
18
+ self,
19
+ db: SqlDatabase,
20
+ repos: list[RepositoryModel[Any]],
21
+ ) -> None:
18
22
  """Initialize the Unit of Work with a database and repositories.
19
23
 
20
24
  Parameters
@@ -28,9 +32,6 @@ class SqlAlchemyUnitOfWork:
28
32
  ------
29
33
  TypeError
30
34
  If the provided database is not a valid SqlDatabase instance.
31
- TypeError
32
- If the provided repositories list is empty or contains invalid
33
- models.
34
35
  """
35
36
  if not isinstance(db, SqlDatabase): # type: ignore
36
37
  raise TypeError("No valid database provided")
@@ -55,17 +56,16 @@ class SqlAlchemyUnitOfWork:
55
56
  self._session = self._db.get_session()
56
57
 
57
58
  for repo in self._repositories:
58
- session = self._session
59
- model = repo.default_model
60
-
61
59
  name: str = repo.name
62
- repository = repo.repository
63
60
  interface: Any = repo.interface
64
61
 
65
62
  self.__setattr__(
66
63
  name,
67
- repository(session=session, default_model=model), # type: ignore
64
+ repo.repository(
65
+ session=self._session, default_model=repo.default_model
66
+ ),
68
67
  )
68
+
69
69
  if interface:
70
70
  if not isinstance(getattr(self, name), interface):
71
71
  raise TypeError(f"Repository for {name} has no interface")
@@ -0,0 +1,35 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime, timezone
3
+ from typing import Sequence, cast
4
+ from uuid import UUID
5
+
6
+ from alpha.domain.models.base_model import BaseDomainModel, DomainModel
7
+ from alpha.domain.models.life_cycle_base import LifeCycleBase
8
+
9
+
10
+ @dataclass(kw_only=True)
11
+ class Group(LifeCycleBase, BaseDomainModel):
12
+ id: UUID | int | str | None = None
13
+ name: str | None = None
14
+ description: str | None = None
15
+ permissions: Sequence[str] | None = None
16
+ is_active: bool = True
17
+
18
+ def update(self, obj: DomainModel) -> DomainModel:
19
+ """Update the Group instance with data from another Group instance.
20
+
21
+ Parameters
22
+ ----------
23
+ obj
24
+ Group object to update from.
25
+ """
26
+ if not isinstance(obj, Group):
27
+ raise TypeError("Group.update expects a Group instance.")
28
+
29
+ self.name = obj.name
30
+ self.description = obj.description
31
+ self.permissions = obj.permissions
32
+ self.modified_at = datetime.now(tz=timezone.utc)
33
+ self.is_active = obj.is_active
34
+
35
+ return cast(DomainModel, self)
@@ -0,0 +1,130 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime, timezone
3
+ from enum import Enum, auto
4
+ from typing import Self, Sequence, cast
5
+ from uuid import UUID
6
+
7
+ from alpha.domain.models.base_model import BaseDomainModel, DomainModel
8
+ from alpha.domain.models.group import Group
9
+ from alpha.domain.models.life_cycle_base import LifeCycleBase
10
+
11
+ from alpha.providers.models.identity import Identity
12
+
13
+
14
+ class Role(Enum):
15
+ """Defines user roles with varying levels of permissions. The roles are
16
+ ordered from highest to lowest permissions. The comparison methods allow
17
+ for easy comparison of roles based on their hierarchy. The roles are
18
+ ordered on a scale from highest to lowest permissions.
19
+
20
+ Typical permissions are as follows:
21
+ - CREATE: Permission to create new content or data, but not modify existing
22
+ content.
23
+ - READ: Permission to read content or data.
24
+ - UPDATE: Permission to modify existing content or data, but not create new
25
+ content.
26
+ - DELETE: Permission to delete content or data.
27
+ - MANAGE_USERS: Permission to manage user accounts and permissions.
28
+ - MANAGE_SETTINGS: Permission to manage system settings and configurations.
29
+ - ALL: Permission to perform all actions, including user management and
30
+ system settings.
31
+
32
+ Roles:
33
+ - ADMIN: Role with permissions to manage users, content, and system
34
+ settings. Typically has the ALL permissions.
35
+ - SUPERUSER: Role with all permissions, including system settings and user
36
+ management. Typically has the ALL permissions, but may be used to denote a
37
+ special type of admin user with additional privileges or responsibilities.
38
+ - OWNER: Role with permissions to manage their own resources and users, but
39
+ not system settings. Typically has permissions similar to ADMIN, but
40
+ limited to their own scope of resources.
41
+ - MODERATOR: Role with permissions to manage content and users, but not
42
+ system settings. Typically has permissions to UPDATE and DELETE content,
43
+ and MANAGE_USERS, but not MANAGE_SETTINGS.
44
+ - EDITOR: Role with permissions to create and edit content, but not manage
45
+ users or settings. Typically has permissions to CREATE, READ, UPDATE, and
46
+ DELETE content, but not MANAGE_USERS or MANAGE_SETTINGS.
47
+ - USER: Default role with standard permissions. Typically has permissions
48
+ to CREATE, READ, and UPDATE their own content, but not DELETE content or
49
+ manage users or settings.
50
+ - VIEWER: Typical read-only role with limited permissions. Typically has
51
+ permission to READ content, but not CREATE, UPDATE, DELETE, or manage users
52
+ or settings.
53
+ """
54
+
55
+ ADMIN = auto()
56
+ SUPERUSER = auto()
57
+ OWNER = auto()
58
+ MODERATOR = auto()
59
+ EDITOR = auto()
60
+ USER = auto()
61
+ VIEWER = auto()
62
+
63
+ def __lt__(self, obj: Self) -> bool:
64
+ return self.value < obj.value
65
+
66
+ def __le__(self, obj: Self) -> bool:
67
+ return self.value <= obj.value
68
+
69
+ def __gt__(self, obj: Self) -> bool:
70
+ return self.value > obj.value
71
+
72
+ def __ge__(self, obj: Self) -> bool:
73
+ return self.value >= obj.value
74
+
75
+
76
+ @dataclass(kw_only=True)
77
+ class User(LifeCycleBase, BaseDomainModel):
78
+ id: UUID | int | str | None = None
79
+ username: str | None = None
80
+ password: str | None = None
81
+ role: str | Role | None = None
82
+ email: str | None = None
83
+ phone: str | None = None
84
+ display_name: str | None = None
85
+ permissions: Sequence[str] | None = None
86
+ groups: Sequence[str | Group] | None = None
87
+ is_active: bool = True
88
+ admin: bool = False
89
+
90
+ @classmethod
91
+ def from_identity(cls, identity: Identity) -> Self:
92
+ """Create a User instance from an Identity instance.
93
+
94
+ Parameters
95
+ ----------
96
+ identity
97
+ Identity object to convert.
98
+
99
+ Returns
100
+ -------
101
+ User instance created from the Identity.
102
+ """
103
+ return cls(
104
+ id=identity.subject,
105
+ username=identity.username,
106
+ email=identity.email,
107
+ display_name=identity.display_name,
108
+ )
109
+
110
+ def update(self, obj: DomainModel) -> DomainModel:
111
+ """Update the User instance with data from another User instance.
112
+
113
+ Parameters
114
+ ----------
115
+ obj
116
+ User object to update from.
117
+ """
118
+ if not isinstance(obj, User):
119
+ raise TypeError("User.update expects a User instance.")
120
+
121
+ self.username = obj.username
122
+ self.email = obj.email
123
+ self.phone = obj.phone
124
+ self.display_name = obj.display_name
125
+ self.permissions = obj.permissions
126
+ self.groups = obj.groups
127
+ self.modified_at = datetime.now(tz=timezone.utc)
128
+ self.is_active = obj.is_active
129
+ self.admin = obj.admin
130
+ return cast(DomainModel, self)
@@ -19,6 +19,10 @@ class NotFoundException(ClientErrorException):
19
19
  """Equivalent to HTTP code 404"""
20
20
 
21
21
 
22
+ class MethodNotAllowedException(ClientErrorException):
23
+ """Equivalent to HTTP code 405"""
24
+
25
+
22
26
  class NotAcceptableException(ClientErrorException):
23
27
  """Equivalent to HTTP code 406"""
24
28
 
@@ -55,11 +59,19 @@ class ServiceUnavailableException(ServerErrorException):
55
59
  """Equivalent to HTTP code 503"""
56
60
 
57
61
 
62
+ class GatewayTimeoutException(ServerErrorException):
63
+ """Equivalent to HTTP code 504"""
64
+
65
+
58
66
  # General Exceptions
59
67
  class MissingConfigurationException(Exception):
60
68
  """Raised when a required configuration is missing."""
61
69
 
62
70
 
71
+ class InvalidAttributeError(Exception):
72
+ """Raised when a required attribute is invalid."""
73
+
74
+
63
75
  class MissingDependencyException(Exception):
64
76
  """Raised when a required dependency is missing."""
65
77