v1.9.3
This commit is contained in:
commit
39cb6f2a0b
23 changed files with 66 additions and 248 deletions
4
.github/workflows/e2e-tests.yml
vendored
4
.github/workflows/e2e-tests.yml
vendored
|
|
@ -144,12 +144,14 @@ jobs:
|
|||
git clone https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com/grafana/ops-devenv.git
|
||||
git clone https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com/grafana/gops-labels.git
|
||||
|
||||
- name: Tilt CI - standard and expensive E2E tests
|
||||
- name: Tilt CI - Expensive E2E tests
|
||||
if: inputs.run-expensive-tests
|
||||
shell: bash
|
||||
env:
|
||||
E2E_TESTS_CMD: "cd ../../grafana-plugin && yarn test:e2e-expensive"
|
||||
GRAFANA_VERSION: ${{ inputs.grafana_version }}
|
||||
GF_FEATURE_TOGGLES_ENABLE: "externalServiceAccounts"
|
||||
ONCALL_API_URL: "http://oncall-dev-engine:8080"
|
||||
GRAFANA_ADMIN_USERNAME: "irm"
|
||||
GRAFANA_ADMIN_PASSWORD: "irm"
|
||||
BROWSERS: ${{ inputs.browsers }}
|
||||
|
|
|
|||
4
.github/workflows/expensive-e2e-tests.yml
vendored
4
.github/workflows/expensive-e2e-tests.yml
vendored
|
|
@ -24,8 +24,8 @@ jobs:
|
|||
# - 9.3.16
|
||||
# - 9.4.13
|
||||
# - 9.5.7
|
||||
- 10.0.11
|
||||
- 10.1.7
|
||||
- 10.3.3
|
||||
# TODO: fix issues with running e2e tests against Grafana v10.2.x and v10.3.x
|
||||
# - 10.2.4
|
||||
# - latest
|
||||
|
|
@ -55,7 +55,7 @@ jobs:
|
|||
#
|
||||
- uses: slackapi/slack-github-action@v1.24.0
|
||||
with:
|
||||
channel-id: gops-oncall-dev
|
||||
channel-id: gops-irm-dev
|
||||
# yamllint disable rule:line-length
|
||||
payload: |
|
||||
{
|
||||
|
|
|
|||
13
.github/workflows/linting-and-tests.yml
vendored
13
.github/workflows/linting-and-tests.yml
vendored
|
|
@ -239,9 +239,16 @@ jobs:
|
|||
end-to-end-tests:
|
||||
name: Standard e2e tests
|
||||
uses: ./.github/workflows/e2e-tests.yml
|
||||
strategy:
|
||||
matrix:
|
||||
grafana_version:
|
||||
- 10.1.7
|
||||
- 10.3.3
|
||||
# TODO: fix issues with running e2e tests against Grafana v10.2.x and latest
|
||||
# - 10.2.4
|
||||
# - latest
|
||||
fail-fast: false
|
||||
with:
|
||||
# TODO: fix issues with running e2e tests against Grafana v10.2.x and v10.3.x
|
||||
grafana_version: 10.1.7
|
||||
# grafana_version: 10.3.3
|
||||
grafana_version: ${{ matrix.grafana_version }}
|
||||
run-expensive-tests: false
|
||||
browsers: "chromium"
|
||||
|
|
|
|||
11
Tiltfile
11
Tiltfile
|
|
@ -1,6 +1,7 @@
|
|||
load('ext://uibutton', 'cmd_button', 'location', 'text_input', 'bool_input')
|
||||
load("ext://configmap", "configmap_create")
|
||||
|
||||
grafana_url = os.getenv("GRAFANA_URL", "http://grafana:3000")
|
||||
running_under_parent_tiltfile = os.getenv("TILT_PARENT", "false") == "true"
|
||||
twilio_values=[
|
||||
"oncall.twilio.accountSid=" + os.getenv("TWILIO_ACCOUNT_SID", ""),
|
||||
|
|
@ -29,6 +30,14 @@ def plugin_json():
|
|||
return plugin_file
|
||||
return 'NOT_A_PLUGIN'
|
||||
|
||||
def extra_env():
|
||||
return {
|
||||
"GF_APP_URL": grafana_url,
|
||||
"GF_SERVER_ROOT_URL": grafana_url,
|
||||
"GF_FEATURE_TOGGLES_ENABLE": "externalServiceAccounts",
|
||||
"ONCALL_API_URL": "http://oncall-dev-engine:8080"
|
||||
}
|
||||
|
||||
|
||||
allow_k8s_contexts(["kind-kind"])
|
||||
|
||||
|
|
@ -83,8 +92,6 @@ def load_grafana():
|
|||
# The user/pass that you will login to Grafana with
|
||||
grafana_admin_user_pass = os.getenv("GRAFANA_ADMIN_USER_PASS", "oncall")
|
||||
grafana_version = os.getenv("GRAFANA_VERSION", "latest")
|
||||
grafana_url = os.getenv("GRAFANA_URL", "http://grafana:3000")
|
||||
|
||||
|
||||
if 'plugin' in profiles:
|
||||
k8s_resource(
|
||||
|
|
|
|||
|
|
@ -201,7 +201,6 @@ class AlertGroup(AlertGroupSlackRenderingMixin, EscalationSnapshotMixin, models.
|
|||
resolved_by_user: typing.Optional["User"]
|
||||
root_alert_group: typing.Optional["AlertGroup"]
|
||||
silenced_by_user: typing.Optional["User"]
|
||||
slack_log_message: typing.Optional["SlackMessage"]
|
||||
slack_messages: "RelatedManager['SlackMessage']"
|
||||
users: "RelatedManager['User']"
|
||||
labels: "RelatedManager['AlertGroupAssociatedLabel']"
|
||||
|
|
@ -396,6 +395,7 @@ class AlertGroup(AlertGroupSlackRenderingMixin, EscalationSnapshotMixin, models.
|
|||
related_name="wiped_alert_groups",
|
||||
)
|
||||
|
||||
# TODO: drop this column in future release
|
||||
slack_log_message = models.OneToOneField(
|
||||
"slack.SlackMessage",
|
||||
on_delete=models.SET_NULL,
|
||||
|
|
|
|||
|
|
@ -36,10 +36,6 @@ alert_group_action_triggered_signal.connect(
|
|||
AlertGroupSlackRepresentative.on_alert_group_action_triggered,
|
||||
)
|
||||
|
||||
alert_group_update_log_report_signal.connect(
|
||||
AlertGroupSlackRepresentative.on_alert_group_update_log_report,
|
||||
)
|
||||
|
||||
alert_group_update_resolution_note_signal.connect(
|
||||
AlertGroupSlackRepresentative.on_alert_group_update_resolution_note,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -90,19 +90,7 @@ def on_alert_group_action_triggered_async(log_record_id):
|
|||
autoretry_for=(Exception,), retry_backoff=True, max_retries=1 if settings.DEBUG else None
|
||||
)
|
||||
def on_alert_group_update_log_report_async(alert_group_id):
|
||||
from apps.alerts.models import AlertGroup
|
||||
|
||||
alert_group = AlertGroup.objects.get(pk=alert_group_id)
|
||||
logger.debug(f"Start on_alert_group_update_log_report for alert_group {alert_group_id}")
|
||||
organization = alert_group.channel.organization
|
||||
if alert_group.slack_message and organization.slack_team_identity:
|
||||
logger.debug(f"Process on_alert_group_update_log_report for alert_group {alert_group_id}")
|
||||
UpdateLogReportMessageStep = ScenarioStep.get_step("distribute_alerts", "UpdateLogReportMessageStep")
|
||||
step = UpdateLogReportMessageStep(organization.slack_team_identity, organization)
|
||||
step.process_signal(alert_group)
|
||||
else:
|
||||
logger.debug(f"Drop on_alert_group_update_log_report for alert_group {alert_group_id}")
|
||||
logger.debug(f"Finish on_alert_group_update_log_report for alert_group {alert_group_id}")
|
||||
return "Deprecated, will be removed after queue cleanup"
|
||||
|
||||
|
||||
class AlertGroupSlackRepresentative(AlertGroupAbstractRepresentative):
|
||||
|
|
@ -173,32 +161,6 @@ class AlertGroupSlackRepresentative(AlertGroupAbstractRepresentative):
|
|||
logger.debug(f"SLACK on_alert_group_action_triggered: async {log_record_id} {force_sync}")
|
||||
on_alert_group_action_triggered_async.apply_async((log_record_id,))
|
||||
|
||||
@classmethod
|
||||
def on_alert_group_update_log_report(cls, **kwargs):
|
||||
from apps.alerts.models import AlertGroup
|
||||
|
||||
alert_group = kwargs["alert_group"]
|
||||
|
||||
if isinstance(alert_group, AlertGroup):
|
||||
alert_group_id = alert_group.pk
|
||||
else:
|
||||
alert_group_id = alert_group
|
||||
try:
|
||||
alert_group = AlertGroup.objects.get(pk=alert_group_id)
|
||||
except AlertGroup.DoesNotExist as e:
|
||||
logger.warning(f"SLACK update log report: alert group {alert_group_id} has been deleted")
|
||||
raise e
|
||||
|
||||
logger.debug(
|
||||
f"Received alert_group_update_log_report signal in SLACK representative for alert_group {alert_group_id}"
|
||||
)
|
||||
|
||||
if alert_group.notify_in_slack_enabled is False:
|
||||
logger.debug(f"Skipping alert_group {alert_group_id} since notify_in_slack is disabled")
|
||||
return
|
||||
|
||||
on_alert_group_update_log_report_async.apply_async((alert_group_id,))
|
||||
|
||||
@classmethod
|
||||
def on_alert_group_update_resolution_note(cls, **kwargs):
|
||||
alert_group = kwargs["alert_group"]
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import typing
|
|||
from datetime import datetime
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.alerts.constants import ActionSource
|
||||
from apps.alerts.incident_appearance.renderers.constants import DEFAULT_BACKUP_TITLE
|
||||
|
|
@ -14,25 +13,17 @@ from apps.api.permissions import RBACPermission
|
|||
from apps.slack.chatops_proxy_routing import make_private_metadata, make_value
|
||||
from apps.slack.constants import CACHE_UPDATE_INCIDENT_SLACK_MESSAGE_LIFETIME
|
||||
from apps.slack.errors import (
|
||||
SlackAPICantUpdateMessageError,
|
||||
SlackAPIChannelArchivedError,
|
||||
SlackAPIChannelInactiveError,
|
||||
SlackAPIChannelNotFoundError,
|
||||
SlackAPIError,
|
||||
SlackAPIInvalidAuthError,
|
||||
SlackAPIMessageNotFoundError,
|
||||
SlackAPIRatelimitError,
|
||||
SlackAPIRestrictedActionError,
|
||||
SlackAPITokenError,
|
||||
)
|
||||
from apps.slack.scenarios import scenario_step
|
||||
from apps.slack.scenarios.slack_renderer import AlertGroupLogSlackRenderer
|
||||
from apps.slack.slack_formatter import SlackFormatter
|
||||
from apps.slack.tasks import (
|
||||
post_or_update_log_report_message_task,
|
||||
send_message_to_thread_if_bot_not_in_channel,
|
||||
update_incident_slack_message,
|
||||
)
|
||||
from apps.slack.tasks import send_message_to_thread_if_bot_not_in_channel, update_incident_slack_message
|
||||
from apps.slack.types import (
|
||||
Block,
|
||||
BlockActionType,
|
||||
|
|
@ -95,7 +86,6 @@ class AlertShootingStep(scenario_step.ScenarioStep):
|
|||
else:
|
||||
# check if alert group was posted to slack before posting message to thread
|
||||
if not alert.group.skip_escalation_in_slack:
|
||||
self._send_log_report_message(alert.group, channel_id)
|
||||
self._send_message_to_thread_if_bot_not_in_channel(alert.group, channel_id)
|
||||
else:
|
||||
# check if alert group was posted to slack before updating its message
|
||||
|
|
@ -208,11 +198,6 @@ class AlertShootingStep(scenario_step.ScenarioStep):
|
|||
blocks=blocks,
|
||||
)
|
||||
|
||||
def _send_log_report_message(self, alert_group: AlertGroup, channel_id: str) -> None:
|
||||
post_or_update_log_report_message_task.apply_async(
|
||||
(alert_group.pk, self.slack_team_identity.pk),
|
||||
)
|
||||
|
||||
def _send_message_to_thread_if_bot_not_in_channel(self, alert_group: AlertGroup, channel_id: str) -> None:
|
||||
send_message_to_thread_if_bot_not_in_channel.apply_async(
|
||||
(alert_group.pk, self.slack_team_identity.pk, channel_id),
|
||||
|
|
@ -895,76 +880,6 @@ class DeleteGroupStep(scenario_step.ScenarioStep):
|
|||
message.delete()
|
||||
|
||||
|
||||
class UpdateLogReportMessageStep(scenario_step.ScenarioStep):
|
||||
def process_signal(self, alert_group: AlertGroup) -> None:
|
||||
if alert_group.skip_escalation_in_slack or alert_group.channel.is_rate_limited_in_slack:
|
||||
return
|
||||
|
||||
self.update_log_message(alert_group)
|
||||
|
||||
def update_log_message(self, alert_group: AlertGroup) -> None:
|
||||
slack_message = alert_group.slack_message
|
||||
if slack_message is None:
|
||||
logger.info(
|
||||
f"Cannot update log message for alert_group {alert_group.pk} because SlackMessage doesn't exist"
|
||||
)
|
||||
return None
|
||||
|
||||
slack_log_message = alert_group.slack_log_message
|
||||
|
||||
if slack_log_message is not None:
|
||||
# prevent too frequent updates
|
||||
if timezone.now() <= slack_log_message.last_updated + timezone.timedelta(seconds=5):
|
||||
return
|
||||
|
||||
attachments = AlertGroupLogSlackRenderer.render_incident_log_report_for_slack(alert_group)
|
||||
logger.debug(
|
||||
f"Update log message for alert_group {alert_group.pk}, slack_log_message {slack_log_message.pk}"
|
||||
)
|
||||
try:
|
||||
self._slack_client.chat_update(
|
||||
channel=slack_message.channel_id,
|
||||
text="Alert Group log",
|
||||
ts=slack_log_message.slack_id,
|
||||
attachments=attachments,
|
||||
)
|
||||
except SlackAPIRatelimitError as e:
|
||||
if not alert_group.channel.is_rate_limited_in_slack:
|
||||
alert_group.channel.start_send_rate_limit_message_task(e.retry_after)
|
||||
except SlackAPIMessageNotFoundError:
|
||||
alert_group.slack_log_message = None
|
||||
alert_group.save(update_fields=["slack_log_message"])
|
||||
except (
|
||||
SlackAPITokenError,
|
||||
SlackAPIChannelNotFoundError,
|
||||
SlackAPIChannelArchivedError,
|
||||
SlackAPIChannelInactiveError,
|
||||
SlackAPIInvalidAuthError,
|
||||
SlackAPICantUpdateMessageError,
|
||||
):
|
||||
pass
|
||||
else:
|
||||
slack_log_message.last_updated = timezone.now()
|
||||
slack_log_message.save(update_fields=["last_updated"])
|
||||
logger.debug(
|
||||
f"Finished update log message for alert_group {alert_group.pk}, "
|
||||
f"slack_log_message {slack_log_message.pk}"
|
||||
)
|
||||
# check how much time has passed since slack message was created
|
||||
# to prevent eternal loop of restarting update log message task
|
||||
elif timezone.now() <= slack_message.created_at + timezone.timedelta(minutes=5):
|
||||
logger.debug(
|
||||
f"Update log message failed for alert_group {alert_group.pk}: "
|
||||
f"log message does not exist yet. Restarting post_or_update_log_report_message_task..."
|
||||
)
|
||||
post_or_update_log_report_message_task.apply_async(
|
||||
(alert_group.pk, self.slack_team_identity.pk, True),
|
||||
countdown=3,
|
||||
)
|
||||
else:
|
||||
logger.debug(f"Update log message failed for alert_group {alert_group.pk}: " f"log message does not exist.")
|
||||
|
||||
|
||||
STEPS_ROUTING: ScenarioRoute.RoutingSteps = [
|
||||
{
|
||||
"payload_type": PayloadType.INTERACTIVE_MESSAGE,
|
||||
|
|
|
|||
|
|
@ -39,17 +39,3 @@ class AlertGroupLogSlackRenderer:
|
|||
for plan_line in escalation_policies_plan[time]:
|
||||
result += f"*{humanize.naturaldelta(time)}:* {plan_line}\n"
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def render_incident_log_report_for_slack(alert_group: "AlertGroup"):
|
||||
attachments = []
|
||||
past = AlertGroupLogSlackRenderer.render_alert_group_past_log_report_text(alert_group)
|
||||
future = AlertGroupLogSlackRenderer.render_alert_group_future_log_report_text(alert_group)
|
||||
text = past + future
|
||||
if len(text) > 0:
|
||||
attachments.append(
|
||||
{
|
||||
"text": text,
|
||||
}
|
||||
)
|
||||
return attachments
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ from apps.slack.errors import (
|
|||
SlackAPITokenError,
|
||||
SlackAPIUsergroupNotFoundError,
|
||||
)
|
||||
from apps.slack.scenarios.scenario_step import ScenarioStep
|
||||
from apps.slack.utils import (
|
||||
get_cache_key_update_incident_slack_message,
|
||||
get_populate_slack_channel_task_id_key,
|
||||
|
|
@ -289,27 +288,7 @@ def populate_slack_user_identities(organization_pk):
|
|||
autoretry_for=(Exception,), retry_backoff=True, max_retries=1 if settings.DEBUG else None
|
||||
)
|
||||
def post_or_update_log_report_message_task(alert_group_pk, slack_team_identity_pk, update=False):
|
||||
logger.debug(f"Start post_or_update_log_report_message_task for alert_group {alert_group_pk}")
|
||||
from apps.alerts.models import AlertGroup
|
||||
from apps.slack.models import SlackTeamIdentity
|
||||
|
||||
UpdateLogReportMessageStep = ScenarioStep.get_step("distribute_alerts", "UpdateLogReportMessageStep")
|
||||
|
||||
slack_team_identity = SlackTeamIdentity.objects.get(pk=slack_team_identity_pk)
|
||||
alert_group = AlertGroup.objects.get(pk=alert_group_pk)
|
||||
step = UpdateLogReportMessageStep(slack_team_identity, alert_group.channel.organization)
|
||||
|
||||
if alert_group.skip_escalation_in_slack or alert_group.channel.is_rate_limited_in_slack:
|
||||
return
|
||||
|
||||
if update: # flag to prevent multiple posting log message to slack
|
||||
step.update_log_message(alert_group)
|
||||
else:
|
||||
# don't post a new message, as it is available from the button
|
||||
# this is an intermediate step, so we will only update posted messages but not post new ones
|
||||
# once majority of messages are updated, we can remove this step (https://github.com/grafana/oncall/pull/4686)
|
||||
pass
|
||||
logger.debug(f"Finish post_or_update_log_report_message_task for alert_group {alert_group_pk}")
|
||||
return "Deprecated, will be removed after queue cleanup"
|
||||
|
||||
|
||||
@shared_dedicated_queue_retry_task(
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.alerts.models import AlertGroup
|
||||
from apps.slack.errors import SlackAPICantUpdateMessageError, SlackAPIRestrictedActionError
|
||||
from apps.slack.errors import SlackAPIRestrictedActionError
|
||||
from apps.slack.models import SlackMessage
|
||||
from apps.slack.scenarios.distribute_alerts import AlertShootingStep, UpdateLogReportMessageStep
|
||||
from apps.slack.scenarios.distribute_alerts import AlertShootingStep
|
||||
from apps.slack.scenarios.scenario_step import ScenarioStep
|
||||
from apps.slack.tests.conftest import build_slack_response
|
||||
|
||||
|
|
@ -65,37 +64,3 @@ def test_alert_shooting_no_channel_filter(
|
|||
|
||||
mock_post_alert_group_to_slack.assert_called_once()
|
||||
assert mock_post_alert_group_to_slack.call_args[1]["channel_id"] == "DEFAULT_CHANNEL_ID"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_update_log_report_cant_update(
|
||||
make_slack_team_identity,
|
||||
make_organization,
|
||||
make_alert_receive_channel,
|
||||
make_alert_group,
|
||||
make_alert,
|
||||
make_slack_message,
|
||||
):
|
||||
slack_team_identity = make_slack_team_identity()
|
||||
organization = make_organization(
|
||||
slack_team_identity=slack_team_identity, general_log_channel_id="DEFAULT_CHANNEL_ID"
|
||||
)
|
||||
alert_receive_channel = make_alert_receive_channel(organization)
|
||||
|
||||
alert_group = make_alert_group(alert_receive_channel, channel_filter=None)
|
||||
# alert = make_alert(alert_group, raw_request_data={})
|
||||
log_message = make_slack_message(
|
||||
alert_group=alert_group,
|
||||
channel_id="RANDOM_CHANNEL_ID",
|
||||
slack_id="RANDOM_MESSAGE_ID",
|
||||
last_updated=timezone.now() - timezone.timedelta(minutes=5),
|
||||
)
|
||||
alert_group.slack_log_message = log_message
|
||||
|
||||
step = UpdateLogReportMessageStep(slack_team_identity, organization)
|
||||
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
|
||||
mock_slack_api_call.side_effect = SlackAPICantUpdateMessageError(
|
||||
response=build_slack_response({"error": "cant_update_message"})
|
||||
)
|
||||
# not raising error, will not retry
|
||||
step.update_log_message(alert_group)
|
||||
|
|
|
|||
|
|
@ -156,9 +156,6 @@ CELERY_TASK_ROUTES = {
|
|||
"apps.grafana_plugin.tasks.sync_v2.sync_organizations_v2": {"queue": "long"},
|
||||
# SLACK
|
||||
"apps.integrations.tasks.notify_about_integration_ratelimit_in_slack": {"queue": "slack"},
|
||||
"apps.slack.helpers.alert_group_representative.on_alert_group_action_triggered_async": {"queue": "slack"},
|
||||
"apps.slack.helpers.alert_group_representative.on_alert_group_update_log_report_async": {"queue": "slack"},
|
||||
"apps.slack.helpers.alert_group_representative.on_create_alert_slack_representative_async": {"queue": "slack"},
|
||||
"apps.slack.tasks.clean_slack_channel_leftovers": {"queue": "slack"},
|
||||
"apps.slack.tasks.check_slack_message_exists_before_post_message_to_thread": {"queue": "slack"},
|
||||
"apps.slack.tasks.clean_slack_integration_leftovers": {"queue": "slack"},
|
||||
|
|
@ -167,7 +164,6 @@ CELERY_TASK_ROUTES = {
|
|||
"apps.slack.tasks.populate_slack_user_identities": {"queue": "slack"},
|
||||
"apps.slack.tasks.populate_slack_usergroups": {"queue": "slack"},
|
||||
"apps.slack.tasks.populate_slack_usergroups_for_team": {"queue": "slack"},
|
||||
"apps.slack.tasks.post_or_update_log_report_message_task": {"queue": "slack"},
|
||||
"apps.slack.tasks.post_slack_rate_limit_message": {"queue": "slack"},
|
||||
"apps.slack.tasks.send_message_to_thread_if_bot_not_in_channel": {"queue": "slack"},
|
||||
"apps.slack.tasks.start_update_slack_user_group_for_schedules": {"queue": "slack"},
|
||||
|
|
@ -178,6 +174,8 @@ CELERY_TASK_ROUTES = {
|
|||
"queue": "slack"
|
||||
},
|
||||
"apps.slack.representatives.alert_group_representative.on_alert_group_action_triggered_async": {"queue": "slack"},
|
||||
# TODO: remove post_or_update_log_report_message_task and on_alert_group_update_log_report_async in subsequent PR
|
||||
"apps.slack.tasks.post_or_update_log_report_message_task": {"queue": "slack"},
|
||||
"apps.slack.representatives.alert_group_representative.on_alert_group_update_log_report_async": {"queue": "slack"},
|
||||
# TELEGRAM
|
||||
"apps.telegram.tasks.edit_message": {"queue": "telegram"},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import semver from 'semver';
|
||||
|
||||
import { test, expect } from '../fixtures';
|
||||
import { isGrafanaVersionLowerThan } from '../utils/constants';
|
||||
import { clickButton, fillInInput } from '../utils/forms';
|
||||
import { goToOnCallPage } from '../utils/navigation';
|
||||
|
||||
|
|
@ -22,9 +21,7 @@ test('we can directly page a user', async ({ adminRolePage }) => {
|
|||
|
||||
const addRespondersPopup = page.getByTestId('add-responders-popup');
|
||||
|
||||
await addRespondersPopup[semver.lt(process.env.CURRENT_GRAFANA_VERSION, '10.3.0') ? 'getByText' : 'getByLabel'](
|
||||
'Users'
|
||||
).click();
|
||||
await addRespondersPopup[isGrafanaVersionLowerThan('10.3.0') ? 'getByText' : 'getByLabel']('Users').click();
|
||||
await addRespondersPopup.getByText(adminRolePage.userName).first().click();
|
||||
|
||||
// If user is not on call, confirm invitation
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import {
|
|||
type APIRequestContext,
|
||||
Page,
|
||||
} from '@playwright/test';
|
||||
import semver from 'semver';
|
||||
|
||||
import { VIEWER_USER_STORAGE_STATE, EDITOR_USER_STORAGE_STATE, ADMIN_USER_STORAGE_STATE } from '../playwright.config';
|
||||
|
||||
|
|
@ -21,6 +20,7 @@ import {
|
|||
IS_CLOUD,
|
||||
IS_OPEN_SOURCE,
|
||||
OrgRole,
|
||||
isGrafanaVersionLowerThan,
|
||||
} from './utils/constants';
|
||||
import { goToOnCallPage } from './utils/navigation';
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ const idempotentlyInitializePlugin = async (page: Page) => {
|
|||
if (await openPluginConfigurationButton.isVisible()) {
|
||||
await openPluginConfigurationButton.click();
|
||||
// Before 10.3 Admin user needs to create service account manually
|
||||
if (semver.lt(process.env.CURRENT_GRAFANA_VERSION, '10.3.0')) {
|
||||
if (isGrafanaVersionLowerThan('10.3.0')) {
|
||||
await page.getByTestId('recreate-service-account').click();
|
||||
}
|
||||
await page.getByTestId('connect-plugin').click();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import semver from 'semver';
|
||||
|
||||
import { test, expect } from '../fixtures';
|
||||
import { resolveFiringAlert } from '../utils/alertGroup';
|
||||
import { isGrafanaVersionLowerThan } from '../utils/constants';
|
||||
import { createEscalationChain, EscalationStep } from '../utils/escalationChain';
|
||||
import { clickButton, generateRandomValue } from '../utils/forms';
|
||||
import { createIntegrationAndSendDemoAlert } from '../utils/integrations';
|
||||
|
|
@ -15,10 +14,7 @@ import { createOnCallSchedule } from '../utils/schedule';
|
|||
* and use the currentGrafanaVersion fixture once this bugged is patched in playwright
|
||||
* https://github.com/microsoft/playwright/issues/29608
|
||||
*/
|
||||
test.skip(
|
||||
() => semver.lt(process.env.CURRENT_GRAFANA_VERSION, '10.0.0'),
|
||||
'Insights is only available in Grafana 10.0.0 and above'
|
||||
);
|
||||
test.skip(() => isGrafanaVersionLowerThan('10.0.0'), 'Insights is only available in Grafana 10.0.0 and above');
|
||||
|
||||
/**
|
||||
* skipping as these tests are currently flaky
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
import { test, expect } from '../fixtures';
|
||||
import { isGrafanaVersionGreaterThan } from '../utils/constants';
|
||||
import { clickButton, generateRandomValidLabel, openDropdown } from '../utils/forms';
|
||||
import { openCreateIntegrationModal } from '../utils/integrations';
|
||||
import { goToOnCallPage } from '../utils/navigation';
|
||||
|
||||
test.skip(
|
||||
() => isGrafanaVersionGreaterThan('10.3.0'),
|
||||
'Above 10.3 labels need enterprise version to validate permissions'
|
||||
);
|
||||
|
||||
test('New label keys and labels can be created @expensive', async ({ adminRolePage }) => {
|
||||
const { page } = adminRolePage;
|
||||
await goToOnCallPage(page, 'integrations');
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import semver from 'semver';
|
||||
|
||||
import { waitInMs } from 'utils/async';
|
||||
|
||||
import { test, expect, Page } from '../fixtures';
|
||||
import { OrgRole } from '../utils/constants';
|
||||
import { OrgRole, isGrafanaVersionLowerThan } from '../utils/constants';
|
||||
import { goToGrafanaPage, goToOnCallPage } from '../utils/navigation';
|
||||
import { createGrafanaUser, loginAndWaitTillGrafanaIsLoaded } from '../utils/users';
|
||||
|
||||
|
|
@ -48,10 +46,7 @@ test.describe('Plugin initialization', () => {
|
|||
}) => {
|
||||
test.slow();
|
||||
|
||||
test.skip(
|
||||
semver.lt(process.env.CURRENT_GRAFANA_VERSION, '10.3.0'),
|
||||
'Extension is only available in Grafana 10.3.0 and above'
|
||||
);
|
||||
test.skip(isGrafanaVersionLowerThan('10.3.0'), 'Extension is only available in Grafana 10.3.0 and above');
|
||||
|
||||
// Create new editor user
|
||||
const USER_NAME = `editor-${new Date().getTime()}`;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import semver from 'semver';
|
||||
|
||||
import { scheduleViewToDaysInOneRow } from 'models/schedule/schedule.helpers';
|
||||
import { ScheduleView } from 'models/schedule/schedule.types';
|
||||
import { HTML_ID } from 'utils/DOM';
|
||||
|
||||
import { expect, Page, test } from '../fixtures';
|
||||
import { isGrafanaVersionLowerThan } from '../utils/constants';
|
||||
import { generateRandomValue } from '../utils/forms';
|
||||
import { createOnCallSchedule } from '../utils/schedule';
|
||||
|
||||
|
|
@ -13,7 +12,7 @@ const getNumberOfWeekdaysInFinalSchedule = async (page: Page) =>
|
|||
const getScheduleViewRadioButtonLocator = (page: Page, view: ScheduleView) =>
|
||||
page
|
||||
.getByTestId('schedule-view-picker')
|
||||
[semver.lt(process.env.CURRENT_GRAFANA_VERSION, '10.2.0') ? 'getByText' : 'getByLabel'](view, { exact: true });
|
||||
[isGrafanaVersionLowerThan('10.2.0') ? 'getByText' : 'getByLabel'](view, { exact: true });
|
||||
|
||||
test('schedule view (week/2 weeks/month) toggler works', async ({ adminRolePage }) => {
|
||||
const { page, userName } = adminRolePage;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import semver from 'semver';
|
||||
|
||||
import { test, expect } from '../fixtures';
|
||||
import { isGrafanaVersionLowerThan } from '../utils/constants';
|
||||
import { goToOnCallPage } from '../utils/navigation';
|
||||
import { verifyThatUserCanViewOtherUsers, accessProfileTabs } from '../utils/users';
|
||||
|
||||
|
|
@ -26,7 +25,7 @@ test.describe('Users screen actions', () => {
|
|||
const tabsToCheck = ['tab-phone-verification', 'tab-slack', 'tab-telegram'];
|
||||
|
||||
// After 10.3 it's been moved to global user profile
|
||||
if (semver.lt(process.env.CURRENT_GRAFANA_VERSION, '10.3.0')) {
|
||||
if (isGrafanaVersionLowerThan('10.3.0')) {
|
||||
tabsToCheck.unshift('tab-mobile-app');
|
||||
}
|
||||
|
||||
|
|
@ -74,10 +73,10 @@ test.describe('Users screen actions', () => {
|
|||
await page.waitForTimeout(2000);
|
||||
|
||||
await page
|
||||
.locator('div')
|
||||
.filter({ hasText: /^Search or filter results\.\.\.$/ })
|
||||
.nth(1)
|
||||
.click();
|
||||
.locator('div')
|
||||
.filter({ hasText: /^Search or filter results\.\.\.$/ })
|
||||
.nth(1)
|
||||
.click();
|
||||
await page.keyboard.insertText(userName);
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForTimeout(2000);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import semver from 'semver';
|
||||
|
||||
export const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
|
||||
export const MAILSLURP_API_KEY = process.env.MAILSLURP_API_KEY;
|
||||
|
||||
|
|
@ -19,3 +21,6 @@ export enum OrgRole {
|
|||
}
|
||||
|
||||
export const MOSCOW_TIMEZONE = 'Europe/Moscow';
|
||||
|
||||
export const isGrafanaVersionGreaterThan = (version: string) => semver.gt(process.env.CURRENT_GRAFANA_VERSION, version);
|
||||
export const isGrafanaVersionLowerThan = (version: string) => semver.lt(process.env.CURRENT_GRAFANA_VERSION, version);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export const verifyUserPhoneNumber = async (page: Page): Promise<void> => {
|
|||
await openUserSettingsModal(page);
|
||||
|
||||
// go to the Phone Verification tab
|
||||
await page.locator('a[aria-label="Tab Phone Verification"]').click();
|
||||
await page.getByTestId('tab-phone-verification').click();
|
||||
|
||||
// check to see if we've already verified our phone number.. no need to do it more than once
|
||||
if (await getForgetPhoneNumberButton(page).isVisible()) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
"test:report": "HTML_REPORT_ENABLED=true yarn test",
|
||||
"test:silent": "yarn test --silent",
|
||||
"test:e2e": "yarn playwright test --grep-invert @expensive",
|
||||
"test:e2e-expensive": "yarn playwright test",
|
||||
"test:e2e-expensive": "yarn playwright test --grep @expensive",
|
||||
"test:e2e:watch": "yarn test:e2e --ui",
|
||||
"test:e2e-expensive:watch": "yarn test:e2e-expensive --ui",
|
||||
"test:e2e:gen": "yarn playwright codegen http://localhost:3000",
|
||||
|
|
|
|||
|
|
@ -674,7 +674,11 @@
|
|||
{
|
||||
"action": "users.roles:read",
|
||||
"scope": "users:*"
|
||||
}
|
||||
},
|
||||
{ "action": "grafana-labels-app.label:read" },
|
||||
{ "action": "grafana-labels-app.label:write" },
|
||||
{ "action": "grafana-labels-app.label:create" },
|
||||
{ "action": "grafana-labels-app.label:delete" }
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue