bugsink 2.2.0__tar.gz → 2.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bugsink-2.2.0 → bugsink-2.2.1}/.github/workflows/ci.yml +15 -11
- {bugsink-2.2.0 → bugsink-2.2.1}/.github/workflows/copilot-setup-steps.yml +2 -2
- {bugsink-2.2.0 → bugsink-2.2.1}/CHANGELOG.md +20 -1
- {bugsink-2.2.0 → bugsink-2.2.1}/PKG-INFO +1 -1
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/prestart.py +11 -2
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/_version.py +3 -3
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/gunicorn.docker.conf.py +1 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/settings/default.py +4 -1
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/settings/development.py +6 -5
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/urls.py +2 -1
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink.egg-info/PKG-INFO +1 -1
- {bugsink-2.2.0 → bugsink-2.2.1}/events/api_views.py +33 -11
- {bugsink-2.2.0 → bugsink-2.2.1}/files/views.py +1 -0
- bugsink-2.2.1/issues/api_views.py +296 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/models.py +163 -1
- bugsink-2.2.1/issues/serializers.py +90 -0
- bugsink-2.2.1/issues/templates/issues/event_details.html +233 -0
- bugsink-2.2.1/issues/test_api.py +366 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/views.py +7 -152
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/api_views.py +38 -8
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/api_views.py +33 -6
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/api_views.py +35 -8
- bugsink-2.2.1/theme/static/css/dist/styles.css +2 -0
- bugsink-2.2.0/issues/api_views.py +0 -134
- bugsink-2.2.0/issues/serializers.py +0 -41
- bugsink-2.2.0/issues/templates/issues/event_details.html +0 -233
- bugsink-2.2.0/issues/test_api.py +0 -168
- bugsink-2.2.0/theme/static/css/dist/styles.css +0 -2
- {bugsink-2.2.0 → bugsink-2.2.1}/.bandit +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/.coveragerc +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/.github/dependabot.yml +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/.gitignore +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/AGENTS.md +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/CLA.md +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/CONTRIBUTING.md +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/Dockerfile +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/Dockerfile.fromwheel +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/LICENSE +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/README.md +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/SECURITY.md +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/admin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/forms.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/migrations/0001_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/migrations/0002_alter_messagingserviceconfig_project.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/migrations/0003_messagingserviceconfig_last_failure_error_message_and_more.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/migrations/0004_alter_messagingserviceconfig_kind.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/migrations/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/models.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/service_backends/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/service_backends/base.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/service_backends/discord.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/service_backends/mattermost.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/service_backends/slack.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/service_backends/webhook_security.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/tasks.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/templates/mails/issue_alert.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/templates/mails/issue_alert.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/alerts/views.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/api/LICENSE +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/api/README.md +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/api/event.schema.altered.json +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/api/event.schema.json +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/admin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/future_python.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/check_migrations.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/cleanup_objectstorage.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/convert_mariadb_uuids.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/create_auth_token.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/fetch_event_schema_json.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/makemigrations.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/migrate.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/migrate_to_current_objectstorage.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/modelcounts.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/munin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/raise_exception.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/send_bomb.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/send_json.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/showstat.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/stress_test.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/commands/vacuum.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/management/utils.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/migrations/0001_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/migrations/0002_cachedmodelcount.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/migrations/0003_add_description_to_authtoken.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/migrations/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/models.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/tasks.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/templates/bsmain/auth_token_list.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/test_vacuum.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/urls.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/utils.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bsmain/views.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/api_fields.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/api_mixins.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/api_pagination.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/api_serializers.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/app_settings.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/authentication.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/conf_templates/docker.py.template +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/conf_templates/local.py.template +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/conf_templates/singleserver.py.template +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/conf_utils.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/context_processors.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/debug_views.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/decorators.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/email_backends.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/event_schema.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/exceptions.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/middleware.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/moreiterutils.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/period_utils.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/permissions.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/pygments_extensions.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/scripts/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/scripts/create_conf.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/scripts/manage.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/scripts/runsnappea.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/scripts/show_version.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/scripts/util.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/settings/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/streams.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/test_api.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/test_utils.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/timed_sqlite_backend/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/timed_sqlite_backend/base.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/tooling.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/transaction.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/utils.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/version.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/views.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/volume_based_condition.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink/wsgi.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink.egg-info/SOURCES.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink.egg-info/dependency_links.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink.egg-info/entry_points.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink.egg-info/requires.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/bugsink.egg-info/top_level.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/builddocker.bash +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/buildxdocker.bash +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/compat/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/compat/auth.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/compat/dsn.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/compat/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/compat/timestamp.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/compat/vars.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/docker-compose-sample.yaml +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ee/LICENSE +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ee/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ee/tenants/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ee/tenants/base.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ee/tenants/database_backend/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ee/tenants/database_backend/base.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ee/tenants/middleware.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ee/tenants/utils.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/admin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/factories.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/management/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/management/commands/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/management/commands/cleanup_eventstorage.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/management/commands/delete_by_age_until_under_retention_max.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/management/commands/delete_old_events.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/management/commands/fix_project_digest_order.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/management/commands/make_consistent.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/management/commands/migrate_to_current_eventstorage.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/management/commands/nuke_events.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/markdown_stacktrace.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0001_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0002_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0003_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0004_b_squashed.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0004_event_irrelevance_for_retention.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0005_event_events_even_project_abe572_idx.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0006_event_never_evict.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0007_set_never_evict.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0008_remove_event_events_even_project_abe572_idx_and_more.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0009_event_events_even_issue_i_90497b_idx.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0010_rename_ingest_order_event_digest_order_and_more.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0011_remove_event_events_even_project_adcdee_idx_and_more.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0012_event_ingested_at.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0013_harmonize_foogested_at.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0014_event_grouping.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0015_set_event_grouping.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0016_truncate_exception_type_128.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0017_alter_event_calculated_type_and_more.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0018_remove_event_has_exception_remove_event_has_logentry.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0019_event_storage_backend.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0020_remove_events_with_null_issue_or_grouping.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0021_alter_do_nothing.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0022_alter_event_project.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0023_event_remote_addr.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0024_remove_event_debug_info.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0025_fix_never_evict.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0026_event_events_even_project_625413_idx.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0027_event_project_digest_order.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/0028_event_events_even_digeste_bf21dd_idx.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/migrations/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/models.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/renderers.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/retention.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/serializers.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/storage.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/storage_registry.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/tasks.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/templates/events/event_stacktrace.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/test_api.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/ua_stuff.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/urls.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/utils.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/events/views.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/admin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/management/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/management/commands/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/management/commands/delete_legacy_sourcemaps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/management/commands/vacuum_files.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/migrations/0001_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/migrations/0002_chunk_created_at_file_accessed_at_file_created_at_and_more.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/migrations/0003_file_storage_backend.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/migrations/0004_alter_filemetadata_unique_together_and_more.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/migrations/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/minidump.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/models.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/object_kinds.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/storage.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/storage_registry.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/tasks.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/test_vacuum.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/files/urls.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/admin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/event_counter.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/exceptions.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/filestore.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/header_validators.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/management/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/management/commands/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/management/commands/vacuum_ingest_dir.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/migrations/0001_set_sqlite_wal.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/migrations/0002_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/migrations/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/models.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/parsers.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/tasks.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/urls.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/ingest/views.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/admin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/factories.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/forms.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/markdown_issue.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0001_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0002_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0003_alter_turningpoint_triggering_event.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0004_b_squashed.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0004_rename_event_count_issue_digested_event_count.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0005_rename_ingest_order_issue_digest_order_and_more.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0006_issue_next_unmute_check.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0007_alter_turningpoint_options.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0008_issue_stored_event_count.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0009_fill_stored_event_count.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0010_issue_list_indexes.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0011_truncate_exception_type_128.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0012_alter_issue_calculated_type_and_more.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0013_fix_issue_stored_event_counts.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0014_grouping_grouping_key_hash.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0015_set_grouping_hash.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0016_alter_grouping_unique_together.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0017_issue_list_indexes_must_start_with_project.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0018_issue_is_deleted.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0019_alter_grouping_grouping_key_hash.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0020_remove_objects_with_null_issue.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0021_alter_do_nothing.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0022_turningpoint_project.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0023_turningpoint_set_project.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0024_turningpoint_project_alter_not_null.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/0025_alter_grouping_project_alter_issue_project.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/migrations/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/regressions.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/tasks.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/templates/issues/_event_nav.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/templates/issues/base.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/templates/issues/breadcrumbs.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/templates/issues/event_404.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/templates/issues/event_list.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/templates/issues/grouping.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/templates/issues/history.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/templates/issues/issue_list.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/templates/issues/stacktrace.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/templates/issues/tags.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/urls.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/issues/utils.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/locale/zh_Hans/LC_MESSAGES/django.po +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/manage.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/performance/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/performance/bursty_data.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/performance/context_managers.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/performance/management/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/performance/management/commands/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/performance/management/commands/pftest_search.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/performance/stress-with-eviction/README.md +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/performance/stress-with-eviction/df-day.png +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/performance/stress-with-eviction/event_ingest_count-day.png +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/performance/stress-with-eviction/response_time_api_avg-day.png +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/performance/stress-with-eviction/response_time_api_max-day.png +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/performance/stress-with-eviction/snappea_queue_size-day.png +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/performance/stress-with-eviction/total_requests-day.png +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/admin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/management/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/management/commands/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/management/commands/print_phonehome.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/migrations/0001_b_squashed_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/migrations/0001_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/migrations/0002_create_installation_id.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/migrations/0002_installation_email_quota_usage.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/migrations/0003_installation_ingest_quotas.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/migrations/0003_outboundmessage.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/migrations/0004_installation_created_at.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/migrations/0004_installation_quota_exceeded_reason.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/migrations/0005_installation_silence_email_system_warning.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/migrations/0005_reset_quota_exceeded_until.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/migrations/0006_recalculate_installation_next_quota_check.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/migrations/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/models.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/tasks.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/utils.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/phonehome/views.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/pre-commit +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/admin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/context_processors.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/forms.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0001_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0002_b_squashed_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0002_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0003_project_projects_pr_name_11d782_idx.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0003_project_retention_max_event_count.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0004_project_quota_exceeded_until.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0005_project_ingested_event_count.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0006_initial_ingested_count_value.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0007_rename_ingested_event_count_project_digested_event_count.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0008_project_next_quota_check.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0009_alter_project_visibility.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0010_project_stored_event_count.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0011_fill_stored_event_count.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0012_project_is_deleted.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0013_delete_objects_pointing_to_null_project.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0014_alter_projectmembership_project.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0015_project_quota_exceeded_reason.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0016_reset_quota_exceeded_until.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/0017_project_issue_count.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/migrations/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/models.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/serializers.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/tasks.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/mails/project_membership_invite.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/mails/project_membership_invite.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/mails/project_membership_invite_new_user.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/mails/project_membership_invite_new_user.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_alerts_setup.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_edit.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_list.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_member_settings.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_members.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_members_accept.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_members_invite.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_messaging_service_edit.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_messaging_service_new.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_new.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_sdk_setup.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_sdk_setup_javascript.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_sdk_setup_php.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/templates/projects/project_sdk_setup_python.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/test_api.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/urls.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/projects/views.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/pyproject.toml +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/admin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/migrations/0001_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/migrations/0002_release_releases_re_sort_ep_5c07c8_idx.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/migrations/0003_alter_release_project.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/migrations/0004_fix_indexes.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/migrations/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/models.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/serializers.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/test_api.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/releases/views.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/requirements.development.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/requirements.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/sentry/LICENSE +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/sentry/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/sentry/assemble.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/sentry/minidump.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/sentry/stacktraces/functions.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/sentry/stacktraces/platform.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/sentry/stacktraces/processing.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/sentry/utils/safe.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/sentry/utils/strings.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/sentry_sdk_extensions/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/sentry_sdk_extensions/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/sentry_sdk_extensions/transport.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/setup.cfg +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/admin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/datastructures.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/dbrouters.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/decorators.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/example_tasks.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/foreman.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/management/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/management/commands/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/management/commands/checksnappea.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/management/commands/runsnappea.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/migrations/0001_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/migrations/0002_create_models.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/migrations/0003_task_created_at.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/migrations/0004_task_snappea_tas_created_eb0824_idx.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/migrations/0005_stat.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/migrations/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/models.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/settings.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/stats.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/utils.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/snappea/views.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/favicon.png +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/fonts/ibm-plex-mono-v19-cyrillic_cyrillic-ext_latin_latin-ext_vietnamese-700.woff2 +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/fonts/ibm-plex-mono-v19-cyrillic_cyrillic-ext_latin_latin-ext_vietnamese-700italic.woff2 +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/fonts/ibm-plex-mono-v19-cyrillic_cyrillic-ext_latin_latin-ext_vietnamese-italic.woff2 +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/fonts/ibm-plex-mono-v19-cyrillic_cyrillic-ext_latin_latin-ext_vietnamese-regular.woff2 +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/fonts/ibm-plex-sans-v19-cyrillic_cyrillic-ext_greek_latin_latin-ext_vietnamese-700.woff2 +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/fonts/ibm-plex-sans-v19-cyrillic_cyrillic-ext_greek_latin_latin-ext_vietnamese-700italic.woff2 +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/fonts/ibm-plex-sans-v19-cyrillic_cyrillic-ext_greek_latin_latin-ext_vietnamese-italic.woff2 +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/fonts/ibm-plex-sans-v19-cyrillic_cyrillic-ext_greek_latin_latin-ext_vietnamese-regular.woff2 +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/images/bugsink-logo-dark.png +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/images/bugsink-logo.png +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/images/lang-javascript.png +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/images/lang-php.png +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/images/lang-python.png +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/js/entity_edit.js +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/js/issue_history.js +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/js/issue_list.js +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/js/issue_stacktrace.js +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/js/project_list.js +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/js/team_list.js +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/static/js/user_list.js +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/admin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/management/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/management/commands/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/management/commands/init_tags.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/management/commands/vacuum_eventless_issuetags.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/management/commands/vacuum_tags.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/migrations/0001_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/migrations/0002_no_cascade.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/migrations/0003_remove_objects_with_null_issue.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/migrations/0004_alter_do_nothing.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/migrations/0005_alter_eventtag_project_alter_issuetag_project_and_more.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/migrations/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/models.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/search.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/tasks.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tags/utils.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/admin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/forms.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/migrations/0001_b_squashed_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/migrations/0001_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/migrations/0002_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/migrations/0002_team_teams_team_name_43e047_idx.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/migrations/0003_alter_team_visibility.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/migrations/0004_remove_team_slug.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/migrations/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/models.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/serializers.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/tasks.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/templates/mails/team_membership_invite.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/templates/mails/team_membership_invite.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/templates/mails/team_membership_invite_new_user.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/templates/mails/team_membership_invite_new_user.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/templates/teams/team_edit.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/templates/teams/team_list.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/templates/teams/team_member_settings.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/templates/teams/team_members.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/templates/teams/team_members_accept.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/templates/teams/team_members_invite.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/templates/teams/team_new.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/test_api.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/urls.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/teams/views.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/400.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/403.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/403_csrf.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/404.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/4xx_5xx_api.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/500.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/503.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/admin/change_form_object_tools.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/bugsink/counts.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/bugsink/csrf_debug.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/bugsink/login.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/bugsink/settings.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/robots.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/templates/signup.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/static_src/.gitignore +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/static_src/package-lock.json +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/static_src/package.json +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/static_src/postcss.config.js +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/static_src/src/styles.css +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/static_src/tailwind.config.js +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/templates/bare_base.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/templates/barest_base.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/templates/base.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/templates/tailwind_forms/formfield.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/templatetags/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/templatetags/add_to_qs.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/templatetags/code.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/templatetags/issues.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/templatetags/stricter_templates.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/templatetags/tailwind_forms.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/templatetags/user.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/templatetags/version.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/theme/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tools/is_tracked_by_tailwind.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/tools/strip-trailing-whitespace.sh +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/admin.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/apps.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/forms.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/management/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/management/commands/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/management/commands/send_welcome_email.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/migrations/0001_initial.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/migrations/0002_user_theme_preference.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/migrations/0003_user_language.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/migrations/0004_alter_user_language.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/migrations/__init__.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/models.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/tasks.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/mails/confirm_email.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/mails/confirm_email.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/mails/reset_password_email.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/mails/reset_password_email.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/mails/welcome_email.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/mails/welcome_email.txt +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/users/confirm_email.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/users/confirm_email_sent.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/users/logged_out.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/users/preferences.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/users/request_reset_password.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/users/resend_confirmation.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/users/reset_password.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/users/reset_password_email_sent.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/users/user_edit.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/templates/users/user_list.html +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/tests.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/urls.py +0 -0
- {bugsink-2.2.0 → bugsink-2.2.1}/users/views.py +0 -0
|
@@ -25,9 +25,9 @@ jobs:
|
|
|
25
25
|
ruff:
|
|
26
26
|
runs-on: ubuntu-latest
|
|
27
27
|
steps:
|
|
28
|
-
- uses: actions/checkout@
|
|
28
|
+
- uses: actions/checkout@v6
|
|
29
29
|
- name: "Set up Python 3.12"
|
|
30
|
-
uses: actions/setup-python@
|
|
30
|
+
uses: actions/setup-python@v6
|
|
31
31
|
with:
|
|
32
32
|
python-version: "3.12"
|
|
33
33
|
- name: Install Ruff
|
|
@@ -40,10 +40,10 @@ jobs:
|
|
|
40
40
|
bandit:
|
|
41
41
|
runs-on: ubuntu-latest
|
|
42
42
|
steps:
|
|
43
|
-
- uses: actions/checkout@
|
|
43
|
+
- uses: actions/checkout@v6
|
|
44
44
|
|
|
45
45
|
- name: Set up Python
|
|
46
|
-
uses: actions/setup-python@
|
|
46
|
+
uses: actions/setup-python@v6
|
|
47
47
|
with:
|
|
48
48
|
python-version: 3.12
|
|
49
49
|
|
|
@@ -106,9 +106,9 @@ jobs:
|
|
|
106
106
|
DB_USER: ${{ matrix.db_user }}
|
|
107
107
|
DB_PASSWORD: ${{ matrix.db_password }}
|
|
108
108
|
steps:
|
|
109
|
-
- uses: actions/checkout@
|
|
109
|
+
- uses: actions/checkout@v6
|
|
110
110
|
- name: Set up Python ${{ matrix.python-version }}
|
|
111
|
-
uses: actions/setup-python@
|
|
111
|
+
uses: actions/setup-python@v6
|
|
112
112
|
with:
|
|
113
113
|
python-version: ${{ matrix.python-version }}
|
|
114
114
|
- name: Start MySQL If Needed
|
|
@@ -134,7 +134,7 @@ jobs:
|
|
|
134
134
|
pip install -r requirements.development.txt
|
|
135
135
|
pip install mysqlclient psycopg2
|
|
136
136
|
- name: Check out event-samples
|
|
137
|
-
uses: actions/checkout@
|
|
137
|
+
uses: actions/checkout@v6
|
|
138
138
|
with:
|
|
139
139
|
repository: bugsink/event-samples
|
|
140
140
|
path: "event-samples"
|
|
@@ -169,17 +169,17 @@ jobs:
|
|
|
169
169
|
# Build the Docker image, start it, wait for readiness, log in at /accounts/login/,
|
|
170
170
|
# and verify that / redirects to /teams/.
|
|
171
171
|
steps:
|
|
172
|
-
- uses: actions/checkout@
|
|
172
|
+
- uses: actions/checkout@v6
|
|
173
173
|
|
|
174
174
|
- name: Build Docker image
|
|
175
175
|
run: docker build -t bugsink-ci .
|
|
176
176
|
|
|
177
|
-
- name: Run container (
|
|
177
|
+
- name: Run container (no-reply@bugsink.com:admin)
|
|
178
178
|
run: |
|
|
179
179
|
docker rm -f bugsink-ci || true
|
|
180
180
|
docker run -d --name bugsink-ci \
|
|
181
181
|
-e SECRET_KEY=UtdHATKzQKLkz3izFFpxCKamhEdspeHmCQFvLx1DAwskqEuyKL \
|
|
182
|
-
-e CREATE_SUPERUSER=
|
|
182
|
+
-e CREATE_SUPERUSER=no-reply@bugsink.com:admin \
|
|
183
183
|
-e PORT=8000 \
|
|
184
184
|
-p 8000:8000 \
|
|
185
185
|
bugsink-ci
|
|
@@ -214,7 +214,7 @@ jobs:
|
|
|
214
214
|
CODE=$(curl -sS -b cookies.txt -c cookies.txt \
|
|
215
215
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
216
216
|
-H "Referer: http://localhost:8000/accounts/login/" \
|
|
217
|
-
-d "username=
|
|
217
|
+
-d "username=no-reply@bugsink.com&password=admin&csrfmiddlewaretoken=${CSRF}&next=/" \
|
|
218
218
|
-o /dev/null -w "%{http_code}" \
|
|
219
219
|
http://localhost:8000/accounts/login/)
|
|
220
220
|
|
|
@@ -236,3 +236,7 @@ jobs:
|
|
|
236
236
|
fi
|
|
237
237
|
|
|
238
238
|
echo "Login OK and / redirects to /teams/"
|
|
239
|
+
|
|
240
|
+
- name: Show Docker logs
|
|
241
|
+
if: always()
|
|
242
|
+
run: docker logs bugsink-ci || true
|
|
@@ -19,14 +19,14 @@ jobs:
|
|
|
19
19
|
SAMPLES_DIR: ${{ github.workspace }}/event-samples
|
|
20
20
|
steps:
|
|
21
21
|
- name: Check out code
|
|
22
|
-
uses: actions/checkout@
|
|
22
|
+
uses: actions/checkout@v6
|
|
23
23
|
|
|
24
24
|
- name: Install project `pre-commit` hook
|
|
25
25
|
run: |
|
|
26
26
|
cp pre-commit .git/hooks/pre-commit
|
|
27
27
|
|
|
28
28
|
- name: Set up Python 3.12
|
|
29
|
-
uses: actions/setup-python@
|
|
29
|
+
uses: actions/setup-python@v6
|
|
30
30
|
with:
|
|
31
31
|
python-version: "3.12"
|
|
32
32
|
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
-
## 2.2.
|
|
3
|
+
## 2.2.1 (22 May 2026)
|
|
4
|
+
|
|
5
|
+
### API
|
|
6
|
+
|
|
7
|
+
* Add issue actions to the canonical API, see #214 and #401.
|
|
8
|
+
* Add canonical API issue comment creation, see #352 and #401.
|
|
9
|
+
* Accept issue friendly IDs in the canonical API, see #389 and #401.
|
|
10
|
+
* Improve OpenAPI endpoint docs, see #390 and #401.
|
|
11
|
+
* Use Bugsink's version as the OpenAPI spec version, see #307.
|
|
12
|
+
|
|
13
|
+
### Smaller fixes
|
|
14
|
+
|
|
15
|
+
* Fix long module names overlapping version values on event detail pages, see #377 and #382.
|
|
16
|
+
* Validate `CREATE_SUPERUSER` email addresses and use the email as username in Docker server-start setup, see #394.
|
|
17
|
+
* Development server: do not send email by default, see d6d5190441b3.
|
|
18
|
+
* Docker: disable Gunicorn's unused control socket, see #405.
|
|
19
|
+
* Sourcemap uploads without a project slug now log server-side context before returning `400`, see #404 and #408.
|
|
20
|
+
|
|
21
|
+
## 2.2.0 (21 May 2026)
|
|
4
22
|
|
|
5
23
|
### Security
|
|
6
24
|
|
|
@@ -26,6 +44,7 @@ https://github.com/bugsink/bugsink/security/advisories/GHSA-5389-f7vh-wxj8
|
|
|
26
44
|
|
|
27
45
|
### Smaller fixes
|
|
28
46
|
|
|
47
|
+
* Fix health-check `ALLOWED_HOSTS`-ignore, see #140, #397
|
|
29
48
|
* Generate an `event_id` on `/store/` when the SDK does not send one, see #383.
|
|
30
49
|
* Refresh issue title fields on every event digest, see #378.
|
|
31
50
|
* Include ingest-dir cleanup in the `vacuum` command and warn about stale ingest-dir files, see 772fb1a9bff6 and
|
|
@@ -2,6 +2,8 @@ import os
|
|
|
2
2
|
|
|
3
3
|
from django.core.management.base import BaseCommand
|
|
4
4
|
from django.contrib.auth import get_user_model
|
|
5
|
+
from django.core.exceptions import ValidationError
|
|
6
|
+
from django.core.validators import validate_email
|
|
5
7
|
|
|
6
8
|
from phonehome.tasks import send_if_due
|
|
7
9
|
|
|
@@ -17,7 +19,7 @@ class Command(BaseCommand):
|
|
|
17
19
|
return
|
|
18
20
|
|
|
19
21
|
if ":" not in os.getenv("CREATE_SUPERUSER"):
|
|
20
|
-
raise ValueError("CREATE_SUPERUSER should be in the format '
|
|
22
|
+
raise ValueError("CREATE_SUPERUSER should be in the format 'email:password'")
|
|
21
23
|
|
|
22
24
|
username, password = os.getenv("CREATE_SUPERUSER").split(":", 1)
|
|
23
25
|
|
|
@@ -28,7 +30,14 @@ class Command(BaseCommand):
|
|
|
28
30
|
|
|
29
31
|
return
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
# Validate only when we're actually creating the user, so existing installations with a stale
|
|
34
|
+
# CREATE_SUPERUSER value keep starting normally.
|
|
35
|
+
try:
|
|
36
|
+
validate_email(username)
|
|
37
|
+
except ValidationError as e:
|
|
38
|
+
raise ValueError("CREATE_SUPERUSER email should be a valid email address") from e
|
|
39
|
+
|
|
40
|
+
User.objects.create_superuser(username=username, email=username, password=password)
|
|
32
41
|
print(f"Superuser created: {username}")
|
|
33
42
|
|
|
34
43
|
def handle(self, *args, **options):
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (2, 2,
|
|
21
|
+
__version__ = version = '2.2.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 2, 1)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'gea814dc8e'
|
|
@@ -6,6 +6,9 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
from django.utils.log import DEFAULT_LOGGING
|
|
8
8
|
|
|
9
|
+
from ..version import version as __version__
|
|
10
|
+
|
|
11
|
+
|
|
9
12
|
# We have a single file for our default settings, and expect (if they use the singleserver setup) the end-users to
|
|
10
13
|
# configure their setup using a single bugsink_conf.py also. To be able to have (slightly) different settings for e.g.
|
|
11
14
|
# logging for various commands, we expose a variable I_AM_RUNNING that can be used to determine what command is being
|
|
@@ -90,7 +93,7 @@ REST_FRAMEWORK = {
|
|
|
90
93
|
SPECTACULAR_SETTINGS = {
|
|
91
94
|
'TITLE': 'Bugsink',
|
|
92
95
|
'DESCRIPTION': 'Bugsink API Documentation',
|
|
93
|
-
'VERSION':
|
|
96
|
+
'VERSION': __version__,
|
|
94
97
|
'SERVE_INCLUDE_SCHEMA': False, # keep the docs clean and not document the docs endpoint itself.
|
|
95
98
|
|
|
96
99
|
"SECURITY": [
|
|
@@ -73,11 +73,12 @@ SNAPPEA = {
|
|
|
73
73
|
"PID_FILE": "/tmp/bugsink/snappea.pid", # nosec B108
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
|
77
|
+
# EMAIL_HOST = os.getenv("EMAIL_HOST")
|
|
78
|
+
# EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER")
|
|
79
|
+
# EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD")
|
|
80
|
+
# EMAIL_PORT = 587
|
|
81
|
+
# EMAIL_USE_TLS = True
|
|
81
82
|
|
|
82
83
|
SERVER_EMAIL = DEFAULT_FROM_EMAIL = 'Bugsink Development Server <bugsink@example.org>'
|
|
83
84
|
|
|
@@ -18,7 +18,7 @@ from files.views import chunk_upload, artifact_bundle_assemble, difs_assemble, a
|
|
|
18
18
|
from bugsink.decorators import login_exempt
|
|
19
19
|
|
|
20
20
|
from events.api_views import EventViewSet
|
|
21
|
-
from issues.api_views import IssueViewSet
|
|
21
|
+
from issues.api_views import IssueCommentViewSet, IssueViewSet
|
|
22
22
|
from projects.api_views import ProjectViewSet
|
|
23
23
|
from releases.api_views import ReleaseViewSet
|
|
24
24
|
from teams.api_views import TeamViewSet
|
|
@@ -34,6 +34,7 @@ admin.site.index_title = "Admin" # everyone calls this the "admin" anyway. Let'
|
|
|
34
34
|
|
|
35
35
|
api_router = routers.DefaultRouter()
|
|
36
36
|
api_router.register(r'events', EventViewSet)
|
|
37
|
+
api_router.register(r'issue-comments', IssueCommentViewSet, basename='issue-comment')
|
|
37
38
|
api_router.register(r'issues', IssueViewSet)
|
|
38
39
|
api_router.register(r'projects', ProjectViewSet)
|
|
39
40
|
api_router.register(r'releases', ReleaseViewSet)
|
|
@@ -3,12 +3,13 @@ from rest_framework import viewsets
|
|
|
3
3
|
from rest_framework.exceptions import ValidationError
|
|
4
4
|
from rest_framework.decorators import action
|
|
5
5
|
from rest_framework.response import Response
|
|
6
|
-
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes, OpenApiResponse
|
|
6
|
+
from drf_spectacular.utils import extend_schema, OpenApiExample, OpenApiParameter, OpenApiTypes, OpenApiResponse
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
from bugsink.utils import assert_
|
|
10
10
|
from bugsink.api_pagination import AscDescCursorPagination
|
|
11
11
|
from bugsink.api_mixins import AtomicRequestMixin
|
|
12
|
+
from issues.models import issue_lookup_kwargs
|
|
12
13
|
|
|
13
14
|
from .models import Event
|
|
14
15
|
from .serializers import EventListSerializer, EventDetailSerializer
|
|
@@ -26,12 +27,6 @@ class EventPagination(AscDescCursorPagination):
|
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class EventViewSet(AtomicRequestMixin, viewsets.ReadOnlyModelViewSet):
|
|
29
|
-
"""
|
|
30
|
-
LIST requires: ?issue=<uuid>
|
|
31
|
-
Optional: ?order=asc|desc (default: desc)
|
|
32
|
-
LIST omits `data`, ordered by digest_order
|
|
33
|
-
RETRIEVE includes `data` (pure PK lookup; no filters/order applied)
|
|
34
|
-
"""
|
|
35
30
|
queryset = Event.objects.all() # router requirement for basename inference
|
|
36
31
|
serializer_class = EventListSerializer
|
|
37
32
|
pagination_class = EventPagination
|
|
@@ -42,16 +37,19 @@ class EventViewSet(AtomicRequestMixin, viewsets.ReadOnlyModelViewSet):
|
|
|
42
37
|
if "issue" not in query_params:
|
|
43
38
|
raise ValidationError({"issue": ["This field is required."]})
|
|
44
39
|
|
|
45
|
-
|
|
40
|
+
lookup_kwargs = {"issue__" + k: v for k, v in issue_lookup_kwargs(query_params["issue"]).items()}
|
|
41
|
+
return queryset.filter(issue__is_deleted=False, **lookup_kwargs)
|
|
46
42
|
|
|
47
43
|
@extend_schema(
|
|
44
|
+
summary="List events",
|
|
45
|
+
description="List events for an issue. The list response omits the full event `data` payload.",
|
|
48
46
|
parameters=[
|
|
49
47
|
OpenApiParameter(
|
|
50
48
|
name="issue",
|
|
51
|
-
type=OpenApiTypes.
|
|
49
|
+
type=OpenApiTypes.STR,
|
|
52
50
|
location=OpenApiParameter.QUERY,
|
|
53
51
|
required=True,
|
|
54
|
-
description="Filter events by issue UUID (required).",
|
|
52
|
+
description="Filter events by issue UUID or friendly ID (required).",
|
|
55
53
|
),
|
|
56
54
|
OpenApiParameter(
|
|
57
55
|
name="order",
|
|
@@ -66,6 +64,17 @@ class EventViewSet(AtomicRequestMixin, viewsets.ReadOnlyModelViewSet):
|
|
|
66
64
|
def list(self, request, *args, **kwargs):
|
|
67
65
|
return super().list(request, *args, **kwargs)
|
|
68
66
|
|
|
67
|
+
@extend_schema(
|
|
68
|
+
summary="Retrieve an event",
|
|
69
|
+
description=(
|
|
70
|
+
"Retrieve an event by internal Bugsink event UUID. "
|
|
71
|
+
"The detail response includes the full `data` payload."
|
|
72
|
+
),
|
|
73
|
+
responses=EventDetailSerializer,
|
|
74
|
+
)
|
|
75
|
+
def retrieve(self, request, *args, **kwargs):
|
|
76
|
+
return super().retrieve(request, *args, **kwargs)
|
|
77
|
+
|
|
69
78
|
def get_object(self):
|
|
70
79
|
"""
|
|
71
80
|
DRF's get_object(), but we intentionally bypass filter_queryset for detail routes to keep PK lookups
|
|
@@ -94,8 +103,21 @@ class EventViewSet(AtomicRequestMixin, viewsets.ReadOnlyModelViewSet):
|
|
|
94
103
|
return EventDetailSerializer if self.action == "retrieve" else EventListSerializer
|
|
95
104
|
|
|
96
105
|
@extend_schema(
|
|
106
|
+
summary="Render an event stacktrace",
|
|
97
107
|
description="Render the event's stacktrace (frames, source, locals) as Markdown-like text.",
|
|
98
|
-
responses={
|
|
108
|
+
responses={
|
|
109
|
+
200: OpenApiResponse(
|
|
110
|
+
response=str,
|
|
111
|
+
description="Stacktrace as Markdown",
|
|
112
|
+
examples=[
|
|
113
|
+
OpenApiExample(
|
|
114
|
+
"Stacktrace",
|
|
115
|
+
value="Traceback (most rece...",
|
|
116
|
+
response_only=True,
|
|
117
|
+
),
|
|
118
|
+
],
|
|
119
|
+
)
|
|
120
|
+
},
|
|
99
121
|
)
|
|
100
122
|
@action(
|
|
101
123
|
detail=True,
|
|
@@ -238,6 +238,7 @@ def artifact_bundle_assemble(request, organization_slug):
|
|
|
238
238
|
chunk_checksums = data["chunks"]
|
|
239
239
|
projects, error = get_artifact_bundle_projects(data)
|
|
240
240
|
if error is not None:
|
|
241
|
+
logger.warning("Rejected artifact bundle: project slug is missing or does not match an existing project.")
|
|
241
242
|
return JsonResponse({"error": error}, status=400)
|
|
242
243
|
|
|
243
244
|
# sentry-cli >= 3.x calls this endpoint before uploading chunks (to learn which ones are missing), then uploads
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
from django.shortcuts import get_object_or_404
|
|
2
|
+
from rest_framework import mixins, status, viewsets
|
|
3
|
+
from rest_framework.decorators import action
|
|
4
|
+
from rest_framework.pagination import CursorPagination
|
|
5
|
+
from rest_framework.exceptions import ValidationError
|
|
6
|
+
from rest_framework.response import Response
|
|
7
|
+
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes
|
|
8
|
+
|
|
9
|
+
from bugsink.api_mixins import AtomicRequestMixin
|
|
10
|
+
from bugsink.utils import assert_
|
|
11
|
+
|
|
12
|
+
from .models import Issue, IssueStateManager, TurningPoint, apply_issue_action, issue_lookup_kwargs
|
|
13
|
+
from .serializers import (
|
|
14
|
+
IssueCommentSerializer,
|
|
15
|
+
IssueMuteForSerializer,
|
|
16
|
+
IssueMuteUntilSerializer,
|
|
17
|
+
IssueSerializer,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class IssuesCursorPagination(CursorPagination):
|
|
22
|
+
"""
|
|
23
|
+
Cursor paginator for /issues supporting ?sort=… and ?order=asc|desc.
|
|
24
|
+
|
|
25
|
+
Sort modes are named after the *primary* column:
|
|
26
|
+
- sort=digest_order → unique per project → no tie-breakers needed
|
|
27
|
+
- sort=last_seen → timestamp → tie-breaker on id
|
|
28
|
+
|
|
29
|
+
Direction applies to primary *and beyond* (i.e. all fields in the list).
|
|
30
|
+
The view MUST filter by project; ordering is handled here.
|
|
31
|
+
"""
|
|
32
|
+
# Cursor pagination requires an indexed, mostly-stable ordering. Stable mode: sort=digest_order (default). We
|
|
33
|
+
# require ?project=<uuid> and have a composite (project_id, digest_order) index, so ORDER BY digest_order after
|
|
34
|
+
# filtering by project is fast and cursor-stable.
|
|
35
|
+
|
|
36
|
+
# We also offer a "recent" mode: sort=last_seen. This is not stable, as new events can come in mid-cursor, and
|
|
37
|
+
# reshuffle things causing misses or duplicates. However, this is the desired UX for a "recent activity" view.
|
|
38
|
+
# i.e. the typical usage would in fact just be to get the "first page" of recent activity.
|
|
39
|
+
page_size = 250
|
|
40
|
+
default_direction = "asc"
|
|
41
|
+
default_sort = "digest_order"
|
|
42
|
+
|
|
43
|
+
VALID_SORTS = ("digest_order", "last_seen")
|
|
44
|
+
VALID_ORDERS = ("asc", "desc")
|
|
45
|
+
|
|
46
|
+
def get_ordering(self, request, queryset, view):
|
|
47
|
+
sort = request.query_params.get("sort", self.default_sort)
|
|
48
|
+
if sort not in self.VALID_SORTS:
|
|
49
|
+
raise ValidationError({"sort": ["Must be 'digest_order' or 'last_seen'."]})
|
|
50
|
+
|
|
51
|
+
order = request.query_params.get("order", self.default_direction)
|
|
52
|
+
if order not in self.VALID_ORDERS:
|
|
53
|
+
raise ValidationError({"order": ["Must be 'asc' or 'desc'."]})
|
|
54
|
+
|
|
55
|
+
desc = (order == "desc")
|
|
56
|
+
|
|
57
|
+
if sort == "digest_order":
|
|
58
|
+
# Unique per project; stable cursor once filtered by project.
|
|
59
|
+
return ["-digest_order" if desc else "digest_order"]
|
|
60
|
+
|
|
61
|
+
# sort == "last_seen": timestamp needs a deterministic tie-breaker.
|
|
62
|
+
if desc:
|
|
63
|
+
return ["-last_seen", "-id"]
|
|
64
|
+
return ["last_seen", "id"]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class IssueViewSet(AtomicRequestMixin, viewsets.ReadOnlyModelViewSet):
|
|
68
|
+
queryset = Issue.objects.filter(is_deleted=False).select_related("project") # hide soft-deleted; router basename
|
|
69
|
+
serializer_class = IssueSerializer
|
|
70
|
+
pagination_class = IssuesCursorPagination
|
|
71
|
+
http_method_names = ["get", "post", "delete", "head", "options"]
|
|
72
|
+
|
|
73
|
+
def get_queryset(self):
|
|
74
|
+
return self.queryset
|
|
75
|
+
|
|
76
|
+
@extend_schema(
|
|
77
|
+
summary="List issues",
|
|
78
|
+
description="List issues for a project.",
|
|
79
|
+
parameters=[
|
|
80
|
+
OpenApiParameter(
|
|
81
|
+
name="project",
|
|
82
|
+
type=OpenApiTypes.INT,
|
|
83
|
+
location=OpenApiParameter.QUERY,
|
|
84
|
+
required=True,
|
|
85
|
+
description="Filter issues by project id (required).",
|
|
86
|
+
),
|
|
87
|
+
OpenApiParameter(
|
|
88
|
+
name="sort",
|
|
89
|
+
type=OpenApiTypes.STR,
|
|
90
|
+
location=OpenApiParameter.QUERY,
|
|
91
|
+
required=False,
|
|
92
|
+
enum=["digest_order", "last_seen"],
|
|
93
|
+
description="Sort mode (default: digest_order).",
|
|
94
|
+
),
|
|
95
|
+
OpenApiParameter(
|
|
96
|
+
name="order",
|
|
97
|
+
type=OpenApiTypes.STR,
|
|
98
|
+
location=OpenApiParameter.QUERY,
|
|
99
|
+
required=False,
|
|
100
|
+
enum=["asc", "desc"],
|
|
101
|
+
description="Sort order (default: asc).",
|
|
102
|
+
),
|
|
103
|
+
]
|
|
104
|
+
)
|
|
105
|
+
def list(self, request, *args, **kwargs):
|
|
106
|
+
return super().list(request, *args, **kwargs)
|
|
107
|
+
|
|
108
|
+
@extend_schema(
|
|
109
|
+
summary="Retrieve an issue",
|
|
110
|
+
description="Retrieve an issue by issue UUID or friendly ID.",
|
|
111
|
+
responses=IssueSerializer,
|
|
112
|
+
)
|
|
113
|
+
def retrieve(self, request, *args, **kwargs):
|
|
114
|
+
return super().retrieve(request, *args, **kwargs)
|
|
115
|
+
|
|
116
|
+
@extend_schema(
|
|
117
|
+
summary="Delete an issue",
|
|
118
|
+
description="Delete an issue.",
|
|
119
|
+
)
|
|
120
|
+
def destroy(self, request, *args, **kwargs):
|
|
121
|
+
issue = self.get_object()
|
|
122
|
+
issue.delete_deferred()
|
|
123
|
+
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
124
|
+
|
|
125
|
+
def filter_queryset(self, queryset):
|
|
126
|
+
queryset = super().filter_queryset(queryset)
|
|
127
|
+
if self.action != "list":
|
|
128
|
+
return queryset
|
|
129
|
+
|
|
130
|
+
project = self.request.query_params.get("project")
|
|
131
|
+
if not project:
|
|
132
|
+
# the below at least until we have a UI for cross-project Issue listing, i.e. #190
|
|
133
|
+
raise ValidationError({"project": ["This field is required."]})
|
|
134
|
+
|
|
135
|
+
return queryset.filter(project=project)
|
|
136
|
+
|
|
137
|
+
def get_object(self):
|
|
138
|
+
"""
|
|
139
|
+
DRF's get_object(), but bypass filter_queryset for detail.
|
|
140
|
+
"""
|
|
141
|
+
# NOTE: alternatively, we just complain hard when a filter is applied to a detail view.
|
|
142
|
+
# TODO: copy/paste from events/api_views.py
|
|
143
|
+
queryset = self.get_queryset()
|
|
144
|
+
|
|
145
|
+
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
|
146
|
+
assert_(
|
|
147
|
+
lookup_url_kwarg in self.kwargs,
|
|
148
|
+
'Expected view %s to be called with a URL keyword argument named "%s".'
|
|
149
|
+
% (self.__class__.__name__, lookup_url_kwarg)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
obj = get_object_or_404(queryset, **issue_lookup_kwargs(self.kwargs[lookup_url_kwarg]))
|
|
153
|
+
self.check_object_permissions(self.request, obj)
|
|
154
|
+
return obj
|
|
155
|
+
|
|
156
|
+
def _action_response(self, issue):
|
|
157
|
+
issue.save()
|
|
158
|
+
return Response(self.get_serializer(issue).data)
|
|
159
|
+
|
|
160
|
+
def _assert_unresolved(self, issue):
|
|
161
|
+
if issue.is_resolved:
|
|
162
|
+
raise ValidationError({"detail": "Issue is already resolved."})
|
|
163
|
+
|
|
164
|
+
def _assert_unmuted(self, issue):
|
|
165
|
+
if issue.is_muted:
|
|
166
|
+
raise ValidationError({"detail": "Issue is already muted."})
|
|
167
|
+
|
|
168
|
+
def _apply_issue_action(self, issue, action):
|
|
169
|
+
# Bearer-token API auth currently represents a global token, not a user.
|
|
170
|
+
apply_issue_action(IssueStateManager, issue, action, user=None)
|
|
171
|
+
return self._action_response(issue)
|
|
172
|
+
|
|
173
|
+
@extend_schema(
|
|
174
|
+
summary="Resolve an issue",
|
|
175
|
+
description="Mark this issue as resolved.",
|
|
176
|
+
request=OpenApiTypes.NONE,
|
|
177
|
+
responses=IssueSerializer,
|
|
178
|
+
)
|
|
179
|
+
@action(detail=True, methods=["post"])
|
|
180
|
+
def resolve(self, request, pk=None):
|
|
181
|
+
issue = self.get_object()
|
|
182
|
+
self._assert_unresolved(issue)
|
|
183
|
+
return self._apply_issue_action(issue, "resolve")
|
|
184
|
+
|
|
185
|
+
@extend_schema(
|
|
186
|
+
summary="Resolve an issue in the next release",
|
|
187
|
+
description="Mark this issue as resolved by the next release.",
|
|
188
|
+
request=OpenApiTypes.NONE,
|
|
189
|
+
responses=IssueSerializer,
|
|
190
|
+
)
|
|
191
|
+
@action(detail=True, methods=["post"], url_path="resolve-next")
|
|
192
|
+
def resolve_next(self, request, pk=None):
|
|
193
|
+
issue = self.get_object()
|
|
194
|
+
self._assert_unresolved(issue)
|
|
195
|
+
return self._apply_issue_action(issue, "resolved_next")
|
|
196
|
+
|
|
197
|
+
@extend_schema(
|
|
198
|
+
summary="Resolve an issue in the latest release",
|
|
199
|
+
description="Mark this issue as resolved in the latest release.",
|
|
200
|
+
request=OpenApiTypes.NONE,
|
|
201
|
+
responses=IssueSerializer,
|
|
202
|
+
)
|
|
203
|
+
@action(detail=True, methods=["post"], url_path="resolve-latest")
|
|
204
|
+
def resolve_latest(self, request, pk=None):
|
|
205
|
+
issue = self.get_object()
|
|
206
|
+
self._assert_unresolved(issue)
|
|
207
|
+
if not issue.project.has_releases:
|
|
208
|
+
raise ValidationError({"detail": "Project has no releases."})
|
|
209
|
+
|
|
210
|
+
latest_release = issue.project.get_latest_release()
|
|
211
|
+
if latest_release.version + "\n" in issue.events_at:
|
|
212
|
+
raise ValidationError({"detail": "Issue has already occurred in the latest release."})
|
|
213
|
+
|
|
214
|
+
return self._apply_issue_action(issue, "resolved_release:" + latest_release.version)
|
|
215
|
+
|
|
216
|
+
@extend_schema(
|
|
217
|
+
summary="Mute an issue",
|
|
218
|
+
description="Mute this issue.",
|
|
219
|
+
request=OpenApiTypes.NONE,
|
|
220
|
+
responses=IssueSerializer,
|
|
221
|
+
)
|
|
222
|
+
@action(detail=True, methods=["post"])
|
|
223
|
+
def mute(self, request, pk=None):
|
|
224
|
+
issue = self.get_object()
|
|
225
|
+
self._assert_unresolved(issue)
|
|
226
|
+
self._assert_unmuted(issue)
|
|
227
|
+
return self._apply_issue_action(issue, "mute")
|
|
228
|
+
|
|
229
|
+
@extend_schema(
|
|
230
|
+
summary="Mute an issue for a period",
|
|
231
|
+
description="Mute this issue for a relative period, e.g. for 3 days.",
|
|
232
|
+
request=IssueMuteForSerializer,
|
|
233
|
+
responses=IssueSerializer,
|
|
234
|
+
)
|
|
235
|
+
@action(detail=True, methods=["post"], url_path="mute-for")
|
|
236
|
+
def mute_for(self, request, pk=None):
|
|
237
|
+
serializer = IssueMuteForSerializer(data=request.data)
|
|
238
|
+
serializer.is_valid(raise_exception=True)
|
|
239
|
+
period_name = serializer.validated_data["period_name"]
|
|
240
|
+
nr_of_periods = serializer.validated_data["nr_of_periods"]
|
|
241
|
+
|
|
242
|
+
issue = self.get_object()
|
|
243
|
+
self._assert_unresolved(issue)
|
|
244
|
+
self._assert_unmuted(issue)
|
|
245
|
+
return self._apply_issue_action(issue, f"mute_for:{period_name},{nr_of_periods},")
|
|
246
|
+
|
|
247
|
+
@extend_schema(
|
|
248
|
+
summary="Mute an issue until a threshold is reached",
|
|
249
|
+
description="Mute this issue until a threshold is reached, e.g. more than 10 events in 1 hour.",
|
|
250
|
+
request=IssueMuteUntilSerializer,
|
|
251
|
+
responses=IssueSerializer,
|
|
252
|
+
)
|
|
253
|
+
@action(detail=True, methods=["post"], url_path="mute-until")
|
|
254
|
+
def mute_until(self, request, pk=None):
|
|
255
|
+
serializer = IssueMuteUntilSerializer(data=request.data)
|
|
256
|
+
serializer.is_valid(raise_exception=True)
|
|
257
|
+
period_name = serializer.validated_data["period_name"]
|
|
258
|
+
nr_of_periods = serializer.validated_data["nr_of_periods"]
|
|
259
|
+
gte_threshold = serializer.validated_data["gte_threshold"]
|
|
260
|
+
|
|
261
|
+
issue = self.get_object()
|
|
262
|
+
self._assert_unresolved(issue)
|
|
263
|
+
self._assert_unmuted(issue)
|
|
264
|
+
return self._apply_issue_action(issue, f"mute_until:{period_name},{nr_of_periods},{gte_threshold}")
|
|
265
|
+
|
|
266
|
+
@extend_schema(
|
|
267
|
+
summary="Unmute an issue",
|
|
268
|
+
description="Unmute this issue.",
|
|
269
|
+
request=OpenApiTypes.NONE,
|
|
270
|
+
responses=IssueSerializer,
|
|
271
|
+
)
|
|
272
|
+
@action(detail=True, methods=["post"])
|
|
273
|
+
def unmute(self, request, pk=None):
|
|
274
|
+
issue = self.get_object()
|
|
275
|
+
self._assert_unresolved(issue)
|
|
276
|
+
if not issue.is_muted:
|
|
277
|
+
raise ValidationError({"detail": "Issue is not muted."})
|
|
278
|
+
|
|
279
|
+
return self._apply_issue_action(issue, "unmute")
|
|
280
|
+
|
|
281
|
+
# NOTE: No 'unresolve' action: reopen is intentionally not exposed in the UI either. See apply_issue_action.
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class IssueCommentViewSet(AtomicRequestMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):
|
|
285
|
+
queryset = TurningPoint.objects.none() # router basename only
|
|
286
|
+
serializer_class = IssueCommentSerializer
|
|
287
|
+
http_method_names = ["post", "head", "options"]
|
|
288
|
+
|
|
289
|
+
@extend_schema(
|
|
290
|
+
summary="Create an issue comment",
|
|
291
|
+
description="Add a comment to an issue. `issue` accepts an issue UUID or friendly ID.",
|
|
292
|
+
request=IssueCommentSerializer,
|
|
293
|
+
responses=IssueCommentSerializer,
|
|
294
|
+
)
|
|
295
|
+
def create(self, request, *args, **kwargs):
|
|
296
|
+
return super().create(request, *args, **kwargs)
|