commit
b54fa79346
27 changed files with 13745 additions and 13090 deletions
|
|
@ -41,9 +41,9 @@ runs:
|
|||
GRAFANA_ACCESS_POLICY_TOKEN: ${{ inputs.grafana_access_policy_token }}
|
||||
run: |
|
||||
jq --arg v "${{ inputs.plugin_version_number }}" '.version=$v' package.json > package.new && mv package.new package.json && jq '.version' package.json;
|
||||
yarn build
|
||||
pnpm build
|
||||
mage buildAll || true
|
||||
yarn sign
|
||||
pnpm sign
|
||||
if [ ! -f dist/MANIFEST.txt ]; then echo "Sign failed, MANIFEST.txt not created, aborting." && exit 1; fi
|
||||
mv dist grafana-oncall-app
|
||||
zip -r grafana-oncall-app.zip ./grafana-oncall-app
|
||||
|
|
|
|||
|
|
@ -1,38 +1,18 @@
|
|||
name: "Install frontend dependencies"
|
||||
description: "Setup node + install frontend dependencies"
|
||||
inputs:
|
||||
working-directory:
|
||||
description: "Relative path to oncall/grafana-plugin directory"
|
||||
required: false
|
||||
default: "."
|
||||
name: Install frontend dependencies
|
||||
description: Setup node/pnpm + install frontend dependencies
|
||||
runs:
|
||||
using: "composite"
|
||||
using: composite
|
||||
steps:
|
||||
- name: Determine grafana-plugin directory location
|
||||
id: grafana-plugin-directory
|
||||
shell: bash
|
||||
run: echo "grafana-plugin-directory=${{ inputs.working-directory }}/grafana-plugin" >> $GITHUB_OUTPUT
|
||||
- name: Determine yarn.lock location
|
||||
id: yarn-lock-location
|
||||
shell: bash
|
||||
# yamllint disable rule:line-length
|
||||
run: echo "yarn-lock-location=${{ steps.grafana-plugin-directory.outputs.grafana-plugin-directory }}/yarn.lock" >> $GITHUB_OUTPUT
|
||||
# yamllint enable rule:line-length
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.1.4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.15.1
|
||||
cache: "yarn"
|
||||
cache-dependency-path: ${{ steps.yarn-lock-location.outputs.yarn-lock-location }}
|
||||
- name: Use cached frontend dependencies
|
||||
id: cache-frontend-dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ inputs.working-directory }}/grafana-plugin/node_modules
|
||||
# yamllint disable rule:line-length
|
||||
key: ${{ runner.os }}-frontend-node-modules-${{ hashFiles(steps.yarn-lock-location.outputs.yarn-lock-location) }}
|
||||
# yamllint enable rule:line-length
|
||||
cache: pnpm
|
||||
cache-dependency-path: grafana-plugin/pnpm-lock.yaml
|
||||
- name: Install frontend dependencies
|
||||
if: steps.cache-frontend-dependencies.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: ${{ steps.grafana-plugin-directory.outputs.grafana-plugin-directory }}
|
||||
run: yarn install --frozen-lockfile --prefer-offline --network-timeout 500000
|
||||
working-directory: grafana-plugin
|
||||
run: pnpm install --frozen-lockfile --prefer-offline
|
||||
|
|
|
|||
8
.github/workflows/e2e-tests.yml
vendored
8
.github/workflows/e2e-tests.yml
vendored
|
|
@ -78,12 +78,12 @@ jobs:
|
|||
uses: actions/cache@v4
|
||||
with:
|
||||
path: grafana-plugin/dist
|
||||
key: ${{ runner.os }}-plugin-frontend-${{ hashFiles('grafana-plugin/src/**/*', 'grafana-plugin/yarn.lock') }}
|
||||
key: ${{ runner.os }}-plugin-frontend-${{ hashFiles('grafana-plugin/src/**/*', 'grafana-plugin/pnpm.lock') }}
|
||||
|
||||
- name: Build plugin frontend
|
||||
if: steps.cache-plugin-frontend.outputs.cache-hit != 'true'
|
||||
working-directory: grafana-plugin
|
||||
run: yarn build:dev
|
||||
run: pnpm build:dev
|
||||
|
||||
# helpful reference for properly caching the playwright binaries/dependencies
|
||||
# https://playwrightsolutions.com/playwright-github-action-to-cache-the-browser-binaries/
|
||||
|
|
@ -107,7 +107,7 @@ jobs:
|
|||
- name: Install Playwright deps
|
||||
shell: bash
|
||||
working-directory: grafana-plugin
|
||||
run: yarn playwright install
|
||||
run: pnpm playwright install
|
||||
|
||||
# ---------- Expensive e2e tests steps start -----------
|
||||
- name: Install Go
|
||||
|
|
@ -150,7 +150,7 @@ jobs:
|
|||
if: inputs.run-expensive-tests
|
||||
shell: bash
|
||||
env:
|
||||
E2E_TESTS_CMD: "cd ../../grafana-plugin && yarn test:e2e-expensive"
|
||||
E2E_TESTS_CMD: "cd ../../grafana-plugin && pnpm test:e2e-expensive"
|
||||
GRAFANA_VERSION: ${{ inputs.grafana_version }}
|
||||
GF_FEATURE_TOGGLES_ENABLE: "externalServiceAccounts"
|
||||
ONCALL_API_URL: "http://oncall-dev-engine:8080"
|
||||
|
|
|
|||
2
.github/workflows/linting-and-tests.yml
vendored
2
.github/workflows/linting-and-tests.yml
vendored
|
|
@ -35,7 +35,7 @@ jobs:
|
|||
uses: ./.github/actions/install-frontend-dependencies
|
||||
- name: Build, lint and test frontend
|
||||
working-directory: grafana-plugin
|
||||
run: yarn lint && yarn type-check && yarn test && yarn build
|
||||
run: pnpm lint && pnpm type-check && pnpm test && pnpm build
|
||||
|
||||
test-technical-documentation:
|
||||
name: "Test technical documentation"
|
||||
|
|
|
|||
3
.github/workflows/on-release-published.yml
vendored
3
.github/workflows/on-release-published.yml
vendored
|
|
@ -38,7 +38,6 @@ jobs:
|
|||
repo_secrets: |
|
||||
GRAFANA_ACCESS_POLICY_TOKEN=github_actions:cloud-access-policy-token
|
||||
GCS_PLUGIN_PUBLISHER_SERVICE_ACCOUNT_JSON=github_actions:gcs-plugin-publisher
|
||||
GCOM_PLUGIN_PUBLISHER_API_KEY=github_actions:gcom-plugin-publisher-api-key
|
||||
- name: Build, sign, and package plugin
|
||||
id: build-sign-and-package-plugin
|
||||
uses: ./.github/actions/build-sign-and-package-plugin
|
||||
|
|
@ -64,7 +63,7 @@ jobs:
|
|||
echo url="https://storage.googleapis.com/grafana-oncall-app/releases/grafana-oncall-app-${{ github.ref_name }}.zip" >> $GITHUB_OUTPUT
|
||||
- name: Publish plugin to grafana.com
|
||||
run: |
|
||||
curl -f -w "status=%{http_code}" -s -H "Authorization: Bearer ${{ env.GCOM_PLUGIN_PUBLISHER_API_KEY }}" -d "download[any][url]=${{ steps.gcs-artifact-url.outputs.url }}" -d "download[any][md5]=$(curl -sL ${{ steps.gcs-artifact-url.outputs.url }} | md5sum | cut -d'' '' -f1)" -d url=https://github.com/grafana/oncall/grafana-plugin https://grafana.com/api/plugins
|
||||
curl -f -w "status=%{http_code}" -s -H "Authorization: Bearer ${{ env.GRAFANA_ACCESS_POLICY_TOKEN }}" -d "download[any][url]=${{ steps.gcs-artifact-url.outputs.url }}" -d "download[any][md5]=$(curl -sL ${{ steps.gcs-artifact-url.outputs.url }} | md5sum | cut -d'' '' -f1)" -d url=https://github.com/grafana/oncall/grafana-plugin https://grafana.com/api/plugins
|
||||
# yamllint enable rule:line-length
|
||||
|
||||
build-engine-docker-image-and-publish-to-dockerhub:
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -8,7 +8,7 @@ venv
|
|||
.DS_Store
|
||||
.env
|
||||
|
||||
yarn.lock
|
||||
pnpm.lock
|
||||
node_modules
|
||||
|
||||
test-results
|
||||
|
|
@ -53,7 +53,7 @@ repos:
|
|||
rev: v8.25.0
|
||||
hooks:
|
||||
- id: eslint
|
||||
entry: bash -c "cd grafana-plugin && eslint --max-warnings=0 --fix ${@/grafana-plugin\//}" --
|
||||
entry: bash -c "cd grafana-plugin && eslint --max-warnings=20 --fix ${@/grafana-plugin\//}" --
|
||||
types: [file]
|
||||
files: ^grafana-plugin/src/(?:(?!autogenerated).)*\.(js|jsx|ts|tsx)$
|
||||
additional_dependencies:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ if is_ci:
|
|||
"build-ui",
|
||||
labels=[label],
|
||||
dir=grafana_plugin_dir,
|
||||
cmd="yarn build",
|
||||
cmd="pnpm build",
|
||||
allow_parallel=True,
|
||||
)
|
||||
|
||||
|
|
@ -19,9 +19,9 @@ if not is_ci:
|
|||
"build-ui",
|
||||
labels=[label],
|
||||
dir=grafana_plugin_dir,
|
||||
cmd="yarn install",
|
||||
cmd="pnpm install",
|
||||
serve_dir=grafana_plugin_dir,
|
||||
serve_cmd="yarn watch",
|
||||
serve_cmd="pnpm watch",
|
||||
allow_parallel=True,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ label = "OnCall.AllTests"
|
|||
|
||||
load('ext://uibutton', 'cmd_button', 'location', 'text_input', 'bool_input')
|
||||
|
||||
e2e_tests_cmd=os.getenv("E2E_TESTS_CMD", "cd ../../grafana-plugin && yarn test:e2e")
|
||||
e2e_tests_cmd=os.getenv("E2E_TESTS_CMD", "cd ../../grafana-plugin && pnpm test:e2e")
|
||||
is_ci=config.tilt_subcommand == "ci"
|
||||
|
||||
local_resource(
|
||||
|
|
@ -16,7 +16,7 @@ local_resource(
|
|||
|
||||
cmd_button(
|
||||
name="E2E Tests - headless run",
|
||||
argv=["sh", "-c", "yarn --cwd ./grafana-plugin test:e2e $STOP_ON_FIRST_FAILURE $TESTS_FILTER"],
|
||||
argv=["sh", "-c", "pnpm --dir ./grafana-plugin test:e2e $STOP_ON_FIRST_FAILURE $TESTS_FILTER"],
|
||||
text="Restart headless run",
|
||||
resource="e2e-tests",
|
||||
icon_name="replay",
|
||||
|
|
@ -29,7 +29,7 @@ cmd_button(
|
|||
|
||||
cmd_button(
|
||||
name="E2E Tests - open watch mode",
|
||||
argv=["sh", "-c", "yarn --cwd grafana-plugin test:e2e:watch"],
|
||||
argv=["sh", "-c", "pnpm --dir grafana-plugin test:e2e:watch"],
|
||||
text="Open watch mode",
|
||||
resource="e2e-tests",
|
||||
icon_name="visibility",
|
||||
|
|
@ -37,7 +37,7 @@ cmd_button(
|
|||
|
||||
cmd_button(
|
||||
name="E2E Tests - show report",
|
||||
argv=["sh", "-c", "yarn --cwd grafana-plugin playwright show-report"],
|
||||
argv=["sh", "-c", "pnpm --dir grafana-plugin playwright show-report"],
|
||||
text="Show last HTML report",
|
||||
resource="e2e-tests",
|
||||
icon_name="assignment",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ extends: default
|
|||
|
||||
ignore: |
|
||||
helm/oncall/templates/**/*.yaml
|
||||
pnpm-lock.yaml
|
||||
|
||||
rules:
|
||||
line-length:
|
||||
|
|
|
|||
18
Makefile
18
Makefile
|
|
@ -141,7 +141,7 @@ init: ## build the frontend plugin code then run make start
|
|||
# this makes sure that it will be available when the grafana container starts up without the need to
|
||||
# restart the grafana container initially
|
||||
ifeq ($(findstring $(UI_PROFILE),$(COMPOSE_PROFILES)),$(UI_PROFILE))
|
||||
$(call run_ui_docker_command,yarn install && yarn build:dev)
|
||||
$(call run_ui_docker_command,pnpm install && pnpm build:dev)
|
||||
endif
|
||||
|
||||
stop: # stop all of the docker containers
|
||||
|
|
@ -168,7 +168,7 @@ install-pre-commit:
|
|||
fi
|
||||
|
||||
lint: install-pre-commit ## run both frontend and backend linters
|
||||
## may need to run `yarn install` from within `grafana-plugin`
|
||||
## may need to run `pnpm install` from within `grafana-plugin`
|
||||
## to install several `pre-commit` dependencies
|
||||
pre-commit run --all-files
|
||||
|
||||
|
|
@ -203,25 +203,25 @@ engine-manage: ## run Django's `manage.py` script, inside of a docker container
|
|||
$(call run_engine_docker_command,python manage.py $(CMD))
|
||||
|
||||
test-e2e: ## run the e2e tests in headless mode
|
||||
yarn --cwd grafana-plugin test:e2e
|
||||
pnpm --dir grafana-plugin test:e2e
|
||||
|
||||
test-e2e-watch: ## start e2e tests in watch mode
|
||||
yarn --cwd grafana-plugin test:e2e:watch
|
||||
pnpm --dir grafana-plugin test:e2e:watch
|
||||
|
||||
test-e2e-show-report: ## open last e2e test report
|
||||
yarn --cwd grafana-plugin playwright show-report
|
||||
pnpm --dir grafana-plugin playwright show-report
|
||||
|
||||
ui-test: ## run the UI tests
|
||||
$(call run_ui_docker_command,yarn test)
|
||||
$(call run_ui_docker_command,pnpm test)
|
||||
|
||||
ui-lint: ## run the UI linter
|
||||
$(call run_ui_docker_command,yarn lint)
|
||||
$(call run_ui_docker_command,pnpm lint)
|
||||
|
||||
ui-build: ## build the UI
|
||||
$(call run_ui_docker_command,yarn build)
|
||||
$(call run_ui_docker_command,pnpm build)
|
||||
|
||||
ui-command: ## run any command, inside of a UI docker container, passing `$CMD` as arguments.
|
||||
## e.g. `make ui-command CMD="yarn test"`
|
||||
## e.g. `make ui-command CMD="pnpm test"`
|
||||
$(call run_ui_docker_command,$(CMD))
|
||||
|
||||
exec-engine: ## exec into engine container's bash
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Related: [How to develop integrations](/engine/config_integrations/README.md)
|
|||
- [tilt-dev/ctlptl: Making local Kubernetes clusters fun and easy to set up](https://github.com/tilt-dev/ctlptl)
|
||||
- [Kind](https://kind.sigs.k8s.io)
|
||||
- [Node.js v20.x](https://nodejs.org/en/download)
|
||||
- [Yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable)
|
||||
- [pnpm](https://pnpm.io/installation)
|
||||
|
||||
### Launch the environment
|
||||
|
||||
|
|
@ -519,7 +519,7 @@ In order to automate types creation and prevent API usage pitfalls, OnCall proje
|
|||
|
||||
### Instruction
|
||||
|
||||
1. Whenever API contract changes, run `yarn generate-types` from `grafana-plugin` directory
|
||||
1. Whenever API contract changes, run `pnpm generate-types` from `grafana-plugin` directory
|
||||
2. Then you can start consuming types and you can use fully typed http client:
|
||||
|
||||
```ts
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2.15 on 2024-09-02 13:34
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('alerts', '0057_remove_alertgroup_slack_log_message_db'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='alertgroup',
|
||||
name='reason_to_skip_escalation',
|
||||
field=models.IntegerField(choices=[(0, 'account_inactive'), (1, 'is_archived'), (2, 'no_reason'), (3, 'rate_limited'), (4, 'channel_not_specified'), (5, 'restricted_action'), (6, 'invalid_auth')], default=2),
|
||||
),
|
||||
]
|
||||
|
|
@ -365,14 +365,23 @@ class AlertGroup(AlertGroupSlackRenderingMixin, EscalationSnapshotMixin, models.
|
|||
else:
|
||||
return AlertGroup.NEW
|
||||
|
||||
ACCOUNT_INACTIVE, CHANNEL_ARCHIVED, NO_REASON, RATE_LIMITED, CHANNEL_NOT_SPECIFIED, RESTRICTED_ACTION = range(6)
|
||||
(
|
||||
ACCOUNT_INACTIVE,
|
||||
CHANNEL_ARCHIVED,
|
||||
NO_REASON,
|
||||
RATE_LIMITED,
|
||||
CHANNEL_NOT_SPECIFIED,
|
||||
RESTRICTED_ACTION,
|
||||
INVALID_AUTH,
|
||||
) = range(7)
|
||||
REASONS_TO_SKIP_ESCALATIONS = (
|
||||
(ACCOUNT_INACTIVE, "account_inactive"),
|
||||
(CHANNEL_ARCHIVED, "channel_archived"),
|
||||
(CHANNEL_ARCHIVED, "is_archived"),
|
||||
(NO_REASON, "no_reason"),
|
||||
(RATE_LIMITED, "rate_limited"),
|
||||
(CHANNEL_NOT_SPECIFIED, "channel_not_specified"),
|
||||
(RESTRICTED_ACTION, "restricted_action"),
|
||||
(INVALID_AUTH, "invalid_auth"),
|
||||
)
|
||||
reason_to_skip_escalation = models.IntegerField(choices=REASONS_TO_SKIP_ESCALATIONS, default=NO_REASON)
|
||||
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ class SyncUserSerializer(serializers.Serializer):
|
|||
class SyncTeamSerializer(serializers.Serializer):
|
||||
team_id = serializers.IntegerField()
|
||||
name = serializers.CharField()
|
||||
email = serializers.EmailField(allow_blank=True)
|
||||
avatar_url = serializers.CharField()
|
||||
email = serializers.CharField(allow_blank=True)
|
||||
avatar_url = serializers.CharField(allow_blank=True)
|
||||
|
||||
def create(self, validated_data):
|
||||
return SyncTeam(**validated_data)
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ from unittest.mock import patch
|
|||
import pytest
|
||||
from django.urls import reverse
|
||||
from rest_framework import status
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.grafana_plugin.serializers.sync_data import SyncTeamSerializer
|
||||
from apps.grafana_plugin.sync_data import SyncData, SyncSettings, SyncUser
|
||||
from apps.grafana_plugin.tasks.sync_v2 import start_sync_organizations_v2
|
||||
|
||||
|
|
@ -136,3 +138,24 @@ def test_sync_v2_content_encoding(
|
|||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
mock_sync.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_team, validation_pass",
|
||||
[
|
||||
({"team_id": 1, "name": "Test Team", "email": "", "avatar_url": ""}, True),
|
||||
({"team_id": 1, "name": "", "email": "", "avatar_url": ""}, False),
|
||||
({"name": "ABC", "email": "", "avatar_url": ""}, False),
|
||||
({"team_id": 1, "name": "ABC", "email": "test@example.com", "avatar_url": ""}, True),
|
||||
({"team_id": 1, "name": "123", "email": "<invalid email>", "avatar_url": ""}, True),
|
||||
],
|
||||
)
|
||||
@pytest.mark.django_db
|
||||
def test_sync_team_serialization(test_team, validation_pass):
|
||||
serializer = SyncTeamSerializer(data=test_team)
|
||||
validation_error = None
|
||||
try:
|
||||
serializer.is_valid(raise_exception=True)
|
||||
except ValidationError as e:
|
||||
validation_error = e
|
||||
assert (validation_error is None) == validation_pass
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ from apps.slack.errors import (
|
|||
SlackAPIChannelArchivedError,
|
||||
SlackAPIChannelNotFoundError,
|
||||
SlackAPIError,
|
||||
SlackAPIInvalidAuthError,
|
||||
SlackAPIMessageNotFoundError,
|
||||
SlackAPIRatelimitError,
|
||||
SlackAPIRestrictedActionError,
|
||||
|
|
@ -161,6 +162,10 @@ class AlertShootingStep(scenario_step.ScenarioStep):
|
|||
alert_group.reason_to_skip_escalation = AlertGroup.ACCOUNT_INACTIVE
|
||||
alert_group.save(update_fields=["reason_to_skip_escalation"])
|
||||
logger.info("Not delivering alert due to account_inactive.")
|
||||
except SlackAPIInvalidAuthError:
|
||||
alert_group.reason_to_skip_escalation = AlertGroup.INVALID_AUTH
|
||||
alert_group.save(update_fields=["reason_to_skip_escalation"])
|
||||
logger.info("Not delivering alert due to invalid_auth.")
|
||||
except SlackAPIChannelArchivedError:
|
||||
alert_group.reason_to_skip_escalation = AlertGroup.CHANNEL_ARCHIVED
|
||||
alert_group.save(update_fields=["reason_to_skip_escalation"])
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from unittest.mock import patch
|
|||
import pytest
|
||||
|
||||
from apps.alerts.models import AlertGroup
|
||||
from apps.slack.errors import SlackAPIRestrictedActionError
|
||||
from apps.slack.errors import get_error_class
|
||||
from apps.slack.models import SlackMessage
|
||||
from apps.slack.scenarios.distribute_alerts import AlertShootingStep
|
||||
from apps.slack.scenarios.scenario_step import ScenarioStep
|
||||
|
|
@ -11,11 +11,21 @@ from apps.slack.tests.conftest import build_slack_response
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_restricted_action_error(
|
||||
@pytest.mark.parametrize(
|
||||
"reason,slack_error",
|
||||
[
|
||||
(reason, slack_error)
|
||||
for reason, slack_error in AlertGroup.REASONS_TO_SKIP_ESCALATIONS
|
||||
if reason != AlertGroup.NO_REASON
|
||||
],
|
||||
)
|
||||
def test_skip_escalations_error(
|
||||
make_organization_and_user_with_slack_identities,
|
||||
make_alert_receive_channel,
|
||||
make_alert_group,
|
||||
make_alert,
|
||||
reason,
|
||||
slack_error,
|
||||
):
|
||||
SlackAlertShootingStep = ScenarioStep.get_step("distribute_alerts", "AlertShootingStep")
|
||||
organization, _, slack_team_identity, _ = make_organization_and_user_with_slack_identities()
|
||||
|
|
@ -26,14 +36,17 @@ def test_restricted_action_error(
|
|||
step = SlackAlertShootingStep(slack_team_identity)
|
||||
|
||||
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
|
||||
mock_slack_api_call.side_effect = SlackAPIRestrictedActionError(
|
||||
response=build_slack_response({"error": "restricted_action"})
|
||||
)
|
||||
step._post_alert_group_to_slack(slack_team_identity, alert_group, alert, None, "channel-id", [])
|
||||
error_response = build_slack_response({"error": slack_error})
|
||||
error_class = get_error_class(error_response)
|
||||
mock_slack_api_call.side_effect = error_class(error_response)
|
||||
channel_id = "channel-id"
|
||||
if reason == AlertGroup.CHANNEL_NOT_SPECIFIED:
|
||||
channel_id = None
|
||||
step._post_alert_group_to_slack(slack_team_identity, alert_group, alert, None, channel_id, [])
|
||||
|
||||
alert_group.refresh_from_db()
|
||||
alert.refresh_from_db()
|
||||
assert alert_group.reason_to_skip_escalation == AlertGroup.RESTRICTED_ACTION
|
||||
assert alert_group.reason_to_skip_escalation == reason
|
||||
assert alert_group.slack_message is None
|
||||
assert SlackMessage.objects.count() == 0
|
||||
assert not alert.delivered
|
||||
|
|
|
|||
|
|
@ -186,4 +186,4 @@ class TelegramClient:
|
|||
|
||||
@staticmethod
|
||||
def error_message_is(error: TelegramError, messages: list[str]) -> bool:
|
||||
return error.message in messages
|
||||
return error.message.lower() in (m.lower() for m in messages)
|
||||
|
|
|
|||
4
grafana-plugin/.gitignore
vendored
4
grafana-plugin/.gitignore
vendored
|
|
@ -8,8 +8,8 @@
|
|||
/dist
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
pnpm-error.log*
|
||||
|
||||
# This file is generated
|
||||
grafana-plugin.yml
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@ FROM node:20.15.1-alpine
|
|||
WORKDIR /etc/app
|
||||
ENV PATH /etc/app/node_modules/.bin:$PATH
|
||||
|
||||
CMD ["yarn", "start"]
|
||||
CMD ["pnpm", "start"]
|
||||
|
|
@ -9,29 +9,29 @@
|
|||
"stylelint:fix": "stylelint --fix ./src/**/*.{css,scss,module.css,module.scss}",
|
||||
"build": "NODE_ENV=production webpack -c ./webpack.config.ts --env production",
|
||||
"build:dev": "NODE_ENV=development webpack -c ./webpack.config.ts --env development",
|
||||
"labels:link": "yarn --cwd ../../gops-labels/frontend link && yarn link \"@grafana/labels\" && yarn --cwd ../../gops-labels/frontend watch",
|
||||
"labels:unlink": "yarn --cwd ../../gops-labels/frontend unlink",
|
||||
"labels:link": "pnpm --dir ../../gops-labels/frontend link && pnpm link \"@grafana/labels\" && pnpm --dir ../../gops-labels/frontend watch",
|
||||
"labels:unlink": "pnpm --dir ../../gops-labels/frontend unlink",
|
||||
"mage:build-dev": "go mod download && mage -v build:debug",
|
||||
"mage:watch": "go mod download && mage -v watch",
|
||||
"test-utc": "TZ=UTC jest --verbose --testNamePattern '^((?!@london-tz).)*$'",
|
||||
"test-london-tz": "TZ=Europe/London jest --verbose --testNamePattern '@london-tz'",
|
||||
"test": "PLUGIN_ID=grafana-oncall-app yarn test-utc && yarn test-london-tz",
|
||||
"test": "PLUGIN_ID=grafana-oncall-app pnpm test-utc && pnpm test-london-tz",
|
||||
"test:ci": "PLUGIN_ID=grafana-oncall-app pnpm test-utc && pnpm test-london-tz",
|
||||
"test:report": "HTML_REPORT_ENABLED=true yarn test",
|
||||
"test:silent": "yarn test --silent",
|
||||
"test:e2e": "PLUGIN_ID=grafana-oncall-app yarn playwright test --grep-invert @expensive",
|
||||
"test:e2e-expensive": "PLUGIN_ID=grafana-oncall-app 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",
|
||||
"e2e-show-report": "yarn playwright show-report --port 31000",
|
||||
"generate-types": "cd ./src/network/oncall-api/types-generator && yarn generate",
|
||||
"test:report": "HTML_REPORT_ENABLED=true pnpm test",
|
||||
"test:silent": "pnpm test --silent",
|
||||
"test:e2e": "PLUGIN_ID=grafana-oncall-app pnpm playwright test --grep-invert @expensive",
|
||||
"test:e2e-expensive": "PLUGIN_ID=grafana-oncall-app pnpm playwright test --grep @expensive",
|
||||
"test:e2e:watch": "pnpm test:e2e --ui",
|
||||
"test:e2e-expensive:watch": "pnpm test:e2e-expensive --ui",
|
||||
"test:e2e:gen": "pnpm playwright codegen http://localhost:3000",
|
||||
"e2e-show-report": "pnpm playwright show-report --port 31000",
|
||||
"generate-types": "cd ./src/network/oncall-api/types-generator && pnpm generate",
|
||||
"watch": "NODE_ENV=development webpack -w -c ./webpack.config.ts --env development",
|
||||
"sign": "npx --yes @grafana/sign-plugin@latest",
|
||||
"start": "yarn watch",
|
||||
"start": "pnpm watch",
|
||||
"setversion": "setversion",
|
||||
"type-check": "tsc --noEmit",
|
||||
"type-check:watch": "yarn type-check --watch --preserveWatchOutput false",
|
||||
"type-check:watch": "pnpm type-check --watch --preserveWatchOutput false",
|
||||
"find-dead-code": "knip"
|
||||
},
|
||||
"repository": {
|
||||
|
|
|
|||
13596
grafana-plugin/pnpm-lock.yaml
generated
Normal file
13596
grafana-plugin/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +1,10 @@
|
|||
import { action, observable, makeObservable } from 'mobx';
|
||||
|
||||
interface LoadingResult {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
class LoaderStoreClass {
|
||||
@observable
|
||||
items: LoadingResult = {};
|
||||
items: {
|
||||
[key: string]: boolean;
|
||||
} = {};
|
||||
|
||||
constructor() {
|
||||
makeObservable(this);
|
||||
|
|
|
|||
|
|
@ -59,14 +59,17 @@ const clientConfig = {
|
|||
};
|
||||
|
||||
// We might want to switch to middleware instead of 2 clients once this is published: https://github.com/drwpow/openapi-typescript/pull/1521
|
||||
const onCallApiWithGlobalErrorHandling = createClient<paths>({
|
||||
const onCallApiWithGlobalErrorHandling: ReturnType<typeof createClient<paths>> = createClient<paths>({
|
||||
...clientConfig,
|
||||
fetch: getCustomFetchFn({ withGlobalErrorHandler: true }),
|
||||
});
|
||||
const onCallApiSkipErrorHandling = createClient<paths>({
|
||||
const onCallApiSkipErrorHandling: ReturnType<typeof createClient<paths>> = createClient<paths>({
|
||||
...clientConfig,
|
||||
fetch: getCustomFetchFn({ withGlobalErrorHandler: false }),
|
||||
});
|
||||
|
||||
export const onCallApi = ({ skipErrorHandling = false }: { skipErrorHandling?: boolean } = {}) =>
|
||||
skipErrorHandling ? onCallApiSkipErrorHandling : onCallApiWithGlobalErrorHandling;
|
||||
export function onCallApi({ skipErrorHandling = false }: { skipErrorHandling?: boolean } = {}):
|
||||
| typeof onCallApiSkipErrorHandling
|
||||
| typeof onCallApiWithGlobalErrorHandling {
|
||||
return skipErrorHandling ? onCallApiSkipErrorHandling : onCallApiWithGlobalErrorHandling;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
"include": ["src", "e2e-tests", "playwright.config.ts"],
|
||||
"types": ["node", "@emotion/core"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"declaration": true,
|
||||
"rootDir": "",
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"noUnusedLocals": false,
|
||||
|
|
|
|||
12992
grafana-plugin/yarn.lock
12992
grafana-plugin/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue