core-framework 0.17.59__tar.gz → 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- core_framework-1.2.0/.cursor/rules/api-layer.mdc +37 -0
- core_framework-1.2.0/.cursor/rules/api-reference-docs.mdc +31 -0
- core_framework-1.2.0/.cursor/rules/api-security.mdc +18 -0
- core_framework-1.2.0/.cursor/rules/api-validation.mdc +107 -0
- core_framework-1.2.0/.cursor/rules/application-layer.mdc +79 -0
- core_framework-1.2.0/.cursor/rules/constants-final.mdc +15 -0
- core_framework-1.2.0/.cursor/rules/database-triggers.md +207 -0
- core_framework-1.2.0/.cursor/rules/domain-caller-context.mdc +45 -0
- core_framework-1.2.0/.cursor/rules/domain-imports.mdc +83 -0
- core_framework-1.2.0/.cursor/rules/domain-input-guards.mdc +71 -0
- core_framework-1.2.0/.cursor/rules/domain-repository-exceptions.mdc +20 -0
- core_framework-1.2.0/.cursor/rules/flow-documentation.mdc +326 -0
- core_framework-1.2.0/.cursor/rules/implementation-workflow.mdc +68 -0
- core_framework-1.2.0/.cursor/rules/integration-test-strategy.mdc +29 -0
- core_framework-1.2.0/.cursor/rules/layer-boundaries.mdc +36 -0
- core_framework-1.2.0/.cursor/rules/no-code-in-docs.mdc +30 -0
- core_framework-1.2.0/.cursor/rules/no-docstrings.mdc +13 -0
- core_framework-1.2.0/.cursor/rules/postgres-config-conventions.mdc +41 -0
- core_framework-1.2.0/.cursor/rules/repository-read-consistency.mdc +55 -0
- core_framework-1.2.0/.cursor/rules/strong-read-opt-in.mdc +22 -0
- core_framework-1.2.0/.cursor/rules/tech-stack.mdc +14 -0
- core_framework-1.2.0/.cursor/skills/add-config/SKILL.md +57 -0
- core_framework-1.2.0/.cursor/skills/add-domain/SKILL.md +203 -0
- core_framework-1.2.0/.cursor/skills/code-review/SKILL.md +92 -0
- core_framework-1.2.0/.cursor/skills/recommend-features/SKILL.md +136 -0
- core_framework-1.2.0/.dockerignore +39 -0
- core_framework-1.2.0/.github/workflows/_deploy.yml +142 -0
- core_framework-1.2.0/.github/workflows/dev-ci-cd.yaml +125 -0
- core_framework-1.2.0/.github/workflows/manual-deployment.yaml +36 -0
- core_framework-1.2.0/.github/workflows/test.yaml +114 -0
- core_framework-1.2.0/.gitignore +26 -0
- core_framework-1.2.0/.logfire/.gitignore +1 -0
- core_framework-1.2.0/.logfire/logfire_credentials.json +6 -0
- core_framework-1.2.0/.pre-commit-config.yaml +51 -0
- core_framework-1.2.0/.python-version +1 -0
- core_framework-1.2.0/CHANGELOG.md +127 -0
- core_framework-1.2.0/LICENSE +21 -0
- core_framework-1.2.0/PKG-INFO +131 -0
- core_framework-1.2.0/README.md +97 -0
- core_framework-1.2.0/alembic/comment/alembic/README +1 -0
- core_framework-1.2.0/alembic/comment/alembic/env.py +77 -0
- core_framework-1.2.0/alembic/comment/alembic/script.py.mako +28 -0
- core_framework-1.2.0/alembic/comment/alembic/versions/v1_comment_init_baseline.py +156 -0
- core_framework-1.2.0/alembic/comment/alembic.ini +52 -0
- core_framework-1.2.0/alembic/extension/alembic/README +1 -0
- core_framework-1.2.0/alembic/extension/alembic/env.py +103 -0
- core_framework-1.2.0/alembic/extension/alembic/script.py.mako +28 -0
- core_framework-1.2.0/alembic/extension/alembic/versions/v1_ext_init_baseline.py +31 -0
- core_framework-1.2.0/alembic/extension/alembic.ini +147 -0
- core_framework-1.2.0/alembic/moderation/alembic/README +1 -0
- core_framework-1.2.0/alembic/moderation/alembic/env.py +103 -0
- core_framework-1.2.0/alembic/moderation/alembic/script.py.mako +28 -0
- core_framework-1.2.0/alembic/moderation/alembic/versions/v1_mod_init_baseline.py +291 -0
- core_framework-1.2.0/alembic/moderation/alembic.ini +147 -0
- core_framework-1.2.0/alembic/notification/alembic/README +1 -0
- core_framework-1.2.0/alembic/notification/alembic/env.py +77 -0
- core_framework-1.2.0/alembic/notification/alembic/script.py.mako +28 -0
- core_framework-1.2.0/alembic/notification/alembic/versions/v1_notif_init_baseline.py +134 -0
- core_framework-1.2.0/alembic/notification/alembic.ini +52 -0
- core_framework-1.2.0/alembic/post/alembic/README +1 -0
- core_framework-1.2.0/alembic/post/alembic/env.py +102 -0
- core_framework-1.2.0/alembic/post/alembic/script.py.mako +28 -0
- core_framework-1.2.0/alembic/post/alembic/versions/v1_post_init_baseline.py +178 -0
- core_framework-1.2.0/alembic/post/alembic.ini +149 -0
- core_framework-1.2.0/alembic/user/alembic/README +1 -0
- core_framework-1.2.0/alembic/user/alembic/env.py +103 -0
- core_framework-1.2.0/alembic/user/alembic/script.py.mako +28 -0
- core_framework-1.2.0/alembic/user/alembic/versions/v1_user_init_baseline.py +299 -0
- core_framework-1.2.0/alembic/user/alembic.ini +147 -0
- core_framework-1.2.0/config.toml +81 -0
- core_framework-1.2.0/config.toml.template +74 -0
- core_framework-1.2.0/core_framework/api/__init__.py +0 -0
- core_framework-1.2.0/core_framework/api/admin/__init__.py +0 -0
- core_framework-1.2.0/core_framework/api/admin/comments/router.py +76 -0
- core_framework-1.2.0/core_framework/api/admin/comments/schemas.py +47 -0
- core_framework-1.2.0/core_framework/api/admin/moderation/__init__.py +0 -0
- core_framework-1.2.0/core_framework/api/admin/moderation/router.py +211 -0
- core_framework-1.2.0/core_framework/api/admin/moderation/schemas.py +112 -0
- core_framework-1.2.0/core_framework/api/admin/posts/router.py +62 -0
- core_framework-1.2.0/core_framework/api/admin/posts/schemas.py +29 -0
- core_framework-1.2.0/core_framework/api/admin/router.py +17 -0
- core_framework-1.2.0/core_framework/api/admin/users/__init__.py +0 -0
- core_framework-1.2.0/core_framework/api/admin/users/router.py +185 -0
- core_framework-1.2.0/core_framework/api/admin/users/schemas.py +138 -0
- core_framework-1.2.0/core_framework/api/auth/__init__.py +0 -0
- core_framework-1.2.0/core_framework/api/auth/router.py +21 -0
- core_framework-1.2.0/core_framework/api/auth/schemas.py +29 -0
- core_framework-1.2.0/core_framework/api/comments/authenticated/router.py +141 -0
- core_framework-1.2.0/core_framework/api/comments/authenticated/schemas.py +41 -0
- core_framework-1.2.0/core_framework/api/comments/public/router.py +100 -0
- core_framework-1.2.0/core_framework/api/comments/public/schemas.py +37 -0
- core_framework-1.2.0/core_framework/api/comments/router.py +9 -0
- core_framework-1.2.0/core_framework/api/comments/schemas.py +18 -0
- core_framework-1.2.0/core_framework/api/constants.py +3 -0
- core_framework-1.2.0/core_framework/api/dependencies.py +168 -0
- core_framework-1.2.0/core_framework/api/events/router.py +47 -0
- core_framework-1.2.0/core_framework/api/events/schemas.py +20 -0
- core_framework-1.2.0/core_framework/api/notifications/authenticated/router.py +84 -0
- core_framework-1.2.0/core_framework/api/notifications/authenticated/schemas.py +104 -0
- core_framework-1.2.0/core_framework/api/notifications/router.py +7 -0
- core_framework-1.2.0/core_framework/api/posts/authenticated/router.py +98 -0
- core_framework-1.2.0/core_framework/api/posts/authenticated/schemas.py +45 -0
- core_framework-1.2.0/core_framework/api/posts/public/router.py +101 -0
- core_framework-1.2.0/core_framework/api/posts/public/schemas.py +40 -0
- core_framework-1.2.0/core_framework/api/posts/router.py +9 -0
- core_framework-1.2.0/core_framework/api/posts/schemas.py +41 -0
- core_framework-1.2.0/core_framework/api/router.py +21 -0
- core_framework-1.2.0/core_framework/api/schemas.py +15 -0
- core_framework-1.2.0/core_framework/api/system/__init__.py +0 -0
- core_framework-1.2.0/core_framework/api/system/router.py +111 -0
- core_framework-1.2.0/core_framework/api/users/__init__.py +0 -0
- core_framework-1.2.0/core_framework/api/users/authenticated/__init__.py +0 -0
- core_framework-1.2.0/core_framework/api/users/authenticated/router.py +456 -0
- core_framework-1.2.0/core_framework/api/users/authenticated/schemas.py +88 -0
- core_framework-1.2.0/core_framework/api/users/public/__init__.py +0 -0
- core_framework-1.2.0/core_framework/api/users/public/router.py +25 -0
- core_framework-1.2.0/core_framework/api/users/public/schemas.py +7 -0
- core_framework-1.2.0/core_framework/api/users/router.py +9 -0
- core_framework-1.2.0/core_framework/api/users/shared/schemas.py +193 -0
- core_framework-1.2.0/core_framework/application/__init__.py +0 -0
- core_framework-1.2.0/core_framework/application/auth/__init__.py +0 -0
- core_framework-1.2.0/core_framework/application/auth/access_service.py +27 -0
- core_framework-1.2.0/core_framework/application/auth/auth_service.py +15 -0
- core_framework-1.2.0/core_framework/application/auth/models.py +10 -0
- core_framework-1.2.0/core_framework/application/bootstrap.py +21 -0
- core_framework-1.2.0/core_framework/application/comments/admin_service.py +243 -0
- core_framework-1.2.0/core_framework/application/comments/aggregation_service.py +28 -0
- core_framework-1.2.0/core_framework/application/comments/authenticated_service.py +118 -0
- core_framework-1.2.0/core_framework/application/comments/public_service.py +274 -0
- core_framework-1.2.0/core_framework/application/events/README.md +26 -0
- core_framework-1.2.0/core_framework/application/events/event_service.py +72 -0
- core_framework-1.2.0/core_framework/application/events/event_token.py +48 -0
- core_framework-1.2.0/core_framework/application/events/models.py +9 -0
- core_framework-1.2.0/core_framework/application/moderation/__init__.py +0 -0
- core_framework-1.2.0/core_framework/application/moderation/appeal_service.py +104 -0
- core_framework-1.2.0/core_framework/application/moderation/moderator_service.py +46 -0
- core_framework-1.2.0/core_framework/application/moderation/report_service.py +202 -0
- core_framework-1.2.0/core_framework/application/moderation/scheduled_service.py +5 -0
- core_framework-1.2.0/core_framework/application/moderation/user_service.py +208 -0
- core_framework-1.2.0/core_framework/application/notifications/README.md +16 -0
- core_framework-1.2.0/core_framework/application/notifications/enums.py +9 -0
- core_framework-1.2.0/core_framework/application/notifications/inbox_service.py +247 -0
- core_framework-1.2.0/core_framework/application/notifications/mute_service.py +17 -0
- core_framework-1.2.0/core_framework/application/notifications/notification_service.py +165 -0
- core_framework-1.2.0/core_framework/application/posts/admin_service.py +112 -0
- core_framework-1.2.0/core_framework/application/posts/aggregation_service.py +35 -0
- core_framework-1.2.0/core_framework/application/posts/authenticated_service.py +80 -0
- core_framework-1.2.0/core_framework/application/posts/public_service.py +266 -0
- core_framework-1.2.0/core_framework/application/shared/__init__.py +0 -0
- core_framework-1.2.0/core_framework/application/shared/enums.py +11 -0
- core_framework-1.2.0/core_framework/application/shared/exceptions.py +16 -0
- core_framework-1.2.0/core_framework/application/shared/user_agent.py +24 -0
- core_framework-1.2.0/core_framework/application/users/__init__.py +0 -0
- core_framework-1.2.0/core_framework/application/users/admin_service.py +304 -0
- core_framework-1.2.0/core_framework/application/users/aggregation_service.py +18 -0
- core_framework-1.2.0/core_framework/application/users/authenticated_service.py +276 -0
- core_framework-1.2.0/core_framework/application/users/public_service.py +8 -0
- core_framework-1.2.0/core_framework/application/users/scheduled_service.py +5 -0
- core_framework-1.2.0/core_framework/asgi.py +4 -0
- core_framework-1.2.0/core_framework/bundled_alembic.py +44 -0
- core_framework-1.2.0/core_framework/constants.py +5 -0
- core_framework-1.2.0/core_framework/core/__init__.py +33 -0
- core_framework-1.2.0/core_framework/core/cache.py +136 -0
- core_framework-1.2.0/core_framework/core/context.py +14 -0
- core_framework-1.2.0/core_framework/core/database.py +116 -0
- core_framework-1.2.0/core_framework/core/exception_handlers/__init__.py +3 -0
- core_framework-1.2.0/core_framework/core/exception_handlers/comment.py +99 -0
- core_framework-1.2.0/core_framework/core/exception_handlers/common.py +5 -0
- core_framework-1.2.0/core_framework/core/exception_handlers/moderation.py +104 -0
- core_framework-1.2.0/core_framework/core/exception_handlers/notification.py +22 -0
- core_framework-1.2.0/core_framework/core/exception_handlers/post.py +54 -0
- core_framework-1.2.0/core_framework/core/exception_handlers/setup.py +98 -0
- core_framework-1.2.0/core_framework/core/exception_handlers/user.py +72 -0
- core_framework-1.2.0/core_framework/core/firebase.py +21 -0
- core_framework-1.2.0/core_framework/core/http_client.py +64 -0
- core_framework-1.2.0/core_framework/core/logging.py +83 -0
- core_framework-1.2.0/core_framework/core/middleware.py +64 -0
- core_framework-1.2.0/core_framework/core/observability.py +31 -0
- core_framework-1.2.0/core_framework/core/pagination.py +200 -0
- core_framework-1.2.0/core_framework/core/redis.py +57 -0
- core_framework-1.2.0/core_framework/core/runtime.py +60 -0
- core_framework-1.2.0/core_framework/core/settings.py +202 -0
- core_framework-1.2.0/core_framework/domains/__init__.py +0 -0
- core_framework-1.2.0/core_framework/domains/comment/README.md +245 -0
- core_framework-1.2.0/core_framework/domains/comment/__init__.py +32 -0
- core_framework-1.2.0/core_framework/domains/comment/constants.py +3 -0
- core_framework-1.2.0/core_framework/domains/comment/dependencies.py +29 -0
- core_framework-1.2.0/core_framework/domains/comment/enums.py +10 -0
- core_framework-1.2.0/core_framework/domains/comment/exceptions.py +31 -0
- core_framework-1.2.0/core_framework/domains/comment/models.py +71 -0
- core_framework-1.2.0/core_framework/domains/comment/repository.py +988 -0
- core_framework-1.2.0/core_framework/domains/comment/service.py +347 -0
- core_framework-1.2.0/core_framework/domains/exceptions.py +6 -0
- core_framework-1.2.0/core_framework/domains/moderation/README.md +138 -0
- core_framework-1.2.0/core_framework/domains/moderation/__init__.py +49 -0
- core_framework-1.2.0/core_framework/domains/moderation/dependencies.py +29 -0
- core_framework-1.2.0/core_framework/domains/moderation/enums.py +62 -0
- core_framework-1.2.0/core_framework/domains/moderation/exceptions.py +31 -0
- core_framework-1.2.0/core_framework/domains/moderation/models.py +94 -0
- core_framework-1.2.0/core_framework/domains/moderation/repository.py +805 -0
- core_framework-1.2.0/core_framework/domains/moderation/service.py +393 -0
- core_framework-1.2.0/core_framework/domains/notification/README.md +151 -0
- core_framework-1.2.0/core_framework/domains/notification/__init__.py +16 -0
- core_framework-1.2.0/core_framework/domains/notification/dependencies.py +29 -0
- core_framework-1.2.0/core_framework/domains/notification/enums.py +19 -0
- core_framework-1.2.0/core_framework/domains/notification/exceptions.py +6 -0
- core_framework-1.2.0/core_framework/domains/notification/models.py +24 -0
- core_framework-1.2.0/core_framework/domains/notification/repository.py +404 -0
- core_framework-1.2.0/core_framework/domains/notification/service.py +168 -0
- core_framework-1.2.0/core_framework/domains/post/README.md +190 -0
- core_framework-1.2.0/core_framework/domains/post/__init__.py +22 -0
- core_framework-1.2.0/core_framework/domains/post/constants.py +3 -0
- core_framework-1.2.0/core_framework/domains/post/dependencies.py +29 -0
- core_framework-1.2.0/core_framework/domains/post/enums.py +18 -0
- core_framework-1.2.0/core_framework/domains/post/exceptions.py +21 -0
- core_framework-1.2.0/core_framework/domains/post/models.py +61 -0
- core_framework-1.2.0/core_framework/domains/post/repository.py +962 -0
- core_framework-1.2.0/core_framework/domains/post/service.py +321 -0
- core_framework-1.2.0/core_framework/domains/user/README.md +99 -0
- core_framework-1.2.0/core_framework/domains/user/__init__.py +45 -0
- core_framework-1.2.0/core_framework/domains/user/constants.py +41 -0
- core_framework-1.2.0/core_framework/domains/user/dependencies.py +29 -0
- core_framework-1.2.0/core_framework/domains/user/enums.py +19 -0
- core_framework-1.2.0/core_framework/domains/user/exceptions.py +31 -0
- core_framework-1.2.0/core_framework/domains/user/models.py +153 -0
- core_framework-1.2.0/core_framework/domains/user/repository.py +854 -0
- core_framework-1.2.0/core_framework/domains/user/service.py +404 -0
- core_framework-1.2.0/core_framework/domains/user/utils.py +187 -0
- core_framework-1.2.0/core_framework/domains/utils.py +53 -0
- core_framework-1.2.0/core_framework/main.py +98 -0
- core_framework-1.2.0/core_framework/migrate_cli.py +16 -0
- core_framework-1.2.0/core_framework/testing/__init__.py +7 -0
- core_framework-1.2.0/core_framework/testing/arq.py +80 -0
- core_framework-1.2.0/core_framework/testing/auth.py +187 -0
- core_framework-1.2.0/core_framework/testing/config.py +28 -0
- core_framework-1.2.0/core_framework/testing/containers.py +63 -0
- core_framework-1.2.0/core_framework/testing/firebase.py +77 -0
- core_framework-1.2.0/core_framework/testing/hookspecs.py +14 -0
- core_framework-1.2.0/core_framework/testing/httpx_test_client.py +17 -0
- core_framework-1.2.0/core_framework/testing/migrations.py +38 -0
- core_framework-1.2.0/core_framework/testing/plugin.py +176 -0
- core_framework-1.2.0/core_framework/worker/__init__.py +0 -0
- core_framework-1.2.0/core_framework/worker/main.py +73 -0
- core_framework-1.2.0/core_framework/worker/schedules/__init__.py +49 -0
- core_framework-1.2.0/core_framework/worker/schedules/schedule_aggregate_comment_stats.py +39 -0
- core_framework-1.2.0/core_framework/worker/schedules/schedule_aggregate_post_stats.py +39 -0
- core_framework-1.2.0/core_framework/worker/schedules/schedule_aggregate_user_stats.py +39 -0
- core_framework-1.2.0/core_framework/worker/schedules/schedule_expired_account_deletions.py +29 -0
- core_framework-1.2.0/core_framework/worker/schedules/schedule_expired_mute_lifts.py +29 -0
- core_framework-1.2.0/core_framework/worker/tasks/__init__.py +13 -0
- core_framework-1.2.0/core_framework/worker/tasks/process_account_deletion.py +15 -0
- core_framework-1.2.0/core_framework/worker/tasks/process_aggregate_comment_stats.py +21 -0
- core_framework-1.2.0/core_framework/worker/tasks/process_aggregate_post_stats.py +18 -0
- core_framework-1.2.0/core_framework/worker/tasks/process_aggregate_user_stats.py +18 -0
- core_framework-1.2.0/core_framework/worker/tasks/process_mute_lift.py +15 -0
- core_framework-1.2.0/core_framework/worker/worker_context.py +20 -0
- core_framework-1.2.0/docker-compose.yml +40 -0
- core_framework-1.2.0/dockerfile +56 -0
- core_framework-1.2.0/docs/api.md +182 -0
- core_framework-1.2.0/docs/architecture-decisions.md +460 -0
- core_framework-1.2.0/docs/architecture.md +129 -0
- core_framework-1.2.0/docs/conventions.md +165 -0
- core_framework-1.2.0/docs/core-framework-migration.md +307 -0
- core_framework-1.2.0/docs/database-triggers.md +88 -0
- core_framework-1.2.0/docs/event-outbox-design.md +528 -0
- core_framework-1.2.0/docs/flows/auth/access_control.md +58 -0
- core_framework-1.2.0/docs/flows/auth/registration.md +58 -0
- core_framework-1.2.0/docs/flows/comments/admin_comments.md +244 -0
- core_framework-1.2.0/docs/flows/comments/comment_report.md +117 -0
- core_framework-1.2.0/docs/flows/comments/comment_stats_aggregation.md +69 -0
- core_framework-1.2.0/docs/flows/comments/create_comment.md +120 -0
- core_framework-1.2.0/docs/flows/comments/delete_comment.md +60 -0
- core_framework-1.2.0/docs/flows/comments/edit_comment.md +67 -0
- core_framework-1.2.0/docs/flows/comments/retrieve_comments.md +216 -0
- core_framework-1.2.0/docs/flows/events/events.md +112 -0
- core_framework-1.2.0/docs/flows/mentions/mentions_in_content.md +177 -0
- core_framework-1.2.0/docs/flows/moderation/appeals.md +216 -0
- core_framework-1.2.0/docs/flows/moderation/internal_notes.md +158 -0
- core_framework-1.2.0/docs/flows/moderation/moderator_actions.md +69 -0
- core_framework-1.2.0/docs/flows/moderation/reports.md +183 -0
- core_framework-1.2.0/docs/flows/moderation/restrictions.md +212 -0
- core_framework-1.2.0/docs/flows/notifications/notification_inbox.md +373 -0
- core_framework-1.2.0/docs/flows/posts/admin_posts.md +246 -0
- core_framework-1.2.0/docs/flows/posts/author_context.md +84 -0
- core_framework-1.2.0/docs/flows/posts/hashtag_discovery.md +59 -0
- core_framework-1.2.0/docs/flows/posts/post_like.md +94 -0
- core_framework-1.2.0/docs/flows/posts/post_stats_aggregation.md +81 -0
- core_framework-1.2.0/docs/flows/users/account.md +50 -0
- core_framework-1.2.0/docs/flows/users/account_deletion.md +108 -0
- core_framework-1.2.0/docs/flows/users/blocks.md +115 -0
- core_framework-1.2.0/docs/flows/users/change_history.md +131 -0
- core_framework-1.2.0/docs/flows/users/check_username_exists.md +56 -0
- core_framework-1.2.0/docs/flows/users/follow.md +215 -0
- core_framework-1.2.0/docs/flows/users/my_posts_and_comments.md +108 -0
- core_framework-1.2.0/docs/flows/users/preferences.md +87 -0
- core_framework-1.2.0/docs/flows/users/profile.md +90 -0
- core_framework-1.2.0/docs/flows/users/user_removal.md +86 -0
- core_framework-1.2.0/docs/follow-system-design.md +419 -0
- core_framework-1.2.0/docs/package-api.md +287 -0
- core_framework-1.2.0/docs/testing-plugin-design.md +391 -0
- core_framework-1.2.0/makefile +54 -0
- core_framework-1.2.0/pyproject.toml +122 -0
- core_framework-1.2.0/tests/__init__.py +0 -0
- core_framework-1.2.0/tests/conftest.py +25 -0
- core_framework-1.2.0/tests/integration/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/_http_helpers.py +90 -0
- core_framework-1.2.0/tests/integration/api/admin/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/admin/comments/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/admin/comments/router_test.py +666 -0
- core_framework-1.2.0/tests/integration/api/admin/moderation/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/admin/moderation/router_test.py +2475 -0
- core_framework-1.2.0/tests/integration/api/admin/posts/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/admin/posts/router_test.py +539 -0
- core_framework-1.2.0/tests/integration/api/admin/users/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/admin/users/router_test.py +1572 -0
- core_framework-1.2.0/tests/integration/api/auth/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/auth/router_test.py +52 -0
- core_framework-1.2.0/tests/integration/api/comments/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/comments/authenticated/comment_writes_integration_test.py +515 -0
- core_framework-1.2.0/tests/integration/api/comments/public/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/comments/public/router_test.py +403 -0
- core_framework-1.2.0/tests/integration/api/events/router_test.py +192 -0
- core_framework-1.2.0/tests/integration/api/notifications/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/notifications/router_test.py +548 -0
- core_framework-1.2.0/tests/integration/api/posts/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/posts/authenticated/post_writes_integration_test.py +340 -0
- core_framework-1.2.0/tests/integration/api/posts/comment_count_aggregation_test.py +144 -0
- core_framework-1.2.0/tests/integration/api/posts/followers_visibility_test.py +123 -0
- core_framework-1.2.0/tests/integration/api/posts/post_stats_dirty_marking_test.py +219 -0
- core_framework-1.2.0/tests/integration/api/posts/public/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/posts/public/router_test.py +1071 -0
- core_framework-1.2.0/tests/integration/api/system/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/system/router_test.py +56 -0
- core_framework-1.2.0/tests/integration/api/users/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/users/authenticated/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/users/authenticated/router_test.py +922 -0
- core_framework-1.2.0/tests/integration/api/users/public/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/api/users/public/router_test.py +49 -0
- core_framework-1.2.0/tests/integration/worker/__init__.py +0 -0
- core_framework-1.2.0/tests/integration/worker/account_deletion_test.py +368 -0
- core_framework-1.2.0/tests/integration/worker/aggregate_comment_stats_test.py +294 -0
- core_framework-1.2.0/tests/integration/worker/aggregate_post_stats_test.py +267 -0
- core_framework-1.2.0/tests/integration/worker/aggregate_user_stats_test.py +144 -0
- core_framework-1.2.0/tests/integration/worker/conftest.py +21 -0
- core_framework-1.2.0/tests/integration/worker/mute_lift_test.py +166 -0
- core_framework-1.2.0/tests/integration/worker/utils_test.py +33 -0
- core_framework-1.2.0/tests/unit/__init__.py +0 -0
- core_framework-1.2.0/tests/unit/application/comments/__init__.py +0 -0
- core_framework-1.2.0/tests/unit/application/events/event_service_test.py +103 -0
- core_framework-1.2.0/tests/unit/application/notifications/inbox_service_test.py +111 -0
- core_framework-1.2.0/tests/unit/core/bundled_alembic_test.py +29 -0
- core_framework-1.2.0/tests/unit/core/migrate_cli_test.py +16 -0
- core_framework-1.2.0/tests/unit/core/pagination_test.py +589 -0
- core_framework-1.2.0/tests/unit/domains/__init__.py +0 -0
- core_framework-1.2.0/tests/unit/domains/comment/__init__.py +0 -0
- core_framework-1.2.0/tests/unit/domains/user/service_test.py +182 -0
- core_framework-1.2.0/uv.lock +2163 -0
- core_framework-0.17.59/.gitignore +0 -148
- core_framework-0.17.59/PKG-INFO +0 -416
- core_framework-0.17.59/README.md +0 -345
- core_framework-0.17.59/core/__init__.py +0 -618
- core_framework-0.17.59/core/admin/__init__.py +0 -132
- core_framework-0.17.59/core/admin/_typing.py +0 -92
- core_framework-0.17.59/core/admin/collectstatic.py +0 -202
- core_framework-0.17.59/core/admin/defaults.py +0 -238
- core_framework-0.17.59/core/admin/discovery.py +0 -199
- core_framework-0.17.59/core/admin/event_tracking.py +0 -197
- core_framework-0.17.59/core/admin/exceptions.py +0 -231
- core_framework-0.17.59/core/admin/infrastructure.py +0 -429
- core_framework-0.17.59/core/admin/log_handler.py +0 -241
- core_framework-0.17.59/core/admin/middleware.py +0 -148
- core_framework-0.17.59/core/admin/models.py +0 -531
- core_framework-0.17.59/core/admin/ops_views.py +0 -1818
- core_framework-0.17.59/core/admin/options.py +0 -1005
- core_framework-0.17.59/core/admin/permissions.py +0 -174
- core_framework-0.17.59/core/admin/resource_inventory.py +0 -183
- core_framework-0.17.59/core/admin/router.py +0 -511
- core_framework-0.17.59/core/admin/serializers.py +0 -265
- core_framework-0.17.59/core/admin/site.py +0 -314
- core_framework-0.17.59/core/admin/static/core-admin/css/admin.css +0 -45
- core_framework-0.17.59/core/admin/static/core-admin/js/admin.js +0 -26
- core_framework-0.17.59/core/admin/templates/admin/base.html +0 -666
- core_framework-0.17.59/core/admin/templates/admin/dashboard.html +0 -265
- core_framework-0.17.59/core/admin/templates/admin/detail.html +0 -2524
- core_framework-0.17.59/core/admin/templates/admin/list.html +0 -450
- core_framework-0.17.59/core/admin/templates/admin/login.html +0 -216
- core_framework-0.17.59/core/admin/templates/admin/ops/dashboard.html +0 -285
- core_framework-0.17.59/core/admin/templates/admin/ops/events.html +0 -569
- core_framework-0.17.59/core/admin/templates/admin/ops/infrastructure.html +0 -245
- core_framework-0.17.59/core/admin/templates/admin/ops/kafka.html +0 -1066
- core_framework-0.17.59/core/admin/templates/admin/ops/logs.html +0 -186
- core_framework-0.17.59/core/admin/templates/admin/ops/tasks.html +0 -515
- core_framework-0.17.59/core/admin/templates/admin/ops/workers.html +0 -309
- core_framework-0.17.59/core/admin/types.py +0 -297
- core_framework-0.17.59/core/admin/views.py +0 -1699
- core_framework-0.17.59/core/admin/ws_events.py +0 -174
- core_framework-0.17.59/core/app.py +0 -922
- core_framework-0.17.59/core/auth/__init__.py +0 -217
- core_framework-0.17.59/core/auth/backends.py +0 -354
- core_framework-0.17.59/core/auth/base.py +0 -851
- core_framework-0.17.59/core/auth/decorators.py +0 -464
- core_framework-0.17.59/core/auth/hashers.py +0 -420
- core_framework-0.17.59/core/auth/helpers.py +0 -104
- core_framework-0.17.59/core/auth/middleware.py +0 -473
- core_framework-0.17.59/core/auth/models.py +0 -1082
- core_framework-0.17.59/core/auth/permissions.py +0 -402
- core_framework-0.17.59/core/auth/schemas.py +0 -139
- core_framework-0.17.59/core/auth/tokens.py +0 -327
- core_framework-0.17.59/core/auth/views.py +0 -607
- core_framework-0.17.59/core/choices.py +0 -383
- core_framework-0.17.59/core/cli/__init__.py +0 -19
- core_framework-0.17.59/core/cli/interactive.py +0 -517
- core_framework-0.17.59/core/cli/main.py +0 -3843
- core_framework-0.17.59/core/cli/project_init.py +0 -103
- core_framework-0.17.59/core/cli/templates/__init__.py +0 -174
- core_framework-0.17.59/core/cli/templates/default/.env.template +0 -12
- core_framework-0.17.59/core/cli/templates/default/.gitignore.template +0 -19
- core_framework-0.17.59/core/cli/templates/default/pyproject.toml.template +0 -22
- core_framework-0.17.59/core/cli/templates/default/src/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/default/src/apps/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/default/src/apps/models.py.template +0 -2
- core_framework-0.17.59/core/cli/templates/default/src/apps/users/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/default/src/apps/users/models.py.template +0 -21
- core_framework-0.17.59/core/cli/templates/default/src/apps/users/routes.py.template +0 -9
- core_framework-0.17.59/core/cli/templates/default/src/apps/users/views.py.template +0 -13
- core_framework-0.17.59/core/cli/templates/default/src/main.py.template +0 -9
- core_framework-0.17.59/core/cli/templates/default/src/models.py.template +0 -2
- core_framework-0.17.59/core/cli/templates/default/src/settings.py.template +0 -30
- core_framework-0.17.59/core/cli/templates/kafka/.env.template +0 -16
- core_framework-0.17.59/core/cli/templates/kafka/.gitignore.template +0 -19
- core_framework-0.17.59/core/cli/templates/kafka/docker-compose.yml.template +0 -42
- core_framework-0.17.59/core/cli/templates/kafka/pyproject.toml.template +0 -24
- core_framework-0.17.59/core/cli/templates/kafka/src/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/kafka/src/apps/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/kafka/src/apps/events/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/kafka/src/apps/events/consumers.py.template +0 -19
- core_framework-0.17.59/core/cli/templates/kafka/src/apps/events/models.py.template +0 -18
- core_framework-0.17.59/core/cli/templates/kafka/src/apps/events/producers.py.template +0 -20
- core_framework-0.17.59/core/cli/templates/kafka/src/apps/events/routes.py.template +0 -6
- core_framework-0.17.59/core/cli/templates/kafka/src/apps/events/views.py.template +0 -22
- core_framework-0.17.59/core/cli/templates/kafka/src/apps/models.py.template +0 -2
- core_framework-0.17.59/core/cli/templates/kafka/src/main.py.template +0 -8
- core_framework-0.17.59/core/cli/templates/kafka/src/models.py.template +0 -2
- core_framework-0.17.59/core/cli/templates/kafka/src/settings.py.template +0 -37
- core_framework-0.17.59/core/cli/templates/minimal/.env.template +0 -11
- core_framework-0.17.59/core/cli/templates/minimal/.gitignore.template +0 -19
- core_framework-0.17.59/core/cli/templates/minimal/pyproject.toml.template +0 -22
- core_framework-0.17.59/core/cli/templates/minimal/src/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/minimal/src/apps/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/minimal/src/apps/items/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/minimal/src/apps/items/models.py.template +0 -17
- core_framework-0.17.59/core/cli/templates/minimal/src/apps/items/routes.py.template +0 -6
- core_framework-0.17.59/core/cli/templates/minimal/src/apps/items/views.py.template +0 -12
- core_framework-0.17.59/core/cli/templates/minimal/src/apps/models.py.template +0 -3
- core_framework-0.17.59/core/cli/templates/minimal/src/apps/users/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/minimal/src/apps/users/models.py.template +0 -10
- core_framework-0.17.59/core/cli/templates/minimal/src/main.py.template +0 -8
- core_framework-0.17.59/core/cli/templates/minimal/src/models.py.template +0 -2
- core_framework-0.17.59/core/cli/templates/minimal/src/settings.py.template +0 -26
- core_framework-0.17.59/core/cli/templates/tenant/.env.template +0 -14
- core_framework-0.17.59/core/cli/templates/tenant/.gitignore.template +0 -19
- core_framework-0.17.59/core/cli/templates/tenant/docker-compose.yml.template +0 -19
- core_framework-0.17.59/core/cli/templates/tenant/pyproject.toml.template +0 -22
- core_framework-0.17.59/core/cli/templates/tenant/src/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/tenant/src/apps/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/tenant/src/apps/items/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/tenant/src/apps/items/models.py.template +0 -19
- core_framework-0.17.59/core/cli/templates/tenant/src/apps/items/routes.py.template +0 -6
- core_framework-0.17.59/core/cli/templates/tenant/src/apps/items/views.py.template +0 -18
- core_framework-0.17.59/core/cli/templates/tenant/src/apps/models.py.template +0 -3
- core_framework-0.17.59/core/cli/templates/tenant/src/apps/tenants/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/tenant/src/apps/tenants/models.py.template +0 -17
- core_framework-0.17.59/core/cli/templates/tenant/src/apps/tenants/routes.py.template +0 -6
- core_framework-0.17.59/core/cli/templates/tenant/src/apps/tenants/views.py.template +0 -13
- core_framework-0.17.59/core/cli/templates/tenant/src/main.py.template +0 -10
- core_framework-0.17.59/core/cli/templates/tenant/src/models.py.template +0 -2
- core_framework-0.17.59/core/cli/templates/tenant/src/settings.py.template +0 -35
- core_framework-0.17.59/core/cli/templates/workers/.env.template +0 -18
- core_framework-0.17.59/core/cli/templates/workers/.gitignore.template +0 -19
- core_framework-0.17.59/core/cli/templates/workers/docker-compose.yml.template +0 -24
- core_framework-0.17.59/core/cli/templates/workers/pyproject.toml.template +0 -23
- core_framework-0.17.59/core/cli/templates/workers/src/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/workers/src/apps/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/workers/src/apps/models.py.template +0 -2
- core_framework-0.17.59/core/cli/templates/workers/src/apps/tasks/__init__.py.template +0 -1
- core_framework-0.17.59/core/cli/templates/workers/src/apps/tasks/models.py.template +0 -32
- core_framework-0.17.59/core/cli/templates/workers/src/apps/tasks/routes.py.template +0 -6
- core_framework-0.17.59/core/cli/templates/workers/src/apps/tasks/views.py.template +0 -54
- core_framework-0.17.59/core/cli/templates/workers/src/apps/tasks/workers.py.template +0 -23
- core_framework-0.17.59/core/cli/templates/workers/src/main.py.template +0 -8
- core_framework-0.17.59/core/cli/templates/workers/src/models.py.template +0 -2
- core_framework-0.17.59/core/cli/templates/workers/src/settings.py.template +0 -40
- core_framework-0.17.59/core/config.py +0 -1843
- core_framework-0.17.59/core/database.py +0 -401
- core_framework-0.17.59/core/datetime.py +0 -1271
- core_framework-0.17.59/core/dependencies.py +0 -452
- core_framework-0.17.59/core/deployment/__init__.py +0 -29
- core_framework-0.17.59/core/deployment/docker.py +0 -361
- core_framework-0.17.59/core/deployment/kubernetes.py +0 -361
- core_framework-0.17.59/core/deployment/pm2.py +0 -143
- core_framework-0.17.59/core/exceptions.py +0 -787
- core_framework-0.17.59/core/fields.py +0 -615
- core_framework-0.17.59/core/messaging/__init__.py +0 -157
- core_framework-0.17.59/core/messaging/avro.py +0 -435
- core_framework-0.17.59/core/messaging/base.py +0 -439
- core_framework-0.17.59/core/messaging/config.py +0 -69
- core_framework-0.17.59/core/messaging/confluent/__init__.py +0 -75
- core_framework-0.17.59/core/messaging/confluent/admin.py +0 -762
- core_framework-0.17.59/core/messaging/confluent/broker.py +0 -251
- core_framework-0.17.59/core/messaging/confluent/consumer.py +0 -199
- core_framework-0.17.59/core/messaging/confluent/producer.py +0 -243
- core_framework-0.17.59/core/messaging/decorators.py +0 -280
- core_framework-0.17.59/core/messaging/kafka/__init__.py +0 -56
- core_framework-0.17.59/core/messaging/kafka/admin.py +0 -647
- core_framework-0.17.59/core/messaging/kafka/broker.py +0 -230
- core_framework-0.17.59/core/messaging/kafka/consumer.py +0 -215
- core_framework-0.17.59/core/messaging/kafka/producer.py +0 -193
- core_framework-0.17.59/core/messaging/rabbitmq/__init__.py +0 -26
- core_framework-0.17.59/core/messaging/rabbitmq/broker.py +0 -181
- core_framework-0.17.59/core/messaging/rabbitmq/consumer.py +0 -121
- core_framework-0.17.59/core/messaging/rabbitmq/producer.py +0 -79
- core_framework-0.17.59/core/messaging/redis/__init__.py +0 -60
- core_framework-0.17.59/core/messaging/redis/broker.py +0 -178
- core_framework-0.17.59/core/messaging/redis/connection.py +0 -232
- core_framework-0.17.59/core/messaging/redis/consumer.py +0 -157
- core_framework-0.17.59/core/messaging/redis/producer.py +0 -76
- core_framework-0.17.59/core/messaging/registry.py +0 -431
- core_framework-0.17.59/core/messaging/topics.py +0 -232
- core_framework-0.17.59/core/messaging/tracking.py +0 -124
- core_framework-0.17.59/core/messaging/workers.py +0 -950
- core_framework-0.17.59/core/middleware.py +0 -815
- core_framework-0.17.59/core/migrations/__init__.py +0 -97
- core_framework-0.17.59/core/migrations/analyzer.py +0 -713
- core_framework-0.17.59/core/migrations/cli.py +0 -459
- core_framework-0.17.59/core/migrations/dialects/__init__.py +0 -95
- core_framework-0.17.59/core/migrations/dialects/base.py +0 -196
- core_framework-0.17.59/core/migrations/dialects/mysql.py +0 -79
- core_framework-0.17.59/core/migrations/dialects/postgresql.py +0 -91
- core_framework-0.17.59/core/migrations/dialects/sqlite.py +0 -71
- core_framework-0.17.59/core/migrations/engine.py +0 -923
- core_framework-0.17.59/core/migrations/migration.py +0 -82
- core_framework-0.17.59/core/migrations/operations.py +0 -930
- core_framework-0.17.59/core/migrations/state.py +0 -576
- core_framework-0.17.59/core/models.py +0 -1188
- core_framework-0.17.59/core/permissions.py +0 -400
- core_framework-0.17.59/core/querysets.py +0 -770
- core_framework-0.17.59/core/realtime.py +0 -585
- core_framework-0.17.59/core/relations.py +0 -833
- core_framework-0.17.59/core/routing.py +0 -1177
- core_framework-0.17.59/core/serializers.py +0 -376
- core_framework-0.17.59/core/storage.py +0 -452
- core_framework-0.17.59/core/tasks/__init__.py +0 -72
- core_framework-0.17.59/core/tasks/base.py +0 -394
- core_framework-0.17.59/core/tasks/config.py +0 -67
- core_framework-0.17.59/core/tasks/decorators.py +0 -197
- core_framework-0.17.59/core/tasks/registry.py +0 -178
- core_framework-0.17.59/core/tasks/scheduler.py +0 -259
- core_framework-0.17.59/core/tasks/worker.py +0 -647
- core_framework-0.17.59/core/tenancy.py +0 -338
- core_framework-0.17.59/core/testing/__init__.py +0 -99
- core_framework-0.17.59/core/testing/assertions.py +0 -347
- core_framework-0.17.59/core/testing/client.py +0 -247
- core_framework-0.17.59/core/testing/database.py +0 -307
- core_framework-0.17.59/core/testing/factories.py +0 -393
- core_framework-0.17.59/core/testing/mocks.py +0 -658
- core_framework-0.17.59/core/testing/plugin.py +0 -635
- core_framework-0.17.59/core/validation.py +0 -768
- core_framework-0.17.59/core/validators.py +0 -982
- core_framework-0.17.59/core/views.py +0 -1434
- core_framework-0.17.59/docs/01-quickstart.md +0 -226
- core_framework-0.17.59/docs/02-settings.md +0 -799
- core_framework-0.17.59/docs/03-models.md +0 -157
- core_framework-0.17.59/docs/04-viewsets.md +0 -257
- core_framework-0.17.59/docs/05-auth.md +0 -361
- core_framework-0.17.59/docs/06-auth-backends.md +0 -204
- core_framework-0.17.59/docs/07-cli.md +0 -195
- core_framework-0.17.59/docs/08-permissions.md +0 -289
- core_framework-0.17.59/docs/10-fields.md +0 -349
- core_framework-0.17.59/docs/11-relations.md +0 -324
- core_framework-0.17.59/docs/12-querysets.md +0 -363
- core_framework-0.17.59/docs/13-serializers.md +0 -286
- core_framework-0.17.59/docs/14-validators.md +0 -406
- core_framework-0.17.59/docs/20-middleware.md +0 -362
- core_framework-0.17.59/docs/21-replicas.md +0 -277
- core_framework-0.17.59/docs/22-soft-delete.md +0 -248
- core_framework-0.17.59/docs/23-routing.md +0 -241
- core_framework-0.17.59/docs/24-dependencies.md +0 -307
- core_framework-0.17.59/docs/25-realtime.md +0 -521
- core_framework-0.17.59/docs/30-messaging.md +0 -496
- core_framework-0.17.59/docs/31-workers.md +0 -370
- core_framework-0.17.59/docs/32-tenancy.md +0 -357
- core_framework-0.17.59/docs/33-choices.md +0 -299
- core_framework-0.17.59/docs/34-exceptions.md +0 -311
- core_framework-0.17.59/docs/35-datetime.md +0 -332
- core_framework-0.17.59/docs/36-security.md +0 -379
- core_framework-0.17.59/docs/37-storage.md +0 -346
- core_framework-0.17.59/docs/40-admin.md +0 -442
- core_framework-0.17.59/docs/41-migrations.md +0 -164
- core_framework-0.17.59/docs/99-faq-troubleshooting.md +0 -1067
- core_framework-0.17.59/docs/CHANGELOG.md +0 -55
- core_framework-0.17.59/docs/README.md +0 -341
- core_framework-0.17.59/example/__init__.py +0 -11
- core_framework-0.17.59/example/app.py +0 -104
- core_framework-0.17.59/example/auth.py +0 -186
- core_framework-0.17.59/example/main.py +0 -65
- core_framework-0.17.59/example/models.py +0 -163
- core_framework-0.17.59/example/schemas.py +0 -247
- core_framework-0.17.59/example/settings.py +0 -35
- core_framework-0.17.59/example/views.py +0 -493
- core_framework-0.17.59/main.py +0 -24
- core_framework-0.17.59/publish.sh +0 -163
- core_framework-0.17.59/pyproject.toml +0 -142
- core_framework-0.17.59/tests/__init__.py +0 -1
- core_framework-0.17.59/tests/conftest.py +0 -40
- core_framework-0.17.59/tests/test_admin.py +0 -994
- core_framework-0.17.59/tests/test_auth_helpers.py +0 -312
- core_framework-0.17.59/tests/test_imports.py +0 -263
- core_framework-0.17.59/tests/test_issues_fixes.py +0 -441
- core_framework-0.17.59/tests/test_models.py +0 -193
- core_framework-0.17.59/tests/test_permissions.py +0 -373
- core_framework-0.17.59/tests/test_querysets.py +0 -412
- core_framework-0.17.59/tests/test_relations.py +0 -374
- core_framework-0.17.59/tests/test_routing_custom_actions.py +0 -64
- core_framework-0.17.59/tests/test_serializers.py +0 -171
- core_framework-0.17.59/tests/test_validation.py +0 -307
- core_framework-0.17.59/uv.lock +0 -2990
- {core_framework-0.17.59/libs → core_framework-1.2.0/core_framework}/__init__.py +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: API layer should contain HTTP adapters only
|
|
3
|
+
globs:
|
|
4
|
+
- core_framework/api/**/*.py
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# API Layer Conventions
|
|
8
|
+
|
|
9
|
+
The API layer is an HTTP adapter, not a business-logic layer.
|
|
10
|
+
|
|
11
|
+
## Scope
|
|
12
|
+
|
|
13
|
+
- Keep API files focused on routers, schemas, and HTTP dependencies.
|
|
14
|
+
- API code may call application services in `core_framework/application/...`.
|
|
15
|
+
- API code must not contain orchestration shared with workers.
|
|
16
|
+
|
|
17
|
+
## Router Responsibilities
|
|
18
|
+
|
|
19
|
+
- Routers own HTTP concerns: status codes, `HTTPException`, `response_model`, request parsing.
|
|
20
|
+
- When a route sets **`response_model`**, annotate the handler return type as **`typing.Any`** (OpenAPI documents the real shape; the handler may return dicts or model-adjacent values before serialization). Routes with no response body (for example **`204`** and no **`response_model`**) continue to use **`None`**.
|
|
21
|
+
- Routers call `validate_*` schema helpers before invoking application services.
|
|
22
|
+
- Routers should remain thin and delegate business decisions to application/domain layers.
|
|
23
|
+
|
|
24
|
+
## Route decorators (OpenAPI)
|
|
25
|
+
|
|
26
|
+
- Do **not** set **`summary=`** or **`description=`** on **`@router.get`**, **`post`**, **`patch`**, **`put`**, **`delete`**, or **`APIRoute`-style** registrations. Rely on paths, handler names, **`response_model`**, and Pydantic schemas. Do **not** add route **docstrings** for the same purpose either (see **`no-docstrings`**).
|
|
27
|
+
|
|
28
|
+
## Response Models
|
|
29
|
+
|
|
30
|
+
- **Do not** use bare collection types (e.g. `list[Item]`) as `response_model`. Wrap them in a named response `BaseModel` with an **`items`** field (e.g. `class FooListResponse(BaseModel): items: list[FooItem]`).
|
|
31
|
+
- This keeps responses extensible (pagination, counts, metadata) without breaking the contract.
|
|
32
|
+
|
|
33
|
+
## What to Avoid
|
|
34
|
+
|
|
35
|
+
- Do NOT implement reusable business workflows in API modules.
|
|
36
|
+
- Do NOT import domain internals directly; follow domain public API and dependencies rules.
|
|
37
|
+
- Do NOT place worker-only logic (scheduling/task orchestration) in API modules.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: docs/api.md is a minimal API index only
|
|
3
|
+
globs:
|
|
4
|
+
- docs/api.md
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# API Reference (docs/api.md)
|
|
8
|
+
|
|
9
|
+
`docs/api.md` is strictly for **quick viewing** of available APIs. It is an index, not detailed documentation.
|
|
10
|
+
|
|
11
|
+
## Format
|
|
12
|
+
|
|
13
|
+
- One line per endpoint: `METHOD path # Short description`
|
|
14
|
+
- Group by domain/section (Auth, Users, Posts, etc.)
|
|
15
|
+
- No additional content below the endpoint list
|
|
16
|
+
|
|
17
|
+
## What to INCLUDE
|
|
18
|
+
|
|
19
|
+
- Endpoint method and path
|
|
20
|
+
- Brief description in the comment (one short phrase)
|
|
21
|
+
|
|
22
|
+
## What to EXCLUDE
|
|
23
|
+
|
|
24
|
+
- Request/response body formats
|
|
25
|
+
- Header documentation
|
|
26
|
+
- Error codes
|
|
27
|
+
- Links to other docs
|
|
28
|
+
- Any prose or bullet lists
|
|
29
|
+
- Detailed behavior notes
|
|
30
|
+
|
|
31
|
+
For detailed API behavior, create or update flow documentation in `docs/flows/` instead.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Prefer 404 when a referenced resource does not exist; list endpoints may return empty collections
|
|
3
|
+
globs:
|
|
4
|
+
- core_framework/api/**/router.py
|
|
5
|
+
- core_framework/application/**/*_service.py
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# API Security: Missing resources
|
|
9
|
+
|
|
10
|
+
**Policy**: When a client references a resource by id (user, post, comment, report, etc.) and that resource does not exist, **prefer HTTP 404** with a clear not-found message unless a different contract is explicitly documented (e.g. idempotent `DELETE` returning 204 when already gone, or list endpoints returning `[]`).
|
|
11
|
+
|
|
12
|
+
This codebase **does not** optimize for hiding whether ids exist (no user-enumeration-by-status-code policy). Prefer **404** when code resolves an id to a stored row and finds nothing: e.g. **application** `UserNotFoundException` for admin user detail; **domain** `PostNotFoundException` / `CommentNotFoundException` for public post/comment fetches. Other flows (block, unblock, user report, etc.) follow domain and repository behavior without an extra application-layer identity preflight unless a route’s contract says otherwise.
|
|
13
|
+
|
|
14
|
+
**List endpoints**: Returning an empty page (`items: []`) for queries that match nothing remains acceptable.
|
|
15
|
+
|
|
16
|
+
**Self routes** (e.g. `GET /users/me/profile`): Prefer sensible defaults or 404 only if the contract says the row must exist; registration/self-heal flows may still create or patch missing rows—document any exception.
|
|
17
|
+
|
|
18
|
+
**Admin routes**: Same default—404 for missing single-resource lookups unless the endpoint is explicitly list-shaped or idempotent-delete.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: API layer must validate all fields that have database constraints
|
|
3
|
+
globs: ["core_framework/api/**/*.py"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# API Validation Rule
|
|
7
|
+
|
|
8
|
+
## Principle: Fail Fast at the API Layer
|
|
9
|
+
|
|
10
|
+
Any field that has a database constraint (CHECK, NOT NULL, UNIQUE, foreign key, etc.) MUST be validated at the API layer before reaching the database.
|
|
11
|
+
|
|
12
|
+
## Why
|
|
13
|
+
|
|
14
|
+
1. **Better User Experience**: Return clear 422 Validation Error instead of 500 Internal Server Error
|
|
15
|
+
2. **Clearer Error Messages**: Pydantic validation errors are more user-friendly than database constraint violation errors
|
|
16
|
+
3. **Fail Fast**: Catch invalid data early in the request lifecycle
|
|
17
|
+
4. **Better Monitoring**: API validation failures are easier to track than database errors
|
|
18
|
+
5. **Reduced Database Load**: Invalid requests don't reach the database
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
### ✅ Good: Validate at API Layer
|
|
23
|
+
|
|
24
|
+
Database constraint:
|
|
25
|
+
```sql
|
|
26
|
+
country_code varchar(2) not null,
|
|
27
|
+
constraint country_code_format check (country_code ~ '^[A-Z]{2}$')
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
API validation:
|
|
31
|
+
```python
|
|
32
|
+
import re
|
|
33
|
+
from typing import Annotated
|
|
34
|
+
|
|
35
|
+
from pydantic import BaseModel, Field, field_validator
|
|
36
|
+
|
|
37
|
+
# Using Field with pattern
|
|
38
|
+
class LoginRequest(BaseModel):
|
|
39
|
+
country_code: Annotated[
|
|
40
|
+
str,
|
|
41
|
+
Field(
|
|
42
|
+
default="XX",
|
|
43
|
+
pattern="^[A-Z]{2}$",
|
|
44
|
+
description="ISO 3166-1 alpha-2 country code",
|
|
45
|
+
)
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
# Or using field_validator for complex validation
|
|
49
|
+
@field_validator("country_code")
|
|
50
|
+
@classmethod
|
|
51
|
+
def validate_country_code(cls, v: str) -> str:
|
|
52
|
+
if not re.match(r"^[A-Z]{2}$", v):
|
|
53
|
+
raise ValueError("country_code must be 2 uppercase letters")
|
|
54
|
+
return v
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### ❌ Bad: No API Validation
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
class LoginRequest(BaseModel):
|
|
61
|
+
# This will cause a database constraint violation
|
|
62
|
+
country_code: str = "invalid" # No validation!
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Common Database Constraints to Validate
|
|
66
|
+
|
|
67
|
+
### 1. **CHECK Constraints**
|
|
68
|
+
- Length limits: `varchar(50)` → `Field(max_length=50)`
|
|
69
|
+
- Format patterns: `check (email ~ '^[^@]+@[^@]+$')` → `EmailStr` or `Field(pattern="...")`
|
|
70
|
+
- Value ranges: `check (age >= 0)` → `Field(ge=0)`
|
|
71
|
+
- Enum values: `check (status in ('active', 'inactive'))` → Python `Enum`
|
|
72
|
+
|
|
73
|
+
### 2. **NOT NULL Constraints**
|
|
74
|
+
- Required fields: `not null` → Don't use `| None`, or use `Field(..., required=True)`
|
|
75
|
+
|
|
76
|
+
### 3. **UNIQUE Constraints**
|
|
77
|
+
- Validate uniqueness where possible (e.g., check username availability before insert)
|
|
78
|
+
- Consider caching frequently checked unique values (like usernames)
|
|
79
|
+
|
|
80
|
+
### 4. **Foreign Key Constraints**
|
|
81
|
+
- Validate referenced entities exist before insert/update
|
|
82
|
+
- Use proper error messages: "User not found" instead of "Foreign key violation"
|
|
83
|
+
|
|
84
|
+
### 5. **Length Constraints**
|
|
85
|
+
- String lengths: `varchar(255)` → `Field(max_length=255)`
|
|
86
|
+
- Array sizes: `text[] check (array_length(...) <= 5)` → validate list length
|
|
87
|
+
|
|
88
|
+
## When NOT to Validate
|
|
89
|
+
|
|
90
|
+
- **Free-form text fields** with no constraints (e.g., `text` columns with no CHECK)
|
|
91
|
+
- **System-generated values** (e.g., timestamps, auto-increment IDs)
|
|
92
|
+
- **Internal fields** not exposed in the API
|
|
93
|
+
|
|
94
|
+
## Implementation Guidelines
|
|
95
|
+
|
|
96
|
+
1. **Review migrations** when creating API endpoints to identify all constraints
|
|
97
|
+
2. **Use Pydantic validators** for complex validation logic
|
|
98
|
+
3. **Use Field constraints** for simple validations (pattern, min/max, length)
|
|
99
|
+
4. **Add descriptive error messages** to help API consumers understand requirements
|
|
100
|
+
5. **Document validation rules** in OpenAPI schema (automatically done by FastAPI/Pydantic)
|
|
101
|
+
|
|
102
|
+
## Related Files
|
|
103
|
+
|
|
104
|
+
- Database migrations: `alembic/*/alembic/versions/*.py`
|
|
105
|
+
- API schemas: `core_framework/api/*/schemas.py`
|
|
106
|
+
- User shared schemas: `core_framework/api/users/shared/schemas.py`
|
|
107
|
+
- Auth schemas: `core_framework/api/auth/schemas.py`
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Application services must be transport-agnostic and return plain Python types
|
|
3
|
+
globs:
|
|
4
|
+
- core_framework/application/**/*_service.py
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Application Layer Service Conventions
|
|
8
|
+
|
|
9
|
+
Services in the application layer (`core_framework/application/**/*_service.py`) are used by both HTTP routers and workers. They must remain transport-agnostic.
|
|
10
|
+
|
|
11
|
+
## No HTTP Handling
|
|
12
|
+
|
|
13
|
+
Services must NOT handle HTTP concerns. They are shared by both API and worker code.
|
|
14
|
+
|
|
15
|
+
- **Do NOT** raise `HTTPException` or any FastAPI/Starlette HTTP types
|
|
16
|
+
- **Do NOT** import from `fastapi` for HTTP-specific logic (status codes, responses)
|
|
17
|
+
- **Do NOT** assume the caller is an HTTP request
|
|
18
|
+
|
|
19
|
+
**Signal "not found"** by returning `None`/`Optional` or by raising **domain** exceptions that have registered global handlers (e.g. `PostNotFoundException`, `CommentNotFoundException`)—never `HTTPException`. How callers map "not found" depends on response shape and endpoint type:
|
|
20
|
+
- **Admin routes that load a user row for a detail payload**: missing user → **404** via `UserNotFoundException` (`application.shared.exceptions`) when the service resolves that user and signals not found.
|
|
21
|
+
- **Public detail routes for posts/comments by id**: missing row → domain service raises `PostNotFoundException` / `CommentNotFoundException`; handlers map to 404.
|
|
22
|
+
- **Admin list routes**: returning an empty list when a filter matches nothing is acceptable.
|
|
23
|
+
- **Admin DELETE routes**: idempotent 204 for already-missing resources is acceptable when the contract is delete-by-id.
|
|
24
|
+
- **Public/authenticated list routes**: returning an empty list is acceptable.
|
|
25
|
+
- **Workers**: handle `None` or propagate domain exceptions according to job policy.
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
# ✅ Raise UserNotFoundException when a referenced user id has no row (after strong read when required)
|
|
29
|
+
async def retrieve_user_detail(*, user_id: UserID) -> dict[str, Any]:
|
|
30
|
+
if user_data is None:
|
|
31
|
+
raise UserNotFoundException()
|
|
32
|
+
return {"identity": ..., "moderation": ...}
|
|
33
|
+
|
|
34
|
+
# ✅ Other routes: return None when resource not found
|
|
35
|
+
async def get_some_resource(*, resource_id: str) -> dict | None:
|
|
36
|
+
if not found:
|
|
37
|
+
return None
|
|
38
|
+
return {...}
|
|
39
|
+
|
|
40
|
+
# ❌ WRONG: HTTP in service layer
|
|
41
|
+
async def retrieve_user_detail(*, user_id: UserID) -> dict[str, Any]:
|
|
42
|
+
if not found:
|
|
43
|
+
raise HTTPException(status_code=404, detail="User not found") # ❌
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## No Pydantic Models as Return Types
|
|
47
|
+
|
|
48
|
+
Services must return plain Python types—not Pydantic API/response `BaseModel` subclasses. Routers use `response_model`; FastAPI converts the return value to the Pydantic model automatically.
|
|
49
|
+
|
|
50
|
+
- **Do NOT** return Pydantic `BaseModel` subclasses from application service functions
|
|
51
|
+
- **Do** return `dict`, `list[dict]`, **domain dataclasses**, or other JSON-serializable values
|
|
52
|
+
- **Routers** declare `response_model`; FastAPI validates and serializes the service return value
|
|
53
|
+
|
|
54
|
+
### Dict vs domain dataclass
|
|
55
|
+
|
|
56
|
+
- **Domain dataclass** when a single domain type (or a homogeneous `list` of domain rows) already matches the **`response_model` field layout**. Pass it through; no hand-built dict. Examples in this repo: `retrieve_my_preferences` → `Preferences`; `retrieve_my_profile` → `Profile`; `retrieve_my_username_suggestions` → `list[UsernameSuggestion]` with items compatible with `UserReference`.
|
|
57
|
+
- **`dict[str, Any]`** (or `list[dict[…]]`) when the HTTP payload is **assembled** from several sources or **does not** map 1:1 to one domain type. Example: `retrieve_my_account` merges `UserIdentity` fields with Firebase email fields into a dict that matches `AccountResponse`.
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
# ✅ Dict: composed payload, not one domain row
|
|
61
|
+
async def retrieve_my_account(...) -> dict[str, Any]:
|
|
62
|
+
return {"username": ..., "email": ..., ...}
|
|
63
|
+
|
|
64
|
+
# ✅ Domain dataclass: same shape as response_model (FastAPI adapts)
|
|
65
|
+
async def retrieve_my_profile(*, user_id: str) -> Profile: ...
|
|
66
|
+
|
|
67
|
+
async def retrieve_my_username_suggestions(...) -> list[UsernameSuggestion]: ...
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Keyword-only arguments
|
|
71
|
+
|
|
72
|
+
- Application service functions (and private helpers in `*_service.py` modules) must use **keyword-only** parameters after a `*` separator, matching the domain layer convention.
|
|
73
|
+
- Exception: zero-argument functions (e.g. `retrieve_admin_user_ids()`).
|
|
74
|
+
|
|
75
|
+
## Validation in Schemas, Not Services
|
|
76
|
+
|
|
77
|
+
- Input validation (`validate_user_id`, `validate_report_id`, etc.) belongs in schemas
|
|
78
|
+
- Services do NOT raise `RequestValidationError` or import from `fastapi.exceptions`
|
|
79
|
+
- Routers call `validate_*` from schemas before invoking services
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Prefer typing.Final for module-level constants (not reassigned)
|
|
3
|
+
globs: core_framework/**/*.py
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Module-level constants and `Final`
|
|
8
|
+
|
|
9
|
+
When introducing or editing **module-level constants** (values that are fixed for the lifetime of the module and not meant to be reassigned):
|
|
10
|
+
|
|
11
|
+
- Annotate them with **`typing.Final`** and an explicit type, e.g. **`NAME: Final[int] = 42`** or **`_INTERNAL: Final[str] = "henry"`**.
|
|
12
|
+
- Prefer **`FrozenSet`**, **`frozenset` literals**, **`MappingProxyType`**, or immutable collections for grouped constants when mutation must be prevented — e.g. **`USER_PREFERENCE_UPDATE_COLUMNS`** in **`core_framework/domains/user/constants.py`**.
|
|
13
|
+
- **`Final`** is for **assignment** semantics (no rebinding): it does not freeze mutable object contents unless the type/container is immutable.
|
|
14
|
+
|
|
15
|
+
Do not refactor unrelated legacy constants only to add **`Final`** unless you are already touching that symbol.
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Database Triggers Rule
|
|
2
|
+
|
|
3
|
+
## Core Principle
|
|
4
|
+
|
|
5
|
+
**Database triggers MUST ONLY be used for audit/history tracking. Business logic MUST NEVER be implemented in triggers.**
|
|
6
|
+
|
|
7
|
+
## When to Use Triggers
|
|
8
|
+
|
|
9
|
+
✅ **ACCEPTABLE USE CASES:**
|
|
10
|
+
|
|
11
|
+
1. **Audit Logging**
|
|
12
|
+
|
|
13
|
+
- Tracking who changed what and when
|
|
14
|
+
- Recording old and new values for compliance
|
|
15
|
+
- Capturing actor information (who made the change)
|
|
16
|
+
|
|
17
|
+
1. **Change History**
|
|
18
|
+
|
|
19
|
+
- Building immutable audit trails
|
|
20
|
+
- Tracking modifications for regulatory compliance
|
|
21
|
+
- Recording metadata about changes
|
|
22
|
+
|
|
23
|
+
1. **Read-Only Operations**
|
|
24
|
+
|
|
25
|
+
- Operations that don't affect business outcomes
|
|
26
|
+
- Recording timestamps of state changes
|
|
27
|
+
- Logging events for analytics (non-critical)
|
|
28
|
+
|
|
29
|
+
## When NOT to Use Triggers
|
|
30
|
+
|
|
31
|
+
❌ **NEVER USE TRIGGERS FOR:**
|
|
32
|
+
|
|
33
|
+
1. **Business Logic**
|
|
34
|
+
|
|
35
|
+
- Validation rules (e.g., "users can only change username once per week")
|
|
36
|
+
- State transitions (e.g., "muted users cannot be warned")
|
|
37
|
+
- Conditional workflows (e.g., "send notification if X happens")
|
|
38
|
+
- Data transformations (e.g., "normalize phone numbers on insert")
|
|
39
|
+
|
|
40
|
+
1. **Side Effects**
|
|
41
|
+
|
|
42
|
+
- Sending notifications
|
|
43
|
+
- Calling external APIs
|
|
44
|
+
- Updating related records based on business rules
|
|
45
|
+
- Enforcing complex constraints
|
|
46
|
+
|
|
47
|
+
1. **Calculations**
|
|
48
|
+
|
|
49
|
+
- Computing derived values
|
|
50
|
+
- Aggregating data
|
|
51
|
+
- Updating summary tables
|
|
52
|
+
|
|
53
|
+
## Why This Rule Exists
|
|
54
|
+
|
|
55
|
+
1. **Testability**: Triggers are difficult to test in isolation and require database setup
|
|
56
|
+
1. **Visibility**: Logic hidden in triggers is invisible during code review
|
|
57
|
+
1. **Debugging**: Trigger failures are harder to diagnose than application errors
|
|
58
|
+
1. **Maintainability**: Changing triggers requires database migrations and coordination
|
|
59
|
+
1. **Performance**: Triggers add overhead to every write operation, affecting all callers
|
|
60
|
+
1. **Complexity**: Trigger interactions can create subtle bugs and unexpected behavior
|
|
61
|
+
|
|
62
|
+
## Examples
|
|
63
|
+
|
|
64
|
+
### ✅ Good: Audit Logging
|
|
65
|
+
|
|
66
|
+
```sql
|
|
67
|
+
-- ACCEPTABLE: Audit trail for username changes
|
|
68
|
+
create or replace function log_user_update_change()
|
|
69
|
+
returns trigger as $$
|
|
70
|
+
begin
|
|
71
|
+
if new.username is distinct from old.username then
|
|
72
|
+
insert into "user".user_change_history (
|
|
73
|
+
user_id, actor_id, entity_type, field, old_value, new_value
|
|
74
|
+
)
|
|
75
|
+
values (
|
|
76
|
+
new.user_id,
|
|
77
|
+
current_setting('app.actor_id', true),
|
|
78
|
+
'users',
|
|
79
|
+
'username',
|
|
80
|
+
old.username,
|
|
81
|
+
new.username
|
|
82
|
+
);
|
|
83
|
+
end if;
|
|
84
|
+
return new;
|
|
85
|
+
end;
|
|
86
|
+
$$ language plpgsql;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Why this is acceptable:**
|
|
90
|
+
|
|
91
|
+
- Records historical information without affecting business logic
|
|
92
|
+
- Doesn't prevent or modify the original operation
|
|
93
|
+
- Fails silently if logging fails (doesn't block the transaction)
|
|
94
|
+
- Pure audit trail - no business decisions are made
|
|
95
|
+
|
|
96
|
+
### ❌ Bad: Business Logic in Trigger
|
|
97
|
+
|
|
98
|
+
```sql
|
|
99
|
+
-- DON'T DO THIS: Username change cooldown validation
|
|
100
|
+
create or replace function enforce_username_cooldown()
|
|
101
|
+
returns trigger as $$
|
|
102
|
+
begin
|
|
103
|
+
if new.username != old.username then
|
|
104
|
+
if exists (
|
|
105
|
+
select 1 from user_change_history
|
|
106
|
+
where user_id = new.user_id
|
|
107
|
+
and field = 'username'
|
|
108
|
+
and created_at > now() - interval '7 days'
|
|
109
|
+
) then
|
|
110
|
+
raise exception 'Username can only be changed once per week';
|
|
111
|
+
end if;
|
|
112
|
+
end if;
|
|
113
|
+
return new;
|
|
114
|
+
end;
|
|
115
|
+
$$ language plpgsql;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Why this is bad:**
|
|
119
|
+
|
|
120
|
+
- Implements business rule (cooldown period)
|
|
121
|
+
- Prevents operation from completing
|
|
122
|
+
- Validation should be in service layer
|
|
123
|
+
- Can't be easily tested or modified
|
|
124
|
+
- Error message isn't localized
|
|
125
|
+
- Makes assumptions about business rules
|
|
126
|
+
|
|
127
|
+
**Correct approach:**
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
# In service layer
|
|
131
|
+
async def change_profile(
|
|
132
|
+
self,
|
|
133
|
+
*,
|
|
134
|
+
actor_id: str,
|
|
135
|
+
user_id: str,
|
|
136
|
+
validated_update_request: dict[str, Any],
|
|
137
|
+
) -> None:
|
|
138
|
+
if "username" in validated_update_request:
|
|
139
|
+
recent_changes = await self.repository.select_recent_username_changes(
|
|
140
|
+
user_id=user_id, since=datetime.now(timezone.utc) - timedelta(days=7)
|
|
141
|
+
)
|
|
142
|
+
if recent_changes:
|
|
143
|
+
raise UsernameCooldownException()
|
|
144
|
+
|
|
145
|
+
await self.repository.update_profile(
|
|
146
|
+
user_id=user_id,
|
|
147
|
+
validated_update_request=validated_update_request,
|
|
148
|
+
actor_id=actor_id,
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### ❌ Bad: Automatic State Transitions
|
|
153
|
+
|
|
154
|
+
```sql
|
|
155
|
+
-- DON'T DO THIS: Auto-approve appeals from first-time offenders
|
|
156
|
+
create or replace function auto_approve_first_offense()
|
|
157
|
+
returns trigger as $$
|
|
158
|
+
begin
|
|
159
|
+
if tg_op = 'INSERT' then
|
|
160
|
+
if (select count(*) from restriction_history where user_id = new.user_id) = 1 then
|
|
161
|
+
update moderation_appeals
|
|
162
|
+
set status = 'approved', decided_at = now()
|
|
163
|
+
where user_id = new.user_id and status = 'pending';
|
|
164
|
+
end if;
|
|
165
|
+
end if;
|
|
166
|
+
return new;
|
|
167
|
+
end;
|
|
168
|
+
$$ language plpgsql;
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Why this is bad:**
|
|
172
|
+
|
|
173
|
+
- Implements business policy (auto-approval rules)
|
|
174
|
+
- Makes decisions that should be explicit in application code
|
|
175
|
+
- Side effect (updating appeals table) is invisible to callers
|
|
176
|
+
- Business rule changes require database migration
|
|
177
|
+
- Can't be A/B tested or feature-flagged
|
|
178
|
+
|
|
179
|
+
## When in Doubt
|
|
180
|
+
|
|
181
|
+
**Ask yourself:**
|
|
182
|
+
|
|
183
|
+
1. "Does this trigger make a business decision?" → If yes, move to application layer
|
|
184
|
+
1. "Would I need to change this if business requirements change?" → If yes, move to application layer
|
|
185
|
+
1. "Does this trigger prevent or modify the operation?" → If yes, move to application layer
|
|
186
|
+
1. "Is this purely recording what happened?" → If yes, trigger is acceptable
|
|
187
|
+
|
|
188
|
+
## Enforcement
|
|
189
|
+
|
|
190
|
+
If an AI assistant (like me) suggests using a trigger for business logic, I should:
|
|
191
|
+
|
|
192
|
+
1. **Stop and remind you** of this rule
|
|
193
|
+
1. **Suggest the application-layer alternative**
|
|
194
|
+
1. **Only proceed with trigger** if you explicitly confirm it's for audit/history only
|
|
195
|
+
|
|
196
|
+
If you ask for a trigger for business logic, I should:
|
|
197
|
+
|
|
198
|
+
1. **Flag the violation** of this rule
|
|
199
|
+
1. **Explain why it's problematic**
|
|
200
|
+
1. **Propose an application-layer implementation instead**
|
|
201
|
+
|
|
202
|
+
## Related Documentation
|
|
203
|
+
|
|
204
|
+
- `docs/database-triggers.md` - **Catalog of all triggers** (update when adding new triggers)
|
|
205
|
+
- `docs/conventions.md` - Database Triggers section
|
|
206
|
+
- `docs/architecture-decisions.md` - Triggers Only for Audit/History
|
|
207
|
+
- `docs/flows/users/change_history.md` - Example of proper trigger usage
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Prevent caller-context leakage in domain layer naming
|
|
3
|
+
globs:
|
|
4
|
+
- core_framework/domains/**/*.py
|
|
5
|
+
- core_framework/domains/**/README.md
|
|
6
|
+
alwaysApply: false
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Domain Caller-Context Boundary
|
|
10
|
+
|
|
11
|
+
Domain layer names must describe domain concepts and data semantics, not who calls them.
|
|
12
|
+
|
|
13
|
+
## Do Not Encode Caller Context
|
|
14
|
+
|
|
15
|
+
- Avoid role/channel/adapter names in domain symbols: `Admin*`, `Public*`, `Authenticated*`, `Api*`, `Worker*`, `Http*`.
|
|
16
|
+
- Avoid method names like `*_for_admin`, `*_for_public`, `*_for_api`, `*_for_worker`.
|
|
17
|
+
- Avoid transport words in domain interfaces: `request`, `response`, `router`, `endpoint`, `status_code`.
|
|
18
|
+
|
|
19
|
+
## Allowed Domain Semantics
|
|
20
|
+
|
|
21
|
+
- Domain names may still use role/status/visibility words when they describe the entity or filter itself, not the caller.
|
|
22
|
+
- Examples:
|
|
23
|
+
- `UserRole.ADMIN` ✅
|
|
24
|
+
- `select_admin_user_ids` when it means "users whose role is admin" ✅
|
|
25
|
+
- `PostVisibility.PUBLIC` ✅
|
|
26
|
+
|
|
27
|
+
## Preferred Naming
|
|
28
|
+
|
|
29
|
+
- Use policy/data-shape naming:
|
|
30
|
+
- `select_posts_unfiltered` ✅
|
|
31
|
+
- `select_comments_unfiltered` ✅
|
|
32
|
+
- `PostWithMetadata` ✅
|
|
33
|
+
- Use constraint-oriented naming for variants:
|
|
34
|
+
- `update_post_status_by_id` ✅
|
|
35
|
+
- `update_post_status_by_id_and_author` ✅
|
|
36
|
+
- `set_post_status_for_author` ✅
|
|
37
|
+
- Prefer naming by data shape or business rule, not persona:
|
|
38
|
+
- `select_post_with_metadata_by_id` ✅
|
|
39
|
+
- `retrieve_user_for_detail` ✅
|
|
40
|
+
- Keep caller-specific naming in API/application layers only.
|
|
41
|
+
|
|
42
|
+
## Review Checklist
|
|
43
|
+
|
|
44
|
+
- If a domain model or method name implies caller identity, flag it as boundary leakage.
|
|
45
|
+
- Suggest neutral alternatives based on behavior, constraint, or data shape (not caller type).
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Enforce importing from domain __init__.py in API/application layers
|
|
3
|
+
globs:
|
|
4
|
+
- core_framework/api/**/*.py
|
|
5
|
+
- core_framework/application/**/*.py
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Domain Import Rules
|
|
9
|
+
|
|
10
|
+
When importing from domain layers (`core_framework/domains/*`) into the API/application layers (`core_framework/api/*`, `core_framework/application/*`), you MUST follow these rules:
|
|
11
|
+
|
|
12
|
+
## ✅ Allowed Imports
|
|
13
|
+
|
|
14
|
+
### 1. Import from domain `__init__.py` (Public API)
|
|
15
|
+
```python
|
|
16
|
+
# CORRECT: Import enums, models, exceptions from __init__.py
|
|
17
|
+
from core_framework.domains.moderation import AppealDecision, RestrictionType
|
|
18
|
+
from core_framework.domains.moderation import UserModeration, UserRestriction
|
|
19
|
+
from core_framework.domains.moderation import BaseModerationException, AppealNotFoundException
|
|
20
|
+
|
|
21
|
+
from core_framework.domains.user import UserRole, BlockedUser, Profile
|
|
22
|
+
from core_framework.domains.user import BaseUserException, UserIdConflictException
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Domain exception boundary**: Some domain exceptions (e.g. `DomainUserNotFoundException`) are internal—not exported in `__init__.py` and must not be imported or raised outside their domain. Use `None` or `Optional` return values instead; callers map missing values by endpoint policy and response shape (e.g. admin user detail → `UserNotFoundException` → 404; list responses may return empty lists; admin DELETE may return idempotent 204).
|
|
26
|
+
|
|
27
|
+
### 2. Import from `dependencies.py` (Dependency Injection)
|
|
28
|
+
```python
|
|
29
|
+
# CORRECT: Import services from dependencies
|
|
30
|
+
from core_framework.domains.moderation.dependencies import moderation_service
|
|
31
|
+
from core_framework.domains.user.dependencies import user_service
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## ❌ Prohibited Imports
|
|
35
|
+
|
|
36
|
+
### DO NOT import directly from internal domain modules
|
|
37
|
+
```python
|
|
38
|
+
# WRONG: Don't import from internal modules
|
|
39
|
+
from core_framework.domains.moderation.models import UserModeration # ❌
|
|
40
|
+
from core_framework.domains.moderation.enums import AppealDecision # ❌
|
|
41
|
+
from core_framework.domains.moderation.exceptions import AppealNotFoundException # ❌
|
|
42
|
+
from core_framework.domains.moderation.repository import ModerationRepository # ❌
|
|
43
|
+
from core_framework.domains.moderation.service import ModerationService # ❌
|
|
44
|
+
|
|
45
|
+
from core_framework.domains.user.models import BlockedUser # ❌
|
|
46
|
+
from core_framework.domains.user.enums import UserRole # ❌
|
|
47
|
+
from core_framework.domains.user.exceptions import DomainUserNotFoundException # ❌
|
|
48
|
+
from core_framework.domains.user.repository import UserRepository # ❌
|
|
49
|
+
from core_framework.domains.user.service import UserService # ❌
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Why This Rule Exists
|
|
53
|
+
|
|
54
|
+
1. **Encapsulation**: The `__init__.py` defines the public API of each domain
|
|
55
|
+
2. **Maintainability**: Internal refactoring doesn't break the API layer
|
|
56
|
+
3. **Clear boundaries**: API layer only accesses what the domain explicitly exports
|
|
57
|
+
4. **Consistency across adapters**: API and application layers follow the same domain import contract
|
|
58
|
+
5. **Dependency direction**: Prevents tight coupling to internal implementation details
|
|
59
|
+
|
|
60
|
+
## Available Domain Exports
|
|
61
|
+
|
|
62
|
+
### Moderation Domain (`from core_framework.domains.moderation import ...`)
|
|
63
|
+
- **Enums**: `RestrictionType`, `AppealDecision`, `ReportType`
|
|
64
|
+
- **Models**: `Report`, `UserModeration`, `UserRestriction`
|
|
65
|
+
- **Exceptions**: `BaseModerationException`, `AppealNotFoundException`, `AppealAlreadyDecidedException`, `SelfReportException`, `ExistingPendingAppealException`, `AppealRequirementException`
|
|
66
|
+
- **Service**: Use `moderation_service` from `dependencies.py`
|
|
67
|
+
|
|
68
|
+
### User Domain (`from core_framework.domains.user import ...`)
|
|
69
|
+
- **Enums**: `UserRole`
|
|
70
|
+
- **Models**: `CreatedUser`, `BlockedUser`, `Profile`, `UserIdentity`
|
|
71
|
+
- **Exceptions**: `BaseUserException`, `SelfBlockException`, `UserIdConflictException`, `UsernameConflictException`, `UserCreationException`
|
|
72
|
+
- **Service**: Use `user_service` from `dependencies.py`
|
|
73
|
+
- **Internal** (do not import from API layer): `DomainUserNotFoundException` — handled within domain; API uses `None` return values
|
|
74
|
+
|
|
75
|
+
## How to Fix Violations
|
|
76
|
+
|
|
77
|
+
If you need something that's not exported in `__init__.py`:
|
|
78
|
+
|
|
79
|
+
1. **Check if it should be public**: If it's needed by the API layer, add it to the domain's `__init__.py`
|
|
80
|
+
2. **Reconsider the design**: If it's internal implementation, maybe the API layer shouldn't access it directly
|
|
81
|
+
3. **Use the service**: Most domain logic should be accessed through `{domain}_service` from `dependencies.py`
|
|
82
|
+
|
|
83
|
+
Repositories (e.g. `UserRepository`) are only wired for domain services and tests. Application code never imports them, so persistence entrypoints like `UserRepository.update_*` always go through `UserService`, which applies PATCH validation.
|