csrd-utils 0.3.18__tar.gz → 0.3.19__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.18 → csrd_utils-0.3.19}/PKG-INFO +2 -2
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/README.md +1 -1
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/pyproject.toml +1 -1
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/__main__.py +50 -24
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/augmentor.py +18 -2
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/doctor.py +8 -1
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/unversioned/items_view.py +44 -12
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/.gitignore +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/__init__.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/audit.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/cluster.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/caching/conftest.fragment.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/caching/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/caching/manifest.yaml +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/caching/requirements.fragment +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/caching/settings.fragment.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/caching/src/app/cache.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/caching/tests/test_cache.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/logging/conftest.fragment.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/logging/manifest.yaml +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/logging/requirements.fragment +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/logging/settings.fragment.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/logging/src/app/middleware/logging.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/logging/tests/test_logging_middleware.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/metrics/conftest.fragment.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/metrics/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/metrics/manifest.yaml +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/metrics/requirements.fragment +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/metrics/settings.fragment.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/metrics/src/app/middleware/metrics.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/metrics/tests/test_metrics_middleware.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/tracing/conftest.fragment.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/tracing/manifest.yaml +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/tracing/requirements.fragment +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/tracing/settings.fragment.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/tracing/src/app/middleware/tracing.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/tracing/tests/test_tracing_middleware.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/conftest.fragment.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/manifest.yaml +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/requirements.fragment +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/settings.fragment.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/src/app/workers/__init__.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/src/app/workers/celery_app.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/src/app/workers/tasks.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/tests/test_workers.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/fragments.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/generator.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/resources.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/README.md +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/cookiecutter.json +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/hooks/post_gen_project.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/.dockerignore +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/.env.example +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/.gitignore +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/Dockerfile +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/README.md +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/docker-compose.yml +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/pyproject.toml +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/requirements.txt +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/__init__.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/cache.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/delegates/__init__.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/delegates/upstreams.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/__init__.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/delegates.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/item_repository.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/worker_broker.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/middleware/__init__.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/middleware/logging.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/py.typed +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/repositories/__init__.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/repositories/item_repository.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/settings.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/__init__.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/workers/__init__.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/workers/tasks.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/__init__.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/acceptance/test_logging_middleware.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/conftest.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/test_items.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/unit/test_cache.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/unit/test_database_settings.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/unit/test_delegates.py +0 -0
- {csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/unit/test_worker_broker.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.19
|
|
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
|
|
@@ -51,7 +51,7 @@ csrd audit --service . # explicitly audit one servi
|
|
|
51
51
|
- Non-interactive generation errors when no workspace marker is present.
|
|
52
52
|
- Workspace cluster source of truth is persisted at `.csrd/cluster.yaml`.
|
|
53
53
|
- If `.csrd/cluster.yaml` already exists, `csrd new service` appends the generated service and refreshes scaffold files (`docker-compose.yml`, `.env.example`, `README.md`, `smoke.sh`). Service source directories and running containers are never touched.
|
|
54
|
-
- If `.csrd/cluster.yaml` is missing
|
|
54
|
+
- If `.csrd/cluster.yaml` is missing, `csrd new service` bootstraps `.csrd/cluster.yaml` and writes initial cluster scaffold files.
|
|
55
55
|
- `csrd new service` runs a compact interactive flow by default (core prompts 1-6), then asks whether to show advanced options.
|
|
56
56
|
- `csrd new workspace` creates a workspace directory and prompts whether to generate `none`, `service`, or `cluster` immediately.
|
|
57
57
|
- In compact mode, advanced options are hidden by default.
|
|
@@ -38,7 +38,7 @@ csrd audit --service . # explicitly audit one servi
|
|
|
38
38
|
- Non-interactive generation errors when no workspace marker is present.
|
|
39
39
|
- Workspace cluster source of truth is persisted at `.csrd/cluster.yaml`.
|
|
40
40
|
- If `.csrd/cluster.yaml` already exists, `csrd new service` appends the generated service and refreshes scaffold files (`docker-compose.yml`, `.env.example`, `README.md`, `smoke.sh`). Service source directories and running containers are never touched.
|
|
41
|
-
- If `.csrd/cluster.yaml` is missing
|
|
41
|
+
- If `.csrd/cluster.yaml` is missing, `csrd new service` bootstraps `.csrd/cluster.yaml` and writes initial cluster scaffold files.
|
|
42
42
|
- `csrd new service` runs a compact interactive flow by default (core prompts 1-6), then asks whether to show advanced options.
|
|
43
43
|
- `csrd new workspace` creates a workspace directory and prompts whether to generate `none`, `service`, or `cluster` immediately.
|
|
44
44
|
- In compact mode, advanced options are hidden by default.
|
|
@@ -489,21 +489,27 @@ def _append_service_to_workspace_cluster(
|
|
|
489
489
|
|
|
490
490
|
|
|
491
491
|
def _workspace_service_roots(workspace_root: Path) -> list[Path]:
|
|
492
|
-
roots:
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
492
|
+
roots: set[Path] = set()
|
|
493
|
+
|
|
494
|
+
def _collect_from(base_dir: Path, skip_names: set[str] | None = None) -> None:
|
|
495
|
+
if not base_dir.is_dir():
|
|
496
|
+
return
|
|
497
|
+
for child in base_dir.iterdir():
|
|
498
|
+
if not child.is_dir() or child.name.startswith("."):
|
|
499
|
+
continue
|
|
500
|
+
if skip_names and child.name in skip_names:
|
|
501
|
+
continue
|
|
502
|
+
if (
|
|
503
|
+
(child / "src" / "app").is_dir()
|
|
504
|
+
and (child / "tests").is_dir()
|
|
505
|
+
and (child / "requirements.txt").is_file()
|
|
506
|
+
):
|
|
507
|
+
roots.add(child.resolve())
|
|
496
508
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
(child / "src" / "app").is_dir()
|
|
502
|
-
and (child / "tests").is_dir()
|
|
503
|
-
and (child / "requirements.txt").is_file()
|
|
504
|
-
and (child / "docker-compose.yml").is_file()
|
|
505
|
-
):
|
|
506
|
-
roots.append(child)
|
|
509
|
+
services_root = _workspace_services_dir(workspace_root)
|
|
510
|
+
_collect_from(services_root)
|
|
511
|
+
# Legacy fallback: older workspaces stored services at workspace root.
|
|
512
|
+
_collect_from(workspace_root, skip_names={"src"})
|
|
507
513
|
return sorted(roots)
|
|
508
514
|
|
|
509
515
|
|
|
@@ -514,20 +520,24 @@ def _resolve_feature_service_root(service_arg: Path) -> Path:
|
|
|
514
520
|
|
|
515
521
|
service_roots = _workspace_service_roots(candidate)
|
|
516
522
|
if len(service_roots) == 1:
|
|
517
|
-
print(f"Detected workspace service target: {service_roots[0]}")
|
|
518
523
|
return service_roots[0]
|
|
519
524
|
if not service_roots:
|
|
520
|
-
raise ValueError(
|
|
521
|
-
"No services found under workspace src/. Pass --service with a service root path."
|
|
522
|
-
)
|
|
525
|
+
raise ValueError("No services found in workspace. Pass --service with a service root path.")
|
|
523
526
|
raise ValueError(
|
|
524
|
-
"Multiple services found
|
|
527
|
+
"Multiple services found in workspace. Pass --service with a specific service root path."
|
|
525
528
|
)
|
|
526
529
|
|
|
527
530
|
|
|
528
|
-
def
|
|
531
|
+
def _workspace_root_for_service(service_root: Path) -> Path | None:
|
|
532
|
+
parent = service_root.parent
|
|
533
|
+
if parent.name == "src" and _is_workspace(parent.parent):
|
|
534
|
+
return parent.parent
|
|
535
|
+
return None
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def _infer_port_from_compose(compose_path: Path, service_name: str | None = None) -> int:
|
|
529
539
|
try:
|
|
530
|
-
payload = yaml.safe_load(
|
|
540
|
+
payload = yaml.safe_load(compose_path.read_text(encoding="utf-8"))
|
|
531
541
|
except (OSError, yaml.YAMLError):
|
|
532
542
|
return 8080
|
|
533
543
|
|
|
@@ -537,10 +547,15 @@ def _infer_port_from_service_root(service_root: Path) -> int:
|
|
|
537
547
|
if not isinstance(services, dict) or not services:
|
|
538
548
|
return 8080
|
|
539
549
|
|
|
540
|
-
|
|
541
|
-
if
|
|
550
|
+
selected: object
|
|
551
|
+
if service_name and service_name in services:
|
|
552
|
+
selected = services[service_name]
|
|
553
|
+
else:
|
|
554
|
+
selected = next(iter(services.values()))
|
|
555
|
+
if not isinstance(selected, dict):
|
|
542
556
|
return 8080
|
|
543
|
-
|
|
557
|
+
|
|
558
|
+
ports = selected.get("ports")
|
|
544
559
|
if not isinstance(ports, list) or not ports:
|
|
545
560
|
return 8080
|
|
546
561
|
|
|
@@ -554,6 +569,17 @@ def _infer_port_from_service_root(service_root: Path) -> int:
|
|
|
554
569
|
return 8080
|
|
555
570
|
|
|
556
571
|
|
|
572
|
+
def _infer_port_from_service_root(service_root: Path) -> int:
|
|
573
|
+
service_compose = service_root / "docker-compose.yml"
|
|
574
|
+
if service_compose.is_file():
|
|
575
|
+
return _infer_port_from_compose(service_compose)
|
|
576
|
+
|
|
577
|
+
workspace_root = _workspace_root_for_service(service_root)
|
|
578
|
+
if workspace_root is not None:
|
|
579
|
+
return _infer_port_from_compose(workspace_root / "docker-compose.yml", service_root.name)
|
|
580
|
+
return 8080
|
|
581
|
+
|
|
582
|
+
|
|
557
583
|
def _infer_existing_workspace_service(service_root: Path) -> ServiceSpec:
|
|
558
584
|
return {
|
|
559
585
|
"name": service_root.name,
|
|
@@ -80,7 +80,7 @@ class ServiceAugmentor:
|
|
|
80
80
|
True if valid
|
|
81
81
|
"""
|
|
82
82
|
required_dirs = ["src/app", "tests"]
|
|
83
|
-
required_files = ["
|
|
83
|
+
required_files = ["requirements.txt"]
|
|
84
84
|
|
|
85
85
|
for d in required_dirs:
|
|
86
86
|
if not (self.service_root / d).is_dir():
|
|
@@ -92,8 +92,24 @@ class ServiceAugmentor:
|
|
|
92
92
|
print(f"❌ Missing required file: {f}")
|
|
93
93
|
return False
|
|
94
94
|
|
|
95
|
+
if not self._docker_compose_path().is_file():
|
|
96
|
+
print("❌ Missing required file: docker-compose.yml")
|
|
97
|
+
return False
|
|
98
|
+
|
|
95
99
|
return True
|
|
96
100
|
|
|
101
|
+
def _docker_compose_path(self) -> Path:
|
|
102
|
+
local = self.service_root / "docker-compose.yml"
|
|
103
|
+
if local.is_file():
|
|
104
|
+
return local
|
|
105
|
+
|
|
106
|
+
parent = self.service_root.parent
|
|
107
|
+
if parent.name == "src" and (parent.parent / ".csrd-workspace").is_file():
|
|
108
|
+
workspace_compose = parent.parent / "docker-compose.yml"
|
|
109
|
+
if workspace_compose.is_file():
|
|
110
|
+
return workspace_compose
|
|
111
|
+
return local
|
|
112
|
+
|
|
97
113
|
def validate_requirements(self) -> list[str]:
|
|
98
114
|
"""
|
|
99
115
|
Validate that feature requirements are met.
|
|
@@ -189,7 +205,7 @@ class ServiceAugmentor:
|
|
|
189
205
|
return True # Not required
|
|
190
206
|
|
|
191
207
|
try:
|
|
192
|
-
dc_path = self.
|
|
208
|
+
dc_path = self._docker_compose_path()
|
|
193
209
|
self.backup_file(dc_path)
|
|
194
210
|
|
|
195
211
|
# Load YAMLs
|
|
@@ -20,7 +20,11 @@ def run_doctor(service_root: Path) -> DoctorReport:
|
|
|
20
20
|
warnings: list[str] = []
|
|
21
21
|
|
|
22
22
|
required_dirs = ["src/app", "tests"]
|
|
23
|
-
required_files = ["
|
|
23
|
+
required_files = ["requirements.txt"]
|
|
24
|
+
|
|
25
|
+
compose_candidates = [root / "docker-compose.yml"]
|
|
26
|
+
if root.parent.name == "src" and (root.parent.parent / ".csrd-workspace").is_file():
|
|
27
|
+
compose_candidates.append(root.parent.parent / "docker-compose.yml")
|
|
24
28
|
|
|
25
29
|
for rel in required_dirs:
|
|
26
30
|
if not (root / rel).is_dir():
|
|
@@ -30,6 +34,9 @@ def run_doctor(service_root: Path) -> DoctorReport:
|
|
|
30
34
|
if not (root / rel).is_file():
|
|
31
35
|
errors.append(f"Missing required file: {rel}")
|
|
32
36
|
|
|
37
|
+
if not any(path.is_file() for path in compose_candidates):
|
|
38
|
+
errors.append("Missing required file: docker-compose.yml")
|
|
39
|
+
|
|
33
40
|
marker_files = {
|
|
34
41
|
"src/app/settings.py": "# [INSERT: fields]",
|
|
35
42
|
"tests/conftest.py": "# [INSERT: fixtures]",
|
|
@@ -31,6 +31,33 @@ class Update{{ cookiecutter.model_name }}Request(BaseModel):
|
|
|
31
31
|
name: str
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
class {{ cookiecutter.model_name }}Response(BaseModel):
|
|
35
|
+
id: str
|
|
36
|
+
name: str
|
|
37
|
+
created_at: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class {{ cookiecutter.model_name }}ListResponse(BaseModel):
|
|
41
|
+
{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}: list[{{ cookiecutter.model_name }}Response]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class {{ cookiecutter.model_name }}UpdateResponse(BaseModel):
|
|
45
|
+
id: str
|
|
46
|
+
name: str
|
|
47
|
+
updated: bool
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class {{ cookiecutter.model_name }}UpsertResponse(BaseModel):
|
|
51
|
+
id: str
|
|
52
|
+
name: str
|
|
53
|
+
rows_affected: int
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class {{ cookiecutter.model_name }}DeleteResponse(BaseModel):
|
|
57
|
+
id: str
|
|
58
|
+
deleted: bool
|
|
59
|
+
|
|
60
|
+
|
|
34
61
|
{%- if cookiecutter.has_delegates and not cookiecutter.has_database %}
|
|
35
62
|
async def _delegate_request(
|
|
36
63
|
method: str,
|
|
@@ -59,13 +86,13 @@ def _dummy_item(record_id: str, name: str, created_at: str) -> dict[str, str]:
|
|
|
59
86
|
{%- endif %}
|
|
60
87
|
|
|
61
88
|
|
|
62
|
-
@router.post(ITEMS_ROUTE)
|
|
89
|
+
@router.post(ITEMS_ROUTE, response_model={{ cookiecutter.model_name }}Response)
|
|
63
90
|
async def create_{{ cookiecutter.model_name | lower }}(
|
|
64
91
|
payload: Create{{ cookiecutter.model_name }}Request,
|
|
65
92
|
{%- if cookiecutter.has_database %}
|
|
66
93
|
repo: RepositoryDep,
|
|
67
94
|
{%- endif %}
|
|
68
|
-
) ->
|
|
95
|
+
) -> {{ cookiecutter.model_name }}Response:
|
|
69
96
|
{%- if cookiecutter.has_database %}
|
|
70
97
|
return await repo.create(payload.name)
|
|
71
98
|
{%- elif cookiecutter.has_delegates %}
|
|
@@ -84,12 +111,12 @@ async def create_{{ cookiecutter.model_name | lower }}(
|
|
|
84
111
|
{%- endif %}
|
|
85
112
|
|
|
86
113
|
|
|
87
|
-
@router.get(ITEMS_ROUTE)
|
|
114
|
+
@router.get(ITEMS_ROUTE, response_model={{ cookiecutter.model_name }}ListResponse)
|
|
88
115
|
async def list_{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}(
|
|
89
116
|
{%- if cookiecutter.has_database %}
|
|
90
117
|
repo: RepositoryDep,
|
|
91
118
|
{%- endif %}
|
|
92
|
-
) ->
|
|
119
|
+
) -> {{ cookiecutter.model_name }}ListResponse:
|
|
93
120
|
{%- if cookiecutter.has_database %}
|
|
94
121
|
return {"{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}": await repo.list()}
|
|
95
122
|
{%- elif cookiecutter.has_delegates %}
|
|
@@ -104,13 +131,13 @@ async def list_{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}(
|
|
|
104
131
|
{%- endif %}
|
|
105
132
|
|
|
106
133
|
|
|
107
|
-
@router.get(f"{ITEMS_ROUTE}" + "/{entity_id}")
|
|
134
|
+
@router.get(f"{ITEMS_ROUTE}" + "/{entity_id}", response_model={{ cookiecutter.model_name }}Response)
|
|
108
135
|
async def get_{{ cookiecutter.model_name | lower }}(
|
|
109
136
|
entity_id: str,
|
|
110
137
|
{%- if cookiecutter.has_database %}
|
|
111
138
|
repo: RepositoryDep,
|
|
112
139
|
{%- endif %}
|
|
113
|
-
) ->
|
|
140
|
+
) -> {{ cookiecutter.model_name }}Response:
|
|
114
141
|
{%- if cookiecutter.has_database %}
|
|
115
142
|
item = await repo.get(entity_id)
|
|
116
143
|
{%- elif cookiecutter.has_delegates %}
|
|
@@ -128,14 +155,16 @@ async def get_{{ cookiecutter.model_name | lower }}(
|
|
|
128
155
|
return item
|
|
129
156
|
|
|
130
157
|
|
|
131
|
-
@router.put(
|
|
158
|
+
@router.put(
|
|
159
|
+
f"{ITEMS_ROUTE}" + "/{entity_id}", response_model={{ cookiecutter.model_name }}UpdateResponse
|
|
160
|
+
)
|
|
132
161
|
async def update_{{ cookiecutter.model_name | lower }}(
|
|
133
162
|
entity_id: str,
|
|
134
163
|
payload: Update{{ cookiecutter.model_name }}Request,
|
|
135
164
|
{%- if cookiecutter.has_database %}
|
|
136
165
|
repo: RepositoryDep,
|
|
137
166
|
{%- endif %}
|
|
138
|
-
) ->
|
|
167
|
+
) -> {{ cookiecutter.model_name }}UpdateResponse:
|
|
139
168
|
{%- if cookiecutter.has_database %}
|
|
140
169
|
rows = await repo.update(entity_id, payload.name)
|
|
141
170
|
{%- elif cookiecutter.has_delegates %}
|
|
@@ -161,14 +190,17 @@ async def update_{{ cookiecutter.model_name | lower }}(
|
|
|
161
190
|
return {"id": entity_id, "name": payload.name, "updated": True}
|
|
162
191
|
|
|
163
192
|
|
|
164
|
-
@router.put(
|
|
193
|
+
@router.put(
|
|
194
|
+
f"{ITEMS_ROUTE}" + "/{entity_id}/upsert",
|
|
195
|
+
response_model={{ cookiecutter.model_name }}UpsertResponse,
|
|
196
|
+
)
|
|
165
197
|
async def upsert_{{ cookiecutter.model_name | lower }}(
|
|
166
198
|
entity_id: str,
|
|
167
199
|
payload: Update{{ cookiecutter.model_name }}Request,
|
|
168
200
|
{%- if cookiecutter.has_database %}
|
|
169
201
|
repo: RepositoryDep,
|
|
170
202
|
{%- endif %}
|
|
171
|
-
) ->
|
|
203
|
+
) -> {{ cookiecutter.model_name }}UpsertResponse:
|
|
172
204
|
{%- if cookiecutter.has_database %}
|
|
173
205
|
rows = await repo.upsert(entity_id, payload.name)
|
|
174
206
|
{%- elif cookiecutter.has_delegates %}
|
|
@@ -194,13 +226,13 @@ async def upsert_{{ cookiecutter.model_name | lower }}(
|
|
|
194
226
|
return {"id": entity_id, "name": payload.name, "rows_affected": rows}
|
|
195
227
|
|
|
196
228
|
|
|
197
|
-
@router.delete(f"{ITEMS_ROUTE}" + "/{entity_id}")
|
|
229
|
+
@router.delete(f"{ITEMS_ROUTE}" + "/{entity_id}", response_model={{ cookiecutter.model_name }}DeleteResponse)
|
|
198
230
|
async def delete_{{ cookiecutter.model_name | lower }}(
|
|
199
231
|
entity_id: str,
|
|
200
232
|
{%- if cookiecutter.has_database %}
|
|
201
233
|
repo: RepositoryDep,
|
|
202
234
|
{%- endif %}
|
|
203
|
-
) ->
|
|
235
|
+
) -> {{ cookiecutter.model_name }}DeleteResponse:
|
|
204
236
|
{%- if cookiecutter.has_database %}
|
|
205
237
|
rows = await repo.delete(entity_id)
|
|
206
238
|
{%- elif cookiecutter.has_delegates %}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/caching/conftest.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/caching/docker-compose.fragment.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/caching/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/caching/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/logging/conftest.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/logging/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/logging/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/metrics/conftest.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/metrics/docker-compose.fragment.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/metrics/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/metrics/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/tracing/conftest.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/tracing/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/tracing/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/conftest.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/docker-compose.fragment.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/settings.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/src/app/workers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/src/app/workers/tasks.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/src/csrd_utils/features/workers/tests/test_workers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.18 → csrd_utils-0.3.19}/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
|