trueppm-api 0.4.0b2__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.
- trueppm_api/__init__.py +14 -0
- trueppm_api/apps/access/__init__.py +0 -0
- trueppm_api/apps/access/apps.py +19 -0
- trueppm_api/apps/access/groups.py +518 -0
- trueppm_api/apps/access/management/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/create_admin.py +164 -0
- trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
- trueppm_api/apps/access/management/commands/revoke_api_tokens.py +209 -0
- trueppm_api/apps/access/migrations/0001_initial.py +61 -0
- trueppm_api/apps/access/migrations/0001_squashed_0012_programmembership_role_title.py +167 -0
- trueppm_api/apps/access/migrations/0002_alter_projectmembership_id.py +29 -0
- trueppm_api/apps/access/migrations/0003_projectmembership_soft_delete.py +24 -0
- trueppm_api/apps/access/migrations/0004_alter_projectmembership_role.py +34 -0
- trueppm_api/apps/access/migrations/0005_program_entity_and_membership.py +76 -0
- trueppm_api/apps/access/migrations/0006_role_ordinal_spacing.py +113 -0
- trueppm_api/apps/access/migrations/0007_projectmembership_joined_at_and_more.py +35 -0
- trueppm_api/apps/access/migrations/0008_projectmembership_source_group.py +25 -0
- trueppm_api/apps/access/migrations/0009_projectmembership_pm_proj_serverver_idx.py +20 -0
- trueppm_api/apps/access/migrations/0010_programmembership_joined_at_and_more.py +38 -0
- trueppm_api/apps/access/migrations/0011_alter_programmembership_unique_together_and_more.py +36 -0
- trueppm_api/apps/access/migrations/0012_programmembership_role_title.py +17 -0
- trueppm_api/apps/access/migrations/0013_userdefinedmentiongroup.py +82 -0
- trueppm_api/apps/access/migrations/0014_programmembership_progm_serverver_idx.py +19 -0
- trueppm_api/apps/access/migrations/0015_programuserdefinedmentiongroup.py +86 -0
- trueppm_api/apps/access/migrations/0016_externalstakeholder.py +67 -0
- trueppm_api/apps/access/migrations/0017_viewer_ordinal_one.py +132 -0
- trueppm_api/apps/access/migrations/0018_programmembership_sync_seq_and_more.py +52 -0
- trueppm_api/apps/access/migrations/0019_programmembership_reinstated_at_and_more.py +22 -0
- trueppm_api/apps/access/migrations/__init__.py +0 -0
- trueppm_api/apps/access/models.py +588 -0
- trueppm_api/apps/access/permissions.py +2837 -0
- trueppm_api/apps/access/serializers.py +946 -0
- trueppm_api/apps/access/services.py +667 -0
- trueppm_api/apps/access/signals.py +103 -0
- trueppm_api/apps/access/tasks.py +50 -0
- trueppm_api/apps/access/throttles.py +95 -0
- trueppm_api/apps/access/urls.py +191 -0
- trueppm_api/apps/access/views.py +1931 -0
- trueppm_api/apps/agents/__init__.py +6 -0
- trueppm_api/apps/agents/apps.py +11 -0
- trueppm_api/apps/agents/canonical.py +73 -0
- trueppm_api/apps/agents/deferred.py +108 -0
- trueppm_api/apps/agents/management/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/audit_prune.py +137 -0
- trueppm_api/apps/agents/management/commands/audit_verify.py +101 -0
- trueppm_api/apps/agents/middleware.py +44 -0
- trueppm_api/apps/agents/migrations/0001_initial.py +231 -0
- trueppm_api/apps/agents/migrations/0002_agentactioncheckpoint.py +71 -0
- trueppm_api/apps/agents/migrations/0003_agentactionrefusaldetail.py +58 -0
- trueppm_api/apps/agents/migrations/__init__.py +0 -0
- trueppm_api/apps/agents/models.py +371 -0
- trueppm_api/apps/agents/refusal.py +103 -0
- trueppm_api/apps/agents/serializers.py +85 -0
- trueppm_api/apps/agents/services.py +397 -0
- trueppm_api/apps/agents/signals.py +30 -0
- trueppm_api/apps/agents/urls.py +15 -0
- trueppm_api/apps/agents/views.py +108 -0
- trueppm_api/apps/csvimport/__init__.py +12 -0
- trueppm_api/apps/csvimport/apps.py +11 -0
- trueppm_api/apps/csvimport/mapping.py +363 -0
- trueppm_api/apps/csvimport/migrations/0001_initial.py +74 -0
- trueppm_api/apps/csvimport/migrations/0002_schedule_outline_batch_operations.py +37 -0
- trueppm_api/apps/csvimport/migrations/0003_csvimportrequest_date_order_and_more.py +26 -0
- trueppm_api/apps/csvimport/migrations/__init__.py +0 -0
- trueppm_api/apps/csvimport/models.py +111 -0
- trueppm_api/apps/csvimport/parser.py +1825 -0
- trueppm_api/apps/csvimport/services.py +48 -0
- trueppm_api/apps/csvimport/tasks.py +418 -0
- trueppm_api/apps/csvimport/template.py +37 -0
- trueppm_api/apps/csvimport/urls.py +50 -0
- trueppm_api/apps/csvimport/views.py +793 -0
- trueppm_api/apps/history/__init__.py +0 -0
- trueppm_api/apps/history/apps.py +12 -0
- trueppm_api/apps/history/changelog.py +359 -0
- trueppm_api/apps/history/diff_policy.py +177 -0
- trueppm_api/apps/history/serializers.py +104 -0
- trueppm_api/apps/history/signals.py +82 -0
- trueppm_api/apps/history/tasks.py +105 -0
- trueppm_api/apps/history/urls.py +38 -0
- trueppm_api/apps/history/views.py +443 -0
- trueppm_api/apps/idempotency/__init__.py +0 -0
- trueppm_api/apps/idempotency/apps.py +11 -0
- trueppm_api/apps/idempotency/migrations/0001_initial.py +71 -0
- trueppm_api/apps/idempotency/migrations/__init__.py +0 -0
- trueppm_api/apps/idempotency/mixins.py +226 -0
- trueppm_api/apps/idempotency/models.py +75 -0
- trueppm_api/apps/idempotency/tasks.py +61 -0
- trueppm_api/apps/integrations/__init__.py +13 -0
- trueppm_api/apps/integrations/apps.py +59 -0
- trueppm_api/apps/integrations/connections.py +806 -0
- trueppm_api/apps/integrations/encryption.py +92 -0
- trueppm_api/apps/integrations/external_sources.py +837 -0
- trueppm_api/apps/integrations/git_automation_services.py +231 -0
- trueppm_api/apps/integrations/git_webhook_auth.py +243 -0
- trueppm_api/apps/integrations/http.py +403 -0
- trueppm_api/apps/integrations/me_work.py +218 -0
- trueppm_api/apps/integrations/migrations/0001_initial.py +64 -0
- trueppm_api/apps/integrations/migrations/0001_squashed_0006_tasklink_description_tasklink_preview_type_and_more.py +187 -0
- trueppm_api/apps/integrations/migrations/0002_tasklink.py +67 -0
- trueppm_api/apps/integrations/migrations/0003_tasklink_tasklink_serverver_idx.py +17 -0
- trueppm_api/apps/integrations/migrations/0004_tasklink_custom_title_tasklink_labels.py +25 -0
- trueppm_api/apps/integrations/migrations/0005_boardautomation.py +56 -0
- trueppm_api/apps/integrations/migrations/0006_tasklink_description_tasklink_preview_type_and_more.py +40 -0
- trueppm_api/apps/integrations/migrations/0007_tasklink_created_at.py +23 -0
- trueppm_api/apps/integrations/migrations/0008_integrationcredential_config_externalworkitem.py +66 -0
- trueppm_api/apps/integrations/migrations/0009_externalsyncrequest_and_more.py +96 -0
- trueppm_api/apps/integrations/migrations/0010_externalworkitem_due_date.py +17 -0
- trueppm_api/apps/integrations/migrations/0011_tasklink_sync_seq_tasklink_tasklink_syncseq_idx.py +22 -0
- trueppm_api/apps/integrations/migrations/0012_boardautomation_last_delivery_at_and_more.py +42 -0
- trueppm_api/apps/integrations/migrations/0013_externalsyncrequest_attempt_count_and_more.py +26 -0
- trueppm_api/apps/integrations/migrations/__init__.py +0 -0
- trueppm_api/apps/integrations/models.py +622 -0
- trueppm_api/apps/integrations/notification_channels.py +57 -0
- trueppm_api/apps/integrations/opengraph.py +229 -0
- trueppm_api/apps/integrations/outgoing.py +201 -0
- trueppm_api/apps/integrations/providers.py +519 -0
- trueppm_api/apps/integrations/registry.py +345 -0
- trueppm_api/apps/integrations/serializers.py +453 -0
- trueppm_api/apps/integrations/services.py +171 -0
- trueppm_api/apps/integrations/tasks.py +731 -0
- trueppm_api/apps/integrations/throttles.py +165 -0
- trueppm_api/apps/integrations/urls.py +77 -0
- trueppm_api/apps/integrations/views.py +1106 -0
- trueppm_api/apps/jiraimport/__init__.py +8 -0
- trueppm_api/apps/jiraimport/apps.py +11 -0
- trueppm_api/apps/jiraimport/migrations/0001_initial.py +72 -0
- trueppm_api/apps/jiraimport/migrations/__init__.py +0 -0
- trueppm_api/apps/jiraimport/models.py +70 -0
- trueppm_api/apps/jiraimport/parser.py +350 -0
- trueppm_api/apps/jiraimport/services.py +46 -0
- trueppm_api/apps/jiraimport/tasks.py +277 -0
- trueppm_api/apps/jiraimport/urls.py +15 -0
- trueppm_api/apps/jiraimport/views.py +153 -0
- trueppm_api/apps/msproject/__init__.py +0 -0
- trueppm_api/apps/msproject/apps.py +11 -0
- trueppm_api/apps/msproject/dataclasses.py +153 -0
- trueppm_api/apps/msproject/exporter.py +500 -0
- trueppm_api/apps/msproject/extended_attributes.py +77 -0
- trueppm_api/apps/msproject/importer.py +1095 -0
- trueppm_api/apps/msproject/migrations/0001_import_request.py +77 -0
- trueppm_api/apps/msproject/migrations/0001_squashed_0002_importrequest_creates_project.py +80 -0
- trueppm_api/apps/msproject/migrations/0002_importrequest_creates_project.py +15 -0
- trueppm_api/apps/msproject/migrations/__init__.py +0 -0
- trueppm_api/apps/msproject/models.py +81 -0
- trueppm_api/apps/msproject/parser.py +1231 -0
- trueppm_api/apps/msproject/serializers.py +136 -0
- trueppm_api/apps/msproject/services.py +58 -0
- trueppm_api/apps/msproject/tasks.py +436 -0
- trueppm_api/apps/msproject/urls.py +39 -0
- trueppm_api/apps/msproject/views.py +531 -0
- trueppm_api/apps/notifications/__init__.py +7 -0
- trueppm_api/apps/notifications/apps.py +15 -0
- trueppm_api/apps/notifications/backfill.py +79 -0
- trueppm_api/apps/notifications/categories.py +112 -0
- trueppm_api/apps/notifications/delivery_limits.py +258 -0
- trueppm_api/apps/notifications/digests.py +447 -0
- trueppm_api/apps/notifications/email_backend.py +330 -0
- trueppm_api/apps/notifications/email_health.py +128 -0
- trueppm_api/apps/notifications/migrations/0001_initial.py +207 -0
- trueppm_api/apps/notifications/migrations/0001_squashed_0006_notification_task.py +294 -0
- trueppm_api/apps/notifications/migrations/0002_project_notification_preference.py +67 -0
- trueppm_api/apps/notifications/migrations/0003_projectnotificationpreference_paused.py +17 -0
- trueppm_api/apps/notifications/migrations/0004_clean_unknown_matrix_keys.py +30 -0
- trueppm_api/apps/notifications/migrations/0005_notification_body_notification_event_type_and_more.py +27 -0
- trueppm_api/apps/notifications/migrations/0006_notification_task.py +25 -0
- trueppm_api/apps/notifications/migrations/0007_workspaceemailsettings.py +80 -0
- trueppm_api/apps/notifications/migrations/0008_notification_snoozed_until.py +17 -0
- trueppm_api/apps/notifications/migrations/0009_usernotificationsettings.py +43 -0
- trueppm_api/apps/notifications/migrations/0010_projectnotificationpreference_schema_version.py +20 -0
- trueppm_api/apps/notifications/migrations/0011_usernotificationsettings_digest_hour_and_more.py +96 -0
- trueppm_api/apps/notifications/migrations/__init__.py +0 -0
- trueppm_api/apps/notifications/models.py +1197 -0
- trueppm_api/apps/notifications/receivers.py +155 -0
- trueppm_api/apps/notifications/schema_migrations.py +56 -0
- trueppm_api/apps/notifications/serializers.py +961 -0
- trueppm_api/apps/notifications/services.py +1451 -0
- trueppm_api/apps/notifications/tasks.py +711 -0
- trueppm_api/apps/notifications/throttles.py +98 -0
- trueppm_api/apps/notifications/urls.py +97 -0
- trueppm_api/apps/notifications/views.py +904 -0
- trueppm_api/apps/observability/__init__.py +0 -0
- trueppm_api/apps/observability/apps.py +31 -0
- trueppm_api/apps/observability/logging.py +213 -0
- trueppm_api/apps/observability/migrations/0001_initial.py +35 -0
- trueppm_api/apps/observability/migrations/0001_squashed_0003_alter_purgerun_trigger.py +154 -0
- trueppm_api/apps/observability/migrations/0002_purgerun_retentionpolicy_retentionschedule.py +131 -0
- trueppm_api/apps/observability/migrations/0003_alter_purgerun_trigger.py +21 -0
- trueppm_api/apps/observability/migrations/0004_alter_retentionpolicy_key.py +28 -0
- trueppm_api/apps/observability/migrations/__init__.py +0 -0
- trueppm_api/apps/observability/models.py +141 -0
- trueppm_api/apps/observability/otel/__init__.py +45 -0
- trueppm_api/apps/observability/otel/attributes.py +247 -0
- trueppm_api/apps/observability/otel/export_health.py +533 -0
- trueppm_api/apps/observability/otel/instrumentation.py +351 -0
- trueppm_api/apps/observability/otel/metrics.py +551 -0
- trueppm_api/apps/observability/otel/provider.py +511 -0
- trueppm_api/apps/observability/otel/request_attributes.py +204 -0
- trueppm_api/apps/observability/purge_registry.py +124 -0
- trueppm_api/apps/observability/retention.py +139 -0
- trueppm_api/apps/observability/selectors.py +969 -0
- trueppm_api/apps/observability/serializers.py +142 -0
- trueppm_api/apps/observability/services.py +407 -0
- trueppm_api/apps/observability/tasks.py +266 -0
- trueppm_api/apps/observability/urls.py +27 -0
- trueppm_api/apps/observability/views.py +523 -0
- trueppm_api/apps/profiles/__init__.py +0 -0
- trueppm_api/apps/profiles/apps.py +15 -0
- trueppm_api/apps/profiles/constants.py +54 -0
- trueppm_api/apps/profiles/migrations/0001_initial.py +55 -0
- trueppm_api/apps/profiles/migrations/0001_squashed_0004_userprofile_role_context.py +125 -0
- trueppm_api/apps/profiles/migrations/0002_userprofile_hidden_views.py +17 -0
- trueppm_api/apps/profiles/migrations/0003_projectvisit.py +63 -0
- trueppm_api/apps/profiles/migrations/0004_userprofile_role_context.py +26 -0
- trueppm_api/apps/profiles/migrations/0005_userprofile_schedule_in_deliver.py +20 -0
- trueppm_api/apps/profiles/migrations/0006_userprofile_date_format_userprofile_timezone.py +36 -0
- trueppm_api/apps/profiles/migrations/0007_userpin.py +91 -0
- trueppm_api/apps/profiles/migrations/0008_remove_userprofile_schedule_in_deliver.py +71 -0
- trueppm_api/apps/profiles/migrations/0009_auth_user_email_upper_idx.py +81 -0
- trueppm_api/apps/profiles/migrations/__init__.py +0 -0
- trueppm_api/apps/profiles/models.py +276 -0
- trueppm_api/apps/profiles/serializers.py +199 -0
- trueppm_api/apps/profiles/services.py +466 -0
- trueppm_api/apps/profiles/views.py +124 -0
- trueppm_api/apps/projects/__init__.py +0 -0
- trueppm_api/apps/projects/actual_date_rules.py +163 -0
- trueppm_api/apps/projects/amend.py +133 -0
- trueppm_api/apps/projects/apps.py +38 -0
- trueppm_api/apps/projects/asset_feed.py +459 -0
- trueppm_api/apps/projects/asset_views.py +317 -0
- trueppm_api/apps/projects/attachment_policy.py +278 -0
- trueppm_api/apps/projects/authentication.py +449 -0
- trueppm_api/apps/projects/backfill.py +287 -0
- trueppm_api/apps/projects/backlog_services.py +324 -0
- trueppm_api/apps/projects/backlog_views.py +302 -0
- trueppm_api/apps/projects/batch_operation_views.py +264 -0
- trueppm_api/apps/projects/blocker_services.py +510 -0
- trueppm_api/apps/projects/board_activity.py +462 -0
- trueppm_api/apps/projects/board_activity_views.py +186 -0
- trueppm_api/apps/projects/board_lanes.py +134 -0
- trueppm_api/apps/projects/bulk_settings.py +194 -0
- trueppm_api/apps/projects/bundled_templates.py +215 -0
- trueppm_api/apps/projects/calendar_settings.py +180 -0
- trueppm_api/apps/projects/ceremony_views.py +244 -0
- trueppm_api/apps/projects/commit_moment.py +204 -0
- trueppm_api/apps/projects/config_notice.py +1325 -0
- trueppm_api/apps/projects/custom_field_values.py +266 -0
- trueppm_api/apps/projects/decisions_services.py +93 -0
- trueppm_api/apps/projects/decisions_views.py +94 -0
- trueppm_api/apps/projects/estimation_scale.py +126 -0
- trueppm_api/apps/projects/export_bundle.py +353 -0
- trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4256 -0
- trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2000 -0
- trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1825 -0
- trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1905 -0
- trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1409 -0
- trueppm_api/apps/projects/guardrail_policy_source.py +269 -0
- trueppm_api/apps/projects/inbound_sync.py +555 -0
- trueppm_api/apps/projects/iteration_label.py +85 -0
- trueppm_api/apps/projects/lifecycle.py +191 -0
- trueppm_api/apps/projects/management/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/backfill_in_progress_status.py +92 -0
- trueppm_api/apps/projects/management/commands/create_demo_share_link.py +368 -0
- trueppm_api/apps/projects/management/commands/export_program.py +50 -0
- trueppm_api/apps/projects/management/commands/import_seed.py +165 -0
- trueppm_api/apps/projects/management/commands/load_sample_project.py +247 -0
- trueppm_api/apps/projects/management/commands/seed_capacity.py +405 -0
- trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +178 -0
- trueppm_api/apps/projects/management/commands/shift_sample_dates.py +120 -0
- trueppm_api/apps/projects/mcp_settings.py +305 -0
- trueppm_api/apps/projects/methodology.py +178 -0
- trueppm_api/apps/projects/migrations/0001_initial.py +208 -0
- trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5595 -0
- trueppm_api/apps/projects/migrations/0002_alter_uuid_pk_serialize_false.py +57 -0
- trueppm_api/apps/projects/migrations/0003_rename_task_project_index.py +25 -0
- trueppm_api/apps/projects/migrations/0004_soft_delete_and_dependency_versioned.py +70 -0
- trueppm_api/apps/projects/migrations/0005_task_assignee.py +33 -0
- trueppm_api/apps/projects/migrations/0006_task_utilization_index.py +31 -0
- trueppm_api/apps/projects/migrations/0007_task_is_milestone.py +28 -0
- trueppm_api/apps/projects/migrations/0008_baseline.py +119 -0
- trueppm_api/apps/projects/migrations/0009_historicaldependency_historicalproject_and_more.py +210 -0
- trueppm_api/apps/projects/migrations/0010_risk.py +229 -0
- trueppm_api/apps/projects/migrations/0011_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0012_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0013_task_planned_start.py +24 -0
- trueppm_api/apps/projects/migrations/0014_short_hex_ids.py +74 -0
- trueppm_api/apps/projects/migrations/0015_backfill_short_ids.py +48 -0
- trueppm_api/apps/projects/migrations/0016_task_actual_dates.py +42 -0
- trueppm_api/apps/projects/migrations/0017_boardcolumnconfig.py +43 -0
- trueppm_api/apps/projects/migrations/0018_estimation_governance.py +62 -0
- trueppm_api/apps/projects/migrations/0019_backfill_wbs_paths.py +28 -0
- trueppm_api/apps/projects/migrations/0020_task_status_5col.py +65 -0
- trueppm_api/apps/projects/migrations/0021_boardsavedview.py +64 -0
- trueppm_api/apps/projects/migrations/0022_task_status_changed_at_priority_rank.py +35 -0
- trueppm_api/apps/projects/migrations/0023_risk_framework_fields.py +96 -0
- trueppm_api/apps/projects/migrations/0024_riskcomment.py +52 -0
- trueppm_api/apps/projects/migrations/0025_sprint.py +352 -0
- trueppm_api/apps/projects/migrations/0026_project_methodology.py +30 -0
- trueppm_api/apps/projects/migrations/0027_sprint_retro.py +89 -0
- trueppm_api/apps/projects/migrations/0028_normalize_notes_field.py +42 -0
- trueppm_api/apps/projects/migrations/0029_complete_implies_full_progress.py +38 -0
- trueppm_api/apps/projects/migrations/0030_review_implies_full_progress.py +37 -0
- trueppm_api/apps/projects/migrations/0031_task_remaining_points.py +22 -0
- trueppm_api/apps/projects/migrations/0032_task_is_subtask_and_sprint_scope_change.py +90 -0
- trueppm_api/apps/projects/migrations/0033_task_my_work_index.py +38 -0
- trueppm_api/apps/projects/migrations/0034_inbound_task_sync.py +313 -0
- trueppm_api/apps/projects/migrations/0035_retro_versioned_and_suggested_assignee.py +173 -0
- trueppm_api/apps/projects/migrations/0036_program_entity_and_membership.py +157 -0
- trueppm_api/apps/projects/migrations/0037_sprint_capacity_points.py +20 -0
- trueppm_api/apps/projects/migrations/0038_taskcomment_commentreaction_commentacknowledgement_and_more.py +233 -0
- trueppm_api/apps/projects/migrations/0039_apitoken_and_more.py +88 -0
- trueppm_api/apps/projects/migrations/0040_program_general_fields.py +119 -0
- trueppm_api/apps/projects/migrations/0041_ceremony_template_and_phase_gate_config.py +210 -0
- trueppm_api/apps/projects/migrations/0041_project_general_fields.py +106 -0
- trueppm_api/apps/projects/migrations/0042_merge_20260522_0410.py +12 -0
- trueppm_api/apps/projects/migrations/0042_program_rollup_config.py +95 -0
- trueppm_api/apps/projects/migrations/0042_workflow_color_and_custom_fields.py +94 -0
- trueppm_api/apps/projects/migrations/0043_merge_20260522_1118.py +10 -0
- trueppm_api/apps/projects/migrations/0044_merge_20260522_rollup.py +10 -0
- trueppm_api/apps/projects/migrations/0044_project_archive_program_close.py +119 -0
- trueppm_api/apps/projects/migrations/0045_program_risk_policy.py +78 -0
- trueppm_api/apps/projects/migrations/0046_merge_20260523_2248.py +12 -0
- trueppm_api/apps/projects/migrations/0047_apitokenauditentry_program_and_more.py +55 -0
- trueppm_api/apps/projects/migrations/0048_historicalprogram_color_program_color.py +32 -0
- trueppm_api/apps/projects/migrations/0049_backlog_item.py +112 -0
- trueppm_api/apps/projects/migrations/0050_backlog_item_trgm_search.py +26 -0
- trueppm_api/apps/projects/migrations/0051_alter_calendar_working_days_alter_dependency_lag_and_more.py +144 -0
- trueppm_api/apps/projects/migrations/0052_recurring_tasks.py +278 -0
- trueppm_api/apps/projects/migrations/0053_dependency_dep_pred_serverver_idx_and_more.py +56 -0
- trueppm_api/apps/projects/migrations/0054_sprintscopechange_goal_impact_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0055_historicaltask_sprint_pending_and_more.py +51 -0
- trueppm_api/apps/projects/migrations/0056_historicalproject_prioritization_model_and_more.py +276 -0
- trueppm_api/apps/projects/migrations/0057_historicalsprint_binding_committed_snapshot_and_more.py +58 -0
- trueppm_api/apps/projects/migrations/0058_historicaltask_delivery_mode_and_more.py +72 -0
- trueppm_api/apps/projects/migrations/0059_forecastsnapshot.py +80 -0
- trueppm_api/apps/projects/migrations/0060_historicalprojectsignalprivacypolicy_and_more.py +90 -0
- trueppm_api/apps/projects/migrations/0061_baselinetask_story_points.py +17 -0
- trueppm_api/apps/projects/migrations/0062_project_is_sample.py +22 -0
- trueppm_api/apps/projects/migrations/0063_historicalsprint_wip_limit_sprint_wip_limit.py +22 -0
- trueppm_api/apps/projects/migrations/0064_sprinttaskoutcome.py +97 -0
- trueppm_api/apps/projects/migrations/0065_historicalsprint_goal_outcome_sprint_goal_outcome.py +32 -0
- trueppm_api/apps/projects/migrations/0066_historicalproject_lead_project_lead.py +38 -0
- trueppm_api/apps/projects/migrations/0067_sprint_exclude_from_velocity.py +22 -0
- trueppm_api/apps/projects/migrations/0068_project_recalculated_at.py +23 -0
- trueppm_api/apps/projects/migrations/0069_iteration_label.py +22 -0
- trueppm_api/apps/projects/migrations/0070_historicalprogram_iteration_label_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0071_backfill_iteration_label_inherit.py +35 -0
- trueppm_api/apps/projects/migrations/0072_pulseresponse_retroboarditem.py +129 -0
- trueppm_api/apps/projects/migrations/0073_sprinttaskoutcome_demo_ready.py +17 -0
- trueppm_api/apps/projects/migrations/0074_historicaltask_blocked_reason_task_blocked_reason.py +30 -0
- trueppm_api/apps/projects/migrations/0075_risk_decimal_short_id.py +42 -0
- trueppm_api/apps/projects/migrations/0076_sprinttaskoutcome_review_polish.py +45 -0
- trueppm_api/apps/projects/migrations/0077_task_blocker_fields.py +111 -0
- trueppm_api/apps/projects/migrations/0078_alter_risktask_unique_together_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0079_historicalproject_status_date_project_status_date.py +22 -0
- trueppm_api/apps/projects/migrations/0080_alter_historicaltask_type_alter_task_type.py +46 -0
- trueppm_api/apps/projects/migrations/0081_historicalprogram_allow_guests_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0082_project_last_sync_version.py +90 -0
- trueppm_api/apps/projects/migrations/0083_historicalprogram_mc_history_attribution_audience_and_more.py +108 -0
- trueppm_api/apps/projects/migrations/0084_tasknote.py +71 -0
- trueppm_api/apps/projects/migrations/0085_sprint_ix_sprint_velocity.py +33 -0
- trueppm_api/apps/projects/migrations/0086_board_card_search_trgm.py +33 -0
- trueppm_api/apps/projects/migrations/0087_historicalprogram_task_duration_change_percent_policy_and_more.py +155 -0
- trueppm_api/apps/projects/migrations/0088_historicalprogram_allowed_attachment_types_and_more.py +61 -0
- trueppm_api/apps/projects/migrations/0089_signalceilingraiseproposal_signalceilingraisevote_and_more.py +157 -0
- trueppm_api/apps/projects/migrations/0090_historicaltask_proj_histdate_index.py +44 -0
- trueppm_api/apps/projects/migrations/0091_project_board_cadence.py +39 -0
- trueppm_api/apps/projects/migrations/0092_program_target_date.py +22 -0
- trueppm_api/apps/projects/migrations/0093_cross_project_dependency_consent.py +58 -0
- trueppm_api/apps/projects/migrations/0094_project_decisions_policy.py +90 -0
- trueppm_api/apps/projects/migrations/0095_poker_session_vote.py +127 -0
- trueppm_api/apps/projects/migrations/0095_squashed_0097_release_0_3.py +225 -0
- trueppm_api/apps/projects/migrations/0096_crossprojectslipconflict.py +91 -0
- trueppm_api/apps/projects/migrations/0097_backlogitem_backlogitem_tags_gin_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0098_historicalproject_show_baselines_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0099_apitoken_scopes.py +48 -0
- trueppm_api/apps/projects/migrations/0100_task_dep_serverver_idx_concurrent.py +61 -0
- trueppm_api/apps/projects/migrations/0101_dependency_deleted_at_task_deleted_at.py +22 -0
- trueppm_api/apps/projects/migrations/0102_project_deleted_at_project_proj_isdel_deletedat_idx.py +25 -0
- trueppm_api/apps/projects/migrations/0103_taskdurationchangeevent_task_dur_evt_sprint_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0104_historicalproject_stale_task_threshold_days_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0104_trash_restore_1113.py +38 -0
- trueppm_api/apps/projects/migrations/0105_historical_changelog_indexes.py +62 -0
- trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -0
- trueppm_api/apps/projects/migrations/0107_task_activity_event.py +70 -0
- trueppm_api/apps/projects/migrations/0108_remove_apitoken_api_token_scope_xor_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0109_projectexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0110_sharelink.py +123 -0
- trueppm_api/apps/projects/migrations/0111_projectcalendarlayer.py +67 -0
- trueppm_api/apps/projects/migrations/0112_sharelink_expires_at_alter_sharelink_content_kind.py +31 -0
- trueppm_api/apps/projects/migrations/0113_alter_historicaltask_percent_complete_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0114_historicalproject_default_member_role_and_more.py +28 -0
- trueppm_api/apps/projects/migrations/0115_historicalproject_end_date_shift_threshold_days_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0116_alter_taskactivityevent_event_type.py +28 -0
- trueppm_api/apps/projects/migrations/0117_label_tasklabel_label_tasks_and_more.py +126 -0
- trueppm_api/apps/projects/migrations/0118_programexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0119_historicalprogram_calendar_program_calendar.py +36 -0
- trueppm_api/apps/projects/migrations/0120_alter_backlogitem_item_type.py +29 -0
- trueppm_api/apps/projects/migrations/0121_task_three_point_estimate_ordered.py +64 -0
- trueppm_api/apps/projects/migrations/0122_task_relation.py +173 -0
- trueppm_api/apps/projects/migrations/0123_remove_historicalproject_agile_features_and_more.py +20 -0
- trueppm_api/apps/projects/migrations/0124_dependency_is_driving.py +17 -0
- trueppm_api/apps/projects/migrations/0125_historicalprogram_estimation_scale_and_more.py +68 -0
- trueppm_api/apps/projects/migrations/0126_projectcustomfield_show_on_card_taskcustomfieldvalue.py +80 -0
- trueppm_api/apps/projects/migrations/0127_historicalprogram_mcp_enabled_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0128_acceptancecriterion_sync_seq_apitoken_sync_seq_and_more.py +192 -0
- trueppm_api/apps/projects/migrations/0129_sharelink_show_milestone_dates.py +20 -0
- trueppm_api/apps/projects/migrations/0130_programimportjob.py +85 -0
- trueppm_api/apps/projects/migrations/0131_historicalprogram_sprint_picker_ready_only_default_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0132_task_scheduled_start.py +17 -0
- trueppm_api/apps/projects/migrations/0133_project_status_date_floor_armed_at.py +17 -0
- trueppm_api/apps/projects/migrations/0134_task_edited_at_task_seeded_at_task_source_id_and_more.py +65 -0
- trueppm_api/apps/projects/migrations/0135_projecttemplate_templateapplication_and_more.py +175 -0
- trueppm_api/apps/projects/migrations/0136_schedule_outline_batch_operations.py +118 -0
- trueppm_api/apps/projects/migrations/0137_collapse_dual_scope_api_tokens.py +54 -0
- trueppm_api/apps/projects/migrations/0138_apitokenauditentry_api_token_audit_owner_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0139_historicaltask_duration_unit_task_duration_unit.py +32 -0
- trueppm_api/apps/projects/migrations/0140_historicaltask_auto_container_and_more.py +113 -0
- trueppm_api/apps/projects/migrations/0141_historicalproject_draft_started_at_and_more.py +44 -0
- trueppm_api/apps/projects/migrations/0142_baseline_calendar_hours_per_day_and_more.py +31 -0
- trueppm_api/apps/projects/migrations/0143_sprintcloserequest_failure_reason_and_more.py +42 -0
- trueppm_api/apps/projects/migrations/0144_projecttemplate_source_project_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0145_seed_bundled_templates.py +65 -0
- trueppm_api/apps/projects/migrations/0146_historicaltask_board_lane_task_board_lane.py +32 -0
- trueppm_api/apps/projects/migrations/0147_structuraloperation.py +128 -0
- trueppm_api/apps/projects/migrations/0148_task_unique_task_wbs_path_per_project_live.py +76 -0
- trueppm_api/apps/projects/migrations/0149_drop_workshops_tables.py +42 -0
- trueppm_api/apps/projects/migrations/0150_clear_uncommitted_cpm_output.py +130 -0
- trueppm_api/apps/projects/migrations/0151_config_notice_request.py +70 -0
- trueppm_api/apps/projects/migrations/0152_aggregate_task_wbs_name_idx.py +20 -0
- trueppm_api/apps/projects/migrations/0153_program_sample_anchor_date.py +22 -0
- trueppm_api/apps/projects/migrations/__init__.py +0 -0
- trueppm_api/apps/projects/models.py +8946 -0
- trueppm_api/apps/projects/poker_services.py +154 -0
- trueppm_api/apps/projects/poker_views.py +398 -0
- trueppm_api/apps/projects/product_backlog_services.py +450 -0
- trueppm_api/apps/projects/program_label_services.py +94 -0
- trueppm_api/apps/projects/program_label_views.py +189 -0
- trueppm_api/apps/projects/program_rollup.py +575 -0
- trueppm_api/apps/projects/program_schedule.py +745 -0
- trueppm_api/apps/projects/program_views.py +3220 -0
- trueppm_api/apps/projects/project_templates.py +422 -0
- trueppm_api/apps/projects/receivers.py +234 -0
- trueppm_api/apps/projects/refusal_codes.py +140 -0
- trueppm_api/apps/projects/reorder_services.py +145 -0
- trueppm_api/apps/projects/restructure_hooks.py +54 -0
- trueppm_api/apps/projects/retro_board_services.py +368 -0
- trueppm_api/apps/projects/retro_services.py +285 -0
- trueppm_api/apps/projects/risk_import.py +396 -0
- trueppm_api/apps/projects/row_identity.py +113 -0
- trueppm_api/apps/projects/schema_migrations.py +228 -0
- trueppm_api/apps/projects/schemas/seed_v1.json +493 -0
- trueppm_api/apps/projects/schemas/seed_v2.json +892 -0
- trueppm_api/apps/projects/seed/__init__.py +61 -0
- trueppm_api/apps/projects/seed/exporter.py +1644 -0
- trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
- trueppm_api/apps/projects/seed/importer.py +2390 -0
- trueppm_api/apps/projects/seed/reanchor.py +551 -0
- trueppm_api/apps/projects/seed/reldates.py +126 -0
- trueppm_api/apps/projects/seed/replace.py +126 -0
- trueppm_api/apps/projects/seed/replay.py +1691 -0
- trueppm_api/apps/projects/seed/replay_ctx.py +41 -0
- trueppm_api/apps/projects/seed/samples.py +427 -0
- trueppm_api/apps/projects/seed/validation.py +1149 -0
- trueppm_api/apps/projects/serializers.py +12310 -0
- trueppm_api/apps/projects/services.py +6966 -0
- trueppm_api/apps/projects/share_serializers.py +132 -0
- trueppm_api/apps/projects/share_services.py +325 -0
- trueppm_api/apps/projects/share_views.py +374 -0
- trueppm_api/apps/projects/sharing_settings.py +187 -0
- trueppm_api/apps/projects/signal_privacy_services.py +909 -0
- trueppm_api/apps/projects/signal_privacy_views.py +476 -0
- trueppm_api/apps/projects/signals.py +132 -0
- trueppm_api/apps/projects/slip_conflict.py +266 -0
- trueppm_api/apps/projects/sprint_cadence.py +453 -0
- trueppm_api/apps/projects/sprint_picker_settings.py +132 -0
- trueppm_api/apps/projects/standup.py +304 -0
- trueppm_api/apps/projects/standup_views.py +99 -0
- trueppm_api/apps/projects/structural_operation_services.py +727 -0
- trueppm_api/apps/projects/structural_operation_views.py +311 -0
- trueppm_api/apps/projects/surface_visibility.py +126 -0
- trueppm_api/apps/projects/task_batch_services.py +379 -0
- trueppm_api/apps/projects/task_bulk.py +1152 -0
- trueppm_api/apps/projects/task_classification.py +561 -0
- trueppm_api/apps/projects/task_classification_defaults.py +95 -0
- trueppm_api/apps/projects/task_duration_settings.py +142 -0
- trueppm_api/apps/projects/task_grouping.py +754 -0
- trueppm_api/apps/projects/tasks.py +1917 -0
- trueppm_api/apps/projects/template_divergence.py +168 -0
- trueppm_api/apps/projects/template_services.py +160 -0
- trueppm_api/apps/projects/template_tasks.py +258 -0
- trueppm_api/apps/projects/template_views.py +799 -0
- trueppm_api/apps/projects/throttles.py +311 -0
- trueppm_api/apps/projects/urls.py +1011 -0
- trueppm_api/apps/projects/utilization.py +885 -0
- trueppm_api/apps/projects/views.py +20572 -0
- trueppm_api/apps/projects/wbs_paths.py +119 -0
- trueppm_api/apps/resources/__init__.py +0 -0
- trueppm_api/apps/resources/apps.py +8 -0
- trueppm_api/apps/resources/capacity.py +121 -0
- trueppm_api/apps/resources/migrations/0001_initial.py +68 -0
- trueppm_api/apps/resources/migrations/0001_squashed_0013_alter_projectresource_unique_together_and_more.py +281 -0
- trueppm_api/apps/resources/migrations/0002_alter_uuid_pk_serialize_false.py +36 -0
- trueppm_api/apps/resources/migrations/0003_resource_soft_delete.py +30 -0
- trueppm_api/apps/resources/migrations/0004_add_taskresource_resource_index.py +25 -0
- trueppm_api/apps/resources/migrations/0005_resource_job_role.py +20 -0
- trueppm_api/apps/resources/migrations/0006_skill.py +34 -0
- trueppm_api/apps/resources/migrations/0007_resource_skill.py +63 -0
- trueppm_api/apps/resources/migrations/0008_project_resource.py +63 -0
- trueppm_api/apps/resources/migrations/0009_task_skill_requirement.py +60 -0
- trueppm_api/apps/resources/migrations/0010_alter_resourceskill_options_and_more.py +20 -0
- trueppm_api/apps/resources/migrations/0011_alter_projectresource_notes.py +17 -0
- trueppm_api/apps/resources/migrations/0012_add_resource_user_fk.py +26 -0
- trueppm_api/apps/resources/migrations/0013_alter_projectresource_unique_together_and_more.py +53 -0
- trueppm_api/apps/resources/migrations/0014_projectresource_sync_seq_resource_sync_seq_and_more.py +37 -0
- trueppm_api/apps/resources/migrations/0015_projectresource_deactivated_with_resource.py +17 -0
- trueppm_api/apps/resources/migrations/__init__.py +0 -0
- trueppm_api/apps/resources/models.py +298 -0
- trueppm_api/apps/resources/serializers.py +426 -0
- trueppm_api/apps/resources/services.py +465 -0
- trueppm_api/apps/resources/urls.py +28 -0
- trueppm_api/apps/resources/views.py +1749 -0
- trueppm_api/apps/scheduling/__init__.py +0 -0
- trueppm_api/apps/scheduling/apps.py +153 -0
- trueppm_api/apps/scheduling/calendars.py +119 -0
- trueppm_api/apps/scheduling/deadletter.py +91 -0
- trueppm_api/apps/scheduling/forecast_history_settings.py +215 -0
- trueppm_api/apps/scheduling/forecast_staleness.py +313 -0
- trueppm_api/apps/scheduling/graph_guard.py +263 -0
- trueppm_api/apps/scheduling/management/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/prune_forecast_snapshots.py +53 -0
- trueppm_api/apps/scheduling/migrations/0001_failed_task.py +62 -0
- trueppm_api/apps/scheduling/migrations/0001_squashed_0007_projectforecastsnapshot.py +309 -0
- trueppm_api/apps/scheduling/migrations/0002_schedulerequest.py +78 -0
- trueppm_api/apps/scheduling/migrations/0003_add_schedule_request_reason.py +27 -0
- trueppm_api/apps/scheduling/migrations/0004_velocity_suggestion.py +94 -0
- trueppm_api/apps/scheduling/migrations/0005_montecarlorun.py +61 -0
- trueppm_api/apps/scheduling/migrations/0006_montecarlorun_distribution.py +17 -0
- trueppm_api/apps/scheduling/migrations/0007_projectforecastsnapshot.py +65 -0
- trueppm_api/apps/scheduling/migrations/0008_alter_schedulerequest_reason.py +28 -0
- trueppm_api/apps/scheduling/migrations/0008_folded_schedulerequest_reason.py +34 -0
- trueppm_api/apps/scheduling/migrations/0009_alter_schedulerequest_reason.py +29 -0
- trueppm_api/apps/scheduling/migrations/0010_failedtask_resolution_note_failedtask_resolved_at_and_more.py +43 -0
- trueppm_api/apps/scheduling/migrations/0011_failedtask_project.py +26 -0
- trueppm_api/apps/scheduling/migrations/0012_montecarlorun_diagnostic.py +17 -0
- trueppm_api/apps/scheduling/migrations/0013_montecarlorun_status_date.py +17 -0
- trueppm_api/apps/scheduling/migrations/0014_montecarlorun_plan_version.py +17 -0
- trueppm_api/apps/scheduling/migrations/__init__.py +0 -0
- trueppm_api/apps/scheduling/models.py +516 -0
- trueppm_api/apps/scheduling/receivers.py +160 -0
- trueppm_api/apps/scheduling/risk_premium.py +283 -0
- trueppm_api/apps/scheduling/serializers.py +564 -0
- trueppm_api/apps/scheduling/services.py +1182 -0
- trueppm_api/apps/scheduling/signals.py +56 -0
- trueppm_api/apps/scheduling/tasks.py +1984 -0
- trueppm_api/apps/scheduling/telemetry.py +80 -0
- trueppm_api/apps/scheduling/units.py +35 -0
- trueppm_api/apps/scheduling/urls.py +57 -0
- trueppm_api/apps/scheduling/views.py +2564 -0
- trueppm_api/apps/sso/__init__.py +0 -0
- trueppm_api/apps/sso/apps.py +19 -0
- trueppm_api/apps/sso/extensions.py +130 -0
- trueppm_api/apps/sso/management/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/seed_sso_keycloak.py +172 -0
- trueppm_api/apps/sso/migrations/0001_initial.py +119 -0
- trueppm_api/apps/sso/migrations/0002_remove_oidcprovider_workspace_ssoproviderpolicy_and_more.py +169 -0
- trueppm_api/apps/sso/migrations/__init__.py +0 -0
- trueppm_api/apps/sso/models.py +148 -0
- trueppm_api/apps/sso/serializers.py +437 -0
- trueppm_api/apps/sso/services.py +1385 -0
- trueppm_api/apps/sso/urls.py +47 -0
- trueppm_api/apps/sso/views.py +935 -0
- trueppm_api/apps/sync/__init__.py +0 -0
- trueppm_api/apps/sync/apps.py +15 -0
- trueppm_api/apps/sync/backfill.py +208 -0
- trueppm_api/apps/sync/broadcast.py +320 -0
- trueppm_api/apps/sync/conflict.py +262 -0
- trueppm_api/apps/sync/consumers.py +471 -0
- trueppm_api/apps/sync/migrations/0001_initial.py +59 -0
- trueppm_api/apps/sync/migrations/0001_squashed_0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +77 -0
- trueppm_api/apps/sync/migrations/0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +41 -0
- trueppm_api/apps/sync/migrations/0003_boardevent.py +46 -0
- trueppm_api/apps/sync/migrations/0004_seed_sync_seq.py +24 -0
- trueppm_api/apps/sync/migrations/0005_program_sync_sequence.py +35 -0
- trueppm_api/apps/sync/migrations/__init__.py +0 -0
- trueppm_api/apps/sync/models.py +225 -0
- trueppm_api/apps/sync/pagination.py +231 -0
- trueppm_api/apps/sync/sequence.py +371 -0
- trueppm_api/apps/sync/serializers.py +687 -0
- trueppm_api/apps/sync/tasks.py +260 -0
- trueppm_api/apps/sync/throttles.py +175 -0
- trueppm_api/apps/sync/upload.py +501 -0
- trueppm_api/apps/sync/urls.py +17 -0
- trueppm_api/apps/sync/views.py +930 -0
- trueppm_api/apps/sync/ws_auth.py +193 -0
- trueppm_api/apps/taskruns/__init__.py +0 -0
- trueppm_api/apps/taskruns/apps.py +11 -0
- trueppm_api/apps/taskruns/migrations/0001_initial.py +92 -0
- trueppm_api/apps/taskruns/migrations/__init__.py +0 -0
- trueppm_api/apps/taskruns/models.py +68 -0
- trueppm_api/apps/taskruns/serializers.py +92 -0
- trueppm_api/apps/taskruns/tasks.py +60 -0
- trueppm_api/apps/taskruns/tracker.py +286 -0
- trueppm_api/apps/taskruns/urls.py +46 -0
- trueppm_api/apps/taskruns/views.py +195 -0
- trueppm_api/apps/teams/__init__.py +8 -0
- trueppm_api/apps/teams/apps.py +16 -0
- trueppm_api/apps/teams/migrations/0001_initial.py +139 -0
- trueppm_api/apps/teams/migrations/0001_squashed_0002_default_teams.py +142 -0
- trueppm_api/apps/teams/migrations/0002_default_teams.py +62 -0
- trueppm_api/apps/teams/migrations/0003_team_sync_seq_teammembership_sync_seq.py +22 -0
- trueppm_api/apps/teams/migrations/__init__.py +0 -0
- trueppm_api/apps/teams/models.py +145 -0
- trueppm_api/apps/teams/permissions.py +149 -0
- trueppm_api/apps/teams/serializers.py +95 -0
- trueppm_api/apps/teams/services.py +434 -0
- trueppm_api/apps/teams/signals.py +61 -0
- trueppm_api/apps/teams/urls.py +34 -0
- trueppm_api/apps/teams/views.py +201 -0
- trueppm_api/apps/timetracking/__init__.py +0 -0
- trueppm_api/apps/timetracking/apps.py +9 -0
- trueppm_api/apps/timetracking/migrations/0001_initial.py +117 -0
- trueppm_api/apps/timetracking/migrations/0002_timesheetsubmission.py +46 -0
- trueppm_api/apps/timetracking/migrations/0003_timeentry_deleted_at_timeentry_deleted_by.py +31 -0
- trueppm_api/apps/timetracking/migrations/0004_timeentry_sync_seq_and_more.py +24 -0
- trueppm_api/apps/timetracking/migrations/__init__.py +0 -0
- trueppm_api/apps/timetracking/models.py +178 -0
- trueppm_api/apps/timetracking/serializers.py +175 -0
- trueppm_api/apps/timetracking/services.py +117 -0
- trueppm_api/apps/timetracking/urls.py +47 -0
- trueppm_api/apps/timetracking/views.py +526 -0
- trueppm_api/apps/webhooks/__init__.py +0 -0
- trueppm_api/apps/webhooks/apps.py +8 -0
- trueppm_api/apps/webhooks/backfill.py +90 -0
- trueppm_api/apps/webhooks/dispatch.py +144 -0
- trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
- trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +243 -0
- trueppm_api/apps/webhooks/migrations/0002_alter_webhook_events.py +32 -0
- trueppm_api/apps/webhooks/migrations/0003_delivery_status_created_idx.py +17 -0
- trueppm_api/apps/webhooks/migrations/0004_webhook_program_alter_webhook_project_and_more.py +64 -0
- trueppm_api/apps/webhooks/migrations/0005_webhook_delivery_sequence_numbers.py +30 -0
- trueppm_api/apps/webhooks/migrations/0006_webhook_format_alter_webhook_events_and_more.py +61 -0
- trueppm_api/apps/webhooks/migrations/0007_sprint_lifecycle_events.py +63 -0
- trueppm_api/apps/webhooks/migrations/0008_risk_baseline_comment_events.py +73 -0
- trueppm_api/apps/webhooks/migrations/0009_encrypt_webhook_secret_and_failure_counters.py +90 -0
- trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
- trueppm_api/apps/webhooks/models.py +372 -0
- trueppm_api/apps/webhooks/serializers.py +318 -0
- trueppm_api/apps/webhooks/tasks.py +663 -0
- trueppm_api/apps/webhooks/urls.py +22 -0
- trueppm_api/apps/webhooks/views.py +383 -0
- trueppm_api/apps/workflow_engine/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/apps.py +23 -0
- trueppm_api/apps/workflow_engine/migrations/0001_initial.py +230 -0
- trueppm_api/apps/workflow_engine/migrations/0001_squashed_0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +234 -0
- trueppm_api/apps/workflow_engine/migrations/0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +16 -0
- trueppm_api/apps/workflow_engine/migrations/0003_workflowoutboxrow_workflow_outbox_recovery_idx.py +21 -0
- trueppm_api/apps/workflow_engine/migrations/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/models.py +265 -0
- trueppm_api/apps/workflow_engine/tasks.py +368 -0
- trueppm_api/apps/workspace/__init__.py +0 -0
- trueppm_api/apps/workspace/apps.py +10 -0
- trueppm_api/apps/workspace/backfill.py +111 -0
- trueppm_api/apps/workspace/export.py +242 -0
- trueppm_api/apps/workspace/migrations/0001_initial.py +326 -0
- trueppm_api/apps/workspace/migrations/0001_squashed_0014_workspacemembership_availability_effective_from_and_more.py +829 -0
- trueppm_api/apps/workspace/migrations/0002_remove_workspace_fiscal_year_start_and_more.py +85 -0
- trueppm_api/apps/workspace/migrations/0003_workspaceexportjob.py +73 -0
- trueppm_api/apps/workspace/migrations/0004_alter_workspaceinvite_status.py +27 -0
- trueppm_api/apps/workspace/migrations/0005_workspace_iteration_label_and_more.py +26 -0
- trueppm_api/apps/workspace/migrations/0006_alter_groupmembership_unique_together_and_more.py +45 -0
- trueppm_api/apps/workspace/migrations/0007_workspace_public_sharing_override_policy.py +21 -0
- trueppm_api/apps/workspace/migrations/0008_workspace_mc_history_attribution_audience_and_more.py +44 -0
- trueppm_api/apps/workspace/migrations/0009_workspace_methodology_and_more.py +186 -0
- trueppm_api/apps/workspace/migrations/0010_workspace_logo.py +38 -0
- trueppm_api/apps/workspace/migrations/0011_historicalworkspace_task_duration_change_percent_override_policy_and_more.py +56 -0
- trueppm_api/apps/workspace/migrations/0012_historicalworkspace_allowed_attachment_types_and_more.py +64 -0
- trueppm_api/apps/workspace/migrations/0013_auditevent.py +70 -0
- trueppm_api/apps/workspace/migrations/0014_workspacemembership_availability_effective_from_and_more.py +39 -0
- trueppm_api/apps/workspace/migrations/0015_trash_restore_1113.py +34 -0
- trueppm_api/apps/workspace/migrations/0016_historicalworkspace_calendar_and_more.py +55 -0
- trueppm_api/apps/workspace/migrations/0017_historicalworkspace_estimation_scale_and_more.py +38 -0
- trueppm_api/apps/workspace/migrations/0018_historicalworkspace_feedback_enabled_and_more.py +32 -0
- trueppm_api/apps/workspace/migrations/0019_historicalworkspace_mcp_enabled_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0020_viewer_ordinal_one.py +34 -0
- trueppm_api/apps/workspace/migrations/0021_group_sync_seq_groupmembership_sync_seq_and_more.py +27 -0
- trueppm_api/apps/workspace/migrations/0022_historicalworkspace_sprint_picker_ready_only_default_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0023_alter_auditevent_event_type.py +32 -0
- trueppm_api/apps/workspace/migrations/0024_alter_auditevent_event_type.py +33 -0
- trueppm_api/apps/workspace/migrations/0025_alter_auditevent_event_type.py +36 -0
- trueppm_api/apps/workspace/migrations/0026_alter_auditevent_event_type.py +37 -0
- trueppm_api/apps/workspace/migrations/0027_alter_auditevent_event_type.py +42 -0
- trueppm_api/apps/workspace/migrations/__init__.py +0 -0
- trueppm_api/apps/workspace/models.py +857 -0
- trueppm_api/apps/workspace/permissions.py +187 -0
- trueppm_api/apps/workspace/serializers.py +590 -0
- trueppm_api/apps/workspace/services.py +919 -0
- trueppm_api/apps/workspace/signals.py +37 -0
- trueppm_api/apps/workspace/tasks.py +555 -0
- trueppm_api/apps/workspace/urls.py +113 -0
- trueppm_api/apps/workspace/views.py +1502 -0
- trueppm_api/asgi.py +42 -0
- trueppm_api/celery.py +31 -0
- trueppm_api/core/__init__.py +0 -0
- trueppm_api/core/admin_site.py +206 -0
- trueppm_api/core/auth_views.py +916 -0
- trueppm_api/core/constant_time.py +51 -0
- trueppm_api/core/csp.py +54 -0
- trueppm_api/core/db.py +87 -0
- trueppm_api/core/db_session.py +37 -0
- trueppm_api/core/exception_handlers.py +157 -0
- trueppm_api/core/export_downloads.py +56 -0
- trueppm_api/core/extension_providers.py +56 -0
- trueppm_api/core/extension_signals.py +147 -0
- trueppm_api/core/idempotent.py +260 -0
- trueppm_api/core/metadata.py +42 -0
- trueppm_api/core/middleware.py +51 -0
- trueppm_api/core/openapi.py +949 -0
- trueppm_api/core/password_reset.py +500 -0
- trueppm_api/core/protect_conflict.py +100 -0
- trueppm_api/core/ratelimit.py +148 -0
- trueppm_api/core/redis_throttle.py +82 -0
- trueppm_api/core/refresh_cookie_policy.py +156 -0
- trueppm_api/core/request_body.py +86 -0
- trueppm_api/core/security_checks.py +884 -0
- trueppm_api/core/storage_config.py +105 -0
- trueppm_api/core/text_decode.py +168 -0
- trueppm_api/core/throttling.py +344 -0
- trueppm_api/core/valkey.py +214 -0
- trueppm_api/core/valkey_cache.py +66 -0
- trueppm_api/core/valkey_checks.py +104 -0
- trueppm_api/core/valkey_config.py +39 -0
- trueppm_api/core/worker_broker_wait.py +149 -0
- trueppm_api/core/worker_heartbeat.py +134 -0
- trueppm_api/fields.py +36 -0
- trueppm_api/permissions.py +30 -0
- trueppm_api/py.typed +0 -0
- trueppm_api/routing.py +11 -0
- trueppm_api/settings/__init__.py +0 -0
- trueppm_api/settings/base.py +2455 -0
- trueppm_api/settings/dev.py +273 -0
- trueppm_api/settings/prod.py +228 -0
- trueppm_api/urls.py +153 -0
- trueppm_api/workflows/__init__.py +38 -0
- trueppm_api/workflows/backends/__init__.py +6 -0
- trueppm_api/workflows/backends/default.py +297 -0
- trueppm_api/workflows/consumers/__init__.py +11 -0
- trueppm_api/workflows/consumers/requeue_failed_task.py +65 -0
- trueppm_api/workflows/interface.py +148 -0
- trueppm_api/workflows/registry.py +106 -0
- trueppm_api/workflows/services.py +90 -0
- trueppm_api/wsgi.py +11 -0
- trueppm_api-0.4.0b2.dist-info/METADATA +70 -0
- trueppm_api-0.4.0b2.dist-info/RECORD +759 -0
- trueppm_api-0.4.0b2.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""Development settings — DEBUG on, relaxed auth, local service URLs.
|
|
2
|
+
|
|
3
|
+
This module is fenced by ``_assert_dev_environment_safe`` because it sets
|
|
4
|
+
``AllowAny`` and ``ALLOWED_HOSTS=['*']`` — loading it in staging or production
|
|
5
|
+
would silently disable authentication on every endpoint. The guard fails loudly
|
|
6
|
+
at import time unless the process is a test runner or the operator has
|
|
7
|
+
explicitly opted in via ``TRUEPPM_ALLOW_DEV_SETTINGS=1``.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
from collections.abc import Mapping
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
import environ
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _assert_dev_environment_safe(
|
|
22
|
+
env: Mapping[str, str] | None = None,
|
|
23
|
+
modules: Mapping[str, Any] | None = None,
|
|
24
|
+
) -> None:
|
|
25
|
+
"""Refuse to load dev settings outside a test runner or developer workstation.
|
|
26
|
+
|
|
27
|
+
Four signals permit the load, in order of precedence:
|
|
28
|
+
1. ``PYTEST_CURRENT_TEST`` is set in the environment (pytest is active)
|
|
29
|
+
2. ``pytest`` is already imported in the running process (test collection)
|
|
30
|
+
3. ``mypy`` is already imported (django-stubs imports settings under mypy)
|
|
31
|
+
4. ``TRUEPPM_ALLOW_DEV_SETTINGS=1`` is set (explicit developer opt-in)
|
|
32
|
+
|
|
33
|
+
Any other environment — staging, production, an unconfigured Docker image —
|
|
34
|
+
raises ``RuntimeError`` to prevent the module from completing its import and
|
|
35
|
+
overriding ``DEFAULT_PERMISSION_CLASSES`` with ``AllowAny``.
|
|
36
|
+
|
|
37
|
+
Args are injectable so the assertion can be unit-tested without mutating the
|
|
38
|
+
real environment or the global module table.
|
|
39
|
+
"""
|
|
40
|
+
env = env if env is not None else os.environ
|
|
41
|
+
modules = modules if modules is not None else sys.modules
|
|
42
|
+
|
|
43
|
+
if env.get("PYTEST_CURRENT_TEST"):
|
|
44
|
+
return
|
|
45
|
+
if "pytest" in modules or "mypy" in modules:
|
|
46
|
+
return
|
|
47
|
+
if env.get("TRUEPPM_ALLOW_DEV_SETTINGS") == "1":
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
raise RuntimeError(
|
|
51
|
+
"trueppm_api.settings.dev was loaded outside local dev or a test runner. "
|
|
52
|
+
"This module sets AllowAny and ALLOWED_HOSTS=['*'] — refusing to import. "
|
|
53
|
+
"Set TRUEPPM_ALLOW_DEV_SETTINGS=1 to override on a developer workstation."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
_assert_dev_environment_safe()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
from .base import * # noqa: F403, E402
|
|
61
|
+
from .base import BASE_DIR, DATABASES, REST_FRAMEWORK # noqa: E402
|
|
62
|
+
|
|
63
|
+
env = environ.Env()
|
|
64
|
+
|
|
65
|
+
# Read .env file if present (optional in dev)
|
|
66
|
+
environ.Env.read_env(env_file=".env", overwrite=False)
|
|
67
|
+
|
|
68
|
+
DEBUG = True
|
|
69
|
+
|
|
70
|
+
ALLOWED_HOSTS = ["*"]
|
|
71
|
+
|
|
72
|
+
# Deterministic Fernet key for dev / pytest only — never use this value in
|
|
73
|
+
# staging or production. The dev settings module is fenced by
|
|
74
|
+
# _assert_dev_environment_safe above; production reads its key from a
|
|
75
|
+
# Kubernetes Secret via the Helm chart and never falls through to this default.
|
|
76
|
+
INTEGRATION_ENCRYPTION_KEY = "cNHot7PnbAHGIuY4zUht8FwB5wYGv06O7ppzGyhzR84="
|
|
77
|
+
|
|
78
|
+
# In dev, allow unauthenticated access to the API for ease of local testing.
|
|
79
|
+
# Throttle classes/rates are inherited from base settings via the spread below;
|
|
80
|
+
# this dev-only module merely relaxes the permission classes. Fenced by
|
|
81
|
+
# _assert_dev_environment_safe — never loaded in staging or production.
|
|
82
|
+
REST_FRAMEWORK = { # nosemgrep: missing-throttle-config
|
|
83
|
+
**REST_FRAMEWORK, # nosemgrep: missing-throttle-config
|
|
84
|
+
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.AllowAny"],
|
|
85
|
+
# Re-add SessionAuthentication on top of base's posture (#2248) so the test
|
|
86
|
+
# suite's `client.force_login()` populates request.user without minting a JWT.
|
|
87
|
+
# Never loaded in staging/prod (fenced by _assert_dev_environment_safe). Keeps
|
|
88
|
+
# base's OwnerScopedApiTokenAuthentication entry (#2547) so PAT-authenticated
|
|
89
|
+
# requests in the test suite exercise the real production auth wiring.
|
|
90
|
+
"DEFAULT_AUTHENTICATION_CLASSES": [
|
|
91
|
+
"trueppm_api.apps.projects.authentication.OwnerScopedApiTokenAuthentication",
|
|
92
|
+
"rest_framework_simplejwt.authentication.JWTAuthentication",
|
|
93
|
+
"rest_framework.authentication.SessionAuthentication",
|
|
94
|
+
],
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# The refresh cookie must work over plain HTTP on localhost — a Secure cookie is
|
|
98
|
+
# dropped by the browser on http://. Production keeps the base default (True).
|
|
99
|
+
AUTH_REFRESH_COOKIE_SECURE = False
|
|
100
|
+
|
|
101
|
+
# Django admin is ON in dev and OFF everywhere else (#3557). base.py defaults it
|
|
102
|
+
# to False so that a production deploy that sets nothing does not expose a second
|
|
103
|
+
# password door; a developer workstation is the one place where reaching /admin/
|
|
104
|
+
# with no ceremony is the point. Still env-overridable, so `pytest` can assert the
|
|
105
|
+
# disabled behavior through the same variable an operator would set. This module
|
|
106
|
+
# is fenced by _assert_dev_environment_safe above, so the relaxation cannot load
|
|
107
|
+
# in staging or production.
|
|
108
|
+
DJANGO_ADMIN_ENABLED = env.bool("TRUEPPM_DJANGO_ADMIN_ENABLED", default=True)
|
|
109
|
+
|
|
110
|
+
# base.py defaults MEDIA_ROOT to /var/lib/trueppm/media — the path the chart and
|
|
111
|
+
# compose mount a writable volume at (#3184). Nothing creates that path on a
|
|
112
|
+
# developer workstation, and pytest runs under this module, so keep uploads
|
|
113
|
+
# inside the checkout. BASE_DIR is packages/api/src, so `.parent` lands on
|
|
114
|
+
# packages/api/media/, which .gitignore has already carried since #817.
|
|
115
|
+
MEDIA_ROOT = Path(env("TRUEPPM_MEDIA_ROOT", default=str(BASE_DIR.parent / "media")))
|
|
116
|
+
|
|
117
|
+
# WhiteNoise (base.py MIDDLEWARE) serves static from STATIC_ROOT, which requires a
|
|
118
|
+
# `collectstatic` run. In dev and under pytest we want /static/ to work without
|
|
119
|
+
# that step, so serve straight from the staticfiles *finders* (each app's static/
|
|
120
|
+
# dir, including drf-spectacular-sidecar's bundles) and re-scan on every request so
|
|
121
|
+
# newly added files appear without a restart. Production keeps the base behavior:
|
|
122
|
+
# collectstatic into STATIC_ROOT, which WhiteNoise serves with far-future caching.
|
|
123
|
+
WHITENOISE_USE_FINDERS = True
|
|
124
|
+
WHITENOISE_AUTOREFRESH = True
|
|
125
|
+
|
|
126
|
+
# Local dev / pytest run in a single process, so per-process memory is a
|
|
127
|
+
# sufficient (and dependency-free) cache for the OIDC login state and the DRF
|
|
128
|
+
# throttles. Production uses the Redis-backed cache from base.py. Overriding here
|
|
129
|
+
# keeps `pytest` from requiring a separate Valkey cache db (the testcontainers
|
|
130
|
+
# fixture provisions PostgreSQL only).
|
|
131
|
+
CACHES = {
|
|
132
|
+
"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"},
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# Detailed SQL logging in dev.
|
|
137
|
+
#
|
|
138
|
+
# This override replaces base.py's build_logging_config() output entirely, so the
|
|
139
|
+
# UTC-timestamp fix in that function (#1952) does not reach dev. That is fine and
|
|
140
|
+
# intentional: these handlers declare no "formatter", so they use logging's
|
|
141
|
+
# default (message-only) formatter, which emits no %(asctime)s at all — there is
|
|
142
|
+
# no log timestamp here to render in the wrong timezone. If a formatter with
|
|
143
|
+
# %(asctime)s is ever added to this dev config, mirror the base fix
|
|
144
|
+
# (logging.Formatter.converter = time.gmtime) so dev timestamps stay UTC too.
|
|
145
|
+
LOGGING = {
|
|
146
|
+
"version": 1,
|
|
147
|
+
"disable_existing_loggers": False,
|
|
148
|
+
"handlers": {
|
|
149
|
+
"console": {"class": "logging.StreamHandler"},
|
|
150
|
+
},
|
|
151
|
+
"root": {
|
|
152
|
+
"handlers": ["console"],
|
|
153
|
+
"level": "WARNING",
|
|
154
|
+
},
|
|
155
|
+
"loggers": {
|
|
156
|
+
"django.db.backends": {
|
|
157
|
+
"handlers": ["console"],
|
|
158
|
+
"level": env("SQL_LOG_LEVEL", default="WARNING"),
|
|
159
|
+
"propagate": False,
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
# ---------------------------------------------------------------------------
|
|
165
|
+
# CI test-DB prewarm (#688)
|
|
166
|
+
# ---------------------------------------------------------------------------
|
|
167
|
+
# When the api:test CI shards have loaded a `migrated` template database from
|
|
168
|
+
# the schema dump produced by api:testdb-dump, clone each pytest-xdist worker
|
|
169
|
+
# DB from it (CREATE DATABASE ... TEMPLATE, a fast file copy) and skip the
|
|
170
|
+
# per-worker migration replay. The template already carries the ltree
|
|
171
|
+
# extension, the wbs_path GiST index, and the data backfills, so this is
|
|
172
|
+
# equivalent to a full migrate — unlike --no-migrations, which skips the
|
|
173
|
+
# RunSQL that creates the ltree extension and breaks test-DB creation.
|
|
174
|
+
#
|
|
175
|
+
# Gated on the CI-set env var: a local `pytest` run (no `migrated` template
|
|
176
|
+
# present) leaves DATABASES untouched and builds its test DB by replaying
|
|
177
|
+
# migrations exactly as before. This is test-only — the TEST sub-dict is read
|
|
178
|
+
# solely by Django's test-database creation, never by a running server.
|
|
179
|
+
_test_db_template = os.environ.get("TRUEPPM_TEST_DB_TEMPLATE")
|
|
180
|
+
if _test_db_template:
|
|
181
|
+
DATABASES["default"].setdefault("TEST", {})
|
|
182
|
+
DATABASES["default"]["TEST"]["TEMPLATE"] = _test_db_template
|
|
183
|
+
DATABASES["default"]["TEST"]["MIGRATE"] = False
|
|
184
|
+
|
|
185
|
+
# ---------------------------------------------------------------------------
|
|
186
|
+
# Per-worktree test DB (scripts/wt)
|
|
187
|
+
# ---------------------------------------------------------------------------
|
|
188
|
+
# `scripts/wt new` writes TRUEPPM_TEST_DB=test_trueppm_wt_<slug> into each
|
|
189
|
+
# worktree's .envrc so N parallel worktrees each create and drop their OWN test
|
|
190
|
+
# database instead of racing on one shared `test_trueppm`. This removes the need
|
|
191
|
+
# for the out-of-band flock mutex that serialized parallel pytest runs (and that
|
|
192
|
+
# flock(1) can't provide on macOS). Test-only: like TEST["TEMPLATE"] above, the
|
|
193
|
+
# TEST["NAME"] key is read solely by Django's test-database creation, never by a
|
|
194
|
+
# running server.
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _apply_test_db_name(databases: dict[str, Any], env: Mapping[str, str]) -> None:
|
|
198
|
+
"""Point Django's test-DB creation at a per-worktree database, if requested.
|
|
199
|
+
|
|
200
|
+
Reads ``TRUEPPM_TEST_DB`` from ``env`` and, when set, writes it to
|
|
201
|
+
``databases["default"]["TEST"]["NAME"]`` so a worktree's ``pytest`` run builds
|
|
202
|
+
an isolated test database instead of racing on the shared one. A no-op when the
|
|
203
|
+
variable is unset or empty. ``databases``/``env`` are injectable so the logic
|
|
204
|
+
is unit-testable without reloading the settings module or mutating the process
|
|
205
|
+
environment (mirrors ``_assert_dev_environment_safe``).
|
|
206
|
+
"""
|
|
207
|
+
name = env.get("TRUEPPM_TEST_DB")
|
|
208
|
+
if not name:
|
|
209
|
+
return
|
|
210
|
+
databases["default"].setdefault("TEST", {})
|
|
211
|
+
databases["default"]["TEST"]["NAME"] = name
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
_apply_test_db_name(DATABASES, os.environ)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# ---------------------------------------------------------------------------
|
|
218
|
+
# Fast password hashing under pytest (#3391)
|
|
219
|
+
# ---------------------------------------------------------------------------
|
|
220
|
+
# Django 5.2 defaults to PBKDF2-SHA256 at 1,000,000 iterations. That cost is the
|
|
221
|
+
# whole point in production and pure waste in the test suite, which makes 1,054
|
|
222
|
+
# `create_user(..., password=...)` calls and asserts nothing about hash strength.
|
|
223
|
+
# Measured: 76.3ms per PBKDF2 hash vs 0.026ms for MD5 — 2,899x, several minutes
|
|
224
|
+
# of every api:test run spent proving nothing.
|
|
225
|
+
#
|
|
226
|
+
# Gated on pytest actually being loaded rather than applied to all of dev: a
|
|
227
|
+
# local `runserver` keeps the production hasher, so a developer never logs in
|
|
228
|
+
# against a weaker algorithm than the one they ship. `"pytest" in sys.modules`
|
|
229
|
+
# is ONE of the four signals `_assert_dev_environment_safe` trusts above, not
|
|
230
|
+
# the same test — that guard also accepts PYTEST_CURRENT_TEST from the env,
|
|
231
|
+
# which this deliberately does not, because it is set per test rather than at
|
|
232
|
+
# settings-import time. The gap is worth naming: a subprocess spawned from a
|
|
233
|
+
# test inherits PYTEST_CURRENT_TEST but not the parent's module table, so it
|
|
234
|
+
# would load PBKDF2 while its parent used MD5, and a cross-process
|
|
235
|
+
# check_password against an md5 hash would raise "Unknown password hashing
|
|
236
|
+
# algorithm". Latent, not live — the suite's only subprocess use is a mocked
|
|
237
|
+
# Popen — but a future test that really shells out has to know this.
|
|
238
|
+
#
|
|
239
|
+
# Where settings.dev actually loads, so the next reader does not have to guess:
|
|
240
|
+
# the local compose stack (docker-compose.yml, three services, which set
|
|
241
|
+
# TRUEPPM_ALLOW_DEV_SETTINGS=1), every api:* CI job, the Makefile, and
|
|
242
|
+
# scripts/export-openapi.sh — plus pytest. It is routine infrastructure, not a
|
|
243
|
+
# rare manual opt-in. The branch is unreachable in all of those but pytest for
|
|
244
|
+
# one reason: packages/api/Dockerfile installs `api[c]`/`api[binary]` and never
|
|
245
|
+
# `api[dev]`, and uninstalls pip, so the runtime image has no pytest to import.
|
|
246
|
+
# Every deployed path (helm values, docker-compose.prod, docker-compose.demo)
|
|
247
|
+
# loads settings.prod and never reaches this file at all.
|
|
248
|
+
#
|
|
249
|
+
# The list is deliberately a SINGLE entry. Adding PBKDF2 back as a fallback
|
|
250
|
+
# looks safer and is not: `hasher_changed` would then be True on every login, so
|
|
251
|
+
# check_password's setter fires and ModelBackend REWRITES stored PBKDF2 hashes
|
|
252
|
+
# as MD5. One entry fails those rows closed instead of silently downgrading them.
|
|
253
|
+
#
|
|
254
|
+
# DX note: with --reuse-db, a test database created before this change still
|
|
255
|
+
# holds PBKDF2 hashes, which no longer verify (Django catches the unknown-hasher
|
|
256
|
+
# ValueError and returns False rather than raising). That reads as an auth
|
|
257
|
+
# regression; it is a stale test DB. Use --create-db once.
|
|
258
|
+
#
|
|
259
|
+
# Asserted by tests/test_password_hashers.py, including the invariant this
|
|
260
|
+
# gate rests on: nothing under src/ may import pytest.
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _use_fast_password_hashers(modules: Mapping[str, Any]) -> bool:
|
|
264
|
+
"""Report whether the test-only fast password hasher should be installed.
|
|
265
|
+
|
|
266
|
+
Injectable so the predicate is unit-testable without mutating the global
|
|
267
|
+
module table (mirrors ``_assert_dev_environment_safe``).
|
|
268
|
+
"""
|
|
269
|
+
return "pytest" in modules
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
if _use_fast_password_hashers(sys.modules):
|
|
273
|
+
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""Production settings — security hardened, secrets from environment."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import urllib.parse
|
|
7
|
+
|
|
8
|
+
import environ
|
|
9
|
+
|
|
10
|
+
from trueppm_api.apps.observability.logging import build_logging_config
|
|
11
|
+
from trueppm_api.core.security_checks import (
|
|
12
|
+
validate_allowed_hosts,
|
|
13
|
+
validate_attachment_storage,
|
|
14
|
+
validate_integration_encryption_key,
|
|
15
|
+
validate_project_soft_delete_retention,
|
|
16
|
+
validate_secret_key,
|
|
17
|
+
validate_service_credentials,
|
|
18
|
+
validate_signing_key,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from .base import * # noqa: F403
|
|
22
|
+
from .base import (
|
|
23
|
+
ALLOW_LOCAL_ATTACHMENT_STORAGE,
|
|
24
|
+
ALLOW_UNENCRYPTED_DB,
|
|
25
|
+
ALLOW_WILDCARD_ALLOWED_HOSTS,
|
|
26
|
+
DATABASES,
|
|
27
|
+
DJANGO_LOG_LEVEL,
|
|
28
|
+
INTEGRATION_ENCRYPTION_KEY,
|
|
29
|
+
MEDIA_ROOT,
|
|
30
|
+
REDIS_URL,
|
|
31
|
+
SIMPLE_JWT,
|
|
32
|
+
STORAGES,
|
|
33
|
+
TRUEPPM_PROJECT_SOFT_DELETE_RETENTION_DAYS,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
env = environ.Env()
|
|
37
|
+
|
|
38
|
+
DEBUG = False
|
|
39
|
+
|
|
40
|
+
# Prefix for every import-time boot-guard failure below.
|
|
41
|
+
_REFUSING_TO_START = "Refusing to start: "
|
|
42
|
+
|
|
43
|
+
# Force single-line JSON logs in production so a collector (Loki/ELK/CloudWatch)
|
|
44
|
+
# can index each record's fields — including the OTel trace_id/span_id/request_id
|
|
45
|
+
# the filter injects (ADR-0223, #1899) — for log-to-trace correlation. The base
|
|
46
|
+
# config defaults to human-readable console output for dev; here we always emit
|
|
47
|
+
# JSON regardless of TRUEPPM_LOG_JSON, while still honouring DJANGO_LOG_LEVEL.
|
|
48
|
+
LOGGING = build_logging_config(level=DJANGO_LOG_LEVEL, json_output=True)
|
|
49
|
+
|
|
50
|
+
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS")
|
|
51
|
+
|
|
52
|
+
# Refuse to boot on a bare-wildcard ALLOWED_HOSTS (#3515). Host validation is the
|
|
53
|
+
# only bound on the host the API reports, and therefore on every absolute URL it
|
|
54
|
+
# builds; "*" removes it. Same import-time enforcement as the guards below, since
|
|
55
|
+
# gunicorn/asgi workers never run `manage.py check`. Operators whose host set is
|
|
56
|
+
# genuinely unknowable opt in via TRUEPPM_ALLOW_WILDCARD_HOSTS=true and keep the
|
|
57
|
+
# warning instead — the TRUEPPM_ALLOW_UNENCRYPTED_DB shape, chosen so an existing
|
|
58
|
+
# install running "*" has a one-variable remedy rather than a crash-loop.
|
|
59
|
+
_wildcard_host_errors = validate_allowed_hosts(
|
|
60
|
+
ALLOWED_HOSTS, debug=DEBUG, allow_wildcard=ALLOW_WILDCARD_ALLOWED_HOSTS
|
|
61
|
+
)
|
|
62
|
+
if _wildcard_host_errors:
|
|
63
|
+
raise RuntimeError(_REFUSING_TO_START + "; ".join(str(e.msg) for e in _wildcard_host_errors))
|
|
64
|
+
if ALLOW_WILDCARD_ALLOWED_HOSTS and "*" in ALLOWED_HOSTS:
|
|
65
|
+
logging.getLogger("trueppm.settings").warning(
|
|
66
|
+
"ALLOWED_HOSTS contains the wildcard '*', so Django performs no host "
|
|
67
|
+
"validation. Absolute URLs the API builds (the OIDC redirect_uri and the "
|
|
68
|
+
"inbound Git-webhook URL) will follow whatever host the caller sends. "
|
|
69
|
+
"Proceeding because TRUEPPM_ALLOW_WILDCARD_HOSTS=true is set."
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
SECRET_KEY = env("SECRET_KEY") # required; no default in prod
|
|
73
|
+
|
|
74
|
+
# Refuse to boot with a weak SECRET_KEY (#566, PYSEC-2025-183).
|
|
75
|
+
# Django's system-check registry only fires under `manage.py check`; gunicorn
|
|
76
|
+
# and asgi workers do not run checks at startup, so the failure must be
|
|
77
|
+
# raised at settings import time to actually stop a broken deploy.
|
|
78
|
+
_secret_key_errors = validate_secret_key(SECRET_KEY, debug=DEBUG)
|
|
79
|
+
if _secret_key_errors:
|
|
80
|
+
raise RuntimeError(_REFUSING_TO_START + "; ".join(str(e.msg) for e in _secret_key_errors))
|
|
81
|
+
|
|
82
|
+
# Dedicated JWT signing key (#2247). base.py already derived JWT_SIGNING_KEY from
|
|
83
|
+
# the SECRET_KEY env var; re-derive it here against the prod-required SECRET_KEY so
|
|
84
|
+
# the binding is explicit and cannot drift, and mirror the value into SIMPLE_JWT.
|
|
85
|
+
# Then refuse to boot on a weak *explicit* key — an unset key inherits the
|
|
86
|
+
# already-validated SECRET_KEY, so validate_signing_key returns clean for it.
|
|
87
|
+
JWT_SIGNING_KEY = env("JWT_SIGNING_KEY", default=SECRET_KEY)
|
|
88
|
+
SIMPLE_JWT["SIGNING_KEY"] = JWT_SIGNING_KEY
|
|
89
|
+
_signing_key_errors = validate_signing_key(JWT_SIGNING_KEY, SECRET_KEY, debug=DEBUG)
|
|
90
|
+
if _signing_key_errors:
|
|
91
|
+
raise RuntimeError(_REFUSING_TO_START + "; ".join(str(e.msg) for e in _signing_key_errors))
|
|
92
|
+
|
|
93
|
+
# Persistent connections for production load.
|
|
94
|
+
DATABASES["default"]["CONN_MAX_AGE"] = 600
|
|
95
|
+
|
|
96
|
+
# Security headers.
|
|
97
|
+
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
|
98
|
+
|
|
99
|
+
# The host is NOT taken from the proxy, and that asymmetry with the line above is
|
|
100
|
+
# deliberate (#3515). Django defaults both of these to False; stating them keeps
|
|
101
|
+
# the file from inheriting a position it never took.
|
|
102
|
+
#
|
|
103
|
+
# Why the scheme is trusted and the host is not:
|
|
104
|
+
#
|
|
105
|
+
# * X-Forwarded-Proto carries information the app cannot obtain any other way —
|
|
106
|
+
# the container always speaks plain HTTP on :8000, so without the header
|
|
107
|
+
# every request looks insecure. Its failure mode here is contained: the
|
|
108
|
+
# Secure flags on the session, CSRF and refresh cookies are hardcoded True
|
|
109
|
+
# above and in base.py rather than derived from is_secure(), so a spoofed
|
|
110
|
+
# value does not downgrade a cookie.
|
|
111
|
+
#
|
|
112
|
+
# * X-Forwarded-Host carries nothing new. Every proxy this project ships
|
|
113
|
+
# preserves the original Host (`proxy_set_header Host $host` in
|
|
114
|
+
# nginx/app.conf.template, app-http.conf.template, demo.conf.template,
|
|
115
|
+
# packages/web/nginx.conf and the chart's web ConfigMap), and none of them
|
|
116
|
+
# sets or strips X-Forwarded-Host. Turning this on would therefore trust a
|
|
117
|
+
# header whose only author in every supported topology is the client: Host is
|
|
118
|
+
# the edge's routing key, so a value that does not route never arrives, while
|
|
119
|
+
# X-Forwarded-Host routes fine and would be believed anyway. That is a strict
|
|
120
|
+
# widening of who can steer request.build_absolute_uri(), and it buys nothing.
|
|
121
|
+
#
|
|
122
|
+
# The premise True would need — "the proxy is the sole ingress path" — is also not
|
|
123
|
+
# something the chart enforces: templates/networkpolicy.yaml restricts the bundled
|
|
124
|
+
# datastore pods only, and the admin docs hand API-pod ingress restriction to the
|
|
125
|
+
# operator. Do not flip this without also closing that gap and stripping the
|
|
126
|
+
# header at every edge.
|
|
127
|
+
#
|
|
128
|
+
# An operator whose proxy genuinely rewrites Host does not need this switch:
|
|
129
|
+
# TRUEPPM_PUBLIC_API_BASE_URL pins the origin for both request-derived absolute
|
|
130
|
+
# URLs (the OIDC redirect_uri and the inbound Git-webhook URL), which is a value
|
|
131
|
+
# they control rather than a belief about a header they cannot verify.
|
|
132
|
+
USE_X_FORWARDED_HOST = False
|
|
133
|
+
USE_X_FORWARDED_PORT = False
|
|
134
|
+
SECURE_HSTS_SECONDS = 31536000
|
|
135
|
+
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
|
136
|
+
SESSION_COOKIE_SECURE = True
|
|
137
|
+
CSRF_COOKIE_SECURE = True
|
|
138
|
+
SECURE_CONTENT_TYPE_NOSNIFF = True
|
|
139
|
+
SECURE_REFERRER_POLICY = "same-origin"
|
|
140
|
+
# HTTP→HTTPS redirect is opt-in: many deploys terminate TLS at the ingress and
|
|
141
|
+
# speak plain HTTP to the app (incl. the k8s liveness/readiness probes that hit
|
|
142
|
+
# /health/ and /edition/), where an unconditional redirect would break them.
|
|
143
|
+
# Operators that expose the app directly over TLS opt in via env; the probe
|
|
144
|
+
# paths stay exempt so they keep working over plain HTTP either way.
|
|
145
|
+
SECURE_SSL_REDIRECT = env.bool("TRUEPPM_SECURE_SSL_REDIRECT", default=False)
|
|
146
|
+
SECURE_REDIRECT_EXEMPT = [r"^api/v1/health/$", r"^api/v1/readyz$", r"^api/v1/edition/$"]
|
|
147
|
+
|
|
148
|
+
# Refuse to boot when task attachments would land on ephemeral local disk in a
|
|
149
|
+
# containerized deploy (#775) — same import-time enforcement as the SECRET_KEY
|
|
150
|
+
# guard, since gunicorn/asgi workers never run `manage.py check`. Passing
|
|
151
|
+
# MEDIA_ROOT extends the guard to the opted-in case (#3184): before this the
|
|
152
|
+
# opt-in was an unverified claim, and the deployment it described — no MEDIA_ROOT
|
|
153
|
+
# at all, on a read-only root filesystem — booted clean and then EROFS'd on the
|
|
154
|
+
# first upload.
|
|
155
|
+
_storage_errors = validate_attachment_storage(
|
|
156
|
+
STORAGES["default"]["BACKEND"],
|
|
157
|
+
debug=DEBUG,
|
|
158
|
+
allow_local=ALLOW_LOCAL_ATTACHMENT_STORAGE,
|
|
159
|
+
media_root=MEDIA_ROOT,
|
|
160
|
+
)
|
|
161
|
+
if _storage_errors:
|
|
162
|
+
raise RuntimeError(_REFUSING_TO_START + "; ".join(str(e.msg) for e in _storage_errors))
|
|
163
|
+
|
|
164
|
+
# Refuse to boot without a valid integration encryption key (#1002). The key
|
|
165
|
+
# encrypts integration PATs at rest; without this guard a missing/malformed key
|
|
166
|
+
# only surfaces as a 500 on the first PAT connect, long after deploy. Same
|
|
167
|
+
# import-time enforcement as the SECRET_KEY and storage guards above.
|
|
168
|
+
_integration_key_errors = validate_integration_encryption_key(
|
|
169
|
+
INTEGRATION_ENCRYPTION_KEY, debug=DEBUG
|
|
170
|
+
)
|
|
171
|
+
if _integration_key_errors:
|
|
172
|
+
raise RuntimeError(_REFUSING_TO_START + "; ".join(str(e.msg) for e in _integration_key_errors))
|
|
173
|
+
|
|
174
|
+
# Refuse to boot when the soft-delete retention window is 0 (#3186). 0 sets the
|
|
175
|
+
# purge cutoff to the present moment, so the next tick HARD-deletes every
|
|
176
|
+
# trashed project and all of its children via CASCADE — irreversibly, with no
|
|
177
|
+
# tombstone. The API has always refused this value
|
|
178
|
+
# (RetentionPolicyWriteSerializer.value is min_value=1); the env var had no
|
|
179
|
+
# floor, and the chart's own comment told operators to set exactly this "to keep
|
|
180
|
+
# the default". Same import-time posture as the guards above, since asgi workers
|
|
181
|
+
# never run `manage.py check`.
|
|
182
|
+
_retention_errors = validate_project_soft_delete_retention(
|
|
183
|
+
TRUEPPM_PROJECT_SOFT_DELETE_RETENTION_DAYS
|
|
184
|
+
)
|
|
185
|
+
if _retention_errors:
|
|
186
|
+
raise RuntimeError(_REFUSING_TO_START + "; ".join(str(e.msg) for e in _retention_errors))
|
|
187
|
+
|
|
188
|
+
# Refuse to boot when DATABASE_URL has no sslmode parameter in a non-DEBUG
|
|
189
|
+
# deployment (#1550). Without sslmode=require the connection falls back to
|
|
190
|
+
# whatever the server negotiates, which may be plaintext — the same fail-closed
|
|
191
|
+
# posture as the SECRET_KEY (#566), attachment-storage (#775), and integration-
|
|
192
|
+
# key (#1002) guards above, since gunicorn/asgi workers never run
|
|
193
|
+
# `manage.py check`. Operators terminating TLS at the proxy/sidecar layer (where
|
|
194
|
+
# the app-to-DB link is already encrypted) opt in via
|
|
195
|
+
# TRUEPPM_ALLOW_UNENCRYPTED_DB=true and keep the warning instead.
|
|
196
|
+
_db_url_raw: str = env("DATABASE_URL", default="")
|
|
197
|
+
if _db_url_raw:
|
|
198
|
+
_db_url_qs = urllib.parse.parse_qs(urllib.parse.urlparse(_db_url_raw).query)
|
|
199
|
+
if "sslmode" not in _db_url_qs:
|
|
200
|
+
if ALLOW_UNENCRYPTED_DB:
|
|
201
|
+
logging.getLogger("trueppm.settings").warning(
|
|
202
|
+
"DATABASE_URL does not include sslmode=require. "
|
|
203
|
+
"Database connections may use unencrypted transport. "
|
|
204
|
+
"Proceeding because TRUEPPM_ALLOW_UNENCRYPTED_DB=true is set; "
|
|
205
|
+
"ensure TLS is enforced at the network layer."
|
|
206
|
+
)
|
|
207
|
+
else:
|
|
208
|
+
raise RuntimeError(
|
|
209
|
+
"Refusing to start: DATABASE_URL does not include sslmode=require "
|
|
210
|
+
"in a non-DEBUG environment; database connections may use "
|
|
211
|
+
"unencrypted transport. Set sslmode=require in DATABASE_URL, or set "
|
|
212
|
+
"TRUEPPM_ALLOW_UNENCRYPTED_DB=true if TLS is enforced at the network "
|
|
213
|
+
"layer."
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Refuse to boot on a placeholder datastore credential (#3176). Compose's
|
|
217
|
+
# ``${DB_PASSWORD:?...}`` only asserts the variable is non-empty, and .env.example
|
|
218
|
+
# shipped ``change-me`` — so the documented copy-paste path produced a deploy in which
|
|
219
|
+
# SECRET_KEY, the attachment backend, the integration key and sslmode were all validated
|
|
220
|
+
# and the database/cache passwords were not. Same import-time enforcement as the guards
|
|
221
|
+
# above, for the same reason: gunicorn/asgi workers never run ``manage.py check``.
|
|
222
|
+
_service_cred_errors = validate_service_credentials(
|
|
223
|
+
DATABASES["default"].get("PASSWORD"),
|
|
224
|
+
urllib.parse.urlparse(REDIS_URL or "").password,
|
|
225
|
+
debug=DEBUG,
|
|
226
|
+
)
|
|
227
|
+
if _service_cred_errors:
|
|
228
|
+
raise RuntimeError(_REFUSING_TO_START + "; ".join(str(e.msg) for e in _service_cred_errors))
|
trueppm_api/urls.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Root URL configuration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from django.conf import settings
|
|
6
|
+
from django.contrib import admin
|
|
7
|
+
from django.urls import include, path
|
|
8
|
+
from drf_spectacular.utils import extend_schema, inline_serializer
|
|
9
|
+
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerSplitView
|
|
10
|
+
from rest_framework import serializers
|
|
11
|
+
from rest_framework.decorators import api_view, permission_classes
|
|
12
|
+
from rest_framework.permissions import AllowAny
|
|
13
|
+
from rest_framework.request import Request
|
|
14
|
+
from rest_framework.response import Response
|
|
15
|
+
|
|
16
|
+
from trueppm_api.apps.observability.views import readyz
|
|
17
|
+
from trueppm_api.core.auth_views import (
|
|
18
|
+
CookieTokenLogoutView,
|
|
19
|
+
CookieTokenObtainPairView,
|
|
20
|
+
CookieTokenRefreshView,
|
|
21
|
+
)
|
|
22
|
+
from trueppm_api.core.password_reset import (
|
|
23
|
+
PasswordResetConfirmView,
|
|
24
|
+
PasswordResetRequestView,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@extend_schema(
|
|
29
|
+
summary="Liveness probe",
|
|
30
|
+
description=(
|
|
31
|
+
'Returns HTTP 200 with `{"status": "ok"}` when the API process is running. '
|
|
32
|
+
"No authentication required. Use for Kubernetes liveness/readiness probes."
|
|
33
|
+
),
|
|
34
|
+
responses={200: inline_serializer("HealthResponse", {"status": serializers.CharField()})},
|
|
35
|
+
auth=[],
|
|
36
|
+
tags=["meta"],
|
|
37
|
+
)
|
|
38
|
+
@api_view(["GET"])
|
|
39
|
+
@permission_classes([AllowAny])
|
|
40
|
+
def health(_request: Request) -> Response:
|
|
41
|
+
return Response({"status": "ok"})
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@extend_schema(
|
|
45
|
+
summary="Running edition",
|
|
46
|
+
description=(
|
|
47
|
+
'Returns `{"edition": "community" | "enterprise"}` from the TRUEPPM_EDITION '
|
|
48
|
+
"Django setting. No authentication required. The React shell calls this once "
|
|
49
|
+
"at startup to decide the post-login redirect target (ADR-0029, ADR-0030)."
|
|
50
|
+
),
|
|
51
|
+
responses={
|
|
52
|
+
200: inline_serializer(
|
|
53
|
+
"EditionResponse",
|
|
54
|
+
{
|
|
55
|
+
"edition": serializers.CharField(),
|
|
56
|
+
"version": serializers.CharField(),
|
|
57
|
+
"build_sha": serializers.CharField(allow_blank=True),
|
|
58
|
+
},
|
|
59
|
+
)
|
|
60
|
+
},
|
|
61
|
+
auth=[],
|
|
62
|
+
tags=["meta"],
|
|
63
|
+
)
|
|
64
|
+
@api_view(["GET"])
|
|
65
|
+
@permission_classes([AllowAny])
|
|
66
|
+
def edition(request: Request) -> Response:
|
|
67
|
+
"""Return the running edition — community or enterprise.
|
|
68
|
+
|
|
69
|
+
Public endpoint (no auth required). The React shell calls this once at
|
|
70
|
+
startup to decide the post-login redirect target (ADR-0029, ADR-0030).
|
|
71
|
+
The value is controlled by the TRUEPPM_EDITION Django setting, which the
|
|
72
|
+
enterprise Helm chart sets to "enterprise".
|
|
73
|
+
"""
|
|
74
|
+
return Response(
|
|
75
|
+
{
|
|
76
|
+
"edition": settings.TRUEPPM_EDITION,
|
|
77
|
+
# Build identity (#2392): a bug report has to name the exact build it
|
|
78
|
+
# came from, and this is the one endpoint the shell already calls at
|
|
79
|
+
# startup without auth. `build_sha` is empty on a source checkout.
|
|
80
|
+
"version": settings.TRUEPPM_VERSION,
|
|
81
|
+
"build_sha": settings.TRUEPPM_BUILD_SHA,
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# Versioned prefix every app router mounts under.
|
|
87
|
+
_API_V1_PREFIX = "api/v1/"
|
|
88
|
+
|
|
89
|
+
urlpatterns = [
|
|
90
|
+
path("api/v1/health/", health, name="health"),
|
|
91
|
+
# Dependency-aware readiness probe (#1894). Unauthenticated like /health/ so
|
|
92
|
+
# kubelet can call it, but returns 503 when the DB or cache is unreachable —
|
|
93
|
+
# unlike /health/, which is a shallow process-liveness check.
|
|
94
|
+
path("api/v1/readyz", readyz, name="readyz"),
|
|
95
|
+
path("api/v1/edition/", edition, name="edition"),
|
|
96
|
+
path("admin/", admin.site.urls),
|
|
97
|
+
# OpenAPI schema and interactive docs. The *split* Swagger view (not the
|
|
98
|
+
# default inline one) is required by our strict CSP: it delivers the UI
|
|
99
|
+
# bootstrap as a separate same-origin JS request instead of an inline
|
|
100
|
+
# <script>, which script-src 'self' would otherwise block (the page rendered
|
|
101
|
+
# blank — #1603). Asset bundles are served from drf-spectacular-sidecar
|
|
102
|
+
# ('self') via SWAGGER_UI_DIST=SIDECAR in settings.
|
|
103
|
+
path("api/schema/", SpectacularAPIView.as_view(), name="schema"),
|
|
104
|
+
path("api/docs/", SpectacularSwaggerSplitView.as_view(url_name="schema"), name="swagger-ui"),
|
|
105
|
+
path(
|
|
106
|
+
"api/schema/swagger-ui/",
|
|
107
|
+
SpectacularSwaggerSplitView.as_view(url_name="schema"),
|
|
108
|
+
name="swagger-ui-compat",
|
|
109
|
+
),
|
|
110
|
+
# JWT auth endpoints (#897). Login returns the access token in the body and
|
|
111
|
+
# sets the refresh token in an httpOnly cookie; refresh reads that cookie;
|
|
112
|
+
# logout clears it (and blacklists the token if the blacklist app is present).
|
|
113
|
+
path("api/v1/auth/token/", CookieTokenObtainPairView.as_view(), name="token_obtain_pair"),
|
|
114
|
+
path("api/v1/auth/token/refresh/", CookieTokenRefreshView.as_view(), name="token_refresh"),
|
|
115
|
+
path("api/v1/auth/logout/", CookieTokenLogoutView.as_view(), name="token_logout"),
|
|
116
|
+
# Self-service password reset (#765, ADR-0209). Both AllowAny + throttled with a
|
|
117
|
+
# dedicated "password_reset" scope. Request always returns 200 (no user
|
|
118
|
+
# enumeration); confirm validates a stateless token, sets the password, and
|
|
119
|
+
# revokes all of the account's other sessions.
|
|
120
|
+
path(
|
|
121
|
+
"api/v1/auth/password/reset/",
|
|
122
|
+
PasswordResetRequestView.as_view(),
|
|
123
|
+
name="password_reset",
|
|
124
|
+
),
|
|
125
|
+
path(
|
|
126
|
+
"api/v1/auth/password/reset/confirm/",
|
|
127
|
+
PasswordResetConfirmView.as_view(),
|
|
128
|
+
name="password_reset_confirm",
|
|
129
|
+
),
|
|
130
|
+
# Versioned API
|
|
131
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.access.urls")),
|
|
132
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.projects.urls")),
|
|
133
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.resources.urls")),
|
|
134
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.scheduling.urls")),
|
|
135
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.sync.urls")),
|
|
136
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.history.urls")),
|
|
137
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.msproject.urls")),
|
|
138
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.jiraimport.urls")),
|
|
139
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.csvimport.urls")),
|
|
140
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.webhooks.urls")),
|
|
141
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.taskruns.urls")),
|
|
142
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.notifications.urls")),
|
|
143
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.integrations.urls")),
|
|
144
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.observability.urls")),
|
|
145
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.workspace.urls")),
|
|
146
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.teams.urls")),
|
|
147
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.timetracking.urls")),
|
|
148
|
+
# Basic SSO — OIDC relying party (ADR-0187). Flow endpoints under
|
|
149
|
+
# auth/oidc/*, admin config under workspace/sso/*.
|
|
150
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.sso.urls")),
|
|
151
|
+
# Team-readable agent-action audit log (ADR-0112 RC1, #1805).
|
|
152
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.agents.urls")),
|
|
153
|
+
]
|