csrd-utils 0.3.60__tar.gz → 0.3.62__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.
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/PKG-INFO +1 -1
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/pyproject.toml +1 -1
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/__main__.py +5 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/generator.py +4 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/cookiecutter.json +1 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +6 -0
- csrd_utils-0.3.62/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/auth_service.py +64 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/requirements.txt +4 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/settings.py +7 -0
- csrd_utils-0.3.62/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/conftest.py +66 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/test_items.py +127 -28
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/items_view.py +21 -0
- csrd_utils-0.3.60/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/conftest.py +0 -33
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/.gitignore +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/README.md +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/audit.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/augmentor.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/cluster.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/doctor.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/caching/conftest.fragment.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/caching/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/caching/manifest.yaml +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/caching/requirements.fragment +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/caching/settings.fragment.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/caching/src/app/cache.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/caching/tests/test_cache.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/logging/conftest.fragment.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/logging/manifest.yaml +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/logging/requirements.fragment +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/logging/settings.fragment.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/logging/src/app/middleware/logging.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/logging/tests/test_logging_middleware.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/metrics/conftest.fragment.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/metrics/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/metrics/manifest.yaml +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/metrics/requirements.fragment +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/metrics/settings.fragment.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/metrics/src/app/middleware/metrics.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/metrics/tests/test_metrics_middleware.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/tracing/conftest.fragment.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/tracing/manifest.yaml +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/tracing/requirements.fragment +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/tracing/settings.fragment.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/tracing/src/app/middleware/tracing.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/tracing/tests/test_tracing_middleware.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/conftest.fragment.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/manifest.yaml +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/requirements.fragment +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/settings.fragment.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/src/app/workers/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/src/app/workers/celery_app.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/src/app/workers/tasks.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/tests/test_workers.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/fragments.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/resources.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/README.md +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/hooks/post_gen_project.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/.cookiecutterignore +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/cache.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/delegates/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/delegates/upstreams.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/delegates.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/item_repository.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/worker_broker.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/docker-compose.yml +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/middleware/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/middleware/logging.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/repositories/item_repository.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/acceptance/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/acceptance/test_logging_middleware.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_cache.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_database_settings.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_delegates.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_settings_contracts.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_worker_broker.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/workers/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/workers/tasks.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/cookiecutter.json +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/repositories/token_repository.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/auth_view.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/cookiecutter.json +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/repositories/user_repository.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/users_view.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csrd-utils
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.62
|
|
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
|
|
@@ -41,6 +41,7 @@ class GenerateServiceArgs(TypedDict):
|
|
|
41
41
|
broker: str
|
|
42
42
|
has_caching: bool
|
|
43
43
|
cache_backend: str
|
|
44
|
+
has_auth: bool
|
|
44
45
|
has_structured_logging: bool
|
|
45
46
|
overwrite_if_exists: bool
|
|
46
47
|
|
|
@@ -236,6 +237,7 @@ def _collect_new_service_config(args: argparse.Namespace) -> GenerateServiceArgs
|
|
|
236
237
|
"broker": args.broker,
|
|
237
238
|
"has_caching": args.caching,
|
|
238
239
|
"cache_backend": args.cache_backend,
|
|
240
|
+
"has_auth": False,
|
|
239
241
|
"has_structured_logging": args.structured_logging,
|
|
240
242
|
"overwrite_if_exists": args.force,
|
|
241
243
|
}
|
|
@@ -327,6 +329,7 @@ def _collect_new_service_config(args: argparse.Namespace) -> GenerateServiceArgs
|
|
|
327
329
|
"broker": broker,
|
|
328
330
|
"has_caching": has_caching,
|
|
329
331
|
"cache_backend": cache_backend,
|
|
332
|
+
"has_auth": False,
|
|
330
333
|
"has_structured_logging": has_structured_logging,
|
|
331
334
|
"overwrite_if_exists": overwrite_if_exists,
|
|
332
335
|
}
|
|
@@ -468,6 +471,7 @@ def _generate_services_from_cluster_spec(
|
|
|
468
471
|
has_delegates = bool(delegates_list)
|
|
469
472
|
has_workers = service_type == "worker" or "workers" in features
|
|
470
473
|
has_caching = "caching" in features
|
|
474
|
+
has_auth = "auth-service" in service.get("requires", []) and name != "auth-service"
|
|
471
475
|
has_structured_logging = "logging" in features
|
|
472
476
|
database = str(service.get("database", "sqlite")) if has_database else "sqlite"
|
|
473
477
|
|
|
@@ -516,6 +520,7 @@ def _generate_services_from_cluster_spec(
|
|
|
516
520
|
broker=broker,
|
|
517
521
|
has_caching=has_caching,
|
|
518
522
|
cache_backend="redis",
|
|
523
|
+
has_auth=has_auth,
|
|
519
524
|
has_structured_logging=has_structured_logging,
|
|
520
525
|
overwrite_if_exists=force,
|
|
521
526
|
)
|
|
@@ -137,6 +137,7 @@ def _build_context(
|
|
|
137
137
|
broker: str,
|
|
138
138
|
has_caching: bool,
|
|
139
139
|
cache_backend: str,
|
|
140
|
+
has_auth: bool,
|
|
140
141
|
has_structured_logging: bool,
|
|
141
142
|
) -> dict[str, Any]:
|
|
142
143
|
return {
|
|
@@ -153,6 +154,7 @@ def _build_context(
|
|
|
153
154
|
"broker": broker,
|
|
154
155
|
"has_caching": has_caching,
|
|
155
156
|
"cache_backend": cache_backend,
|
|
157
|
+
"has_auth": has_auth,
|
|
156
158
|
"has_structured_logging": has_structured_logging,
|
|
157
159
|
}
|
|
158
160
|
|
|
@@ -175,6 +177,7 @@ def generate_service(
|
|
|
175
177
|
cache_backend: str,
|
|
176
178
|
has_structured_logging: bool,
|
|
177
179
|
overwrite_if_exists: bool,
|
|
180
|
+
has_auth: bool = False,
|
|
178
181
|
) -> Path:
|
|
179
182
|
"""Generate a service from the bundled cookiecutter-service template."""
|
|
180
183
|
output_root = output_dir.resolve()
|
|
@@ -204,6 +207,7 @@ def generate_service(
|
|
|
204
207
|
broker=broker,
|
|
205
208
|
has_caching=has_caching,
|
|
206
209
|
cache_backend=cache_backend,
|
|
210
|
+
has_auth=has_auth,
|
|
207
211
|
has_structured_logging=has_structured_logging,
|
|
208
212
|
)
|
|
209
213
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{%- if cookiecutter.has_auth %}
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
from fastapi import HTTPException, Security
|
|
6
|
+
from fastapi.requests import Request
|
|
7
|
+
|
|
8
|
+
from csrd.auth import CallbackAuthenticator, create_bearer_dependency
|
|
9
|
+
from csrd.models.claims import UserClaims
|
|
10
|
+
from csrd.versioning import find_token
|
|
11
|
+
|
|
12
|
+
from ..settings import settings
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _map_claims(payload: dict[str, object]) -> UserClaims:
|
|
16
|
+
claims = payload.get("claims") if isinstance(payload.get("claims"), dict) else payload
|
|
17
|
+
if not isinstance(claims, dict):
|
|
18
|
+
raise HTTPException(status_code=502, detail="invalid auth response")
|
|
19
|
+
|
|
20
|
+
authorities = claims.get("authorities") or claims.get("roles") or []
|
|
21
|
+
if isinstance(authorities, str):
|
|
22
|
+
authorities = authorities.split()
|
|
23
|
+
|
|
24
|
+
sub = str(claims.get("sub", ""))
|
|
25
|
+
user_name = str(claims.get("user_name") or sub)
|
|
26
|
+
|
|
27
|
+
return UserClaims(
|
|
28
|
+
sub=sub,
|
|
29
|
+
user_name=user_name,
|
|
30
|
+
authorities=list(authorities),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def _remote_verify(_: Request, token: str) -> UserClaims:
|
|
35
|
+
try:
|
|
36
|
+
async with httpx.AsyncClient(timeout=5.0) as client:
|
|
37
|
+
response = await client.post(
|
|
38
|
+
f"{settings.auth_service_url}/api/verify",
|
|
39
|
+
json={"token": token},
|
|
40
|
+
)
|
|
41
|
+
except httpx.ConnectError as exc:
|
|
42
|
+
raise HTTPException(status_code=503, detail="auth service unavailable") from exc
|
|
43
|
+
|
|
44
|
+
if response.status_code != 200:
|
|
45
|
+
raise HTTPException(status_code=401, detail="invalid token")
|
|
46
|
+
|
|
47
|
+
payload = response.json()
|
|
48
|
+
if not isinstance(payload, dict):
|
|
49
|
+
raise HTTPException(status_code=502, detail="invalid auth response")
|
|
50
|
+
return _map_claims(payload)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
_authenticator = CallbackAuthenticator(callback=_remote_verify)
|
|
54
|
+
|
|
55
|
+
VerifiedTokenDep = Annotated[
|
|
56
|
+
UserClaims,
|
|
57
|
+
Security(create_bearer_dependency(_authenticator, token_finder=find_token)),
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
__all__ = ("VerifiedTokenDep",)
|
|
62
|
+
{%- else %}
|
|
63
|
+
__all__: tuple[str, ...] = ()
|
|
64
|
+
{%- endif %}
|
|
@@ -57,6 +57,13 @@ class Settings(BaseSettings):
|
|
|
57
57
|
has_caching: bool = False
|
|
58
58
|
{%- endif %}
|
|
59
59
|
|
|
60
|
+
{%- if cookiecutter.has_auth %}
|
|
61
|
+
has_auth: bool = True
|
|
62
|
+
auth_service_url: str = "http://auth-service:8081"
|
|
63
|
+
{%- else %}
|
|
64
|
+
has_auth: bool = False
|
|
65
|
+
{%- endif %}
|
|
66
|
+
|
|
60
67
|
@model_validator(mode="after")
|
|
61
68
|
def validate_db_credentials(self) -> Self:
|
|
62
69
|
if not self.has_database:
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Shared test fixtures for {{ cookiecutter.service_name }}."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import secrets
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from fastapi.testclient import TestClient
|
|
8
|
+
{%- if cookiecutter.has_auth %}
|
|
9
|
+
from fastapi import HTTPException
|
|
10
|
+
|
|
11
|
+
from csrd.models.claims import UserClaims
|
|
12
|
+
|
|
13
|
+
from {{cookiecutter.__service_name_snake}}.dependencies import auth_service as auth_dependency
|
|
14
|
+
{%- endif %}
|
|
15
|
+
|
|
16
|
+
from {{cookiecutter.__service_name_snake}} import build_app
|
|
17
|
+
|
|
18
|
+
HAS_DATABASE = {{"True" if cookiecutter.has_database else "False"}}
|
|
19
|
+
DB_KIND = "{{ cookiecutter.database }}"
|
|
20
|
+
HAS_AUTH = {{"True" if cookiecutter.has_auth else "False"}}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# [INSERT: fixtures]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
{%- if cookiecutter.has_auth %}
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def auth_token() -> str:
|
|
29
|
+
# Generate a per-test bearer token value used by auth-guarded endpoints.
|
|
30
|
+
return f"test-token-{secrets.token_hex(8)}"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.fixture(autouse=True)
|
|
34
|
+
def mock_auth_verifier(auth_token: str):
|
|
35
|
+
original_callback = auth_dependency._authenticator.callback
|
|
36
|
+
|
|
37
|
+
async def _fake_verify(_request, token: str) -> UserClaims:
|
|
38
|
+
if token != auth_token:
|
|
39
|
+
raise HTTPException(status_code=401, detail="invalid token")
|
|
40
|
+
return UserClaims(sub="test-user", user_name="test-user", authorities=["ROLE_USER"])
|
|
41
|
+
|
|
42
|
+
auth_dependency._authenticator.callback = _fake_verify
|
|
43
|
+
try:
|
|
44
|
+
yield
|
|
45
|
+
finally:
|
|
46
|
+
auth_dependency._authenticator.callback = original_callback
|
|
47
|
+
{%- endif %}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@pytest.fixture
|
|
51
|
+
def client():
|
|
52
|
+
if HAS_DATABASE and DB_KIND == "maria":
|
|
53
|
+
os.environ.setdefault("DB_HOST", "localhost")
|
|
54
|
+
os.environ.setdefault("DB_PORT", "3306")
|
|
55
|
+
os.environ.setdefault("DB_USER", "service_user")
|
|
56
|
+
os.environ.setdefault("DB_PASSWORD", "change_me")
|
|
57
|
+
os.environ.setdefault("DB_NAME", "service_db")
|
|
58
|
+
elif HAS_DATABASE and DB_KIND == "postgres":
|
|
59
|
+
os.environ.setdefault("DB_HOST", "localhost")
|
|
60
|
+
os.environ.setdefault("DB_PORT", "5432")
|
|
61
|
+
os.environ.setdefault("DB_USER", "service_user")
|
|
62
|
+
os.environ.setdefault("DB_PASSWORD", "change_me")
|
|
63
|
+
os.environ.setdefault("DB_NAME", "service_db")
|
|
64
|
+
|
|
65
|
+
app = build_app()
|
|
66
|
+
return TestClient(app)
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
from fastapi import HTTPException
|
|
4
4
|
|
|
5
|
+
{%- if cookiecutter.has_auth %}
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _auth_headers(token: str) -> dict[str, str]:
|
|
9
|
+
return {"Authorization": f"Bearer {token}"}
|
|
10
|
+
{%- endif %}
|
|
11
|
+
|
|
5
12
|
|
|
6
13
|
def test_health(client):
|
|
7
14
|
response = client.get("/health")
|
|
@@ -10,9 +17,13 @@ def test_health(client):
|
|
|
10
17
|
|
|
11
18
|
|
|
12
19
|
{%- if cookiecutter.has_database or not cookiecutter.has_delegates %}
|
|
13
|
-
def test_create_item_returns_data(client):
|
|
20
|
+
def test_create_item_returns_data(client{%- if cookiecutter.has_auth %}, auth_token{% endif %}):
|
|
14
21
|
create_response = client.post(
|
|
15
|
-
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}",
|
|
22
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}",
|
|
23
|
+
json={"name": "Test Item"},
|
|
24
|
+
{%- if cookiecutter.has_auth %}
|
|
25
|
+
headers=_auth_headers(auth_token),
|
|
26
|
+
{%- endif %}
|
|
16
27
|
)
|
|
17
28
|
assert create_response.status_code == 200
|
|
18
29
|
item = create_response.json()
|
|
@@ -21,11 +32,28 @@ def test_create_item_returns_data(client):
|
|
|
21
32
|
assert "created_at" in item
|
|
22
33
|
|
|
23
34
|
|
|
24
|
-
def test_list_items(client):
|
|
25
|
-
client.post(
|
|
26
|
-
|
|
35
|
+
def test_list_items(client{%- if cookiecutter.has_auth %}, auth_token{% endif %}):
|
|
36
|
+
client.post(
|
|
37
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}",
|
|
38
|
+
json={"name": "Item 1"},
|
|
39
|
+
{%- if cookiecutter.has_auth %}
|
|
40
|
+
headers=_auth_headers(auth_token),
|
|
41
|
+
{%- endif %}
|
|
42
|
+
)
|
|
43
|
+
client.post(
|
|
44
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}",
|
|
45
|
+
json={"name": "Item 2"},
|
|
46
|
+
{%- if cookiecutter.has_auth %}
|
|
47
|
+
headers=_auth_headers(auth_token),
|
|
48
|
+
{%- endif %}
|
|
49
|
+
)
|
|
27
50
|
|
|
28
|
-
response = client.get(
|
|
51
|
+
response = client.get(
|
|
52
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}",
|
|
53
|
+
{%- if cookiecutter.has_auth %}
|
|
54
|
+
headers=_auth_headers(auth_token),
|
|
55
|
+
{%- endif %}
|
|
56
|
+
)
|
|
29
57
|
assert response.status_code == 200
|
|
30
58
|
items = response.json()["{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}"]
|
|
31
59
|
names = {item["name"] for item in items}
|
|
@@ -33,46 +61,73 @@ def test_list_items(client):
|
|
|
33
61
|
assert "Item 2" in names
|
|
34
62
|
|
|
35
63
|
|
|
36
|
-
def test_get_item_missing_returns_404(client):
|
|
37
|
-
response = client.get(
|
|
64
|
+
def test_get_item_missing_returns_404(client{%- if cookiecutter.has_auth %}, auth_token{% endif %}):
|
|
65
|
+
response = client.get(
|
|
66
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/missing-id",
|
|
67
|
+
{%- if cookiecutter.has_auth %}
|
|
68
|
+
headers=_auth_headers(auth_token),
|
|
69
|
+
{%- endif %}
|
|
70
|
+
)
|
|
38
71
|
assert response.status_code == 404
|
|
39
72
|
|
|
40
73
|
|
|
41
|
-
def test_get_item_returns_created_item(client):
|
|
74
|
+
def test_get_item_returns_created_item(client{%- if cookiecutter.has_auth %}, auth_token{% endif %}):
|
|
42
75
|
created = client.post(
|
|
43
|
-
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}",
|
|
76
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}",
|
|
77
|
+
json={"name": "Lookup"},
|
|
78
|
+
{%- if cookiecutter.has_auth %}
|
|
79
|
+
headers=_auth_headers(auth_token),
|
|
80
|
+
{%- endif %}
|
|
44
81
|
).json()
|
|
45
82
|
response = client.get(
|
|
46
|
-
f"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/{created['id']}"
|
|
83
|
+
f"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/{created['id']}",
|
|
84
|
+
{%- if cookiecutter.has_auth %}
|
|
85
|
+
headers=_auth_headers(auth_token),
|
|
86
|
+
{%- endif %}
|
|
47
87
|
)
|
|
48
88
|
assert response.status_code == 200
|
|
49
89
|
assert response.json()["id"] == created["id"]
|
|
50
90
|
assert response.json()["name"] == "Lookup"
|
|
51
91
|
|
|
52
92
|
|
|
53
|
-
def test_update_and_delete_item(client):
|
|
93
|
+
def test_update_and_delete_item(client{%- if cookiecutter.has_auth %}, auth_token{% endif %}):
|
|
54
94
|
created = client.post(
|
|
55
|
-
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}",
|
|
95
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}",
|
|
96
|
+
json={"name": "Original"},
|
|
97
|
+
{%- if cookiecutter.has_auth %}
|
|
98
|
+
headers=_auth_headers(auth_token),
|
|
99
|
+
{%- endif %}
|
|
56
100
|
).json()
|
|
57
101
|
item_id = created["id"]
|
|
58
102
|
|
|
59
103
|
updated = client.put(
|
|
60
104
|
f"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/{item_id}",
|
|
61
105
|
json={"name": "Updated"},
|
|
106
|
+
{%- if cookiecutter.has_auth %}
|
|
107
|
+
headers=_auth_headers(auth_token),
|
|
108
|
+
{%- endif %}
|
|
62
109
|
)
|
|
63
110
|
assert updated.status_code == 200
|
|
64
111
|
assert updated.json()["updated"] is True
|
|
65
112
|
|
|
66
|
-
deleted = client.delete(
|
|
113
|
+
deleted = client.delete(
|
|
114
|
+
f"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/{item_id}",
|
|
115
|
+
{%- if cookiecutter.has_auth %}
|
|
116
|
+
headers=_auth_headers(auth_token),
|
|
117
|
+
{%- endif %}
|
|
118
|
+
)
|
|
67
119
|
assert deleted.status_code == 200
|
|
68
120
|
assert deleted.json()["deleted"] is True
|
|
69
121
|
|
|
70
122
|
|
|
71
|
-
def test_upsert_item(client):
|
|
123
|
+
def test_upsert_item(client{%- if cookiecutter.has_auth %}, auth_token{% endif %}):
|
|
72
124
|
item_id = "custom-upsert-id"
|
|
73
125
|
first = client.put(
|
|
74
126
|
f"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/{item_id}/upsert",
|
|
75
127
|
json={"name": "Created"},
|
|
128
|
+
{%- if cookiecutter.has_auth %}
|
|
129
|
+
headers=_auth_headers(auth_token),
|
|
130
|
+
{%- endif %}
|
|
76
131
|
)
|
|
77
132
|
assert first.status_code == 200
|
|
78
133
|
assert first.json()["rows_affected"] == 1
|
|
@@ -80,29 +135,38 @@ def test_upsert_item(client):
|
|
|
80
135
|
second = client.put(
|
|
81
136
|
f"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/{item_id}/upsert",
|
|
82
137
|
json={"name": "Updated"},
|
|
138
|
+
{%- if cookiecutter.has_auth %}
|
|
139
|
+
headers=_auth_headers(auth_token),
|
|
140
|
+
{%- endif %}
|
|
83
141
|
)
|
|
84
142
|
assert second.status_code == 200
|
|
85
143
|
assert second.json()["rows_affected"] == 1
|
|
86
144
|
|
|
87
145
|
|
|
88
|
-
def test_update_missing_returns_404(client):
|
|
146
|
+
def test_update_missing_returns_404(client{%- if cookiecutter.has_auth %}, auth_token{% endif %}):
|
|
89
147
|
response = client.put(
|
|
90
148
|
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/missing-id",
|
|
91
149
|
json={"name": "Nope"},
|
|
150
|
+
{%- if cookiecutter.has_auth %}
|
|
151
|
+
headers=_auth_headers(auth_token),
|
|
152
|
+
{%- endif %}
|
|
92
153
|
)
|
|
93
154
|
assert response.status_code == 404
|
|
94
155
|
|
|
95
156
|
|
|
96
|
-
def test_delete_missing_returns_404(client):
|
|
157
|
+
def test_delete_missing_returns_404(client{%- if cookiecutter.has_auth %}, auth_token{% endif %}):
|
|
97
158
|
response = client.delete(
|
|
98
|
-
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/missing-id"
|
|
159
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/missing-id",
|
|
160
|
+
{%- if cookiecutter.has_auth %}
|
|
161
|
+
headers=_auth_headers(auth_token),
|
|
162
|
+
{%- endif %}
|
|
99
163
|
)
|
|
100
164
|
assert response.status_code == 404
|
|
101
165
|
{%- endif %}
|
|
102
166
|
|
|
103
167
|
|
|
104
168
|
{%- if cookiecutter.has_delegates and not cookiecutter.has_database %}
|
|
105
|
-
def test_delegate_create_and_list(monkeypatch, client):
|
|
169
|
+
def test_delegate_create_and_list(monkeypatch, client{%- if cookiecutter.has_auth %}, auth_token{% endif %}):
|
|
106
170
|
calls: list[tuple[str, str]] = []
|
|
107
171
|
|
|
108
172
|
async def fake_delegate_request(method: str, path: str, payload=None):
|
|
@@ -114,28 +178,42 @@ def test_delegate_create_and_list(monkeypatch, client):
|
|
|
114
178
|
monkeypatch.setattr("{{cookiecutter.__service_name_snake}}.views.unversioned.items_view._delegate_request", fake_delegate_request)
|
|
115
179
|
|
|
116
180
|
created = client.post(
|
|
117
|
-
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}",
|
|
181
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}",
|
|
182
|
+
json={"name": "X"},
|
|
183
|
+
{%- if cookiecutter.has_auth %}
|
|
184
|
+
headers=_auth_headers(auth_token),
|
|
185
|
+
{%- endif %}
|
|
118
186
|
)
|
|
119
187
|
assert created.status_code == 200
|
|
120
188
|
assert created.json()["id"] == "d1"
|
|
121
189
|
|
|
122
|
-
listed = client.get(
|
|
190
|
+
listed = client.get(
|
|
191
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}",
|
|
192
|
+
{%- if cookiecutter.has_auth %}
|
|
193
|
+
headers=_auth_headers(auth_token),
|
|
194
|
+
{%- endif %}
|
|
195
|
+
)
|
|
123
196
|
assert listed.status_code == 200
|
|
124
197
|
assert len(listed.json()["{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}"]) == 1
|
|
125
198
|
assert calls[0][0] == "POST"
|
|
126
199
|
assert calls[1][0] == "GET"
|
|
127
200
|
|
|
128
201
|
|
|
129
|
-
def test_delegate_error_mapping(monkeypatch, client):
|
|
202
|
+
def test_delegate_error_mapping(monkeypatch, client{%- if cookiecutter.has_auth %}, auth_token{% endif %}):
|
|
130
203
|
async def fake_delegate_request(_method: str, _path: str, payload=None):
|
|
131
204
|
raise HTTPException(status_code=404, detail="not found")
|
|
132
205
|
|
|
133
206
|
monkeypatch.setattr("{{cookiecutter.__service_name_snake}}.views.unversioned.items_view._delegate_request", fake_delegate_request)
|
|
134
|
-
response = client.get(
|
|
207
|
+
response = client.get(
|
|
208
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/missing",
|
|
209
|
+
{%- if cookiecutter.has_auth %}
|
|
210
|
+
headers=_auth_headers(auth_token),
|
|
211
|
+
{%- endif %}
|
|
212
|
+
)
|
|
135
213
|
assert response.status_code == 404
|
|
136
214
|
|
|
137
215
|
|
|
138
|
-
def test_delegate_get_update_upsert_delete(monkeypatch, client):
|
|
216
|
+
def test_delegate_get_update_upsert_delete(monkeypatch, client{%- if cookiecutter.has_auth %}, auth_token{% endif %}):
|
|
139
217
|
async def fake_delegate_request(method: str, path: str, payload=None):
|
|
140
218
|
if method == "GET":
|
|
141
219
|
return {"id": "d2", "name": "From delegate", "created_at": "2026-01-01T00:00:00Z"}
|
|
@@ -149,13 +227,21 @@ def test_delegate_get_update_upsert_delete(monkeypatch, client):
|
|
|
149
227
|
|
|
150
228
|
monkeypatch.setattr("{{cookiecutter.__service_name_snake}}.views.unversioned.items_view._delegate_request", fake_delegate_request)
|
|
151
229
|
|
|
152
|
-
fetched = client.get(
|
|
230
|
+
fetched = client.get(
|
|
231
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/d2",
|
|
232
|
+
{%- if cookiecutter.has_auth %}
|
|
233
|
+
headers=_auth_headers(auth_token),
|
|
234
|
+
{%- endif %}
|
|
235
|
+
)
|
|
153
236
|
assert fetched.status_code == 200
|
|
154
237
|
assert fetched.json()["id"] == "d2"
|
|
155
238
|
|
|
156
239
|
updated = client.put(
|
|
157
240
|
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/d2",
|
|
158
241
|
json={"name": "Updated"},
|
|
242
|
+
{%- if cookiecutter.has_auth %}
|
|
243
|
+
headers=_auth_headers(auth_token),
|
|
244
|
+
{%- endif %}
|
|
159
245
|
)
|
|
160
246
|
assert updated.status_code == 200
|
|
161
247
|
assert updated.json()["updated"] is True
|
|
@@ -163,20 +249,33 @@ def test_delegate_get_update_upsert_delete(monkeypatch, client):
|
|
|
163
249
|
upserted = client.put(
|
|
164
250
|
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/d2/upsert",
|
|
165
251
|
json={"name": "Upserted"},
|
|
252
|
+
{%- if cookiecutter.has_auth %}
|
|
253
|
+
headers=_auth_headers(auth_token),
|
|
254
|
+
{%- endif %}
|
|
166
255
|
)
|
|
167
256
|
assert upserted.status_code == 200
|
|
168
257
|
assert upserted.json()["rows_affected"] == 1
|
|
169
258
|
|
|
170
|
-
deleted = client.delete(
|
|
259
|
+
deleted = client.delete(
|
|
260
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}/d2",
|
|
261
|
+
{%- if cookiecutter.has_auth %}
|
|
262
|
+
headers=_auth_headers(auth_token),
|
|
263
|
+
{%- endif %}
|
|
264
|
+
)
|
|
171
265
|
assert deleted.status_code == 200
|
|
172
266
|
assert deleted.json()["deleted"] is True
|
|
173
267
|
|
|
174
268
|
|
|
175
|
-
def test_delegate_list_invalid_payload_returns_502(monkeypatch, client):
|
|
269
|
+
def test_delegate_list_invalid_payload_returns_502(monkeypatch, client{%- if cookiecutter.has_auth %}, auth_token{% endif %}):
|
|
176
270
|
async def fake_delegate_request(_method: str, _path: str, payload=None):
|
|
177
271
|
return {"{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}": "bad"}
|
|
178
272
|
|
|
179
273
|
monkeypatch.setattr("{{cookiecutter.__service_name_snake}}.views.unversioned.items_view._delegate_request", fake_delegate_request)
|
|
180
|
-
response = client.get(
|
|
274
|
+
response = client.get(
|
|
275
|
+
"/api/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}",
|
|
276
|
+
{%- if cookiecutter.has_auth %}
|
|
277
|
+
headers=_auth_headers(auth_token),
|
|
278
|
+
{%- endif %}
|
|
279
|
+
)
|
|
181
280
|
assert response.status_code == 502
|
|
182
281
|
{%- endif %}
|
|
@@ -11,6 +11,9 @@ from pydantic import BaseModel
|
|
|
11
11
|
{%- if cookiecutter.has_database %}
|
|
12
12
|
from ...dependencies import RepositoryDep
|
|
13
13
|
{%- endif %}
|
|
14
|
+
{%- if cookiecutter.has_auth %}
|
|
15
|
+
from ...dependencies import VerifiedTokenDep
|
|
16
|
+
{%- endif %}
|
|
14
17
|
|
|
15
18
|
ITEMS_ROUTE = "/{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}"
|
|
16
19
|
|
|
@@ -89,6 +92,9 @@ def _dummy_item(record_id: str, name: str, created_at: str) -> dict[str, str]:
|
|
|
89
92
|
@router.post(ITEMS_ROUTE, response_model={{ cookiecutter.model_name }}Response)
|
|
90
93
|
async def create_{{ cookiecutter.model_name | lower }}(
|
|
91
94
|
payload: Create{{ cookiecutter.model_name }}Request,
|
|
95
|
+
{%- if cookiecutter.has_auth %}
|
|
96
|
+
_verified: VerifiedTokenDep,
|
|
97
|
+
{%- endif %}
|
|
92
98
|
{%- if cookiecutter.has_database %}
|
|
93
99
|
repo: RepositoryDep,
|
|
94
100
|
{%- endif %}
|
|
@@ -113,6 +119,9 @@ async def create_{{ cookiecutter.model_name | lower }}(
|
|
|
113
119
|
|
|
114
120
|
@router.get(ITEMS_ROUTE, response_model={{ cookiecutter.model_name }}ListResponse)
|
|
115
121
|
async def list_{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}(
|
|
122
|
+
{%- if cookiecutter.has_auth %}
|
|
123
|
+
_verified: VerifiedTokenDep,
|
|
124
|
+
{%- endif %}
|
|
116
125
|
{%- if cookiecutter.has_database %}
|
|
117
126
|
repo: RepositoryDep,
|
|
118
127
|
{%- endif %}
|
|
@@ -134,6 +143,9 @@ async def list_{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}(
|
|
|
134
143
|
@router.get(f"{ITEMS_ROUTE}" + "/{entity_id}", response_model={{ cookiecutter.model_name }}Response)
|
|
135
144
|
async def get_{{ cookiecutter.model_name | lower }}(
|
|
136
145
|
entity_id: str,
|
|
146
|
+
{%- if cookiecutter.has_auth %}
|
|
147
|
+
_verified: VerifiedTokenDep,
|
|
148
|
+
{%- endif %}
|
|
137
149
|
{%- if cookiecutter.has_database %}
|
|
138
150
|
repo: RepositoryDep,
|
|
139
151
|
{%- endif %}
|
|
@@ -161,6 +173,9 @@ async def get_{{ cookiecutter.model_name | lower }}(
|
|
|
161
173
|
async def update_{{ cookiecutter.model_name | lower }}(
|
|
162
174
|
entity_id: str,
|
|
163
175
|
payload: Update{{ cookiecutter.model_name }}Request,
|
|
176
|
+
{%- if cookiecutter.has_auth %}
|
|
177
|
+
_verified: VerifiedTokenDep,
|
|
178
|
+
{%- endif %}
|
|
164
179
|
{%- if cookiecutter.has_database %}
|
|
165
180
|
repo: RepositoryDep,
|
|
166
181
|
{%- endif %}
|
|
@@ -197,6 +212,9 @@ async def update_{{ cookiecutter.model_name | lower }}(
|
|
|
197
212
|
async def upsert_{{ cookiecutter.model_name | lower }}(
|
|
198
213
|
entity_id: str,
|
|
199
214
|
payload: Update{{ cookiecutter.model_name }}Request,
|
|
215
|
+
{%- if cookiecutter.has_auth %}
|
|
216
|
+
_verified: VerifiedTokenDep,
|
|
217
|
+
{%- endif %}
|
|
200
218
|
{%- if cookiecutter.has_database %}
|
|
201
219
|
repo: RepositoryDep,
|
|
202
220
|
{%- endif %}
|
|
@@ -229,6 +247,9 @@ async def upsert_{{ cookiecutter.model_name | lower }}(
|
|
|
229
247
|
@router.delete(f"{ITEMS_ROUTE}" + "/{entity_id}", response_model={{ cookiecutter.model_name }}DeleteResponse)
|
|
230
248
|
async def delete_{{ cookiecutter.model_name | lower }}(
|
|
231
249
|
entity_id: str,
|
|
250
|
+
{%- if cookiecutter.has_auth %}
|
|
251
|
+
_verified: VerifiedTokenDep,
|
|
252
|
+
{%- endif %}
|
|
232
253
|
{%- if cookiecutter.has_database %}
|
|
233
254
|
repo: RepositoryDep,
|
|
234
255
|
{%- endif %}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"""Shared test fixtures for {{ cookiecutter.service_name }}."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
6
|
-
from fastapi.testclient import TestClient
|
|
7
|
-
|
|
8
|
-
from {{cookiecutter.__service_name_snake}} import build_app
|
|
9
|
-
|
|
10
|
-
HAS_DATABASE = {{"True" if cookiecutter.has_database else "False"}}
|
|
11
|
-
DB_KIND = "{{ cookiecutter.database }}"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# [INSERT: fixtures]
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@pytest.fixture
|
|
18
|
-
def client():
|
|
19
|
-
if HAS_DATABASE and DB_KIND == "maria":
|
|
20
|
-
os.environ.setdefault("DB_HOST", "localhost")
|
|
21
|
-
os.environ.setdefault("DB_PORT", "3306")
|
|
22
|
-
os.environ.setdefault("DB_USER", "service_user")
|
|
23
|
-
os.environ.setdefault("DB_PASSWORD", "change_me")
|
|
24
|
-
os.environ.setdefault("DB_NAME", "service_db")
|
|
25
|
-
elif HAS_DATABASE and DB_KIND == "postgres":
|
|
26
|
-
os.environ.setdefault("DB_HOST", "localhost")
|
|
27
|
-
os.environ.setdefault("DB_PORT", "5432")
|
|
28
|
-
os.environ.setdefault("DB_USER", "service_user")
|
|
29
|
-
os.environ.setdefault("DB_PASSWORD", "change_me")
|
|
30
|
-
os.environ.setdefault("DB_NAME", "service_db")
|
|
31
|
-
|
|
32
|
-
app = build_app()
|
|
33
|
-
return TestClient(app)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/caching/conftest.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/caching/docker-compose.fragment.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/caching/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/caching/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/logging/conftest.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/logging/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/logging/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/metrics/conftest.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/metrics/docker-compose.fragment.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/metrics/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/metrics/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/tracing/conftest.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/tracing/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/tracing/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/conftest.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/docker-compose.fragment.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/settings.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/src/app/workers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/src/app/workers/tasks.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/features/workers/tests/test_workers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.60 → csrd_utils-0.3.62}/src/csrd_utils/templates/cookiecutter-service/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|