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,22 @@
|
|
|
1
|
+
"""URL patterns for the webhooks app."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from django.urls import URLPattern, URLResolver
|
|
6
|
+
from rest_framework.routers import DefaultRouter
|
|
7
|
+
|
|
8
|
+
from trueppm_api.apps.webhooks.views import ProgramWebhookViewSet, WebhookViewSet
|
|
9
|
+
|
|
10
|
+
router = DefaultRouter()
|
|
11
|
+
router.register(
|
|
12
|
+
r"projects/(?P<project_pk>[^/.]+)/webhooks",
|
|
13
|
+
WebhookViewSet,
|
|
14
|
+
basename="project-webhooks",
|
|
15
|
+
)
|
|
16
|
+
router.register(
|
|
17
|
+
r"programs/(?P<program_pk>[^/.]+)/webhooks",
|
|
18
|
+
ProgramWebhookViewSet,
|
|
19
|
+
basename="program-webhooks",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
urlpatterns: list[URLPattern | URLResolver] = router.urls
|
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
"""Views for webhook management API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
import redis as redis_lib
|
|
8
|
+
from django.db import transaction
|
|
9
|
+
from django.db.models import QuerySet
|
|
10
|
+
from drf_spectacular.utils import (
|
|
11
|
+
OpenApiResponse,
|
|
12
|
+
extend_schema,
|
|
13
|
+
extend_schema_view,
|
|
14
|
+
inline_serializer,
|
|
15
|
+
)
|
|
16
|
+
from kombu.exceptions import ( # type: ignore[import-untyped]
|
|
17
|
+
OperationalError as KombuOperationalError,
|
|
18
|
+
)
|
|
19
|
+
from rest_framework import serializers, status
|
|
20
|
+
from rest_framework.decorators import action
|
|
21
|
+
from rest_framework.exceptions import PermissionDenied
|
|
22
|
+
from rest_framework.mixins import (
|
|
23
|
+
CreateModelMixin,
|
|
24
|
+
DestroyModelMixin,
|
|
25
|
+
ListModelMixin,
|
|
26
|
+
RetrieveModelMixin,
|
|
27
|
+
UpdateModelMixin,
|
|
28
|
+
)
|
|
29
|
+
from rest_framework.pagination import CursorPagination
|
|
30
|
+
from rest_framework.permissions import BasePermission, IsAuthenticated
|
|
31
|
+
from rest_framework.request import Request
|
|
32
|
+
from rest_framework.response import Response
|
|
33
|
+
from rest_framework.serializers import BaseSerializer
|
|
34
|
+
from rest_framework.viewsets import GenericViewSet
|
|
35
|
+
|
|
36
|
+
from trueppm_api.apps.access.models import ProjectMembership
|
|
37
|
+
from trueppm_api.apps.access.permissions import (
|
|
38
|
+
IsNotTokenAuthenticated,
|
|
39
|
+
IsProgramAdmin,
|
|
40
|
+
IsProgramMember,
|
|
41
|
+
IsProgramNotClosed,
|
|
42
|
+
IsProjectAdmin,
|
|
43
|
+
IsProjectMember,
|
|
44
|
+
IsProjectNotArchived,
|
|
45
|
+
)
|
|
46
|
+
from trueppm_api.apps.idempotency.mixins import IdempotencyMixin
|
|
47
|
+
from trueppm_api.apps.projects.models import Program, Project
|
|
48
|
+
from trueppm_api.apps.webhooks.dispatch import build_delivery_body
|
|
49
|
+
from trueppm_api.apps.webhooks.models import (
|
|
50
|
+
PING_EVENT_TYPE,
|
|
51
|
+
Webhook,
|
|
52
|
+
WebhookDelivery,
|
|
53
|
+
WebhookEditor,
|
|
54
|
+
_next_delivery_sequence,
|
|
55
|
+
)
|
|
56
|
+
from trueppm_api.apps.webhooks.serializers import (
|
|
57
|
+
WebhookCreateResponseSerializer,
|
|
58
|
+
WebhookDeliverySerializer,
|
|
59
|
+
WebhookSerializer,
|
|
60
|
+
)
|
|
61
|
+
from trueppm_api.apps.webhooks.tasks import deliver_webhook
|
|
62
|
+
|
|
63
|
+
logger = logging.getLogger(__name__)
|
|
64
|
+
|
|
65
|
+
_BROKER_ERRORS = (KombuOperationalError, ConnectionError, redis_lib.ConnectionError)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class WebhookDeliveryCursorPagination(CursorPagination):
|
|
69
|
+
"""Depth-independent pagination for a webhook's delivery log (#1317).
|
|
70
|
+
|
|
71
|
+
The delivery history grows without bound — one row per event fired — and the
|
|
72
|
+
previous hard ``[:50]`` slice could only ever surface the newest 50 with no
|
|
73
|
+
way to page back. A created_at cursor is stable under the concurrent inserts
|
|
74
|
+
the dispatcher produces. Mirrors AuditEventCursorPagination.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
ordering = "-created_at"
|
|
78
|
+
page_size = 50
|
|
79
|
+
page_size_query_param = "page_size"
|
|
80
|
+
max_page_size = 200
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# The real 200 body of a webhook's delivery log. Declared once and shared by the
|
|
84
|
+
# project- and program-scoped viewsets so both publish the identical component
|
|
85
|
+
# (two `inline_serializer` calls with the same name would be a duplicate-component
|
|
86
|
+
# hazard). This action paginates with a CURSOR while its viewset's own
|
|
87
|
+
# pagination_class is page-number, and drf-spectacular's auto-wrap reads the class
|
|
88
|
+
# attribute — so a `many=True` response was wrapped in the page-number envelope and
|
|
89
|
+
# published `count` as *required* for a body that never carries one. Same defect
|
|
90
|
+
# class as #2583's named instances: a client typing `count` as non-optional breaks
|
|
91
|
+
# on first call. Declare the envelope rather than lean on a heuristic that cannot
|
|
92
|
+
# see which paginator actually runs.
|
|
93
|
+
WEBHOOK_DELIVERY_PAGE = inline_serializer(
|
|
94
|
+
name="WebhookDeliveryCursorPage",
|
|
95
|
+
fields={
|
|
96
|
+
"next": serializers.URLField(allow_null=True),
|
|
97
|
+
"previous": serializers.URLField(allow_null=True),
|
|
98
|
+
"results": WebhookDeliverySerializer(many=True),
|
|
99
|
+
},
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# The 201 body carries the one-time `secret`; the shared Webhook schema does not
|
|
104
|
+
# declare it (the field is write_only, which is right for every read). Declared per
|
|
105
|
+
#: Both causes of the 403 the project-scoped create now answers. Declared as one string
|
|
106
|
+
#: because the two are indistinguishable on the wire — same status, bare ``detail`` — so
|
|
107
|
+
#: a client branches on the message either way, and publishing only one of them would
|
|
108
|
+
#: tell an integrator the other cannot happen.
|
|
109
|
+
#:
|
|
110
|
+
#: Used ONLY where `ProgramWebhookViewSet` overrides the declaration with its own. A
|
|
111
|
+
#: declaration on an inherited action that the subclass does not re-declare publishes on
|
|
112
|
+
#: BOTH paths — which also collides their pinned operationIds — so `update`,
|
|
113
|
+
#: `partial_update` and `destroy` are deliberately left undeclared here rather than
|
|
114
|
+
#: gaining a project-flavored 403 on the program routes. They are part of the wider
|
|
115
|
+
#: undeclared-403-on-DELETE gap recorded in the MR for #3414.
|
|
116
|
+
_ARCHIVED_OR_NOT_ADMIN = (
|
|
117
|
+
"Caller is not a project Admin, or the project is archived (#3414) — archived "
|
|
118
|
+
"plans are read-only, so a webhook cannot be added, edited, deleted or "
|
|
119
|
+
"test-fired on one. Reading the webhook and its deliveries still works."
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# viewset because one shared declaration would publish the same operationId on both
|
|
124
|
+
# the project- and program-scoped paths — a duplicate-operationId collision. Ids are
|
|
125
|
+
# pinned for the same reason as `deliveries`: operationId is the *method name* in a
|
|
126
|
+
# generated client, so fixing a response shape must not rename every caller's method
|
|
127
|
+
# (#2583).
|
|
128
|
+
@extend_schema_view(
|
|
129
|
+
create=extend_schema(
|
|
130
|
+
summary="Register a webhook",
|
|
131
|
+
responses={
|
|
132
|
+
201: WebhookCreateResponseSerializer,
|
|
133
|
+
403: OpenApiResponse(description=_ARCHIVED_OR_NOT_ADMIN),
|
|
134
|
+
},
|
|
135
|
+
operation_id="v1_projects_webhooks_create",
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
class WebhookViewSet(
|
|
139
|
+
IdempotencyMixin,
|
|
140
|
+
CreateModelMixin,
|
|
141
|
+
RetrieveModelMixin,
|
|
142
|
+
UpdateModelMixin,
|
|
143
|
+
DestroyModelMixin,
|
|
144
|
+
ListModelMixin,
|
|
145
|
+
GenericViewSet[Webhook],
|
|
146
|
+
):
|
|
147
|
+
"""CRUD for outbound webhooks scoped to a project.
|
|
148
|
+
|
|
149
|
+
List/retrieve: requires Viewer+ (IsProjectMember).
|
|
150
|
+
Create/update/delete: requires Admin+ (IsProjectAdmin).
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
serializer_class = WebhookSerializer
|
|
154
|
+
|
|
155
|
+
# Exempt from the generic Idempotency-Key path (ADR-0170): the create response
|
|
156
|
+
# carries the one-time plaintext signing secret, which must never be persisted
|
|
157
|
+
# in the idempotency store for replay. Mirrors ProjectApiTokenViewSet, and it is
|
|
158
|
+
# the same reasoning #2885 encrypts the column for — storing the plaintext in
|
|
159
|
+
# IdempotencyKey.response_body (a plain JSONField, retained for
|
|
160
|
+
# IDEMPOTENCY_RETENTION_HOURS) would move the secret to another table rather than
|
|
161
|
+
# remove it, and would re-serve it on replay, contradicting the documented
|
|
162
|
+
# "returned exactly once" guarantee.
|
|
163
|
+
idempotency_exempt = True
|
|
164
|
+
|
|
165
|
+
def get_permissions(self) -> list[BasePermission]:
|
|
166
|
+
# `deliveries` exposes the full event payload (task notes, comment
|
|
167
|
+
# snippets, assignee emails) of every event ever sent (#903). That is
|
|
168
|
+
# information disclosure beyond plain project membership, so it is
|
|
169
|
+
# Admin-only — consistent with webhook create/update/delete/test, which
|
|
170
|
+
# are already Admin-gated (only an Admin could have created the webhook).
|
|
171
|
+
if self.action == "deliveries":
|
|
172
|
+
# A read. Archived projects stay readable, so no lifecycle gate here.
|
|
173
|
+
return [IsAuthenticated(), IsProjectAdmin()]
|
|
174
|
+
if self.action in ("create", "update", "partial_update"):
|
|
175
|
+
# IsNotTokenAuthenticated (#4461): create mints a durable HMAC signing
|
|
176
|
+
# secret, and update can repoint the delivery URL or rotate that secret
|
|
177
|
+
# — both are the same "a leaked PAT should not be able to extend itself
|
|
178
|
+
# with a new durable grant" shape #2939 already blocked for share links
|
|
179
|
+
# and git-automation secrets. destroy/test_ping stay token-reachable:
|
|
180
|
+
# deleting only shrinks what a leaked token can reach, and test_ping
|
|
181
|
+
# sends no new credential anywhere.
|
|
182
|
+
return [
|
|
183
|
+
IsAuthenticated(),
|
|
184
|
+
IsProjectAdmin(),
|
|
185
|
+
IsProjectNotArchived(),
|
|
186
|
+
IsNotTokenAuthenticated(),
|
|
187
|
+
]
|
|
188
|
+
if self.action in ("destroy", "test_ping"):
|
|
189
|
+
# IsProjectNotArchived fires in `has_permission` on these routes because the
|
|
190
|
+
# URL names `project_pk` — the kwarg `_project_pk_from_view` reads (#3414).
|
|
191
|
+
# `destroy` is gated like the rest since the archive bypass was scoped to
|
|
192
|
+
# ProjectViewSet; before that, the router-minted action name alone was
|
|
193
|
+
# enough to skip the check on every project-scoped viewset.
|
|
194
|
+
return [IsAuthenticated(), IsProjectAdmin(), IsProjectNotArchived()]
|
|
195
|
+
return [IsAuthenticated(), IsProjectMember()]
|
|
196
|
+
|
|
197
|
+
def get_queryset(self) -> QuerySet[Webhook]:
|
|
198
|
+
project_pk = self.kwargs["project_pk"]
|
|
199
|
+
return (
|
|
200
|
+
Webhook.objects.filter(project_id=project_pk)
|
|
201
|
+
.select_related("project", "created_by")
|
|
202
|
+
.order_by("-created_at")
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
def perform_create(self, serializer: BaseSerializer[Webhook]) -> None:
|
|
206
|
+
project_pk = self.kwargs["project_pk"]
|
|
207
|
+
try:
|
|
208
|
+
project = Project.objects.get(pk=project_pk, is_deleted=False)
|
|
209
|
+
except Project.DoesNotExist:
|
|
210
|
+
from rest_framework.exceptions import NotFound
|
|
211
|
+
|
|
212
|
+
raise NotFound("Project not found.") from None
|
|
213
|
+
|
|
214
|
+
# Check object-level permission.
|
|
215
|
+
self.check_object_permissions(self.request, project)
|
|
216
|
+
|
|
217
|
+
serializer.save(project=project, created_by=self.request.user)
|
|
218
|
+
|
|
219
|
+
def perform_update(self, serializer: BaseSerializer[Webhook]) -> None:
|
|
220
|
+
"""Record an editor when this write is durable-grant-shaped (#4461).
|
|
221
|
+
|
|
222
|
+
Two cases upsert into ``WebhookEditor`` — never an overwrite, and
|
|
223
|
+
nothing here ever removes a row once it exists:
|
|
224
|
+
|
|
225
|
+
- ``url`` or ``secret`` is actually present in ``validated_data``:
|
|
226
|
+
creating or repointing the durable grant itself. A project
|
|
227
|
+
webhook's update has no registrant check (unlike the
|
|
228
|
+
program-scoped viewset below), so any project Admin may repoint
|
|
229
|
+
any project webhook's URL or rotate its secret; without this,
|
|
230
|
+
``revoke_personal_durable_grants``'s sweep could only match
|
|
231
|
+
``created_by`` and would miss exactly that case.
|
|
232
|
+
- ``is_active`` is set to ``True`` on a currently-inactive row:
|
|
233
|
+
**re-enabling** is durable-grant-shaped on its own, even with
|
|
234
|
+
``url``/``secret`` untouched. A disabled webhook (by this sweep,
|
|
235
|
+
or by the auto-disable guard) still carries whatever ``url``/
|
|
236
|
+
``secret`` it had; a second hijacked session that does nothing
|
|
237
|
+
but flip it back on restarts delivery to wherever it currently
|
|
238
|
+
points, and that re-enabler must be recorded or their own future
|
|
239
|
+
containment would miss the row. *Disabling* is deliberately NOT
|
|
240
|
+
recorded — it only shrinks what is live, the opposite shape.
|
|
241
|
+
"""
|
|
242
|
+
changed_credential_fields = {"url", "secret"} & set(serializer.validated_data)
|
|
243
|
+
is_reenable = (
|
|
244
|
+
serializer.validated_data.get("is_active") is True
|
|
245
|
+
and serializer.instance is not None
|
|
246
|
+
and not serializer.instance.is_active
|
|
247
|
+
)
|
|
248
|
+
serializer.save()
|
|
249
|
+
if changed_credential_fields or is_reenable:
|
|
250
|
+
from typing import cast
|
|
251
|
+
|
|
252
|
+
from django.contrib.auth.models import User
|
|
253
|
+
|
|
254
|
+
# IsAuthenticated (already enforced by get_permissions) guarantees a
|
|
255
|
+
# real user, never AnonymousUser; narrow for WebhookEditor.user's FK.
|
|
256
|
+
WebhookEditor.objects.update_or_create(
|
|
257
|
+
webhook=serializer.instance, user=cast(User, self.request.user)
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
def _check_delivery_log_access(self, webhook: Webhook) -> None:
|
|
261
|
+
"""Hook for scope-specific delivery-log access. No extra gate at project scope."""
|
|
262
|
+
|
|
263
|
+
def get_object(self) -> Webhook:
|
|
264
|
+
obj: Webhook = super().get_object()
|
|
265
|
+
# Object-level permission check against the webhook's scope object —
|
|
266
|
+
# the Project for project-scoped webhooks, the Program for program-scoped
|
|
267
|
+
# ones (ADR-0076). The active permission classes (Is{Project,Program}*)
|
|
268
|
+
# match because get_permissions is scope-specific per subclass.
|
|
269
|
+
self.check_object_permissions(self.request, self._scope_object(obj))
|
|
270
|
+
return obj
|
|
271
|
+
|
|
272
|
+
def _scope_object(self, webhook: Webhook) -> Project | Program:
|
|
273
|
+
"""Return the Project or Program a webhook is scoped to (XOR)."""
|
|
274
|
+
scope = webhook.program if webhook.program_id else webhook.project
|
|
275
|
+
# The webhook_scope_xor DB constraint guarantees exactly one is set, so
|
|
276
|
+
# scope is never None here — assert narrows the type for mypy.
|
|
277
|
+
assert scope is not None
|
|
278
|
+
return scope
|
|
279
|
+
|
|
280
|
+
@extend_schema(
|
|
281
|
+
summary="Send a test webhook ping",
|
|
282
|
+
responses={
|
|
283
|
+
202: inline_serializer(
|
|
284
|
+
name="WebhookTestPingResponse",
|
|
285
|
+
fields={"delivery_id": serializers.CharField()},
|
|
286
|
+
),
|
|
287
|
+
403: OpenApiResponse(
|
|
288
|
+
description=(
|
|
289
|
+
"Caller lacks Admin on the webhook's scope. On a PROJECT webhook the "
|
|
290
|
+
"same status is also how an archived project refuses (#3414); the "
|
|
291
|
+
"program-scoped route has no archived state. Declared once because "
|
|
292
|
+
"this handler is shared by both viewsets."
|
|
293
|
+
)
|
|
294
|
+
),
|
|
295
|
+
},
|
|
296
|
+
)
|
|
297
|
+
@action(detail=True, methods=["post"], url_path="test")
|
|
298
|
+
def test_ping(self, request: Request, **kwargs: object) -> Response:
|
|
299
|
+
"""Send a test ping to the webhook URL.
|
|
300
|
+
|
|
301
|
+
The ping takes exactly the same path as a real delivery: it renders through
|
|
302
|
+
the subscription's registered format, is signed identically, and consumes a
|
|
303
|
+
sequence number with a matching ``_meta.sequence``. A test therefore fails
|
|
304
|
+
wherever a real delivery would, and never shows up as a gap in the
|
|
305
|
+
consumer's sequence counter.
|
|
306
|
+
|
|
307
|
+
Returns 202 with the ``delivery_id``. That acknowledges the *enqueue*, not
|
|
308
|
+
the receiver's answer — read the ``deliveries`` action back for that row's
|
|
309
|
+
terminal ``status`` and ``response_status`` to learn what the receiver said.
|
|
310
|
+
"""
|
|
311
|
+
# Building the ping inline (rather than through the provider) was how a
|
|
312
|
+
# slack-format webhook — the UI default — got sent a body Slack rejects with
|
|
313
|
+
# 400 invalid_payload while the API reported success on the 202 (#2884).
|
|
314
|
+
webhook = self.get_object()
|
|
315
|
+
sequence = _next_delivery_sequence(webhook.pk)
|
|
316
|
+
body = build_delivery_body(
|
|
317
|
+
webhook,
|
|
318
|
+
PING_EVENT_TYPE,
|
|
319
|
+
{"event": PING_EVENT_TYPE, "webhook_id": str(webhook.pk)},
|
|
320
|
+
sequence,
|
|
321
|
+
)
|
|
322
|
+
delivery = WebhookDelivery.objects.create(
|
|
323
|
+
webhook=webhook,
|
|
324
|
+
event_type=PING_EVENT_TYPE,
|
|
325
|
+
payload=body,
|
|
326
|
+
sequence_number=sequence,
|
|
327
|
+
)
|
|
328
|
+
# Defer dispatch until the delivery row is committed so the task never
|
|
329
|
+
# races against an uncommitted row. If the broker is down the delay()
|
|
330
|
+
# call is a no-op — the delivery row stays PENDING and drain_webhook_queue
|
|
331
|
+
# picks it up within _DRAIN_ORPHAN_MINUTES.
|
|
332
|
+
delivery_id = str(delivery.pk)
|
|
333
|
+
|
|
334
|
+
def _enqueue_ping() -> None:
|
|
335
|
+
try:
|
|
336
|
+
deliver_webhook.delay(delivery_id)
|
|
337
|
+
except _BROKER_ERRORS:
|
|
338
|
+
logger.warning(
|
|
339
|
+
"test_ping: broker unavailable — delivery %s will be drained",
|
|
340
|
+
delivery_id,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
transaction.on_commit(_enqueue_ping)
|
|
344
|
+
return Response(
|
|
345
|
+
{"delivery_id": delivery_id},
|
|
346
|
+
status=status.HTTP_202_ACCEPTED,
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
@extend_schema(
|
|
350
|
+
summary="List recent webhook deliveries",
|
|
351
|
+
responses={200: WEBHOOK_DELIVERY_PAGE},
|
|
352
|
+
# Pinned because declaring the envelope changes what drf-spectacular
|
|
353
|
+
# thinks this operation is. It derives the operationId's `_list`/
|
|
354
|
+
# `_retrieve` suffix from whether the 200 is a ``many=True`` serializer,
|
|
355
|
+
# so switching to an object envelope silently renamed
|
|
356
|
+
# ``…_deliveries_list`` to ``…_deliveries_retrieve`` — and operationId is
|
|
357
|
+
# the *method name* in a generated client. Fixing a broken response type
|
|
358
|
+
# by breaking every caller's method name would trade one #2583 for
|
|
359
|
+
# another, so the shipped id is held fixed.
|
|
360
|
+
operation_id="v1_projects_webhooks_deliveries_list",
|
|
361
|
+
)
|
|
362
|
+
@action(detail=True, methods=["get"], url_path="deliveries")
|
|
363
|
+
def deliveries(self, request: Request, **kwargs: object) -> Response:
|
|
364
|
+
"""List deliveries for this webhook, newest first (cursor-paginated, #1317).
|
|
365
|
+
|
|
366
|
+
A bare ``GET`` returns the most recent page; follow ``next`` to page
|
|
367
|
+
back through history. Instantiates the cursor paginator directly rather
|
|
368
|
+
than going through ``self.paginator`` (the viewset default is page-number
|
|
369
|
+
for the webhook CRUD list — the delivery log wants a cursor).
|
|
370
|
+
"""
|
|
371
|
+
webhook = self.get_object()
|
|
372
|
+
self._check_delivery_log_access(webhook)
|
|
373
|
+
qs = WebhookDelivery.objects.filter(webhook=webhook)
|
|
374
|
+
paginator = WebhookDeliveryCursorPagination()
|
|
375
|
+
page = paginator.paginate_queryset(qs, request, view=self)
|
|
376
|
+
serializer = WebhookDeliverySerializer(page, many=True)
|
|
377
|
+
return paginator.get_paginated_response(serializer.data)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
@extend_schema_view(
|
|
381
|
+
# Annotates the INHERITED action rather than redefining it. A single pinned
|
|
382
|
+
# operation_id on the base class would publish the same id on both the
|
|
383
|
+
# project- and program-scoped paths — a duplicate-operationId collision — so
|
|
384
|
+
# each concrete viewset needs its own. `extend_schema_view` does that without
|
|
385
|
+
# a subclass override, which would duplicate the handler for no runtime
|
|
386
|
+
# reason. See WebhookViewSet.deliveries for why the envelope must be explicit
|
|
387
|
+
# and why the id is pinned at all (#2583).
|
|
388
|
+
deliveries=extend_schema(
|
|
389
|
+
summary="List recent webhook deliveries",
|
|
390
|
+
responses={
|
|
391
|
+
200: WEBHOOK_DELIVERY_PAGE,
|
|
392
|
+
403: OpenApiResponse(
|
|
393
|
+
description=(
|
|
394
|
+
"Not the webhook's creator (#4310). The delivery log replays past "
|
|
395
|
+
"payloads from every member project, so it is restricted to the "
|
|
396
|
+
"registrant even for another Program Admin."
|
|
397
|
+
)
|
|
398
|
+
),
|
|
399
|
+
},
|
|
400
|
+
operation_id="v1_programs_webhooks_deliveries_list",
|
|
401
|
+
),
|
|
402
|
+
create=extend_schema(
|
|
403
|
+
summary="Register a webhook",
|
|
404
|
+
responses={201: WebhookCreateResponseSerializer},
|
|
405
|
+
operation_id="v1_programs_webhooks_create",
|
|
406
|
+
),
|
|
407
|
+
update=extend_schema(
|
|
408
|
+
summary="Replace a webhook",
|
|
409
|
+
responses={
|
|
410
|
+
200: WebhookSerializer,
|
|
411
|
+
400: OpenApiResponse(description="Validation error."),
|
|
412
|
+
403: OpenApiResponse(
|
|
413
|
+
description=(
|
|
414
|
+
"Refused (#4310) unless the requester is the webhook's creator AND "
|
|
415
|
+
"still holds live ProjectMembership on every project the program "
|
|
416
|
+
"covers. Checked before validation, so a no-op update is refused too."
|
|
417
|
+
)
|
|
418
|
+
),
|
|
419
|
+
},
|
|
420
|
+
),
|
|
421
|
+
partial_update=extend_schema(
|
|
422
|
+
summary="Update a webhook",
|
|
423
|
+
responses={
|
|
424
|
+
200: WebhookSerializer,
|
|
425
|
+
400: OpenApiResponse(description="Validation error."),
|
|
426
|
+
403: OpenApiResponse(
|
|
427
|
+
description=(
|
|
428
|
+
"Refused (#4310) unless the requester is the webhook's creator AND "
|
|
429
|
+
"still holds live ProjectMembership on every project the program "
|
|
430
|
+
"covers. Checked before validation, so a no-op PATCH is refused too."
|
|
431
|
+
)
|
|
432
|
+
),
|
|
433
|
+
},
|
|
434
|
+
),
|
|
435
|
+
)
|
|
436
|
+
class ProgramWebhookViewSet(WebhookViewSet):
|
|
437
|
+
"""CRUD for outbound webhooks scoped to a program (ADR-0076).
|
|
438
|
+
|
|
439
|
+
A program-scoped webhook fires for events on any project within the program.
|
|
440
|
+
Inherits the test/deliveries actions and the rendering/dispatch substrate
|
|
441
|
+
from WebhookViewSet; only the scope resolution and RBAC ladder change:
|
|
442
|
+
list/retrieve require Program Viewer+ (IsProgramMember). Create requires Program
|
|
443
|
+
Admin+ (IsProgramAdmin). Update (PUT/PATCH) requires more than that (#4310): the
|
|
444
|
+
requester must be the webhook'''s registrant (created_by) AND still hold live
|
|
445
|
+
ProjectMembership on every project the program covers, checked in get_object
|
|
446
|
+
before validation — refused with 403 otherwise, including a no-op PATCH. The
|
|
447
|
+
delivery log is likewise restricted to the registrant, not to Admin+ generally.
|
|
448
|
+
"""
|
|
449
|
+
|
|
450
|
+
def get_permissions(self) -> list[BasePermission]:
|
|
451
|
+
# `deliveries` exposes the full event payload (task notes, comment
|
|
452
|
+
# snippets, assignee emails) of every event ever sent (#903). That is
|
|
453
|
+
# information disclosure beyond plain project membership, so it is
|
|
454
|
+
# Admin-only — consistent with webhook create/update/delete/test, which
|
|
455
|
+
# are already Admin-gated (only an Admin could have created the webhook).
|
|
456
|
+
#
|
|
457
|
+
# Lifecycle (#4014): a closed program's shell is read-only (#530), so
|
|
458
|
+
# registering or re-pointing a webhook is refused like minting a program API
|
|
459
|
+
# token. `destroy` is deliberately NOT gated: child projects keep accepting
|
|
460
|
+
# writes on a closed program, so a program webhook keeps delivering their
|
|
461
|
+
# events to an external URL, and deleting it is the only way to stop that
|
|
462
|
+
# outbound flow without reopening the program — the same containment argument
|
|
463
|
+
# as program token revocation. `test_ping` and `deliveries` touch no program
|
|
464
|
+
# state and stay available for diagnosing a still-live webhook.
|
|
465
|
+
if self.action in ("create", "update", "partial_update"):
|
|
466
|
+
# IsNotTokenAuthenticated (#4461): see WebhookViewSet.get_permissions —
|
|
467
|
+
# same durable-grant shape, mint/repoint/rotate only through a session
|
|
468
|
+
# or JWT.
|
|
469
|
+
return [
|
|
470
|
+
IsAuthenticated(),
|
|
471
|
+
IsProgramAdmin(),
|
|
472
|
+
IsProgramNotClosed(),
|
|
473
|
+
IsNotTokenAuthenticated(),
|
|
474
|
+
]
|
|
475
|
+
if self.action in ("destroy", "test_ping", "deliveries"):
|
|
476
|
+
return [IsAuthenticated(), IsProgramAdmin()]
|
|
477
|
+
return [IsAuthenticated(), IsProgramMember()]
|
|
478
|
+
|
|
479
|
+
def get_object(self) -> Webhook:
|
|
480
|
+
obj: Webhook = super().get_object()
|
|
481
|
+
# #4310 (B2, option c): only the registrant, and only while still a live member
|
|
482
|
+
# of every project the webhook covers, may change it. Checked before validation
|
|
483
|
+
# so a no-op PATCH is refused too, and so a non-member cannot re-point the URL
|
|
484
|
+
# and start receiving a member project's events.
|
|
485
|
+
if self.action in ("update", "partial_update") and not self._can_edit(obj):
|
|
486
|
+
raise PermissionDenied("Only the webhook's creator can edit it while a member.")
|
|
487
|
+
return obj
|
|
488
|
+
|
|
489
|
+
def _can_edit(self, webhook: Webhook) -> bool:
|
|
490
|
+
if webhook.program_id is None:
|
|
491
|
+
return False
|
|
492
|
+
if webhook.created_by_id is None or webhook.created_by_id != self.request.user.pk:
|
|
493
|
+
return False
|
|
494
|
+
project_ids = set(
|
|
495
|
+
Project.objects.filter(program_id=webhook.program_id, is_deleted=False).values_list(
|
|
496
|
+
"pk", flat=True
|
|
497
|
+
)
|
|
498
|
+
)
|
|
499
|
+
if not project_ids:
|
|
500
|
+
return True
|
|
501
|
+
live = set(
|
|
502
|
+
ProjectMembership.live()
|
|
503
|
+
.filter(user=self.request.user, project_id__in=project_ids)
|
|
504
|
+
.values_list("project_id", flat=True)
|
|
505
|
+
)
|
|
506
|
+
return live == project_ids
|
|
507
|
+
|
|
508
|
+
def get_queryset(self) -> QuerySet[Webhook]:
|
|
509
|
+
program_pk = self.kwargs["program_pk"]
|
|
510
|
+
return (
|
|
511
|
+
Webhook.objects.filter(program_id=program_pk)
|
|
512
|
+
.select_related("program", "created_by")
|
|
513
|
+
.order_by("-created_at")
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
def _check_delivery_log_access(self, webhook: Webhook) -> None:
|
|
517
|
+
# #4310 (B3): the delivery log replays past event payloads from every member
|
|
518
|
+
# project, so it is limited to the webhook's registrant. Another Program Admin
|
|
519
|
+
# gets 403 even though IsProgramAdmin passes. A NULL registrant fails closed.
|
|
520
|
+
if webhook.created_by_id is None or webhook.created_by_id != self.request.user.pk:
|
|
521
|
+
raise PermissionDenied("Only the webhook's creator can read its delivery log.")
|
|
522
|
+
|
|
523
|
+
def perform_create(self, serializer: BaseSerializer[Webhook]) -> None:
|
|
524
|
+
program_pk = self.kwargs["program_pk"]
|
|
525
|
+
try:
|
|
526
|
+
program = Program.objects.get(pk=program_pk, is_deleted=False)
|
|
527
|
+
except Program.DoesNotExist:
|
|
528
|
+
from rest_framework.exceptions import NotFound
|
|
529
|
+
|
|
530
|
+
raise NotFound("Program not found.") from None
|
|
531
|
+
|
|
532
|
+
self.check_object_permissions(self.request, program)
|
|
533
|
+
serializer.save(program=program, created_by=self.request.user)
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""App config for the workflow execution engine (ADR-0080).
|
|
2
|
+
|
|
3
|
+
Holds the persistence layer (models, migrations, beat drain) for the default
|
|
4
|
+
workflow backend. The public import surface workflow authors target lives at
|
|
5
|
+
``trueppm_api.workflows`` (interface, services, backends, registry); this app
|
|
6
|
+
is a backend implementation detail those consumers never import directly, which
|
|
7
|
+
is what preserves backend-neutrality (ADR-0080 §E).
|
|
8
|
+
|
|
9
|
+
Named ``workflow_engine`` (not ``workflows``) so the app label does not collide
|
|
10
|
+
with the ``trueppm_api.workflows`` public package — that collision confused tools
|
|
11
|
+
that map app label → module (the mypy django-stubs plugin stopped recognizing
|
|
12
|
+
these as models), and is avoided by naming the engine's persistence app and the
|
|
13
|
+
public API package distinctly.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from django.apps import AppConfig
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class WorkflowEngineConfig(AppConfig):
|
|
22
|
+
default_auto_field = "django.db.models.BigAutoField"
|
|
23
|
+
name = "trueppm_api.apps.workflow_engine"
|