From ac57c8ee9f231c14cc800180036fe66b3f20a5d2 Mon Sep 17 00:00:00 2001 From: Vadim Stepanov Date: Thu, 30 Mar 2023 16:34:55 +0100 Subject: [PATCH 1/8] Fix team search (#1680) # What this PR does Team search doesn't work when filtering by team: Screenshot 2023-03-30 at 15 48 50 This PR fixes it + adds backend unit tests. ## 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] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --- CHANGELOG.md | 6 +++++ engine/apps/api/tests/test_team.py | 43 ++++++++++++++++++++++++++++++ engine/apps/api/views/team.py | 18 ++++++------- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cee21a54..68569af4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed + +- Fix team search when filtering resources by @vadimkerr ([#1680](https://github.com/grafana/oncall/pull/1680)) + ## v1.2.6 (2023-03-30) ### Fixed diff --git a/engine/apps/api/tests/test_team.py b/engine/apps/api/tests/test_team.py index 1eb8be04..2f2259cb 100644 --- a/engine/apps/api/tests/test_team.py +++ b/engine/apps/api/tests/test_team.py @@ -45,6 +45,49 @@ def test_list_teams( assert response.json() == expected_payload +@pytest.mark.django_db +@pytest.mark.parametrize( + "search,team_names", + [ + ("", [GENERAL_TEAM.name, "team 1", "team 2"]), + ("team", [GENERAL_TEAM.name, "team 1", "team 2"]), + ("no team", [GENERAL_TEAM.name]), + ("team ", [GENERAL_TEAM.name, "team 1", "team 2"]), + ("team 1", [GENERAL_TEAM.name, "team 1"]), + ], +) +def test_list_teams_search_by_name( + make_organization, + make_team, + make_user_for_organization, + make_token_for_organization, + make_user_auth_headers, + search, + team_names, +): + organization = make_organization() + user = make_user_for_organization(organization) + _, token = make_token_for_organization(organization) + + for team_name in team_names: + if team_name != GENERAL_TEAM.name: + make_team(organization, name=team_name) + + client = APIClient() + + url = reverse("api-internal:team-list") + f"?search={search}" + response = client.get(url, format="json", **make_user_auth_headers(user, token)) + assert response.status_code == status.HTTP_200_OK + + expected_json = [ + get_payload_from_team(organization.teams.get(name=team_name)) + if team_name != GENERAL_TEAM.name + else get_payload_from_team(GENERAL_TEAM) + for team_name in team_names + ] + assert response.json() == expected_json + + @pytest.mark.django_db def test_list_teams_for_non_member( make_organization, diff --git a/engine/apps/api/views/team.py b/engine/apps/api/views/team.py index a7395483..3cfb827a 100644 --- a/engine/apps/api/views/team.py +++ b/engine/apps/api/views/team.py @@ -1,6 +1,6 @@ from rest_framework import mixins, viewsets +from rest_framework.filters import SearchFilter from rest_framework.permissions import IsAuthenticated -from rest_framework.response import Response from apps.api.permissions import RBACPermission from apps.api.serializers.team import TeamSerializer @@ -23,17 +23,15 @@ class TeamViewSet(PublicPrimaryKeyMixin, mixins.ListModelMixin, mixins.UpdateMod } serializer_class = TeamSerializer + filter_backends = [SearchFilter] + search_fields = ["name"] def get_queryset(self): return self.request.user.available_teams - def list(self, request, *args, **kwargs): - queryset = self.filter_queryset(self.get_queryset()) + def filter_queryset(self, queryset): + """ + Adds general team to the queryset in a way that it always shows up first (even when not searched for). + """ general_team = Team(public_primary_key="null", name="No team", email=None, avatar_url=None) - - page = self.paginate_queryset(queryset) - if page is not None: - serializer = self.get_serializer([general_team] + list(page), many=True) - return self.get_paginated_response(serializer.data) - serializer = self.get_serializer([general_team] + list(queryset), many=True) - return Response(serializer.data) + return [general_team] + list(super().filter_queryset(queryset)) From 11d62245e91c86ce0d9b04ddb9aec3d41c83d0ce Mon Sep 17 00:00:00 2001 From: Maxim Mordasov Date: Thu, 30 Mar 2023 21:17:58 +0300 Subject: [PATCH 2/8] fix safari scroll (#1663) # What this PR does Fix scroll in Safari ## Which issue(s) this PR fixes https://github.com/grafana/oncall/issues/415 ## Checklist - [ ] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --------- Co-authored-by: Joey Orlando Co-authored-by: Joey Orlando --- .github/workflows/linting-and-tests.yml | 6 ++---- CHANGELOG.md | 1 + .../alerts/onCallSchedule.test.ts | 3 +++ .../integration-tests/utils/forms.ts | 2 +- grafana-plugin/playwright.config.ts | 20 +++++++------------ .../src/img/grafanaGlobalStyles.css | 3 ++- .../escalation-chains/EscalationChains.tsx | 7 +------ 7 files changed, 17 insertions(+), 25 deletions(-) diff --git a/.github/workflows/linting-and-tests.yml b/.github/workflows/linting-and-tests.yml index d7b0dcc8..351f698b 100644 --- a/.github/workflows/linting-and-tests.yml +++ b/.github/workflows/linting-and-tests.yml @@ -373,16 +373,14 @@ jobs: uses: actions/cache@v3 with: path: "~/.cache/ms-playwright" - key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }} + key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}-chromium-firefox-webkit - name: Install Playwright binaries/dependencies if: steps.playwright-cache.outputs.cache-hit != 'true' - # if more browsers are added, will need to modify the "npx playwright install" command # https://stackoverflow.com/questions/65900299/install-single-dependency-from-package-json-with-yarn run: | yarn add "@playwright/test@${{ env.PLAYWRIGHT_VERSION }}" - npx playwright install --with-deps chromium firefox - npx playwright install-deps + npx playwright install --with-deps chromium firefox webkit - name: Await k8s pods and other resources up uses: jupyterhub/action-k8s-await-workloads@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 68569af4..5e1f4c87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fix team search when filtering resources by @vadimkerr ([#1680](https://github.com/grafana/oncall/pull/1680)) +- Fix issue when trying to scroll in Safari ([#415](https://github.com/grafana/oncall/issues/415)) ## v1.2.6 (2023-03-30) diff --git a/grafana-plugin/integration-tests/alerts/onCallSchedule.test.ts b/grafana-plugin/integration-tests/alerts/onCallSchedule.test.ts index 800faea6..3ba425a9 100644 --- a/grafana-plugin/integration-tests/alerts/onCallSchedule.test.ts +++ b/grafana-plugin/integration-tests/alerts/onCallSchedule.test.ts @@ -6,6 +6,9 @@ import { createIntegrationAndSendDemoAlert } from '../utils/integrations'; import { createOnCallSchedule } from '../utils/schedule'; test('we can create an oncall schedule + receive an alert', async ({ page }) => { + // this test does a lot of stuff, lets give it adequate time to do its thing + test.slow(); + const escalationChainName = generateRandomValue(); const integrationName = generateRandomValue(); const onCallScheduleName = generateRandomValue(); diff --git a/grafana-plugin/integration-tests/utils/forms.ts b/grafana-plugin/integration-tests/utils/forms.ts index 46142e64..10709207 100644 --- a/grafana-plugin/integration-tests/utils/forms.ts +++ b/grafana-plugin/integration-tests/utils/forms.ts @@ -39,7 +39,7 @@ export const clickButton = async ({ dataTestId, }: ClickButtonArgs): Promise => { const baseLocator = dataTestId ? `button[data-testid="${dataTestId}"]` : 'button'; - const button = (startingLocator || page).locator(`${baseLocator} >> text=${buttonText}`); + const button = (startingLocator || page).locator(`${baseLocator}:not([disabled]) >> text=${buttonText}`); await button.waitFor({ state: 'visible' }); await button.click(); diff --git a/grafana-plugin/playwright.config.ts b/grafana-plugin/playwright.config.ts index e1bfa6d8..9084e46a 100644 --- a/grafana-plugin/playwright.config.ts +++ b/grafana-plugin/playwright.config.ts @@ -14,7 +14,6 @@ const config: PlaywrightTestConfig = { testDir: './integration-tests', globalSetup: './integration-tests/globalSetup.ts', /* Maximum time one test can run for. */ - // TODO: set this back to 60 when GSelect component is refactored timeout: 90 * 1000, expect: { /** @@ -28,10 +27,7 @@ const config: PlaywrightTestConfig = { /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, /* Retry on CI only */ - retries: process.env.CI ? 1 : 0, - // TODO: when GSelect component is refactored, run using 3 workers - // locally use one worker, on CI use 3 - // workers: process.env.CI ? 3 : 1, + retries: process.env.CI ? 3 : 0, workers: 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: 'html', @@ -64,14 +60,12 @@ const config: PlaywrightTestConfig = { ...devices['Desktop Firefox'], }, }, - - // TODO: enable tests on Safari once the scroll bug when creating an integration is patched - // { - // name: 'webkit', - // use: { - // ...devices['Desktop Safari'], - // }, - // }, + { + name: 'webkit', + use: { + ...devices['Desktop Safari'], + }, + }, /* Test against mobile viewports. */ // { diff --git a/grafana-plugin/src/img/grafanaGlobalStyles.css b/grafana-plugin/src/img/grafanaGlobalStyles.css index 525dd58d..31e4fa5d 100644 --- a/grafana-plugin/src/img/grafanaGlobalStyles.css +++ b/grafana-plugin/src/img/grafanaGlobalStyles.css @@ -1,6 +1,7 @@ /* Navigation/Layout */ -.drawer-content { +.drawer-content, +.rc-drawer-content { overflow: auto !important; /* fix https://github.com/grafana/oncall/issues/415 */ } diff --git a/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx b/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx index fd67d695..0b4001dd 100644 --- a/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx +++ b/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx @@ -286,12 +286,7 @@ class EscalationChainsPage extends React.Component - + {escalationChain.name}
From 2b3e269e28b2c190fdb2e3ebfba29ed47468fed0 Mon Sep 17 00:00:00 2001 From: Maxim Mordasov Date: Fri, 31 Mar 2023 12:12:21 +0300 Subject: [PATCH 3/8] Save selected teams filter in local storage (#1613) # What this PR does Selected teams filter is now saved in local storage ## Which issue(s) this PR fixes https://github.com/grafana/oncall/issues/1611 ## Checklist - [ ] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --------- Co-authored-by: Joey Orlando --- CHANGELOG.md | 4 ++++ grafana-plugin/src/models/filters/filters.ts | 20 +++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e1f4c87..52e26ac6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,10 @@ Only some minor performance/developer setup changes to report in this version. ## v1.2.2 (2023-03-27) +### Added + +- Save selected teams filter in local storage ([1611](https://github.com/grafana/oncall/issues/1611)) + ### Changed - Drawers with Forms are not closing by clicking outside of the drawer. Only by clicking Cancel or X (by @Ukochka in [#1608](https://github.com/grafana/oncall/pull/1608)) diff --git a/grafana-plugin/src/models/filters/filters.ts b/grafana-plugin/src/models/filters/filters.ts index 8d5e2406..eaca60d7 100644 --- a/grafana-plugin/src/models/filters/filters.ts +++ b/grafana-plugin/src/models/filters/filters.ts @@ -3,10 +3,13 @@ import { action, observable } from 'mobx'; import BaseStore from 'models/base_store'; import { makeRequest } from 'network'; import { RootStore } from 'state'; +import { getItem, setItem } from 'utils/localStorage'; import { getApiPathByPage } from './filters.helpers'; import { FilterOption, FiltersValues } from './filters.types'; +const LOCAL_STORAGE_FILTERS_KEY = 'grafana.oncall.global-filters'; + export class FiltersStore extends BaseStore { @observable.shallow public options: { [page: string]: FilterOption[] } = {}; @@ -14,10 +17,25 @@ export class FiltersStore extends BaseStore { @observable.shallow public values: { [page: string]: FiltersValues } = {}; - public globalValues: FiltersValues = {}; + private _globalValues: FiltersValues = {}; constructor(rootStore: RootStore) { super(rootStore); + + const savedFilters = getItem(LOCAL_STORAGE_FILTERS_KEY); + if (savedFilters) { + this._globalValues = { ...savedFilters }; + } + } + + set globalValues(value: any) { + this._globalValues = value; + + setItem(LOCAL_STORAGE_FILTERS_KEY, value); + } + + get globalValues() { + return this._globalValues; } @action From 0acac58af089fb0d4fb6e1e74f973289e130f085 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Fri, 31 Mar 2023 12:54:01 +0300 Subject: [PATCH 4/8] Incidents mapped to /alert-groups (#1678) # What this PR does Reopened old reverted PR that @Ukochka worked on. In addition it has fixed the redirect from `?page=incident` to `/alert-groups` --------- Co-authored-by: Yulia Shanyrova Co-authored-by: Joey Orlando --- CHANGELOG.md | 4 +++ .../src/components/Tutorial/Tutorial.tsx | 2 +- .../AlertReceiveChannelCard.tsx | 2 +- .../src/containers/AlertRules/AlertRules.tsx | 2 +- .../AttachIncidentForm/AttachIncidentForm.tsx | 6 ++-- .../IncidentMatcher/IncidentMatcher.tsx | 2 +- .../IntegrationSettings.tsx | 1 + .../parts/Autoresolve.module.css | 7 +++- .../IntegrationSettings/parts/Autoresolve.tsx | 6 ++-- .../MaintenanceForm.config.tsx | 2 +- .../RotationForm/RotationForm.module.css | 3 +- .../escalation-chains/EscalationChains.tsx | 1 + .../src/pages/incident/Incident.tsx | 14 ++++---- .../src/pages/incidents/Incidents.tsx | 10 ++++-- grafana-plugin/src/pages/index.tsx | 25 +++++--------- grafana-plugin/src/plugin.json | 2 +- .../src/plugin/GrafanaPluginRootPage.tsx | 33 +++++++++++++++++-- grafana-plugin/src/utils/consts.ts | 2 +- 18 files changed, 79 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52e26ac6..3de3cbc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Changes + +- Renamed routes from /incidents to /alert-groups ([#1678](https://github.com/grafana/oncall/pull/1678)) + ### Fixed - Fix team search when filtering resources by @vadimkerr ([#1680](https://github.com/grafana/oncall/pull/1680)) diff --git a/grafana-plugin/src/components/Tutorial/Tutorial.tsx b/grafana-plugin/src/components/Tutorial/Tutorial.tsx index 51a05bee..213e33bd 100644 --- a/grafana-plugin/src/components/Tutorial/Tutorial.tsx +++ b/grafana-plugin/src/components/Tutorial/Tutorial.tsx @@ -66,7 +66,7 @@ const Tutorial: FC = (props) => {
- +
diff --git a/grafana-plugin/src/containers/AlertReceiveChannelCard/AlertReceiveChannelCard.tsx b/grafana-plugin/src/containers/AlertReceiveChannelCard/AlertReceiveChannelCard.tsx index 71c1b2f1..cc4699d5 100644 --- a/grafana-plugin/src/containers/AlertReceiveChannelCard/AlertReceiveChannelCard.tsx +++ b/grafana-plugin/src/containers/AlertReceiveChannelCard/AlertReceiveChannelCard.tsx @@ -65,7 +65,7 @@ const AlertReceiveChannelCard = observer((props: AlertReceiveChannelCardProps) = {alertReceiveChannelCounter && ( (
Demo alert was generated. Find it on the - "Alert Groups" + "Alert Groups" page and make sure it didn't freak out your colleagues 😉
); diff --git a/grafana-plugin/src/containers/AttachIncidentForm/AttachIncidentForm.tsx b/grafana-plugin/src/containers/AttachIncidentForm/AttachIncidentForm.tsx index a7445181..74b110ce 100644 --- a/grafana-plugin/src/containers/AttachIncidentForm/AttachIncidentForm.tsx +++ b/grafana-plugin/src/containers/AttachIncidentForm/AttachIncidentForm.tsx @@ -63,15 +63,15 @@ const AttachIncidentForm = observer(({ id, onUpdate, onHide }: AttachIncidentFor title={ - Attach to another incident + Attach to another alert group } className={cx('root')} onDismiss={onHide} > { {selectedAlertItem ? ( {JSON.stringify(selectedAlertItem, null, 2)} ) : ( - ← Select incident first + ← Select alert group first )}
diff --git a/grafana-plugin/src/containers/IntegrationSettings/IntegrationSettings.tsx b/grafana-plugin/src/containers/IntegrationSettings/IntegrationSettings.tsx index ace4c65d..0154fe2c 100644 --- a/grafana-plugin/src/containers/IntegrationSettings/IntegrationSettings.tsx +++ b/grafana-plugin/src/containers/IntegrationSettings/IntegrationSettings.tsx @@ -59,6 +59,7 @@ const IntegrationSettings = observer((props: IntegrationSettingsProps) => { const [expanded, _setExpanded] = useState(false); const handleSwitchToTemplate = (templateName: string) => { + setActiveTab(IntegrationSettingsTab.Templates); setSelectedTemplate(templateName); }; diff --git a/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.module.css b/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.module.css index c3710fe4..0614492e 100644 --- a/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.module.css +++ b/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.module.css @@ -34,7 +34,12 @@ padding: 4px 8px; margin-top: 8px; min-width: 500px; - width: 520px; + width: 620px; +} + +.autoresolve-div { + display: flex; + align-items: baseline; } .warning-icon-color { diff --git a/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.tsx b/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.tsx index 8d61fcb4..6b4e8c83 100644 --- a/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.tsx +++ b/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.tsx @@ -150,7 +150,7 @@ const Autoresolve = ({ alertReceiveChannelId, onSwitchToTemplate, alertGroupId }
@@ -172,9 +172,9 @@ const Autoresolve = ({ alertReceiveChannelId, onSwitchToTemplate, alertGroupId } {autoresolveSelected && ( <> -
+
- Incident will be automatically resolved when it matches{' '} + Alert group will be automatically resolved when it matches{' '} @@ -244,7 +244,7 @@ class IncidentPage extends React.Component - + {/* @ts-ignore*/} @@ -256,12 +256,12 @@ class IncidentPage extends React.Component {incident.root_alert_group && ( Attached to{' '} - + #{incident.root_alert_group.inside_organization_number}{' '} {incident.root_alert_group.render_for_web.title} {' '} - @@ -421,9 +421,7 @@ class IncidentPage extends React.Component getUnattachClickHandler = (pk: Alert['pk']) => { const { store } = this.props; - return () => { - store.alertGroupStore.unattachAlert(pk).then(this.update); - }; + return store.alertGroupStore.unattachAlert(pk).then(this.update); }; renderTimeline = () => { @@ -762,7 +760,7 @@ function AttachedIncidentsList({ {alerts.map((incident) => { return ( - + #{incident.inside_organization_number} {incident.render_for_web.title} diff --git a/grafana-plugin/src/pages/incidents/Incidents.tsx b/grafana-plugin/src/pages/incidents/Incidents.tsx index cc4769cc..5c2fe3de 100644 --- a/grafana-plugin/src/pages/incidents/Incidents.tsx +++ b/grafana-plugin/src/pages/incidents/Incidents.tsx @@ -127,7 +127,7 @@ class Incidents extends React.Component this.setState({ showAddAlertGroupForm: false }); }} onCreate={(id: Alert['pk']) => { - history.push(`${PLUGIN_ROOT}/incidents/${id}`); + history.push(`${PLUGIN_ROOT}/alert-groups/${id}`); }} /> )} @@ -557,7 +557,13 @@ class Incidents extends React.Component
{record.render_for_web.title} diff --git a/grafana-plugin/src/pages/index.tsx b/grafana-plugin/src/pages/index.tsx index acdeceb4..c3c228be 100644 --- a/grafana-plugin/src/pages/index.tsx +++ b/grafana-plugin/src/pages/index.tsx @@ -27,24 +27,11 @@ function getPath(name = '') { export const pages: { [id: string]: PageDefinition } = [ { icon: 'bell', - id: 'incidents', + id: 'alert-groups', hideFromBreadcrumbs: true, text: 'Alert Groups', hideTitle: true, - path: getPath('incidents'), - action: UserActions.AlertGroupsRead, - }, - { - icon: 'bell', - id: 'incident', - text: '', - hideFromTabs: true, - hideFromBreadcrumbs: true, - parentItem: { - text: 'Incident', - url: `${PLUGIN_ROOT}/incidents`, - }, - path: getPath('incident'), + path: getPath('alert-groups'), action: UserActions.AlertGroupsRead, }, { @@ -189,8 +176,8 @@ export const pages: { [id: string]: PageDefinition } = [ }, {}); export const ROUTES = { - incidents: ['incidents'], - incident: ['incidents/:id'], + 'alert-groups': ['alert-groups'], + 'alert-group': ['alert-groups/:id'], users: ['users', 'users/:id'], integrations: ['integrations', 'integrations/:id'], escalations: ['escalations', 'escalations/:id'], @@ -205,6 +192,10 @@ export const ROUTES = { 'live-settings': ['live-settings'], cloud: ['cloud'], test: ['test'], + + // backwards compatible to redirect to new alert-groups + incident: ['incidents/:id'], + incidents: ['incidents'], }; export const getRoutesForPage = (name: string) => { diff --git a/grafana-plugin/src/plugin.json b/grafana-plugin/src/plugin.json index 7f10dc71..2ab9172e 100644 --- a/grafana-plugin/src/plugin.json +++ b/grafana-plugin/src/plugin.json @@ -41,7 +41,7 @@ { "type": "page", "name": "Alert Groups", - "path": "/a/grafana-oncall-app/incidents", + "path": "/a/grafana-oncall-app/alert-groups", "role": "Viewer", "action": "grafana-oncall-app.alert-groups:read", "addToNav": true diff --git a/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx b/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx index 04fafaf8..4412037f 100644 --- a/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx +++ b/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx @@ -14,7 +14,7 @@ import weekday from 'dayjs/plugin/weekday'; import { observer, Provider } from 'mobx-react'; import Header from 'navbar/Header/Header'; import LegacyNavTabsBar from 'navbar/LegacyNavTabsBar'; -import { Route, Switch, useLocation } from 'react-router-dom'; +import { Redirect, Route, Switch, useLocation } from 'react-router-dom'; import { AppRootProps } from 'types'; import Unauthorized from 'components/Unauthorized'; @@ -138,10 +138,10 @@ export const Root = observer((props: AppRootProps) => { > {userHasAccess ? ( - + - + @@ -183,6 +183,33 @@ export const Root = observer((props: AppRootProps) => { + + {/* Backwards compatibility redirect routes */} + ( + + )} + > + ( + + )} + > + diff --git a/grafana-plugin/src/utils/consts.ts b/grafana-plugin/src/utils/consts.ts index 57e004ba..11c01ed8 100644 --- a/grafana-plugin/src/utils/consts.ts +++ b/grafana-plugin/src/utils/consts.ts @@ -11,7 +11,7 @@ export const GRAFANA_LICENSE_OSS = 'OpenSource'; export const BREAKPOINT_TABS = 1024; // Default redirect page -export const DEFAULT_PAGE = 'incidents'; +export const DEFAULT_PAGE = 'alert-groups'; export const PLUGIN_ROOT = '/a/grafana-oncall-app'; From cf026c6c9a0435b1618448e164618df33819c22d Mon Sep 17 00:00:00 2001 From: Vadim Stepanov Date: Fri, 31 Mar 2023 14:53:27 +0100 Subject: [PATCH 5/8] PD migrator: migrate push notification user rules (#1685) --- tools/pagerduty-migrator/README.md | 2 +- tools/pagerduty-migrator/migrator/config.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/pagerduty-migrator/README.md b/tools/pagerduty-migrator/README.md index 90e571ef..0b102d9c 100644 --- a/tools/pagerduty-migrator/README.md +++ b/tools/pagerduty-migrator/README.md @@ -86,7 +86,7 @@ pd-oncall-migrator It's possible to specify a default contact method type for user notification rules that cannot be migrated as-is by changing the `ONCALL_DEFAULT_CONTACT_METHOD` env variable. -Options are: `email`, `sms`, `phone_call`, `slack`, `telegram` (default is `email`). +Options are: `email`, `sms`, `phone_call`, `slack`, `telegram`, `mobile_app` (default is `email`). ### After migration diff --git a/tools/pagerduty-migrator/migrator/config.py b/tools/pagerduty-migrator/migrator/config.py index f2baeccb..cc2fc812 100644 --- a/tools/pagerduty-migrator/migrator/config.py +++ b/tools/pagerduty-migrator/migrator/config.py @@ -21,7 +21,7 @@ PAGERDUTY_TO_ONCALL_CONTACT_METHOD_MAP = { "sms_contact_method": "notify_by_sms", "phone_contact_method": "notify_by_phone_call", "email_contact_method": "notify_by_email", - "push_notification_contact_method": ONCALL_DEFAULT_CONTACT_METHOD, + "push_notification_contact_method": "notify_by_mobile_app", } PAGERDUTY_TO_ONCALL_VENDOR_MAP = { "Datadog": "datadog", From 47b0ab446587b5cd988f1fb75ec79c5df36b8c41 Mon Sep 17 00:00:00 2001 From: Vadim Stepanov Date: Fri, 31 Mar 2023 18:12:45 +0100 Subject: [PATCH 6/8] PD migrator: include service & integration names to ruleset (#1687) # What this PR does Allows to include service & integration names to migrated ruleset name when passing `EXPERIMENTAL_MIGRATE_EVENT_RULES_LONG_NAMES=True`. --- tools/pagerduty-migrator/migrator/__main__.py | 8 +++- tools/pagerduty-migrator/migrator/config.py | 4 ++ tools/pagerduty-migrator/migrator/report.py | 6 +-- .../migrator/resources/rulesets.py | 43 +++++++++++++++++-- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/tools/pagerduty-migrator/migrator/__main__.py b/tools/pagerduty-migrator/migrator/__main__.py index 403f9232..086273f0 100644 --- a/tools/pagerduty-migrator/migrator/__main__.py +++ b/tools/pagerduty-migrator/migrator/__main__.py @@ -126,7 +126,13 @@ def main() -> None: if rulesets is not None: for ruleset in rulesets: - match_ruleset(ruleset, oncall_integrations, escalation_policies, services) + match_ruleset( + ruleset, + oncall_integrations, + escalation_policies, + services, + integrations, + ) if MODE == MODE_PLAN: print(user_report(users), end="\n\n") diff --git a/tools/pagerduty-migrator/migrator/config.py b/tools/pagerduty-migrator/migrator/config.py index cc2fc812..21c2013d 100644 --- a/tools/pagerduty-migrator/migrator/config.py +++ b/tools/pagerduty-migrator/migrator/config.py @@ -46,3 +46,7 @@ SCHEDULE_MIGRATION_MODE = os.getenv( EXPERIMENTAL_MIGRATE_EVENT_RULES = ( os.getenv("EXPERIMENTAL_MIGRATE_EVENT_RULES", "false").lower() == "true" ) +# Set to true to include service & integration names in the ruleset name +EXPERIMENTAL_MIGRATE_EVENT_RULES_LONG_NAMES = ( + os.getenv("EXPERIMENTAL_MIGRATE_EVENT_RULES_LONG_NAMES", "false").lower() == "true" +) diff --git a/tools/pagerduty-migrator/migrator/report.py b/tools/pagerduty-migrator/migrator/report.py index b9b6aeef..d63dbd0e 100644 --- a/tools/pagerduty-migrator/migrator/report.py +++ b/tools/pagerduty-migrator/migrator/report.py @@ -189,10 +189,8 @@ def ruleset_report(rulesets: list[dict]) -> str: ): result += "\n" + TAB + format_ruleset(ruleset) if not ruleset["flawed_escalation_policies"] and ruleset["oncall_integration"]: - result += ( - " (existing integration with name '{} Ruleset' will be deleted)".format( - ruleset["name"] - ) + result += " (existing integration with name '{}' will be deleted)".format( + ruleset["oncall_name"] ) return result diff --git a/tools/pagerduty-migrator/migrator/resources/rulesets.py b/tools/pagerduty-migrator/migrator/resources/rulesets.py index a486a0ad..4dd25c64 100644 --- a/tools/pagerduty-migrator/migrator/resources/rulesets.py +++ b/tools/pagerduty-migrator/migrator/resources/rulesets.py @@ -1,4 +1,5 @@ from migrator import oncall_api_client +from migrator.config import EXPERIMENTAL_MIGRATE_EVENT_RULES_LONG_NAMES from migrator.utils import find_by_id @@ -7,14 +8,16 @@ def match_ruleset( oncall_integrations: list[dict], escalation_policies: list[dict], services: list[dict], + integrations: list[dict], ) -> None: # Find existing integration with the same name oncall_integration = None - name = "{} Ruleset".format(ruleset["name"]).lower().strip() + name = _generate_ruleset_name(ruleset, services, integrations) for candidate in oncall_integrations: - if candidate["name"].lower().strip() == name: + if candidate["name"].lower().strip() == name.lower().strip(): oncall_integration = candidate ruleset["oncall_integration"] = oncall_integration + ruleset["oncall_name"] = name # Find services that use escalation policies that cannot be migrated service_ids = [ @@ -52,7 +55,7 @@ def migrate_ruleset( # Create new integration with type "webhook" integration_payload = { - "name": "{} Ruleset".format(ruleset["name"]), + "name": ruleset["oncall_name"], "type": "webhook", "team_id": None, } @@ -163,3 +166,37 @@ def _pd_service_id_to_oncall_escalation_chain_id( escalation_chain_id = escalation_policy["oncall_escalation_chain"]["id"] return escalation_chain_id + + +def _generate_ruleset_name(ruleset, services, integrations): + result = "{} Ruleset".format(ruleset["name"]) + if not EXPERIMENTAL_MIGRATE_EVENT_RULES_LONG_NAMES: + return result + + service_ids = [ + r["actions"]["route"]["value"] + for r in sorted(ruleset["rules"], key=lambda r: r["position"]) + if not r["disabled"] and r["actions"]["route"] + ] + + ruleset_services = [find_by_id(services, service_id) for service_id in service_ids] + ruleset_services = [s for s in ruleset_services if s is not None] + if not ruleset_services: + return result + + service_names = [] + for service in ruleset_services: + service_name = service["name"] + service_integrations = [ + integration + for integration in integrations + if integration["service"]["id"] == service["id"] + ] + if service_integrations: + service_name += " ({})".format( + ", ".join([integration["name"] for integration in service_integrations]) + ) + service_names.append(service_name) + + # OnCall limit for integration name is 150 chars + return "{}: {}".format(result, ", ".join(service_names))[:150] From 0b23ad3d244eb8588355605ed84eff9deafd4a16 Mon Sep 17 00:00:00 2001 From: Joey Orlando Date: Mon, 3 Apr 2023 11:50:00 +0200 Subject: [PATCH 7/8] fix changelog --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3de3cbc2..479cbb46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -### Changes +### Added + +- Save selected teams filter in local storage ([1611](https://github.com/grafana/oncall/issues/1611)) + +### Changed - Renamed routes from /incidents to /alert-groups ([#1678](https://github.com/grafana/oncall/pull/1678)) @@ -46,10 +50,6 @@ Only some minor performance/developer setup changes to report in this version. ## v1.2.2 (2023-03-27) -### Added - -- Save selected teams filter in local storage ([1611](https://github.com/grafana/oncall/issues/1611)) - ### Changed - Drawers with Forms are not closing by clicking outside of the drawer. Only by clicking Cancel or X (by @Ukochka in [#1608](https://github.com/grafana/oncall/pull/1608)) From c4f2b28c2fb737a6a137ee2d73da44b942734099 Mon Sep 17 00:00:00 2001 From: Joey Orlando Date: Mon, 3 Apr 2023 11:50:55 +0200 Subject: [PATCH 8/8] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 479cbb46..278c8470 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## v1.2.7 (2023-04-03) ### Added