From b79c5d0c1c8c10e34019f8cff2ad69f36e5e29bf Mon Sep 17 00:00:00 2001 From: Matias Bordese Date: Wed, 9 Oct 2024 15:10:39 -0300 Subject: [PATCH 1/4] Skip user notification if alert group is already resolved (#5145) Sometimes a task is queued and scheduled for later (or for a retry too), but the alert group is resolved by then. Skip notification in that case. --- engine/apps/alerts/tasks/notify_user.py | 14 +++++++++ engine/apps/alerts/tests/test_notify_user.py | 32 ++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/engine/apps/alerts/tasks/notify_user.py b/engine/apps/alerts/tasks/notify_user.py index 10f62b257e..53b881294c 100644 --- a/engine/apps/alerts/tasks/notify_user.py +++ b/engine/apps/alerts/tasks/notify_user.py @@ -419,6 +419,20 @@ def perform_notification(log_record_pk, use_default_notification_policy_fallback ).save() return + if alert_group.resolved: + # skip notification if alert group was resolved + UserNotificationPolicyLogRecord( + author=user, + type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED, + notification_policy=notification_policy, + reason="Skipped notification because alert group is resolved", + alert_group=alert_group, + notification_step=notification_policy.step if notification_policy else None, + notification_channel=notification_channel, + notification_error_code=None, + ).save() + return + if notification_channel == UserNotificationPolicy.NotificationChannel.SMS: phone_backend = PhoneBackend() phone_backend.notify_by_sms(user, alert_group, notification_policy) diff --git a/engine/apps/alerts/tests/test_notify_user.py b/engine/apps/alerts/tests/test_notify_user.py index 5c0d638178..7124f957d1 100644 --- a/engine/apps/alerts/tests/test_notify_user.py +++ b/engine/apps/alerts/tests/test_notify_user.py @@ -186,6 +186,38 @@ def test_notify_user_error_if_viewer( assert error_log_record.notification_error_code == UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_FORBIDDEN +@pytest.mark.django_db +def test_notify_user_perform_notification_skip_if_resolved( + make_organization, + make_user, + make_user_notification_policy, + make_alert_receive_channel, + make_alert_group, + make_user_notification_policy_log_record, +): + organization = make_organization() + user_1 = make_user(organization=organization, _verified_phone_number="1234567890") + user_notification_policy = make_user_notification_policy( + user=user_1, + step=UserNotificationPolicy.Step.NOTIFY, + notify_by=UserNotificationPolicy.NotificationChannel.SMS, + ) + alert_receive_channel = make_alert_receive_channel(organization=organization) + alert_group = make_alert_group(alert_receive_channel=alert_receive_channel, resolved=True) + log_record = make_user_notification_policy_log_record( + author=user_1, + alert_group=alert_group, + notification_policy=user_notification_policy, + type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_TRIGGERED, + ) + + perform_notification(log_record.pk, False) + + error_log_record = UserNotificationPolicyLogRecord.objects.last() + assert error_log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED + assert error_log_record.reason == "Skipped notification because alert group is resolved" + + @pytest.mark.django_db @pytest.mark.parametrize( "reason_to_skip_escalation,error_code", From cf589d45c637251c1318a38cb5bd657a54dda13b Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Wed, 9 Oct 2024 19:27:48 +0100 Subject: [PATCH 2/4] feat: add `timedeltaparse` function to the jinja template (#5142) # What this PR does If an OnCall template needs to contain a date relative to a date in the alert response, currently there is no way for the template to add or subtract time from a parsed date. This PR adds a function that allows a time-window (e.g., 1s, 5m, 6h, 7d, 2w) to be converted into a Python timedelta, which can then be added or subtracted from a datetime. An example usage might be: ``` {% set delta = alert.timeWindow | timedeltaparse %} {% set time = alert.startsAt | iso8601_to_time - delta | datetimeformat('%s') %} ``` ## Checklist - [X] Unit, integration, and e2e (if applicable) tests updated - [X] Documentation added (or `pr:no public docs` PR label added if not required) - [X] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes. --- .../advanced-templates/index.md | 2 + engine/common/jinja_templater/filters.py | 31 ++++++++++++- .../jinja_templater/jinja_template_env.py | 2 + .../common/tests/test_apply_jinja_template.py | 43 ++++++++++++++++++- .../CheatSheet/CheatSheet.config.ts | 2 +- 5 files changed, 77 insertions(+), 3 deletions(-) diff --git a/docs/sources/configure/jinja2-templating/advanced-templates/index.md b/docs/sources/configure/jinja2-templating/advanced-templates/index.md index a92f43afce..d98f642253 100644 --- a/docs/sources/configure/jinja2-templating/advanced-templates/index.md +++ b/docs/sources/configure/jinja2-templating/advanced-templates/index.md @@ -92,6 +92,8 @@ Grafana OnCall enhances Jinja with additional functions: - `datetimeformat_as_timezone`: Converts datetime to string with timezone conversion (`UTC` by default) - Usage example: `{{ payload.alerts.startsAt | iso8601_to_time | datetimeformat_as_timezone('%Y-%m-%dT%H:%M:%S%z', 'America/Chicago') }}` - `datetimeparse`: Converts string to datetime according to strftime format codes (`%H:%M / %d-%m-%Y` by default) +- `timedeltaparse`: Converts a time range (e.g., `5s`, `2m`, `6h`, `3d`) to a timedelta that can be added to or subtracted from a datetime + - Usage example: `{% set delta = alert.window | timedeltaparse %}{{ alert.startsAt | iso8601_to_time - delta | datetimeformat }}` - `regex_replace`: Performs a regex find and replace - `regex_match`: Performs a regex match, returns `True` or `False` - Usage example: `{{ payload.ruleName | regex_match(".*") }}` diff --git a/engine/common/jinja_templater/filters.py b/engine/common/jinja_templater/filters.py index 430967309b..742931d725 100644 --- a/engine/common/jinja_templater/filters.py +++ b/engine/common/jinja_templater/filters.py @@ -1,6 +1,6 @@ import base64 import json -from datetime import datetime +from datetime import datetime, timedelta import regex from django.utils.dateparse import parse_datetime @@ -37,6 +37,35 @@ def iso8601_to_time(value): return None +range_duration_re = regex.compile("^(?P[-+]?)(?P\\d+)(?P[smhdwMQy])$") + + +def timedeltaparse(value): + try: + match = range_duration_re.match(value) + if match: + kw = match.groupdict() + amount = int(kw["amount"]) + if kw["sign"] == "-": + amount = -amount + if kw["unit"] == "s": + return timedelta(seconds=amount) + elif kw["unit"] == "m": + return timedelta(minutes=amount) + elif kw["unit"] == "h": + return timedelta(hours=amount) + elif kw["unit"] == "d": + return timedelta(days=amount) + elif kw["unit"] == "w": + return timedelta(weeks=amount) + # The remaining units (MQy) are not supported by timedelta + else: + return None + except (ValueError, AttributeError, TypeError): + return None + return None + + def to_pretty_json(value): try: return json.dumps(value, sort_keys=True, indent=4, separators=(",", ": "), ensure_ascii=False) diff --git a/engine/common/jinja_templater/jinja_template_env.py b/engine/common/jinja_templater/jinja_template_env.py index f24a0c5517..910c287daf 100644 --- a/engine/common/jinja_templater/jinja_template_env.py +++ b/engine/common/jinja_templater/jinja_template_env.py @@ -14,6 +14,7 @@ regex_match, regex_replace, regex_search, + timedeltaparse, to_pretty_json, ) @@ -28,6 +29,7 @@ def raise_security_exception(name): jinja_template_env.filters["datetimeformat_as_timezone"] = datetimeformat_as_timezone jinja_template_env.filters["datetimeparse"] = datetimeparse jinja_template_env.filters["iso8601_to_time"] = iso8601_to_time +jinja_template_env.filters["timedeltaparse"] = timedeltaparse jinja_template_env.filters["tojson_pretty"] = to_pretty_json jinja_template_env.globals["time"] = timezone.now jinja_template_env.globals["range"] = lambda *args: raise_security_exception("range") diff --git a/engine/common/tests/test_apply_jinja_template.py b/engine/common/tests/test_apply_jinja_template.py index 3694ffa953..03fcec1c20 100644 --- a/engine/common/tests/test_apply_jinja_template.py +++ b/engine/common/tests/test_apply_jinja_template.py @@ -1,6 +1,6 @@ import base64 import json -from datetime import datetime +from datetime import datetime, timedelta from unittest.mock import patch import pytest @@ -140,6 +140,47 @@ def test_apply_jinja_template_datetimeparse(): ) == str(datetime.strptime(payload["naive"], "%Y-%m-%dT%H:%M:%S")) +def test_apply_jinja_template_timedeltaparse(): + payload = {"seconds": "-100s", "hours": "12h", "days": "-5d", "weeks": "52w"} + + assert apply_jinja_template( + "{{ payload.seconds | timedeltaparse }}", + payload, + ) == str(timedelta(seconds=-100)) + assert apply_jinja_template( + "{{ payload.hours | timedeltaparse }}", + payload, + ) == str(timedelta(hours=12)) + assert apply_jinja_template( + "{{ payload.days | timedeltaparse }}", + payload, + ) == str(timedelta(days=-5)) + assert apply_jinja_template( + "{{ payload.weeks | timedeltaparse }}", + payload, + ) == str(timedelta(weeks=52)) + + +def test_apply_jinja_template_timedelta_arithmetic(): + payload = { + "dt": "2023-11-22T15:30:00.000000000Z", + "delta": "1h", + "before": "2023-11-22T14:30:00.000000000Z", + "after": "2023-11-22T16:30:00.000000000Z", + } + + result = apply_jinja_template( + "{% set delta = payload.delta | timedeltaparse -%}{{ payload.dt | iso8601_to_time - delta }}", + payload, + ) + assert result == str(parse_datetime(payload["before"])) + result = apply_jinja_template( + "{% set delta = payload.delta | timedeltaparse -%}{{ payload.dt | iso8601_to_time + delta }}", + payload, + ) + assert result == str(parse_datetime(payload["after"])) + + def test_apply_jinja_template_b64decode(): payload = {"name": "SGVsbG8sIHdvcmxkIQ=="} diff --git a/grafana-plugin/src/components/CheatSheet/CheatSheet.config.ts b/grafana-plugin/src/components/CheatSheet/CheatSheet.config.ts index 66846368c1..5ed0cb6774 100644 --- a/grafana-plugin/src/components/CheatSheet/CheatSheet.config.ts +++ b/grafana-plugin/src/components/CheatSheet/CheatSheet.config.ts @@ -84,7 +84,7 @@ export const genericTemplateCheatSheet: CheatSheetInterface = { { listItemName: 'labels - labels assigned to the last alert in the group' }, { listItemName: 'web_title, web_mesage, web_image_url - templates from Web' }, { listItemName: 'payload, grafana_oncall_link, grafana_oncall_incident_id, integration_name, source_link' }, - { listItemName: 'time(), datetimeformat, datetimeformat_as_timezone, datetimeparse, iso8601_to_time' }, + { listItemName: 'time(), datetimeformat, datetimeformat_as_timezone, datetimeparse, iso8601_to_time, timedeltaparse' }, { listItemName: 'to_pretty_json' }, { listItemName: 'regex_replace, regex_match, regex_search' }, { listItemName: 'b64decode' }, From e11ae8a5f7ca79171f48c650d0ab1a63bf8c2902 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 18:31:03 +0000 Subject: [PATCH 3/4] Bump django from 4.2.15 to 4.2.16 in /engine (#5140) Bumps [django](https://github.com/django/django) from 4.2.15 to 4.2.16.
Commits
  • 6f9fea3 [4.2.x] Bumped version for 4.2.16 release.
  • bf4888d [4.2.x] Fixed CVE-2024-45231 -- Avoided server error on password reset when e...
  • d147a8e [4.2.x] Fixed CVE-2024-45230 -- Mitigated potential DoS in urlize and urlizet...
  • 705066d [4.2.x] Fixed grammatical error in stub release notes for upcoming security r...
  • b07d4f2 [4.2.x] Added stub release notes and release date for 4.2.16.
  • e0579ce [4.2.x] Added CVE-2024-41989, CVE-2024-41990, CVE-2024-41991, and CVE-2024-42...
  • ae0ca83 [4.2.x] Post-release version bump.
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=django&package-manager=pip&previous-version=4.2.15&new-version=4.2.16)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/grafana/oncall/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- engine/requirements-dev.txt | 12 ++-- engine/requirements.in | 2 +- engine/requirements.txt | 138 +++++++++++++++++------------------- 3 files changed, 72 insertions(+), 80 deletions(-) diff --git a/engine/requirements-dev.txt b/engine/requirements-dev.txt index a49c02b3dc..6a7b88bf3d 100644 --- a/engine/requirements-dev.txt +++ b/engine/requirements-dev.txt @@ -18,21 +18,21 @@ charset-normalizer==3.3.2 # requests distlib==0.3.8 # via virtualenv -django==4.2.15 +django==4.2.16 # via # -c requirements.txt # django-stubs # django-stubs-ext django-filter-stubs==0.1.3 # via -r requirements-dev.in -django-stubs==4.2.2 +django-stubs[compatible-mypy]==4.2.2 # via # -r requirements-dev.in # django-filter-stubs # djangorestframework-stubs django-stubs-ext==4.2.7 # via django-stubs -djangorestframework-stubs==3.14.2 +djangorestframework-stubs[compatible-mypy]==3.14.2 # via # -r requirements-dev.in # django-filter-stubs @@ -96,7 +96,7 @@ pytest-django==4.8.0 # via -r requirements-dev.in pytest-factoryboy==2.7.0 # via -r requirements-dev.in -pytest-xdist==3.6.1 +pytest-xdist[psutil]==3.6.1 # via -r requirements-dev.in python-dateutil==2.8.2 # via @@ -110,10 +110,6 @@ requests==2.32.3 # via # -c requirements.txt # djangorestframework-stubs -setuptools==73.0.0 - # via - # -c requirements.txt - # nodeenv six==1.16.0 # via # -c requirements.txt diff --git a/engine/requirements.in b/engine/requirements.in index 323847ab4f..5df7561312 100644 --- a/engine/requirements.in +++ b/engine/requirements.in @@ -2,7 +2,7 @@ babel==2.12.1 beautifulsoup4==4.12.2 celery[redis]==5.3.1 cryptography==43.0.1 -django==4.2.15 +django==4.2.16 django-add-default-value==0.10.0 django-amazon-ses==4.0.1 django-anymail==11.1 diff --git a/engine/requirements.txt b/engine/requirements.txt index ce4d1e6b99..1ded95f66c 100644 --- a/engine/requirements.txt +++ b/engine/requirements.txt @@ -15,9 +15,9 @@ attrs==23.2.0 autopep8==2.0.4 # via django-silk babel==2.12.1 - # via -r engine/requirements.in + # via -r requirements.in beautifulsoup4==4.12.2 - # via -r engine/requirements.in + # via -r requirements.in billiard==4.2.0 # via celery blinker==1.7.0 @@ -34,8 +34,8 @@ cachetools==4.2.2 # via # google-auth # python-telegram-bot -celery==5.3.1 - # via -r engine/requirements.in +celery[redis]==5.3.1 + # via -r requirements.in certifi==2024.7.4 # via # python-telegram-bot @@ -62,7 +62,7 @@ click-repl==0.3.0 # via celery cryptography==43.0.1 # via - # -r engine/requirements.in + # -r requirements.in # django-mirage-field # pyopenssl # social-auth-core @@ -75,9 +75,9 @@ deprecated==1.2.14 # opentelemetry-api # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-semantic-conventions -django==4.2.15 +django==4.2.16 # via - # -r engine/requirements.in + # -r requirements.in # django-add-default-value # django-amazon-ses # django-anymail @@ -98,67 +98,67 @@ django==4.2.15 # fcm-django # social-auth-app-django django-add-default-value==0.10.0 - # via -r engine/requirements.in + # via -r requirements.in django-amazon-ses==4.0.1 - # via -r engine/requirements.in + # via -r requirements.in django-anymail==11.1 - # via -r engine/requirements.in + # via -r requirements.in django-cors-headers==3.7.0 - # via -r engine/requirements.in + # via -r requirements.in django-dbconn-retry==0.1.7 - # via -r engine/requirements.in + # via -r requirements.in django-debug-toolbar==4.1.0 - # via -r engine/requirements.in + # via -r requirements.in django-deprecate-fields==0.1.1 - # via -r engine/requirements.in + # via -r requirements.in django-filter==2.4.0 - # via -r engine/requirements.in + # via -r requirements.in django-ipware==4.0.2 - # via -r engine/requirements.in + # via -r requirements.in django-log-request-id==1.6.0 - # via -r engine/requirements.in + # via -r requirements.in django-migration-linter==4.1.0 - # via -r engine/requirements.in + # via -r requirements.in django-mirage-field==1.3.0 - # via -r engine/requirements.in + # via -r requirements.in django-mysql==4.6.0 - # via -r engine/requirements.in + # via -r requirements.in django-polymorphic==3.1.0 # via - # -r engine/requirements.in + # -r requirements.in # django-rest-polymorphic django-ratelimit==2.0.0 - # via -r engine/requirements.in + # via -r requirements.in django-redis==5.4.0 - # via -r engine/requirements.in + # via -r requirements.in django-rest-polymorphic==0.1.10 - # via -r engine/requirements.in + # via -r requirements.in django-silk==5.0.3 - # via -r engine/requirements.in + # via -r requirements.in django-sns-view==0.1.2 - # via -r engine/requirements.in + # via -r requirements.in djangorestframework==3.15.2 # via - # -r engine/requirements.in + # -r requirements.in # django-rest-polymorphic # drf-spectacular drf-spectacular==0.26.5 - # via -r engine/requirements.in + # via -r requirements.in emoji==2.4.0 # via - # -r engine/requirements.in + # -r requirements.in # slack-export-viewer factory-boy==2.12.0 - # via -r engine/requirements.in + # via -r requirements.in faker==23.1.0 # via factory-boy fcm-django @ https://github.com/grafana/fcm-django/archive/refs/tags/v1.0.12r1.tar.gz#sha256=7ec7cd9d353fc9edf19a4acd4fa14090a31d83d02ac986c5e5e081dea29f564f - # via -r engine/requirements.in + # via -r requirements.in firebase-admin==5.4.0 # via fcm-django flask==3.0.2 # via slack-export-viewer -google-api-core==2.17.0 +google-api-core[grpc]==2.17.0 # via # firebase-admin # google-api-python-client @@ -167,7 +167,7 @@ google-api-core==2.17.0 # google-cloud-storage google-api-python-client==2.122.0 # via - # -r engine/requirements.in + # -r requirements.in # firebase-admin google-auth==2.27.0 # via @@ -179,10 +179,10 @@ google-auth==2.27.0 # google-cloud-storage google-auth-httplib2==0.2.0 # via - # -r engine/requirements.in + # -r requirements.in # google-api-python-client google-auth-oauthlib==1.2.0 - # via -r engine/requirements.in + # via -r requirements.in google-cloud-core==2.4.1 # via # google-cloud-firestore @@ -206,28 +206,28 @@ gprof2dot==2022.7.29 # via django-silk grpcio==1.64.1 # via - # -r engine/requirements.in + # -r requirements.in # google-api-core # grpcio-status # opentelemetry-exporter-otlp-proto-grpc grpcio-status==1.57.0 # via google-api-core hiredis==2.2.3 - # via -r engine/requirements.in + # via -r requirements.in httplib2==0.22.0 # via # google-api-python-client # google-auth-httplib2 humanize==4.10.0 - # via -r engine/requirements.in + # via -r requirements.in icalendar==5.0.10 # via - # -r engine/requirements.in + # -r requirements.in # recurring-ical-events # x-wr-timezone idna==3.7 # via - # -r engine/requirements.in + # -r requirements.in # requests importlib-metadata==6.11.0 # via opentelemetry-api @@ -248,12 +248,12 @@ jsonschema-specifications==2023.12.1 kombu==5.3.5 # via celery lxml==5.2.2 - # via -r engine/requirements.in + # via -r requirements.in markdown==3.5.2 # via pymdown-extensions markdown2==2.4.10 # via - # -r engine/requirements.in + # -r requirements.in # slack-export-viewer markupsafe==2.1.5 # via @@ -267,7 +267,7 @@ oauthlib==3.2.2 # social-auth-core opentelemetry-api==1.26.0 # via - # -r engine/requirements.in + # -r requirements.in # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-instrumentation # opentelemetry-instrumentation-django @@ -279,7 +279,7 @@ opentelemetry-api==1.26.0 opentelemetry-exporter-otlp-proto-common==1.26.0 # via opentelemetry-exporter-otlp-proto-grpc opentelemetry-exporter-otlp-proto-grpc==1.26.0 - # via -r engine/requirements.in + # via -r requirements.in opentelemetry-instrumentation==0.47b0 # via # opentelemetry-instrumentation-django @@ -287,14 +287,14 @@ opentelemetry-instrumentation==0.47b0 # opentelemetry-instrumentation-requests # opentelemetry-instrumentation-wsgi opentelemetry-instrumentation-django==0.47b0 - # via -r engine/requirements.in + # via -r requirements.in opentelemetry-instrumentation-logging==0.47b0 - # via -r engine/requirements.in + # via -r requirements.in opentelemetry-instrumentation-requests==0.47b0 - # via -r engine/requirements.in + # via -r requirements.in opentelemetry-instrumentation-wsgi==0.47b0 # via - # -r engine/requirements.in + # -r requirements.in # opentelemetry-instrumentation-django opentelemetry-proto==1.26.0 # via @@ -302,7 +302,7 @@ opentelemetry-proto==1.26.0 # opentelemetry-exporter-otlp-proto-grpc opentelemetry-sdk==1.26.0 # via - # -r engine/requirements.in + # -r requirements.in # opentelemetry-exporter-otlp-proto-grpc opentelemetry-semantic-conventions==0.47b0 # via @@ -318,9 +318,9 @@ opentelemetry-util-http==0.47b0 pem==23.1.0 # via django-sns-view phonenumbers==8.10.0 - # via -r engine/requirements.in + # via -r requirements.in prometheus-client==0.16.0 - # via -r engine/requirements.in + # via -r requirements.in prompt-toolkit==3.0.43 # via click-repl proto-plus==1.23.0 @@ -334,9 +334,9 @@ protobuf==4.25.2 # opentelemetry-proto # proto-plus psutil==5.9.4 - # via -r engine/requirements.in + # via -r requirements.in psycopg2==2.9.3 - # via -r engine/requirements.in + # via -r requirements.in pyasn1==0.5.1 # via # pyasn1-modules @@ -352,9 +352,9 @@ pyjwt==2.8.0 # social-auth-core # twilio pymdown-extensions==10.0 - # via -r engine/requirements.in + # via -r requirements.in pymysql==1.1.1 - # via -r engine/requirements.in + # via -r requirements.in pyopenssl==24.2.1 # via django-sns-view pyparsing==3.1.1 @@ -367,7 +367,7 @@ python-dateutil==2.8.2 # icalendar # recurring-ical-events python-telegram-bot==13.13 - # via -r engine/requirements.in + # via -r requirements.in python3-openid==3.2.0 # via social-auth-core pytz==2024.1 @@ -383,10 +383,10 @@ pyyaml==6.0.1 # drf-spectacular # pymdown-extensions recurring-ical-events==2.1.0 - # via -r engine/requirements.in + # via -r requirements.in redis==5.0.1 # via - # -r engine/requirements.in + # -r requirements.in # celery # django-redis referencing==0.33.0 @@ -394,10 +394,10 @@ referencing==0.33.0 # jsonschema # jsonschema-specifications regex==2024.7.24 - # via -r engine/requirements.in + # via -r requirements.in requests==2.32.3 # via - # -r engine/requirements.in + # -r requirements.in # cachecontrol # django-anymail # django-sns-view @@ -418,10 +418,6 @@ rsa==4.9 # via google-auth s3transfer==0.10.0 # via boto3 -setuptools==73.0.0 - # via - # apscheduler - # opentelemetry-instrumentation six==1.16.0 # via # apscheduler @@ -429,11 +425,11 @@ six==1.16.0 # python-dateutil # twilio slack-export-viewer==1.1.4 - # via -r engine/requirements.in + # via -r requirements.in slack-sdk==3.21.3 - # via -r engine/requirements.in + # via -r requirements.in social-auth-app-django==5.4.1 - # via -r engine/requirements.in + # via -r requirements.in social-auth-core==4.5.2 # via social-auth-app-django soupsieve==2.5 @@ -450,7 +446,7 @@ tornado==6.4.1 tqdm==4.66.3 # via django-mirage-field twilio==6.37.0 - # via -r engine/requirements.in + # via -r requirements.in typing-extensions==4.9.0 # via opentelemetry-sdk tzdata==2024.1 @@ -463,12 +459,12 @@ uritemplate==4.1.1 # google-api-python-client urllib3==1.26.19 # via - # -r engine/requirements.in + # -r requirements.in # botocore # django-anymail # requests uwsgi==2.0.26 - # via -r engine/requirements.in + # via -r requirements.in vine==5.1.0 # via # amqp @@ -479,7 +475,7 @@ wcwidth==0.2.13 werkzeug==3.0.3 # via flask whitenoise==5.3.0 - # via -r engine/requirements.in + # via -r requirements.in wrapt==1.16.0 # via # deprecated From e5693531d22438909d7a8b1cadf1bccbef2745d4 Mon Sep 17 00:00:00 2001 From: Joey Orlando Date: Wed, 9 Oct 2024 17:46:38 -0400 Subject: [PATCH 4/4] remove `deprecate_field` usage (#5148) --- engine/apps/alerts/models/alert.py | 3 +++ engine/apps/alerts/models/alert_group.py | 24 +++++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/engine/apps/alerts/models/alert.py b/engine/apps/alerts/models/alert.py index 14ae02e9df..844cbf6771 100644 --- a/engine/apps/alerts/models/alert.py +++ b/engine/apps/alerts/models/alert.py @@ -25,6 +25,8 @@ from common.public_primary_keys import generate_public_primary_key, increase_public_primary_key_length if typing.TYPE_CHECKING: + from django.db.models.manager import RelatedManager + from apps.alerts.models import AlertGroup, AlertReceiveChannel, ChannelFilter logger = logging.getLogger(__name__) @@ -47,6 +49,7 @@ def generate_public_primary_key_for_alert(): class Alert(models.Model): group: typing.Optional["AlertGroup"] + resolved_alert_groups: "RelatedManager['AlertGroup']" public_primary_key = models.CharField( max_length=20, diff --git a/engine/apps/alerts/models/alert_group.py b/engine/apps/alerts/models/alert_group.py index 2e38272129..7a7b4e193c 100644 --- a/engine/apps/alerts/models/alert_group.py +++ b/engine/apps/alerts/models/alert_group.py @@ -12,7 +12,6 @@ from django.db.models import JSONField, Q, QuerySet from django.utils import timezone from django.utils.functional import cached_property -from django_deprecate_fields import deprecate_field from apps.alerts.constants import ActionSource, AlertGroupState from apps.alerts.escalation_snapshot import EscalationSnapshotMixin @@ -288,17 +287,20 @@ class AlertGroup(AlertGroupSlackRenderingMixin, EscalationSnapshotMixin, models. related_name="resolved_alert_groups", ) - # NOTE: see https://raintank-corp.slack.com/archives/C07RGREUH4Z/p1728494111646319 - # This field should eventually be dropped as it's no longer being set/read anywhere - resolved_by_alert = deprecate_field( - models.ForeignKey( - "alerts.Alert", - on_delete=models.SET_NULL, - null=True, - default=None, - related_name="resolved_alert_groups", - ) + resolved_by_alert = models.ForeignKey( + "alerts.Alert", + on_delete=models.SET_NULL, + null=True, + default=None, + related_name="resolved_alert_groups", ) + """ + ⚠️ This field is no longer being set/read anywhere, DON'T USE IT! ⚠️ + + TODO: We still need to figure out how to remove it safely. + + See [this conversation](https://raintank-corp.slack.com/archives/C07RGREUH4Z/p1728494111646319) for more context + """ resolved_at = models.DateTimeField(blank=True, null=True) acknowledged = models.BooleanField(default=False)