solokit 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- solokit/__init__.py +10 -0
- solokit/__version__.py +3 -0
- solokit/cli.py +374 -0
- solokit/core/__init__.py +1 -0
- solokit/core/cache.py +102 -0
- solokit/core/command_runner.py +278 -0
- solokit/core/config.py +453 -0
- solokit/core/config_validator.py +204 -0
- solokit/core/constants.py +291 -0
- solokit/core/error_formatter.py +279 -0
- solokit/core/error_handlers.py +346 -0
- solokit/core/exceptions.py +1567 -0
- solokit/core/file_ops.py +309 -0
- solokit/core/logging_config.py +166 -0
- solokit/core/output.py +99 -0
- solokit/core/performance.py +57 -0
- solokit/core/protocols.py +141 -0
- solokit/core/types.py +312 -0
- solokit/deployment/__init__.py +1 -0
- solokit/deployment/executor.py +411 -0
- solokit/git/__init__.py +1 -0
- solokit/git/integration.py +619 -0
- solokit/init/__init__.py +41 -0
- solokit/init/claude_commands_installer.py +87 -0
- solokit/init/dependency_installer.py +313 -0
- solokit/init/docs_structure.py +90 -0
- solokit/init/env_generator.py +160 -0
- solokit/init/environment_validator.py +334 -0
- solokit/init/git_hooks_installer.py +71 -0
- solokit/init/git_setup.py +188 -0
- solokit/init/gitignore_updater.py +195 -0
- solokit/init/initial_commit.py +145 -0
- solokit/init/initial_scans.py +109 -0
- solokit/init/orchestrator.py +246 -0
- solokit/init/readme_generator.py +207 -0
- solokit/init/session_structure.py +239 -0
- solokit/init/template_installer.py +424 -0
- solokit/learning/__init__.py +1 -0
- solokit/learning/archiver.py +115 -0
- solokit/learning/categorizer.py +126 -0
- solokit/learning/curator.py +428 -0
- solokit/learning/extractor.py +352 -0
- solokit/learning/reporter.py +351 -0
- solokit/learning/repository.py +254 -0
- solokit/learning/similarity.py +342 -0
- solokit/learning/validator.py +144 -0
- solokit/project/__init__.py +1 -0
- solokit/project/init.py +1162 -0
- solokit/project/stack.py +436 -0
- solokit/project/sync_plugin.py +438 -0
- solokit/project/tree.py +375 -0
- solokit/quality/__init__.py +1 -0
- solokit/quality/api_validator.py +424 -0
- solokit/quality/checkers/__init__.py +25 -0
- solokit/quality/checkers/base.py +114 -0
- solokit/quality/checkers/context7.py +221 -0
- solokit/quality/checkers/custom.py +162 -0
- solokit/quality/checkers/deployment.py +323 -0
- solokit/quality/checkers/documentation.py +179 -0
- solokit/quality/checkers/formatting.py +161 -0
- solokit/quality/checkers/integration.py +394 -0
- solokit/quality/checkers/linting.py +159 -0
- solokit/quality/checkers/security.py +261 -0
- solokit/quality/checkers/spec_completeness.py +127 -0
- solokit/quality/checkers/tests.py +184 -0
- solokit/quality/env_validator.py +306 -0
- solokit/quality/gates.py +655 -0
- solokit/quality/reporters/__init__.py +10 -0
- solokit/quality/reporters/base.py +25 -0
- solokit/quality/reporters/console.py +98 -0
- solokit/quality/reporters/json_reporter.py +34 -0
- solokit/quality/results.py +98 -0
- solokit/session/__init__.py +1 -0
- solokit/session/briefing/__init__.py +245 -0
- solokit/session/briefing/documentation_loader.py +53 -0
- solokit/session/briefing/formatter.py +476 -0
- solokit/session/briefing/git_context.py +282 -0
- solokit/session/briefing/learning_loader.py +212 -0
- solokit/session/briefing/milestone_builder.py +78 -0
- solokit/session/briefing/orchestrator.py +137 -0
- solokit/session/briefing/stack_detector.py +51 -0
- solokit/session/briefing/tree_generator.py +52 -0
- solokit/session/briefing/work_item_loader.py +209 -0
- solokit/session/briefing.py +353 -0
- solokit/session/complete.py +1188 -0
- solokit/session/status.py +246 -0
- solokit/session/validate.py +452 -0
- solokit/templates/.claude/commands/end.md +109 -0
- solokit/templates/.claude/commands/init.md +159 -0
- solokit/templates/.claude/commands/learn-curate.md +88 -0
- solokit/templates/.claude/commands/learn-search.md +62 -0
- solokit/templates/.claude/commands/learn-show.md +69 -0
- solokit/templates/.claude/commands/learn.md +136 -0
- solokit/templates/.claude/commands/start.md +114 -0
- solokit/templates/.claude/commands/status.md +22 -0
- solokit/templates/.claude/commands/validate.md +27 -0
- solokit/templates/.claude/commands/work-delete.md +119 -0
- solokit/templates/.claude/commands/work-graph.md +139 -0
- solokit/templates/.claude/commands/work-list.md +26 -0
- solokit/templates/.claude/commands/work-new.md +114 -0
- solokit/templates/.claude/commands/work-next.md +25 -0
- solokit/templates/.claude/commands/work-show.md +24 -0
- solokit/templates/.claude/commands/work-update.md +141 -0
- solokit/templates/CHANGELOG.md +17 -0
- solokit/templates/WORK_ITEM_TYPES.md +141 -0
- solokit/templates/__init__.py +1 -0
- solokit/templates/bug_spec.md +217 -0
- solokit/templates/config.schema.json +150 -0
- solokit/templates/dashboard_refine/base/.gitignore +36 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/layout.tsx +22 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/page.tsx +68 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/users/page.tsx +77 -0
- solokit/templates/dashboard_refine/base/app/globals.css +60 -0
- solokit/templates/dashboard_refine/base/app/layout.tsx +23 -0
- solokit/templates/dashboard_refine/base/app/page.tsx +9 -0
- solokit/templates/dashboard_refine/base/components/client-refine-wrapper.tsx +21 -0
- solokit/templates/dashboard_refine/base/components/layout/header.tsx +44 -0
- solokit/templates/dashboard_refine/base/components/layout/sidebar.tsx +82 -0
- solokit/templates/dashboard_refine/base/components/ui/button.tsx +53 -0
- solokit/templates/dashboard_refine/base/components/ui/card.tsx +78 -0
- solokit/templates/dashboard_refine/base/components/ui/table.tsx +116 -0
- solokit/templates/dashboard_refine/base/components.json +16 -0
- solokit/templates/dashboard_refine/base/lib/refine.tsx +65 -0
- solokit/templates/dashboard_refine/base/lib/utils.ts +13 -0
- solokit/templates/dashboard_refine/base/next.config.ts +10 -0
- solokit/templates/dashboard_refine/base/package.json.template +40 -0
- solokit/templates/dashboard_refine/base/postcss.config.mjs +8 -0
- solokit/templates/dashboard_refine/base/providers/refine-provider.tsx +26 -0
- solokit/templates/dashboard_refine/base/tailwind.config.ts +57 -0
- solokit/templates/dashboard_refine/base/tsconfig.json +27 -0
- solokit/templates/dashboard_refine/docker/Dockerfile +57 -0
- solokit/templates/dashboard_refine/docker/docker-compose.prod.yml +31 -0
- solokit/templates/dashboard_refine/docker/docker-compose.yml +21 -0
- solokit/templates/dashboard_refine/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/dashboard_refine/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/dashboard_refine/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/dashboard_refine/tier-1-essential/package.json.tier1.template +57 -0
- solokit/templates/dashboard_refine/tier-1-essential/tests/setup.ts +26 -0
- solokit/templates/dashboard_refine/tier-1-essential/tests/unit/example.test.tsx +73 -0
- solokit/templates/dashboard_refine/tier-2-standard/package.json.tier2.template +62 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/eslint.config.mjs +22 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/package.json.tier3.template +79 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/stryker.conf.json +38 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/e2e/dashboard.spec.ts +88 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/e2e/user-management.spec.ts +102 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/integration/dashboard.test.tsx +90 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/type-coverage.json +16 -0
- solokit/templates/dashboard_refine/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/dashboard_refine/tier-4-production/k6/dashboard-load-test.js +70 -0
- solokit/templates/dashboard_refine/tier-4-production/next.config.ts +46 -0
- solokit/templates/dashboard_refine/tier-4-production/package.json.tier4.template +89 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/deployment_spec.md +500 -0
- solokit/templates/feature_spec.md +248 -0
- solokit/templates/fullstack_nextjs/base/.gitignore +36 -0
- solokit/templates/fullstack_nextjs/base/app/api/example/route.ts +65 -0
- solokit/templates/fullstack_nextjs/base/app/globals.css +27 -0
- solokit/templates/fullstack_nextjs/base/app/layout.tsx +20 -0
- solokit/templates/fullstack_nextjs/base/app/page.tsx +32 -0
- solokit/templates/fullstack_nextjs/base/components/example-component.tsx +20 -0
- solokit/templates/fullstack_nextjs/base/lib/prisma.ts +17 -0
- solokit/templates/fullstack_nextjs/base/lib/utils.ts +13 -0
- solokit/templates/fullstack_nextjs/base/lib/validations.ts +20 -0
- solokit/templates/fullstack_nextjs/base/next.config.ts +7 -0
- solokit/templates/fullstack_nextjs/base/package.json.template +32 -0
- solokit/templates/fullstack_nextjs/base/postcss.config.mjs +8 -0
- solokit/templates/fullstack_nextjs/base/prisma/schema.prisma +21 -0
- solokit/templates/fullstack_nextjs/base/tailwind.config.ts +19 -0
- solokit/templates/fullstack_nextjs/base/tsconfig.json +27 -0
- solokit/templates/fullstack_nextjs/docker/Dockerfile +60 -0
- solokit/templates/fullstack_nextjs/docker/docker-compose.prod.yml +57 -0
- solokit/templates/fullstack_nextjs/docker/docker-compose.yml +47 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/package.json.tier1.template +48 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/api/example.test.ts +88 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/setup.ts +22 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/unit/example.test.tsx +22 -0
- solokit/templates/fullstack_nextjs/tier-2-standard/package.json.tier2.template +52 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/eslint.config.mjs +39 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/package.json.tier3.template +68 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/stryker.conf.json +33 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/tests/e2e/flow.spec.ts +59 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/tests/integration/api.test.ts +165 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/type-coverage.json +12 -0
- solokit/templates/fullstack_nextjs/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/fullstack_nextjs/tier-4-production/k6/load-test.js +45 -0
- solokit/templates/fullstack_nextjs/tier-4-production/next.config.ts +46 -0
- solokit/templates/fullstack_nextjs/tier-4-production/package.json.tier4.template +77 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/git-hooks/prepare-commit-msg +24 -0
- solokit/templates/integration_test_spec.md +363 -0
- solokit/templates/learnings.json +15 -0
- solokit/templates/ml_ai_fastapi/base/.gitignore +104 -0
- solokit/templates/ml_ai_fastapi/base/alembic/env.py +96 -0
- solokit/templates/ml_ai_fastapi/base/alembic.ini +114 -0
- solokit/templates/ml_ai_fastapi/base/pyproject.toml.template +91 -0
- solokit/templates/ml_ai_fastapi/base/requirements.txt.template +28 -0
- solokit/templates/ml_ai_fastapi/base/src/__init__.py +5 -0
- solokit/templates/ml_ai_fastapi/base/src/api/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/api/dependencies.py +20 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/example.py +134 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/health.py +66 -0
- solokit/templates/ml_ai_fastapi/base/src/core/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/core/config.py +64 -0
- solokit/templates/ml_ai_fastapi/base/src/core/database.py +50 -0
- solokit/templates/ml_ai_fastapi/base/src/main.py +64 -0
- solokit/templates/ml_ai_fastapi/base/src/models/__init__.py +7 -0
- solokit/templates/ml_ai_fastapi/base/src/models/example.py +61 -0
- solokit/templates/ml_ai_fastapi/base/src/services/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/services/example.py +115 -0
- solokit/templates/ml_ai_fastapi/docker/Dockerfile +59 -0
- solokit/templates/ml_ai_fastapi/docker/docker-compose.prod.yml +112 -0
- solokit/templates/ml_ai_fastapi/docker/docker-compose.yml +77 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pyproject.toml.tier1.template +112 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pyrightconfig.json +41 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pytest.ini +69 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/requirements-dev.txt +17 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/ruff.toml +81 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/conftest.py +72 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/test_main.py +49 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/unit/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/unit/test_example.py +113 -0
- solokit/templates/ml_ai_fastapi/tier-2-standard/pyproject.toml.tier2.template +130 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/locustfile.py +99 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/mutmut_config.py +53 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/pyproject.toml.tier3.template +150 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/conftest.py +74 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/test_api.py +131 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/pyproject.toml.tier4.template +162 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/requirements-prod.txt +25 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/api/routes/metrics.py +19 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/logging.py +74 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/monitoring.py +68 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/sentry.py +66 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/logging.py +79 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/tracing.py +60 -0
- solokit/templates/refactor_spec.md +287 -0
- solokit/templates/saas_t3/base/.gitignore +36 -0
- solokit/templates/saas_t3/base/app/api/trpc/[trpc]/route.ts +33 -0
- solokit/templates/saas_t3/base/app/globals.css +27 -0
- solokit/templates/saas_t3/base/app/layout.tsx +23 -0
- solokit/templates/saas_t3/base/app/page.tsx +31 -0
- solokit/templates/saas_t3/base/lib/api.tsx +77 -0
- solokit/templates/saas_t3/base/lib/utils.ts +13 -0
- solokit/templates/saas_t3/base/next.config.ts +7 -0
- solokit/templates/saas_t3/base/package.json.template +38 -0
- solokit/templates/saas_t3/base/postcss.config.mjs +8 -0
- solokit/templates/saas_t3/base/prisma/schema.prisma +20 -0
- solokit/templates/saas_t3/base/server/api/root.ts +19 -0
- solokit/templates/saas_t3/base/server/api/routers/example.ts +28 -0
- solokit/templates/saas_t3/base/server/api/trpc.ts +52 -0
- solokit/templates/saas_t3/base/server/db.ts +17 -0
- solokit/templates/saas_t3/base/tailwind.config.ts +19 -0
- solokit/templates/saas_t3/base/tsconfig.json +27 -0
- solokit/templates/saas_t3/docker/Dockerfile +60 -0
- solokit/templates/saas_t3/docker/docker-compose.prod.yml +59 -0
- solokit/templates/saas_t3/docker/docker-compose.yml +49 -0
- solokit/templates/saas_t3/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/saas_t3/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/saas_t3/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/saas_t3/tier-1-essential/package.json.tier1.template +54 -0
- solokit/templates/saas_t3/tier-1-essential/tests/setup.ts +22 -0
- solokit/templates/saas_t3/tier-1-essential/tests/unit/example.test.tsx +24 -0
- solokit/templates/saas_t3/tier-2-standard/package.json.tier2.template +58 -0
- solokit/templates/saas_t3/tier-3-comprehensive/eslint.config.mjs +39 -0
- solokit/templates/saas_t3/tier-3-comprehensive/package.json.tier3.template +74 -0
- solokit/templates/saas_t3/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/saas_t3/tier-3-comprehensive/stryker.conf.json +34 -0
- solokit/templates/saas_t3/tier-3-comprehensive/tests/e2e/home.spec.ts +41 -0
- solokit/templates/saas_t3/tier-3-comprehensive/tests/integration/api.test.ts +44 -0
- solokit/templates/saas_t3/tier-3-comprehensive/type-coverage.json +12 -0
- solokit/templates/saas_t3/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/saas_t3/tier-4-production/k6/load-test.js +51 -0
- solokit/templates/saas_t3/tier-4-production/next.config.ts +46 -0
- solokit/templates/saas_t3/tier-4-production/package.json.tier4.template +83 -0
- solokit/templates/saas_t3/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/saas_t3/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/saas_t3/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/saas_t3/tier-4-production/vercel.json +37 -0
- solokit/templates/security_spec.md +287 -0
- solokit/templates/stack-versions.yaml +617 -0
- solokit/templates/status_update.json +6 -0
- solokit/templates/template-registry.json +257 -0
- solokit/templates/work_items.json +11 -0
- solokit/testing/__init__.py +1 -0
- solokit/testing/integration_runner.py +550 -0
- solokit/testing/performance.py +637 -0
- solokit/visualization/__init__.py +1 -0
- solokit/visualization/dependency_graph.py +788 -0
- solokit/work_items/__init__.py +1 -0
- solokit/work_items/creator.py +217 -0
- solokit/work_items/delete.py +264 -0
- solokit/work_items/get_dependencies.py +185 -0
- solokit/work_items/get_dependents.py +113 -0
- solokit/work_items/get_metadata.py +121 -0
- solokit/work_items/get_next_recommendations.py +133 -0
- solokit/work_items/manager.py +235 -0
- solokit/work_items/milestones.py +137 -0
- solokit/work_items/query.py +376 -0
- solokit/work_items/repository.py +267 -0
- solokit/work_items/scheduler.py +184 -0
- solokit/work_items/spec_parser.py +838 -0
- solokit/work_items/spec_validator.py +493 -0
- solokit/work_items/updater.py +157 -0
- solokit/work_items/validator.py +205 -0
- solokit-0.1.1.dist-info/METADATA +640 -0
- solokit-0.1.1.dist-info/RECORD +323 -0
- solokit-0.1.1.dist-info/WHEEL +5 -0
- solokit-0.1.1.dist-info/entry_points.txt +2 -0
- solokit-0.1.1.dist-info/licenses/LICENSE +21 -0
- solokit-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "{project_name}"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "{project_description}"
|
|
5
|
+
requires-python = ">=3.11"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"fastapi==0.115.6",
|
|
8
|
+
"uvicorn[standard]==0.34.0",
|
|
9
|
+
"pydantic==2.12.4",
|
|
10
|
+
"pydantic-core==2.41.5",
|
|
11
|
+
"pydantic-settings==2.11.0",
|
|
12
|
+
"sqlmodel==0.0.25",
|
|
13
|
+
"sqlalchemy==2.0.37",
|
|
14
|
+
"psycopg2-binary==2.9.10",
|
|
15
|
+
"alembic==1.14.0",
|
|
16
|
+
"python-dotenv==1.2.1",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
dev = [
|
|
21
|
+
"pytest==8.3.4",
|
|
22
|
+
"pytest-cov==6.0.0",
|
|
23
|
+
"pytest-asyncio==0.25.2",
|
|
24
|
+
"httpx==0.28.1",
|
|
25
|
+
"ruff==0.9.2",
|
|
26
|
+
"pyright==1.1.396",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["setuptools>=61.0"]
|
|
31
|
+
build-backend = "setuptools.build_meta"
|
|
32
|
+
|
|
33
|
+
[tool.setuptools]
|
|
34
|
+
packages = ["src"]
|
|
35
|
+
|
|
36
|
+
[tool.ruff]
|
|
37
|
+
line-length = 100
|
|
38
|
+
target-version = "py311"
|
|
39
|
+
|
|
40
|
+
select = [
|
|
41
|
+
"E", # pycodestyle errors
|
|
42
|
+
"W", # pycodestyle warnings
|
|
43
|
+
"F", # pyflakes
|
|
44
|
+
"I", # isort
|
|
45
|
+
"N", # pep8-naming
|
|
46
|
+
"UP", # pyupgrade
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
ignore = [
|
|
50
|
+
"E501", # line too long (handled by formatter)
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
exclude = [
|
|
54
|
+
".git",
|
|
55
|
+
".venv",
|
|
56
|
+
"__pycache__",
|
|
57
|
+
"alembic/versions",
|
|
58
|
+
".pytest_cache",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[tool.ruff.format]
|
|
62
|
+
quote-style = "double"
|
|
63
|
+
indent-style = "space"
|
|
64
|
+
|
|
65
|
+
[tool.ruff.isort]
|
|
66
|
+
known-first-party = ["src"]
|
|
67
|
+
|
|
68
|
+
[tool.pytest.ini_options]
|
|
69
|
+
asyncio_mode = "auto"
|
|
70
|
+
testpaths = ["tests"]
|
|
71
|
+
python_files = ["test_*.py", "*_test.py"]
|
|
72
|
+
python_classes = ["Test*"]
|
|
73
|
+
python_functions = ["test_*"]
|
|
74
|
+
addopts = [
|
|
75
|
+
"-v",
|
|
76
|
+
"--strict-markers",
|
|
77
|
+
"--tb=short",
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
[tool.coverage.run]
|
|
81
|
+
source = ["src"]
|
|
82
|
+
omit = [
|
|
83
|
+
"tests/*",
|
|
84
|
+
"alembic/*",
|
|
85
|
+
"*/__init__.py",
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
[tool.coverage.report]
|
|
89
|
+
precision = 2
|
|
90
|
+
show_missing = true
|
|
91
|
+
skip_covered = false
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Base Dependencies for {project_name}
|
|
2
|
+
# FastAPI Framework
|
|
3
|
+
fastapi==0.115.6
|
|
4
|
+
uvicorn[standard]==0.34.0
|
|
5
|
+
|
|
6
|
+
# Data Validation
|
|
7
|
+
pydantic==2.12.4
|
|
8
|
+
pydantic-core==2.41.5
|
|
9
|
+
pydantic-settings==2.11.0
|
|
10
|
+
|
|
11
|
+
# Database ORM
|
|
12
|
+
sqlmodel==0.0.25
|
|
13
|
+
sqlalchemy==2.0.37
|
|
14
|
+
psycopg2-binary==2.9.10
|
|
15
|
+
|
|
16
|
+
# Database Migrations
|
|
17
|
+
alembic==1.14.0
|
|
18
|
+
|
|
19
|
+
# Environment Variables
|
|
20
|
+
python-dotenv==1.2.1
|
|
21
|
+
|
|
22
|
+
# Development Dependencies (Tier 1)
|
|
23
|
+
pytest==8.3.4
|
|
24
|
+
pytest-cov==6.0.0
|
|
25
|
+
pytest-asyncio==0.25.2
|
|
26
|
+
httpx==0.28.1
|
|
27
|
+
ruff==0.9.2
|
|
28
|
+
pyright==1.1.396
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastAPI dependency injection - provides shared dependencies like database sessions
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from collections.abc import AsyncGenerator
|
|
6
|
+
|
|
7
|
+
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
8
|
+
|
|
9
|
+
from src.core.database import async_session_maker
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def get_db() -> AsyncGenerator[AsyncSession, None]:
|
|
13
|
+
"""
|
|
14
|
+
Dependency that provides a database session.
|
|
15
|
+
|
|
16
|
+
Yields:
|
|
17
|
+
AsyncSession: Database session
|
|
18
|
+
"""
|
|
19
|
+
async with async_session_maker() as session:
|
|
20
|
+
yield session
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example CRUD endpoints demonstrating SQLModel usage
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from fastapi import APIRouter, Depends, HTTPException, status
|
|
6
|
+
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
7
|
+
|
|
8
|
+
from src.api.dependencies import get_db
|
|
9
|
+
from src.models.example import Item, ItemCreate, ItemRead, ItemUpdate
|
|
10
|
+
from src.services.example import ItemService
|
|
11
|
+
|
|
12
|
+
router = APIRouter()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@router.post("/items", response_model=ItemRead, status_code=status.HTTP_201_CREATED)
|
|
16
|
+
async def create_item(
|
|
17
|
+
item: ItemCreate,
|
|
18
|
+
db: AsyncSession = Depends(get_db),
|
|
19
|
+
) -> Item:
|
|
20
|
+
"""
|
|
21
|
+
Create a new item.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
item: Item data
|
|
25
|
+
db: Database session
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Item: Created item
|
|
29
|
+
"""
|
|
30
|
+
service = ItemService(db)
|
|
31
|
+
return await service.create_item(item)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@router.get("/items", response_model=list[ItemRead])
|
|
35
|
+
async def list_items(
|
|
36
|
+
skip: int = 0,
|
|
37
|
+
limit: int = 100,
|
|
38
|
+
db: AsyncSession = Depends(get_db),
|
|
39
|
+
) -> list[Item]:
|
|
40
|
+
"""
|
|
41
|
+
List all items with pagination.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
skip: Number of items to skip
|
|
45
|
+
limit: Maximum number of items to return
|
|
46
|
+
db: Database session
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
List[Item]: List of items
|
|
50
|
+
"""
|
|
51
|
+
service = ItemService(db)
|
|
52
|
+
return await service.get_items(skip=skip, limit=limit)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@router.get("/items/{item_id}", response_model=ItemRead)
|
|
56
|
+
async def get_item(
|
|
57
|
+
item_id: int,
|
|
58
|
+
db: AsyncSession = Depends(get_db),
|
|
59
|
+
) -> Item:
|
|
60
|
+
"""
|
|
61
|
+
Get a specific item by ID.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
item_id: Item ID
|
|
65
|
+
db: Database session
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Item: Item data
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
HTTPException: If item not found
|
|
72
|
+
"""
|
|
73
|
+
service = ItemService(db)
|
|
74
|
+
item = await service.get_item(item_id)
|
|
75
|
+
if not item:
|
|
76
|
+
raise HTTPException(
|
|
77
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
78
|
+
detail=f"Item with id {item_id} not found",
|
|
79
|
+
)
|
|
80
|
+
return item
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@router.patch("/items/{item_id}", response_model=ItemRead)
|
|
84
|
+
async def update_item(
|
|
85
|
+
item_id: int,
|
|
86
|
+
item_update: ItemUpdate,
|
|
87
|
+
db: AsyncSession = Depends(get_db),
|
|
88
|
+
) -> Item:
|
|
89
|
+
"""
|
|
90
|
+
Update an item.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
item_id: Item ID
|
|
94
|
+
item_update: Fields to update
|
|
95
|
+
db: Database session
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Item: Updated item
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
HTTPException: If item not found
|
|
102
|
+
"""
|
|
103
|
+
service = ItemService(db)
|
|
104
|
+
item = await service.update_item(item_id, item_update)
|
|
105
|
+
if not item:
|
|
106
|
+
raise HTTPException(
|
|
107
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
108
|
+
detail=f"Item with id {item_id} not found",
|
|
109
|
+
)
|
|
110
|
+
return item
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@router.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
114
|
+
async def delete_item(
|
|
115
|
+
item_id: int,
|
|
116
|
+
db: AsyncSession = Depends(get_db),
|
|
117
|
+
) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Delete an item.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
item_id: Item ID
|
|
123
|
+
db: Database session
|
|
124
|
+
|
|
125
|
+
Raises:
|
|
126
|
+
HTTPException: If item not found
|
|
127
|
+
"""
|
|
128
|
+
service = ItemService(db)
|
|
129
|
+
success = await service.delete_item(item_id)
|
|
130
|
+
if not success:
|
|
131
|
+
raise HTTPException(
|
|
132
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
133
|
+
detail=f"Item with id {item_id} not found",
|
|
134
|
+
)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Health check endpoints for monitoring and load balancers
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from fastapi import APIRouter, Depends
|
|
6
|
+
from sqlmodel import text
|
|
7
|
+
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
8
|
+
|
|
9
|
+
from src.api.dependencies import get_db
|
|
10
|
+
from src.core.config import settings
|
|
11
|
+
|
|
12
|
+
router = APIRouter()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@router.get("/health")
|
|
16
|
+
async def health_check() -> dict[str, str]:
|
|
17
|
+
"""
|
|
18
|
+
Basic health check endpoint.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
dict: Health status
|
|
22
|
+
"""
|
|
23
|
+
return {
|
|
24
|
+
"status": "healthy",
|
|
25
|
+
"service": settings.APP_NAME,
|
|
26
|
+
"version": settings.APP_VERSION,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@router.get("/health/ready")
|
|
31
|
+
async def readiness_check(db: AsyncSession = Depends(get_db)) -> dict[str, str]:
|
|
32
|
+
"""
|
|
33
|
+
Readiness check - verifies database connectivity.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
db: Database session
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
dict: Readiness status
|
|
40
|
+
"""
|
|
41
|
+
try:
|
|
42
|
+
# Test database connection
|
|
43
|
+
await db.exec(text("SELECT 1"))
|
|
44
|
+
return {
|
|
45
|
+
"status": "ready",
|
|
46
|
+
"database": "connected",
|
|
47
|
+
}
|
|
48
|
+
except Exception as e:
|
|
49
|
+
return {
|
|
50
|
+
"status": "not ready",
|
|
51
|
+
"database": "disconnected",
|
|
52
|
+
"error": str(e),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@router.get("/health/live")
|
|
57
|
+
async def liveness_check() -> dict[str, str]:
|
|
58
|
+
"""
|
|
59
|
+
Liveness check - verifies the application is running.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
dict: Liveness status
|
|
63
|
+
"""
|
|
64
|
+
return {
|
|
65
|
+
"status": "alive",
|
|
66
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Application configuration using pydantic-settings
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pydantic import field_validator
|
|
6
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Settings(BaseSettings):
|
|
10
|
+
"""
|
|
11
|
+
Application settings loaded from environment variables.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
APP_NAME: Application name
|
|
15
|
+
APP_VERSION: Application version
|
|
16
|
+
DEBUG: Debug mode
|
|
17
|
+
ENVIRONMENT: Environment (development, staging, production)
|
|
18
|
+
DATABASE_URL: Database connection string
|
|
19
|
+
SECRET_KEY: Secret key for cryptographic operations
|
|
20
|
+
CORS_ORIGINS: List of allowed CORS origins
|
|
21
|
+
LOG_LEVEL: Logging level
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
model_config = SettingsConfigDict(
|
|
25
|
+
env_file=".env",
|
|
26
|
+
env_file_encoding="utf-8",
|
|
27
|
+
case_sensitive=True,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Application
|
|
31
|
+
APP_NAME: str = "{project_name}"
|
|
32
|
+
APP_VERSION: str = "0.1.0"
|
|
33
|
+
DEBUG: bool = True
|
|
34
|
+
ENVIRONMENT: str = "development"
|
|
35
|
+
|
|
36
|
+
# Database
|
|
37
|
+
DATABASE_URL: str = "postgresql://user:password@localhost:5432/{project_name}_db"
|
|
38
|
+
|
|
39
|
+
# Security
|
|
40
|
+
SECRET_KEY: str = "change-this-to-a-random-secret-key"
|
|
41
|
+
ALLOWED_HOSTS: list[str] = ["localhost", "127.0.0.1"]
|
|
42
|
+
|
|
43
|
+
# CORS
|
|
44
|
+
CORS_ORIGINS: list[str] = ["http://localhost:3000", "http://localhost:8000"]
|
|
45
|
+
|
|
46
|
+
@field_validator("CORS_ORIGINS", mode="before")
|
|
47
|
+
@classmethod
|
|
48
|
+
def parse_cors_origins(cls, v: str | list[str]) -> list[str]:
|
|
49
|
+
"""Parse CORS origins from string or list."""
|
|
50
|
+
if isinstance(v, str):
|
|
51
|
+
return [origin.strip() for origin in v.split(",")]
|
|
52
|
+
return v
|
|
53
|
+
|
|
54
|
+
# Logging
|
|
55
|
+
LOG_LEVEL: str = "DEBUG"
|
|
56
|
+
LOG_FORMAT: str = "json"
|
|
57
|
+
|
|
58
|
+
# Server
|
|
59
|
+
HOST: str = "0.0.0.0" # nosec B104 - Binding to all interfaces is intentional for development
|
|
60
|
+
PORT: int = 8000
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Global settings instance
|
|
64
|
+
settings = Settings()
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Database configuration and session management
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from sqlalchemy.ext.asyncio import create_async_engine
|
|
6
|
+
from sqlalchemy.orm import sessionmaker
|
|
7
|
+
from sqlmodel import SQLModel
|
|
8
|
+
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
9
|
+
|
|
10
|
+
from src.core.config import settings
|
|
11
|
+
|
|
12
|
+
# Convert postgresql:// to postgresql+asyncpg:// for async support
|
|
13
|
+
DATABASE_URL = settings.DATABASE_URL.replace("postgresql://", "postgresql+asyncpg://")
|
|
14
|
+
|
|
15
|
+
# Create async engine
|
|
16
|
+
engine = create_async_engine(
|
|
17
|
+
DATABASE_URL,
|
|
18
|
+
echo=settings.DEBUG,
|
|
19
|
+
future=True,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# Create async session maker
|
|
23
|
+
async_session_maker = sessionmaker(
|
|
24
|
+
engine,
|
|
25
|
+
class_=AsyncSession,
|
|
26
|
+
expire_on_commit=False,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def create_db_and_tables() -> None:
|
|
31
|
+
"""
|
|
32
|
+
Create all database tables.
|
|
33
|
+
In production, use Alembic migrations instead.
|
|
34
|
+
"""
|
|
35
|
+
async with engine.begin() as conn:
|
|
36
|
+
# Import all models here to ensure they are registered
|
|
37
|
+
from src.models import Item # noqa: F401
|
|
38
|
+
|
|
39
|
+
await conn.run_sync(SQLModel.metadata.create_all)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def get_session() -> AsyncSession:
|
|
43
|
+
"""
|
|
44
|
+
Get a database session.
|
|
45
|
+
|
|
46
|
+
Yields:
|
|
47
|
+
AsyncSession: Database session
|
|
48
|
+
"""
|
|
49
|
+
async with async_session_maker() as session:
|
|
50
|
+
yield session
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastAPI application entry point for {project_name}
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from collections.abc import AsyncGenerator
|
|
6
|
+
from contextlib import asynccontextmanager
|
|
7
|
+
|
|
8
|
+
from fastapi import FastAPI
|
|
9
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
10
|
+
|
|
11
|
+
from src.api.routes import example, health
|
|
12
|
+
from src.core.config import settings
|
|
13
|
+
from src.core.database import create_db_and_tables
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@asynccontextmanager
|
|
17
|
+
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
|
18
|
+
"""
|
|
19
|
+
Application lifespan manager - handles startup and shutdown events.
|
|
20
|
+
"""
|
|
21
|
+
# Startup
|
|
22
|
+
print(f"Starting {settings.APP_NAME} v{settings.APP_VERSION}")
|
|
23
|
+
print(f"Environment: {settings.ENVIRONMENT}")
|
|
24
|
+
|
|
25
|
+
# Create database tables
|
|
26
|
+
await create_db_and_tables()
|
|
27
|
+
|
|
28
|
+
yield
|
|
29
|
+
|
|
30
|
+
# Shutdown
|
|
31
|
+
print(f"Shutting down {settings.APP_NAME}")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Create FastAPI application
|
|
35
|
+
app = FastAPI(
|
|
36
|
+
title=settings.APP_NAME,
|
|
37
|
+
version=settings.APP_VERSION,
|
|
38
|
+
description="{project_description}",
|
|
39
|
+
lifespan=lifespan,
|
|
40
|
+
debug=settings.DEBUG,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Configure CORS
|
|
44
|
+
app.add_middleware(
|
|
45
|
+
CORSMiddleware,
|
|
46
|
+
allow_origins=settings.CORS_ORIGINS,
|
|
47
|
+
allow_credentials=True,
|
|
48
|
+
allow_methods=["*"],
|
|
49
|
+
allow_headers=["*"],
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Include routers
|
|
53
|
+
app.include_router(health.router, tags=["health"])
|
|
54
|
+
app.include_router(example.router, prefix="/api/v1", tags=["examples"])
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@app.get("/")
|
|
58
|
+
async def root() -> dict[str, str]:
|
|
59
|
+
"""Root endpoint - returns API information."""
|
|
60
|
+
return {
|
|
61
|
+
"name": settings.APP_NAME,
|
|
62
|
+
"version": settings.APP_VERSION,
|
|
63
|
+
"status": "running",
|
|
64
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example SQLModel model demonstrating best practices
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from sqlmodel import Field, SQLModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ItemBase(SQLModel):
|
|
12
|
+
"""Base model with shared fields."""
|
|
13
|
+
|
|
14
|
+
name: str = Field(index=True, max_length=255)
|
|
15
|
+
description: Optional[str] = Field(default=None, max_length=1000)
|
|
16
|
+
price: float = Field(gt=0)
|
|
17
|
+
is_active: bool = Field(default=True)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Item(ItemBase, table=True):
|
|
21
|
+
"""
|
|
22
|
+
Item database model.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
id: Primary key
|
|
26
|
+
name: Item name
|
|
27
|
+
description: Item description
|
|
28
|
+
price: Item price (must be > 0)
|
|
29
|
+
is_active: Whether item is active
|
|
30
|
+
created_at: Timestamp when item was created
|
|
31
|
+
updated_at: Timestamp when item was last updated
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
__tablename__ = "items"
|
|
35
|
+
|
|
36
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
|
37
|
+
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
38
|
+
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ItemCreate(ItemBase):
|
|
42
|
+
"""Schema for creating a new item."""
|
|
43
|
+
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ItemRead(ItemBase):
|
|
48
|
+
"""Schema for reading an item (includes id and timestamps)."""
|
|
49
|
+
|
|
50
|
+
id: int
|
|
51
|
+
created_at: datetime
|
|
52
|
+
updated_at: datetime
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ItemUpdate(SQLModel):
|
|
56
|
+
"""Schema for updating an item (all fields optional)."""
|
|
57
|
+
|
|
58
|
+
name: Optional[str] = Field(default=None, max_length=255)
|
|
59
|
+
description: Optional[str] = Field(default=None, max_length=1000)
|
|
60
|
+
price: Optional[float] = Field(default=None, gt=0)
|
|
61
|
+
is_active: Optional[bool] = None
|