trueppm-api 0.4.0__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 +27 -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 +346 -0
- trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
- trueppm_api/apps/access/management/commands/revoke_api_tokens.py +483 -0
- trueppm_api/apps/access/migrations/0001_initial.py +61 -0
- trueppm_api/apps/access/migrations/0001_squashed_0012_programmembership_role_title.py +184 -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_squashed_0019_release_0_4.py +302 -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 +143 -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/minter_role.py +38 -0
- trueppm_api/apps/access/models.py +588 -0
- trueppm_api/apps/access/permissions.py +3263 -0
- trueppm_api/apps/access/serializers.py +1007 -0
- trueppm_api/apps/access/services.py +878 -0
- trueppm_api/apps/access/signals.py +171 -0
- trueppm_api/apps/access/tasks.py +50 -0
- trueppm_api/apps/access/throttles.py +96 -0
- trueppm_api/apps/access/urls.py +191 -0
- trueppm_api/apps/access/views.py +1965 -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/0001_squashed_0003_release_0_4.py +344 -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 +109 -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/0001_squashed_0003_release_0_4.py +90 -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 +459 -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 +178 -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 +863 -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 +597 -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_squashed_0014_release_0_4.py +225 -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/0014_tasklink_created_by.py +27 -0
- trueppm_api/apps/integrations/migrations/__init__.py +0 -0
- trueppm_api/apps/integrations/models.py +635 -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 +528 -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 +734 -0
- trueppm_api/apps/integrations/throttles.py +165 -0
- trueppm_api/apps/integrations/urls.py +77 -0
- trueppm_api/apps/integrations/views.py +1158 -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 +280 -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 +551 -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 +1276 -0
- trueppm_api/apps/msproject/serializers.py +136 -0
- trueppm_api/apps/msproject/services.py +58 -0
- trueppm_api/apps/msproject/tasks.py +439 -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 +462 -0
- trueppm_api/apps/notifications/email_backend.py +350 -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 +299 -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_squashed_0011_release_0_4.py +204 -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 +1148 -0
- trueppm_api/apps/notifications/receivers.py +155 -0
- trueppm_api/apps/notifications/schema_migrations.py +56 -0
- trueppm_api/apps/notifications/serializers.py +1094 -0
- trueppm_api/apps/notifications/services.py +1489 -0
- trueppm_api/apps/notifications/tasks.py +762 -0
- trueppm_api/apps/notifications/throttles.py +98 -0
- trueppm_api/apps/notifications/urls.py +97 -0
- trueppm_api/apps/notifications/views.py +919 -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 +566 -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 +993 -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 +537 -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_squashed_0009_release_0_4.py +128 -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 +541 -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 +458 -0
- trueppm_api/apps/projects/backfill.py +459 -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 +212 -0
- trueppm_api/apps/projects/config_notice.py +1330 -0
- trueppm_api/apps/projects/custom_field_values.py +266 -0
- trueppm_api/apps/projects/decisions_services.py +132 -0
- trueppm_api/apps/projects/decisions_views.py +97 -0
- trueppm_api/apps/projects/estimation_scale.py +126 -0
- trueppm_api/apps/projects/export_bundle.py +423 -0
- trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4278 -0
- trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2001 -0
- trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1827 -0
- trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1909 -0
- trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1410 -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/key_views.py +468 -0
- trueppm_api/apps/projects/keys.py +205 -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 +514 -0
- trueppm_api/apps/projects/management/commands/seed_capacity.py +406 -0
- trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +192 -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/migration_helpers.py +75 -0
- trueppm_api/apps/projects/migrations/0001_initial.py +208 -0
- trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5659 -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 +59 -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 +75 -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 +76 -0
- trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -0
- trueppm_api/apps/projects/migrations/0106_squashed_0158_release_0_4.py +2357 -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/0154_object_keys.py +184 -0
- trueppm_api/apps/projects/migrations/0155_task_milestone_at_day_end.py +20 -0
- trueppm_api/apps/projects/migrations/0156_program_import_job_replaced_project_ids.py +17 -0
- trueppm_api/apps/projects/migrations/0157_forecastsnapshot_cpm_finish_at_day_start.py +17 -0
- trueppm_api/apps/projects/migrations/0158_baselinetask_finish_at_day_start.py +17 -0
- trueppm_api/apps/projects/migrations/__init__.py +0 -0
- trueppm_api/apps/projects/models.py +9209 -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 +903 -0
- trueppm_api/apps/projects/program_schedule.py +767 -0
- trueppm_api/apps/projects/program_views.py +3445 -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 +492 -0
- trueppm_api/apps/projects/schemas/seed_v2.json +891 -0
- trueppm_api/apps/projects/seed/__init__.py +61 -0
- trueppm_api/apps/projects/seed/demo_landing_overlay.py +532 -0
- trueppm_api/apps/projects/seed/exporter.py +1717 -0
- trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
- trueppm_api/apps/projects/seed/importer.py +2764 -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 +128 -0
- trueppm_api/apps/projects/seed/replay.py +1695 -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 +1169 -0
- trueppm_api/apps/projects/serializers.py +12904 -0
- trueppm_api/apps/projects/services.py +7525 -0
- trueppm_api/apps/projects/share_serializers.py +142 -0
- trueppm_api/apps/projects/share_services.py +373 -0
- trueppm_api/apps/projects/share_views.py +395 -0
- trueppm_api/apps/projects/sharing_settings.py +187 -0
- trueppm_api/apps/projects/signal_privacy_services.py +910 -0
- trueppm_api/apps/projects/signal_privacy_views.py +485 -0
- trueppm_api/apps/projects/signals.py +132 -0
- trueppm_api/apps/projects/slip_conflict.py +288 -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 +1244 -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 +1979 -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 +261 -0
- trueppm_api/apps/projects/template_views.py +829 -0
- trueppm_api/apps/projects/throttles.py +311 -0
- trueppm_api/apps/projects/urls.py +1016 -0
- trueppm_api/apps/projects/utilization.py +909 -0
- trueppm_api/apps/projects/views.py +21225 -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/0014_squashed_0015_release_0_4.py +47 -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 +1752 -0
- trueppm_api/apps/scheduling/__init__.py +0 -0
- trueppm_api/apps/scheduling/apps.py +153 -0
- trueppm_api/apps/scheduling/calendars.py +144 -0
- trueppm_api/apps/scheduling/deadletter.py +91 -0
- trueppm_api/apps/scheduling/finish_reading.py +198 -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/mc_admission.py +181 -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/0010_squashed_0016_release_0_4.py +106 -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/0015_projectforecastsnapshot_cpm_finish_at_day_start.py +17 -0
- trueppm_api/apps/scheduling/migrations/0016_montecarlorun_finish_readings.py +32 -0
- trueppm_api/apps/scheduling/migrations/__init__.py +0 -0
- trueppm_api/apps/scheduling/models.py +542 -0
- trueppm_api/apps/scheduling/receivers.py +160 -0
- trueppm_api/apps/scheduling/risk_premium.py +334 -0
- trueppm_api/apps/scheduling/serializers.py +723 -0
- trueppm_api/apps/scheduling/services.py +1251 -0
- trueppm_api/apps/scheduling/signals.py +56 -0
- trueppm_api/apps/scheduling/tasks.py +2248 -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 +2908 -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 +446 -0
- trueppm_api/apps/sso/services.py +1473 -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 +369 -0
- trueppm_api/apps/sync/conflict.py +262 -0
- trueppm_api/apps/sync/consumers.py +548 -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/0003_squashed_0005_release_0_4.py +77 -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/0006_boardevent_per_project_seq.py +77 -0
- trueppm_api/apps/sync/migrations/__init__.py +0 -0
- trueppm_api/apps/sync/models.py +272 -0
- trueppm_api/apps/sync/pagination.py +231 -0
- trueppm_api/apps/sync/sequence.py +488 -0
- trueppm_api/apps/sync/serializers.py +688 -0
- trueppm_api/apps/sync/tasks.py +260 -0
- trueppm_api/apps/sync/throttles.py +175 -0
- trueppm_api/apps/sync/upload.py +556 -0
- trueppm_api/apps/sync/urls.py +17 -0
- trueppm_api/apps/sync/views.py +957 -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 +159 -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/0004_backfill_default_teams_on_upgrade.py +49 -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 +440 -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/0001_squashed_0004_release_0_4.py +167 -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 +211 -0
- trueppm_api/apps/timetracking/urls.py +47 -0
- trueppm_api/apps/timetracking/views.py +597 -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 +279 -0
- trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
- trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +249 -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 +100 -0
- trueppm_api/apps/webhooks/migrations/0010_alter_webhook_disabled_at.py +21 -0
- trueppm_api/apps/webhooks/migrations/0011_webhookeditor.py +48 -0
- trueppm_api/apps/webhooks/migrations/0012_alter_webhook_disabled_at.py +21 -0
- trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
- trueppm_api/apps/webhooks/models.py +437 -0
- trueppm_api/apps/webhooks/serializers.py +326 -0
- trueppm_api/apps/webhooks/tasks.py +680 -0
- trueppm_api/apps/webhooks/urls.py +22 -0
- trueppm_api/apps/webhooks/views.py +533 -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 +371 -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 +254 -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_squashed_0027_release_0_4.py +199 -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 +967 -0
- trueppm_api/apps/workspace/signals.py +37 -0
- trueppm_api/apps/workspace/tasks.py +616 -0
- trueppm_api/apps/workspace/urls.py +113 -0
- trueppm_api/apps/workspace/views.py +1581 -0
- trueppm_api/asgi.py +49 -0
- trueppm_api/celery.py +40 -0
- trueppm_api/core/__init__.py +0 -0
- trueppm_api/core/admin_site.py +206 -0
- trueppm_api/core/auth_views.py +934 -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/demo_read_only.py +314 -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 +1221 -0
- trueppm_api/core/password_reset.py +513 -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 +1034 -0
- trueppm_api/core/storage_config.py +105 -0
- trueppm_api/core/text_decode.py +168 -0
- trueppm_api/core/throttling.py +514 -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/core/worker_selfheal.py +205 -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 +2605 -0
- trueppm_api/settings/dev.py +296 -0
- trueppm_api/settings/prod.py +265 -0
- trueppm_api/urls.py +217 -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.0.dist-info/METADATA +273 -0
- trueppm_api-0.4.0.dist-info/RECORD +799 -0
- trueppm_api-0.4.0.dist-info/WHEEL +4 -0
- trueppm_api-0.4.0.dist-info/licenses/LICENSE +201 -0
- trueppm_api-0.4.0.dist-info/licenses/NOTICE +7 -0
- trueppm_api-0.4.0.dist-info/licenses/NOTICES +104 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/GPL-3.0.txt +674 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/LGPL-3.0.txt +165 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/MPL-2.0.txt +373 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Data-backfill helpers for the webhooks app migrations.
|
|
2
|
+
|
|
3
|
+
These functions are extracted from migration files so tests can import them
|
|
4
|
+
without coupling to migration file names, which break on squash (CLAUDE.md rule 3).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def backfill_sequence_numbers(apps: Any, schema_editor: Any) -> None:
|
|
13
|
+
"""Assign monotonic per-subscription sequence numbers to existing deliveries (#664).
|
|
14
|
+
|
|
15
|
+
Orders deliveries by created_at (then id as a stable tie-break) so the
|
|
16
|
+
historical sequence reflects the order deliveries were actually created.
|
|
17
|
+
Persists the counter on the Webhook so new deliveries continue the sequence
|
|
18
|
+
rather than restarting from 1.
|
|
19
|
+
"""
|
|
20
|
+
Webhook = apps.get_model("webhooks", "Webhook")
|
|
21
|
+
WebhookDelivery = apps.get_model("webhooks", "WebhookDelivery")
|
|
22
|
+
|
|
23
|
+
for webhook in Webhook.objects.all():
|
|
24
|
+
seq = 0
|
|
25
|
+
# Order by created_at (then id as a stable tie-break) so the historical
|
|
26
|
+
# sequence reflects the order deliveries were actually created.
|
|
27
|
+
deliveries = WebhookDelivery.objects.filter(webhook=webhook).order_by("created_at", "id")
|
|
28
|
+
for delivery in deliveries:
|
|
29
|
+
seq += 1
|
|
30
|
+
WebhookDelivery.objects.filter(pk=delivery.pk).update(sequence_number=seq)
|
|
31
|
+
# Persist the counter so deliveries created after the migration continue
|
|
32
|
+
# the same sequence rather than restarting from 1.
|
|
33
|
+
if seq > 0:
|
|
34
|
+
Webhook.objects.filter(pk=webhook.pk).update(delivery_sequence=seq)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def encrypt_webhook_secrets(apps: Any, schema_editor: Any) -> None:
|
|
38
|
+
"""Fernet-encrypt every existing plaintext ``Webhook.secret`` (#2885).
|
|
39
|
+
|
|
40
|
+
Runs between the ``secret_ciphertext`` AddField and the ``secret`` RemoveField,
|
|
41
|
+
so it is the only window in which both columns exist. A row whose secret is
|
|
42
|
+
empty (impossible through the API, but the historical column was not
|
|
43
|
+
constrained) is left with an empty ciphertext rather than crashing the
|
|
44
|
+
migration — the delivery task refuses to sign with an empty key and records the
|
|
45
|
+
delivery as a permanent failure, which surfaces the bad row to an operator
|
|
46
|
+
instead of silently signing with ``b""``.
|
|
47
|
+
|
|
48
|
+
Reads ``.iterator()`` rather than materializing the table: this runs inside the
|
|
49
|
+
migration's transaction on a table that is unbounded in principle.
|
|
50
|
+
"""
|
|
51
|
+
from trueppm_api.apps.integrations.encryption import encrypt_secret
|
|
52
|
+
|
|
53
|
+
Webhook = apps.get_model("webhooks", "Webhook")
|
|
54
|
+
for webhook in Webhook.objects.all().iterator(chunk_size=500):
|
|
55
|
+
plaintext = webhook.secret or ""
|
|
56
|
+
Webhook.objects.filter(pk=webhook.pk).update(
|
|
57
|
+
secret_ciphertext=encrypt_secret(plaintext) if plaintext else b""
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def reverse_encrypt_webhook_secrets(apps: Any, schema_editor: Any) -> None:
|
|
62
|
+
"""Decrypt ciphertexts back into the plaintext column (migration reverse).
|
|
63
|
+
|
|
64
|
+
Reversing re-creates the vulnerability the forward migration closed, so it
|
|
65
|
+
exists only to keep the migration reversible for local development. A
|
|
66
|
+
ciphertext that cannot be decrypted (the key was rotated) leaves the plaintext
|
|
67
|
+
column empty rather than aborting the whole reverse.
|
|
68
|
+
"""
|
|
69
|
+
from trueppm_api.apps.integrations.encryption import (
|
|
70
|
+
CredentialEncryptionError,
|
|
71
|
+
decrypt_secret,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
Webhook = apps.get_model("webhooks", "Webhook")
|
|
75
|
+
for webhook in Webhook.objects.all().iterator(chunk_size=500):
|
|
76
|
+
if not webhook.secret_ciphertext:
|
|
77
|
+
continue
|
|
78
|
+
try:
|
|
79
|
+
plaintext = decrypt_secret(bytes(webhook.secret_ciphertext))
|
|
80
|
+
except CredentialEncryptionError:
|
|
81
|
+
continue
|
|
82
|
+
Webhook.objects.filter(pk=webhook.pk).update(secret=plaintext)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def reverse_backfill(apps: Any, schema_editor: Any) -> None:
|
|
86
|
+
"""Reset all sequence counters back to zero (migration reverse operation)."""
|
|
87
|
+
Webhook = apps.get_model("webhooks", "Webhook")
|
|
88
|
+
WebhookDelivery = apps.get_model("webhooks", "WebhookDelivery")
|
|
89
|
+
WebhookDelivery.objects.all().update(sequence_number=0)
|
|
90
|
+
Webhook.objects.all().update(delivery_sequence=0)
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""Webhook dispatch helper — called from transaction.on_commit callbacks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Any
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
|
|
9
|
+
import redis as redis_lib
|
|
10
|
+
from kombu.exceptions import ( # type: ignore[import-untyped]
|
|
11
|
+
OperationalError as KombuOperationalError,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
# Transient broker/connection errors that must not bubble out of dispatch —
|
|
17
|
+
# the delivery row stays PENDING and drain_webhook_queue retries it. Narrow
|
|
18
|
+
# on purpose so serialization or programming bugs are not silently swallowed.
|
|
19
|
+
_BROKER_ERRORS = (KombuOperationalError, ConnectionError, redis_lib.ConnectionError)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def build_delivery_body(
|
|
23
|
+
webhook: Any,
|
|
24
|
+
event_type: str,
|
|
25
|
+
payload: dict[str, Any],
|
|
26
|
+
sequence: int,
|
|
27
|
+
project_id: str | None = None,
|
|
28
|
+
) -> dict[str, Any]:
|
|
29
|
+
"""Render ``payload`` for ``webhook``'s format and add the ``_meta`` envelope.
|
|
30
|
+
|
|
31
|
+
The single place a delivery body is constructed. It exists because the
|
|
32
|
+
"Send test" action used to build its ping inline as
|
|
33
|
+
``{"event": "ping", "webhook_id": ...}`` — skipping the renderer entirely and
|
|
34
|
+
omitting ``_meta`` — while the real dispatch path did both (#2884). A
|
|
35
|
+
``slack``-format subscription (the modal's default) therefore rejected the test
|
|
36
|
+
body with 400 ``invalid_payload``, so the admin's only diagnostic tool reported
|
|
37
|
+
success on a guaranteed failure, and the ping still consumed a sequence number
|
|
38
|
+
with no ``_meta.sequence`` to match, breaking the documented "always identical"
|
|
39
|
+
invariant and firing phantom gap alarms on every Test click.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
webhook: The subscription being delivered to (supplies ``format``).
|
|
43
|
+
event_type: The event type, including the reserved ``ping``.
|
|
44
|
+
payload: The domain payload to render.
|
|
45
|
+
sequence: The already-allocated per-subscription sequence number. Passed in
|
|
46
|
+
rather than allocated here because ``render()`` runs before the delivery
|
|
47
|
+
row exists, and the stored row must equal the wire body (ADR-0083).
|
|
48
|
+
project_id: Project the event belongs to, when known. Falls back to the
|
|
49
|
+
webhook's own project so a program-scoped ping still carries a scope.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
The exact dict to persist as ``WebhookDelivery.payload`` and POST verbatim.
|
|
53
|
+
"""
|
|
54
|
+
from trueppm_api.apps.integrations.registry import (
|
|
55
|
+
OUTGOING_CHANNEL_PROVIDERS,
|
|
56
|
+
OutgoingChannelEvent,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
scope_id = project_id or (str(webhook.project_id) if webhook.project_id else "")
|
|
60
|
+
event = OutgoingChannelEvent(event_type=event_type, project_id=scope_id, payload=payload)
|
|
61
|
+
# An un-registered format (e.g. an Enterprise provider after a downgrade)
|
|
62
|
+
# degrades to the raw payload rather than 500ing — matches
|
|
63
|
+
# ProviderRegistry.get() returning None by design.
|
|
64
|
+
provider_cls = OUTGOING_CHANNEL_PROVIDERS.get(webhook.format)
|
|
65
|
+
rendered = provider_cls().render(event) if provider_cls is not None else payload
|
|
66
|
+
# ``_meta`` is a reserved top-level namespace (ADR-0089): the generic body is a
|
|
67
|
+
# flat domain dict, so a bare ``sequence`` key could collide with a future event
|
|
68
|
+
# field, and Slack ignores an unknown ``_meta`` key — one uniform rule covers
|
|
69
|
+
# every format. A fresh dict so the shared rendered/payload object is never
|
|
70
|
+
# mutated across the fan-out.
|
|
71
|
+
return {**rendered, "_meta": {"sequence": sequence}}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _drop_webhooks_without_live_member_owner(webhooks: list[Any], project_id: str) -> list[Any]:
|
|
75
|
+
"""Keep a webhook only when its registrant is a live Admin+ member of the firing project.
|
|
76
|
+
|
|
77
|
+
Originally written for program-scoped webhooks (#4310): a program-scoped
|
|
78
|
+
webhook fires for events on every member project, but the program grant is
|
|
79
|
+
not a read grant on the project, so its registrant must hold live
|
|
80
|
+
``ProjectMembership`` on the project the event came from. #4325 extends the
|
|
81
|
+
identical check to project-scoped webhooks — the ADR-0161 amendment's "project-
|
|
82
|
+
scoped webhooks are unchanged" reasoning was never actually true for a
|
|
83
|
+
project-scoped webhook's own registrant losing membership on that very
|
|
84
|
+
project, only for the cross-project *program* grant the amendment was about.
|
|
85
|
+
A project-scoped webhook's only possible firing project is the one it is
|
|
86
|
+
scoped to, so the same ``project_id`` the caller passes in is also the right
|
|
87
|
+
membership check for it — no separate query shape needed.
|
|
88
|
+
|
|
89
|
+
#4330 adds a role floor on top of that same ``ProjectMembership`` row:
|
|
90
|
+
membership alone (any role) used to be enough to keep receiving events, but
|
|
91
|
+
the delivery-log read endpoint (``ProjectWebhookViewSet.get_permissions``)
|
|
92
|
+
has always been Admin-only — payloads are documented information disclosure
|
|
93
|
+
beyond plain project membership (#903). A registrant demoted to Viewer or
|
|
94
|
+
Member (not removed) kept receiving the same payloads via dispatch that they
|
|
95
|
+
could no longer read back through the delivery log, which is the gap #4330
|
|
96
|
+
closes: dispatch now requires ``role >= Role.ADMIN`` on that live
|
|
97
|
+
membership, matching ``IsProjectAdmin`` (the project-scoped read gate)
|
|
98
|
+
exactly so the two cannot drift again.
|
|
99
|
+
|
|
100
|
+
This project-role check runs identically for program-scoped webhooks,
|
|
101
|
+
because the membership source here is always the firing project, never the
|
|
102
|
+
program — a program grant was never a project-read grant (#4310), so a
|
|
103
|
+
program-scoped registrant has always needed live membership on the
|
|
104
|
+
specific member project an event comes from, in addition to (not instead
|
|
105
|
+
of) the program-level check in
|
|
106
|
+
``_drop_program_webhooks_without_live_program_admin_owner`` below, which
|
|
107
|
+
#4330 also adds to close the matching gap on the *program* axis.
|
|
108
|
+
|
|
109
|
+
Membership is read here, at dispatch time, so a removed member, or one
|
|
110
|
+
demoted below Admin, stops receiving events on the next write rather than
|
|
111
|
+
when the subscription is edited.
|
|
112
|
+
|
|
113
|
+
Fails closed: a webhook whose ``created_by`` is NULL (the registrant was
|
|
114
|
+
deleted — ``SET_NULL``) or whose registrant is deactivated is dropped.
|
|
115
|
+
|
|
116
|
+
One membership query per event, not per webhook: all distinct registrants
|
|
117
|
+
of the candidate webhooks (project- and program-scoped alike) are resolved
|
|
118
|
+
together against ``project_id``.
|
|
119
|
+
"""
|
|
120
|
+
from trueppm_api.apps.access.models import ProjectMembership, Role
|
|
121
|
+
|
|
122
|
+
owner_ids = {w.created_by_id for w in webhooks if w.created_by_id}
|
|
123
|
+
if not owner_ids:
|
|
124
|
+
live_owners: set[Any] = set()
|
|
125
|
+
else:
|
|
126
|
+
live_owners = set(
|
|
127
|
+
ProjectMembership.live()
|
|
128
|
+
.filter(
|
|
129
|
+
project_id=project_id,
|
|
130
|
+
user_id__in=owner_ids,
|
|
131
|
+
user__is_active=True,
|
|
132
|
+
role__gte=Role.ADMIN,
|
|
133
|
+
)
|
|
134
|
+
.values_list("user_id", flat=True)
|
|
135
|
+
)
|
|
136
|
+
return [w for w in webhooks if w.created_by_id in live_owners]
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _drop_program_webhooks_without_live_program_admin_owner(
|
|
140
|
+
webhooks: list[Any], program_id: UUID | None
|
|
141
|
+
) -> list[Any]:
|
|
142
|
+
"""Keep a program-scoped webhook only when its registrant also holds a live
|
|
143
|
+
Admin+ ``ProgramMembership`` on the program itself (#4330).
|
|
144
|
+
|
|
145
|
+
``_drop_webhooks_without_live_member_owner`` above gates every webhook,
|
|
146
|
+
project- and program-scoped alike, on the registrant's role on the FIRING
|
|
147
|
+
PROJECT — necessary because a program grant was never a project-read grant
|
|
148
|
+
(#4310). But the program-scoped delivery-log read gate
|
|
149
|
+
(``ProgramWebhookViewSet._check_delivery_log_access`` plus ``IsProgramAdmin``
|
|
150
|
+
in ``get_permissions``) is keyed on the registrant's *program* role, not
|
|
151
|
+
their project role: it requires them to still be the webhook's creator AND
|
|
152
|
+
still hold Program Admin+ on the program. Without this second check, a
|
|
153
|
+
creator demoted to Program Viewer — while remaining Project Admin on the
|
|
154
|
+
firing project, which is all the function above verifies — would keep
|
|
155
|
+
receiving full program-webhook payloads via dispatch that the read gate
|
|
156
|
+
would already refuse them, the same class of gap #4330 closed on the
|
|
157
|
+
project axis. A program-scoped webhook's dispatch eligibility is therefore
|
|
158
|
+
the AND of both functions, not a replacement of one by the other: the
|
|
159
|
+
project-role check is still required (a program grant alone has never been
|
|
160
|
+
a project-read grant), and this program-role check is now required too.
|
|
161
|
+
|
|
162
|
+
Project-scoped webhooks (``program_id`` is ``None`` on the webhook row) are
|
|
163
|
+
untouched — they pass straight through regardless of this project's
|
|
164
|
+
resolved ``program_id``.
|
|
165
|
+
|
|
166
|
+
One membership query per event, not per webhook, and skipped entirely
|
|
167
|
+
(zero extra queries) whenever no program-scoped webhook survives the
|
|
168
|
+
project-role filter — the common case for a project with no program, or
|
|
169
|
+
whose program-scoped webhooks already failed that first check. All
|
|
170
|
+
program-scoped candidates in a single ``dispatch_webhooks`` call share the
|
|
171
|
+
same ``program_id`` (the queryset that builds ``webhooks`` filters on
|
|
172
|
+
exactly one project's resolved program), so one lookup against that single
|
|
173
|
+
``program_id`` resolves every program-scoped registrant together.
|
|
174
|
+
"""
|
|
175
|
+
from trueppm_api.apps.access.models import ProgramMembership, Role
|
|
176
|
+
|
|
177
|
+
program_webhooks = [w for w in webhooks if w.program_id is not None]
|
|
178
|
+
if not program_webhooks or program_id is None:
|
|
179
|
+
return webhooks
|
|
180
|
+
|
|
181
|
+
owner_ids = {w.created_by_id for w in program_webhooks if w.created_by_id}
|
|
182
|
+
if not owner_ids:
|
|
183
|
+
live_program_admins: set[Any] = set()
|
|
184
|
+
else:
|
|
185
|
+
live_program_admins = set(
|
|
186
|
+
ProgramMembership.live()
|
|
187
|
+
.filter(
|
|
188
|
+
program_id=program_id,
|
|
189
|
+
user_id__in=owner_ids,
|
|
190
|
+
user__is_active=True,
|
|
191
|
+
role__gte=Role.ADMIN,
|
|
192
|
+
)
|
|
193
|
+
.values_list("user_id", flat=True)
|
|
194
|
+
)
|
|
195
|
+
return [w for w in webhooks if w.program_id is None or w.created_by_id in live_program_admins]
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def dispatch_webhooks(project_id: str, event_type: str, payload: dict[str, Any]) -> None:
|
|
199
|
+
"""Query matching active webhooks and enqueue a delivery task for each.
|
|
200
|
+
|
|
201
|
+
This function MUST be called inside a ``transaction.on_commit`` callback
|
|
202
|
+
(same as ``broadcast_board_event``) so that delivery is never enqueued
|
|
203
|
+
for a rolled-back mutation.
|
|
204
|
+
|
|
205
|
+
Fans out to BOTH project-scoped webhooks (events on this project) and
|
|
206
|
+
program-scoped webhooks (events on any project within the program that
|
|
207
|
+
owns this project) per ADR-0076. The two queries are combined with a
|
|
208
|
+
single ``Q`` union so the database performs the OR in one round-trip.
|
|
209
|
+
Either scope is then dropped if its registrant lacks a live Admin+
|
|
210
|
+
membership on this project (#4310, #4325, #4330) — see
|
|
211
|
+
``_drop_webhooks_without_live_member_owner``. A program-scoped webhook is
|
|
212
|
+
further dropped if its registrant also lacks a live Admin+ membership on
|
|
213
|
+
the program itself (#4330) — see
|
|
214
|
+
``_drop_program_webhooks_without_live_program_admin_owner``.
|
|
215
|
+
"""
|
|
216
|
+
from django.db.models import Q
|
|
217
|
+
|
|
218
|
+
from trueppm_api.apps.projects.models import Project
|
|
219
|
+
from trueppm_api.apps.webhooks.models import (
|
|
220
|
+
Webhook,
|
|
221
|
+
WebhookDelivery,
|
|
222
|
+
_next_delivery_sequence,
|
|
223
|
+
)
|
|
224
|
+
from trueppm_api.apps.webhooks.tasks import deliver_webhook
|
|
225
|
+
|
|
226
|
+
# Resolve the project's program (if any) so program-scoped webhooks fire
|
|
227
|
+
# for events on any of the program's projects. One extra SELECT for the
|
|
228
|
+
# program FK; cached by Django's queryset evaluation.
|
|
229
|
+
program_id = Project.objects.filter(pk=project_id).values_list("program_id", flat=True).first()
|
|
230
|
+
|
|
231
|
+
scope_filter = Q(project_id=project_id)
|
|
232
|
+
if program_id is not None:
|
|
233
|
+
scope_filter |= Q(program_id=program_id)
|
|
234
|
+
|
|
235
|
+
webhooks = list(
|
|
236
|
+
Webhook.objects.filter(
|
|
237
|
+
scope_filter,
|
|
238
|
+
is_active=True,
|
|
239
|
+
events__contains=[event_type],
|
|
240
|
+
)
|
|
241
|
+
)
|
|
242
|
+
webhooks = _drop_webhooks_without_live_member_owner(webhooks, project_id)
|
|
243
|
+
webhooks = _drop_program_webhooks_without_live_program_admin_owner(webhooks, program_id)
|
|
244
|
+
|
|
245
|
+
for webhook in webhooks:
|
|
246
|
+
# Render per-webhook: each subscription may have a different format
|
|
247
|
+
# (one project can have a Slack webhook and a generic JSON webhook on
|
|
248
|
+
# the same event). The rendered dict is frozen onto the delivery row,
|
|
249
|
+
# so deliver_webhook posts it verbatim and the row is the audit record
|
|
250
|
+
# of exactly what was sent.
|
|
251
|
+
#
|
|
252
|
+
# render() runs before the row exists, so we pre-allocate the sequence
|
|
253
|
+
# number (#664) and pass it to create() as both the body value and
|
|
254
|
+
# ``sequence_number`` — WebhookDelivery.save()'s lazy-allocation guard then
|
|
255
|
+
# no-ops, keeping this a single write where the stored row equals the wire
|
|
256
|
+
# body (ADR-0083 audit invariant).
|
|
257
|
+
sequence = _next_delivery_sequence(webhook.id)
|
|
258
|
+
body = build_delivery_body(webhook, event_type, payload, sequence, str(project_id))
|
|
259
|
+
delivery = WebhookDelivery.objects.create(
|
|
260
|
+
webhook=webhook,
|
|
261
|
+
event_type=event_type,
|
|
262
|
+
payload=body,
|
|
263
|
+
sequence_number=sequence,
|
|
264
|
+
)
|
|
265
|
+
try:
|
|
266
|
+
deliver_webhook.delay(str(delivery.pk))
|
|
267
|
+
logger.debug(
|
|
268
|
+
"dispatch_webhooks: enqueued delivery %s for webhook %s (%s)",
|
|
269
|
+
delivery.pk,
|
|
270
|
+
webhook.pk,
|
|
271
|
+
event_type,
|
|
272
|
+
)
|
|
273
|
+
except _BROKER_ERRORS:
|
|
274
|
+
# Broker unavailable — delivery row stays PENDING with attempt_count=0
|
|
275
|
+
# so drain_webhook_queue picks it up within _DRAIN_ORPHAN_MINUTES.
|
|
276
|
+
logger.warning(
|
|
277
|
+
"dispatch_webhooks: broker unavailable — delivery %s will be drained",
|
|
278
|
+
delivery.pk,
|
|
279
|
+
)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Create Webhook and WebhookDelivery models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
import django.contrib.postgres.fields
|
|
8
|
+
import django.db.models.deletion
|
|
9
|
+
from django.conf import settings
|
|
10
|
+
from django.db import migrations, models
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Migration(migrations.Migration):
|
|
14
|
+
initial = True
|
|
15
|
+
|
|
16
|
+
dependencies = [
|
|
17
|
+
("projects", "0013_task_planned_start"),
|
|
18
|
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
operations = [
|
|
22
|
+
migrations.CreateModel(
|
|
23
|
+
name="Webhook",
|
|
24
|
+
fields=[
|
|
25
|
+
(
|
|
26
|
+
"id",
|
|
27
|
+
models.UUIDField(
|
|
28
|
+
default=uuid.uuid4,
|
|
29
|
+
editable=False,
|
|
30
|
+
primary_key=True,
|
|
31
|
+
serialize=False,
|
|
32
|
+
),
|
|
33
|
+
),
|
|
34
|
+
("url", models.URLField(max_length=2048)),
|
|
35
|
+
("secret", models.CharField(max_length=255)),
|
|
36
|
+
(
|
|
37
|
+
"events",
|
|
38
|
+
django.contrib.postgres.fields.ArrayField(
|
|
39
|
+
base_field=models.CharField(
|
|
40
|
+
choices=[
|
|
41
|
+
("task.created", "Task Created"),
|
|
42
|
+
("task.updated", "Task Updated"),
|
|
43
|
+
("task.deleted", "Task Deleted"),
|
|
44
|
+
("dependency.created", "Dependency Created"),
|
|
45
|
+
("dependency.deleted", "Dependency Deleted"),
|
|
46
|
+
("schedule.recalculated", "Schedule Recalculated"),
|
|
47
|
+
("project.created", "Project Created"),
|
|
48
|
+
],
|
|
49
|
+
max_length=30,
|
|
50
|
+
),
|
|
51
|
+
size=None,
|
|
52
|
+
),
|
|
53
|
+
),
|
|
54
|
+
("is_active", models.BooleanField(default=True)),
|
|
55
|
+
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
56
|
+
(
|
|
57
|
+
"created_by",
|
|
58
|
+
models.ForeignKey(
|
|
59
|
+
blank=True,
|
|
60
|
+
null=True,
|
|
61
|
+
on_delete=django.db.models.deletion.SET_NULL,
|
|
62
|
+
to=settings.AUTH_USER_MODEL,
|
|
63
|
+
),
|
|
64
|
+
),
|
|
65
|
+
(
|
|
66
|
+
"project",
|
|
67
|
+
models.ForeignKey(
|
|
68
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
69
|
+
related_name="webhooks",
|
|
70
|
+
to="projects.project",
|
|
71
|
+
),
|
|
72
|
+
),
|
|
73
|
+
],
|
|
74
|
+
options={
|
|
75
|
+
"indexes": [
|
|
76
|
+
models.Index(
|
|
77
|
+
fields=["project", "is_active"],
|
|
78
|
+
name="webhook_project_active_idx",
|
|
79
|
+
),
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
),
|
|
83
|
+
migrations.CreateModel(
|
|
84
|
+
name="WebhookDelivery",
|
|
85
|
+
fields=[
|
|
86
|
+
(
|
|
87
|
+
"id",
|
|
88
|
+
models.UUIDField(
|
|
89
|
+
default=uuid.uuid4,
|
|
90
|
+
editable=False,
|
|
91
|
+
primary_key=True,
|
|
92
|
+
serialize=False,
|
|
93
|
+
),
|
|
94
|
+
),
|
|
95
|
+
(
|
|
96
|
+
"event_type",
|
|
97
|
+
models.CharField(
|
|
98
|
+
choices=[
|
|
99
|
+
("task.created", "Task Created"),
|
|
100
|
+
("task.updated", "Task Updated"),
|
|
101
|
+
("task.deleted", "Task Deleted"),
|
|
102
|
+
("dependency.created", "Dependency Created"),
|
|
103
|
+
("dependency.deleted", "Dependency Deleted"),
|
|
104
|
+
("schedule.recalculated", "Schedule Recalculated"),
|
|
105
|
+
("project.created", "Project Created"),
|
|
106
|
+
],
|
|
107
|
+
max_length=30,
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
("payload", models.JSONField()),
|
|
111
|
+
(
|
|
112
|
+
"status",
|
|
113
|
+
models.CharField(
|
|
114
|
+
choices=[
|
|
115
|
+
("pending", "Pending"),
|
|
116
|
+
("success", "Success"),
|
|
117
|
+
("failed", "Failed"),
|
|
118
|
+
],
|
|
119
|
+
default="pending",
|
|
120
|
+
max_length=10,
|
|
121
|
+
),
|
|
122
|
+
),
|
|
123
|
+
("response_status", models.SmallIntegerField(blank=True, null=True)),
|
|
124
|
+
("attempt_count", models.SmallIntegerField(default=0)),
|
|
125
|
+
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
126
|
+
("completed_at", models.DateTimeField(blank=True, null=True)),
|
|
127
|
+
(
|
|
128
|
+
"webhook",
|
|
129
|
+
models.ForeignKey(
|
|
130
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
131
|
+
related_name="deliveries",
|
|
132
|
+
to="webhooks.webhook",
|
|
133
|
+
),
|
|
134
|
+
),
|
|
135
|
+
],
|
|
136
|
+
options={
|
|
137
|
+
"ordering": ["-created_at"],
|
|
138
|
+
"indexes": [
|
|
139
|
+
models.Index(
|
|
140
|
+
fields=["webhook", "created_at"],
|
|
141
|
+
name="delivery_webhook_created_idx",
|
|
142
|
+
),
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
),
|
|
146
|
+
]
|