csrd-utils 0.3.98__tar.gz → 0.3.100__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 (209) hide show
  1. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/PKG-INFO +1 -1
  2. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/pyproject.toml +1 -1
  3. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/compose/augments.py +87 -1
  4. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/compose/presets.py +4 -0
  5. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/compose/scaffolder.py +29 -0
  6. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/compose/service_renderers.py +57 -36
  7. csrd_utils-0.3.100/src/csrd_utils/v2/templates/augments/crud-scaffold/dependencies/${entity_name_snake}_repository.py.template +21 -0
  8. csrd_utils-0.3.100/src/csrd_utils/v2/templates/augments/crud-scaffold/migrations_${entity_name_snake}.py.template +26 -0
  9. csrd_utils-0.3.100/src/csrd_utils/v2/templates/augments/crud-scaffold/models/${entity_name_snake}.py.template +28 -0
  10. csrd_utils-0.3.100/src/csrd_utils/v2/templates/augments/crud-scaffold/repositories/${entity_name_snake}_repository.py.template +65 -0
  11. csrd_utils-0.3.100/src/csrd_utils/v2/templates/augments/crud-scaffold/views/${entity_name_snake}_view.py.template +67 -0
  12. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/.gitignore +0 -0
  13. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/README.md +0 -0
  14. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/__init__.py +0 -0
  15. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/__main__.py +0 -0
  16. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/audit.py +0 -0
  17. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/augmentor.py +0 -0
  18. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/cluster.py +0 -0
  19. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/doctor.py +0 -0
  20. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/caching/conftest.fragment.py +0 -0
  21. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/caching/docker-compose.fragment.yaml +0 -0
  22. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/caching/manifest.yaml +0 -0
  23. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/caching/requirements.fragment +0 -0
  24. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/caching/settings.fragment.py +0 -0
  25. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/caching/src/app/cache.py +0 -0
  26. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/caching/tests/test_cache.py +0 -0
  27. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/logging/conftest.fragment.py +0 -0
  28. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/logging/manifest.yaml +0 -0
  29. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/logging/requirements.fragment +0 -0
  30. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/logging/settings.fragment.py +0 -0
  31. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/logging/src/app/middleware/logging.py +0 -0
  32. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/logging/tests/test_logging_middleware.py +0 -0
  33. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/metrics/conftest.fragment.py +0 -0
  34. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/metrics/docker-compose.fragment.yaml +0 -0
  35. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/metrics/manifest.yaml +0 -0
  36. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/metrics/requirements.fragment +0 -0
  37. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/metrics/settings.fragment.py +0 -0
  38. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/metrics/src/app/middleware/metrics.py +0 -0
  39. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/metrics/tests/test_metrics_middleware.py +0 -0
  40. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/tracing/conftest.fragment.py +0 -0
  41. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/tracing/manifest.yaml +0 -0
  42. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/tracing/requirements.fragment +0 -0
  43. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/tracing/settings.fragment.py +0 -0
  44. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/tracing/src/app/middleware/tracing.py +0 -0
  45. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/tracing/tests/test_tracing_middleware.py +0 -0
  46. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/workers/conftest.fragment.py +0 -0
  47. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/workers/docker-compose.fragment.yaml +0 -0
  48. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/workers/manifest.yaml +0 -0
  49. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/workers/requirements.fragment +0 -0
  50. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/workers/settings.fragment.py +0 -0
  51. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/workers/src/app/workers/__init__.py +0 -0
  52. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/workers/src/app/workers/celery_app.py +0 -0
  53. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/workers/src/app/workers/tasks.py +0 -0
  54. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/features/workers/tests/test_workers.py +0 -0
  55. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/fragments.py +0 -0
  56. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/generator.py +0 -0
  57. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/resources.py +0 -0
  58. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/README.md +0 -0
  59. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/cookiecutter.json +0 -0
  60. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/hooks/post_gen_project.py +0 -0
  61. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/.cookiecutterignore +0 -0
  62. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
  63. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
  64. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
  65. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
  66. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/cache.py +0 -0
  67. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/delegates/__init__.py +0 -0
  68. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/delegates/upstreams.py +0 -0
  69. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
  70. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/auth_service.py +0 -0
  71. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/delegates.py +0 -0
  72. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/item_repository.py +0 -0
  73. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/worker_broker.py +0 -0
  74. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/docker-compose.yml +0 -0
  75. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/middleware/__init__.py +0 -0
  76. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/middleware/logging.py +0 -0
  77. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
  78. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/repositories/item_repository.py +0 -0
  79. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
  80. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
  81. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/__init__.py +0 -0
  82. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/acceptance/__init__.py +0 -0
  83. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/acceptance/test_logging_middleware.py +0 -0
  84. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/conftest.py +0 -0
  85. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/test_items.py +0 -0
  86. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/__init__.py +0 -0
  87. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_cache.py +0 -0
  88. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_database_settings.py +0 -0
  89. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_delegates.py +0 -0
  90. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_settings_contracts.py +0 -0
  91. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_worker_broker.py +0 -0
  92. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
  93. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
  94. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
  95. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/items_view.py +0 -0
  96. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/workers/__init__.py +0 -0
  97. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/workers/tasks.py +0 -0
  98. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/cookiecutter.json +0 -0
  99. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
  100. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
  101. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
  102. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
  103. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/delegates/__init__.py +0 -0
  104. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/delegates/auth_delegate.py +0 -0
  105. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/delegates/identity_delegate.py +0 -0
  106. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
  107. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/auth_passthrough.py +0 -0
  108. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/auth_service.py +0 -0
  109. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/item_repository.py +0 -0
  110. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
  111. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/models/auth_service.py +0 -0
  112. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/models/items.py +0 -0
  113. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
  114. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/repositories/item_repository.py +0 -0
  115. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
  116. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
  117. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
  118. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
  119. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/auth_service_view.py +0 -0
  120. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
  121. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/items_view.py +0 -0
  122. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/cookiecutter.json +0 -0
  123. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
  124. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
  125. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
  126. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
  127. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
  128. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
  129. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/models/auth.py +0 -0
  130. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
  131. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/repositories/token_repository.py +0 -0
  132. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
  133. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
  134. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
  135. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
  136. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/auth_view.py +0 -0
  137. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
  138. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/cookiecutter.json +0 -0
  139. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
  140. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
  141. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
  142. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
  143. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
  144. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
  145. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/models/users.py +0 -0
  146. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
  147. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/repositories/user_repository.py +0 -0
  148. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
  149. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
  150. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
  151. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
  152. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
  153. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/users_view.py +0 -0
  154. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/AGENTS.md +0 -0
  155. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/__init__.py +0 -0
  156. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/compose/__init__.py +0 -0
  157. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/compose/git.py +0 -0
  158. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/compose/infra.py +0 -0
  159. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/compose/loader.py +0 -0
  160. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/compose/operations.py +0 -0
  161. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/compose/renderer.py +0 -0
  162. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/compose/yaml_editor.py +0 -0
  163. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/generate/__init__.py +0 -0
  164. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/generate/handlers.py +0 -0
  165. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/generate/helpers.py +0 -0
  166. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/generate/menu.py +0 -0
  167. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/models/__init__.py +0 -0
  168. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/models/base.py +0 -0
  169. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/models/spec.py +0 -0
  170. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/models/types.py +0 -0
  171. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/auth-passthrough/delegates/auth_delegate.py +0 -0
  172. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/auth-passthrough/dependencies/auth_passthrough.py +0 -0
  173. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/auth-passthrough/models/auth_passthrough.py +0 -0
  174. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/auth-passthrough/views/auth_passthrough_view.py +0 -0
  175. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/db-config/dependencies/db.py.template +0 -0
  176. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/db-config/migrations.py.template +0 -0
  177. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/jwt-auth-consumer/dependencies/auth.py.template +0 -0
  178. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/jwt-auth-provider/dependencies/db.py.template +0 -0
  179. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/jwt-auth-provider/dependencies/token_service.py +0 -0
  180. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/jwt-auth-provider/migrations.py.template +0 -0
  181. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/jwt-auth-provider/models/auth.py +0 -0
  182. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/jwt-auth-provider/models/jwks.py +0 -0
  183. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/jwt-auth-provider/models/users.py +0 -0
  184. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/jwt-auth-provider/repositories/user_repository.py +0 -0
  185. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/jwt-auth-provider/services/token_service.py +0 -0
  186. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/jwt-auth-provider/views/auth_view.py +0 -0
  187. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/jwt-auth-provider/views/jwks_view.py +0 -0
  188. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/augments/jwt-auth-provider/views/users_view.py +0 -0
  189. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/cookiecutter.json +0 -0
  190. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
  191. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
  192. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
  193. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
  194. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
  195. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/models/health.py +0 -0
  196. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
  197. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
  198. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/tests/__init__.py +0 -0
  199. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/tests/conftest.py +0 -0
  200. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/tests/test_health.py +0 -0
  201. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
  202. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/views/health_view.py +0 -0
  203. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/tui_wizard/__init__.py +0 -0
  204. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/tui_wizard/exceptions.py +0 -0
  205. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/tui_wizard/menu.py +0 -0
  206. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/tui_wizard/models.py +0 -0
  207. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/tui_wizard/prompts.py +0 -0
  208. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/tui_wizard/terminal.py +0 -0
  209. {csrd_utils-0.3.98 → csrd_utils-0.3.100}/src/tui_wizard/wizard.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csrd-utils
3
- Version: 0.3.98
3
+ Version: 0.3.100
4
4
  Summary: CLI utilities for csrd service generation and feature augmentation
5
5
  Project-URL: Repository, https://github.com/csrd-api/fastapi-common
6
6
  Project-URL: Documentation, https://github.com/csrd-api/fastapi-common/tree/main/packages/utils
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "csrd-utils"
3
- version = "0.3.98"
3
+ version = "0.3.100"
4
4
  description = "CLI utilities for csrd service generation and feature augmentation"
5
5
  license = { text = "MIT" }
6
6
  requires-python = ">=3.12"
@@ -13,6 +13,44 @@ from dataclasses import dataclass, field
13
13
  from typing import Literal
14
14
 
15
15
 
16
+ @dataclass(frozen=True)
17
+ class RouterDescriptor:
18
+ """Describes a router contributed by an augment.
19
+
20
+ Parameters
21
+ ----------
22
+ import_path
23
+ Dot-path relative to the service package, e.g.
24
+ ``".views.auth_passthrough_view"``.
25
+ attr
26
+ Name of the router attribute in the module, e.g. ``"router"``.
27
+ alias
28
+ Import alias used in ``__init__.py``, e.g.
29
+ ``"auth_passthrough_router"``.
30
+ """
31
+
32
+ import_path: str
33
+ attr: str = "router"
34
+ alias: str = ""
35
+
36
+
37
+ @dataclass(frozen=True)
38
+ class MiddlewareDescriptor:
39
+ """Describes middleware contributed by an augment."""
40
+
41
+ import_path: str
42
+ class_name: str
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class ExceptionHandlerDescriptor:
47
+ """Describes an exception handler contributed by an augment."""
48
+
49
+ import_path: str
50
+ handler_name: str
51
+ exception_class: str = "Exception"
52
+
53
+
16
54
  @dataclass(frozen=True)
17
55
  class AugmentDescriptor:
18
56
  """Complete description of an augment capability.
@@ -81,6 +119,15 @@ class AugmentDescriptor:
81
119
  # service with this role. ``None`` means no scaffolding.
82
120
  applies_to_role: str | None = None
83
121
 
122
+ # Routers to wire into __init__.py via include_router
123
+ routers: list[RouterDescriptor] = field(default_factory=list)
124
+
125
+ # Middleware to add to the app
126
+ middleware: list[MiddlewareDescriptor] = field(default_factory=list)
127
+
128
+ # Exception handlers to register on the app
129
+ exception_handlers: list[ExceptionHandlerDescriptor] = field(default_factory=list)
130
+
84
131
 
85
132
  # ---------------------------------------------------------------------------
86
133
  # Registry
@@ -102,6 +149,20 @@ AUGMENT_REGISTRY: dict[str, AugmentDescriptor] = {
102
149
  ],
103
150
  template_dir="jwt-auth-provider",
104
151
  applies_to_role="auth",
152
+ routers=[
153
+ RouterDescriptor(
154
+ import_path=".views.auth_view",
155
+ alias="auth_router",
156
+ ),
157
+ RouterDescriptor(
158
+ import_path=".views.users_view",
159
+ alias="users_router",
160
+ ),
161
+ RouterDescriptor(
162
+ import_path=".views.jwks_view",
163
+ alias="jwks_router",
164
+ ),
165
+ ],
105
166
  ),
106
167
  # ── Service-scope ──────────────────────────────────────────────
107
168
  "db-config": AugmentDescriptor(
@@ -138,7 +199,12 @@ AUGMENT_REGISTRY: dict[str, AugmentDescriptor] = {
138
199
  requires_workspace_augment="jwt-auth-provider",
139
200
  packages=["csrd-delegate", "csrd-models"],
140
201
  template_dir="auth-passthrough",
141
- # compose_env, compose_depends_on, settings_fields resolved from spec
202
+ routers=[
203
+ RouterDescriptor(
204
+ import_path=".views.auth_passthrough_view",
205
+ alias="auth_passthrough_router",
206
+ ),
207
+ ],
142
208
  ),
143
209
  "context-logging": AugmentDescriptor(
144
210
  name="context-logging",
@@ -146,6 +212,12 @@ AUGMENT_REGISTRY: dict[str, AugmentDescriptor] = {
146
212
  description="Request context middleware and structured logging",
147
213
  packages=["csrd-logging", "csrd-context"],
148
214
  template_dir="context-logging",
215
+ middleware=[
216
+ MiddlewareDescriptor(
217
+ import_path="csrd.context.middleware",
218
+ class_name="RequestContextMiddleware",
219
+ ),
220
+ ],
149
221
  ),
150
222
  "service-layer": AugmentDescriptor(
151
223
  name="service-layer",
@@ -153,6 +225,20 @@ AUGMENT_REGISTRY: dict[str, AugmentDescriptor] = {
153
225
  description="BaseService + ServiceError hierarchy + exception handler",
154
226
  packages=["csrd-service", "csrd-models"],
155
227
  template_dir="service-layer",
228
+ exception_handlers=[
229
+ ExceptionHandlerDescriptor(
230
+ import_path="csrd.service",
231
+ handler_name="service_exception_handler",
232
+ ),
233
+ ],
234
+ ),
235
+ "crud-scaffold": AugmentDescriptor(
236
+ name="crud-scaffold",
237
+ scope="service",
238
+ description="CRUD endpoints for a named entity with token-guarded views",
239
+ packages=[], # db-config + jwt-auth-consumer cover dependencies
240
+ template_dir="crud-scaffold",
241
+ routers=[], # routers are dynamic per entity — wired via options
156
242
  ),
157
243
  }
158
244
 
@@ -35,6 +35,10 @@ PRESET_REGISTRY: dict[str, PresetDefinition] = {
35
35
  ServiceAugment(name="jwt-auth-consumer"),
36
36
  ServiceAugment(name="auth-passthrough"),
37
37
  ServiceAugment(name="db-config"),
38
+ ServiceAugment(
39
+ name="crud-scaffold",
40
+ options={"entity_name": "item"},
41
+ ),
38
42
  ],
39
43
  ),
40
44
  ServiceNode(
@@ -139,6 +139,33 @@ def _augment_template_vars(
139
139
  "port": str(service.port),
140
140
  }
141
141
 
142
+ # Auto-pass augment options as template variables
143
+ for key, value in augment.options.items():
144
+ if isinstance(value, str):
145
+ variables[key] = value
146
+
147
+ # Derive entity name variants for crud-scaffold
148
+ if augment.name == "crud-scaffold":
149
+ entity_raw = augment.options.get("entity_name", "item")
150
+ entity = entity_raw if isinstance(entity_raw, str) else entity_raw[0]
151
+ snake = entity.replace("-", "_").lower()
152
+ pascal = "".join(w.capitalize() for w in snake.split("_"))
153
+ plural_raw = augment.options.get("entity_name_plural", snake + "s")
154
+ plural = plural_raw if isinstance(plural_raw, str) else plural_raw[0]
155
+ variables["entity_name"] = entity
156
+ variables["entity_name_snake"] = snake
157
+ variables["entity_name_pascal"] = pascal
158
+ variables["entity_name_plural"] = plural
159
+
160
+ # SQL dialect for migration (same logic as db-config)
161
+ db = detect_configured_db({n.type for n in spec.infra})
162
+ if db == "postgres":
163
+ variables["sql_auto_id"] = "SERIAL PRIMARY KEY"
164
+ elif db == "mariadb":
165
+ variables["sql_auto_id"] = "INTEGER PRIMARY KEY AUTO_INCREMENT"
166
+ else:
167
+ variables["sql_auto_id"] = "INTEGER PRIMARY KEY AUTOINCREMENT"
168
+
142
169
  # Database adapter variables — shared by db-config and jwt-auth-provider
143
170
  if augment.name in ("db-config", "jwt-auth-provider"):
144
171
  db = detect_configured_db({n.type for n in spec.infra})
@@ -258,6 +285,8 @@ def scaffold_augment(
258
285
  continue
259
286
 
260
287
  relative = template_file.relative_to(template_dir)
288
+ # Apply template substitution to file path (e.g. $entity_name_snake.py → item.py)
289
+ relative = Path(Template(str(relative)).safe_substitute(variables))
261
290
  dest = svc_dest / relative
262
291
 
263
292
  # Strip .template suffix so e.g. db.py.template → db.py
@@ -8,7 +8,7 @@ separate scaffolded files (views, repositories, services, dependencies).
8
8
  import re
9
9
 
10
10
  from ..models import ComposeSpec, ServiceNode
11
- from .augments import AUGMENT_REGISTRY
11
+ from .augments import AUGMENT_REGISTRY, AugmentDescriptor
12
12
  from .infra import INFRA_REGISTRY, detect_configured_db
13
13
 
14
14
  # Pattern to extract default from docker-compose interpolation: ${VAR:-default}
@@ -254,11 +254,21 @@ def render_settings(svc: ServiceNode, spec: ComposeSpec) -> str:
254
254
 
255
255
  def render_init(svc: ServiceNode, spec: ComposeSpec) -> str:
256
256
  """Render ``__init__.py`` with ``build_app()`` + augment middleware/handlers."""
257
- augment_names = {a.name for a in svc.augments}
258
- ws_augment_names = {a.name for a in spec.workspace.augments}
259
257
 
260
- # Check if this service is the auth provider target
261
- is_auth_provider = "jwt-auth-provider" in ws_augment_names and svc.role == "auth"
258
+ # Collect all augment descriptors that apply to this service
259
+ descriptors: list[tuple[str, AugmentDescriptor]] = []
260
+
261
+ # Workspace augments targeting this service's role
262
+ for ws_aug in spec.workspace.augments:
263
+ desc = AUGMENT_REGISTRY.get(ws_aug.name)
264
+ if desc is not None and desc.applies_to_role == svc.role:
265
+ descriptors.append((ws_aug.name, desc))
266
+
267
+ # Service-scope augments
268
+ for augment in svc.augments:
269
+ desc = AUGMENT_REGISTRY.get(augment.name)
270
+ if desc is not None:
271
+ descriptors.append((augment.name, desc))
262
272
 
263
273
  # Collect imports
264
274
  imports = [
@@ -275,26 +285,39 @@ def render_init(svc: ServiceNode, spec: ComposeSpec) -> str:
275
285
  "from .views import app as unversioned_app",
276
286
  ]
277
287
 
278
- if is_auth_provider:
279
- imports.extend(
280
- [
281
- "from .views.auth_view import router as auth_router",
282
- "from .views.users_view import router as users_router",
283
- "from .views.jwks_view import router as jwks_router",
284
- ]
285
- )
286
-
287
- if "context-logging" in augment_names:
288
- imports.append("")
289
- imports.append("from csrd.context.middleware import RequestContextMiddleware")
290
-
291
- if "auth-passthrough" in augment_names:
292
- imports.append("")
293
- imports.append("from .views.auth_passthrough_view import router as auth_passthrough_router")
288
+ # Router imports (from descriptors)
289
+ router_aliases: list[str] = []
290
+ for _name, desc in descriptors:
291
+ for r in desc.routers:
292
+ alias = r.alias or r.import_path.rsplit(".", 1)[-1] + "_router"
293
+ imports.append(f"from {r.import_path} import {r.attr} as {alias}")
294
+ router_aliases.append(alias)
294
295
 
295
- if "service-layer" in augment_names:
296
- imports.append("")
297
- imports.append("from csrd.service import service_exception_handler")
296
+ # Dynamic routers from crud-scaffold augments (entity-name-based)
297
+ for augment in svc.augments:
298
+ if augment.name == "crud-scaffold":
299
+ entity_raw = augment.options.get("entity_name", "item")
300
+ entity = entity_raw if isinstance(entity_raw, str) else entity_raw[0]
301
+ snake = entity.replace("-", "_").lower()
302
+ alias = f"{snake}_router"
303
+ imports.append(f"from .views.{snake}_view import router as {alias}")
304
+ router_aliases.append(alias)
305
+
306
+ # Middleware imports
307
+ middleware_classes: list[str] = []
308
+ for _name, desc in descriptors:
309
+ for m in desc.middleware:
310
+ imports.append("")
311
+ imports.append(f"from {m.import_path} import {m.class_name}")
312
+ middleware_classes.append(m.class_name)
313
+
314
+ # Exception handler imports
315
+ handler_pairs: list[tuple[str, str]] = [] # (exception_class, handler_name)
316
+ for _name, desc in descriptors:
317
+ for eh in desc.exception_handlers:
318
+ imports.append("")
319
+ imports.append(f"from {eh.import_path} import {eh.handler_name}")
320
+ handler_pairs.append((eh.exception_class, eh.handler_name))
298
321
 
299
322
  # Build app body
300
323
  body = [
@@ -303,14 +326,10 @@ def render_init(svc: ServiceNode, spec: ComposeSpec) -> str:
303
326
  "def build_app() -> FastAPI:",
304
327
  ]
305
328
 
306
- if is_auth_provider:
307
- body.append(" unversioned_app.include_router(auth_router)")
308
- body.append(" unversioned_app.include_router(users_router)")
309
- body.append(" unversioned_app.include_router(jwks_router)")
310
- body.append("")
311
-
312
- if "auth-passthrough" in augment_names:
313
- body.append(" unversioned_app.include_router(auth_passthrough_router)")
329
+ # Wire routers
330
+ if router_aliases:
331
+ for alias in router_aliases:
332
+ body.append(f" unversioned_app.include_router({alias})")
314
333
  body.append("")
315
334
 
316
335
  body.extend(
@@ -329,13 +348,15 @@ def render_init(svc: ServiceNode, spec: ComposeSpec) -> str:
329
348
  ]
330
349
  )
331
350
 
332
- if "context-logging" in augment_names:
351
+ # Wire middleware
352
+ for cls in middleware_classes:
333
353
  body.append("")
334
- body.append(" app.add_middleware(RequestContextMiddleware)")
354
+ body.append(f" app.add_middleware({cls})")
335
355
 
336
- if "service-layer" in augment_names:
356
+ # Wire exception handlers
357
+ for exc_cls, handler in handler_pairs:
337
358
  body.append("")
338
- body.append(" app.add_exception_handler(Exception, service_exception_handler)")
359
+ body.append(f" app.add_exception_handler({exc_cls}, {handler})")
339
360
 
340
361
  body.extend(
341
362
  [
@@ -0,0 +1,21 @@
1
+ """${entity_name_pascal} repository dependency.
2
+
3
+ This file is scaffolded once and never overwritten.
4
+ """
5
+
6
+ from typing import Annotated
7
+
8
+ from fastapi import Depends
9
+
10
+ from ..repositories.${entity_name_snake}_repository import ${entity_name_pascal}Repository
11
+ from .db import Adapter
12
+
13
+
14
+ def _${entity_name_snake}_repository_factory(adapter: Adapter) -> ${entity_name_pascal}Repository:
15
+ """Create a ``${entity_name_pascal}Repository`` backed by the database adapter."""
16
+ return ${entity_name_pascal}Repository(adapter)
17
+
18
+
19
+ ${entity_name_pascal}Repo = Annotated[${entity_name_pascal}Repository, Depends(_${entity_name_snake}_repository_factory)]
20
+
21
+ __all__ = ("${entity_name_pascal}Repo",)
@@ -0,0 +1,26 @@
1
+ """Migration for the ${entity_name_plural} table.
2
+
3
+ Import this into your ``migrations.py`` and append to the migrations list:
4
+
5
+ from .migrations_${entity_name_snake} import ${entity_name_snake}_migration
6
+ migrations.append(${entity_name_snake}_migration)
7
+
8
+ This file is scaffolded once and never overwritten.
9
+ """
10
+
11
+ from csrd.migration import Migration
12
+
13
+ ${entity_name_snake}_migration = Migration(
14
+ version="${entity_name_snake}_001",
15
+ description="Create ${entity_name_plural} table",
16
+ up=(
17
+ "CREATE TABLE IF NOT EXISTS ${entity_name_plural} ("
18
+ " id TEXT PRIMARY KEY,"
19
+ " name TEXT NOT NULL,"
20
+ " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
21
+ ")"
22
+ ),
23
+ down="DROP TABLE IF EXISTS ${entity_name_plural}",
24
+ )
25
+
26
+ __all__ = ("${entity_name_snake}_migration",)
@@ -0,0 +1,28 @@
1
+ """${entity_name_pascal} models for CRUD operations."""
2
+
3
+ from csrd.models import BaseModel
4
+
5
+
6
+ class Create${entity_name_pascal}Request(BaseModel):
7
+ """Request body for creating a new ${entity_name}."""
8
+
9
+ name: str
10
+
11
+
12
+ class Update${entity_name_pascal}Request(BaseModel):
13
+ """Request body for updating an existing ${entity_name}."""
14
+
15
+ name: str
16
+
17
+
18
+ class ${entity_name_pascal}Response(BaseModel):
19
+ """Response model for a single ${entity_name}."""
20
+
21
+ id: str
22
+ name: str
23
+
24
+
25
+ class ${entity_name_pascal}ListResponse(BaseModel):
26
+ """Response model for a list of ${entity_name_plural}."""
27
+
28
+ items: list[${entity_name_pascal}Response]
@@ -0,0 +1,65 @@
1
+ """${entity_name_pascal} repository — CRUD operations.
2
+
3
+ This file is scaffolded once and never overwritten.
4
+ """
5
+
6
+ import uuid
7
+
8
+ from fastapi import HTTPException
9
+
10
+ from csrd.repository import BaseRepository
11
+
12
+ from ..models.${entity_name_snake} import (
13
+ ${entity_name_pascal}ListResponse,
14
+ ${entity_name_pascal}Response,
15
+ )
16
+
17
+
18
+ class ${entity_name_pascal}Repository(BaseRepository):
19
+ """CRUD storage for ${entity_name_plural}."""
20
+
21
+ async def create(self, name: str) -> ${entity_name_pascal}Response:
22
+ """Create a new ${entity_name}."""
23
+ entity_id = str(uuid.uuid4())
24
+ await self.execute(
25
+ "INSERT INTO ${entity_name_plural} (id, name) VALUES (:id, :name)",
26
+ {"id": entity_id, "name": name},
27
+ )
28
+ return ${entity_name_pascal}Response(id=entity_id, name=name)
29
+
30
+ async def get_by_id(self, entity_id: str) -> ${entity_name_pascal}Response:
31
+ """Get a ${entity_name} by ID. Raises 404 if not found."""
32
+ return await self.require_one(
33
+ "SELECT id, name FROM ${entity_name_plural} WHERE id = :id",
34
+ {"id": entity_id},
35
+ model=${entity_name_pascal}Response,
36
+ detail="${entity_name_pascal} not found",
37
+ )
38
+
39
+ async def list_all(self) -> ${entity_name_pascal}ListResponse:
40
+ """List all ${entity_name_plural}."""
41
+ rows = await self.fetch_all(
42
+ "SELECT id, name FROM ${entity_name_plural} ORDER BY name",
43
+ )
44
+ items = [self.apply_model(row, model=${entity_name_pascal}Response) for row in rows]
45
+ return ${entity_name_pascal}ListResponse(items=items)
46
+
47
+ async def update(self, entity_id: str, name: str) -> ${entity_name_pascal}Response:
48
+ """Update a ${entity_name}. Raises 404 if not found."""
49
+ await self.get_by_id(entity_id) # ensures exists
50
+ await self.execute(
51
+ "UPDATE ${entity_name_plural} SET name = :name WHERE id = :id",
52
+ {"id": entity_id, "name": name},
53
+ )
54
+ return ${entity_name_pascal}Response(id=entity_id, name=name)
55
+
56
+ async def delete(self, entity_id: str) -> None:
57
+ """Delete a ${entity_name}. Raises 404 if not found."""
58
+ await self.get_by_id(entity_id) # ensures exists
59
+ await self.execute(
60
+ "DELETE FROM ${entity_name_plural} WHERE id = :id",
61
+ {"id": entity_id},
62
+ )
63
+
64
+
65
+ __all__ = ("${entity_name_pascal}Repository",)
@@ -0,0 +1,67 @@
1
+ """${entity_name_pascal} CRUD endpoints — token-guarded.
2
+
3
+ This file is scaffolded once and never overwritten.
4
+ """
5
+
6
+ from fastapi import APIRouter, Security
7
+
8
+ from ..dependencies.auth import auth_dep
9
+ from ..dependencies.${entity_name_snake}_repository import ${entity_name_pascal}Repo
10
+ from ..models.${entity_name_snake} import (
11
+ Create${entity_name_pascal}Request,
12
+ ${entity_name_pascal}ListResponse,
13
+ ${entity_name_pascal}Response,
14
+ Update${entity_name_pascal}Request,
15
+ )
16
+
17
+ router = APIRouter(prefix="/api/${entity_name_plural}", tags=["${entity_name_plural}"])
18
+
19
+
20
+ @router.get("", response_model=${entity_name_pascal}ListResponse)
21
+ async def list_${entity_name_plural}(
22
+ repo: ${entity_name_pascal}Repo,
23
+ _claims=Security(auth_dep),
24
+ ) -> ${entity_name_pascal}ListResponse:
25
+ """List all ${entity_name_plural}."""
26
+ return await repo.list_all()
27
+
28
+
29
+ @router.get("/{entity_id}", response_model=${entity_name_pascal}Response)
30
+ async def get_${entity_name_snake}(
31
+ entity_id: str,
32
+ repo: ${entity_name_pascal}Repo,
33
+ _claims=Security(auth_dep),
34
+ ) -> ${entity_name_pascal}Response:
35
+ """Get a ${entity_name} by ID."""
36
+ return await repo.get_by_id(entity_id)
37
+
38
+
39
+ @router.post("", status_code=201, response_model=${entity_name_pascal}Response)
40
+ async def create_${entity_name_snake}(
41
+ body: Create${entity_name_pascal}Request,
42
+ repo: ${entity_name_pascal}Repo,
43
+ _claims=Security(auth_dep),
44
+ ) -> ${entity_name_pascal}Response:
45
+ """Create a new ${entity_name}."""
46
+ return await repo.create(body.name)
47
+
48
+
49
+ @router.put("/{entity_id}", response_model=${entity_name_pascal}Response)
50
+ async def update_${entity_name_snake}(
51
+ entity_id: str,
52
+ body: Update${entity_name_pascal}Request,
53
+ repo: ${entity_name_pascal}Repo,
54
+ _claims=Security(auth_dep),
55
+ ) -> ${entity_name_pascal}Response:
56
+ """Update an existing ${entity_name}."""
57
+ return await repo.update(entity_id, body.name)
58
+
59
+
60
+ @router.delete("/{entity_id}", status_code=204)
61
+ async def delete_${entity_name_snake}(
62
+ entity_id: str,
63
+ repo: ${entity_name_pascal}Repo,
64
+ _claims=Security(auth_dep),
65
+ ) -> None:
66
+ """Delete a ${entity_name}."""
67
+ await repo.delete(entity_id)
File without changes
File without changes