python-ddd-framework 0.3.1__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.
Files changed (355) hide show
  1. python_ddd_framework/__init__.py +449 -0
  2. python_ddd_framework/application/__init__.py +6 -0
  3. python_ddd_framework/application/build_spec.py +37 -0
  4. python_ddd_framework/application/builder.py +260 -0
  5. python_ddd_framework/application/composition.py +219 -0
  6. python_ddd_framework/application/runtime.py +549 -0
  7. python_ddd_framework/application/runtime_hooks.py +327 -0
  8. python_ddd_framework/application/state.py +216 -0
  9. python_ddd_framework/application_services/__init__.py +54 -0
  10. python_ddd_framework/application_services/catalog.py +500 -0
  11. python_ddd_framework/application_services/contracts.py +190 -0
  12. python_ddd_framework/application_services/dispatcher.py +423 -0
  13. python_ddd_framework/application_services/errors.py +89 -0
  14. python_ddd_framework/application_services/execution.py +199 -0
  15. python_ddd_framework/application_services/interceptors.py +44 -0
  16. python_ddd_framework/application_services/invocation.py +431 -0
  17. python_ddd_framework/application_services/policies.py +46 -0
  18. python_ddd_framework/application_services/seeding.py +72 -0
  19. python_ddd_framework/application_services/signature.py +49 -0
  20. python_ddd_framework/application_services/validation.py +84 -0
  21. python_ddd_framework/auditing/__init__.py +12 -0
  22. python_ddd_framework/auditing/contracts.py +39 -0
  23. python_ddd_framework/auditing/control.py +48 -0
  24. python_ddd_framework/auditing/sqlalchemy/__init__.py +6 -0
  25. python_ddd_framework/auditing/sqlalchemy/migrations/0001_auditing.py +45 -0
  26. python_ddd_framework/auditing/sqlalchemy/migrations/__init__.py +1 -0
  27. python_ddd_framework/auditing/sqlalchemy/models.py +31 -0
  28. python_ddd_framework/auditing/sqlalchemy/module.py +32 -0
  29. python_ddd_framework/auditing/sqlalchemy/store.py +63 -0
  30. python_ddd_framework/authorization/__init__.py +38 -0
  31. python_ddd_framework/authorization/catalog.py +63 -0
  32. python_ddd_framework/authorization/contracts.py +241 -0
  33. python_ddd_framework/authorization/definition_discovery.py +81 -0
  34. python_ddd_framework/authorization/definitions.py +43 -0
  35. python_ddd_framework/authorization/errors.py +40 -0
  36. python_ddd_framework/background_execution/__init__.py +17 -0
  37. python_ddd_framework/background_execution/application.py +41 -0
  38. python_ddd_framework/background_execution/child.py +123 -0
  39. python_ddd_framework/background_execution/contracts.py +48 -0
  40. python_ddd_framework/background_execution/lifecycle.py +22 -0
  41. python_ddd_framework/background_execution/local.py +127 -0
  42. python_ddd_framework/background_execution/locks.py +33 -0
  43. python_ddd_framework/background_execution/management.py +17 -0
  44. python_ddd_framework/background_execution/module.py +10 -0
  45. python_ddd_framework/background_execution/permissions.py +14 -0
  46. python_ddd_framework/background_execution/processes.py +331 -0
  47. python_ddd_framework/background_jobs/__init__.py +31 -0
  48. python_ddd_framework/background_jobs/catalog.py +340 -0
  49. python_ddd_framework/background_jobs/contracts.py +208 -0
  50. python_ddd_framework/background_jobs/declaration.py +67 -0
  51. python_ddd_framework/background_jobs/errors.py +28 -0
  52. python_ddd_framework/background_jobs/execution.py +203 -0
  53. python_ddd_framework/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +21 -0
  54. python_ddd_framework/background_jobs/pgqueuer/__init__.py +6 -0
  55. python_ddd_framework/background_jobs/pgqueuer/enqueue.py +86 -0
  56. python_ddd_framework/background_jobs/pgqueuer/migrations/0001_pgqueuer_1_3_2.py +95 -0
  57. python_ddd_framework/background_jobs/pgqueuer/migrations/__init__.py +1 -0
  58. python_ddd_framework/background_jobs/pgqueuer/module.py +106 -0
  59. python_ddd_framework/background_jobs/pgqueuer/options.py +82 -0
  60. python_ddd_framework/background_jobs/pgqueuer/runtime.py +479 -0
  61. python_ddd_framework/background_jobs/pgqueuer/sql/pgqueuer_1_3_2_install.sql +118 -0
  62. python_ddd_framework/background_jobs/pgqueuer/supervision.py +201 -0
  63. python_ddd_framework/background_workers/__init__.py +22 -0
  64. python_ddd_framework/background_workers/catalog.py +118 -0
  65. python_ddd_framework/background_workers/contracts.py +152 -0
  66. python_ddd_framework/background_workers/errors.py +20 -0
  67. python_ddd_framework/background_workers/execution.py +133 -0
  68. python_ddd_framework/background_workers/runtime.py +219 -0
  69. python_ddd_framework/caching/__init__.py +11 -0
  70. python_ddd_framework/caching/catalog.py +73 -0
  71. python_ddd_framework/caching/contracts.py +73 -0
  72. python_ddd_framework/caching/errors.py +20 -0
  73. python_ddd_framework/cli/__init__.py +105 -0
  74. python_ddd_framework/cli/development.py +85 -0
  75. python_ddd_framework/cli/errors.py +5 -0
  76. python_ddd_framework/cli/inspection.py +96 -0
  77. python_ddd_framework/cli/project.py +69 -0
  78. python_ddd_framework/cli/runtime.py +56 -0
  79. python_ddd_framework/configuration/__init__.py +21 -0
  80. python_ddd_framework/configuration/composition.py +65 -0
  81. python_ddd_framework/configuration/contracts.py +68 -0
  82. python_ddd_framework/configuration/dotenv_source.py +51 -0
  83. python_ddd_framework/configuration/environment_source.py +38 -0
  84. python_ddd_framework/configuration/immutability.py +124 -0
  85. python_ddd_framework/configuration/input_shape.py +58 -0
  86. python_ddd_framework/configuration/merge.py +103 -0
  87. python_ddd_framework/configuration/root.py +215 -0
  88. python_ddd_framework/configuration/sources.py +519 -0
  89. python_ddd_framework/configuration/values.py +427 -0
  90. python_ddd_framework/configuration/yaml_source.py +51 -0
  91. python_ddd_framework/developer_kit/__init__.py +1 -0
  92. python_ddd_framework/developer_kit/generation.py +182 -0
  93. python_ddd_framework/developer_kit/project_metadata.py +45 -0
  94. python_ddd_framework/developer_kit/source.py +53 -0
  95. python_ddd_framework/developer_kit/templates/module/cookiecutter.json +1 -0
  96. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/README.md +70 -0
  97. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/__init__.py.jinja +1 -0
  98. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/__init__.py.jinja +1 -0
  99. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/cache.py.jinja +10 -0
  100. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/events.py.jinja +24 -0
  101. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration.py.jinja +41 -0
  102. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/module.py.jinja +20 -0
  103. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/options.py.jinja +7 -0
  104. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/orders.py.jinja +83 -0
  105. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/tasks.py.jinja +84 -0
  106. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/__init__.py.jinja +1 -0
  107. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/module.py.jinja +10 -0
  108. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/orders.py.jinja +35 -0
  109. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/__init__.py.jinja +1 -0
  110. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/module.py.jinja +10 -0
  111. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/orders.py.jinja +56 -0
  112. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repository.py.jinja +14 -0
  113. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding.py.jinja +19 -0
  114. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/settings.py.jinja +16 -0
  115. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/__init__.py.jinja +1 -0
  116. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/definitions.py.jinja +24 -0
  117. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/module.py.jinja +7 -0
  118. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions.py.jinja +15 -0
  119. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/__init__.py.jinja +1 -0
  120. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/files.py.jinja +73 -0
  121. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/module.py.jinja +25 -0
  122. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/realtime.py.jinja +36 -0
  123. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/__init__.py.jinja +1 -0
  124. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/migrations/__init__.py.jinja +1 -0
  125. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/__init__.py.jinja +7 -0
  126. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/orders.py.jinja +25 -0
  127. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/module.py.jinja +26 -0
  128. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/__init__.py.jinja +1 -0
  129. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/orders.py.jinja +49 -0
  130. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/test_domain.py.jinja +18 -0
  131. python_ddd_framework/developer_kit/templates/project/cookiecutter.json +6 -0
  132. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/.dockerignore +9 -0
  133. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/.gitignore +6 -0
  134. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/.python-version +1 -0
  135. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/AGENTS.md +38 -0
  136. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/Dockerfile +22 -0
  137. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/README.md +130 -0
  138. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/app.development.yaml.jinja +30 -0
  139. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/compose.dev.yaml.jinja +24 -0
  140. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/compose.production.yaml.jinja +20 -0
  141. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/architecture.md +84 -0
  142. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/development.md +185 -0
  143. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/pyproject.toml.jinja +28 -0
  144. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/src/host/__init__.py.jinja +1 -0
  145. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/src/host/main.py.jinja +47 -0
  146. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/src/host/module.py.jinja +37 -0
  147. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/tests/conftest.py.jinja +8 -0
  148. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/tests/host/test_http.py.jinja +57 -0
  149. python_ddd_framework/developer_kit/wiring.py +162 -0
  150. python_ddd_framework/diagnostics/__init__.py +23 -0
  151. python_ddd_framework/diagnostics/journal.py +174 -0
  152. python_ddd_framework/diagnostics/model.py +83 -0
  153. python_ddd_framework/diagnostics/source.py +25 -0
  154. python_ddd_framework/distributed_lock/__init__.py +6 -0
  155. python_ddd_framework/distributed_lock/contracts.py +20 -0
  156. python_ddd_framework/distributed_lock/options.py +19 -0
  157. python_ddd_framework/domain/__init__.py +13 -0
  158. python_ddd_framework/domain/aggregates.py +64 -0
  159. python_ddd_framework/domain/entities.py +33 -0
  160. python_ddd_framework/domain/errors.py +12 -0
  161. python_ddd_framework/domain/value_objects.py +31 -0
  162. python_ddd_framework/errors/__init__.py +75 -0
  163. python_ddd_framework/errors/base.py +13 -0
  164. python_ddd_framework/errors/business.py +53 -0
  165. python_ddd_framework/errors/configuration.py +119 -0
  166. python_ddd_framework/errors/diagnostics.py +12 -0
  167. python_ddd_framework/errors/lifecycle.py +164 -0
  168. python_ddd_framework/errors/modularity.py +100 -0
  169. python_ddd_framework/errors/services.py +100 -0
  170. python_ddd_framework/events/__init__.py +25 -0
  171. python_ddd_framework/events/aggregate.py +39 -0
  172. python_ddd_framework/events/catalog.py +136 -0
  173. python_ddd_framework/events/contracts.py +39 -0
  174. python_ddd_framework/events/contribution.py +128 -0
  175. python_ddd_framework/events/discovery.py +105 -0
  176. python_ddd_framework/events/errors.py +48 -0
  177. python_ddd_framework/events/runtime.py +192 -0
  178. python_ddd_framework/fastapi/__init__.py +49 -0
  179. python_ddd_framework/fastapi/action.py +175 -0
  180. python_ddd_framework/fastapi/adapter.py +391 -0
  181. python_ddd_framework/fastapi/application_services.py +654 -0
  182. python_ddd_framework/fastapi/background.py +42 -0
  183. python_ddd_framework/fastapi/contracts.py +275 -0
  184. python_ddd_framework/fastapi/errors.py +32 -0
  185. python_ddd_framework/fastapi/filters.py +111 -0
  186. python_ddd_framework/fastapi/health.py +44 -0
  187. python_ddd_framework/fastapi/http.py +72 -0
  188. python_ddd_framework/fastapi/http_router.py +216 -0
  189. python_ddd_framework/fastapi/manual_action.py +105 -0
  190. python_ddd_framework/fastapi/middleware.py +93 -0
  191. python_ddd_framework/fastapi/parameters.py +97 -0
  192. python_ddd_framework/fastapi/realtime/__init__.py +13 -0
  193. python_ddd_framework/fastapi/realtime/authentication.py +181 -0
  194. python_ddd_framework/fastapi/realtime/connection.py +166 -0
  195. python_ddd_framework/fastapi/realtime/module.py +41 -0
  196. python_ddd_framework/fastapi/realtime/options.py +50 -0
  197. python_ddd_framework/fastapi/realtime/runtime.py +423 -0
  198. python_ddd_framework/fastapi/request_context.py +247 -0
  199. python_ddd_framework/fastapi/route_integrity.py +88 -0
  200. python_ddd_framework/fastapi/routing.py +424 -0
  201. python_ddd_framework/fastapi/server.py +182 -0
  202. python_ddd_framework/fastapi/settings.py +40 -0
  203. python_ddd_framework/fastapi/tracing.py +62 -0
  204. python_ddd_framework/fastapi/transfer.py +128 -0
  205. python_ddd_framework/fastapi/upload_limits.py +63 -0
  206. python_ddd_framework/fastapi/uploads.py +94 -0
  207. python_ddd_framework/hosted_services/__init__.py +29 -0
  208. python_ddd_framework/hosted_services/bridge.py +420 -0
  209. python_ddd_framework/hosted_services/catalog.py +265 -0
  210. python_ddd_framework/hosted_services/contracts.py +55 -0
  211. python_ddd_framework/hosted_services/errors.py +53 -0
  212. python_ddd_framework/hosted_services/options.py +17 -0
  213. python_ddd_framework/hosted_services/runtime.py +267 -0
  214. python_ddd_framework/hosted_services/state.py +40 -0
  215. python_ddd_framework/hosting/__init__.py +4 -0
  216. python_ddd_framework/hosting/instance.py +40 -0
  217. python_ddd_framework/identity/__init__.py +73 -0
  218. python_ddd_framework/identity/application.py +287 -0
  219. python_ddd_framework/identity/contracts.py +283 -0
  220. python_ddd_framework/identity/errors.py +28 -0
  221. python_ddd_framework/identity/http_api.py +90 -0
  222. python_ddd_framework/identity/module.py +49 -0
  223. python_ddd_framework/identity/passwords.py +34 -0
  224. python_ddd_framework/identity/permissions.py +14 -0
  225. python_ddd_framework/identity/services.py +108 -0
  226. python_ddd_framework/identity/sqlalchemy/__init__.py +5 -0
  227. python_ddd_framework/identity/sqlalchemy/migrations/0001_identity.py +143 -0
  228. python_ddd_framework/identity/sqlalchemy/migrations/0002_physical_delete.py +42 -0
  229. python_ddd_framework/identity/sqlalchemy/migrations/__init__.py +1 -0
  230. python_ddd_framework/identity/sqlalchemy/models.py +85 -0
  231. python_ddd_framework/identity/sqlalchemy/module.py +62 -0
  232. python_ddd_framework/identity/sqlalchemy/stores.py +526 -0
  233. python_ddd_framework/identity/tokens.py +106 -0
  234. python_ddd_framework/invocation/__init__.py +3 -0
  235. python_ddd_framework/invocation/callables.py +171 -0
  236. python_ddd_framework/invocation/contracts.py +33 -0
  237. python_ddd_framework/invocation/entries.py +54 -0
  238. python_ddd_framework/invocation/function_runtime.py +48 -0
  239. python_ddd_framework/invocation/interception.py +193 -0
  240. python_ddd_framework/lifecycle/__init__.py +25 -0
  241. python_ddd_framework/lifecycle/composition.py +35 -0
  242. python_ddd_framework/lifecycle/context.py +31 -0
  243. python_ddd_framework/lifecycle/runtime.py +43 -0
  244. python_ddd_framework/lifecycle/state.py +20 -0
  245. python_ddd_framework/modularity/__init__.py +15 -0
  246. python_ddd_framework/modularity/contracts.py +91 -0
  247. python_ddd_framework/modularity/discovery.py +157 -0
  248. python_ddd_framework/modularity/graph.py +334 -0
  249. python_ddd_framework/modularity/registry.py +100 -0
  250. python_ddd_framework/modularity/selection.py +16 -0
  251. python_ddd_framework/notifications/__init__.py +19 -0
  252. python_ddd_framework/notifications/catalog.py +46 -0
  253. python_ddd_framework/notifications/contracts.py +107 -0
  254. python_ddd_framework/observability/__init__.py +5 -0
  255. python_ddd_framework/observability/context.py +43 -0
  256. python_ddd_framework/observability/export.py +73 -0
  257. python_ddd_framework/observability/formatting.py +89 -0
  258. python_ddd_framework/observability/logging.py +118 -0
  259. python_ddd_framework/observability/options.py +45 -0
  260. python_ddd_framework/observability/tracing.py +80 -0
  261. python_ddd_framework/options/__init__.py +19 -0
  262. python_ddd_framework/options/aliases.py +331 -0
  263. python_ddd_framework/options/contribution.py +62 -0
  264. python_ddd_framework/options/immutability.py +48 -0
  265. python_ddd_framework/options/input_keys.py +214 -0
  266. python_ddd_framework/options/issues.py +335 -0
  267. python_ddd_framework/options/models.py +114 -0
  268. python_ddd_framework/options/registry.py +280 -0
  269. python_ddd_framework/options/schema.py +488 -0
  270. python_ddd_framework/options/validation.py +120 -0
  271. python_ddd_framework/py.typed +0 -0
  272. python_ddd_framework/realtime/__init__.py +12 -0
  273. python_ddd_framework/realtime/contracts.py +28 -0
  274. python_ddd_framework/realtime/diagnostics.py +37 -0
  275. python_ddd_framework/realtime/messages.py +113 -0
  276. python_ddd_framework/redis/__init__.py +6 -0
  277. python_ddd_framework/redis/distributed_lock.py +212 -0
  278. python_ddd_framework/redis/lease_lock.py +36 -0
  279. python_ddd_framework/redis/module.py +50 -0
  280. python_ddd_framework/redis/notification_runtime.py +182 -0
  281. python_ddd_framework/redis/notifications.py +25 -0
  282. python_ddd_framework/redis/options.py +35 -0
  283. python_ddd_framework/redis/runtime.py +116 -0
  284. python_ddd_framework/services/__init__.py +41 -0
  285. python_ddd_framework/services/application_bindings.py +221 -0
  286. python_ddd_framework/services/arbitration.py +381 -0
  287. python_ddd_framework/services/binding.py +102 -0
  288. python_ddd_framework/services/contribution.py +438 -0
  289. python_ddd_framework/services/convention.py +301 -0
  290. python_ddd_framework/services/convention_contracts.py +101 -0
  291. python_ddd_framework/services/exposure.py +23 -0
  292. python_ddd_framework/services/fixed_lifetime.py +52 -0
  293. python_ddd_framework/services/framework_provider.py +178 -0
  294. python_ddd_framework/services/native_graph.py +201 -0
  295. python_ddd_framework/services/provider.py +204 -0
  296. python_ddd_framework/services/registration.py +101 -0
  297. python_ddd_framework/services/repository.py +38 -0
  298. python_ddd_framework/services/runtime.py +302 -0
  299. python_ddd_framework/settings/__init__.py +30 -0
  300. python_ddd_framework/settings/application.py +95 -0
  301. python_ddd_framework/settings/binding.py +33 -0
  302. python_ddd_framework/settings/catalog.py +121 -0
  303. python_ddd_framework/settings/changes.py +14 -0
  304. python_ddd_framework/settings/contracts.py +115 -0
  305. python_ddd_framework/settings/definition_discovery.py +71 -0
  306. python_ddd_framework/settings/definitions.py +41 -0
  307. python_ddd_framework/settings/errors.py +22 -0
  308. python_ddd_framework/settings/handlers.py +43 -0
  309. python_ddd_framework/settings/management.py +57 -0
  310. python_ddd_framework/settings/manager.py +89 -0
  311. python_ddd_framework/settings/module.py +10 -0
  312. python_ddd_framework/settings/notifications.py +39 -0
  313. python_ddd_framework/settings/permissions.py +14 -0
  314. python_ddd_framework/settings/provider.py +74 -0
  315. python_ddd_framework/settings/refresh.py +140 -0
  316. python_ddd_framework/settings/refresh_module.py +45 -0
  317. python_ddd_framework/settings/sqlalchemy/__init__.py +5 -0
  318. python_ddd_framework/settings/sqlalchemy/migrations/0001_settings.py +31 -0
  319. python_ddd_framework/settings/sqlalchemy/migrations/0002_physical_delete.py +32 -0
  320. python_ddd_framework/settings/sqlalchemy/migrations/0003_version_tokens.py +39 -0
  321. python_ddd_framework/settings/sqlalchemy/migrations/__init__.py +1 -0
  322. python_ddd_framework/settings/sqlalchemy/models.py +36 -0
  323. python_ddd_framework/settings/sqlalchemy/module.py +36 -0
  324. python_ddd_framework/settings/sqlalchemy/store.py +86 -0
  325. python_ddd_framework/settings/store.py +16 -0
  326. python_ddd_framework/settings/values.py +39 -0
  327. python_ddd_framework/sqlalchemy/__init__.py +54 -0
  328. python_ddd_framework/sqlalchemy/alembic_runtime/__init__.py +1 -0
  329. python_ddd_framework/sqlalchemy/alembic_runtime/env.py +33 -0
  330. python_ddd_framework/sqlalchemy/alembic_runtime/script.py.mako +14 -0
  331. python_ddd_framework/sqlalchemy/auditing.py +48 -0
  332. python_ddd_framework/sqlalchemy/errors.py +85 -0
  333. python_ddd_framework/sqlalchemy/metadata.py +576 -0
  334. python_ddd_framework/sqlalchemy/migration.py +378 -0
  335. python_ddd_framework/sqlalchemy/migration_options.py +56 -0
  336. python_ddd_framework/sqlalchemy/module.py +51 -0
  337. python_ddd_framework/sqlalchemy/module_migration.py +151 -0
  338. python_ddd_framework/sqlalchemy/options.py +66 -0
  339. python_ddd_framework/sqlalchemy/repository.py +33 -0
  340. python_ddd_framework/sqlalchemy/runtime.py +85 -0
  341. python_ddd_framework/sqlalchemy/session_provider.py +45 -0
  342. python_ddd_framework/sqlalchemy/unit_of_work.py +97 -0
  343. python_ddd_framework/testing/__init__.py +5 -0
  344. python_ddd_framework/testing/runtime.py +112 -0
  345. python_ddd_framework/unit_of_work/__init__.py +14 -0
  346. python_ddd_framework/unit_of_work/contracts.py +223 -0
  347. python_ddd_framework/unit_of_work/errors.py +27 -0
  348. python_ddd_framework/unit_of_work/manager.py +210 -0
  349. python_ddd_framework/unit_of_work/options.py +92 -0
  350. python_ddd_framework-0.3.1.dist-info/METADATA +379 -0
  351. python_ddd_framework-0.3.1.dist-info/RECORD +355 -0
  352. python_ddd_framework-0.3.1.dist-info/WHEEL +4 -0
  353. python_ddd_framework-0.3.1.dist-info/entry_points.txt +9 -0
  354. python_ddd_framework-0.3.1.dist-info/licenses/LICENSE +7 -0
  355. python_ddd_framework-0.3.1.dist-info/licenses/src/python_ddd_framework/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +21 -0
@@ -0,0 +1,49 @@
1
+ """Provider-neutral Identity Module 只注册正式 contract 与 security services。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dishka import Provider, Scope
6
+
7
+ from ..authorization import AccessTokenAuthenticator, PermissionChecker
8
+ from ..lifecycle import ConfigureContext
9
+ from ..modularity import AppModule
10
+ from .contracts import (
11
+ IdentityOptions,
12
+ )
13
+ from .passwords import PasswordHasher
14
+ from .services import (
15
+ IDENTITY_PERMISSION_CACHE,
16
+ IdentityAuthenticator,
17
+ IdentityPermissionChecker,
18
+ )
19
+ from .tokens import JwtTokenService
20
+
21
+
22
+ class IdentityModule(AppModule):
23
+ scan_packages = (
24
+ "python_ddd_framework.identity.application",
25
+ "python_ddd_framework.identity.permissions",
26
+ )
27
+
28
+ cache_definitions = (IDENTITY_PERMISSION_CACHE,)
29
+
30
+ def configure(self, context: ConfigureContext) -> None:
31
+ context.configure(
32
+ IdentityOptions,
33
+ section="identity",
34
+ secret_paths=frozenset({"keys", "seed_admin_password"}),
35
+ )
36
+ provider = Provider()
37
+ provider.provide(PasswordHasher, provides=PasswordHasher, scope=Scope.APP)
38
+ provider.provide(JwtTokenService, provides=JwtTokenService, scope=Scope.APP)
39
+ provider.provide(
40
+ IdentityAuthenticator,
41
+ provides=AccessTokenAuthenticator,
42
+ scope=Scope.APP,
43
+ )
44
+ provider.provide(
45
+ IdentityPermissionChecker,
46
+ provides=PermissionChecker,
47
+ scope=Scope.APP,
48
+ )
49
+ context.services.contribute(provider, reason="Identity security services")
@@ -0,0 +1,34 @@
1
+ """pwdlib Argon2id boundary;昂贵 hash/verify 不阻塞 asyncio event loop。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+
7
+ from pwdlib import PasswordHash
8
+
9
+ _DUMMY_HASH = (
10
+ "$argon2id$v=19$m=65536,t=3,p=4$VyPuPaiooyVPpvuUem+SEA$"
11
+ "FGOxAPGfz6q2biOigVe+t5G92Au3TgjT/eOEKxT6+Mk"
12
+ )
13
+
14
+
15
+ class PasswordHasher:
16
+ __slots__ = ("_password_hash",)
17
+
18
+ def __init__(self) -> None:
19
+ self._password_hash = PasswordHash.recommended()
20
+
21
+ async def hash(self, password: str) -> str:
22
+ return await asyncio.to_thread(self._password_hash.hash, password)
23
+
24
+ async def verify(self, password: str, password_hash: str | None) -> tuple[bool, str | None]:
25
+ selected_hash = password_hash if password_hash is not None else _DUMMY_HASH
26
+ try:
27
+ return await asyncio.to_thread(
28
+ self._password_hash.verify_and_update,
29
+ password,
30
+ selected_hash,
31
+ )
32
+ except Exception:
33
+ # 持久化 hash 损坏与错误密码共享失败语义,且绝不回显 hash/parser 文本。
34
+ return False, None
@@ -0,0 +1,14 @@
1
+ """identity 权限定义;持久化名称继续来自所属正式契约。"""
2
+
3
+ from ..authorization import (
4
+ PermissionDefinition,
5
+ PermissionDefinitionContext,
6
+ PermissionDefinitionProvider,
7
+ )
8
+ from .contracts import IDENTITY_ROLES_MANAGE, IDENTITY_USERS_MANAGE
9
+
10
+
11
+ class IdentityPermissionDefinitionProvider(PermissionDefinitionProvider):
12
+ def define(self, context: PermissionDefinitionContext) -> None:
13
+ context.add(PermissionDefinition(IDENTITY_USERS_MANAGE, "Manage identity users"))
14
+ context.add(PermissionDefinition(IDENTITY_ROLES_MANAGE, "Manage identity roles"))
@@ -0,0 +1,108 @@
1
+ """Access token authentication 与 version-bound permission cache orchestration。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from datetime import UTC, datetime, timedelta
7
+
8
+ from dishka import AsyncContainer
9
+ from dishka.exceptions import NoFactoryError
10
+ from pydantic import BaseModel, ConfigDict
11
+
12
+ from ..authorization import CurrentUser, PermissionCatalog, PermissionChecker, PermissionName
13
+ from ..caching import CacheDefinition, DistributedCache, DistributedCacheError
14
+ from .contracts import IdentityAccessStore
15
+ from .errors import InvalidCredentialsError
16
+ from .tokens import JwtTokenService
17
+
18
+ _LOGGER = logging.getLogger(__name__)
19
+
20
+
21
+ class PermissionCacheEntry(BaseModel):
22
+ model_config = ConfigDict(frozen=True)
23
+
24
+ permissions: tuple[str, ...]
25
+
26
+
27
+ IDENTITY_PERMISSION_CACHE = CacheDefinition(
28
+ "identity.permissions",
29
+ PermissionCacheEntry,
30
+ ttl=timedelta(minutes=5),
31
+ )
32
+
33
+
34
+ class IdentityAuthenticator:
35
+ __slots__ = ("_access", "_tokens")
36
+
37
+ def __init__(self, tokens: JwtTokenService, access: IdentityAccessStore) -> None:
38
+ self._tokens = tokens
39
+ self._access = access
40
+
41
+ async def authenticate_access_token(self, token: str) -> CurrentUser:
42
+ claims = self._tokens.verify(token)
43
+ current_user = await self._access.load_current_user(
44
+ claims.user_id,
45
+ claims.session_id,
46
+ datetime.now(UTC),
47
+ )
48
+ if current_user is None:
49
+ raise InvalidCredentialsError
50
+ return current_user
51
+
52
+
53
+ class IdentityPermissionChecker(PermissionChecker):
54
+ __slots__ = ("_access", "_catalog", "_container")
55
+
56
+ def __init__(
57
+ self,
58
+ access: IdentityAccessStore,
59
+ catalog: PermissionCatalog,
60
+ container: AsyncContainer,
61
+ ) -> None:
62
+ self._access = access
63
+ self._catalog = catalog
64
+ self._container = container
65
+
66
+ async def is_granted(self, user: CurrentUser, permission: PermissionName) -> bool:
67
+ if not user.is_authenticated or permission not in self._catalog:
68
+ return False
69
+ cache = await self._optional_cache()
70
+ cache_key = _permission_cache_key(user, self._catalog.fingerprint)
71
+ if cache is not None:
72
+ try:
73
+ cached = await cache.get(IDENTITY_PERMISSION_CACHE, cache_key)
74
+ except DistributedCacheError:
75
+ cached = None
76
+ _LOGGER.warning(
77
+ "permission cache read failed; falling back to authoritative database",
78
+ extra={"cache": IDENTITY_PERMISSION_CACHE.name},
79
+ )
80
+ if cached is not None:
81
+ return permission.value in cached.permissions
82
+
83
+ permissions = await self._access.permissions_for_roles(user.role_versions)
84
+ if cache is not None:
85
+ try:
86
+ await cache.set(
87
+ IDENTITY_PERMISSION_CACHE,
88
+ cache_key,
89
+ PermissionCacheEntry(permissions=tuple(sorted(permissions))),
90
+ )
91
+ except DistributedCacheError:
92
+ _LOGGER.warning(
93
+ "permission cache write failed; database result remains authoritative",
94
+ extra={"cache": IDENTITY_PERMISSION_CACHE.name},
95
+ )
96
+ return permission.value in permissions
97
+
98
+ async def _optional_cache(self) -> DistributedCache | None:
99
+ try:
100
+ return await self._container.get(DistributedCache)
101
+ except NoFactoryError:
102
+ return None
103
+
104
+
105
+ def _permission_cache_key(user: CurrentUser, catalog_fingerprint: str) -> str:
106
+ role_versions = ",".join(f"{item.role_id.hex}.{item.version}" for item in user.role_versions)
107
+ assert user.user_id is not None
108
+ return f"{catalog_fingerprint}:{user.user_id.hex}:{user.permission_version}:{role_versions}"
@@ -0,0 +1,5 @@
1
+ """Identity 的 SQLAlchemy/PostgreSQL provider。"""
2
+
3
+ from .module import SqlAlchemyIdentityModule
4
+
5
+ __all__ = ("SqlAlchemyIdentityModule",)
@@ -0,0 +1,143 @@
1
+ """Create the independent framework identity migration branch."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import sqlalchemy as sa
8
+ from alembic import op
9
+
10
+ revision = "identity_0001"
11
+ down_revision = None
12
+ branch_labels = ("identity",)
13
+ depends_on = None
14
+
15
+
16
+ def _audit_columns() -> tuple[sa.Column[Any], ...]:
17
+ return (
18
+ sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
19
+ sa.Column("created_by", sa.Uuid(), nullable=True),
20
+ sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True),
21
+ sa.Column("updated_by", sa.Uuid(), nullable=True),
22
+ )
23
+
24
+
25
+ def _soft_delete_columns() -> tuple[sa.Column[Any], ...]:
26
+ return (
27
+ sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
28
+ sa.Column("deleted_by", sa.Uuid(), nullable=True),
29
+ )
30
+
31
+
32
+ def upgrade() -> None:
33
+ op.create_table(
34
+ "identity_users",
35
+ sa.Column("user_id", sa.Uuid(), nullable=False),
36
+ sa.Column("username", sa.String(length=120), nullable=False),
37
+ sa.Column("normalized_username", sa.String(length=120), nullable=False),
38
+ sa.Column("password_hash", sa.String(length=512), nullable=False),
39
+ sa.Column("is_active", sa.Boolean(), nullable=False),
40
+ sa.Column("failed_access_count", sa.Integer(), nullable=False),
41
+ sa.Column("lockout_end", sa.DateTime(timezone=True), nullable=True),
42
+ sa.Column("permission_version", sa.BigInteger(), nullable=False),
43
+ *_audit_columns(),
44
+ *_soft_delete_columns(),
45
+ sa.PrimaryKeyConstraint("user_id"),
46
+ )
47
+ op.create_index(
48
+ "ix_identity_users_normalized_username",
49
+ "identity_users",
50
+ ["normalized_username"],
51
+ unique=True,
52
+ )
53
+ op.create_table(
54
+ "identity_roles",
55
+ sa.Column("role_id", sa.Uuid(), nullable=False),
56
+ sa.Column("name", sa.String(length=120), nullable=False),
57
+ sa.Column("normalized_name", sa.String(length=120), nullable=False),
58
+ sa.Column("permission_version", sa.BigInteger(), nullable=False),
59
+ *_audit_columns(),
60
+ *_soft_delete_columns(),
61
+ sa.PrimaryKeyConstraint("role_id"),
62
+ )
63
+ op.create_index(
64
+ "ix_identity_roles_normalized_name",
65
+ "identity_roles",
66
+ ["normalized_name"],
67
+ unique=True,
68
+ )
69
+ op.create_table(
70
+ "identity_user_roles",
71
+ sa.Column("user_id", sa.Uuid(), nullable=False),
72
+ sa.Column("role_id", sa.Uuid(), nullable=False),
73
+ *_audit_columns(),
74
+ sa.ForeignKeyConstraint(["role_id"], ["identity_roles.role_id"], ondelete="CASCADE"),
75
+ sa.ForeignKeyConstraint(["user_id"], ["identity_users.user_id"], ondelete="CASCADE"),
76
+ sa.PrimaryKeyConstraint("user_id", "role_id"),
77
+ )
78
+ op.create_table(
79
+ "identity_role_permissions",
80
+ sa.Column("role_id", sa.Uuid(), nullable=False),
81
+ sa.Column("permission_name", sa.String(length=200), nullable=False),
82
+ *_audit_columns(),
83
+ sa.ForeignKeyConstraint(["role_id"], ["identity_roles.role_id"], ondelete="CASCADE"),
84
+ sa.PrimaryKeyConstraint("role_id", "permission_name"),
85
+ )
86
+ op.create_table(
87
+ "identity_refresh_sessions",
88
+ sa.Column("session_id", sa.Uuid(), nullable=False),
89
+ sa.Column("user_id", sa.Uuid(), nullable=False),
90
+ sa.Column("family_id", sa.Uuid(), nullable=False),
91
+ sa.Column("token_hash", sa.String(length=64), nullable=False),
92
+ sa.Column("family_created_at", sa.DateTime(timezone=True), nullable=False),
93
+ sa.Column("last_used_at", sa.DateTime(timezone=True), nullable=False),
94
+ sa.Column("revoked_at", sa.DateTime(timezone=True), nullable=True),
95
+ sa.Column("replaced_by_session_id", sa.Uuid(), nullable=True),
96
+ *_audit_columns(),
97
+ sa.ForeignKeyConstraint(["user_id"], ["identity_users.user_id"], ondelete="CASCADE"),
98
+ sa.PrimaryKeyConstraint("session_id"),
99
+ )
100
+ op.create_index(
101
+ "ix_identity_refresh_sessions_user_id",
102
+ "identity_refresh_sessions",
103
+ ["user_id"],
104
+ )
105
+ op.create_index(
106
+ "ix_identity_refresh_sessions_family_id",
107
+ "identity_refresh_sessions",
108
+ ["family_id"],
109
+ )
110
+ op.create_index(
111
+ "ix_identity_refresh_sessions_token_hash",
112
+ "identity_refresh_sessions",
113
+ ["token_hash"],
114
+ unique=True,
115
+ )
116
+
117
+
118
+ def downgrade() -> None:
119
+ op.drop_index(
120
+ "ix_identity_refresh_sessions_token_hash",
121
+ table_name="identity_refresh_sessions",
122
+ )
123
+ op.drop_index(
124
+ "ix_identity_refresh_sessions_family_id",
125
+ table_name="identity_refresh_sessions",
126
+ )
127
+ op.drop_index(
128
+ "ix_identity_refresh_sessions_user_id",
129
+ table_name="identity_refresh_sessions",
130
+ )
131
+ op.drop_table("identity_refresh_sessions")
132
+ op.drop_table("identity_role_permissions")
133
+ op.drop_table("identity_user_roles")
134
+ op.drop_index(
135
+ "ix_identity_roles_normalized_name",
136
+ table_name="identity_roles",
137
+ )
138
+ op.drop_table("identity_roles")
139
+ op.drop_index(
140
+ "ix_identity_users_normalized_username",
141
+ table_name="identity_users",
142
+ )
143
+ op.drop_table("identity_users")
@@ -0,0 +1,42 @@
1
+ """退出软删除,升级只接受已经人工处理完历史软删除记录的数据库。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sqlalchemy as sa
6
+ from alembic import op
7
+
8
+ from python_ddd_framework.sqlalchemy.errors import SoftDeleteMigrationBlockedError
9
+
10
+ revision = "identity_0002"
11
+ down_revision = "identity_0001"
12
+ branch_labels = None
13
+ depends_on = None
14
+
15
+
16
+ def upgrade() -> None:
17
+ connection = op.get_bind()
18
+ tables = ("identity_users", "identity_roles")
19
+ # 检查和 DDL 共享事务锁,旧进程不能在检查后插入新的软删除记录。
20
+ for name in tables:
21
+ connection.execute(sa.text(f"LOCK TABLE {name} IN ACCESS EXCLUSIVE MODE"))
22
+ affected = tuple(
23
+ (
24
+ name,
25
+ int(
26
+ connection.scalar(
27
+ sa.text(f"SELECT count(*) FROM {name} WHERE deleted_at IS NOT NULL")
28
+ )
29
+ or 0
30
+ ),
31
+ )
32
+ for name in tables
33
+ )
34
+ if any(count for _, count in affected):
35
+ raise SoftDeleteMigrationBlockedError(tuple(item for item in affected if item[1]))
36
+ for name in tables:
37
+ op.drop_column(name, "deleted_at")
38
+ op.drop_column(name, "deleted_by")
39
+
40
+
41
+ def downgrade() -> None:
42
+ raise RuntimeError("physical deletion cannot restore historical deleted records")
@@ -0,0 +1 @@
1
+ """Framework identity Alembic branch。"""
@@ -0,0 +1,85 @@
1
+ """Identity rows 与独立 metadata owner。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+ from uuid import UUID
7
+
8
+ from sqlalchemy import BigInteger, Boolean, DateTime, ForeignKey, Integer, String, Uuid
9
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
10
+
11
+ from ...sqlalchemy import AuditedEntityMixin
12
+
13
+
14
+ class IdentityBase(DeclarativeBase):
15
+ pass
16
+
17
+
18
+ class UserRow(AuditedEntityMixin, IdentityBase):
19
+ __tablename__ = "identity_users"
20
+
21
+ user_id: Mapped[UUID] = mapped_column(Uuid, primary_key=True)
22
+ username: Mapped[str] = mapped_column(String(120), nullable=False)
23
+ normalized_username: Mapped[str] = mapped_column(
24
+ String(120), nullable=False, unique=True, index=True
25
+ )
26
+ password_hash: Mapped[str] = mapped_column(String(512), nullable=False)
27
+ is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
28
+ failed_access_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
29
+ lockout_end: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
30
+ permission_version: Mapped[int] = mapped_column(BigInteger, nullable=False, default=0)
31
+
32
+
33
+ class RoleRow(AuditedEntityMixin, IdentityBase):
34
+ __tablename__ = "identity_roles"
35
+
36
+ role_id: Mapped[UUID] = mapped_column(Uuid, primary_key=True)
37
+ name: Mapped[str] = mapped_column(String(120), nullable=False)
38
+ normalized_name: Mapped[str] = mapped_column(
39
+ String(120), nullable=False, unique=True, index=True
40
+ )
41
+ permission_version: Mapped[int] = mapped_column(BigInteger, nullable=False, default=0)
42
+
43
+
44
+ class UserRoleRow(AuditedEntityMixin, IdentityBase):
45
+ __tablename__ = "identity_user_roles"
46
+
47
+ user_id: Mapped[UUID] = mapped_column(
48
+ Uuid,
49
+ ForeignKey("identity_users.user_id", ondelete="CASCADE"),
50
+ primary_key=True,
51
+ )
52
+ role_id: Mapped[UUID] = mapped_column(
53
+ Uuid,
54
+ ForeignKey("identity_roles.role_id", ondelete="CASCADE"),
55
+ primary_key=True,
56
+ )
57
+
58
+
59
+ class RolePermissionRow(AuditedEntityMixin, IdentityBase):
60
+ __tablename__ = "identity_role_permissions"
61
+
62
+ role_id: Mapped[UUID] = mapped_column(
63
+ Uuid,
64
+ ForeignKey("identity_roles.role_id", ondelete="CASCADE"),
65
+ primary_key=True,
66
+ )
67
+ permission_name: Mapped[str] = mapped_column(String(200), primary_key=True)
68
+
69
+
70
+ class RefreshSessionRow(AuditedEntityMixin, IdentityBase):
71
+ __tablename__ = "identity_refresh_sessions"
72
+
73
+ session_id: Mapped[UUID] = mapped_column(Uuid, primary_key=True)
74
+ user_id: Mapped[UUID] = mapped_column(
75
+ Uuid,
76
+ ForeignKey("identity_users.user_id", ondelete="CASCADE"),
77
+ nullable=False,
78
+ index=True,
79
+ )
80
+ family_id: Mapped[UUID] = mapped_column(Uuid, nullable=False, index=True)
81
+ token_hash: Mapped[str] = mapped_column(String(64), nullable=False, unique=True, index=True)
82
+ family_created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
83
+ last_used_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
84
+ revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
85
+ replaced_by_session_id: Mapped[UUID | None] = mapped_column(Uuid, nullable=True)
@@ -0,0 +1,62 @@
1
+ """Host 显式选择的 Identity SQLAlchemy provider Module。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dishka import Provider, Scope
6
+
7
+ from ...lifecycle import ConfigureContext
8
+ from ...modularity import AppModule
9
+ from ...sqlalchemy import (
10
+ SqlAlchemyModelRegistration,
11
+ SqlAlchemyPersistenceModule,
12
+ contributes_sqlalchemy_models,
13
+ )
14
+ from ..contracts import (
15
+ IdentityAccessStore,
16
+ IdentityLoginStore,
17
+ IdentityRepository,
18
+ RefreshSessionStore,
19
+ )
20
+ from ..module import IdentityModule
21
+ from .models import IdentityBase
22
+ from .stores import (
23
+ SqlAlchemyIdentityAccessStore,
24
+ SqlAlchemyIdentityLoginStore,
25
+ SqlAlchemyIdentityRepository,
26
+ SqlAlchemyRefreshSessionStore,
27
+ )
28
+
29
+
30
+ @contributes_sqlalchemy_models(
31
+ SqlAlchemyModelRegistration(
32
+ metadata=IdentityBase.metadata,
33
+ migrations_package="python_ddd_framework.identity.sqlalchemy.migrations",
34
+ branch_label="identity",
35
+ )
36
+ )
37
+ class SqlAlchemyIdentityModule(AppModule):
38
+ dependencies = (IdentityModule, SqlAlchemyPersistenceModule)
39
+
40
+ def configure(self, context: ConfigureContext) -> None:
41
+ provider = Provider()
42
+ provider.provide(
43
+ SqlAlchemyIdentityRepository,
44
+ provides=IdentityRepository,
45
+ scope=Scope.REQUEST,
46
+ )
47
+ provider.provide(
48
+ SqlAlchemyIdentityAccessStore,
49
+ provides=IdentityAccessStore,
50
+ scope=Scope.APP,
51
+ )
52
+ provider.provide(
53
+ SqlAlchemyIdentityLoginStore,
54
+ provides=IdentityLoginStore,
55
+ scope=Scope.APP,
56
+ )
57
+ provider.provide(
58
+ SqlAlchemyRefreshSessionStore,
59
+ provides=RefreshSessionStore,
60
+ scope=Scope.APP,
61
+ )
62
+ context.services.contribute(provider, reason="Identity SQLAlchemy providers")