pycharter 0.0.24__py3-none-any.whl → 0.0.26__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.
- pycharter/__init__.py +6 -0
- pycharter/api/README.md +340 -0
- {api → pycharter/api}/__init__.py +1 -1
- {api → pycharter/api}/dependencies/__init__.py +2 -2
- pycharter/api/dependencies/auth.py +158 -0
- {api → pycharter/api}/main.py +32 -4
- {api → pycharter/api}/models/__init__.py +4 -4
- pycharter/api/models/etl.py +66 -0
- {api → pycharter/api}/routes/v1/__init__.py +5 -1
- pycharter/api/routes/v1/auth.py +97 -0
- {api → pycharter/api}/routes/v1/contracts.py +14 -12
- {api → pycharter/api}/routes/v1/docs.py +2 -2
- pycharter/api/routes/v1/etl.py +131 -0
- {api → pycharter/api}/routes/v1/evolution.py +2 -2
- {api → pycharter/api}/routes/v1/metadata.py +5 -5
- {api → pycharter/api}/routes/v1/quality.py +3 -3
- {api → pycharter/api}/routes/v1/schemas.py +1 -1
- {api → pycharter/api}/routes/v1/settings.py +1 -1
- {api → pycharter/api}/routes/v1/tracking.py +1 -1
- {api → pycharter/api}/routes/v1/validation.py +2 -2
- {api → pycharter/api}/routes/v1/validation_jobs.py +3 -3
- pycharter/cli.py +9 -11
- pycharter/config.py +69 -0
- pycharter/contract_builder/builder.py +32 -37
- pycharter/data/seed/compliance_frameworks.yaml +22 -0
- pycharter/data/seed/contracts.yaml +130 -0
- pycharter/data/seed/data_feeds.yaml +22 -0
- pycharter/data/seed/domains.yaml +13 -0
- pycharter/data/seed/environments.yaml +19 -0
- pycharter/data/seed/owners.yaml +21 -0
- pycharter/data/seed/systems.yaml +13 -0
- pycharter/data/seed/tags.yaml +25 -0
- pycharter/data/templates/contract/README.md +161 -0
- pycharter/data/templates/contract/template_contract.yaml +37 -0
- pycharter/data/templates/etl/README.md +1 -1
- pycharter/data/templates/etl/extract_with_validation.yaml +86 -0
- pycharter/data/templates/etl/load_with_validation.yaml +111 -0
- pycharter/data/templates/etl/settings.yaml +55 -0
- pycharter/db/README.md +179 -0
- pycharter/db/cli.py +126 -4
- pycharter/db/migrations/versions/20260122000000_change_artifact_unique_constraints_to_title_version.py +2 -2
- pycharter/db/schemas/README.md +96 -0
- pycharter/etl_generator/ASYNC_AND_EXECUTION.md +91 -0
- pycharter/etl_generator/INTERFACES.md +142 -0
- pycharter/etl_generator/README.md +271 -0
- pycharter/etl_generator/TRANSFORMATION_GUIDE.md +452 -0
- pycharter/etl_generator/__init__.py +47 -11
- pycharter/etl_generator/config_models.py +673 -0
- pycharter/etl_generator/config_validator.py +133 -157
- pycharter/etl_generator/context.py +3 -0
- pycharter/etl_generator/database.py +5 -1
- pycharter/etl_generator/extractors/__init__.py +4 -2
- pycharter/etl_generator/extractors/cloud_storage.py +9 -9
- pycharter/etl_generator/extractors/database.py +2 -2
- pycharter/etl_generator/extractors/factory.py +15 -33
- pycharter/etl_generator/extractors/file.py +2 -2
- pycharter/etl_generator/extractors/http.py +2 -2
- pycharter/etl_generator/extractors/mongodb.py +393 -0
- pycharter/etl_generator/extractors/streaming.py +2 -2
- pycharter/etl_generator/loaders/__init__.py +15 -9
- pycharter/etl_generator/loaders/{cloud_storage_loader.py → cloud_storage.py} +95 -2
- pycharter/etl_generator/loaders/factory.py +16 -29
- pycharter/etl_generator/loaders/file.py +135 -1
- pycharter/etl_generator/loaders/mongodb.py +416 -0
- pycharter/etl_generator/pipeline.py +283 -164
- pycharter/etl_generator/result.py +16 -0
- pycharter/etl_generator/schemas/__init__.py +71 -42
- pycharter/etl_generator/transformers/config.py +3 -2
- pycharter/etl_generator/transformers/simple_operations.py +57 -4
- pycharter/etl_generator/validation.py +551 -0
- pycharter/metadata_store/README.md +229 -0
- pycharter/quality/README.md +235 -0
- pycharter/runtime_validator/__init__.py +7 -0
- pycharter/runtime_validator/utils.py +33 -0
- pycharter/runtime_validator/validator.py +13 -10
- pycharter/ui/.eslintrc.json +4 -0
- pycharter/ui/README.md +186 -0
- {ui → pycharter/ui}/__init__.py +3 -3
- pycharter/ui/components.json +17 -0
- pycharter/ui/package-lock.json +6617 -0
- pycharter/ui/package.json +37 -0
- {ui → pycharter/ui}/server.py +7 -8
- pycharter/ui/static/404/index.html +1 -0
- pycharter/ui/static/404.html +1 -0
- pycharter/ui/static/__next.__PAGE__.txt +10 -0
- pycharter/ui/static/__next._full.txt +30 -0
- pycharter/ui/static/__next._head.txt +7 -0
- pycharter/ui/static/__next._index.txt +9 -0
- pycharter/ui/static/__next._tree.txt +2 -0
- pycharter/ui/static/_next/static/YCnlK66gA7FV5vvcixspB/_clientMiddlewareManifest.json +1 -0
- pycharter/ui/static/_next/static/chunks/0fc1f70b787b8845.js +1 -0
- pycharter/ui/static/_next/static/chunks/17bb8075d7b75663.css +1 -0
- pycharter/ui/static/_next/static/chunks/381932864dcbfdb8.js +1 -0
- pycharter/ui/static/_next/static/chunks/4c951b8e4507e2b3.js +1 -0
- pycharter/ui/static/_next/static/chunks/68b87a6f65abd3ed.js +1 -0
- pycharter/ui/static/_next/static/chunks/78572617b8fae189.js +1 -0
- pycharter/ui/static/_next/static/chunks/8b7be2803e3fe184.js +1 -0
- pycharter/ui/static/_next/static/chunks/a8e529fd1e67f121.js +1 -0
- pycharter/ui/static/_next/static/chunks/c35d998f80be3ff5.js +1 -0
- pycharter/ui/static/_next/static/chunks/e453aa5d01c32c17.js +1 -0
- pycharter/ui/static/_next/static/chunks/f2d240eb057f898a.js +970 -0
- pycharter/ui/static/_next/static/chunks/f7722448f6040846.js +1 -0
- pycharter/ui/static/_not-found/__next._full.txt +17 -0
- pycharter/ui/static/_not-found/__next._head.txt +7 -0
- pycharter/ui/static/_not-found/__next._index.txt +9 -0
- pycharter/ui/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
- pycharter/ui/static/_not-found/__next._not-found.txt +4 -0
- pycharter/ui/static/_not-found/__next._tree.txt +2 -0
- pycharter/ui/static/_not-found/index.html +1 -0
- pycharter/ui/static/_not-found/index.txt +17 -0
- pycharter/ui/static/contracts/__next._full.txt +21 -0
- pycharter/ui/static/contracts/__next._head.txt +7 -0
- pycharter/ui/static/contracts/__next._index.txt +9 -0
- pycharter/ui/static/contracts/__next._tree.txt +2 -0
- pycharter/ui/static/contracts/__next.contracts.__PAGE__.txt +9 -0
- pycharter/ui/static/contracts/__next.contracts.txt +4 -0
- pycharter/ui/static/contracts/index.html +1 -0
- pycharter/ui/static/contracts/index.txt +21 -0
- pycharter/ui/static/documentation/__next._full.txt +21 -0
- pycharter/ui/static/documentation/__next._head.txt +7 -0
- pycharter/ui/static/documentation/__next._index.txt +9 -0
- pycharter/ui/static/documentation/__next._tree.txt +2 -0
- pycharter/ui/static/documentation/__next.documentation.__PAGE__.txt +9 -0
- pycharter/ui/static/documentation/__next.documentation.txt +4 -0
- pycharter/ui/static/documentation/index.html +93 -0
- pycharter/ui/static/documentation/index.txt +21 -0
- pycharter/ui/static/etl/__next._full.txt +21 -0
- pycharter/ui/static/etl/__next._head.txt +7 -0
- pycharter/ui/static/etl/__next._index.txt +9 -0
- pycharter/ui/static/etl/__next._tree.txt +2 -0
- pycharter/ui/static/etl/__next.etl.__PAGE__.txt +9 -0
- pycharter/ui/static/etl/__next.etl.txt +4 -0
- pycharter/ui/static/etl/index.html +2 -0
- pycharter/ui/static/etl/index.txt +21 -0
- pycharter/ui/static/index.html +1 -0
- pycharter/ui/static/index.txt +30 -0
- pycharter/ui/static/metadata/__next._full.txt +21 -0
- pycharter/ui/static/metadata/__next._head.txt +7 -0
- pycharter/ui/static/metadata/__next._index.txt +9 -0
- pycharter/ui/static/metadata/__next._tree.txt +2 -0
- pycharter/ui/static/metadata/__next.metadata.__PAGE__.txt +9 -0
- pycharter/ui/static/metadata/__next.metadata.txt +4 -0
- pycharter/ui/static/metadata/index.html +1 -0
- pycharter/ui/static/metadata/index.txt +21 -0
- pycharter/ui/static/quality/__next._full.txt +21 -0
- pycharter/ui/static/quality/__next._head.txt +7 -0
- pycharter/ui/static/quality/__next._index.txt +9 -0
- pycharter/ui/static/quality/__next._tree.txt +2 -0
- pycharter/ui/static/quality/__next.quality.__PAGE__.txt +9 -0
- pycharter/ui/static/quality/__next.quality.txt +4 -0
- pycharter/ui/static/quality/index.html +2 -0
- pycharter/ui/static/quality/index.txt +21 -0
- pycharter/ui/static/rules/__next._full.txt +21 -0
- pycharter/ui/static/rules/__next._head.txt +7 -0
- pycharter/ui/static/rules/__next._index.txt +9 -0
- pycharter/ui/static/rules/__next._tree.txt +2 -0
- pycharter/ui/static/rules/__next.rules.__PAGE__.txt +9 -0
- pycharter/ui/static/rules/__next.rules.txt +4 -0
- pycharter/ui/static/rules/index.html +1 -0
- pycharter/ui/static/rules/index.txt +21 -0
- pycharter/ui/static/schemas/__next._full.txt +21 -0
- pycharter/ui/static/schemas/__next._head.txt +7 -0
- pycharter/ui/static/schemas/__next._index.txt +9 -0
- pycharter/ui/static/schemas/__next._tree.txt +2 -0
- pycharter/ui/static/schemas/__next.schemas.__PAGE__.txt +9 -0
- pycharter/ui/static/schemas/__next.schemas.txt +4 -0
- pycharter/ui/static/schemas/index.html +1 -0
- pycharter/ui/static/schemas/index.txt +21 -0
- pycharter/ui/static/settings/__next._full.txt +21 -0
- pycharter/ui/static/settings/__next._head.txt +7 -0
- pycharter/ui/static/settings/__next._index.txt +9 -0
- pycharter/ui/static/settings/__next._tree.txt +2 -0
- pycharter/ui/static/settings/__next.settings.__PAGE__.txt +9 -0
- pycharter/ui/static/settings/__next.settings.txt +4 -0
- pycharter/ui/static/settings/index.html +1 -0
- pycharter/ui/static/settings/index.txt +21 -0
- pycharter/ui/static/static/_next/static/2gKjNv6YvE6BcIdFthBLs/_clientMiddlewareManifest.json +1 -0
- pycharter/ui/static/static/static/_next/static/0rYA78L88aUyD2Uh38hhX/_clientMiddlewareManifest.json +1 -0
- pycharter/ui/static/static/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
- pycharter/ui/static/static/static/static/.gitkeep +0 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/222442f6da32302a.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/247eb132b7f7b574.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/297d55555b71baba.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/414e77373f8ff61c.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/652ad0aa26265c47.js +2 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/9c23f44fff36548a.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/b32a0963684b9933.js +4 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/db913959c675cea6.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/f2e7afeab1178138.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +3 -0
- pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +11 -0
- pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_clientMiddlewareManifest.json +1 -0
- pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +1 -0
- pycharter/ui/static/validation/__next._full.txt +21 -0
- pycharter/ui/static/validation/__next._head.txt +7 -0
- pycharter/ui/static/validation/__next._index.txt +9 -0
- pycharter/ui/static/validation/__next._tree.txt +2 -0
- pycharter/ui/static/validation/__next.validation.__PAGE__.txt +9 -0
- pycharter/ui/static/validation/__next.validation.txt +4 -0
- pycharter/ui/static/validation/index.html +1 -0
- pycharter/ui/static/validation/index.txt +21 -0
- pycharter/ui/tsconfig.json +42 -0
- pycharter/worker/README.md +187 -0
- pycharter/worker/backends/__init__.py +8 -0
- pycharter/worker/backends/base.py +46 -0
- pycharter/worker/backends/spark.py +233 -0
- {worker → pycharter/worker}/cli.py +1 -1
- {worker → pycharter/worker}/processor.py +2 -2
- pycharter/worker/queue/__init__.py +8 -0
- pycharter/worker/queue/redis_queue.py +147 -0
- {pycharter-0.0.24.dist-info → pycharter-0.0.26.dist-info}/METADATA +57 -26
- pycharter-0.0.26.dist-info/RECORD +702 -0
- pycharter-0.0.26.dist-info/top_level.txt +1 -0
- pycharter/etl_generator/config_loader.py +0 -394
- pycharter/etl_generator/loaders/cloud.py +0 -87
- pycharter/etl_generator/loaders/file_loader.py +0 -130
- pycharter-0.0.24.dist-info/RECORD +0 -543
- pycharter-0.0.24.dist-info/top_level.txt +0 -4
- {api → pycharter/api}/dependencies/database.py +0 -0
- {api → pycharter/api}/dependencies/store.py +0 -0
- {api → pycharter/api}/models/contracts.py +0 -0
- {api → pycharter/api}/models/docs.py +0 -0
- {api → pycharter/api}/models/evolution.py +0 -0
- {api → pycharter/api}/models/metadata.py +0 -0
- {api → pycharter/api}/models/metadata_entities.py +0 -0
- {api → pycharter/api}/models/quality.py +0 -0
- {api → pycharter/api}/models/schemas.py +0 -0
- {api → pycharter/api}/models/tracking.py +0 -0
- {api → pycharter/api}/models/validation.py +0 -0
- {api → pycharter/api}/routes/__init__.py +0 -0
- {api → pycharter/api}/routes/v1/templates.py +0 -0
- {api → pycharter/api}/utils.py +0 -0
- {ui → pycharter/ui}/build.py +0 -0
- {ui → pycharter/ui}/dev.py +0 -0
- {ui → pycharter/ui}/static/.gitkeep +0 -0
- {ui/static/_next/static/2gKjNv6YvE6BcIdFthBLs → pycharter/ui/static/_next/static/YCnlK66gA7FV5vvcixspB}/_buildManifest.js +0 -0
- {ui/static/_next/static/2gKjNv6YvE6BcIdFthBLs → pycharter/ui/static/_next/static/YCnlK66gA7FV5vvcixspB}/_ssgManifest.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/222442f6da32302a.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/247eb132b7f7b574.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/297d55555b71baba.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/414e77373f8ff61c.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/652ad0aa26265c47.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/9c23f44fff36548a.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/a6dad97d9634a72d.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/b32a0963684b9933.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/db913959c675cea6.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/f2e7afeab1178138.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/f7d1a90dd75d2572.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/ff1a16fafef87110.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +0 -0
- {ui → pycharter/ui}/static/static/.gitkeep +0 -0
- {ui → pycharter/ui/static}/static/404/index.html +0 -0
- {ui → pycharter/ui/static}/static/404.html +0 -0
- {ui → pycharter/ui/static}/static/__next.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/__next._tree.txt +0 -0
- {ui/static/static/_next/static/0rYA78L88aUyD2Uh38hhX → pycharter/ui/static/static/_next/static/2gKjNv6YvE6BcIdFthBLs}/_buildManifest.js +0 -0
- {ui/static/static/_next/static/0rYA78L88aUyD2Uh38hhX → pycharter/ui/static/static/_next/static/2gKjNv6YvE6BcIdFthBLs}/_ssgManifest.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/13d4a0fbd74c1ee4.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/222442f6da32302a.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/247eb132b7f7b574.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/26dfc590f7714c03.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/297d55555b71baba.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/2ab439ce003cd691.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/2edb43b48432ac04.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/34d289e6db2ef551.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/414e77373f8ff61c.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/49ca65abd26ae49e.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/652ad0aa26265c47.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/9667e7a3d359eb39.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/99508d9d5869cc27.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/9c23f44fff36548a.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/a6dad97d9634a72d.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/b313c35a6ba76574.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/b32a0963684b9933.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/c69f6cba366bd988.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/d2363397e1b2bcab.css +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/db913959c675cea6.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/f061a4be97bfc3b3.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/f2e7afeab1178138.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/f7d1a90dd75d2572.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/ff1a16fafef87110.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +0 -0
- {ui → pycharter/ui/static}/static/_not-found/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/_not-found/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/_not-found/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/_not-found/__next._not-found.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/_not-found/__next._not-found.txt +0 -0
- {ui → pycharter/ui/static}/static/_not-found/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/_not-found/index.html +0 -0
- {ui → pycharter/ui/static}/static/_not-found/index.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/__next.contracts.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/__next.contracts.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/index.html +0 -0
- {ui → pycharter/ui/static}/static/contracts/index.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/__next.documentation.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/__next.documentation.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/index.html +0 -0
- {ui → pycharter/ui/static}/static/documentation/index.txt +0 -0
- {ui → pycharter/ui/static}/static/index.html +0 -0
- {ui → pycharter/ui/static}/static/index.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/__next.metadata.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/__next.metadata.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/index.html +0 -0
- {ui → pycharter/ui/static}/static/metadata/index.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/__next.quality.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/__next.quality.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/index.html +0 -0
- {ui → pycharter/ui/static}/static/quality/index.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/__next.rules.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/__next.rules.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/index.html +0 -0
- {ui → pycharter/ui/static}/static/rules/index.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/__next.schemas.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/__next.schemas.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/index.html +0 -0
- {ui → pycharter/ui/static}/static/schemas/index.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/__next.settings.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/__next.settings.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/index.html +0 -0
- {ui → pycharter/ui/static}/static/settings/index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/.gitkeep +0 -0
- {ui → pycharter/ui/static}/static/static/404/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/404.html +0 -0
- {ui → pycharter/ui/static}/static/static/__next.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/__next._tree.txt +0 -0
- {ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl → pycharter/ui/static/static/static/_next/static/0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
- {ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl → pycharter/ui/static/static/static/_next/static/0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/13d4a0fbd74c1ee4.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/222442f6da32302a.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/247eb132b7f7b574.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/297d55555b71baba.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/2ab439ce003cd691.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/2edb43b48432ac04.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/414e77373f8ff61c.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/49ca65abd26ae49e.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/5e04d10c4a7b58a3.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/652ad0aa26265c47.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/75d88a058d8ffaa6.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/8c89634cf6bad76f.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/9667e7a3d359eb39.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/9c23f44fff36548a.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/b32a0963684b9933.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/c4fa4f4114b7c352.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/c69f6cba366bd988.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/d2363397e1b2bcab.css +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/db913959c675cea6.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/f061a4be97bfc3b3.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/f2e7afeab1178138.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/ff1a16fafef87110.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/__next._not-found.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/__next._not-found.txt +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/__next.contracts.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/__next.contracts.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/__next.documentation.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/__next.documentation.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/__next.metadata.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/__next.metadata.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/__next.quality.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/__next.quality.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/quality/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/__next.rules.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/__next.rules.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/rules/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/__next.schemas.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/__next.schemas.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/__next.settings.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/__next.settings.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/settings/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/404/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/404.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/__next.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/2ab439ce003cd691.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/49ca65abd26ae49e.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/4e310fe5005770a3.css +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/5e04d10c4a7b58a3.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/5fc14c00a2779dc5.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/75d88a058d8ffaa6.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/8c89634cf6bad76f.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/9667e7a3d359eb39.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/b584574fdc8ab13e.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/c69f6cba366bd988.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/d5989c94d3614b3a.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/f061a4be97bfc3b3.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/__next._not-found.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/__next._not-found.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/__next.contracts.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/__next.contracts.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/__next.documentation.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/__next.documentation.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/__next.metadata.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/__next.metadata.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/__next.quality.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/__next.quality.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/__next.rules.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/__next.rules.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/__next.schemas.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/__next.schemas.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/__next.settings.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/__next.settings.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/__next.validation.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/__next.validation.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/__next.validation.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/__next.validation.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/validation/index.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/__next.validation.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/__next.validation.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/index.html +0 -0
- {ui → pycharter/ui/static}/static/validation/index.txt +0 -0
- {worker → pycharter/worker}/__init__.py +0 -0
- {worker → pycharter/worker}/models.py +0 -0
- {pycharter-0.0.24.dist-info → pycharter-0.0.26.dist-info}/WHEEL +0 -0
- {pycharter-0.0.24.dist-info → pycharter-0.0.26.dist-info}/entry_points.txt +0 -0
- {pycharter-0.0.24.dist-info → pycharter-0.0.26.dist-info}/licenses/LICENSE +0 -0
pycharter/__init__.py
CHANGED
|
@@ -166,6 +166,9 @@ from pycharter.runtime_validator import (
|
|
|
166
166
|
validate_input,
|
|
167
167
|
validate_output,
|
|
168
168
|
validate_with_contract_decorator,
|
|
169
|
+
# Schema utilities
|
|
170
|
+
merge_rules_into_schema,
|
|
171
|
+
get_merged_schema_from_contract,
|
|
169
172
|
)
|
|
170
173
|
|
|
171
174
|
# ============================================================================
|
|
@@ -345,6 +348,9 @@ __all__ = [
|
|
|
345
348
|
"validate_input",
|
|
346
349
|
"validate_output",
|
|
347
350
|
"validate_with_contract_decorator",
|
|
351
|
+
# Schema utilities
|
|
352
|
+
"merge_rules_into_schema",
|
|
353
|
+
"get_merged_schema_from_contract",
|
|
348
354
|
# Quality
|
|
349
355
|
"QualityCheck",
|
|
350
356
|
"QualityCheckOptions",
|
pycharter/api/README.md
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# PyCharter API
|
|
2
|
+
|
|
3
|
+
REST API wrapper for PyCharter services using FastAPI.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The PyCharter API is located at the root level of the repository (`api/`) and provides HTTP endpoints for all core PyCharter services:
|
|
8
|
+
- **Contract Parsing**: Parse data contract files into components
|
|
9
|
+
- **Contract Building**: Reconstruct contracts from metadata store
|
|
10
|
+
- **Metadata Storage**: Store and retrieve schemas, metadata, and rules
|
|
11
|
+
- **Schema Generation**: Generate Pydantic models from JSON Schemas
|
|
12
|
+
- **Schema Conversion**: Convert Pydantic models to JSON Schemas
|
|
13
|
+
- **Runtime Validation**: Validate data against schemas
|
|
14
|
+
- **Quality Assurance**: Run quality checks, query metrics and violations
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Install PyCharter with API dependencies:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install pycharter[api]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Running the API Server
|
|
25
|
+
|
|
26
|
+
### Using the CLI command:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pycharter api
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Using uvicorn directly:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uvicorn api.main:app --reload
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Using Python:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from api.main import main
|
|
42
|
+
main()
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Configuration
|
|
46
|
+
|
|
47
|
+
The API requires a database connection for most endpoints. Configure it using environment variables:
|
|
48
|
+
|
|
49
|
+
### Database URL Configuration
|
|
50
|
+
|
|
51
|
+
The API uses `PYCHARTER_DATABASE_URL` environment variable to connect to the database. You can set it in several ways:
|
|
52
|
+
|
|
53
|
+
#### Method 1: Export in Shell (Recommended)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Set the environment variable
|
|
57
|
+
export PYCHARTER_DATABASE_URL="postgresql://user:password@localhost:5432/pycharter"
|
|
58
|
+
|
|
59
|
+
# Then run the API
|
|
60
|
+
pycharter api
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### Method 2: Inline with Command
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Set and run in one line
|
|
67
|
+
PYCHARTER_DATABASE_URL="postgresql://user:password@localhost:5432/pycharter" pycharter api
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### Method 3: Add to Shell Profile (Persistent)
|
|
71
|
+
|
|
72
|
+
Add to your `~/.bashrc`, `~/.zshrc`, or `~/.profile`:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
export PYCHARTER_DATABASE_URL="postgresql://user:password@localhost:5432/pycharter"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Then reload your shell:
|
|
79
|
+
```bash
|
|
80
|
+
source ~/.bashrc # or source ~/.zshrc
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### Method 4: Using a .env File
|
|
84
|
+
|
|
85
|
+
Create a `.env` file in your project root:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
PYCHARTER_DATABASE_URL=postgresql://user:password@localhost:5432/pycharter
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Then load it before running (requires `python-dotenv`):
|
|
92
|
+
```bash
|
|
93
|
+
export $(cat .env | xargs)
|
|
94
|
+
pycharter api
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Or use `dotenv-cli`:
|
|
98
|
+
```bash
|
|
99
|
+
pip install dotenv-cli
|
|
100
|
+
dotenv run pycharter api
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Alternative Environment Variable
|
|
104
|
+
|
|
105
|
+
PyCharter also supports Airflow-style configuration:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
export PYCHARTER__DATABASE__SQL_ALCHEMY_CONN="postgresql://user:password@localhost:5432/pycharter"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Metadata Store Configuration
|
|
112
|
+
|
|
113
|
+
The API can be configured to use different metadata store backends:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Metadata store type (in_memory, postgres, mongodb, redis)
|
|
117
|
+
export PYCHARTER_API_STORE_TYPE=postgres
|
|
118
|
+
|
|
119
|
+
# Connection string for database-backed stores
|
|
120
|
+
export PYCHARTER_API_CONNECTION_STRING=postgresql://user:pass@localhost:5432/pycharter
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Note**: If `PYCHARTER_DATABASE_URL` is set, it will be used for both the database connection and metadata store (for PostgreSQL).
|
|
124
|
+
|
|
125
|
+
## API Endpoints
|
|
126
|
+
|
|
127
|
+
### Contract Endpoints
|
|
128
|
+
|
|
129
|
+
#### `POST /api/v1/contracts/parse`
|
|
130
|
+
Parse a data contract dictionary into components.
|
|
131
|
+
|
|
132
|
+
**Request Body:**
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"contract": {
|
|
136
|
+
"schema": {...},
|
|
137
|
+
"metadata": {...},
|
|
138
|
+
"ownership": {...},
|
|
139
|
+
"governance_rules": {...}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Response:**
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"schema": {...},
|
|
148
|
+
"metadata": {...},
|
|
149
|
+
"ownership": {...},
|
|
150
|
+
"governance_rules": {...},
|
|
151
|
+
"coercion_rules": {...},
|
|
152
|
+
"validation_rules": {...},
|
|
153
|
+
"versions": {...}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### `POST /api/v1/contracts/build`
|
|
158
|
+
Build a complete contract from metadata store.
|
|
159
|
+
|
|
160
|
+
**Request Body:**
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"schema_id": "user_schema",
|
|
164
|
+
"version": "1.0.0",
|
|
165
|
+
"include_metadata": true,
|
|
166
|
+
"include_ownership": true,
|
|
167
|
+
"include_governance": true
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### `GET /api/v1/contracts`
|
|
172
|
+
List all data contracts stored in the database.
|
|
173
|
+
|
|
174
|
+
#### `GET /api/v1/contracts/{contract_id}`
|
|
175
|
+
Get a specific data contract by ID.
|
|
176
|
+
|
|
177
|
+
### Metadata Endpoints
|
|
178
|
+
|
|
179
|
+
#### `GET /api/v1/metadata/schemas`
|
|
180
|
+
List all schemas stored in the metadata store.
|
|
181
|
+
|
|
182
|
+
#### `GET /api/v1/metadata/schemas/{schema_id}`
|
|
183
|
+
Get a schema by ID (optional version parameter).
|
|
184
|
+
|
|
185
|
+
#### `GET /api/v1/metadata/schemas/{schema_id}/complete`
|
|
186
|
+
Get complete schema with coercion and validation rules merged (for display/docs). For validation, use the Validator class, which merges rules internally.
|
|
187
|
+
|
|
188
|
+
#### `POST /api/v1/metadata/schemas`
|
|
189
|
+
Store a schema in the metadata store.
|
|
190
|
+
|
|
191
|
+
#### `GET /api/v1/metadata/metadata/{schema_id}`
|
|
192
|
+
Get metadata for a schema (optional version parameter).
|
|
193
|
+
|
|
194
|
+
#### `POST /api/v1/metadata/metadata`
|
|
195
|
+
Store metadata for a schema.
|
|
196
|
+
|
|
197
|
+
#### `GET /api/v1/metadata/coercion-rules/{schema_id}`
|
|
198
|
+
Get coercion rules for a schema (optional version parameter).
|
|
199
|
+
|
|
200
|
+
#### `POST /api/v1/metadata/coercion-rules`
|
|
201
|
+
Store coercion rules for a schema.
|
|
202
|
+
|
|
203
|
+
#### `GET /api/v1/metadata/validation-rules/{schema_id}`
|
|
204
|
+
Get validation rules for a schema (optional version parameter).
|
|
205
|
+
|
|
206
|
+
#### `POST /api/v1/metadata/validation-rules`
|
|
207
|
+
Store validation rules for a schema.
|
|
208
|
+
|
|
209
|
+
### Quality Assurance Endpoints
|
|
210
|
+
|
|
211
|
+
#### `GET /api/v1/quality/metrics`
|
|
212
|
+
List quality metrics (optional filtering by schema_id, pagination).
|
|
213
|
+
|
|
214
|
+
#### `GET /api/v1/quality/metrics/{metric_id}`
|
|
215
|
+
Get a specific quality metric by ID.
|
|
216
|
+
|
|
217
|
+
#### `GET /api/v1/quality/reports/{schema_id}`
|
|
218
|
+
Get quality reports for a schema/data feed (optional data_source filter).
|
|
219
|
+
|
|
220
|
+
#### `POST /api/v1/quality/check`
|
|
221
|
+
Run a quality check against a data contract.
|
|
222
|
+
|
|
223
|
+
#### `POST /api/v1/quality/violations`
|
|
224
|
+
Query data quality violations.
|
|
225
|
+
|
|
226
|
+
### Schema Endpoints
|
|
227
|
+
|
|
228
|
+
#### `POST /api/v1/schemas/generate`
|
|
229
|
+
Generate Pydantic model from JSON Schema.
|
|
230
|
+
|
|
231
|
+
#### `POST /api/v1/schemas/convert`
|
|
232
|
+
Convert Pydantic model to JSON Schema.
|
|
233
|
+
|
|
234
|
+
### Validation Endpoints
|
|
235
|
+
|
|
236
|
+
#### `POST /api/v1/validation/validate`
|
|
237
|
+
Validate a single record against a schema.
|
|
238
|
+
|
|
239
|
+
#### `POST /api/v1/validation/validate-batch`
|
|
240
|
+
Validate a batch of records against a schema.
|
|
241
|
+
|
|
242
|
+
## API Documentation
|
|
243
|
+
|
|
244
|
+
Once the API server is running, you can access:
|
|
245
|
+
|
|
246
|
+
- **Swagger UI**: http://localhost:8000/docs
|
|
247
|
+
- **ReDoc**: http://localhost:8000/redoc
|
|
248
|
+
- **OpenAPI JSON**: http://localhost:8000/openapi.json
|
|
249
|
+
|
|
250
|
+
## Example Usage
|
|
251
|
+
|
|
252
|
+
### Using curl:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
# List all contracts
|
|
256
|
+
curl http://localhost:8000/api/v1/contracts
|
|
257
|
+
|
|
258
|
+
# Get a specific contract
|
|
259
|
+
curl http://localhost:8000/api/v1/contracts/{contract_id}
|
|
260
|
+
|
|
261
|
+
# List all schemas
|
|
262
|
+
curl http://localhost:8000/api/v1/metadata/schemas
|
|
263
|
+
|
|
264
|
+
# Get a schema
|
|
265
|
+
curl http://localhost:8000/api/v1/metadata/schemas/{schema_id}
|
|
266
|
+
|
|
267
|
+
# Get quality metrics
|
|
268
|
+
curl http://localhost:8000/api/v1/quality/metrics
|
|
269
|
+
|
|
270
|
+
# Get quality report for a schema
|
|
271
|
+
curl http://localhost:8000/api/v1/quality/reports/{schema_id}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Using Python requests:
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
import requests
|
|
278
|
+
|
|
279
|
+
# List contracts
|
|
280
|
+
response = requests.get("http://localhost:8000/api/v1/contracts")
|
|
281
|
+
contracts = response.json()
|
|
282
|
+
|
|
283
|
+
# Get quality metrics
|
|
284
|
+
response = requests.get("http://localhost:8000/api/v1/quality/metrics")
|
|
285
|
+
metrics = response.json()
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Troubleshooting
|
|
289
|
+
|
|
290
|
+
### Database Connection Issues
|
|
291
|
+
|
|
292
|
+
If you see errors about database URL not being configured:
|
|
293
|
+
|
|
294
|
+
1. **Check environment variable is set:**
|
|
295
|
+
```bash
|
|
296
|
+
echo $PYCHARTER_DATABASE_URL
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
2. **Verify database is accessible:**
|
|
300
|
+
```bash
|
|
301
|
+
psql $PYCHARTER_DATABASE_URL -c "SELECT 1;"
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
3. **Ensure database is initialized:**
|
|
305
|
+
```bash
|
|
306
|
+
pycharter db init $PYCHARTER_DATABASE_URL
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### API Not Starting
|
|
310
|
+
|
|
311
|
+
If the API fails to start:
|
|
312
|
+
|
|
313
|
+
1. **Check if uvicorn is installed:**
|
|
314
|
+
```bash
|
|
315
|
+
pip install pycharter[api]
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
2. **Check for port conflicts:**
|
|
319
|
+
```bash
|
|
320
|
+
# Use a different port
|
|
321
|
+
pycharter api --port 8001
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
3. **Check logs for errors:**
|
|
325
|
+
The API will print error messages to stderr if there are configuration issues.
|
|
326
|
+
|
|
327
|
+
## Production Deployment
|
|
328
|
+
|
|
329
|
+
For production, consider:
|
|
330
|
+
|
|
331
|
+
1. **Use a process manager** (systemd, supervisor, etc.)
|
|
332
|
+
2. **Set environment variables** in your deployment configuration
|
|
333
|
+
3. **Use a reverse proxy** (nginx, traefik) in front of the API
|
|
334
|
+
4. **Enable HTTPS** for secure connections
|
|
335
|
+
5. **Configure CORS** appropriately in `api/main.py`
|
|
336
|
+
6. **Use a production ASGI server** like Gunicorn with Uvicorn workers:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
gunicorn api.main:app -w 4 -k uvicorn.workers.UvicornWorker
|
|
340
|
+
```
|
|
@@ -17,7 +17,7 @@ __version__ = "0.0.3"
|
|
|
17
17
|
|
|
18
18
|
# Check if FastAPI is available
|
|
19
19
|
try:
|
|
20
|
-
from api.main import app, create_application
|
|
20
|
+
from pycharter.api.main import app, create_application
|
|
21
21
|
__all__ = ["app", "create_application"]
|
|
22
22
|
except (ImportError, ModuleNotFoundError):
|
|
23
23
|
# FastAPI not installed or api module not available
|
|
@@ -4,7 +4,7 @@ Dependencies for API routes.
|
|
|
4
4
|
This module provides FastAPI dependency injection for shared resources.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from api.dependencies.database import get_db_session
|
|
8
|
-
from api.dependencies.store import get_metadata_store
|
|
7
|
+
from pycharter.api.dependencies.database import get_db_session
|
|
8
|
+
from pycharter.api.dependencies.store import get_metadata_store
|
|
9
9
|
|
|
10
10
|
__all__ = ["get_db_session", "get_metadata_store"]
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authentication dependency for API routes.
|
|
3
|
+
|
|
4
|
+
Supports:
|
|
5
|
+
- Local/dev: initial credentials from env or pycharter.cfg; JWT issued and verified in-process.
|
|
6
|
+
- Optional auth service: token introspect via HTTP (when auth_service_url + introspect_path set).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import hmac
|
|
10
|
+
import time
|
|
11
|
+
from typing import Any, Optional
|
|
12
|
+
|
|
13
|
+
import jwt
|
|
14
|
+
from fastapi import Depends, HTTPException, status
|
|
15
|
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
16
|
+
|
|
17
|
+
from pycharter.config import (
|
|
18
|
+
get_auth_initial_credentials,
|
|
19
|
+
get_auth_jwt_secret,
|
|
20
|
+
get_auth_service_introspect_path,
|
|
21
|
+
get_auth_service_url,
|
|
22
|
+
is_auth_disabled,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# HTTPBearer returns the token from Authorization: Bearer <token>
|
|
26
|
+
security = HTTPBearer(auto_error=False)
|
|
27
|
+
|
|
28
|
+
# Default JWT expiry (seconds)
|
|
29
|
+
ACCESS_TOKEN_EXPIRE_SECONDS = 3600 # 1 hour
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _constant_time_compare(a: str, b: str) -> bool:
|
|
33
|
+
"""Constant-time string comparison to avoid timing attacks."""
|
|
34
|
+
return hmac.compare_digest(a.encode("utf-8"), b.encode("utf-8"))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def verify_initial_credentials(username: str, password: str) -> bool:
|
|
38
|
+
"""Verify username/password against initial credentials (env or cfg). Returns True if valid."""
|
|
39
|
+
creds = get_auth_initial_credentials()
|
|
40
|
+
if not creds:
|
|
41
|
+
return False
|
|
42
|
+
u, p = creds
|
|
43
|
+
return _constant_time_compare(username, u) and _constant_time_compare(password, p)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def create_access_token(username: str, expires_delta_seconds: int = ACCESS_TOKEN_EXPIRE_SECONDS) -> str:
|
|
47
|
+
"""Create a JWT access token for the given username."""
|
|
48
|
+
secret = get_auth_jwt_secret()
|
|
49
|
+
if not secret:
|
|
50
|
+
raise ValueError("JWT secret not configured (set PYCHARTER_AUTH_JWT_SECRET or auth.jwt_secret)")
|
|
51
|
+
payload = {
|
|
52
|
+
"sub": username,
|
|
53
|
+
"exp": int(time.time()) + expires_delta_seconds,
|
|
54
|
+
"iat": int(time.time()),
|
|
55
|
+
}
|
|
56
|
+
return jwt.encode(payload, secret, algorithm="HS256")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def verify_jwt(token: str) -> Optional[dict[str, Any]]:
|
|
60
|
+
"""Verify JWT and return payload (with 'sub' = username) or None if invalid."""
|
|
61
|
+
secret = get_auth_jwt_secret()
|
|
62
|
+
if not secret:
|
|
63
|
+
return None
|
|
64
|
+
try:
|
|
65
|
+
payload = jwt.decode(token, secret, algorithms=["HS256"])
|
|
66
|
+
return payload
|
|
67
|
+
except jwt.PyJWTError:
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async def introspect_token(token: str) -> Optional[dict[str, Any]]:
|
|
72
|
+
"""Call auth service introspect endpoint. Return payload if active, else None."""
|
|
73
|
+
base = get_auth_service_url()
|
|
74
|
+
path = get_auth_service_introspect_path()
|
|
75
|
+
if not base or not path:
|
|
76
|
+
return None
|
|
77
|
+
url = f"{base.rstrip('/')}{path}"
|
|
78
|
+
try:
|
|
79
|
+
import httpx
|
|
80
|
+
async with httpx.AsyncClient() as client:
|
|
81
|
+
# Common pattern: POST with token in body or header
|
|
82
|
+
r = await client.post(
|
|
83
|
+
url,
|
|
84
|
+
data={"token": token},
|
|
85
|
+
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
86
|
+
timeout=5.0,
|
|
87
|
+
)
|
|
88
|
+
if r.status_code != 200:
|
|
89
|
+
return None
|
|
90
|
+
data = r.json()
|
|
91
|
+
# Typical introspect: {"active": true, "sub": "username", ...}
|
|
92
|
+
if not data.get("active", False):
|
|
93
|
+
return None
|
|
94
|
+
return data
|
|
95
|
+
except Exception:
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
async def get_current_user(
|
|
100
|
+
credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
|
|
101
|
+
) -> dict[str, Any]:
|
|
102
|
+
"""
|
|
103
|
+
Resolve current user from Bearer token. Uses JWT verify or auth service introspect.
|
|
104
|
+
When auth is disabled, returns a dummy user so routes do not need to branch.
|
|
105
|
+
"""
|
|
106
|
+
if is_auth_disabled():
|
|
107
|
+
return {"username": "anonymous", "auth_disabled": True}
|
|
108
|
+
|
|
109
|
+
token = None
|
|
110
|
+
if credentials and credentials.credentials:
|
|
111
|
+
token = credentials.credentials
|
|
112
|
+
|
|
113
|
+
if not token:
|
|
114
|
+
raise HTTPException(
|
|
115
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
116
|
+
detail="Not authenticated",
|
|
117
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Prefer JWT verify (no HTTP); fall back to introspect if configured
|
|
121
|
+
payload = verify_jwt(token)
|
|
122
|
+
if payload is None and (get_auth_service_url() and get_auth_service_introspect_path()):
|
|
123
|
+
payload = await introspect_token(token)
|
|
124
|
+
|
|
125
|
+
if payload is None:
|
|
126
|
+
raise HTTPException(
|
|
127
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
128
|
+
detail="Invalid or expired token",
|
|
129
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
username = payload.get("sub") or payload.get("username")
|
|
133
|
+
if not username:
|
|
134
|
+
raise HTTPException(
|
|
135
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
136
|
+
detail="Invalid token payload",
|
|
137
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
return {"username": username}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
async def get_optional_user(
|
|
144
|
+
credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
|
|
145
|
+
) -> Optional[dict[str, Any]]:
|
|
146
|
+
"""Like get_current_user but returns None instead of 401 when no/invalid token. Useful for public endpoints."""
|
|
147
|
+
if is_auth_disabled():
|
|
148
|
+
return {"username": "anonymous", "auth_disabled": True}
|
|
149
|
+
token = credentials.credentials if credentials and credentials.credentials else None
|
|
150
|
+
if not token:
|
|
151
|
+
return None
|
|
152
|
+
payload = verify_jwt(token)
|
|
153
|
+
if payload is None and (get_auth_service_url() and get_auth_service_introspect_path()):
|
|
154
|
+
payload = await introspect_token(token)
|
|
155
|
+
if payload is None:
|
|
156
|
+
return None
|
|
157
|
+
username = payload.get("sub") or payload.get("username")
|
|
158
|
+
return {"username": username} if username else None
|
{api → pycharter/api}/main.py
RENAMED
|
@@ -16,7 +16,8 @@ from fastapi.routing import APIRoute
|
|
|
16
16
|
from pycharter import __version__ as pycharter_version
|
|
17
17
|
|
|
18
18
|
# Import routers from v1
|
|
19
|
-
from api.routes.v1 import (
|
|
19
|
+
from pycharter.api.routes.v1 import (
|
|
20
|
+
auth,
|
|
20
21
|
contracts,
|
|
21
22
|
metadata,
|
|
22
23
|
quality,
|
|
@@ -27,7 +28,10 @@ from api.routes.v1 import (
|
|
|
27
28
|
docs,
|
|
28
29
|
tracking,
|
|
29
30
|
evolution,
|
|
31
|
+
etl,
|
|
30
32
|
)
|
|
33
|
+
from pycharter.api.dependencies.auth import get_current_user
|
|
34
|
+
from fastapi import Depends
|
|
31
35
|
|
|
32
36
|
# Try to import validation_jobs router (requires worker component)
|
|
33
37
|
try:
|
|
@@ -102,57 +106,80 @@ def create_application() -> FastAPI:
|
|
|
102
106
|
allow_headers=["*"],
|
|
103
107
|
)
|
|
104
108
|
|
|
105
|
-
#
|
|
106
|
-
|
|
109
|
+
# Auth router: no global auth dependency (login/logout public; /auth/me uses Depends in route)
|
|
110
|
+
app.include_router(
|
|
111
|
+
auth.router,
|
|
112
|
+
prefix=f"/api/{API_VERSION}",
|
|
113
|
+
tags=["Auth"],
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Protected routers: require valid Bearer token (when auth enabled; when disabled, get_current_user returns dummy user)
|
|
117
|
+
_auth_dep = [Depends(get_current_user)]
|
|
107
118
|
app.include_router(
|
|
108
119
|
contracts.router,
|
|
109
120
|
prefix=f"/api/{API_VERSION}",
|
|
110
121
|
tags=["Contracts"],
|
|
122
|
+
dependencies=_auth_dep,
|
|
111
123
|
)
|
|
112
124
|
app.include_router(
|
|
113
125
|
metadata.router,
|
|
114
126
|
prefix=f"/api/{API_VERSION}",
|
|
115
127
|
tags=["Metadata"],
|
|
128
|
+
dependencies=_auth_dep,
|
|
116
129
|
)
|
|
117
130
|
app.include_router(
|
|
118
131
|
schemas.router,
|
|
119
132
|
prefix=f"/api/{API_VERSION}",
|
|
120
133
|
tags=["Schemas"],
|
|
134
|
+
dependencies=_auth_dep,
|
|
121
135
|
)
|
|
122
136
|
app.include_router(
|
|
123
137
|
validation.router,
|
|
124
138
|
prefix=f"/api/{API_VERSION}",
|
|
125
139
|
tags=["Validation"],
|
|
140
|
+
dependencies=_auth_dep,
|
|
126
141
|
)
|
|
127
142
|
app.include_router(
|
|
128
143
|
quality.router,
|
|
129
144
|
prefix=f"/api/{API_VERSION}",
|
|
130
145
|
tags=["Quality"],
|
|
146
|
+
dependencies=_auth_dep,
|
|
131
147
|
)
|
|
132
148
|
app.include_router(
|
|
133
149
|
templates.router,
|
|
134
150
|
prefix=f"/api/{API_VERSION}",
|
|
135
151
|
tags=["Templates"],
|
|
152
|
+
dependencies=_auth_dep,
|
|
153
|
+
)
|
|
154
|
+
app.include_router(
|
|
155
|
+
etl.router,
|
|
156
|
+
prefix=f"/api/{API_VERSION}",
|
|
157
|
+
tags=["ETL"],
|
|
158
|
+
dependencies=_auth_dep,
|
|
136
159
|
)
|
|
137
160
|
app.include_router(
|
|
138
161
|
settings.router,
|
|
139
162
|
prefix=f"/api/{API_VERSION}",
|
|
140
163
|
tags=["Settings"],
|
|
164
|
+
dependencies=_auth_dep,
|
|
141
165
|
)
|
|
142
166
|
app.include_router(
|
|
143
167
|
docs.router,
|
|
144
168
|
prefix=f"/api/{API_VERSION}",
|
|
145
169
|
tags=["Documentation"],
|
|
170
|
+
dependencies=_auth_dep,
|
|
146
171
|
)
|
|
147
172
|
app.include_router(
|
|
148
173
|
tracking.router,
|
|
149
174
|
prefix=f"/api/{API_VERSION}",
|
|
150
175
|
tags=["Quality Tracking"],
|
|
176
|
+
dependencies=_auth_dep,
|
|
151
177
|
)
|
|
152
178
|
app.include_router(
|
|
153
179
|
evolution.router,
|
|
154
180
|
prefix=f"/api/{API_VERSION}",
|
|
155
181
|
tags=["Schema Evolution"],
|
|
182
|
+
dependencies=_auth_dep,
|
|
156
183
|
)
|
|
157
184
|
|
|
158
185
|
# Include validation_jobs router if worker component is available
|
|
@@ -161,6 +188,7 @@ def create_application() -> FastAPI:
|
|
|
161
188
|
validation_jobs.router,
|
|
162
189
|
prefix=f"/api/{API_VERSION}",
|
|
163
190
|
tags=["Validation Jobs"],
|
|
191
|
+
dependencies=_auth_dep,
|
|
164
192
|
)
|
|
165
193
|
|
|
166
194
|
# Root endpoint
|
|
@@ -296,7 +324,7 @@ def main():
|
|
|
296
324
|
import uvicorn
|
|
297
325
|
|
|
298
326
|
uvicorn.run(
|
|
299
|
-
"api.main:app",
|
|
327
|
+
"pycharter.api.main:app",
|
|
300
328
|
host="0.0.0.0",
|
|
301
329
|
port=8000,
|
|
302
330
|
reload=True, # Set to False in production
|
|
@@ -4,13 +4,13 @@ API request and response models.
|
|
|
4
4
|
This module contains Pydantic models for API request/response validation.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from api.models.contracts import (
|
|
7
|
+
from pycharter.api.models.contracts import (
|
|
8
8
|
ContractParseRequest,
|
|
9
9
|
ContractParseResponse,
|
|
10
10
|
ContractBuildRequest,
|
|
11
11
|
ContractBuildResponse,
|
|
12
12
|
)
|
|
13
|
-
from api.models.metadata import (
|
|
13
|
+
from pycharter.api.models.metadata import (
|
|
14
14
|
MetadataStoreRequest,
|
|
15
15
|
MetadataStoreResponse,
|
|
16
16
|
MetadataGetRequest,
|
|
@@ -20,13 +20,13 @@ from api.models.metadata import (
|
|
|
20
20
|
SchemaGetRequest,
|
|
21
21
|
SchemaGetResponse,
|
|
22
22
|
)
|
|
23
|
-
from api.models.schemas import (
|
|
23
|
+
from pycharter.api.models.schemas import (
|
|
24
24
|
SchemaGenerateRequest,
|
|
25
25
|
SchemaGenerateResponse,
|
|
26
26
|
SchemaConvertRequest,
|
|
27
27
|
SchemaConvertResponse,
|
|
28
28
|
)
|
|
29
|
-
from api.models.validation import (
|
|
29
|
+
from pycharter.api.models.validation import (
|
|
30
30
|
ValidationRequest,
|
|
31
31
|
ValidationResponse,
|
|
32
32
|
ValidationBatchRequest,
|