From df6f7af8a562f2fd228e8b5eb513fc338d99a448 Mon Sep 17 00:00:00 2001 From: Dominik Broj Date: Thu, 5 Sep 2024 16:51:29 +0200 Subject: [PATCH 1/5] chore: adjust dir names to irm (#4989) # What this PR does - rename utils to helpers - rename types.ts to app-types.ts ## Which issue(s) this PR closes Related to https://raintank-corp.slack.com/archives/C0762D6EUDV/p1725477060488709 https://raintank-corp.slack.com/archives/C0762D6EUDV/p1724936143487849 TL;DR IRM needs imported modules from oncall/incident to have uniq absolute import paths ## Checklist - [ ] 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. --- .../configuration.test.ts | 2 +- .../initialization.test.ts | 2 +- .../e2e-tests/schedules/scheduleView.test.ts | 3 ++- grafana-plugin/e2e-tests/utils/navigation.ts | 2 +- grafana-plugin/src/PluginPage.tsx | 2 +- grafana-plugin/src/{types.ts => app-types.ts} | 3 +-- .../src/components/CardButton/CardButton.tsx | 2 +- .../src/components/CheatSheet/CheatSheet.tsx | 4 ++-- .../CopyToClipboardIcon.tsx | 3 +-- .../CursorPagination/CursorPagination.tsx | 2 +- .../ExtensionLinkDropdown.tsx | 2 +- .../ExtensionLinkMenu/ExtensionLinkMenu.tsx | 4 ++-- .../FullPageError/FullPageError.tsx | 2 +- .../HamburgerContextMenu.tsx | 2 +- .../IntegrationContactPoint.tsx | 4 ++-- .../IntegrationHowToConnect.tsx | 2 +- .../IntegrationInputField.tsx | 2 +- .../IntegrationLogoWithTitle.tsx | 2 +- .../IntegrationSendDemoAlertModal.tsx | 4 ++-- .../Integrations/IntegrationTemplateBlock.tsx | 2 +- .../LabelsTooltipBadge/LabelsTooltipBadge.tsx | 2 +- .../ManualAlertGroup/ManualAlertGroup.tsx | 2 +- .../components/MonacoEditor/MonacoEditor.tsx | 3 +-- .../NewScheduleSelector.tsx | 4 ++-- .../PageErrorHandlingWrapper.tsx | 4 ++-- .../src/components/PluginLink/PluginLink.tsx | 3 +-- .../components/Policy/EscalationPolicy.tsx | 4 ++-- .../components/Policy/NotificationPolicy.tsx | 4 ++-- .../ScheduleQuality/ScheduleQuality.tsx | 2 +- .../ScheduleQualityDetails.tsx | 2 +- .../src/components/SourceCode/SourceCode.tsx | 5 ++-- grafana-plugin/src/components/Tabs/Tabs.tsx | 3 +-- grafana-plugin/src/components/Text/Text.tsx | 3 +-- .../TextEllipsisTooltip.tsx | 2 +- .../components/TooltipBadge/TooltipBadge.tsx | 2 +- .../components/Unauthorized/Unauthorized.tsx | 4 ++-- .../src/components/UserGroups/UserGroups.tsx | 2 +- .../components/UsersFilters/UsersFilters.tsx | 3 +-- .../Webhooks/WebhookLastEventDetails.tsx | 2 +- .../AddResponders/AddResponders.tsx | 4 ++-- .../AddRespondersPopup/AddRespondersPopup.tsx | 4 ++-- .../parts/connectors/MSTeamsConnector.tsx | 4 ++-- .../parts/connectors/SlackConnector.tsx | 4 ++-- .../parts/connectors/TelegramConnector.tsx | 4 ++-- .../src/containers/Alerts/Alerts.tsx | 8 +++---- .../ApiTokenSettings/ApiTokenForm.tsx | 2 +- .../ApiTokenSettings/ApiTokenSettings.tsx | 6 ++++- .../AttachIncidentForm/AttachIncidentForm.tsx | 2 +- .../ColumnsSelector/ColumnsSelector.tsx | 6 ++--- .../ColumnsSelectorWrapper/ColumnsModal.tsx | 10 ++++---- .../ColumnsSelectorWrapper.tsx | 8 +++---- .../DefaultPageLayout/DefaultPageLayout.tsx | 2 +- .../EditRegexpRouteTemplateModal.tsx | 4 ++-- .../EscalationChainCard.tsx | 2 +- .../EscalationChainForm.tsx | 2 +- .../EscalationChainSteps.tsx | 2 +- .../src/containers/GSelect/GSelect.tsx | 3 +-- .../GrafanaTeamSelect/GrafanaTeamSelect.tsx | 2 +- .../CollapsedIntegrationRouteDisplay.tsx | 2 +- .../ExpandedIntegrationRouteDisplay.tsx | 6 ++--- .../IntegrationHeartbeatForm.tsx | 6 ++--- .../IntegrationTemplatesList.tsx | 2 +- .../IntegrationForm/IntegrationForm.tsx | 22 ++++++++--------- .../IntegrationFormContainer.tsx | 2 +- .../IntegrationLabelsForm.tsx | 4 ++-- .../IntegrationTemplate.tsx | 4 ++-- .../src/containers/Labels/Labels.tsx | 4 ++-- .../MSTeams/MSTeamsInstructions.tsx | 4 ++-- .../MSTeamsIntegrationButton.tsx | 2 +- .../MaintenanceForm/MaintenanceForm.tsx | 4 ++-- .../MobileAppConnection.test.tsx | 4 ++-- .../MobileAppConnection.tsx | 8 +++---- .../parts/DownloadIcons/DownloadIcons.tsx | 2 +- .../parts/LinkLoginButton/LinkLoginButton.tsx | 2 +- .../OutgoingWebhookForm.tsx | 6 ++--- .../OutgoingWebhookFormFields.tsx | 2 +- .../WebhookPresetBlocks.tsx | 2 +- .../OutgoingWebhookStatus.tsx | 2 +- .../PersonalNotificationSettings.tsx | 2 +- .../PluginConfigPage/PluginConfigPage.tsx | 24 +++++++++---------- .../PluginInitializer/PluginInitializer.tsx | 6 ++--- .../RemoteFilters/RemoteFilters.tsx | 6 ++--- .../containers/RemoteSelect/RemoteSelect.tsx | 4 ++-- .../RotationForm/RotationForm.helpers.ts | 2 +- .../containers/RotationForm/RotationForm.tsx | 4 ++-- .../RotationForm/ScheduleOverrideForm.tsx | 4 ++-- .../containers/RotationForm/ShiftSwapForm.tsx | 6 ++--- .../RotationForm/parts/DeletionModal.tsx | 2 +- .../containers/Rotations/Rotations.helpers.ts | 2 +- .../src/containers/Rotations/Rotations.tsx | 4 ++-- .../containers/Rotations/ScheduleFinal.tsx | 2 +- .../Rotations/ScheduleOverrides.tsx | 4 ++-- .../containers/Rotations/SchedulePersonal.tsx | 4 ++-- .../RouteLabelsDisplay/RouteLabelsDisplay.tsx | 4 ++-- .../containers/ScheduleForm/ScheduleForm.tsx | 4 ++-- .../ScheduleIcalLink/ScheduleIcalLink.tsx | 2 +- .../containers/ScheduleSlot/ScheduleSlot.tsx | 4 ++-- .../CompleteServiceNowConfigModal.tsx | 4 ++-- .../ServiceNowAuthSection.tsx | 4 ++-- .../ServiceNowConfig.helpers.ts | 2 +- .../ServiceNowConfigDrawer.tsx | 8 +++---- .../ServiceNowStatusSection.tsx | 2 +- .../ServiceNowTokenSection.tsx | 4 ++-- .../src/containers/TeamsList/TeamsList.tsx | 2 +- .../TelegramIntegrationButton.tsx | 6 ++--- .../TemplatePreview/TemplatePreview.tsx | 6 ++--- .../TemplateResult/TemplateResult.tsx | 2 +- .../UserDisplay/UserDisplayWithAvatar.tsx | 2 +- .../containers/UserSettings/UserSettings.tsx | 6 ++--- .../UserSettings/parts/UserSettingsParts.tsx | 2 +- .../parts/connectors/GoogleConnector.tsx | 4 ++-- .../parts/connectors/ICalConnector.tsx | 6 ++--- .../parts/connectors/MSTeamsConnector.tsx | 2 +- .../parts/connectors/PhoneConnector.tsx | 2 +- .../parts/connectors/SlackConnector.tsx | 4 ++-- .../parts/connectors/TelegramConnector.tsx | 2 +- .../CloudPhoneSettings/CloudPhoneSettings.tsx | 4 ++-- .../tabs/GoogleCalendar/GoogleCalendar.tsx | 4 ++-- .../PhoneVerification/PhoneVerification.tsx | 6 ++--- .../parts/tabs/SlackTab/SlackTab.tsx | 4 ++-- .../parts/tabs/TelegramInfo/TelegramInfo.tsx | 6 ++--- .../ScheduleUserDetails.tsx | 4 ++-- .../WebhooksTemplateEditor.tsx | 2 +- .../WithPermissionControlDisplay.tsx | 4 ++-- .../WithPermissionControlTooltip.tsx | 3 +-- grafana-plugin/src/{utils => helpers}/DOM.ts | 0 .../src/{utils => helpers}/LocationHelper.ts | 0 .../src/{utils => helpers}/async.test.ts | 0 .../src/{utils => helpers}/async.ts | 0 .../authorization/authorization.test.ts | 3 +-- .../authorization/authorization.ts | 3 +-- .../src/{utils => helpers}/consts.ts | 2 +- .../src/{utils => helpers}/datetime.test.ts | 0 .../src/{utils => helpers}/datetime.ts | 0 .../src/{utils => helpers}/decorators.ts | 3 ++- .../src/{utils => helpers}/faro.test.tsx | 3 +-- grafana-plugin/src/{utils => helpers}/faro.ts | 0 .../utils.test.ts => helpers/helpers.test.ts} | 3 +-- .../{utils/utils.ts => helpers/helpers.ts} | 0 grafana-plugin/src/{utils => helpers}/hoc.tsx | 0 .../src/{utils => helpers}/hooks.tsx | 0 .../src/{utils => helpers}/loadJs.ts | 0 .../src/{utils => helpers}/localStorage.ts | 0 .../src/{utils => helpers}/sanitize.ts | 0 .../src/{utils => helpers}/string.ts | 0 .../src/{utils => helpers}/styles.ts | 0 .../src/{utils => helpers}/types.ts | 0 grafana-plugin/src/{utils => helpers}/url.ts | 0 .../alert_receive_channel.helpers.ts | 7 +++--- .../alert_receive_channel.ts | 4 ++-- ...lert_receive_channel_connected_channels.ts | 2 +- .../alert_receive_channel_webhooks.ts | 4 ++-- .../models/alertgroup/alertgroup.helpers.ts | 3 ++- .../src/models/alertgroup/alertgroup.ts | 6 ++--- grafana-plugin/src/models/base_store.ts | 2 +- .../src/models/filters/filters.helpers.ts | 2 +- grafana-plugin/src/models/filters/filters.ts | 6 ++--- .../src/models/heartbeat/heartbeat.ts | 2 +- grafana-plugin/src/models/label/label.ts | 4 ++-- .../outgoing_webhook.types.ts | 2 +- grafana-plugin/src/models/plugin/plugin.ts | 6 ++--- .../src/models/schedule/schedule.ts | 2 +- grafana-plugin/src/models/slack/slack.ts | 4 ++-- .../src/models/user/user.helpers.tsx | 2 +- grafana-plugin/src/models/user/user.ts | 4 ++-- grafana-plugin/src/module.ts | 6 ++--- grafana-plugin/src/navbar/Header/Header.tsx | 2 +- .../src/network/grafana-api/api.types.d.ts | 2 +- .../src/network/grafana-api/http-client.ts | 5 ++-- grafana-plugin/src/network/network.ts | 7 +++--- .../network/oncall-api/http-client.test.ts | 4 ++-- .../src/network/oncall-api/http-client.ts | 9 ++++--- grafana-plugin/src/pages/NoMatch.tsx | 5 ++-- .../escalation-chains/EscalationChains.tsx | 6 ++--- .../src/pages/incident/Incident.helpers.tsx | 4 ++-- .../src/pages/incident/Incident.tsx | 14 +++++------ .../src/pages/incidents/Incidents.tsx | 12 +++++----- .../incidents/parts/IncidentDropdown.tsx | 2 +- .../incidents/parts/IncidentSilenceModal.tsx | 4 ++-- .../pages/incidents/parts/SilenceSelect.tsx | 2 +- .../src/pages/insights/Insights.tsx | 2 +- .../pages/integration/Integration.helper.ts | 2 +- .../src/pages/integration/Integration.tsx | 14 +++++------ .../pages/integration/IntegrationActions.tsx | 8 +++---- .../integration/IntegrationCommon.config.ts | 2 +- .../OutgoingTab/ConnectIntegrationModal.tsx | 2 +- .../ConnectedIntegrationsTable.tsx | 4 ++-- .../NewOutgoingWebhookDrawerContent.tsx | 4 ++-- .../OutgoingTab/OutgoingTab.hooks.ts | 2 +- .../integration/OutgoingTab/OutgoingTab.tsx | 2 +- .../OutgoingWebhookDetailsDrawerTabs.tsx | 4 ++-- .../OutgoingTab/OutgoingWebhookFormFields.tsx | 2 +- .../OutgoingTab/OutgoingWebhooksTable.tsx | 10 ++++---- .../src/pages/integrations/Integrations.tsx | 10 ++++---- .../outgoing_webhooks/OutgoingWebhooks.tsx | 8 +++---- grafana-plugin/src/pages/pages.tsx | 4 ++-- .../src/pages/schedule/Schedule.tsx | 8 +++---- .../src/pages/schedules/Schedules.tsx | 8 +++---- .../src/pages/settings/SettingsPage.tsx | 4 ++-- .../src/pages/settings/SettingsPage.types.ts | 2 +- .../settings/tabs/ChatOps/ChatOps.helpers.ts | 4 ++-- .../pages/settings/tabs/ChatOps/ChatOps.tsx | 2 +- .../tabs/SlackSettings/SlackSettings.tsx | 8 +++---- .../TelegramSettings/TelegramSettings.tsx | 2 +- .../pages/settings/tabs/Cloud/CloudPage.tsx | 8 +++---- .../tabs/LiveSettings/LiveSettingsPage.tsx | 2 +- .../tabs/MainSettings/MainSettings.tsx | 2 +- grafana-plugin/src/pages/users/Users.tsx | 12 ++++++---- .../GrafanaPluginRootPage.helpers.test.tsx | 3 +-- .../src/plugin/GrafanaPluginRootPage.tsx | 10 ++++---- .../src/state/rootBaseStore/RootBaseStore.ts | 8 +++---- grafana-plugin/src/state/types.ts | 2 +- 212 files changed, 397 insertions(+), 404 deletions(-) rename grafana-plugin/src/{types.ts => app-types.ts} (96%) rename grafana-plugin/src/{utils => helpers}/DOM.ts (100%) rename grafana-plugin/src/{utils => helpers}/LocationHelper.ts (100%) rename grafana-plugin/src/{utils => helpers}/async.test.ts (100%) rename grafana-plugin/src/{utils => helpers}/async.ts (100%) rename grafana-plugin/src/{utils => helpers}/authorization/authorization.test.ts (98%) rename grafana-plugin/src/{utils => helpers}/authorization/authorization.ts (99%) rename grafana-plugin/src/{utils => helpers}/consts.ts (98%) rename grafana-plugin/src/{utils => helpers}/datetime.test.ts (100%) rename grafana-plugin/src/{utils => helpers}/datetime.ts (100%) rename grafana-plugin/src/{utils => helpers}/decorators.ts (98%) rename grafana-plugin/src/{utils => helpers}/faro.test.tsx (97%) rename grafana-plugin/src/{utils => helpers}/faro.ts (100%) rename grafana-plugin/src/{utils/utils.test.ts => helpers/helpers.test.ts} (98%) rename grafana-plugin/src/{utils/utils.ts => helpers/helpers.ts} (100%) rename grafana-plugin/src/{utils => helpers}/hoc.tsx (100%) rename grafana-plugin/src/{utils => helpers}/hooks.tsx (100%) rename grafana-plugin/src/{utils => helpers}/loadJs.ts (100%) rename grafana-plugin/src/{utils => helpers}/localStorage.ts (100%) rename grafana-plugin/src/{utils => helpers}/sanitize.ts (100%) rename grafana-plugin/src/{utils => helpers}/string.ts (100%) rename grafana-plugin/src/{utils => helpers}/styles.ts (100%) rename grafana-plugin/src/{utils => helpers}/types.ts (100%) rename grafana-plugin/src/{utils => helpers}/url.ts (100%) diff --git a/grafana-plugin/e2e-tests/pluginInitialization/configuration.test.ts b/grafana-plugin/e2e-tests/pluginInitialization/configuration.test.ts index 1c66b63bec..aecf58457e 100644 --- a/grafana-plugin/e2e-tests/pluginInitialization/configuration.test.ts +++ b/grafana-plugin/e2e-tests/pluginInitialization/configuration.test.ts @@ -1,4 +1,4 @@ -import { PLUGIN_CONFIG } from 'utils/consts'; +import { PLUGIN_CONFIG } from 'helpers/consts'; import { test, expect } from '../fixtures'; import { goToGrafanaPage } from '../utils/navigation'; diff --git a/grafana-plugin/e2e-tests/pluginInitialization/initialization.test.ts b/grafana-plugin/e2e-tests/pluginInitialization/initialization.test.ts index 774c18404b..4472548248 100644 --- a/grafana-plugin/e2e-tests/pluginInitialization/initialization.test.ts +++ b/grafana-plugin/e2e-tests/pluginInitialization/initialization.test.ts @@ -1,4 +1,4 @@ -import { waitInMs } from 'utils/async'; +import { waitInMs } from 'helpers/async'; import { test, expect, Page } from '../fixtures'; import { OrgRole, isGrafanaVersionLowerThan } from '../utils/constants'; diff --git a/grafana-plugin/e2e-tests/schedules/scheduleView.test.ts b/grafana-plugin/e2e-tests/schedules/scheduleView.test.ts index 4e0bfa640c..95b7b548e7 100644 --- a/grafana-plugin/e2e-tests/schedules/scheduleView.test.ts +++ b/grafana-plugin/e2e-tests/schedules/scheduleView.test.ts @@ -1,6 +1,7 @@ +import { HTML_ID } from 'helpers/DOM'; + 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'; diff --git a/grafana-plugin/e2e-tests/utils/navigation.ts b/grafana-plugin/e2e-tests/utils/navigation.ts index 3d8cdbefd9..3eb32a8bd6 100644 --- a/grafana-plugin/e2e-tests/utils/navigation.ts +++ b/grafana-plugin/e2e-tests/utils/navigation.ts @@ -1,8 +1,8 @@ import { KeyValue } from '@grafana/data'; import type { Page } from '@playwright/test'; +import { getPluginId } from 'helpers/consts'; import qs from 'query-string'; -import { getPluginId } from 'utils/consts'; import { BASE_URL } from './constants'; diff --git a/grafana-plugin/src/PluginPage.tsx b/grafana-plugin/src/PluginPage.tsx index 895a190583..70020c93f7 100644 --- a/grafana-plugin/src/PluginPage.tsx +++ b/grafana-plugin/src/PluginPage.tsx @@ -1,12 +1,12 @@ import React from 'react'; import { PluginPageProps, PluginPage as RealPluginPage } from '@grafana/runtime'; +import { DEFAULT_PAGE } from 'helpers/consts'; import { Header } from 'navbar/Header/Header'; import { RenderConditionally } from 'components/RenderConditionally/RenderConditionally'; import { pages } from 'pages/pages'; import { isTopNavbar } from 'plugin/GrafanaPluginRootPage.helpers'; -import { DEFAULT_PAGE } from 'utils/consts'; interface AppPluginPageProps extends PluginPageProps { page?: string; diff --git a/grafana-plugin/src/types.ts b/grafana-plugin/src/app-types.ts similarity index 96% rename from grafana-plugin/src/types.ts rename to grafana-plugin/src/app-types.ts index 44547883c5..497d3488dd 100644 --- a/grafana-plugin/src/types.ts +++ b/grafana-plugin/src/app-types.ts @@ -1,6 +1,5 @@ import { AppRootProps as BaseAppRootProps, AppPluginMeta, PluginConfigPageProps } from '@grafana/data'; - -import { getPluginId } from 'utils/consts'; +import { getPluginId } from 'helpers/consts'; export type OnCallPluginMetaJSONData = { stackId: number; diff --git a/grafana-plugin/src/components/CardButton/CardButton.tsx b/grafana-plugin/src/components/CardButton/CardButton.tsx index 43d0da6177..102a49db13 100644 --- a/grafana-plugin/src/components/CardButton/CardButton.tsx +++ b/grafana-plugin/src/components/CardButton/CardButton.tsx @@ -2,10 +2,10 @@ import React, { FC } from 'react'; import { cx } from '@emotion/css'; import { Stack, useStyles2 } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; import { Block } from 'components/GBlock/Block'; import { Text } from 'components/Text/Text'; -import { StackSize } from 'utils/consts'; import { getCardButtonStyles } from './CardButton.styles'; diff --git a/grafana-plugin/src/components/CheatSheet/CheatSheet.tsx b/grafana-plugin/src/components/CheatSheet/CheatSheet.tsx index bb28a07ba6..b60354a9f7 100644 --- a/grafana-plugin/src/components/CheatSheet/CheatSheet.tsx +++ b/grafana-plugin/src/components/CheatSheet/CheatSheet.tsx @@ -1,13 +1,13 @@ import React from 'react'; import { IconButton, Stack, useStyles2 } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; +import { openNotification } from 'helpers/helpers'; import CopyToClipboard from 'react-copy-to-clipboard'; import { bem, getUtilStyles } from 'styles/utils.styles'; import { Block } from 'components/GBlock/Block'; import { Text } from 'components/Text/Text'; -import { StackSize } from 'utils/consts'; -import { openNotification } from 'utils/utils'; import { CheatSheetInterface, CheatSheetItem } from './CheatSheet.config'; import { getCheatSheetStyles } from './CheatSheet.styles'; diff --git a/grafana-plugin/src/components/CopyToClipboardIcon/CopyToClipboardIcon.tsx b/grafana-plugin/src/components/CopyToClipboardIcon/CopyToClipboardIcon.tsx index 2bbfa34d56..a474da16eb 100644 --- a/grafana-plugin/src/components/CopyToClipboardIcon/CopyToClipboardIcon.tsx +++ b/grafana-plugin/src/components/CopyToClipboardIcon/CopyToClipboardIcon.tsx @@ -1,10 +1,9 @@ import React, { FC } from 'react'; import { IconButton } from '@grafana/ui'; +import { openNotification } from 'helpers/helpers'; import CopyToClipboard from 'react-copy-to-clipboard'; -import { openNotification } from 'utils/utils'; - interface CopyToClipboardProps { text: string; iconButtonProps?: Partial[0]>; diff --git a/grafana-plugin/src/components/CursorPagination/CursorPagination.tsx b/grafana-plugin/src/components/CursorPagination/CursorPagination.tsx index a24eb49a09..124c0220b8 100644 --- a/grafana-plugin/src/components/CursorPagination/CursorPagination.tsx +++ b/grafana-plugin/src/components/CursorPagination/CursorPagination.tsx @@ -2,9 +2,9 @@ import React, { FC, useCallback, useEffect, useState } from 'react'; import { SelectableValue } from '@grafana/data'; import { Button, Icon, Select, Stack } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; import { Text } from 'components/Text/Text'; -import { StackSize } from 'utils/consts'; interface CursorPaginationProps { current: string; diff --git a/grafana-plugin/src/components/ExtensionLinkMenu/ExtensionLinkDropdown.tsx b/grafana-plugin/src/components/ExtensionLinkMenu/ExtensionLinkDropdown.tsx index 16d077f8d4..29b0545e21 100644 --- a/grafana-plugin/src/components/ExtensionLinkMenu/ExtensionLinkDropdown.tsx +++ b/grafana-plugin/src/components/ExtensionLinkMenu/ExtensionLinkDropdown.tsx @@ -7,7 +7,7 @@ import { usePluginLinks as originalUsePluginLinks, } from '@grafana/runtime'; import { Dropdown, ToolbarButton } from '@grafana/ui'; -import { OnCallPluginExtensionPoints } from 'types'; +import { OnCallPluginExtensionPoints } from 'app-types'; import { ApiSchemas } from 'network/oncall-api/api.types'; diff --git a/grafana-plugin/src/components/ExtensionLinkMenu/ExtensionLinkMenu.tsx b/grafana-plugin/src/components/ExtensionLinkMenu/ExtensionLinkMenu.tsx index f271a00cba..f8f530f832 100644 --- a/grafana-plugin/src/components/ExtensionLinkMenu/ExtensionLinkMenu.tsx +++ b/grafana-plugin/src/components/ExtensionLinkMenu/ExtensionLinkMenu.tsx @@ -2,10 +2,10 @@ import React, { ReactElement, useMemo } from 'react'; import { locationUtil, PluginExtensionLink, PluginExtensionTypes } from '@grafana/data'; import { IconName, Menu } from '@grafana/ui'; +import { getPluginId } from 'helpers/consts'; +import { truncateTitle } from 'helpers/string'; import { PluginBridge, SupportedPlugin } from 'components/PluginBridge/PluginBridge'; -import { getPluginId } from 'utils/consts'; -import { truncateTitle } from 'utils/string'; type Props = { extensions: PluginExtensionLink[]; diff --git a/grafana-plugin/src/components/FullPageError/FullPageError.tsx b/grafana-plugin/src/components/FullPageError/FullPageError.tsx index 8b440d7ca5..129c7f6bfa 100644 --- a/grafana-plugin/src/components/FullPageError/FullPageError.tsx +++ b/grafana-plugin/src/components/FullPageError/FullPageError.tsx @@ -2,10 +2,10 @@ import React, { FC } from 'react'; import { css } from '@emotion/css'; import { useStyles2, Stack } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; import errorSVG from 'assets/img/error.svg'; import { Text } from 'components/Text/Text'; -import { StackSize } from 'utils/consts'; interface FullPageErrorProps { children?: React.ReactNode; diff --git a/grafana-plugin/src/components/HamburgerContextMenu/HamburgerContextMenu.tsx b/grafana-plugin/src/components/HamburgerContextMenu/HamburgerContextMenu.tsx index bd84f8cff2..9c36e97da0 100644 --- a/grafana-plugin/src/components/HamburgerContextMenu/HamburgerContextMenu.tsx +++ b/grafana-plugin/src/components/HamburgerContextMenu/HamburgerContextMenu.tsx @@ -3,11 +3,11 @@ import React, { FC, ReactNode } from 'react'; import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2 } from '@grafana/ui'; +import { isUserActionAllowed, UserAction } from 'helpers/authorization/authorization'; import { HamburgerMenuIcon } from 'components/HamburgerMenuIcon/HamburgerMenuIcon'; import { WithContextMenu } from 'components/WithContextMenu/WithContextMenu'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; -import { isUserActionAllowed, UserAction } from 'utils/authorization/authorization'; interface HamburgerContextMenuProps { items: Array< diff --git a/grafana-plugin/src/components/IntegrationContactPoint/IntegrationContactPoint.tsx b/grafana-plugin/src/components/IntegrationContactPoint/IntegrationContactPoint.tsx index 25abaf35ef..da9ef14295 100644 --- a/grafana-plugin/src/components/IntegrationContactPoint/IntegrationContactPoint.tsx +++ b/grafana-plugin/src/components/IntegrationContactPoint/IntegrationContactPoint.tsx @@ -3,6 +3,8 @@ import React, { useEffect, useReducer } from 'react'; import { SelectableValue } from '@grafana/data'; import { Button, Drawer, Icon, IconButton, Input, RadioButtonGroup, Select, Tooltip, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { GENERIC_ERROR, StackSize } from 'helpers/consts'; +import { openErrorNotification, openNotification } from 'helpers/helpers'; import { observer } from 'mobx-react'; import { GTable } from 'components/GTable/GTable'; @@ -15,8 +17,6 @@ import { ContactPoint } from 'models/alert_receive_channel/alert_receive_channel import { ApiSchemas } from 'network/oncall-api/api.types'; import styles from 'pages/integration/Integration.module.scss'; import { useStore } from 'state/useStore'; -import { GENERIC_ERROR, StackSize } from 'utils/consts'; -import { openErrorNotification, openNotification } from 'utils/utils'; const cx = cn.bind(styles); diff --git a/grafana-plugin/src/components/IntegrationHowToConnect/IntegrationHowToConnect.tsx b/grafana-plugin/src/components/IntegrationHowToConnect/IntegrationHowToConnect.tsx index aa4f3ed74d..a3a7295dbe 100644 --- a/grafana-plugin/src/components/IntegrationHowToConnect/IntegrationHowToConnect.tsx +++ b/grafana-plugin/src/components/IntegrationHowToConnect/IntegrationHowToConnect.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Icon, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; import { noop } from 'lodash-es'; import { IntegrationInputField } from 'components/IntegrationInputField/IntegrationInputField'; @@ -11,7 +12,6 @@ import { Text } from 'components/Text/Text'; import { ApiSchemas } from 'network/oncall-api/api.types'; import styles from 'pages/integration/Integration.module.scss'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; const cx = cn.bind(styles); diff --git a/grafana-plugin/src/components/IntegrationInputField/IntegrationInputField.tsx b/grafana-plugin/src/components/IntegrationInputField/IntegrationInputField.tsx index 8b01302345..c79fdac45e 100644 --- a/grafana-plugin/src/components/IntegrationInputField/IntegrationInputField.tsx +++ b/grafana-plugin/src/components/IntegrationInputField/IntegrationInputField.tsx @@ -2,9 +2,9 @@ import React, { useState } from 'react'; import { cx } from '@emotion/css'; import { IconButton, Input, Stack, useStyles2 } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; import { CopyToClipboardIcon } from 'components/CopyToClipboardIcon/CopyToClipboardIcon'; -import { StackSize } from 'utils/consts'; import { getIntegrationInputFieldStyles } from './IntegrationInputField.styles'; diff --git a/grafana-plugin/src/components/IntegrationLogo/IntegrationLogoWithTitle.tsx b/grafana-plugin/src/components/IntegrationLogo/IntegrationLogoWithTitle.tsx index 203bb241be..037229ccc6 100644 --- a/grafana-plugin/src/components/IntegrationLogo/IntegrationLogoWithTitle.tsx +++ b/grafana-plugin/src/components/IntegrationLogo/IntegrationLogoWithTitle.tsx @@ -1,9 +1,9 @@ import React, { FC } from 'react'; import { Stack } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; import { Text } from 'components/Text/Text'; -import { StackSize } from 'utils/consts'; import { IntegrationLogo, IntegrationLogoProps } from './IntegrationLogo'; diff --git a/grafana-plugin/src/components/IntegrationSendDemoAlertModal/IntegrationSendDemoAlertModal.tsx b/grafana-plugin/src/components/IntegrationSendDemoAlertModal/IntegrationSendDemoAlertModal.tsx index eac18542cf..fefd973b5b 100644 --- a/grafana-plugin/src/components/IntegrationSendDemoAlertModal/IntegrationSendDemoAlertModal.tsx +++ b/grafana-plugin/src/components/IntegrationSendDemoAlertModal/IntegrationSendDemoAlertModal.tsx @@ -2,6 +2,8 @@ import React, { useState } from 'react'; import { Button, Icon, Modal, Tooltip, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; +import { openNotification } from 'helpers/helpers'; import CopyToClipboard from 'react-copy-to-clipboard'; import Emoji from 'react-emoji-render'; import { debounce } from 'throttle-debounce'; @@ -14,8 +16,6 @@ import { AlertReceiveChannelHelper } from 'models/alert_receive_channel/alert_re import { ApiSchemas } from 'network/oncall-api/api.types'; import styles from 'pages/integration/Integration.module.scss'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; -import { openNotification } from 'utils/utils'; const cx = cn.bind(styles); diff --git a/grafana-plugin/src/components/Integrations/IntegrationTemplateBlock.tsx b/grafana-plugin/src/components/Integrations/IntegrationTemplateBlock.tsx index d25ea77aba..65c47112ea 100644 --- a/grafana-plugin/src/components/Integrations/IntegrationTemplateBlock.tsx +++ b/grafana-plugin/src/components/Integrations/IntegrationTemplateBlock.tsx @@ -3,10 +3,10 @@ import React from 'react'; import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, InlineLabel, LoadingPlaceholder, useStyles2 } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; import { WithConfirm } from 'components/WithConfirm/WithConfirm'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; -import { UserActions } from 'utils/authorization/authorization'; interface IntegrationTemplateBlockProps { label: string; diff --git a/grafana-plugin/src/components/LabelsTooltipBadge/LabelsTooltipBadge.tsx b/grafana-plugin/src/components/LabelsTooltipBadge/LabelsTooltipBadge.tsx index 77eea52451..d42e5109ee 100644 --- a/grafana-plugin/src/components/LabelsTooltipBadge/LabelsTooltipBadge.tsx +++ b/grafana-plugin/src/components/LabelsTooltipBadge/LabelsTooltipBadge.tsx @@ -2,12 +2,12 @@ import React, { FC } from 'react'; import { LabelTag } from '@grafana/labels'; import { Stack, Button, Tooltip } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; import { RenderConditionally } from 'components/RenderConditionally/RenderConditionally'; import { TooltipBadge } from 'components/TooltipBadge/TooltipBadge'; import { LabelKeyValue } from 'models/label/label.types'; import { components } from 'network/oncall-api/autogenerated-api.types'; -import { StackSize } from 'utils/consts'; interface LabelsTooltipBadgeProps { labels: LabelKeyValue[]; diff --git a/grafana-plugin/src/components/ManualAlertGroup/ManualAlertGroup.tsx b/grafana-plugin/src/components/ManualAlertGroup/ManualAlertGroup.tsx index 0321db1566..09511065ef 100644 --- a/grafana-plugin/src/components/ManualAlertGroup/ManualAlertGroup.tsx +++ b/grafana-plugin/src/components/ManualAlertGroup/ManualAlertGroup.tsx @@ -2,6 +2,7 @@ import React, { FC, useCallback } from 'react'; import { css } from '@emotion/css'; import { Button, Drawer, Field, TextArea, useStyles2, Stack } from '@grafana/ui'; +import { openWarningNotification } from 'helpers/helpers'; import { observer } from 'mobx-react'; import { Controller, FormProvider, useForm } from 'react-hook-form'; import { getUtilStyles } from 'styles/utils.styles'; @@ -11,7 +12,6 @@ import { prepareForUpdate } from 'containers/AddResponders/AddResponders.helpers import { AlertReceiveChannelStore } from 'models/alert_receive_channel/alert_receive_channel'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { openWarningNotification } from 'utils/utils'; export type FormData = { message: string; diff --git a/grafana-plugin/src/components/MonacoEditor/MonacoEditor.tsx b/grafana-plugin/src/components/MonacoEditor/MonacoEditor.tsx index b108eb6b86..4dcbbca921 100644 --- a/grafana-plugin/src/components/MonacoEditor/MonacoEditor.tsx +++ b/grafana-plugin/src/components/MonacoEditor/MonacoEditor.tsx @@ -2,8 +2,7 @@ import React, { ComponentProps, FC, useCallback } from 'react'; import { CodeEditor, CodeEditorSuggestionItemKind, LoadingPlaceholder } from '@grafana/ui'; import cn from 'classnames'; - -import { getPaths } from 'utils/utils'; +import { getPaths } from 'helpers/helpers'; import { conf, language as jinja2Language } from './jinja2'; diff --git a/grafana-plugin/src/components/NewScheduleSelector/NewScheduleSelector.tsx b/grafana-plugin/src/components/NewScheduleSelector/NewScheduleSelector.tsx index 2b0a4e1610..5a4ca4302d 100644 --- a/grafana-plugin/src/components/NewScheduleSelector/NewScheduleSelector.tsx +++ b/grafana-plugin/src/components/NewScheduleSelector/NewScheduleSelector.tsx @@ -2,14 +2,14 @@ import React, { FC, useCallback, useState } from 'react'; import { css } from '@emotion/css'; import { Button, Drawer, Icon, Stack, useStyles2 } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; import { Block } from 'components/GBlock/Block'; import { Text } from 'components/Text/Text'; import { ScheduleForm } from 'containers/ScheduleForm/ScheduleForm'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; import { Schedule, ScheduleType } from 'models/schedule/schedule.types'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; interface NewScheduleSelectorProps { onHide: () => void; diff --git a/grafana-plugin/src/components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.tsx b/grafana-plugin/src/components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.tsx index bf9598f55e..cca9d052f8 100644 --- a/grafana-plugin/src/components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.tsx +++ b/grafana-plugin/src/components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.tsx @@ -3,11 +3,11 @@ import React, { ReactElement, useEffect } from 'react'; import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Stack, useStyles2 } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; +import { openWarningNotification } from 'helpers/helpers'; import { PluginLink } from 'components/PluginLink/PluginLink'; import { Text } from 'components/Text/Text'; -import { StackSize } from 'utils/consts'; -import { openWarningNotification } from 'utils/utils'; export interface PageBaseState { errorData: PageErrorData; diff --git a/grafana-plugin/src/components/PluginLink/PluginLink.tsx b/grafana-plugin/src/components/PluginLink/PluginLink.tsx index a6d5d2cd1b..412366d5a0 100644 --- a/grafana-plugin/src/components/PluginLink/PluginLink.tsx +++ b/grafana-plugin/src/components/PluginLink/PluginLink.tsx @@ -3,11 +3,10 @@ import React, { FC, useCallback, useMemo } from 'react'; import { css, cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2 } from '@grafana/ui'; +import { getPathFromQueryParams } from 'helpers/url'; import { Link } from 'react-router-dom-v5-compat'; import { bem } from 'styles/utils.styles'; -import { getPathFromQueryParams } from 'utils/url'; - interface PluginLinkProps { disabled?: boolean; className?: string; diff --git a/grafana-plugin/src/components/Policy/EscalationPolicy.tsx b/grafana-plugin/src/components/Policy/EscalationPolicy.tsx index 719c9bc029..c9a145d4ce 100644 --- a/grafana-plugin/src/components/Policy/EscalationPolicy.tsx +++ b/grafana-plugin/src/components/Policy/EscalationPolicy.tsx @@ -3,6 +3,8 @@ import React, { ChangeEvent } from 'react'; import { cx } from '@emotion/css'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { Button, Input, Select, IconButton, withTheme2 } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { openWarningNotification } from 'helpers/helpers'; import { isNumber } from 'lodash-es'; import { observer } from 'mobx-react'; import moment from 'moment-timezone'; @@ -30,8 +32,6 @@ import { UserGroup } from 'models/user_group/user_group.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { openWarningNotification } from 'utils/utils'; import { DragHandle } from './DragHandle'; import { getEscalationPolicyStyles } from './EscalationPolicy.styles'; diff --git a/grafana-plugin/src/components/Policy/NotificationPolicy.tsx b/grafana-plugin/src/components/Policy/NotificationPolicy.tsx index b6fb4a9f3e..c2ad3722e5 100644 --- a/grafana-plugin/src/components/Policy/NotificationPolicy.tsx +++ b/grafana-plugin/src/components/Policy/NotificationPolicy.tsx @@ -3,6 +3,8 @@ import React from 'react'; import { css, cx } from '@emotion/css'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { Button, IconButton, Select, withTheme2 } from '@grafana/ui'; +import { UserAction } from 'helpers/authorization/authorization'; +import { openWarningNotification } from 'helpers/helpers'; import { isNumber } from 'lodash'; import { SortableElement } from 'react-sortable-hoc'; @@ -15,8 +17,6 @@ import { ApiSchemas } from 'network/oncall-api/api.types'; import { AppFeature } from 'state/features'; import { RootStore } from 'state/rootStore'; import { SelectOption } from 'state/types'; -import { UserAction } from 'utils/authorization/authorization'; -import { openWarningNotification } from 'utils/utils'; import { DragHandle } from './DragHandle'; import { POLICY_DURATION_LIST_MINUTES, POLICY_DURATION_LIST_SECONDS } from './Policy.consts'; diff --git a/grafana-plugin/src/components/ScheduleQuality/ScheduleQuality.tsx b/grafana-plugin/src/components/ScheduleQuality/ScheduleQuality.tsx index fd0c71e56d..68724896ca 100644 --- a/grafana-plugin/src/components/ScheduleQuality/ScheduleQuality.tsx +++ b/grafana-plugin/src/components/ScheduleQuality/ScheduleQuality.tsx @@ -2,6 +2,7 @@ import React, { FC, useEffect } from 'react'; import { cx } from '@emotion/css'; import { Tooltip, Stack, useStyles2 } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { getUtilStyles } from 'styles/utils.styles'; @@ -12,7 +13,6 @@ import { Text } from 'components/Text/Text'; import { TooltipBadge } from 'components/TooltipBadge/TooltipBadge'; import { Schedule, ScheduleScoreQualityResult } from 'models/schedule/schedule.types'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; import { getScheduleQualityStyles } from './ScheduleQuality.styles'; diff --git a/grafana-plugin/src/components/ScheduleQualityDetails/ScheduleQualityDetails.tsx b/grafana-plugin/src/components/ScheduleQualityDetails/ScheduleQualityDetails.tsx index ec36031818..a31cd567ba 100644 --- a/grafana-plugin/src/components/ScheduleQualityDetails/ScheduleQualityDetails.tsx +++ b/grafana-plugin/src/components/ScheduleQualityDetails/ScheduleQualityDetails.tsx @@ -2,11 +2,11 @@ import React, { FC, useCallback, useState } from 'react'; import { cx } from '@emotion/css'; import { Icon, IconButton, Stack, useStyles2 } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; import { bem, getUtilStyles } from 'styles/utils.styles'; import { Text } from 'components/Text/Text'; import { ScheduleScoreQualityResponse, ScheduleScoreQualityResult } from 'models/schedule/schedule.types'; -import { StackSize } from 'utils/consts'; import { getScheduleQualityDetailsStyles } from './ScheduleQualityDetails.styles'; import { ScheduleQualityProgressBar } from './ScheduleQualityProgressBar'; diff --git a/grafana-plugin/src/components/SourceCode/SourceCode.tsx b/grafana-plugin/src/components/SourceCode/SourceCode.tsx index 96158c522b..fb43d7a38e 100644 --- a/grafana-plugin/src/components/SourceCode/SourceCode.tsx +++ b/grafana-plugin/src/components/SourceCode/SourceCode.tsx @@ -3,12 +3,11 @@ import React, { FC } from 'react'; import { css, cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, IconButton, useStyles2 } from '@grafana/ui'; +import { openNotification } from 'helpers/helpers'; +import { formatSourceCodeJsonString } from 'helpers/string'; import CopyToClipboard from 'react-copy-to-clipboard'; import { bem } from 'styles/utils.styles'; -import { formatSourceCodeJsonString } from 'utils/string'; -import { openNotification } from 'utils/utils'; - interface SourceCodeProps { noMaxHeight?: boolean; noMinHeight?: boolean; diff --git a/grafana-plugin/src/components/Tabs/Tabs.tsx b/grafana-plugin/src/components/Tabs/Tabs.tsx index 19e2e68eae..715e60d23a 100644 --- a/grafana-plugin/src/components/Tabs/Tabs.tsx +++ b/grafana-plugin/src/components/Tabs/Tabs.tsx @@ -3,8 +3,7 @@ import React, { FC, useEffect, useState } from 'react'; import { css } from '@emotion/css'; import { Tab, TabsBar, TabContent, useStyles2 } from '@grafana/ui'; import cn from 'classnames'; - -import { LocationHelper } from 'utils/LocationHelper'; +import { LocationHelper } from 'helpers/LocationHelper'; interface TabConfig { label: string; diff --git a/grafana-plugin/src/components/Text/Text.tsx b/grafana-plugin/src/components/Text/Text.tsx index b62540b468..ba8c5ee8d8 100644 --- a/grafana-plugin/src/components/Text/Text.tsx +++ b/grafana-plugin/src/components/Text/Text.tsx @@ -2,11 +2,10 @@ import React, { FC, HTMLAttributes, ChangeEvent, useState, useCallback } from 'r import { cx } from '@emotion/css'; import { IconButton, Modal, Input, Stack, Button, useStyles2 } from '@grafana/ui'; +import { openNotification } from 'helpers/helpers'; import CopyToClipboard from 'react-copy-to-clipboard'; import { bem } from 'styles/utils.styles'; -import { openNotification } from 'utils/utils'; - import { getTextStyles } from './Text.styles'; export type TextType = 'primary' | 'secondary' | 'disabled' | 'link' | 'success' | 'warning' | 'danger'; diff --git a/grafana-plugin/src/components/TextEllipsisTooltip/TextEllipsisTooltip.tsx b/grafana-plugin/src/components/TextEllipsisTooltip/TextEllipsisTooltip.tsx index 8aaae76df0..bb70c8574b 100644 --- a/grafana-plugin/src/components/TextEllipsisTooltip/TextEllipsisTooltip.tsx +++ b/grafana-plugin/src/components/TextEllipsisTooltip/TextEllipsisTooltip.tsx @@ -2,9 +2,9 @@ import React, { ReactElement, useEffect, useRef, useState } from 'react'; import { Tooltip } from '@grafana/ui'; import cn from 'classnames/bind'; +import { TEXT_ELLIPSIS_CLASS } from 'helpers/consts'; import styles from 'assets/style/utils.css'; -import { TEXT_ELLIPSIS_CLASS } from 'utils/consts'; const cx = cn.bind(styles); diff --git a/grafana-plugin/src/components/TooltipBadge/TooltipBadge.tsx b/grafana-plugin/src/components/TooltipBadge/TooltipBadge.tsx index 9678d17da7..b3d85a06dc 100644 --- a/grafana-plugin/src/components/TooltipBadge/TooltipBadge.tsx +++ b/grafana-plugin/src/components/TooltipBadge/TooltipBadge.tsx @@ -2,10 +2,10 @@ import React, { FC } from 'react'; import { cx } from '@emotion/css'; import { Icon, Tooltip, IconName, Stack, useStyles2 } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; import { bem } from 'styles/utils.styles'; import { Text, TextType } from 'components/Text/Text'; -import { StackSize } from 'utils/consts'; import { getTooltipBadgeStyles } from './TooltipBadge.styles'; diff --git a/grafana-plugin/src/components/Unauthorized/Unauthorized.tsx b/grafana-plugin/src/components/Unauthorized/Unauthorized.tsx index 31929e6564..83295b37c4 100644 --- a/grafana-plugin/src/components/Unauthorized/Unauthorized.tsx +++ b/grafana-plugin/src/components/Unauthorized/Unauthorized.tsx @@ -4,10 +4,10 @@ import { css } from '@emotion/css'; import { GrafanaTheme2, OrgRole } from '@grafana/data'; import { Stack, useStyles2 } from '@grafana/ui'; import { contextSrv } from 'grafana/app/core/core'; +import { UserAction } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; import { Text } from 'components/Text/Text'; -import { UserAction } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; type Props = { requiredUserAction: UserAction; diff --git a/grafana-plugin/src/components/UserGroups/UserGroups.tsx b/grafana-plugin/src/components/UserGroups/UserGroups.tsx index 5478b956fe..0f35269b63 100644 --- a/grafana-plugin/src/components/UserGroups/UserGroups.tsx +++ b/grafana-plugin/src/components/UserGroups/UserGroups.tsx @@ -3,13 +3,13 @@ import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { cx } from '@emotion/css'; import { Stack, IconButton, useStyles2 } from '@grafana/ui'; import { arrayMoveImmutable } from 'array-move'; +import { UserActions } from 'helpers/authorization/authorization'; import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc'; import { bem } from 'styles/utils.styles'; import { Text } from 'components/Text/Text'; import { RemoteSelect } from 'containers/RemoteSelect/RemoteSelect'; import { ApiSchemas } from 'network/oncall-api/api.types'; -import { UserActions } from 'utils/authorization/authorization'; import { fromPlainArray, toPlainArray } from './UserGroups.helpers'; import { getUserGroupStyles } from './UserGroups.styles'; diff --git a/grafana-plugin/src/components/UsersFilters/UsersFilters.tsx b/grafana-plugin/src/components/UsersFilters/UsersFilters.tsx index f6725b6ff0..8b11021c7d 100644 --- a/grafana-plugin/src/components/UsersFilters/UsersFilters.tsx +++ b/grafana-plugin/src/components/UsersFilters/UsersFilters.tsx @@ -3,8 +3,7 @@ import React, { ChangeEvent, useCallback, useEffect, useRef } from 'react'; import { css, cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Icon, Input, useStyles2 } from '@grafana/ui'; - -import { useDebouncedCallback } from 'utils/hooks'; +import { useDebouncedCallback } from 'helpers/hooks'; interface UsersFiltersProps { value: any; diff --git a/grafana-plugin/src/components/Webhooks/WebhookLastEventDetails.tsx b/grafana-plugin/src/components/Webhooks/WebhookLastEventDetails.tsx index 0606becb5e..a908b622a6 100644 --- a/grafana-plugin/src/components/Webhooks/WebhookLastEventDetails.tsx +++ b/grafana-plugin/src/components/Webhooks/WebhookLastEventDetails.tsx @@ -4,13 +4,13 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Stack, Badge, useStyles2, useTheme2 } from '@grafana/ui'; import dayjs from 'dayjs'; +import { StackSize } from 'helpers/consts'; import { SourceCode } from 'components/SourceCode/SourceCode'; import { Tabs } from 'components/Tabs/Tabs'; import { Text } from 'components/Text/Text'; import { getTzOffsetString } from 'models/timezone/timezone.helpers'; import { ApiSchemas } from 'network/oncall-api/api.types'; -import { StackSize } from 'utils/consts'; import { WebhookStatusCodeBadge } from './WebhookStatusCodeBadge'; diff --git a/grafana-plugin/src/containers/AddResponders/AddResponders.tsx b/grafana-plugin/src/containers/AddResponders/AddResponders.tsx index f72b0cd765..52aca97b88 100644 --- a/grafana-plugin/src/containers/AddResponders/AddResponders.tsx +++ b/grafana-plugin/src/containers/AddResponders/AddResponders.tsx @@ -3,6 +3,8 @@ import React, { useState, useCallback, useMemo } from 'react'; import { SelectableValue } from '@grafana/data'; import { Button, Modal, Alert, Stack, Icon, useStyles2 } from '@grafana/ui'; import dayjs from 'dayjs'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { Block } from 'components/GBlock/Block'; @@ -11,8 +13,6 @@ import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/W import { UserHelper } from 'models/user/user.helpers'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; import { getAddRespondersStyles } from './AddResponders.styles'; import { NotificationPolicyValue, UserResponder as UserResponderType } from './AddResponders.types'; diff --git a/grafana-plugin/src/containers/AddResponders/parts/AddRespondersPopup/AddRespondersPopup.tsx b/grafana-plugin/src/containers/AddResponders/parts/AddRespondersPopup/AddRespondersPopup.tsx index da2fb3d963..6a7f65a543 100644 --- a/grafana-plugin/src/containers/AddResponders/parts/AddRespondersPopup/AddRespondersPopup.tsx +++ b/grafana-plugin/src/containers/AddResponders/parts/AddRespondersPopup/AddRespondersPopup.tsx @@ -2,6 +2,8 @@ import React, { useState, useCallback, useEffect, useRef, FC } from 'react'; import { Alert, Icon, Input, LoadingPlaceholder, RadioButtonGroup, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; +import { useDebouncedCallback, useOnClickOutside } from 'helpers/hooks'; import { observer } from 'mobx-react'; import { ColumnsType } from 'rc-table/lib/interface'; @@ -12,8 +14,6 @@ import { GrafanaTeam } from 'models/grafana_team/grafana_team.types'; import { UserHelper } from 'models/user/user.helpers'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; -import { useDebouncedCallback, useOnClickOutside } from 'utils/hooks'; import styles from './AddRespondersPopup.module.scss'; diff --git a/grafana-plugin/src/containers/AlertRules/parts/connectors/MSTeamsConnector.tsx b/grafana-plugin/src/containers/AlertRules/parts/connectors/MSTeamsConnector.tsx index c4ff554807..401caf6217 100644 --- a/grafana-plugin/src/containers/AlertRules/parts/connectors/MSTeamsConnector.tsx +++ b/grafana-plugin/src/containers/AlertRules/parts/connectors/MSTeamsConnector.tsx @@ -2,6 +2,8 @@ import React, { useCallback } from 'react'; import { InlineSwitch, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { GSelect } from 'containers/GSelect/GSelect'; @@ -9,8 +11,6 @@ import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/W import { ChannelFilter } from 'models/channel_filter/channel_filter.types'; import { MSTeamsChannel } from 'models/msteams_channel/msteams_channel.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; import styles from 'containers/AlertRules/parts/connectors/Connectors.module.css'; diff --git a/grafana-plugin/src/containers/AlertRules/parts/connectors/SlackConnector.tsx b/grafana-plugin/src/containers/AlertRules/parts/connectors/SlackConnector.tsx index f887cb183b..9eb037a926 100644 --- a/grafana-plugin/src/containers/AlertRules/parts/connectors/SlackConnector.tsx +++ b/grafana-plugin/src/containers/AlertRules/parts/connectors/SlackConnector.tsx @@ -2,6 +2,8 @@ import React, { useCallback } from 'react'; import { InlineSwitch, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { GSelect } from 'containers/GSelect/GSelect'; @@ -10,8 +12,6 @@ import { ChannelFilter } from 'models/channel_filter/channel_filter.types'; import { PRIVATE_CHANNEL_NAME } from 'models/slack_channel/slack_channel.config'; import { SlackChannel } from 'models/slack_channel/slack_channel.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; import styles from './Connectors.module.css'; diff --git a/grafana-plugin/src/containers/AlertRules/parts/connectors/TelegramConnector.tsx b/grafana-plugin/src/containers/AlertRules/parts/connectors/TelegramConnector.tsx index b53cdba288..5ed5b18abd 100644 --- a/grafana-plugin/src/containers/AlertRules/parts/connectors/TelegramConnector.tsx +++ b/grafana-plugin/src/containers/AlertRules/parts/connectors/TelegramConnector.tsx @@ -2,6 +2,8 @@ import React, { useCallback } from 'react'; import { InlineSwitch, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { GSelect } from 'containers/GSelect/GSelect'; @@ -9,8 +11,6 @@ import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/W import { ChannelFilter } from 'models/channel_filter/channel_filter.types'; import { TelegramChannel } from 'models/telegram_channel/telegram_channel.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; import styles from './Connectors.module.css'; diff --git a/grafana-plugin/src/containers/Alerts/Alerts.tsx b/grafana-plugin/src/containers/Alerts/Alerts.tsx index 5b6ccde89b..3f1a179abe 100644 --- a/grafana-plugin/src/containers/Alerts/Alerts.tsx +++ b/grafana-plugin/src/containers/Alerts/Alerts.tsx @@ -3,6 +3,10 @@ import React, { useCallback, useEffect, useState } from 'react'; import { Alert } from '@grafana/ui'; import cn from 'classnames/bind'; import { sanitize } from 'dompurify'; +import { LocationHelper } from 'helpers/LocationHelper'; +import { isUserActionAllowed, UserActions } from 'helpers/authorization/authorization'; +import { useForceUpdate, useQueryParams } from 'helpers/hooks'; +import { getItem, setItem } from 'helpers/localStorage'; import { observer } from 'mobx-react'; import { PluginLink } from 'components/PluginLink/PluginLink'; @@ -13,10 +17,6 @@ import { UserHelper } from 'models/user/user.helpers'; import { isTopNavbar } from 'plugin/GrafanaPluginRootPage.helpers'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; -import { LocationHelper } from 'utils/LocationHelper'; -import { isUserActionAllowed, UserActions } from 'utils/authorization/authorization'; -import { useForceUpdate, useQueryParams } from 'utils/hooks'; -import { getItem, setItem } from 'utils/localStorage'; import styles from './Alerts.module.scss'; diff --git a/grafana-plugin/src/containers/ApiTokenSettings/ApiTokenForm.tsx b/grafana-plugin/src/containers/ApiTokenSettings/ApiTokenForm.tsx index c5d32a7d85..09b6352dda 100644 --- a/grafana-plugin/src/containers/ApiTokenSettings/ApiTokenForm.tsx +++ b/grafana-plugin/src/containers/ApiTokenSettings/ApiTokenForm.tsx @@ -2,6 +2,7 @@ import React, { HTMLAttributes, useState } from 'react'; import { Button, Field, Input, Label, Modal, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { openErrorNotification, openNotification } from 'helpers/helpers'; import { get } from 'lodash-es'; import { observer } from 'mobx-react'; import CopyToClipboard from 'react-copy-to-clipboard'; @@ -10,7 +11,6 @@ import { Controller, FormProvider, useForm } from 'react-hook-form'; import { RenderConditionally } from 'components/RenderConditionally/RenderConditionally'; import { SourceCode } from 'components/SourceCode/SourceCode'; import { useStore } from 'state/useStore'; -import { openErrorNotification, openNotification } from 'utils/utils'; import styles from './ApiTokenForm.module.css'; diff --git a/grafana-plugin/src/containers/ApiTokenSettings/ApiTokenSettings.tsx b/grafana-plugin/src/containers/ApiTokenSettings/ApiTokenSettings.tsx index e2dbd9f325..f91b536363 100644 --- a/grafana-plugin/src/containers/ApiTokenSettings/ApiTokenSettings.tsx +++ b/grafana-plugin/src/containers/ApiTokenSettings/ApiTokenSettings.tsx @@ -2,6 +2,11 @@ import React from 'react'; import { Button, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { + generateMissingPermissionMessage, + isUserActionAllowed, + UserActions, +} from 'helpers/authorization/authorization'; import { observer } from 'mobx-react'; import moment from 'moment-timezone'; @@ -12,7 +17,6 @@ import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/W import { ApiToken } from 'models/api_token/api_token.types'; import { WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { generateMissingPermissionMessage, isUserActionAllowed, UserActions } from 'utils/authorization/authorization'; import { ApiTokenForm } from './ApiTokenForm'; diff --git a/grafana-plugin/src/containers/AttachIncidentForm/AttachIncidentForm.tsx b/grafana-plugin/src/containers/AttachIncidentForm/AttachIncidentForm.tsx index 37c65138e0..3901922334 100644 --- a/grafana-plugin/src/containers/AttachIncidentForm/AttachIncidentForm.tsx +++ b/grafana-plugin/src/containers/AttachIncidentForm/AttachIncidentForm.tsx @@ -3,6 +3,7 @@ import React, { useCallback, useState } from 'react'; import { SelectableValue } from '@grafana/data'; import { Button, Field, Icon, Modal, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; import { observer } from 'mobx-react'; import moment from 'moment-timezone'; @@ -12,7 +13,6 @@ import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/W import { AlertGroupHelper } from 'models/alertgroup/alertgroup.helpers'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; import styles from './AttachIncidentForm.module.css'; diff --git a/grafana-plugin/src/containers/ColumnsSelector/ColumnsSelector.tsx b/grafana-plugin/src/containers/ColumnsSelector/ColumnsSelector.tsx index 3758f19725..848ad81045 100644 --- a/grafana-plugin/src/containers/ColumnsSelector/ColumnsSelector.tsx +++ b/grafana-plugin/src/containers/ColumnsSelector/ColumnsSelector.tsx @@ -19,6 +19,9 @@ import { } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import { Button, Checkbox, Icon, IconButton, LoadingPlaceholder, Tooltip, useStyles2 } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { openErrorNotification } from 'helpers/helpers'; +import { useIsLoading } from 'helpers/hooks'; import { observer } from 'mobx-react'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; @@ -28,9 +31,6 @@ import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/W import { AlertGroupColumn, AlertGroupColumnType } from 'models/alertgroup/alertgroup.types'; import { ActionKey } from 'models/loader/action-keys'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { useIsLoading } from 'utils/hooks'; -import { openErrorNotification } from 'utils/utils'; import { getColumnsSelectorStyles } from './ColumnsSelector.styles'; diff --git a/grafana-plugin/src/containers/ColumnsSelectorWrapper/ColumnsModal.tsx b/grafana-plugin/src/containers/ColumnsSelectorWrapper/ColumnsModal.tsx index 25e4809c9c..8648d6c94d 100644 --- a/grafana-plugin/src/containers/ColumnsSelectorWrapper/ColumnsModal.tsx +++ b/grafana-plugin/src/containers/ColumnsSelectorWrapper/ColumnsModal.tsx @@ -3,6 +3,11 @@ import React, { useMemo, useState } from 'react'; import { LabelTag } from '@grafana/labels'; import { Button, Checkbox, IconButton, Input, LoadingPlaceholder, Modal, Stack, useStyles2 } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { PROCESSING_REQUEST_ERROR, StackSize } from 'helpers/consts'; +import { WrapWithGlobalNotification } from 'helpers/decorators'; +import { pluralize } from 'helpers/helpers'; +import { useDebouncedCallback, useIsLoading } from 'helpers/hooks'; import { observer } from 'mobx-react'; import styles from 'assets/style/utils.css'; @@ -15,11 +20,6 @@ import { ActionKey } from 'models/loader/action-keys'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { components } from 'network/oncall-api/autogenerated-api.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { PROCESSING_REQUEST_ERROR, StackSize } from 'utils/consts'; -import { WrapWithGlobalNotification } from 'utils/decorators'; -import { useDebouncedCallback, useIsLoading } from 'utils/hooks'; -import { pluralize } from 'utils/utils'; import { getColumnsSelectorWrapperStyles } from './ColumnsSelectorWrapper.styles'; diff --git a/grafana-plugin/src/containers/ColumnsSelectorWrapper/ColumnsSelectorWrapper.tsx b/grafana-plugin/src/containers/ColumnsSelectorWrapper/ColumnsSelectorWrapper.tsx index 84b2d7b083..c7a9bada87 100644 --- a/grafana-plugin/src/containers/ColumnsSelectorWrapper/ColumnsSelectorWrapper.tsx +++ b/grafana-plugin/src/containers/ColumnsSelectorWrapper/ColumnsSelectorWrapper.tsx @@ -1,6 +1,10 @@ import React, { useEffect, useRef, useState } from 'react'; import { useStyles2, Button, Icon, LoadingPlaceholder, Modal, Stack } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { PROCESSING_REQUEST_ERROR, StackSize } from 'helpers/consts'; +import { WrapAutoLoadingState, WrapWithGlobalNotification } from 'helpers/decorators'; +import { useIsLoading } from 'helpers/hooks'; import { observer } from 'mobx-react'; import { Text } from 'components/Text/Text'; @@ -12,10 +16,6 @@ import { AlertGroupColumn } from 'models/alertgroup/alertgroup.types'; import { ActionKey } from 'models/loader/action-keys'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { PROCESSING_REQUEST_ERROR, StackSize } from 'utils/consts'; -import { WrapAutoLoadingState, WrapWithGlobalNotification } from 'utils/decorators'; -import { useIsLoading } from 'utils/hooks'; import { ColumnsModal } from './ColumnsModal'; diff --git a/grafana-plugin/src/containers/DefaultPageLayout/DefaultPageLayout.tsx b/grafana-plugin/src/containers/DefaultPageLayout/DefaultPageLayout.tsx index 3b36716210..3a13a3f597 100644 --- a/grafana-plugin/src/containers/DefaultPageLayout/DefaultPageLayout.tsx +++ b/grafana-plugin/src/containers/DefaultPageLayout/DefaultPageLayout.tsx @@ -2,9 +2,9 @@ import React, { FC, ReactElement } from 'react'; import { NavModelItem } from '@grafana/data'; import { PluginPage } from 'PluginPage'; +import { AppRootProps } from 'app-types'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; -import { AppRootProps } from 'types'; import { Alerts } from 'containers/Alerts/Alerts'; import { isTopNavbar } from 'plugin/GrafanaPluginRootPage.helpers'; diff --git a/grafana-plugin/src/containers/EditRegexpRouteTemplateModal/EditRegexpRouteTemplateModal.tsx b/grafana-plugin/src/containers/EditRegexpRouteTemplateModal/EditRegexpRouteTemplateModal.tsx index 2b3a4df21f..e16ce0f036 100644 --- a/grafana-plugin/src/containers/EditRegexpRouteTemplateModal/EditRegexpRouteTemplateModal.tsx +++ b/grafana-plugin/src/containers/EditRegexpRouteTemplateModal/EditRegexpRouteTemplateModal.tsx @@ -2,6 +2,8 @@ import React, { useState, useCallback } from 'react'; import { Stack, Modal, Tooltip, Icon, Button } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; +import { openErrorNotification } from 'helpers/helpers'; import { debounce } from 'lodash-es'; import { observer } from 'mobx-react'; @@ -13,8 +15,6 @@ import { AlertReceiveChannelHelper } from 'models/alert_receive_channel/alert_re import { ChannelFilter } from 'models/channel_filter/channel_filter.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; -import { openErrorNotification } from 'utils/utils'; import styles from './EditRegexpRouteTemplateModal.module.css'; diff --git a/grafana-plugin/src/containers/EscalationChainCard/EscalationChainCard.tsx b/grafana-plugin/src/containers/EscalationChainCard/EscalationChainCard.tsx index 855bfcb4ec..4dca5d915e 100644 --- a/grafana-plugin/src/containers/EscalationChainCard/EscalationChainCard.tsx +++ b/grafana-plugin/src/containers/EscalationChainCard/EscalationChainCard.tsx @@ -2,13 +2,13 @@ import React from 'react'; import { Stack, Badge } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { Text } from 'components/Text/Text'; import { TeamName } from 'containers/TeamName/TeamName'; import { EscalationChain } from 'models/escalation_chain/escalation_chain.types'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; import styles from './EscalationChainCard.module.css'; diff --git a/grafana-plugin/src/containers/EscalationChainForm/EscalationChainForm.tsx b/grafana-plugin/src/containers/EscalationChainForm/EscalationChainForm.tsx index 402eba69a3..b72dee6764 100644 --- a/grafana-plugin/src/containers/EscalationChainForm/EscalationChainForm.tsx +++ b/grafana-plugin/src/containers/EscalationChainForm/EscalationChainForm.tsx @@ -2,6 +2,7 @@ import React, { FC } from 'react'; import { Button, Field, Input, Modal, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { openWarningNotification } from 'helpers/helpers'; import { observer } from 'mobx-react'; import { Controller, FormProvider, useForm } from 'react-hook-form'; @@ -9,7 +10,6 @@ import { GSelect } from 'containers/GSelect/GSelect'; import { EscalationChain } from 'models/escalation_chain/escalation_chain.types'; import { GrafanaTeam } from 'models/grafana_team/grafana_team.types'; import { useStore } from 'state/useStore'; -import { openWarningNotification } from 'utils/utils'; import styles from 'containers/EscalationChainForm/EscalationChainForm.module.css'; diff --git a/grafana-plugin/src/containers/EscalationChainSteps/EscalationChainSteps.tsx b/grafana-plugin/src/containers/EscalationChainSteps/EscalationChainSteps.tsx index f3e324262a..b657796ef4 100644 --- a/grafana-plugin/src/containers/EscalationChainSteps/EscalationChainSteps.tsx +++ b/grafana-plugin/src/containers/EscalationChainSteps/EscalationChainSteps.tsx @@ -4,6 +4,7 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { LoadingPlaceholder, Select, useStyles2, useTheme2 } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; import { observer } from 'mobx-react'; import { getLabelBackgroundTextColorObject } from 'styles/utils.styles'; @@ -14,7 +15,6 @@ import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/W import { EscalationChain } from 'models/escalation_chain/escalation_chain.types'; import { EscalationPolicyOption } from 'models/escalation_policy/escalation_policy.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; import styles from './EscalationChainSteps.module.css'; diff --git a/grafana-plugin/src/containers/GSelect/GSelect.tsx b/grafana-plugin/src/containers/GSelect/GSelect.tsx index fe18d5dacd..3d87aaf21a 100644 --- a/grafana-plugin/src/containers/GSelect/GSelect.tsx +++ b/grafana-plugin/src/containers/GSelect/GSelect.tsx @@ -3,11 +3,10 @@ import React, { ReactElement, useCallback, useEffect } from 'react'; import { SelectableValue } from '@grafana/data'; import { AsyncMultiSelect, AsyncSelect } from '@grafana/ui'; import cn from 'classnames/bind'; +import { useDebouncedCallback } from 'helpers/hooks'; import { get, isNil } from 'lodash-es'; import { observer } from 'mobx-react'; -import { useDebouncedCallback } from 'utils/hooks'; - import styles from './GSelect.module.scss'; const cx = cn.bind(styles); diff --git a/grafana-plugin/src/containers/GrafanaTeamSelect/GrafanaTeamSelect.tsx b/grafana-plugin/src/containers/GrafanaTeamSelect/GrafanaTeamSelect.tsx index 6ac090f7a0..d3f96badea 100644 --- a/grafana-plugin/src/containers/GrafanaTeamSelect/GrafanaTeamSelect.tsx +++ b/grafana-plugin/src/containers/GrafanaTeamSelect/GrafanaTeamSelect.tsx @@ -2,13 +2,13 @@ import React, { useCallback, useState } from 'react'; import { Button, Icon, Label, Modal, Tooltip, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; import { observer } from 'mobx-react'; import { GSelect } from 'containers/GSelect/GSelect'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; import { GrafanaTeam } from 'models/grafana_team/grafana_team.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; import styles from './GrafanaTeamSelect.module.scss'; diff --git a/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx b/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx index 6908310617..be5f9e5920 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx +++ b/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx @@ -2,6 +2,7 @@ import React, { useMemo, useState } from 'react'; import { ConfirmModal, Icon, IconName, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { IntegrationBlock } from 'components/Integrations/IntegrationBlock'; @@ -15,7 +16,6 @@ import { ApiSchemas } from 'network/oncall-api/api.types'; import { CommonIntegrationHelper } from 'pages/integration/CommonIntegration.helper'; import { IntegrationHelper } from 'pages/integration/Integration.helper'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; const cx = cn.bind(styles); diff --git a/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx b/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx index 83140b9e0e..5785c763af 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx +++ b/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx @@ -13,6 +13,9 @@ import { Alert, } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; +import { openNotification } from 'helpers/helpers'; import { observer } from 'mobx-react'; import CopyToClipboard from 'react-copy-to-clipboard'; @@ -42,9 +45,6 @@ import { IntegrationHelper } from 'pages/integration/Integration.helper'; import { MONACO_INPUT_HEIGHT_SMALL } from 'pages/integration/IntegrationCommon.config'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; -import { openNotification } from 'utils/utils'; const cx = cn.bind(styles); diff --git a/grafana-plugin/src/containers/IntegrationContainers/IntegrationHeartbeatForm/IntegrationHeartbeatForm.tsx b/grafana-plugin/src/containers/IntegrationContainers/IntegrationHeartbeatForm/IntegrationHeartbeatForm.tsx index 7602ea991f..3dff84fea5 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/IntegrationHeartbeatForm/IntegrationHeartbeatForm.tsx +++ b/grafana-plugin/src/containers/IntegrationContainers/IntegrationHeartbeatForm/IntegrationHeartbeatForm.tsx @@ -3,6 +3,9 @@ import React, { ReactElement, useEffect, useState } from 'react'; import { SelectableValue } from '@grafana/data'; import { Button, Drawer, Field, Icon, Select, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; +import { openNotification } from 'helpers/helpers'; import { observer } from 'mobx-react'; import { IntegrationInputField } from 'components/IntegrationInputField/IntegrationInputField'; @@ -13,9 +16,6 @@ import { ApiSchemas } from 'network/oncall-api/api.types'; import { SelectOption } from 'state/types'; import { useStore } from 'state/useStore'; import { withMobXProviderContext } from 'state/withStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; -import { openNotification } from 'utils/utils'; import styles from './IntegrationHeartbeatForm.module.scss'; diff --git a/grafana-plugin/src/containers/IntegrationContainers/IntegrationTemplatesList.tsx b/grafana-plugin/src/containers/IntegrationContainers/IntegrationTemplatesList.tsx index 85b68d9335..71015714eb 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/IntegrationTemplatesList.tsx +++ b/grafana-plugin/src/containers/IntegrationContainers/IntegrationTemplatesList.tsx @@ -2,6 +2,7 @@ import React, { useState, useCallback } from 'react'; import { InlineSwitch, Tooltip } from '@grafana/ui'; import cn from 'classnames/bind'; +import { openErrorNotification, openNotification } from 'helpers/helpers'; import { observer } from 'mobx-react'; import { IntegrationBlockItem } from 'components/Integrations/IntegrationBlockItem'; @@ -16,7 +17,6 @@ import { IntegrationHelper } from 'pages/integration/Integration.helper'; import styles from 'pages/integration/Integration.module.scss'; import { MONACO_INPUT_HEIGHT_TALL } from 'pages/integration/IntegrationCommon.config'; import { useStore } from 'state/useStore'; -import { openErrorNotification, openNotification } from 'utils/utils'; const cx = cn.bind(styles); diff --git a/grafana-plugin/src/containers/IntegrationForm/IntegrationForm.tsx b/grafana-plugin/src/containers/IntegrationForm/IntegrationForm.tsx index e61eb91acf..c30f538f0f 100644 --- a/grafana-plugin/src/containers/IntegrationForm/IntegrationForm.tsx +++ b/grafana-plugin/src/containers/IntegrationForm/IntegrationForm.tsx @@ -16,6 +16,17 @@ import { Stack, useStyles2, } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { + PLUGIN_ROOT, + generateAssignToTeamInputDescription, + DOCS_ROOT, + INTEGRATION_SERVICENOW, + StackSize, +} from 'helpers/consts'; +import { useIsLoading } from 'helpers/hooks'; +import { validateURL } from 'helpers/string'; +import { OmitReadonlyMembers } from 'helpers/types'; import { observer } from 'mobx-react'; import { Controller, useForm, useFormContext, FormProvider } from 'react-hook-form'; import { useNavigate } from 'react-router-dom-v5-compat'; @@ -35,17 +46,6 @@ import { ApiSchemas } from 'network/oncall-api/api.types'; import { IntegrationHelper, getIsBidirectionalIntegration } from 'pages/integration/Integration.helper'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { - PLUGIN_ROOT, - generateAssignToTeamInputDescription, - DOCS_ROOT, - INTEGRATION_SERVICENOW, - StackSize, -} from 'utils/consts'; -import { useIsLoading } from 'utils/hooks'; -import { validateURL } from 'utils/string'; -import { OmitReadonlyMembers } from 'utils/types'; import { prepareForEdit } from './IntegrationForm.helpers'; import { getIntegrationFormStyles } from './IntegrationForm.styles'; diff --git a/grafana-plugin/src/containers/IntegrationForm/IntegrationFormContainer.tsx b/grafana-plugin/src/containers/IntegrationForm/IntegrationFormContainer.tsx index 8ec381f452..be17c17e14 100644 --- a/grafana-plugin/src/containers/IntegrationForm/IntegrationFormContainer.tsx +++ b/grafana-plugin/src/containers/IntegrationForm/IntegrationFormContainer.tsx @@ -2,6 +2,7 @@ import React, { useState, ChangeEvent } from 'react'; import { Drawer, Stack, Input, Tag, EmptySearchResult } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { Block } from 'components/GBlock/Block'; @@ -9,7 +10,6 @@ import { IntegrationLogo } from 'components/IntegrationLogo/IntegrationLogo'; import { Text } from 'components/Text/Text'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; import { IntegrationForm } from './IntegrationForm'; import styles from './IntegrationFormContainer.module.scss'; diff --git a/grafana-plugin/src/containers/IntegrationLabelsForm/IntegrationLabelsForm.tsx b/grafana-plugin/src/containers/IntegrationLabelsForm/IntegrationLabelsForm.tsx index 8e3ea458e2..6875186706 100644 --- a/grafana-plugin/src/containers/IntegrationLabelsForm/IntegrationLabelsForm.tsx +++ b/grafana-plugin/src/containers/IntegrationLabelsForm/IntegrationLabelsForm.tsx @@ -3,6 +3,8 @@ import React, { ChangeEvent, useState } from 'react'; import { ServiceLabels } from '@grafana/labels'; import { Alert, Button, Drawer, Dropdown, InlineSwitch, Input, Menu, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { DOCS_ROOT, GENERIC_ERROR, StackSize } from 'helpers/consts'; +import { openErrorNotification } from 'helpers/helpers'; import { observer } from 'mobx-react'; import { Collapse } from 'components/Collapse/Collapse'; @@ -16,8 +18,6 @@ import { LabelsErrors } from 'models/label/label.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { LabelTemplateOptions } from 'pages/integration/IntegrationCommon.config'; import { useStore } from 'state/useStore'; -import { DOCS_ROOT, GENERIC_ERROR, StackSize } from 'utils/consts'; -import { openErrorNotification } from 'utils/utils'; import { getIsAddBtnDisabled, getIsTooManyLabelsWarningVisible } from './IntegrationLabelsForm.helpers'; diff --git a/grafana-plugin/src/containers/IntegrationTemplate/IntegrationTemplate.tsx b/grafana-plugin/src/containers/IntegrationTemplate/IntegrationTemplate.tsx index 6481b64efc..0cce3b48f4 100644 --- a/grafana-plugin/src/containers/IntegrationTemplate/IntegrationTemplate.tsx +++ b/grafana-plugin/src/containers/IntegrationTemplate/IntegrationTemplate.tsx @@ -2,6 +2,8 @@ import React, { useCallback, useState, useEffect } from 'react'; import { Button, Drawer, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { LocationHelper } from 'helpers/LocationHelper'; +import { UserActions } from 'helpers/authorization/authorization'; import { debounce } from 'lodash-es'; import { observer } from 'mobx-react'; @@ -26,8 +28,6 @@ import { ChannelFilter } from 'models/channel_filter/channel_filter.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { IntegrationTemplateOptions, LabelTemplateOptions } from 'pages/integration/IntegrationCommon.config'; import { useStore } from 'state/useStore'; -import { LocationHelper } from 'utils/LocationHelper'; -import { UserActions } from 'utils/authorization/authorization'; import styles from './IntegrationTemplate.module.scss'; diff --git a/grafana-plugin/src/containers/Labels/Labels.tsx b/grafana-plugin/src/containers/Labels/Labels.tsx index 02625f461b..78555a7816 100644 --- a/grafana-plugin/src/containers/Labels/Labels.tsx +++ b/grafana-plugin/src/containers/Labels/Labels.tsx @@ -2,14 +2,14 @@ import React, { forwardRef, useImperativeHandle, useState } from 'react'; import { ServiceLabelsProps, ServiceLabels } from '@grafana/labels'; import { Field, Label } from '@grafana/ui'; +import { GENERIC_ERROR } from 'helpers/consts'; +import { openErrorNotification } from 'helpers/helpers'; import { isEmpty } from 'lodash-es'; import { observer } from 'mobx-react'; import { splitToGroups } from 'models/label/label.helpers'; import { LabelKeyValue } from 'models/label/label.types'; import { useStore } from 'state/useStore'; -import { GENERIC_ERROR } from 'utils/consts'; -import { openErrorNotification } from 'utils/utils'; export interface LabelsProps { value: LabelKeyValue[]; diff --git a/grafana-plugin/src/containers/MSTeams/MSTeamsInstructions.tsx b/grafana-plugin/src/containers/MSTeams/MSTeamsInstructions.tsx index e546a5fe8d..30d71291bf 100644 --- a/grafana-plugin/src/containers/MSTeams/MSTeamsInstructions.tsx +++ b/grafana-plugin/src/containers/MSTeams/MSTeamsInstructions.tsx @@ -2,6 +2,8 @@ import React, { FC } from 'react'; import { Button, Icon, Stack, Field, Input } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; +import { openNotification, openWarningNotification } from 'helpers/helpers'; import { observer } from 'mobx-react'; import CopyToClipboard from 'react-copy-to-clipboard'; @@ -10,8 +12,6 @@ import { PluginLink } from 'components/PluginLink/PluginLink'; import { Text } from 'components/Text/Text'; import MSTeamsLogo from 'icons/MSTeamsLogo'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; -import { openNotification, openWarningNotification } from 'utils/utils'; import styles from './MSTeamsInstructions.module.css'; diff --git a/grafana-plugin/src/containers/MSTeamsIntegrationButton/MSTeamsIntegrationButton.tsx b/grafana-plugin/src/containers/MSTeamsIntegrationButton/MSTeamsIntegrationButton.tsx index f6920b3a28..a2ecea4c5c 100644 --- a/grafana-plugin/src/containers/MSTeamsIntegrationButton/MSTeamsIntegrationButton.tsx +++ b/grafana-plugin/src/containers/MSTeamsIntegrationButton/MSTeamsIntegrationButton.tsx @@ -2,12 +2,12 @@ import React, { useCallback, useState, useEffect } from 'react'; import { Button, Modal } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; import { observer } from 'mobx-react'; import { MSTeamsInstructions } from 'containers/MSTeams/MSTeamsInstructions'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; import styles from './MSTeamsIntegrationButton.module.css'; diff --git a/grafana-plugin/src/containers/MaintenanceForm/MaintenanceForm.tsx b/grafana-plugin/src/containers/MaintenanceForm/MaintenanceForm.tsx index f1f367baec..3b009a4730 100644 --- a/grafana-plugin/src/containers/MaintenanceForm/MaintenanceForm.tsx +++ b/grafana-plugin/src/containers/MaintenanceForm/MaintenanceForm.tsx @@ -3,6 +3,8 @@ import React, { useCallback } from 'react'; import { SelectableValue } from '@grafana/data'; import { Button, Drawer, Field, Select, Stack, useStyles2 } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { openNotification, showApiError } from 'helpers/helpers'; import { observer } from 'mobx-react'; import Emoji from 'react-emoji-render'; import { Controller, FormProvider, useForm } from 'react-hook-form'; @@ -14,8 +16,6 @@ import { AlertReceiveChannelHelper } from 'models/alert_receive_channel/alert_re import { MaintenanceMode } from 'models/alert_receive_channel/alert_receive_channel.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { openNotification, showApiError } from 'utils/utils'; import styles from './MaintenanceForm.module.css'; diff --git a/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.test.tsx b/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.test.tsx index 932cc87658..124fde2a7e 100644 --- a/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.test.tsx +++ b/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.test.tsx @@ -13,8 +13,8 @@ jest.mock('plugin/GrafanaPluginRootPage.helpers', () => ({ isTopNavbar: () => false, })); -jest.mock('utils/authorization/authorization', () => ({ - ...jest.requireActual('utils/authorization/authorization'), +jest.mock('helpers/authorization/authorization', () => ({ + ...jest.requireActual('helpers/authorization/authorization'), isUserActionAllowed: jest.fn().mockReturnValue(true), })); diff --git a/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.tsx b/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.tsx index 1ab887b21e..b672f53bce 100644 --- a/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.tsx +++ b/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.tsx @@ -2,6 +2,10 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import { Button, Icon, LoadingPlaceholder, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; +import { isMobile, openErrorNotification, openNotification, openWarningNotification } from 'helpers/helpers'; +import { useInitializePlugin } from 'helpers/hooks'; import { observer } from 'mobx-react'; import qrCodeImage from 'assets/img/qr-code.png'; @@ -15,10 +19,6 @@ import { UserHelper } from 'models/user/user.helpers'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { AppFeature } from 'state/features'; import { RootStore, rootStore as store } from 'state/rootStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; -import { useInitializePlugin } from 'utils/hooks'; -import { isMobile, openErrorNotification, openNotification, openWarningNotification } from 'utils/utils'; import styles from './MobileAppConnection.module.scss'; import { DisconnectButton } from './parts/DisconnectButton/DisconnectButton'; diff --git a/grafana-plugin/src/containers/MobileAppConnection/parts/DownloadIcons/DownloadIcons.tsx b/grafana-plugin/src/containers/MobileAppConnection/parts/DownloadIcons/DownloadIcons.tsx index 076c63705b..5d39a25651 100644 --- a/grafana-plugin/src/containers/MobileAppConnection/parts/DownloadIcons/DownloadIcons.tsx +++ b/grafana-plugin/src/containers/MobileAppConnection/parts/DownloadIcons/DownloadIcons.tsx @@ -2,12 +2,12 @@ import React, { FC } from 'react'; import { Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; import AppleLogoSVG from 'assets/img/apple-logo.svg'; import PlayStoreLogoSVG from 'assets/img/play-store-logo.svg'; import { Block } from 'components/GBlock/Block'; import { Text } from 'components/Text/Text'; -import { StackSize } from 'utils/consts'; import styles from './DownloadIcons.module.scss'; diff --git a/grafana-plugin/src/containers/MobileAppConnection/parts/LinkLoginButton/LinkLoginButton.tsx b/grafana-plugin/src/containers/MobileAppConnection/parts/LinkLoginButton/LinkLoginButton.tsx index a6b5cde521..55d51554b6 100644 --- a/grafana-plugin/src/containers/MobileAppConnection/parts/LinkLoginButton/LinkLoginButton.tsx +++ b/grafana-plugin/src/containers/MobileAppConnection/parts/LinkLoginButton/LinkLoginButton.tsx @@ -1,9 +1,9 @@ import React, { FC } from 'react'; import { Button, Stack } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; import { Text } from 'components/Text/Text'; -import { StackSize } from 'utils/consts'; type Props = { baseUrl: string; diff --git a/grafana-plugin/src/containers/OutgoingWebhookForm/OutgoingWebhookForm.tsx b/grafana-plugin/src/containers/OutgoingWebhookForm/OutgoingWebhookForm.tsx index 7de7a09d2e..05db8418d9 100644 --- a/grafana-plugin/src/containers/OutgoingWebhookForm/OutgoingWebhookForm.tsx +++ b/grafana-plugin/src/containers/OutgoingWebhookForm/OutgoingWebhookForm.tsx @@ -2,6 +2,9 @@ import React, { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'r import { Button, ConfirmModal, ConfirmModalProps, Drawer, Input, Tab, TabsBar, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { PLUGIN_ROOT } from 'helpers/consts'; +import { KeyValuePair } from 'helpers/helpers'; import { observer } from 'mobx-react'; import { FormProvider, useForm, useFormContext } from 'react-hook-form'; import { useNavigate } from 'react-router-dom-v5-compat'; @@ -15,9 +18,6 @@ import { ApiSchemas } from 'network/oncall-api/api.types'; import { WebhookFormActionType } from 'pages/outgoing_webhooks/OutgoingWebhooks.types'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { PLUGIN_ROOT } from 'utils/consts'; -import { KeyValuePair } from 'utils/utils'; import { TemplateParams, WebhookFormFieldName } from './OutgoingWebhookForm.types'; import { OutgoingWebhookFormFields } from './OutgoingWebhookFormFields'; diff --git a/grafana-plugin/src/containers/OutgoingWebhookForm/OutgoingWebhookFormFields.tsx b/grafana-plugin/src/containers/OutgoingWebhookForm/OutgoingWebhookFormFields.tsx index 0250c06bf7..e7a4ac9711 100644 --- a/grafana-plugin/src/containers/OutgoingWebhookForm/OutgoingWebhookFormFields.tsx +++ b/grafana-plugin/src/containers/OutgoingWebhookForm/OutgoingWebhookFormFields.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { SelectableValue } from '@grafana/data'; import { Button, Field, Input, RadioButtonList, Select, Switch, useStyles2 } from '@grafana/ui'; +import { generateAssignToTeamInputDescription } from 'helpers/consts'; import { observer } from 'mobx-react'; import Emoji from 'react-emoji-render'; import { Controller, useFormContext } from 'react-hook-form'; @@ -20,7 +21,6 @@ import { } from 'models/outgoing_webhook/outgoing_webhook.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { generateAssignToTeamInputDescription } from 'utils/consts'; import { getStyles } from './OutgoingWebhookForm.styles'; import { TemplateParams, WebhookFormFieldName } from './OutgoingWebhookForm.types'; diff --git a/grafana-plugin/src/containers/OutgoingWebhookForm/WebhookPresetBlocks.tsx b/grafana-plugin/src/containers/OutgoingWebhookForm/WebhookPresetBlocks.tsx index 82a6592dac..fa8fd2e205 100644 --- a/grafana-plugin/src/containers/OutgoingWebhookForm/WebhookPresetBlocks.tsx +++ b/grafana-plugin/src/containers/OutgoingWebhookForm/WebhookPresetBlocks.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { EmptySearchResult, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { Block } from 'components/GBlock/Block'; @@ -11,7 +12,6 @@ import { Text } from 'components/Text/Text'; import { getWebhookPresetIcons } from 'containers/OutgoingWebhookForm/WebhookPresetIcons.config'; import { OutgoingWebhookPreset } from 'models/outgoing_webhook/outgoing_webhook.types'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; import styles from 'containers/OutgoingWebhookForm/OutgoingWebhookForm.module.css'; diff --git a/grafana-plugin/src/containers/OutgoingWebhookStatus/OutgoingWebhookStatus.tsx b/grafana-plugin/src/containers/OutgoingWebhookStatus/OutgoingWebhookStatus.tsx index d62156e1ed..ee7dbf18ba 100644 --- a/grafana-plugin/src/containers/OutgoingWebhookStatus/OutgoingWebhookStatus.tsx +++ b/grafana-plugin/src/containers/OutgoingWebhookStatus/OutgoingWebhookStatus.tsx @@ -2,12 +2,12 @@ import React from 'react'; import { Button, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { useCommonStyles } from 'helpers/hooks'; import { observer } from 'mobx-react'; import { WebhookLastEventDetails } from 'components/Webhooks/WebhookLastEventDetails'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { useCommonStyles } from 'utils/hooks'; import styles from 'containers/OutgoingWebhookForm/OutgoingWebhookForm.module.css'; diff --git a/grafana-plugin/src/containers/PersonalNotificationSettings/PersonalNotificationSettings.tsx b/grafana-plugin/src/containers/PersonalNotificationSettings/PersonalNotificationSettings.tsx index 0758740f1e..58b408e786 100644 --- a/grafana-plugin/src/containers/PersonalNotificationSettings/PersonalNotificationSettings.tsx +++ b/grafana-plugin/src/containers/PersonalNotificationSettings/PersonalNotificationSettings.tsx @@ -2,6 +2,7 @@ import React, { useCallback } from 'react'; import { Button, Icon, LoadingPlaceholder, Stack, Tooltip } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; import { get } from 'lodash-es'; import { observer } from 'mobx-react'; @@ -14,7 +15,6 @@ import { NotificationPolicyType } from 'models/notification_policy/notification_ import { ApiSchemas } from 'network/oncall-api/api.types'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; import { getColor } from './PersonalNotificationSettings.helpers'; import img from './img/default-step.png'; diff --git a/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx b/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx index a421a13799..722a16e903 100644 --- a/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx +++ b/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx @@ -3,10 +3,21 @@ import React, { useEffect, useState } from 'react'; import { css } from '@emotion/css'; import { GrafanaTheme2, PluginConfigPageProps, PluginMeta } from '@grafana/data'; import { Alert, Field, Input, LoadingPlaceholder, useStyles2, Stack } from '@grafana/ui'; +import { OnCallPluginMetaJSONData } from 'app-types'; +import { + DEFAULT_PAGE, + DOCS_ONCALL_OSS_INSTALL, + DOCS_SERVICE_ACCOUNTS, + PLUGIN_CONFIG, + PLUGIN_ROOT, + REQUEST_HELP_URL, +} from 'helpers/consts'; +import { getIsExternalServiceAccountFeatureAvailable, getIsRunningOpenSourceVersion } from 'helpers/helpers'; +import { useOnMount } from 'helpers/hooks'; +import { validateURL } from 'helpers/string'; import { observer } from 'mobx-react'; import { Controller, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom-v5-compat'; -import { OnCallPluginMetaJSONData } from 'types'; import { Button } from 'components/Button/Button'; import { CollapsibleTreeView } from 'components/CollapsibleTreeView/CollapsibleTreeView'; @@ -14,17 +25,6 @@ import { RenderConditionally } from 'components/RenderConditionally/RenderCondit import { Text } from 'components/Text/Text'; import { ActionKey } from 'models/loader/action-keys'; import { rootStore } from 'state/rootStore'; -import { - DEFAULT_PAGE, - DOCS_ONCALL_OSS_INSTALL, - DOCS_SERVICE_ACCOUNTS, - PLUGIN_CONFIG, - PLUGIN_ROOT, - REQUEST_HELP_URL, -} from 'utils/consts'; -import { useOnMount } from 'utils/hooks'; -import { validateURL } from 'utils/string'; -import { getIsExternalServiceAccountFeatureAvailable, getIsRunningOpenSourceVersion } from 'utils/utils'; type PluginConfigFormValues = { onCallApiUrl: string; diff --git a/grafana-plugin/src/containers/PluginInitializer/PluginInitializer.tsx b/grafana-plugin/src/containers/PluginInitializer/PluginInitializer.tsx index 2ba9aa66cb..ffdb6b3bbf 100644 --- a/grafana-plugin/src/containers/PluginInitializer/PluginInitializer.tsx +++ b/grafana-plugin/src/containers/PluginInitializer/PluginInitializer.tsx @@ -1,14 +1,14 @@ import React, { FC } from 'react'; import { Button, Stack, LoadingPlaceholder } from '@grafana/ui'; +import { REQUEST_HELP_URL, PLUGIN_CONFIG } from 'helpers/consts'; +import { getIsRunningOpenSourceVersion } from 'helpers/helpers'; +import { useInitializePlugin } from 'helpers/hooks'; import { observer } from 'mobx-react'; import { useNavigate } from 'react-router-dom-v5-compat'; import { FullPageError } from 'components/FullPageError/FullPageError'; import { RenderConditionally } from 'components/RenderConditionally/RenderConditionally'; -import { REQUEST_HELP_URL, PLUGIN_CONFIG } from 'utils/consts'; -import { useInitializePlugin } from 'utils/hooks'; -import { getIsRunningOpenSourceVersion } from 'utils/utils'; interface PluginInitializerProps { children: React.ReactNode; diff --git a/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx b/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx index 0c35a0fee7..92474fdcee 100644 --- a/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx +++ b/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx @@ -14,6 +14,9 @@ import { withTheme2, } from '@grafana/ui'; import { capitalCase } from 'change-case'; +import { LocationHelper } from 'helpers/LocationHelper'; +import { PAGE } from 'helpers/consts'; +import { convertTimerangeToFilterValue, getValueForDateRangeFilterType } from 'helpers/datetime'; import { debounce, isUndefined, omitBy, pickBy } from 'lodash-es'; import { observer } from 'mobx-react'; import moment from 'moment-timezone'; @@ -29,9 +32,6 @@ import { FilterExtraInformation, FilterExtraInformationValues } from 'models/fil import { GrafanaTeamStore } from 'models/grafana_team/grafana_team'; import { SelectOption, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { LocationHelper } from 'utils/LocationHelper'; -import { PAGE } from 'utils/consts'; -import { convertTimerangeToFilterValue, getValueForDateRangeFilterType } from 'utils/datetime'; import { parseFilters } from './RemoteFilters.helpers'; import { FilterOption } from './RemoteFilters.types'; diff --git a/grafana-plugin/src/containers/RemoteSelect/RemoteSelect.tsx b/grafana-plugin/src/containers/RemoteSelect/RemoteSelect.tsx index 94ec2fbc47..f33ab4c21a 100644 --- a/grafana-plugin/src/containers/RemoteSelect/RemoteSelect.tsx +++ b/grafana-plugin/src/containers/RemoteSelect/RemoteSelect.tsx @@ -2,11 +2,11 @@ import React, { useCallback, useMemo, useReducer, useState } from 'react'; import { SelectableValue } from '@grafana/data'; import { AsyncMultiSelect, AsyncSelect } from '@grafana/ui'; +import { UserAction, generateMissingPermissionMessage } from 'helpers/authorization/authorization'; +import { useDebouncedCallback } from 'helpers/hooks'; import { inject, observer } from 'mobx-react'; import { makeRequest, isNetworkError } from 'network/network'; -import { UserAction, generateMissingPermissionMessage } from 'utils/authorization/authorization'; -import { useDebouncedCallback } from 'utils/hooks'; interface RemoteSelectProps { autoFocus?: boolean; diff --git a/grafana-plugin/src/containers/RotationForm/RotationForm.helpers.ts b/grafana-plugin/src/containers/RotationForm/RotationForm.helpers.ts index 37671a36fe..7043f614bd 100644 --- a/grafana-plugin/src/containers/RotationForm/RotationForm.helpers.ts +++ b/grafana-plugin/src/containers/RotationForm/RotationForm.helpers.ts @@ -1,8 +1,8 @@ import { Dayjs, ManipulateType } from 'dayjs'; +import { GRAFANA_HEADER_HEIGHT, GRAFANA_LEGACY_SIDEBAR_WIDTH } from 'helpers/consts'; import { DraggableData } from 'react-draggable'; import { isTopNavbar } from 'plugin/GrafanaPluginRootPage.helpers'; -import { GRAFANA_HEADER_HEIGHT, GRAFANA_LEGACY_SIDEBAR_WIDTH } from 'utils/consts'; import { RepeatEveryPeriod } from './RotationForm.types'; diff --git a/grafana-plugin/src/containers/RotationForm/RotationForm.tsx b/grafana-plugin/src/containers/RotationForm/RotationForm.tsx index 8e5d38c6f0..e3f10fd678 100644 --- a/grafana-plugin/src/containers/RotationForm/RotationForm.tsx +++ b/grafana-plugin/src/containers/RotationForm/RotationForm.tsx @@ -3,6 +3,8 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Alert, Button, Field, Icon, IconButton, InlineSwitch, Select, Switch, Tooltip, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; import dayjs from 'dayjs'; +import { GRAFANA_HEADER_HEIGHT, StackSize } from 'helpers/consts'; +import { useDebouncedCallback, useResize } from 'helpers/hooks'; import { observer } from 'mobx-react'; import Draggable, { DraggableData, DraggableEvent } from 'react-draggable'; @@ -51,8 +53,6 @@ import { toDateWithTimezoneOffsetAtMidnight, } from 'pages/schedule/Schedule.helpers'; import { useStore } from 'state/useStore'; -import { GRAFANA_HEADER_HEIGHT, StackSize } from 'utils/consts'; -import { useDebouncedCallback, useResize } from 'utils/hooks'; import styles from './RotationForm.module.css'; diff --git a/grafana-plugin/src/containers/RotationForm/ScheduleOverrideForm.tsx b/grafana-plugin/src/containers/RotationForm/ScheduleOverrideForm.tsx index 12a69d5ae4..9a23727a30 100644 --- a/grafana-plugin/src/containers/RotationForm/ScheduleOverrideForm.tsx +++ b/grafana-plugin/src/containers/RotationForm/ScheduleOverrideForm.tsx @@ -3,6 +3,8 @@ import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { IconButton, Stack, Field, Button, useTheme2 } from '@grafana/ui'; import cn from 'classnames/bind'; import dayjs from 'dayjs'; +import { StackSize } from 'helpers/consts'; +import { useDebouncedCallback, useResize } from 'helpers/hooks'; import Draggable, { DraggableData, DraggableEvent } from 'react-draggable'; import { Modal } from 'components/Modal/Modal'; @@ -16,8 +18,6 @@ import { Schedule, Shift } from 'models/schedule/schedule.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { getDateTime, getUTCString, toDateWithTimezoneOffset } from 'pages/schedule/Schedule.helpers'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; -import { useDebouncedCallback, useResize } from 'utils/hooks'; import { getDraggableModalCoordinatesOnInit } from './RotationForm.helpers'; import { DateTimePicker } from './parts/DateTimePicker'; diff --git a/grafana-plugin/src/containers/RotationForm/ShiftSwapForm.tsx b/grafana-plugin/src/containers/RotationForm/ShiftSwapForm.tsx index 12176718c0..f8e01084ae 100644 --- a/grafana-plugin/src/containers/RotationForm/ShiftSwapForm.tsx +++ b/grafana-plugin/src/containers/RotationForm/ShiftSwapForm.tsx @@ -3,6 +3,9 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Button, Field, IconButton, Input, TextArea, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; import dayjs from 'dayjs'; +import { UserActions } from 'helpers/authorization/authorization'; +import { GRAFANA_HEADER_HEIGHT, StackSize } from 'helpers/consts'; +import { useDebouncedCallback, useResize } from 'helpers/hooks'; import Draggable, { DraggableData, DraggableEvent } from 'react-draggable'; import { Modal } from 'components/Modal/Modal'; @@ -15,9 +18,6 @@ import { SHIFT_SWAP_COLOR } from 'models/schedule/schedule.helpers'; import { Schedule, ShiftSwap } from 'models/schedule/schedule.types'; import { getUTCString } from 'pages/schedule/Schedule.helpers'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { GRAFANA_HEADER_HEIGHT, StackSize } from 'utils/consts'; -import { useDebouncedCallback, useResize } from 'utils/hooks'; import { getDraggableModalCoordinatesOnInit } from './RotationForm.helpers'; import { DateTimePicker } from './parts/DateTimePicker'; diff --git a/grafana-plugin/src/containers/RotationForm/parts/DeletionModal.tsx b/grafana-plugin/src/containers/RotationForm/parts/DeletionModal.tsx index 5132a69a5d..d5cef9b69b 100644 --- a/grafana-plugin/src/containers/RotationForm/parts/DeletionModal.tsx +++ b/grafana-plugin/src/containers/RotationForm/parts/DeletionModal.tsx @@ -2,9 +2,9 @@ import React, { ChangeEvent, useCallback, useState } from 'react'; import { Stack, Modal as GrafanaModal, Button, InlineSwitch } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; import { Text } from 'components/Text/Text'; -import { StackSize } from 'utils/consts'; import styles from 'containers/RotationForm/RotationForm.module.css'; diff --git a/grafana-plugin/src/containers/Rotations/Rotations.helpers.ts b/grafana-plugin/src/containers/Rotations/Rotations.helpers.ts index 0c6dfb18c8..5ee4382340 100644 --- a/grafana-plugin/src/containers/Rotations/Rotations.helpers.ts +++ b/grafana-plugin/src/containers/Rotations/Rotations.helpers.ts @@ -1,10 +1,10 @@ import dayjs from 'dayjs'; +import { waitForElement } from 'helpers/DOM'; import { getColor, getOverrideColor } from 'models/schedule/schedule.helpers'; import { Layer, Shift } from 'models/schedule/schedule.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { toDateWithTimezoneOffset } from 'pages/schedule/Schedule.helpers'; -import { waitForElement } from 'utils/DOM'; export const calculateScheduleFormOffset = async (queryClassName: string) => { const modal = await waitForElement(queryClassName); diff --git a/grafana-plugin/src/containers/Rotations/Rotations.tsx b/grafana-plugin/src/containers/Rotations/Rotations.tsx index a769c76efe..9034d7b719 100644 --- a/grafana-plugin/src/containers/Rotations/Rotations.tsx +++ b/grafana-plugin/src/containers/Rotations/Rotations.tsx @@ -4,6 +4,8 @@ import { cx } from '@emotion/css'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { ValuePicker, Button, Tooltip, withTheme2, Stack } from '@grafana/ui'; import dayjs from 'dayjs'; +import { HTML_ID } from 'helpers/DOM'; +import { UserActions } from 'helpers/authorization/authorization'; import { observer } from 'mobx-react'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; @@ -19,8 +21,6 @@ import { Schedule, ScheduleType, Shift, ShiftSwap, Event, Layer } from 'models/s import { getCurrentTimeX, toDateWithTimezoneOffset } from 'pages/schedule/Schedule.helpers'; import { WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { HTML_ID } from 'utils/DOM'; -import { UserActions } from 'utils/authorization/authorization'; import { DEFAULT_TRANSITION_TIMEOUT } from './Rotations.config'; import { findColor, getCalendarStartDateInTimezone } from './Rotations.helpers'; diff --git a/grafana-plugin/src/containers/Rotations/ScheduleFinal.tsx b/grafana-plugin/src/containers/Rotations/ScheduleFinal.tsx index 9effe9a648..a2e7485f97 100644 --- a/grafana-plugin/src/containers/Rotations/ScheduleFinal.tsx +++ b/grafana-plugin/src/containers/Rotations/ScheduleFinal.tsx @@ -4,6 +4,7 @@ import { cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Stack, useStyles2, withTheme2 } from '@grafana/ui'; import dayjs from 'dayjs'; +import { HTML_ID } from 'helpers/DOM'; import { observer } from 'mobx-react'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; import { bem } from 'styles/utils.styles'; @@ -24,7 +25,6 @@ import { Event, Schedule, ScheduleView, ShiftSwap } from 'models/schedule/schedu import { getCurrentTimeX } from 'pages/schedule/Schedule.helpers'; import { WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { HTML_ID } from 'utils/DOM'; import { DEFAULT_TRANSITION_TIMEOUT } from './Rotations.config'; import { findColor } from './Rotations.helpers'; diff --git a/grafana-plugin/src/containers/Rotations/ScheduleOverrides.tsx b/grafana-plugin/src/containers/Rotations/ScheduleOverrides.tsx index eb09c17b5d..db490348ad 100644 --- a/grafana-plugin/src/containers/Rotations/ScheduleOverrides.tsx +++ b/grafana-plugin/src/containers/Rotations/ScheduleOverrides.tsx @@ -4,6 +4,8 @@ import { cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, Stack, Tooltip, withTheme2 } from '@grafana/ui'; import dayjs from 'dayjs'; +import { HTML_ID } from 'helpers/DOM'; +import { UserActions } from 'helpers/authorization/authorization'; import { observer } from 'mobx-react'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; @@ -25,8 +27,6 @@ import { Schedule, Shift, ShiftEvents, ShiftSwap } from 'models/schedule/schedul import { getCurrentTimeX, toDateWithTimezoneOffset } from 'pages/schedule/Schedule.helpers'; import { WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { HTML_ID } from 'utils/DOM'; -import { UserActions } from 'utils/authorization/authorization'; import { DEFAULT_TRANSITION_TIMEOUT } from './Rotations.config'; import { findColor } from './Rotations.helpers'; diff --git a/grafana-plugin/src/containers/Rotations/SchedulePersonal.tsx b/grafana-plugin/src/containers/Rotations/SchedulePersonal.tsx index 2d18d994ff..91b360e809 100644 --- a/grafana-plugin/src/containers/Rotations/SchedulePersonal.tsx +++ b/grafana-plugin/src/containers/Rotations/SchedulePersonal.tsx @@ -4,6 +4,8 @@ import { cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Badge, BadgeColor, Button, Icon, Stack, useStyles2, withTheme2 } from '@grafana/ui'; import dayjs from 'dayjs'; +import { PLUGIN_ROOT, StackSize } from 'helpers/consts'; +import { useIsLoading } from 'helpers/hooks'; import { observer } from 'mobx-react'; import { useNavigate } from 'react-router-dom-v5-compat'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; @@ -24,8 +26,6 @@ import { Event, ScheduleView } from 'models/schedule/schedule.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { getCurrentTimeX, getStartOfWeekBasedOnCurrentDate } from 'pages/schedule/Schedule.helpers'; import { useStore } from 'state/useStore'; -import { PLUGIN_ROOT, StackSize } from 'utils/consts'; -import { useIsLoading } from 'utils/hooks'; import { DEFAULT_TRANSITION_TIMEOUT } from './Rotations.config'; import { getRotationsStyles } from './Rotations.styles'; diff --git a/grafana-plugin/src/containers/RouteLabelsDisplay/RouteLabelsDisplay.tsx b/grafana-plugin/src/containers/RouteLabelsDisplay/RouteLabelsDisplay.tsx index 5674438fe5..e2e3d5ff01 100644 --- a/grafana-plugin/src/containers/RouteLabelsDisplay/RouteLabelsDisplay.tsx +++ b/grafana-plugin/src/containers/RouteLabelsDisplay/RouteLabelsDisplay.tsx @@ -2,12 +2,12 @@ import React from 'react'; import { ServiceLabels } from '@grafana/labels'; import { Button, Stack } from '@grafana/ui'; +import { GENERIC_ERROR } from 'helpers/consts'; +import { openErrorNotification } from 'helpers/helpers'; import { splitToGroups } from 'models/label/label.helpers'; import { components } from 'network/oncall-api/autogenerated-api.types'; import { useStore } from 'state/useStore'; -import { GENERIC_ERROR } from 'utils/consts'; -import { openErrorNotification } from 'utils/utils'; interface RouteLabelsDisplayProps { labels: Array; diff --git a/grafana-plugin/src/containers/ScheduleForm/ScheduleForm.tsx b/grafana-plugin/src/containers/ScheduleForm/ScheduleForm.tsx index d3364ff66c..4161e7aa12 100644 --- a/grafana-plugin/src/containers/ScheduleForm/ScheduleForm.tsx +++ b/grafana-plugin/src/containers/ScheduleForm/ScheduleForm.tsx @@ -2,6 +2,8 @@ import React, { useCallback, useMemo } from 'react'; import { css } from '@emotion/css'; import { Button, Drawer, Field, Input, Switch, TextArea, Stack, useStyles2 } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { openWarningNotification } from 'helpers/helpers'; import { observer } from 'mobx-react'; import { Controller, FormProvider, useForm, useFormContext } from 'react-hook-form'; import { getUtilStyles } from 'styles/utils.styles'; @@ -16,8 +18,6 @@ import { PRIVATE_CHANNEL_NAME } from 'models/slack_channel/slack_channel.config' import { SlackChannel } from 'models/slack_channel/slack_channel.types'; import { UserGroup } from 'models/user_group/user_group.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { openWarningNotification } from 'utils/utils'; import { prepareForEdit } from './ScheduleForm.helpers'; diff --git a/grafana-plugin/src/containers/ScheduleIcalLink/ScheduleIcalLink.tsx b/grafana-plugin/src/containers/ScheduleIcalLink/ScheduleIcalLink.tsx index 2f71a05c94..111a68ed54 100644 --- a/grafana-plugin/src/containers/ScheduleIcalLink/ScheduleIcalLink.tsx +++ b/grafana-plugin/src/containers/ScheduleIcalLink/ScheduleIcalLink.tsx @@ -2,13 +2,13 @@ import React, { FC, useEffect, useState } from 'react'; import { Button, Icon, Label, LoadingPlaceholder, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { openNotification } from 'helpers/helpers'; import { observer } from 'mobx-react'; import CopyToClipboard from 'react-copy-to-clipboard'; import { Text } from 'components/Text/Text'; import { CreateScheduleExportTokenResponse, Schedule } from 'models/schedule/schedule.types'; import { useStore } from 'state/useStore'; -import { openNotification } from 'utils/utils'; import styles from './ScheduleIcalLink.module.css'; diff --git a/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.tsx b/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.tsx index 8e0f8fb5b8..2cd9735092 100644 --- a/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.tsx +++ b/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.tsx @@ -4,6 +4,8 @@ import { css, cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, Icon, Tooltip, useStyles2, Stack } from '@grafana/ui'; import dayjs from 'dayjs'; +import { StackSize } from 'helpers/consts'; +import { truncateTitle } from 'helpers/string'; import { observer } from 'mobx-react'; import { Colors, getLabelCss } from 'styles/utils.styles'; @@ -17,8 +19,6 @@ import { Event, ScheduleView, ShiftSwap } from 'models/schedule/schedule.types'; import { getTzOffsetString } from 'models/timezone/timezone.helpers'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; -import { truncateTitle } from 'utils/string'; import { getScheduleSlotStyleParams, getTitle } from './ScheduleSlot.helpers'; diff --git a/grafana-plugin/src/containers/ServiceNowConfigDrawer/CompleteServiceNowConfigModal.tsx b/grafana-plugin/src/containers/ServiceNowConfigDrawer/CompleteServiceNowConfigModal.tsx index 20569cd8e5..d9e71d3053 100644 --- a/grafana-plugin/src/containers/ServiceNowConfigDrawer/CompleteServiceNowConfigModal.tsx +++ b/grafana-plugin/src/containers/ServiceNowConfigDrawer/CompleteServiceNowConfigModal.tsx @@ -3,13 +3,13 @@ import React, { useState } from 'react'; import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, Modal, Stack, useStyles2 } from '@grafana/ui'; +import { openNotification } from 'helpers/helpers'; +import { OmitReadonlyMembers } from 'helpers/types'; import { FormProvider, useForm } from 'react-hook-form'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useCurrentIntegration } from 'pages/integration/OutgoingTab/OutgoingTab.hooks'; import { useStore } from 'state/useStore'; -import { OmitReadonlyMembers } from 'utils/types'; -import { openNotification } from 'utils/utils'; import { getCommonServiceNowConfigStyles } from './ServiceNow.styles'; import { ServiceNowStatusSection } from './ServiceNowStatusSection'; diff --git a/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowAuthSection.tsx b/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowAuthSection.tsx index ce2a7ae7f7..3f84bb6b64 100644 --- a/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowAuthSection.tsx +++ b/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowAuthSection.tsx @@ -2,6 +2,8 @@ import React, { forwardRef, useImperativeHandle, useState } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { Alert, Button, LoadingPlaceholder, Stack, useStyles2 } from '@grafana/ui'; +import { INTEGRATION_SERVICENOW } from 'helpers/consts'; +import { OmitReadonlyMembers } from 'helpers/types'; import { observer } from 'mobx-react'; import { useFormContext } from 'react-hook-form'; @@ -11,8 +13,6 @@ import { IntegrationFormFields } from 'containers/IntegrationForm/IntegrationFor import { AlertReceiveChannelHelper } from 'models/alert_receive_channel/alert_receive_channel.helpers'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useCurrentIntegration } from 'pages/integration/OutgoingTab/OutgoingTab.hooks'; -import { INTEGRATION_SERVICENOW } from 'utils/consts'; -import { OmitReadonlyMembers } from 'utils/types'; import { getCommonServiceNowConfigStyles } from './ServiceNow.styles'; import { ServiceNowFormFields } from './ServiceNowStatusSection'; diff --git a/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowConfig.helpers.ts b/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowConfig.helpers.ts index d479942ef5..0b395bf07e 100644 --- a/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowConfig.helpers.ts +++ b/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowConfig.helpers.ts @@ -1,8 +1,8 @@ import { SelectableValue } from '@grafana/data'; +import { OnCallAGStatus } from 'helpers/consts'; import { UseFormGetValues } from 'react-hook-form'; import { AlertReceiveChannelStore } from 'models/alert_receive_channel/alert_receive_channel'; -import { OnCallAGStatus } from 'utils/consts'; import { ServiceNowFormFields } from './ServiceNowStatusSection'; diff --git a/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowConfigDrawer.tsx b/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowConfigDrawer.tsx index 4110a869cf..6ff772df1a 100644 --- a/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowConfigDrawer.tsx +++ b/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowConfigDrawer.tsx @@ -3,6 +3,10 @@ import React from 'react'; import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Drawer, Field, Input, useStyles2, Button, Stack } from '@grafana/ui'; +import { openNotification } from 'helpers/helpers'; +import { useIsLoading } from 'helpers/hooks'; +import { validateURL } from 'helpers/string'; +import { OmitReadonlyMembers } from 'helpers/types'; import { observer } from 'mobx-react'; import { Controller, FormProvider, useForm } from 'react-hook-form'; @@ -10,10 +14,6 @@ import { ActionKey } from 'models/loader/action-keys'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useCurrentIntegration } from 'pages/integration/OutgoingTab/OutgoingTab.hooks'; import { useStore } from 'state/useStore'; -import { useIsLoading } from 'utils/hooks'; -import { validateURL } from 'utils/string'; -import { OmitReadonlyMembers } from 'utils/types'; -import { openNotification } from 'utils/utils'; import { getCommonServiceNowConfigStyles } from './ServiceNow.styles'; import { ServiceNowAuthSection } from './ServiceNowAuthSection'; diff --git a/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowStatusSection.tsx b/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowStatusSection.tsx index 131319d42b..01fbfd2b4e 100644 --- a/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowStatusSection.tsx +++ b/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowStatusSection.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useReducer } from 'react'; import { SelectableValue } from '@grafana/data'; import { Select, SelectBaseProps, Stack } from '@grafana/ui'; +import { OnCallAGStatus, StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { Controller, useFormContext } from 'react-hook-form'; @@ -9,7 +10,6 @@ import { Text } from 'components/Text/Text'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useCurrentIntegration } from 'pages/integration/OutgoingTab/OutgoingTab.hooks'; import { useStore } from 'state/useStore'; -import { OnCallAGStatus, StackSize } from 'utils/consts'; import { ServiceNowHelper } from './ServiceNowConfig.helpers'; diff --git a/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowTokenSection.tsx b/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowTokenSection.tsx index 7f1a7411f1..67aa8617c6 100644 --- a/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowTokenSection.tsx +++ b/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowTokenSection.tsx @@ -3,6 +3,8 @@ import React, { useEffect, useState } from 'react'; import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, Input, LoadingPlaceholder, Stack, useStyles2 } from '@grafana/ui'; +import { DOCS_ROOT, StackSize } from 'helpers/consts'; +import { useIsLoading } from 'helpers/hooks'; import { observer } from 'mobx-react'; import { RenderConditionally } from 'components/RenderConditionally/RenderConditionally'; @@ -11,8 +13,6 @@ import { Text } from 'components/Text/Text'; import { AlertReceiveChannelHelper } from 'models/alert_receive_channel/alert_receive_channel.helpers'; import { ActionKey } from 'models/loader/action-keys'; import { useCurrentIntegration } from 'pages/integration/OutgoingTab/OutgoingTab.hooks'; -import { DOCS_ROOT, StackSize } from 'utils/consts'; -import { useIsLoading } from 'utils/hooks'; import { getCommonServiceNowConfigStyles } from './ServiceNow.styles'; diff --git a/grafana-plugin/src/containers/TeamsList/TeamsList.tsx b/grafana-plugin/src/containers/TeamsList/TeamsList.tsx index a780574d7e..8242780c2f 100644 --- a/grafana-plugin/src/containers/TeamsList/TeamsList.tsx +++ b/grafana-plugin/src/containers/TeamsList/TeamsList.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useState } from 'react'; import { Badge, Button, Field, Modal, RadioButtonList, Tooltip, Stack } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; import { observer } from 'mobx-react'; import { Avatar } from 'components/Avatar/Avatar'; @@ -9,7 +10,6 @@ import { Text } from 'components/Text/Text'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; import { GrafanaTeam } from 'models/grafana_team/grafana_team.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; export const TeamsList = observer(() => { const store = useStore(); diff --git a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx index 50c19963eb..513d44985c 100644 --- a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx +++ b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx @@ -2,6 +2,9 @@ import React, { useCallback, useState, useEffect } from 'react'; import { Button, Modal, Icon, Stack, Field, Input } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; +import { openNotification } from 'helpers/helpers'; import { observer } from 'mobx-react'; import CopyToClipboard from 'react-copy-to-clipboard'; @@ -9,9 +12,6 @@ import { Block } from 'components/GBlock/Block'; import { Text } from 'components/Text/Text'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; -import { openNotification } from 'utils/utils'; import styles from './TelegramIntegrationButton.module.css'; diff --git a/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.tsx b/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.tsx index 4ce1f76edb..d993af3398 100644 --- a/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.tsx +++ b/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.tsx @@ -2,6 +2,9 @@ import React, { useEffect, useState } from 'react'; import { Badge, Icon, LoadingPlaceholder, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { openErrorNotification } from 'helpers/helpers'; +import { useDebouncedCallback } from 'helpers/hooks'; +import { sanitize } from 'helpers/sanitize'; import { observer } from 'mobx-react'; import { Text } from 'components/Text/Text'; @@ -10,9 +13,6 @@ import { AlertGroupHelper } from 'models/alertgroup/alertgroup.helpers'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { LabelTemplateOptions } from 'pages/integration/IntegrationCommon.config'; import { useStore } from 'state/useStore'; -import { useDebouncedCallback } from 'utils/hooks'; -import { sanitize } from 'utils/sanitize'; -import { openErrorNotification } from 'utils/utils'; import styles from './TemplatePreview.module.css'; diff --git a/grafana-plugin/src/containers/TemplateResult/TemplateResult.tsx b/grafana-plugin/src/containers/TemplateResult/TemplateResult.tsx index e7746c4612..ac5538dfff 100644 --- a/grafana-plugin/src/containers/TemplateResult/TemplateResult.tsx +++ b/grafana-plugin/src/containers/TemplateResult/TemplateResult.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Button, Icon, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; import { TemplateForEdit } from 'components/AlertTemplates/CommonAlertTemplatesForm.config'; import { Block } from 'components/GBlock/Block'; @@ -9,7 +10,6 @@ import { Text } from 'components/Text/Text'; import styles from 'containers/IntegrationTemplate/IntegrationTemplate.module.scss'; import { TemplatePreview, TemplatePage } from 'containers/TemplatePreview/TemplatePreview'; import { ApiSchemas } from 'network/oncall-api/api.types'; -import { StackSize } from 'utils/consts'; const cx = cn.bind(styles); diff --git a/grafana-plugin/src/containers/UserDisplay/UserDisplayWithAvatar.tsx b/grafana-plugin/src/containers/UserDisplay/UserDisplayWithAvatar.tsx index e05344e35d..c48fda5edd 100644 --- a/grafana-plugin/src/containers/UserDisplay/UserDisplayWithAvatar.tsx +++ b/grafana-plugin/src/containers/UserDisplay/UserDisplayWithAvatar.tsx @@ -1,13 +1,13 @@ import React, { useEffect } from 'react'; import { Stack } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { Avatar } from 'components/Avatar/Avatar'; import { Text } from 'components/Text/Text'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; interface UserDisplayProps { id: ApiSchemas['User']['pk']; diff --git a/grafana-plugin/src/containers/UserSettings/UserSettings.tsx b/grafana-plugin/src/containers/UserSettings/UserSettings.tsx index 25e63e715e..238467741f 100644 --- a/grafana-plugin/src/containers/UserSettings/UserSettings.tsx +++ b/grafana-plugin/src/containers/UserSettings/UserSettings.tsx @@ -2,6 +2,9 @@ import React, { useEffect, useState, useCallback } from 'react'; import { Alert, Modal, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { LocationHelper } from 'helpers/LocationHelper'; +import { BREAKPOINT_TABS } from 'helpers/consts'; +import { useQueryParams } from 'helpers/hooks'; import { observer } from 'mobx-react'; import { useMediaQuery } from 'react-responsive'; @@ -10,9 +13,6 @@ import { Tabs, TabsContent } from 'containers/UserSettings/parts/UserSettingsPar import { ApiSchemas } from 'network/oncall-api/api.types'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; -import { LocationHelper } from 'utils/LocationHelper'; -import { BREAKPOINT_TABS } from 'utils/consts'; -import { useQueryParams } from 'utils/hooks'; import { UserSettingsTab } from './UserSettings.types'; diff --git a/grafana-plugin/src/containers/UserSettings/parts/UserSettingsParts.tsx b/grafana-plugin/src/containers/UserSettings/parts/UserSettingsParts.tsx index a6262e5852..d50b8e840c 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/UserSettingsParts.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/UserSettingsParts.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useEffect } from 'react'; import { Tab, TabContent, TabsBar } from '@grafana/ui'; import cn from 'classnames/bind'; +import { isUseProfileExtensionPointEnabled } from 'helpers/helpers'; import { observer } from 'mobx-react'; import { Block } from 'components/GBlock/Block'; @@ -19,7 +20,6 @@ import { UserInfoTab } from 'containers/UserSettings/parts/tabs/UserInfoTab/User import { ApiSchemas } from 'network/oncall-api/api.types'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; -import { isUseProfileExtensionPointEnabled } from 'utils/utils'; import styles from 'containers/UserSettings/parts/UserSettingsParts.module.css'; diff --git a/grafana-plugin/src/containers/UserSettings/parts/connectors/GoogleConnector.tsx b/grafana-plugin/src/containers/UserSettings/parts/connectors/GoogleConnector.tsx index 9f5b8f4442..29e9ebee15 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/connectors/GoogleConnector.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/connectors/GoogleConnector.tsx @@ -1,6 +1,8 @@ import React from 'react'; import { Button, InlineField, Stack } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { WithConfirm } from 'components/WithConfirm/WithConfirm'; @@ -8,8 +10,6 @@ import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/W import { UserHelper } from 'models/user/user.helpers'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; interface GoogleConnectorProps { id: ApiSchemas['User']['pk']; diff --git a/grafana-plugin/src/containers/UserSettings/parts/connectors/ICalConnector.tsx b/grafana-plugin/src/containers/UserSettings/parts/connectors/ICalConnector.tsx index 1782b0b1d2..459287919f 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/connectors/ICalConnector.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/connectors/ICalConnector.tsx @@ -1,6 +1,9 @@ import React, { useEffect, useState } from 'react'; import { Alert, Button, InlineField, Input, LoadingPlaceholder, Stack, Tooltip } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; +import { openNotification } from 'helpers/helpers'; import CopyToClipboard from 'react-copy-to-clipboard'; import { WithConfirm } from 'components/WithConfirm/WithConfirm'; @@ -8,9 +11,6 @@ import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/W import { UserHelper } from 'models/user/user.helpers'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; -import { openNotification } from 'utils/utils'; // eslint-disable-next-line @typescript-eslint/naming-convention interface ICalConnectorProps { diff --git a/grafana-plugin/src/containers/UserSettings/parts/connectors/MSTeamsConnector.tsx b/grafana-plugin/src/containers/UserSettings/parts/connectors/MSTeamsConnector.tsx index 072c8afe48..9b1c437979 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/connectors/MSTeamsConnector.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/connectors/MSTeamsConnector.tsx @@ -2,13 +2,13 @@ import React, { useCallback } from 'react'; import { Button, InlineField, Input, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { WithConfirm } from 'components/WithConfirm/WithConfirm'; import { UserSettingsTab } from 'containers/UserSettings/UserSettings.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; import styles from 'containers/UserSettings/parts/connectors/Connectors.module.css'; diff --git a/grafana-plugin/src/containers/UserSettings/parts/connectors/PhoneConnector.tsx b/grafana-plugin/src/containers/UserSettings/parts/connectors/PhoneConnector.tsx index 8abe428460..877e68a91c 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/connectors/PhoneConnector.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/connectors/PhoneConnector.tsx @@ -2,6 +2,7 @@ import React, { useCallback } from 'react'; import { Alert, Button, InlineField, Input, Stack, useTheme2 } from '@grafana/ui'; import cn from 'classnames/bind'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { Tag } from 'components/Tag/Tag'; @@ -11,7 +12,6 @@ import { UserSettingsTab } from 'containers/UserSettings/UserSettings.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; import styles from 'containers/UserSettings/parts/UserSettingsParts.module.css'; diff --git a/grafana-plugin/src/containers/UserSettings/parts/connectors/SlackConnector.tsx b/grafana-plugin/src/containers/UserSettings/parts/connectors/SlackConnector.tsx index 742d8afb50..6c6917a049 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/connectors/SlackConnector.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/connectors/SlackConnector.tsx @@ -1,14 +1,14 @@ import React, { useCallback, useMemo } from 'react'; import { Button, InlineField, Input, Stack } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; +import { getPathFromQueryParams } from 'helpers/url'; import { observer } from 'mobx-react'; import { WithConfirm } from 'components/WithConfirm/WithConfirm'; import { UserSettingsTab } from 'containers/UserSettings/UserSettings.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; -import { getPathFromQueryParams } from 'utils/url'; interface SlackConnectorProps { id: ApiSchemas['User']['pk']; diff --git a/grafana-plugin/src/containers/UserSettings/parts/connectors/TelegramConnector.tsx b/grafana-plugin/src/containers/UserSettings/parts/connectors/TelegramConnector.tsx index 46cb91a19f..27d7191f21 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/connectors/TelegramConnector.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/connectors/TelegramConnector.tsx @@ -1,13 +1,13 @@ import React, { useCallback } from 'react'; import { Button, InlineField, Input, Stack } from '@grafana/ui'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { WithConfirm } from 'components/WithConfirm/WithConfirm'; import { UserSettingsTab } from 'containers/UserSettings/UserSettings.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { StackSize } from 'utils/consts'; interface TelegramConnectorProps { id: ApiSchemas['User']['pk']; diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/CloudPhoneSettings/CloudPhoneSettings.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/CloudPhoneSettings/CloudPhoneSettings.tsx index 43359ea164..c583f4c864 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/CloudPhoneSettings/CloudPhoneSettings.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/CloudPhoneSettings/CloudPhoneSettings.tsx @@ -1,6 +1,8 @@ import React, { useEffect, useState } from 'react'; import { Button, LoadingPlaceholder, Stack } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { PluginLink } from 'components/PluginLink/PluginLink'; @@ -10,8 +12,6 @@ import { ApiSchemas } from 'network/oncall-api/api.types'; import { WithStoreProps } from 'state/types'; import { useStore } from 'state/useStore'; import { withMobXProviderContext } from 'state/withStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; interface CloudPhoneSettingsProps extends WithStoreProps { userPk?: ApiSchemas['User']['pk']; diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/GoogleCalendar/GoogleCalendar.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/GoogleCalendar/GoogleCalendar.tsx index 53988ecc59..4cbd8ad2ae 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/GoogleCalendar/GoogleCalendar.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/GoogleCalendar/GoogleCalendar.tsx @@ -2,6 +2,8 @@ import React, { useEffect, useState } from 'react'; import { css } from '@emotion/css'; import { Button, Switch, Stack, useStyles2 } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { DOCS_ROOT, StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { getUtilStyles } from 'styles/utils.styles'; @@ -15,8 +17,6 @@ import { Schedule } from 'models/schedule/schedule.types'; import { UserHelper } from 'models/user/user.helpers'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { DOCS_ROOT, StackSize } from 'utils/consts'; const GoogleCalendar: React.FC<{ id: ApiSchemas['User']['pk'] }> = observer(({ id }) => { const { diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/PhoneVerification/PhoneVerification.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/PhoneVerification/PhoneVerification.tsx index 13add81269..1a3dcd2792 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/PhoneVerification/PhoneVerification.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/PhoneVerification/PhoneVerification.tsx @@ -2,6 +2,9 @@ import React, { HTMLAttributes, useCallback, useRef, useReducer } from 'react'; import { Alert, Button, Field, Icon, Input, Switch, Tooltip, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { isUserActionAllowed, UserAction, UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; +import { useIsLoading } from 'helpers/hooks'; import { observer } from 'mobx-react'; import { PluginLink } from 'components/PluginLink/PluginLink'; @@ -15,9 +18,6 @@ import { ApiSchemas } from 'network/oncall-api/api.types'; import { AppFeature } from 'state/features'; import { rootStore } from 'state/rootStore'; import { useStore } from 'state/useStore'; -import { isUserActionAllowed, UserAction, UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; -import { useIsLoading } from 'utils/hooks'; import styles from './PhoneVerification.module.css'; diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx index e14c7b2264..20489da2ad 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx @@ -2,14 +2,14 @@ import React, { useCallback } from 'react'; import { Button, Stack, Icon } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { DOCS_SLACK_SETUP, getPluginId, StackSize } from 'helpers/consts'; import { Block } from 'components/GBlock/Block'; import { Text } from 'components/Text/Text'; import { WithPermissionControlDisplay } from 'containers/WithPermissionControl/WithPermissionControlDisplay'; import { SlackNewIcon } from 'icons/Icons'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { DOCS_SLACK_SETUP, getPluginId, StackSize } from 'utils/consts'; import styles from './SlackTab.module.css'; diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx index 567ba82f39..4090c40b95 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx @@ -2,6 +2,9 @@ import React, { HTMLAttributes, useEffect, useState } from 'react'; import { Button, Icon, Stack, Field, Input } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { DOCS_TELEGRAM_SETUP, StackSize } from 'helpers/consts'; +import { openNotification } from 'helpers/helpers'; import { observer } from 'mobx-react'; import CopyToClipboard from 'react-copy-to-clipboard'; @@ -13,9 +16,6 @@ import { TelegramColorIcon } from 'icons/Icons'; import { UserHelper } from 'models/user/user.helpers'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { DOCS_TELEGRAM_SETUP, StackSize } from 'utils/consts'; -import { openNotification } from 'utils/utils'; import styles from './TelegramInfo.module.css'; diff --git a/grafana-plugin/src/containers/UsersTimezones/ScheduleUserDetails/ScheduleUserDetails.tsx b/grafana-plugin/src/containers/UsersTimezones/ScheduleUserDetails/ScheduleUserDetails.tsx index bfb5e1ac0d..83610a6ec9 100644 --- a/grafana-plugin/src/containers/UsersTimezones/ScheduleUserDetails/ScheduleUserDetails.tsx +++ b/grafana-plugin/src/containers/UsersTimezones/ScheduleUserDetails/ScheduleUserDetails.tsx @@ -3,6 +3,8 @@ import React, { FC } from 'react'; import { Stack, Icon, Badge } from '@grafana/ui'; import cn from 'classnames/bind'; import dayjs from 'dayjs'; +import { isUserActionAllowed, UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { Avatar } from 'components/Avatar/Avatar'; @@ -17,8 +19,6 @@ import { import { ApiSchemas } from 'network/oncall-api/api.types'; import { getColorSchemeMappingForUsers } from 'pages/schedule/Schedule.helpers'; import { useStore } from 'state/useStore'; -import { isUserActionAllowed, UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; import styles from './ScheduleUserDetails.module.css'; diff --git a/grafana-plugin/src/containers/WebhooksTemplateEditor/WebhooksTemplateEditor.tsx b/grafana-plugin/src/containers/WebhooksTemplateEditor/WebhooksTemplateEditor.tsx index 20921c56ef..60f7606384 100644 --- a/grafana-plugin/src/containers/WebhooksTemplateEditor/WebhooksTemplateEditor.tsx +++ b/grafana-plugin/src/containers/WebhooksTemplateEditor/WebhooksTemplateEditor.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useState } from 'react'; import { Button, Drawer, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; import { debounce } from 'lodash-es'; import { CheatSheet } from 'components/CheatSheet/CheatSheet'; @@ -14,7 +15,6 @@ import { TemplateResult } from 'containers/TemplateResult/TemplateResult'; import { TemplatesAlertGroupsList } from 'containers/TemplatesAlertGroupsList/TemplatesAlertGroupsList'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; import { ApiSchemas } from 'network/oncall-api/api.types'; -import { UserActions } from 'utils/authorization/authorization'; const cx = cn.bind(styles); diff --git a/grafana-plugin/src/containers/WithPermissionControl/WithPermissionControlDisplay.tsx b/grafana-plugin/src/containers/WithPermissionControl/WithPermissionControlDisplay.tsx index 21335c8b54..6cb9a82666 100644 --- a/grafana-plugin/src/containers/WithPermissionControl/WithPermissionControlDisplay.tsx +++ b/grafana-plugin/src/containers/WithPermissionControl/WithPermissionControlDisplay.tsx @@ -1,10 +1,10 @@ import React, { ReactElement } from 'react'; import { Stack } from '@grafana/ui'; +import { isUserActionAllowed, UserAction } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; import { Text } from 'components/Text/Text'; -import { isUserActionAllowed, UserAction } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; interface WithPermissionControlDisplayProps { userAction: UserAction; diff --git a/grafana-plugin/src/containers/WithPermissionControl/WithPermissionControlTooltip.tsx b/grafana-plugin/src/containers/WithPermissionControl/WithPermissionControlTooltip.tsx index 846a279105..6a559618c3 100644 --- a/grafana-plugin/src/containers/WithPermissionControl/WithPermissionControlTooltip.tsx +++ b/grafana-plugin/src/containers/WithPermissionControl/WithPermissionControlTooltip.tsx @@ -2,10 +2,9 @@ import React, { ReactElement, useCallback } from 'react'; import { Tooltip } from '@grafana/ui'; import cn from 'classnames/bind'; +import { isUserActionAllowed, UserAction } from 'helpers/authorization/authorization'; import { observer } from 'mobx-react'; -import { isUserActionAllowed, UserAction } from 'utils/authorization/authorization'; - import styles from './WithPermissionControlTooltip.module.css'; const cx = cn.bind(styles); diff --git a/grafana-plugin/src/utils/DOM.ts b/grafana-plugin/src/helpers/DOM.ts similarity index 100% rename from grafana-plugin/src/utils/DOM.ts rename to grafana-plugin/src/helpers/DOM.ts diff --git a/grafana-plugin/src/utils/LocationHelper.ts b/grafana-plugin/src/helpers/LocationHelper.ts similarity index 100% rename from grafana-plugin/src/utils/LocationHelper.ts rename to grafana-plugin/src/helpers/LocationHelper.ts diff --git a/grafana-plugin/src/utils/async.test.ts b/grafana-plugin/src/helpers/async.test.ts similarity index 100% rename from grafana-plugin/src/utils/async.test.ts rename to grafana-plugin/src/helpers/async.test.ts diff --git a/grafana-plugin/src/utils/async.ts b/grafana-plugin/src/helpers/async.ts similarity index 100% rename from grafana-plugin/src/utils/async.ts rename to grafana-plugin/src/helpers/async.ts diff --git a/grafana-plugin/src/utils/authorization/authorization.test.ts b/grafana-plugin/src/helpers/authorization/authorization.test.ts similarity index 98% rename from grafana-plugin/src/utils/authorization/authorization.test.ts rename to grafana-plugin/src/helpers/authorization/authorization.test.ts index c9c3d52dc4..753e4fd5c9 100644 --- a/grafana-plugin/src/utils/authorization/authorization.test.ts +++ b/grafana-plugin/src/helpers/authorization/authorization.test.ts @@ -1,8 +1,7 @@ import { OrgRole } from '@grafana/data'; import { config } from '@grafana/runtime'; import { contextSrv } from 'grafana/app/core/core'; - -import { getPluginId } from 'utils/consts'; +import { getPluginId } from 'helpers/consts'; import * as auth from './authorization'; diff --git a/grafana-plugin/src/utils/authorization/authorization.ts b/grafana-plugin/src/helpers/authorization/authorization.ts similarity index 99% rename from grafana-plugin/src/utils/authorization/authorization.ts rename to grafana-plugin/src/helpers/authorization/authorization.ts index 149a896273..4019e7a30a 100644 --- a/grafana-plugin/src/utils/authorization/authorization.ts +++ b/grafana-plugin/src/helpers/authorization/authorization.ts @@ -1,8 +1,7 @@ import { OrgRole } from '@grafana/data'; import { config } from '@grafana/runtime'; import { contextSrv } from 'grafana/app/core/core'; - -import { PluginId } from 'utils/consts'; +import { PluginId } from 'helpers/consts'; export type UserAction = { permission: string; diff --git a/grafana-plugin/src/utils/consts.ts b/grafana-plugin/src/helpers/consts.ts similarity index 98% rename from grafana-plugin/src/utils/consts.ts rename to grafana-plugin/src/helpers/consts.ts index 2aec11fbf2..d11503e097 100644 --- a/grafana-plugin/src/utils/consts.ts +++ b/grafana-plugin/src/helpers/consts.ts @@ -1,4 +1,4 @@ -import { OnCallAppPluginMeta } from 'types'; +import { OnCallAppPluginMeta } from 'app-types'; //@ts-ignore import plugin from '../../package.json'; // eslint-disable-line diff --git a/grafana-plugin/src/utils/datetime.test.ts b/grafana-plugin/src/helpers/datetime.test.ts similarity index 100% rename from grafana-plugin/src/utils/datetime.test.ts rename to grafana-plugin/src/helpers/datetime.test.ts diff --git a/grafana-plugin/src/utils/datetime.ts b/grafana-plugin/src/helpers/datetime.ts similarity index 100% rename from grafana-plugin/src/utils/datetime.ts rename to grafana-plugin/src/helpers/datetime.ts diff --git a/grafana-plugin/src/utils/decorators.ts b/grafana-plugin/src/helpers/decorators.ts similarity index 98% rename from grafana-plugin/src/utils/decorators.ts rename to grafana-plugin/src/helpers/decorators.ts index c1d10a751a..8ebb865fbf 100644 --- a/grafana-plugin/src/utils/decorators.ts +++ b/grafana-plugin/src/helpers/decorators.ts @@ -1,5 +1,6 @@ +import { openErrorNotification, openNotification, openWarningNotification } from 'helpers/helpers'; + import { LoaderStore } from 'models/loader/loader'; -import { openErrorNotification, openNotification, openWarningNotification } from 'utils/utils'; export function AutoLoadingState(actionKey: string) { return function (_target: object, _key: string, descriptor: PropertyDescriptor) { diff --git a/grafana-plugin/src/utils/faro.test.tsx b/grafana-plugin/src/helpers/faro.test.tsx similarity index 97% rename from grafana-plugin/src/utils/faro.test.tsx rename to grafana-plugin/src/helpers/faro.test.tsx index bf0f697699..f9e8a56e9d 100644 --- a/grafana-plugin/src/utils/faro.test.tsx +++ b/grafana-plugin/src/helpers/faro.test.tsx @@ -1,7 +1,6 @@ import 'jest/matchMedia'; import { describe, test } from '@jest/globals'; - -import { FaroHelper } from 'utils/faro'; +import { FaroHelper } from 'helpers/faro'; import '@testing-library/jest-dom'; import { ONCALL_DEV, ONCALL_OPS, ONCALL_PROD } from './consts'; diff --git a/grafana-plugin/src/utils/faro.ts b/grafana-plugin/src/helpers/faro.ts similarity index 100% rename from grafana-plugin/src/utils/faro.ts rename to grafana-plugin/src/helpers/faro.ts diff --git a/grafana-plugin/src/utils/utils.test.ts b/grafana-plugin/src/helpers/helpers.test.ts similarity index 98% rename from grafana-plugin/src/utils/utils.test.ts rename to grafana-plugin/src/helpers/helpers.test.ts index 0accc9164d..bed4aac3a8 100644 --- a/grafana-plugin/src/utils/utils.test.ts +++ b/grafana-plugin/src/helpers/helpers.test.ts @@ -1,6 +1,5 @@ import * as runtime from '@grafana/runtime'; - -import { getGrafanaVersion, isCurrentGrafanaVersionEqualOrGreaterThan } from 'utils/utils'; +import { getGrafanaVersion, isCurrentGrafanaVersionEqualOrGreaterThan } from 'helpers/helpers'; jest.mock('@grafana/runtime', () => ({ config: jest.fn(), diff --git a/grafana-plugin/src/utils/utils.ts b/grafana-plugin/src/helpers/helpers.ts similarity index 100% rename from grafana-plugin/src/utils/utils.ts rename to grafana-plugin/src/helpers/helpers.ts diff --git a/grafana-plugin/src/utils/hoc.tsx b/grafana-plugin/src/helpers/hoc.tsx similarity index 100% rename from grafana-plugin/src/utils/hoc.tsx rename to grafana-plugin/src/helpers/hoc.tsx diff --git a/grafana-plugin/src/utils/hooks.tsx b/grafana-plugin/src/helpers/hooks.tsx similarity index 100% rename from grafana-plugin/src/utils/hooks.tsx rename to grafana-plugin/src/helpers/hooks.tsx diff --git a/grafana-plugin/src/utils/loadJs.ts b/grafana-plugin/src/helpers/loadJs.ts similarity index 100% rename from grafana-plugin/src/utils/loadJs.ts rename to grafana-plugin/src/helpers/loadJs.ts diff --git a/grafana-plugin/src/utils/localStorage.ts b/grafana-plugin/src/helpers/localStorage.ts similarity index 100% rename from grafana-plugin/src/utils/localStorage.ts rename to grafana-plugin/src/helpers/localStorage.ts diff --git a/grafana-plugin/src/utils/sanitize.ts b/grafana-plugin/src/helpers/sanitize.ts similarity index 100% rename from grafana-plugin/src/utils/sanitize.ts rename to grafana-plugin/src/helpers/sanitize.ts diff --git a/grafana-plugin/src/utils/string.ts b/grafana-plugin/src/helpers/string.ts similarity index 100% rename from grafana-plugin/src/utils/string.ts rename to grafana-plugin/src/helpers/string.ts diff --git a/grafana-plugin/src/utils/styles.ts b/grafana-plugin/src/helpers/styles.ts similarity index 100% rename from grafana-plugin/src/utils/styles.ts rename to grafana-plugin/src/helpers/styles.ts diff --git a/grafana-plugin/src/utils/types.ts b/grafana-plugin/src/helpers/types.ts similarity index 100% rename from grafana-plugin/src/utils/types.ts rename to grafana-plugin/src/helpers/types.ts diff --git a/grafana-plugin/src/utils/url.ts b/grafana-plugin/src/helpers/url.ts similarity index 100% rename from grafana-plugin/src/utils/url.ts rename to grafana-plugin/src/helpers/url.ts diff --git a/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.helpers.ts b/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.helpers.ts index c920b0090c..298fb56432 100644 --- a/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.helpers.ts +++ b/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.helpers.ts @@ -1,3 +1,7 @@ +import { AutoLoadingState, WithGlobalNotification } from 'helpers/decorators'; +import { showApiError } from 'helpers/helpers'; +import { OmitReadonlyMembers } from 'helpers/types'; + import { ChannelFilter } from 'models/channel_filter/channel_filter.types'; import { GrafanaTeam } from 'models/grafana_team/grafana_team.types'; import { ActionKey } from 'models/loader/action-keys'; @@ -5,9 +9,6 @@ import { makeRequest } from 'network/network'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { onCallApi } from 'network/oncall-api/http-client'; import { SelectOption } from 'state/types'; -import { AutoLoadingState, WithGlobalNotification } from 'utils/decorators'; -import { OmitReadonlyMembers } from 'utils/types'; -import { showApiError } from 'utils/utils'; import { AlertReceiveChannelStore } from './alert_receive_channel'; import { MaintenanceMode } from './alert_receive_channel.types'; diff --git a/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts b/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts index 4173e8b701..bf17bfb8b0 100644 --- a/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts +++ b/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts @@ -1,3 +1,5 @@ +import { AutoLoadingState, WithGlobalNotification } from 'helpers/decorators'; +import { OmitReadonlyMembers } from 'helpers/types'; import { omit } from 'lodash-es'; import { runInAction, makeAutoObservable } from 'mobx'; @@ -11,8 +13,6 @@ import { operations } from 'network/oncall-api/autogenerated-api.types'; import { onCallApi } from 'network/oncall-api/http-client'; import { move } from 'state/helpers'; import { RootBaseStore } from 'state/rootBaseStore/RootBaseStore'; -import { AutoLoadingState, WithGlobalNotification } from 'utils/decorators'; -import { OmitReadonlyMembers } from 'utils/types'; import { AlertReceiveChannelCounters, ContactPoint } from './alert_receive_channel.types'; diff --git a/grafana-plugin/src/models/alert_receive_channel_connected_channels/alert_receive_channel_connected_channels.ts b/grafana-plugin/src/models/alert_receive_channel_connected_channels/alert_receive_channel_connected_channels.ts index e5e0dab87c..1eaf46e80c 100644 --- a/grafana-plugin/src/models/alert_receive_channel_connected_channels/alert_receive_channel_connected_channels.ts +++ b/grafana-plugin/src/models/alert_receive_channel_connected_channels/alert_receive_channel_connected_channels.ts @@ -1,3 +1,4 @@ +import { AutoLoadingState } from 'helpers/decorators'; import { keyBy } from 'lodash-es'; import { makeAutoObservable, runInAction } from 'mobx'; @@ -5,7 +6,6 @@ import { ActionKey } from 'models/loader/action-keys'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { onCallApi } from 'network/oncall-api/http-client'; import { RootBaseStore } from 'state/rootBaseStore/RootBaseStore'; -import { AutoLoadingState } from 'utils/decorators'; export class AlertReceiveChannelConnectedChannelsStore { rootStore: RootBaseStore; diff --git a/grafana-plugin/src/models/alert_receive_channel_webhooks/alert_receive_channel_webhooks.ts b/grafana-plugin/src/models/alert_receive_channel_webhooks/alert_receive_channel_webhooks.ts index ce01395ae9..9d85ca62cb 100644 --- a/grafana-plugin/src/models/alert_receive_channel_webhooks/alert_receive_channel_webhooks.ts +++ b/grafana-plugin/src/models/alert_receive_channel_webhooks/alert_receive_channel_webhooks.ts @@ -1,11 +1,11 @@ +import { WithGlobalNotification } from 'helpers/decorators'; +import { OmitReadonlyMembers } from 'helpers/types'; import { keyBy } from 'lodash-es'; import { makeAutoObservable, runInAction } from 'mobx'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { onCallApi } from 'network/oncall-api/http-client'; import { RootBaseStore } from 'state/rootBaseStore/RootBaseStore'; -import { WithGlobalNotification } from 'utils/decorators'; -import { OmitReadonlyMembers } from 'utils/types'; export class AlertReceiveChannelWebhooksStore { rootStore: RootBaseStore; diff --git a/grafana-plugin/src/models/alertgroup/alertgroup.helpers.ts b/grafana-plugin/src/models/alertgroup/alertgroup.helpers.ts index 72a9d45000..7fa206a8c2 100644 --- a/grafana-plugin/src/models/alertgroup/alertgroup.helpers.ts +++ b/grafana-plugin/src/models/alertgroup/alertgroup.helpers.ts @@ -1,9 +1,10 @@ +import { AutoLoadingState } from 'helpers/decorators'; + import { ActionKey } from 'models/loader/action-keys'; import { makeRequest } from 'network/network'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { components } from 'network/oncall-api/autogenerated-api.types'; import { onCallApi } from 'network/oncall-api/http-client'; -import { AutoLoadingState } from 'utils/decorators'; import { AlertGroupStore } from './alertgroup'; diff --git a/grafana-plugin/src/models/alertgroup/alertgroup.ts b/grafana-plugin/src/models/alertgroup/alertgroup.ts index bc22ed4bd7..5f3619a182 100644 --- a/grafana-plugin/src/models/alertgroup/alertgroup.ts +++ b/grafana-plugin/src/models/alertgroup/alertgroup.ts @@ -1,3 +1,6 @@ +import { LocationHelper } from 'helpers/LocationHelper'; +import { GENERIC_ERROR, PAGE, PROCESSING_REQUEST_ERROR } from 'helpers/consts'; +import { AutoLoadingState, WithGlobalNotification } from 'helpers/decorators'; import { runInAction, makeAutoObservable } from 'mobx'; import qs from 'query-string'; @@ -8,9 +11,6 @@ import { ApiSchemas } from 'network/oncall-api/api.types'; import { onCallApi } from 'network/oncall-api/http-client'; import { RootStore } from 'state/rootStore'; import { SelectOption } from 'state/types'; -import { LocationHelper } from 'utils/LocationHelper'; -import { GENERIC_ERROR, PAGE, PROCESSING_REQUEST_ERROR } from 'utils/consts'; -import { AutoLoadingState, WithGlobalNotification } from 'utils/decorators'; import { AlertGroupHelper } from './alertgroup.helpers'; import { AlertGroupColumn, AlertAction, IncidentStatus } from './alertgroup.types'; diff --git a/grafana-plugin/src/models/base_store.ts b/grafana-plugin/src/models/base_store.ts index b3dc76fe00..47b036b6f1 100644 --- a/grafana-plugin/src/models/base_store.ts +++ b/grafana-plugin/src/models/base_store.ts @@ -1,9 +1,9 @@ import { sentenceCase } from 'change-case'; +import { openWarningNotification } from 'helpers/helpers'; import { action } from 'mobx'; import { makeRequest } from 'network/network'; import { RootStore } from 'state/rootStore'; -import { openWarningNotification } from 'utils/utils'; export class BaseStore { protected rootStore: RootStore; diff --git a/grafana-plugin/src/models/filters/filters.helpers.ts b/grafana-plugin/src/models/filters/filters.helpers.ts index 531055a6b9..f4ffc7908e 100644 --- a/grafana-plugin/src/models/filters/filters.helpers.ts +++ b/grafana-plugin/src/models/filters/filters.helpers.ts @@ -1,4 +1,4 @@ -import { convertRelativeToAbsoluteDate } from 'utils/datetime'; +import { convertRelativeToAbsoluteDate } from 'helpers/datetime'; import { FilterOption, FiltersValues } from './filters.types'; diff --git a/grafana-plugin/src/models/filters/filters.ts b/grafana-plugin/src/models/filters/filters.ts index dd452dcb64..4bbdf3c17e 100644 --- a/grafana-plugin/src/models/filters/filters.ts +++ b/grafana-plugin/src/models/filters/filters.ts @@ -1,12 +1,12 @@ +import { LocationHelper } from 'helpers/LocationHelper'; +import { PAGE } from 'helpers/consts'; +import { getItem, setItem } from 'helpers/localStorage'; import { action, observable, makeObservable, runInAction } from 'mobx'; import { BaseStore } from 'models/base_store'; import { LabelKeyValue } from 'models/label/label.types'; import { makeRequest } from 'network/network'; import { RootStore } from 'state/rootStore'; -import { LocationHelper } from 'utils/LocationHelper'; -import { PAGE } from 'utils/consts'; -import { getItem, setItem } from 'utils/localStorage'; import { getApiPathByPage } from './filters.helpers'; import { FilterOption, FiltersValues } from './filters.types'; diff --git a/grafana-plugin/src/models/heartbeat/heartbeat.ts b/grafana-plugin/src/models/heartbeat/heartbeat.ts index a102973ada..987a214a07 100644 --- a/grafana-plugin/src/models/heartbeat/heartbeat.ts +++ b/grafana-plugin/src/models/heartbeat/heartbeat.ts @@ -1,10 +1,10 @@ +import { WithGlobalNotification } from 'helpers/decorators'; import { action, observable, makeObservable, runInAction } from 'mobx'; import { BaseStore } from 'models/base_store'; import { makeRequest } from 'network/network'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { RootStore } from 'state/rootStore'; -import { WithGlobalNotification } from 'utils/decorators'; import { Heartbeat } from './heartbeat.types'; diff --git a/grafana-plugin/src/models/label/label.ts b/grafana-plugin/src/models/label/label.ts index 7559632ac8..d723626627 100644 --- a/grafana-plugin/src/models/label/label.ts +++ b/grafana-plugin/src/models/label/label.ts @@ -1,3 +1,5 @@ +import { PROCESSING_REQUEST_ERROR } from 'helpers/consts'; +import { WithGlobalNotification } from 'helpers/decorators'; import { action, makeObservable } from 'mobx'; import { BaseStore } from 'models/base_store'; @@ -6,8 +8,6 @@ import { ApiSchemas } from 'network/oncall-api/api.types'; import { components } from 'network/oncall-api/autogenerated-api.types'; import { onCallApi } from 'network/oncall-api/http-client'; import { RootStore } from 'state/rootStore'; -import { PROCESSING_REQUEST_ERROR } from 'utils/consts'; -import { WithGlobalNotification } from 'utils/decorators'; export class LabelStore extends BaseStore { constructor(rootStore: RootStore) { diff --git a/grafana-plugin/src/models/outgoing_webhook/outgoing_webhook.types.ts b/grafana-plugin/src/models/outgoing_webhook/outgoing_webhook.types.ts index a063bb1066..661404ef5b 100644 --- a/grafana-plugin/src/models/outgoing_webhook/outgoing_webhook.types.ts +++ b/grafana-plugin/src/models/outgoing_webhook/outgoing_webhook.types.ts @@ -1,4 +1,4 @@ -import { KeyValuePair } from 'utils/utils'; +import { KeyValuePair } from 'helpers/helpers'; export interface OutgoingWebhookResponse { timestamp: string; diff --git a/grafana-plugin/src/models/plugin/plugin.ts b/grafana-plugin/src/models/plugin/plugin.ts index c8ffe32d60..db18373973 100644 --- a/grafana-plugin/src/models/plugin/plugin.ts +++ b/grafana-plugin/src/models/plugin/plugin.ts @@ -1,14 +1,14 @@ +import { OnCallPluginMetaJSONData } from 'app-types'; +import { waitInMs } from 'helpers/async'; +import { AutoLoadingState } from 'helpers/decorators'; import { isEqual } from 'lodash-es'; import { makeAutoObservable, runInAction } from 'mobx'; -import { OnCallPluginMetaJSONData } from 'types'; import { ActionKey } from 'models/loader/action-keys'; import { GrafanaApiClient } from 'network/grafana-api/http-client'; import { makeRequest } from 'network/network'; import { PluginConnection, StatusResponse } from 'network/oncall-api/api.types'; import { RootBaseStore } from 'state/rootBaseStore/RootBaseStore'; -import { waitInMs } from 'utils/async'; -import { AutoLoadingState } from 'utils/decorators'; import { PluginHelper } from './plugin.helper'; diff --git a/grafana-plugin/src/models/schedule/schedule.ts b/grafana-plugin/src/models/schedule/schedule.ts index d44469042f..520338a77a 100644 --- a/grafana-plugin/src/models/schedule/schedule.ts +++ b/grafana-plugin/src/models/schedule/schedule.ts @@ -1,4 +1,5 @@ import dayjs from 'dayjs'; +import { AutoLoadingState } from 'helpers/decorators'; import { action, makeObservable, observable, runInAction } from 'mobx'; import { PageErrorData } from 'components/PageErrorHandlingWrapper/PageErrorHandlingWrapper'; @@ -11,7 +12,6 @@ import { makeRequest } from 'network/network'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { RootStore } from 'state/rootStore'; import { SelectOption } from 'state/types'; -import { AutoLoadingState } from 'utils/decorators'; import { createShiftSwapEventFromShiftSwap, diff --git a/grafana-plugin/src/models/slack/slack.ts b/grafana-plugin/src/models/slack/slack.ts index 6a4b6ce82d..20aa19689b 100644 --- a/grafana-plugin/src/models/slack/slack.ts +++ b/grafana-plugin/src/models/slack/slack.ts @@ -1,11 +1,11 @@ +import { GENERIC_ERROR } from 'helpers/consts'; +import { openErrorNotification } from 'helpers/helpers'; import { action, observable, makeObservable, runInAction } from 'mobx'; import { BaseStore } from 'models/base_store'; import { SlackChannel } from 'models/slack_channel/slack_channel.types'; import { makeRequest, makeRequestRaw } from 'network/network'; import { RootStore } from 'state/rootStore'; -import { GENERIC_ERROR } from 'utils/consts'; -import { openErrorNotification } from 'utils/utils'; import { SlackSettings } from './slack.types'; diff --git a/grafana-plugin/src/models/user/user.helpers.tsx b/grafana-plugin/src/models/user/user.helpers.tsx index fecb1db2c6..7f02e2cf70 100644 --- a/grafana-plugin/src/models/user/user.helpers.tsx +++ b/grafana-plugin/src/models/user/user.helpers.tsx @@ -1,10 +1,10 @@ import React from 'react'; +import { throttlingError } from 'helpers/helpers'; import { pick } from 'lodash-es'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { onCallApi } from 'network/oncall-api/http-client'; -import { throttlingError } from 'utils/utils'; import { UserStore } from './user'; diff --git a/grafana-plugin/src/models/user/user.ts b/grafana-plugin/src/models/user/user.ts index d18f4d7762..7f905ea728 100644 --- a/grafana-plugin/src/models/user/user.ts +++ b/grafana-plugin/src/models/user/user.ts @@ -1,5 +1,7 @@ import { config } from '@grafana/runtime'; import dayjs from 'dayjs'; +import { isUserActionAllowed, UserActions } from 'helpers/authorization/authorization'; +import { AutoLoadingState } from 'helpers/decorators'; import { get } from 'lodash-es'; import { action, computed, runInAction, makeAutoObservable } from 'mobx'; @@ -10,8 +12,6 @@ import { ApiSchemas } from 'network/oncall-api/api.types'; import { onCallApi } from 'network/oncall-api/http-client'; import { move } from 'state/helpers'; import { RootStore } from 'state/rootStore'; -import { isUserActionAllowed, UserActions } from 'utils/authorization/authorization'; -import { AutoLoadingState } from 'utils/decorators'; import { UserHelper } from './user.helpers'; diff --git a/grafana-plugin/src/module.ts b/grafana-plugin/src/module.ts index 8a36c224f9..f73db2ac20 100644 --- a/grafana-plugin/src/module.ts +++ b/grafana-plugin/src/module.ts @@ -1,14 +1,14 @@ import { ComponentClass } from 'react'; import { AppPlugin, PluginExtensionPoints } from '@grafana/data'; +import { IRM_TAB } from 'helpers/consts'; +import { isCurrentGrafanaVersionEqualOrGreaterThan } from 'helpers/helpers'; import { MobileAppConnectionWrapper } from 'containers/MobileAppConnection/MobileAppConnection'; import { PluginConfigPage } from 'containers/PluginConfigPage/PluginConfigPage'; import { GrafanaPluginRootPage } from 'plugin/GrafanaPluginRootPage'; -import { IRM_TAB } from 'utils/consts'; -import { isCurrentGrafanaVersionEqualOrGreaterThan } from 'utils/utils'; -import { OnCallPluginConfigPageProps, OnCallPluginMetaJSONData } from './types'; +import { OnCallPluginConfigPageProps, OnCallPluginMetaJSONData } from './app-types'; const plugin = new AppPlugin().setRootPage(GrafanaPluginRootPage).addConfigPage({ title: 'Configuration', diff --git a/grafana-plugin/src/navbar/Header/Header.tsx b/grafana-plugin/src/navbar/Header/Header.tsx index e501e06c56..d69837c8d6 100644 --- a/grafana-plugin/src/navbar/Header/Header.tsx +++ b/grafana-plugin/src/navbar/Header/Header.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { cx } from '@emotion/css'; import { Card, Stack, useStyles2 } from '@grafana/ui'; +import { APP_SUBTITLE } from 'helpers/consts'; import { observer } from 'mobx-react'; import gitHubStarSVG from 'assets/img/github_star.svg'; @@ -9,7 +10,6 @@ import logo from 'assets/img/logo.svg'; import { Alerts } from 'containers/Alerts/Alerts'; import { isTopNavbar } from 'plugin/GrafanaPluginRootPage.helpers'; import { useStore } from 'state/useStore'; -import { APP_SUBTITLE } from 'utils/consts'; import { getHeaderStyles } from './Header.styles'; diff --git a/grafana-plugin/src/network/grafana-api/api.types.d.ts b/grafana-plugin/src/network/grafana-api/api.types.d.ts index 918bef67b6..fcc75ba8a0 100644 --- a/grafana-plugin/src/network/grafana-api/api.types.d.ts +++ b/grafana-plugin/src/network/grafana-api/api.types.d.ts @@ -1,4 +1,4 @@ -import { OnCallPluginMetaJSONData, OnCallPluginMetaSecureJSONData } from 'types'; +import { OnCallPluginMetaJSONData, OnCallPluginMetaSecureJSONData } from 'app-types'; export type ServiceAccountDTO = { description: string; diff --git a/grafana-plugin/src/network/grafana-api/http-client.ts b/grafana-plugin/src/network/grafana-api/http-client.ts index e4173c3303..44a9ead325 100644 --- a/grafana-plugin/src/network/grafana-api/http-client.ts +++ b/grafana-plugin/src/network/grafana-api/http-client.ts @@ -1,7 +1,6 @@ import { getBackendSrv } from '@grafana/runtime'; -import { OnCallPluginMetaJSONData } from 'types'; - -import { getPluginId } from 'utils/consts'; +import { OnCallPluginMetaJSONData } from 'app-types'; +import { getPluginId } from 'helpers/consts'; import { ApiAuthKeyDTO, diff --git a/grafana-plugin/src/network/network.ts b/grafana-plugin/src/network/network.ts index f111126f17..6f92684548 100644 --- a/grafana-plugin/src/network/network.ts +++ b/grafana-plugin/src/network/network.ts @@ -1,10 +1,9 @@ import axios, { AxiosError } from 'axios'; +import { getOnCallApiPath } from 'helpers/consts'; +import { FaroHelper } from 'helpers/faro'; +import { safeJSONStringify } from 'helpers/string'; import qs from 'query-string'; -import { getOnCallApiPath } from 'utils/consts'; -import { FaroHelper } from 'utils/faro'; -import { safeJSONStringify } from 'utils/string'; - const instance = axios.create(); instance.interceptors.request.use(function (config) { diff --git a/grafana-plugin/src/network/oncall-api/http-client.test.ts b/grafana-plugin/src/network/oncall-api/http-client.test.ts index fda1430220..fd910e0730 100644 --- a/grafana-plugin/src/network/oncall-api/http-client.test.ts +++ b/grafana-plugin/src/network/oncall-api/http-client.test.ts @@ -1,8 +1,8 @@ -import { FaroHelper } from 'utils/faro'; +import { FaroHelper } from 'helpers/faro'; import { getCustomFetchFn } from './http-client'; -jest.mock('utils/faro', () => ({ +jest.mock('helpers/faro', () => ({ __esModule: true, FaroHelper: { diff --git a/grafana-plugin/src/network/oncall-api/http-client.ts b/grafana-plugin/src/network/oncall-api/http-client.ts index ab08d3ac76..795718f2bf 100644 --- a/grafana-plugin/src/network/oncall-api/http-client.ts +++ b/grafana-plugin/src/network/oncall-api/http-client.ts @@ -1,11 +1,10 @@ +import { getOnCallApiPath } from 'helpers/consts'; +import { FaroHelper } from 'helpers/faro'; +import { formatBackendError, openErrorNotification } from 'helpers/helpers'; +import { safeJSONStringify } from 'helpers/string'; import createClient from 'openapi-fetch'; import qs from 'query-string'; -import { getOnCallApiPath } from 'utils/consts'; -import { FaroHelper } from 'utils/faro'; -import { safeJSONStringify } from 'utils/string'; -import { formatBackendError, openErrorNotification } from 'utils/utils'; - import { paths } from './autogenerated-api.types'; const showApiError = (status: number, errorData: string | Record) => { diff --git a/grafana-plugin/src/pages/NoMatch.tsx b/grafana-plugin/src/pages/NoMatch.tsx index 414c9fd0ac..fc861c9baf 100644 --- a/grafana-plugin/src/pages/NoMatch.tsx +++ b/grafana-plugin/src/pages/NoMatch.tsx @@ -1,11 +1,10 @@ import React, { useEffect, useMemo } from 'react'; +import { DEFAULT_PAGE, PLUGIN_ROOT } from 'helpers/consts'; +import { getPathFromQueryParams } from 'helpers/url'; import qs from 'query-string'; import { useNavigate } from 'react-router-dom-v5-compat'; -import { DEFAULT_PAGE, PLUGIN_ROOT } from 'utils/consts'; -import { getPathFromQueryParams } from 'utils/url'; - export const NoMatch = () => { const navigate = useNavigate(); diff --git a/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx b/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx index ee13ac1bc7..883fefe808 100644 --- a/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx +++ b/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx @@ -2,6 +2,9 @@ import React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, Icon, IconButton, Tooltip, Stack, withTheme2 } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { PAGE, PLUGIN_ROOT, StackSize } from 'helpers/consts'; +import { PropsWithRouter, withRouter } from 'helpers/hoc'; import { observer } from 'mobx-react'; import { getUtilStyles } from 'styles/utils.styles'; @@ -27,9 +30,6 @@ import { EscalationChain } from 'models/escalation_chain/escalation_chain.types' import { FiltersValues } from 'models/filters/filters.types'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { PAGE, PLUGIN_ROOT, StackSize } from 'utils/consts'; -import { PropsWithRouter, withRouter } from 'utils/hoc'; import { getEscalationChainStyles } from './EscalationChains.styles'; diff --git a/grafana-plugin/src/pages/incident/Incident.helpers.tsx b/grafana-plugin/src/pages/incident/Incident.helpers.tsx index a3925429a1..b52ba753b5 100644 --- a/grafana-plugin/src/pages/incident/Incident.helpers.tsx +++ b/grafana-plugin/src/pages/incident/Incident.helpers.tsx @@ -2,6 +2,8 @@ import React from 'react'; import { css, cx } from '@emotion/css'; import { Button, IconButton, Tooltip, Stack, useStyles2 } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; import { getUtilStyles } from 'styles/utils.styles'; import { Avatar } from 'components/Avatar/Avatar'; @@ -13,8 +15,6 @@ import { IncidentStatus } from 'models/alertgroup/alertgroup.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { SilenceSelect } from 'pages/incidents/parts/SilenceSelect'; import { move } from 'state/helpers'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; export const IncidentRelatedUsers = (props: { incident: ApiSchemas['AlertGroup']; isFull: boolean }) => { const { incident, isFull } = props; diff --git a/grafana-plugin/src/pages/incident/Incident.tsx b/grafana-plugin/src/pages/incident/Incident.tsx index aa4a577298..bedd93687b 100644 --- a/grafana-plugin/src/pages/incident/Incident.tsx +++ b/grafana-plugin/src/pages/incident/Incident.tsx @@ -19,13 +19,19 @@ import { withTheme2, useStyles2, } from '@grafana/ui'; +import { OnCallPluginExtensionPoints } from 'app-types'; +import { UserActions } from 'helpers/authorization/authorization'; +import { INTEGRATION_SERVICENOW, PLUGIN_ROOT, StackSize } from 'helpers/consts'; +import { openNotification } from 'helpers/helpers'; +import { PropsWithRouter, withRouter } from 'helpers/hoc'; +import { sanitize } from 'helpers/sanitize'; +import { parseURL } from 'helpers/url'; import Linkify from 'linkify-react'; import { observer } from 'mobx-react'; import moment from 'moment-timezone'; import CopyToClipboard from 'react-copy-to-clipboard'; import Emoji from 'react-emoji-render'; import reactStringReplace from 'react-string-replace'; -import { OnCallPluginExtensionPoints } from 'types'; import { Collapse } from 'components/Collapse/Collapse'; import { ExtensionLinkDropdown } from 'components/ExtensionLinkMenu/ExtensionLinkDropdown'; @@ -59,12 +65,6 @@ import { AppFeature } from 'state/features'; import { PageProps, WithStoreProps } from 'state/types'; import { useStore } from 'state/useStore'; import { withMobXProviderContext } from 'state/withStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { INTEGRATION_SERVICENOW, PLUGIN_ROOT, StackSize } from 'utils/consts'; -import { PropsWithRouter, withRouter } from 'utils/hoc'; -import { sanitize } from 'utils/sanitize'; -import { parseURL } from 'utils/url'; -import { openNotification } from 'utils/utils'; import { getActionButtons } from './Incident.helpers'; import { getIncidentStyles } from './Incident.styles'; diff --git a/grafana-plugin/src/pages/incidents/Incidents.tsx b/grafana-plugin/src/pages/incidents/Incidents.tsx index 0460a460c8..7507aac66d 100644 --- a/grafana-plugin/src/pages/incidents/Incidents.tsx +++ b/grafana-plugin/src/pages/incidents/Incidents.tsx @@ -4,6 +4,12 @@ import { cx } from '@emotion/css'; import { GrafanaTheme2, durationToMilliseconds, parseDuration, SelectableValue } from '@grafana/data'; import { LabelTag } from '@grafana/labels'; import { Button, Icon, RadioButtonGroup, RefreshPicker, Tooltip, Stack, withTheme2 } from '@grafana/ui'; +import { LocationHelper } from 'helpers/LocationHelper'; +import { UserActions } from 'helpers/authorization/authorization'; +import { INCIDENT_HORIZONTAL_SCROLLING_STORAGE, PAGE, PLUGIN_ROOT, StackSize } from 'helpers/consts'; +import { PropsWithRouter, withRouter } from 'helpers/hoc'; +import { getItem, setItem } from 'helpers/localStorage'; +import { TableColumn } from 'helpers/types'; import { capitalize } from 'lodash-es'; import { observer } from 'mobx-react'; import moment from 'moment-timezone'; @@ -43,12 +49,6 @@ import { AppFeature } from 'state/features'; import { RootStore } from 'state/rootStore'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { LocationHelper } from 'utils/LocationHelper'; -import { UserActions } from 'utils/authorization/authorization'; -import { INCIDENT_HORIZONTAL_SCROLLING_STORAGE, PAGE, PLUGIN_ROOT, StackSize } from 'utils/consts'; -import { PropsWithRouter, withRouter } from 'utils/hoc'; -import { getItem, setItem } from 'utils/localStorage'; -import { TableColumn } from 'utils/types'; import { getIncidentsStyles } from './Incidents.styles'; import { IncidentDropdown } from './parts/IncidentDropdown'; diff --git a/grafana-plugin/src/pages/incidents/parts/IncidentDropdown.tsx b/grafana-plugin/src/pages/incidents/parts/IncidentDropdown.tsx index 66580eadeb..070b77b473 100644 --- a/grafana-plugin/src/pages/incidents/parts/IncidentDropdown.tsx +++ b/grafana-plugin/src/pages/incidents/parts/IncidentDropdown.tsx @@ -3,6 +3,7 @@ import React, { FC, SyntheticEvent, useRef, useState } from 'react'; import { cx } from '@emotion/css'; import { intervalToAbbreviatedDurationString } from '@grafana/data'; import { Icon, LoadingPlaceholder, Tooltip, useStyles2 } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; import { getUtilStyles } from 'styles/utils.styles'; import { CUSTOM_SILENCE_VALUE } from 'components/Policy/Policy.consts'; @@ -12,7 +13,6 @@ import { WithContextMenu } from 'components/WithContextMenu/WithContextMenu'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; import { AlertAction, IncidentStatus } from 'models/alertgroup/alertgroup.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; -import { UserActions } from 'utils/authorization/authorization'; import { getIncidentDropdownStyles } from './IncidentDropdown.styles'; import { IncidentSilenceModal } from './IncidentSilenceModal'; diff --git a/grafana-plugin/src/pages/incidents/parts/IncidentSilenceModal.tsx b/grafana-plugin/src/pages/incidents/parts/IncidentSilenceModal.tsx index 0a046057d0..d38532e43a 100644 --- a/grafana-plugin/src/pages/incidents/parts/IncidentSilenceModal.tsx +++ b/grafana-plugin/src/pages/incidents/parts/IncidentSilenceModal.tsx @@ -11,12 +11,12 @@ import { parseDuration, } from '@grafana/data'; import { Button, DateTimePicker, Field, Input, Modal, Stack, useStyles2 } from '@grafana/ui'; +import { openWarningNotification } from 'helpers/helpers'; +import { useDebouncedCallback } from 'helpers/hooks'; import { Controller, useForm } from 'react-hook-form'; import { bem, getUtilStyles } from 'styles/utils.styles'; import { Text } from 'components/Text/Text'; -import { useDebouncedCallback } from 'utils/hooks'; -import { openWarningNotification } from 'utils/utils'; interface IncidentSilenceModalProps { isOpen: boolean; diff --git a/grafana-plugin/src/pages/incidents/parts/SilenceSelect.tsx b/grafana-plugin/src/pages/incidents/parts/SilenceSelect.tsx index 4f0c0b15a1..57d26a4dab 100644 --- a/grafana-plugin/src/pages/incidents/parts/SilenceSelect.tsx +++ b/grafana-plugin/src/pages/incidents/parts/SilenceSelect.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { Select } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; import { observer } from 'mobx-react'; import { SILENCE_DURATION_LIST } from 'components/Policy/Policy.consts'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; -import { UserActions } from 'utils/authorization/authorization'; interface SilenceSelectProps { placeholder?: string; diff --git a/grafana-plugin/src/pages/insights/Insights.tsx b/grafana-plugin/src/pages/insights/Insights.tsx index 3657834f9e..b40cc735dd 100644 --- a/grafana-plugin/src/pages/insights/Insights.tsx +++ b/grafana-plugin/src/pages/insights/Insights.tsx @@ -15,13 +15,13 @@ import { useSceneApp, } from '@grafana/scenes'; import { Alert, LoadingPlaceholder, Stack } from '@grafana/ui'; +import { DOCS_ROOT, PLUGIN_ROOT, StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { Text } from 'components/Text/Text'; import { Tutorial } from 'components/Tutorial/Tutorial'; import { TutorialStep } from 'components/Tutorial/Tutorial.types'; import { useStore } from 'state/useStore'; -import { DOCS_ROOT, PLUGIN_ROOT, StackSize } from 'utils/consts'; import { useAlertCreationChecker } from './Insights.hooks'; import styles from './Insights.module.scss'; diff --git a/grafana-plugin/src/pages/integration/Integration.helper.ts b/grafana-plugin/src/pages/integration/Integration.helper.ts index 042b2606d2..0812cc8ff5 100644 --- a/grafana-plugin/src/pages/integration/Integration.helper.ts +++ b/grafana-plugin/src/pages/integration/Integration.helper.ts @@ -1,12 +1,12 @@ import { IconName } from '@grafana/ui'; import dayjs from 'dayjs'; +import { INTEGRATION_SERVICENOW } from 'helpers/consts'; import { MaintenanceMode } from 'models/alert_receive_channel/alert_receive_channel.types'; import { ChannelFilter } from 'models/channel_filter/channel_filter.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { AppFeature } from 'state/features'; import { RootStore } from 'state/rootStore'; -import { INTEGRATION_SERVICENOW } from 'utils/consts'; import { MAX_CHARACTERS_COUNT, TEXTAREA_ROWS_COUNT } from './IntegrationCommon.config'; diff --git a/grafana-plugin/src/pages/integration/Integration.tsx b/grafana-plugin/src/pages/integration/Integration.tsx index f4b87ec264..33ea7b1eda 100644 --- a/grafana-plugin/src/pages/integration/Integration.tsx +++ b/grafana-plugin/src/pages/integration/Integration.tsx @@ -4,6 +4,13 @@ import { GrafanaTheme2 } from '@grafana/data'; import { LabelTag } from '@grafana/labels'; import { Button, Stack, LoadingPlaceholder, IconButton, Drawer, Alert } from '@grafana/ui'; import cn from 'classnames/bind'; +import { LocationHelper } from 'helpers/LocationHelper'; +import { UserActions } from 'helpers/authorization/authorization'; +import { INTEGRATION_SERVICENOW, PLUGIN_ROOT, StackSize } from 'helpers/consts'; +import { openNotification, openErrorNotification } from 'helpers/helpers'; +import { PropsWithRouter, withDrawer, withRouter } from 'helpers/hoc'; +import { getItem, setItem } from 'helpers/localStorage'; +import { sanitize } from 'helpers/sanitize'; import { get } from 'lodash-es'; import { observer } from 'mobx-react'; import moment from 'moment-timezone'; @@ -43,13 +50,6 @@ import { AppFeature } from 'state/features'; import { PageProps, SelectOption, WithDrawerConfig, WithStoreProps } from 'state/types'; import { useStore } from 'state/useStore'; import { withMobXProviderContext } from 'state/withStore'; -import { LocationHelper } from 'utils/LocationHelper'; -import { UserActions } from 'utils/authorization/authorization'; -import { INTEGRATION_SERVICENOW, PLUGIN_ROOT, StackSize } from 'utils/consts'; -import { PropsWithRouter, withDrawer, withRouter } from 'utils/hoc'; -import { getItem, setItem } from 'utils/localStorage'; -import { sanitize } from 'utils/sanitize'; -import { openNotification, openErrorNotification } from 'utils/utils'; import { IntegrationActions } from './IntegrationActions'; import { OutgoingTab } from './OutgoingTab/OutgoingTab'; diff --git a/grafana-plugin/src/pages/integration/IntegrationActions.tsx b/grafana-plugin/src/pages/integration/IntegrationActions.tsx index d364ded140..115ce7ddd0 100644 --- a/grafana-plugin/src/pages/integration/IntegrationActions.tsx +++ b/grafana-plugin/src/pages/integration/IntegrationActions.tsx @@ -2,6 +2,10 @@ import React, { useEffect, useState } from 'react'; import { Button, ConfirmModal, Icon, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { GENERIC_ERROR, INTEGRATION_SERVICENOW, PLUGIN_ROOT, StackSize } from 'helpers/consts'; +import { openErrorNotification, openNotification } from 'helpers/helpers'; +import { useDrawer } from 'helpers/hooks'; import CopyToClipboard from 'react-copy-to-clipboard'; import Emoji from 'react-emoji-render'; import { useNavigate } from 'react-router-dom-v5-compat'; @@ -21,10 +25,6 @@ import { ApiSchemas } from 'network/oncall-api/api.types'; import styles from 'pages/integration/Integration.module.scss'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { GENERIC_ERROR, INTEGRATION_SERVICENOW, PLUGIN_ROOT, StackSize } from 'utils/consts'; -import { useDrawer } from 'utils/hooks'; -import { openErrorNotification, openNotification } from 'utils/utils'; import { IntegrationDrawerKey } from './Integration'; import { getIsBidirectionalIntegration } from './Integration.helper'; diff --git a/grafana-plugin/src/pages/integration/IntegrationCommon.config.ts b/grafana-plugin/src/pages/integration/IntegrationCommon.config.ts index d56bfa57c8..e794eaf620 100644 --- a/grafana-plugin/src/pages/integration/IntegrationCommon.config.ts +++ b/grafana-plugin/src/pages/integration/IntegrationCommon.config.ts @@ -1,4 +1,4 @@ -import { KeyValuePair } from 'utils/utils'; +import { KeyValuePair } from 'helpers/helpers'; export const TEXTAREA_ROWS_COUNT = 4; export const MAX_CHARACTERS_COUNT = 50; diff --git a/grafana-plugin/src/pages/integration/OutgoingTab/ConnectIntegrationModal.tsx b/grafana-plugin/src/pages/integration/OutgoingTab/ConnectIntegrationModal.tsx index de89b5ddee..7e144f66b2 100644 --- a/grafana-plugin/src/pages/integration/OutgoingTab/ConnectIntegrationModal.tsx +++ b/grafana-plugin/src/pages/integration/OutgoingTab/ConnectIntegrationModal.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'; import { Button, Icon, Input, Modal, Stack, useStyles2 } from '@grafana/ui'; import cn from 'classnames'; +import { useCommonStyles, useIsLoading } from 'helpers/hooks'; import { debounce } from 'lodash-es'; import { observer } from 'mobx-react'; @@ -10,7 +11,6 @@ import { AlertReceiveChannelHelper } from 'models/alert_receive_channel/alert_re import { ActionKey } from 'models/loader/action-keys'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { useCommonStyles, useIsLoading } from 'utils/hooks'; import ConnectedIntegrationsTable from './ConnectedIntegrationsTable'; import { useCurrentIntegration } from './OutgoingTab.hooks'; diff --git a/grafana-plugin/src/pages/integration/OutgoingTab/ConnectedIntegrationsTable.tsx b/grafana-plugin/src/pages/integration/OutgoingTab/ConnectedIntegrationsTable.tsx index 691d5b02be..aa71a6fac7 100644 --- a/grafana-plugin/src/pages/integration/OutgoingTab/ConnectedIntegrationsTable.tsx +++ b/grafana-plugin/src/pages/integration/OutgoingTab/ConnectedIntegrationsTable.tsx @@ -1,6 +1,8 @@ import React, { FC } from 'react'; import { Tooltip, Icon, useStyles2, IconButton, Switch, Checkbox, ConfirmModal, useTheme2, Stack } from '@grafana/ui'; +import { PLUGIN_ROOT } from 'helpers/consts'; +import { useConfirmModal } from 'helpers/hooks'; import { observer } from 'mobx-react'; import Emoji from 'react-emoji-render'; @@ -11,8 +13,6 @@ import { AlertReceiveChannelHelper } from 'models/alert_receive_channel/alert_re import { ApiSchemas } from 'network/oncall-api/api.types'; import { useIntegrationTokenCheck } from 'pages/integration/Integration.hooks'; import { useStore } from 'state/useStore'; -import { PLUGIN_ROOT } from 'utils/consts'; -import { useConfirmModal } from 'utils/hooks'; import { useIntegrationIdFromUrl } from './OutgoingTab.hooks'; import { getStyles } from './OutgoingTab.styles'; diff --git a/grafana-plugin/src/pages/integration/OutgoingTab/NewOutgoingWebhookDrawerContent.tsx b/grafana-plugin/src/pages/integration/OutgoingTab/NewOutgoingWebhookDrawerContent.tsx index 086f522a49..015ace2952 100644 --- a/grafana-plugin/src/pages/integration/OutgoingTab/NewOutgoingWebhookDrawerContent.tsx +++ b/grafana-plugin/src/pages/integration/OutgoingTab/NewOutgoingWebhookDrawerContent.tsx @@ -1,12 +1,12 @@ import React, { FC } from 'react'; import { Button, Stack, useStyles2 } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { useCommonStyles } from 'helpers/hooks'; import { useForm, FormProvider } from 'react-hook-form'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { useCommonStyles } from 'utils/hooks'; import { useIntegrationIdFromUrl } from './OutgoingTab.hooks'; import { getStyles } from './OutgoingTab.styles'; diff --git a/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingTab.hooks.ts b/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingTab.hooks.ts index 90c61c0698..ffcb886251 100644 --- a/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingTab.hooks.ts +++ b/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingTab.hooks.ts @@ -1,7 +1,7 @@ +import { LocationHelper } from 'helpers/LocationHelper'; import { useParams } from 'react-router-dom-v5-compat'; import { useStore } from 'state/useStore'; -import { LocationHelper } from 'utils/LocationHelper'; import { TriggerDetailsQueryStringKey } from './OutgoingTab.types'; diff --git a/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingTab.tsx b/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingTab.tsx index 2efbaa8336..020a5e384c 100644 --- a/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingTab.tsx +++ b/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingTab.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { useStyles2, Input, IconButton, Drawer, Stack } from '@grafana/ui'; +import { useDrawer } from 'helpers/hooks'; import { observer } from 'mobx-react'; import { Button } from 'components/Button/Button'; @@ -9,7 +10,6 @@ import { CopyToClipboardIcon } from 'components/CopyToClipboardIcon/CopyToClipbo import { IntegrationBlock } from 'components/Integrations/IntegrationBlock'; import { IntegrationTag } from 'components/Integrations/IntegrationTag'; import { Text } from 'components/Text/Text'; -import { useDrawer } from 'utils/hooks'; import { NewOutgoingWebhookDrawerContent } from './NewOutgoingWebhookDrawerContent'; import { OtherIntegrations } from './OtherIntegrations'; diff --git a/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingWebhookDetailsDrawerTabs.tsx b/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingWebhookDetailsDrawerTabs.tsx index 25dc51fd86..667b76e74c 100644 --- a/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingWebhookDetailsDrawerTabs.tsx +++ b/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingWebhookDetailsDrawerTabs.tsx @@ -1,6 +1,8 @@ import React, { FC } from 'react'; import { Button, ConfirmModal, useStyles2, Stack } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; +import { useCommonStyles, useConfirmModal } from 'helpers/hooks'; import { observer } from 'mobx-react'; import { useForm, FormProvider } from 'react-hook-form'; @@ -9,8 +11,6 @@ import { Tabs } from 'components/Tabs/Tabs'; import { WebhookLastEventDetails } from 'components/Webhooks/WebhookLastEventDetails'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { useCommonStyles, useConfirmModal } from 'utils/hooks'; import { useDrawerWebhook, useIntegrationIdFromUrl } from './OutgoingTab.hooks'; import { getStyles } from './OutgoingTab.styles'; diff --git a/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingWebhookFormFields.tsx b/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingWebhookFormFields.tsx index 900ac88440..4a8cac51b3 100644 --- a/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingWebhookFormFields.tsx +++ b/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingWebhookFormFields.tsx @@ -2,13 +2,13 @@ import React, { FC, useState } from 'react'; import { Button, Field, Label, Select, Switch, useStyles2, Stack } from '@grafana/ui'; import cn from 'classnames'; +import { StackSize } from 'helpers/consts'; import { Controller, useFormContext } from 'react-hook-form'; import { MonacoEditor } from 'components/MonacoEditor/MonacoEditor'; import { MONACO_EDITABLE_CONFIG, MONACO_READONLY_CONFIG } from 'components/MonacoEditor/MonacoEditor.config'; import { WebhooksTemplateEditor } from 'containers/WebhooksTemplateEditor/WebhooksTemplateEditor'; import { HTTP_METHOD_OPTIONS, WEBHOOK_TRIGGGER_TYPE_OPTIONS } from 'models/outgoing_webhook/outgoing_webhook.types'; -import { StackSize } from 'utils/consts'; import { getStyles } from './OutgoingTab.styles'; import { OutgoingTabFormValues } from './OutgoingTab.types'; diff --git a/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingWebhooksTable.tsx b/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingWebhooksTable.tsx index b040b74938..b8e8d073d4 100644 --- a/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingWebhooksTable.tsx +++ b/grafana-plugin/src/pages/integration/OutgoingTab/OutgoingWebhooksTable.tsx @@ -1,6 +1,11 @@ import React, { FC, ReactElement, useEffect } from 'react'; import { IconButton, Icon, ConfirmModal, useStyles2, Stack } from '@grafana/ui'; +import { LocationHelper } from 'helpers/LocationHelper'; +import { UserActions } from 'helpers/authorization/authorization'; +import { StackSize } from 'helpers/consts'; +import { openNotification } from 'helpers/helpers'; +import { useConfirmModal } from 'helpers/hooks'; import { observer } from 'mobx-react'; import CopyToClipboard from 'react-copy-to-clipboard'; @@ -11,11 +16,6 @@ import { WebhookLastEventTimestamp } from 'components/Webhooks/WebhookLastEventT import { WebhookName } from 'components/Webhooks/WebhookName'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { useStore } from 'state/useStore'; -import { LocationHelper } from 'utils/LocationHelper'; -import { UserActions } from 'utils/authorization/authorization'; -import { StackSize } from 'utils/consts'; -import { useConfirmModal } from 'utils/hooks'; -import { openNotification } from 'utils/utils'; import { useIntegrationIdFromUrl } from './OutgoingTab.hooks'; import { getStyles } from './OutgoingTab.styles'; diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index 8a5e6e4cd8..f1f9a0da5c 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -3,6 +3,11 @@ import React from 'react'; import { cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, Stack, Icon, ConfirmModal, Tooltip, Tab, TabsBar, TabContent, Alert, withTheme2 } from '@grafana/ui'; +import { LocationHelper } from 'helpers/LocationHelper'; +import { UserActions } from 'helpers/authorization/authorization'; +import { PAGE, StackSize, TEXT_ELLIPSIS_CLASS } from 'helpers/consts'; +import { openNotification } from 'helpers/helpers'; +import { PropsWithRouter, withRouter } from 'helpers/hoc'; import { debounce } from 'lodash-es'; import { runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -40,11 +45,6 @@ import { IntegrationHelper } from 'pages/integration/Integration.helper'; import { AppFeature } from 'state/features'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { LocationHelper } from 'utils/LocationHelper'; -import { UserActions } from 'utils/authorization/authorization'; -import { PAGE, StackSize, TEXT_ELLIPSIS_CLASS } from 'utils/consts'; -import { PropsWithRouter, withRouter } from 'utils/hoc'; -import { openNotification } from 'utils/utils'; import { getIntegrationsStyles } from './Integrations.styles'; diff --git a/grafana-plugin/src/pages/outgoing_webhooks/OutgoingWebhooks.tsx b/grafana-plugin/src/pages/outgoing_webhooks/OutgoingWebhooks.tsx index 44baa96f36..39e66c4d33 100644 --- a/grafana-plugin/src/pages/outgoing_webhooks/OutgoingWebhooks.tsx +++ b/grafana-plugin/src/pages/outgoing_webhooks/OutgoingWebhooks.tsx @@ -3,6 +3,10 @@ import React from 'react'; import { css, cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, ConfirmModal, ConfirmModalProps, Icon, IconButton, Stack, withTheme2 } from '@grafana/ui'; +import { isUserActionAllowed, UserActions } from 'helpers/authorization/authorization'; +import { PAGE, PLUGIN_ROOT, StackSize, TEXT_ELLIPSIS_CLASS } from 'helpers/consts'; +import { openErrorNotification, openNotification } from 'helpers/helpers'; +import { PropsWithRouter, withRouter } from 'helpers/hoc'; import { observer } from 'mobx-react'; import { LegacyNavHeading } from 'navbar/LegacyNavHeading'; import CopyToClipboard from 'react-copy-to-clipboard'; @@ -30,10 +34,6 @@ import { ApiSchemas } from 'network/oncall-api/api.types'; import { AppFeature } from 'state/features'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { isUserActionAllowed, UserActions } from 'utils/authorization/authorization'; -import { PAGE, PLUGIN_ROOT, StackSize, TEXT_ELLIPSIS_CLASS } from 'utils/consts'; -import { PropsWithRouter, withRouter } from 'utils/hoc'; -import { openErrorNotification, openNotification } from 'utils/utils'; import { WebhookFormActionType } from './OutgoingWebhooks.types'; diff --git a/grafana-plugin/src/pages/pages.tsx b/grafana-plugin/src/pages/pages.tsx index 980497a4c6..8379ba9b5d 100644 --- a/grafana-plugin/src/pages/pages.tsx +++ b/grafana-plugin/src/pages/pages.tsx @@ -1,11 +1,11 @@ import { NavModelItem } from '@grafana/data'; +import { UserActions, UserAction, isUserActionAllowed } from 'helpers/authorization/authorization'; +import { PLUGIN_ROOT } from 'helpers/consts'; import { matchPath } from 'react-router-dom-v5-compat'; import { isTopNavbar } from 'plugin/GrafanaPluginRootPage.helpers'; import { AppFeature } from 'state/features'; import { RootBaseStore } from 'state/rootBaseStore/RootBaseStore'; -import { UserActions, UserAction, isUserActionAllowed } from 'utils/authorization/authorization'; -import { PLUGIN_ROOT } from 'utils/consts'; export type PageDefinition = { path: string; diff --git a/grafana-plugin/src/pages/schedule/Schedule.tsx b/grafana-plugin/src/pages/schedule/Schedule.tsx index a08a485673..4f5a861a55 100644 --- a/grafana-plugin/src/pages/schedule/Schedule.tsx +++ b/grafana-plugin/src/pages/schedule/Schedule.tsx @@ -16,6 +16,10 @@ import { DatePicker, } from '@grafana/ui'; import dayjs from 'dayjs'; +import { HTML_ID, scrollToElement } from 'helpers/DOM'; +import { isUserActionAllowed, UserActions } from 'helpers/authorization/authorization'; +import { PLUGIN_ROOT, StackSize } from 'helpers/consts'; +import { PropsWithRouter, withRouter } from 'helpers/hoc'; import { observer } from 'mobx-react'; import { PageErrorHandlingWrapper } from 'components/PageErrorHandlingWrapper/PageErrorHandlingWrapper'; @@ -41,10 +45,6 @@ import { Event, Layer, Schedule, ScheduleType, ScheduleView, Shift, ShiftSwap } import { UserHelper } from 'models/user/user.helpers'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { HTML_ID, scrollToElement } from 'utils/DOM'; -import { isUserActionAllowed, UserActions } from 'utils/authorization/authorization'; -import { PLUGIN_ROOT, StackSize } from 'utils/consts'; -import { PropsWithRouter, withRouter } from 'utils/hoc'; import { getCalendarStartDate, diff --git a/grafana-plugin/src/pages/schedules/Schedules.tsx b/grafana-plugin/src/pages/schedules/Schedules.tsx index a0db5d5a20..bc3ff0a6c0 100644 --- a/grafana-plugin/src/pages/schedules/Schedules.tsx +++ b/grafana-plugin/src/pages/schedules/Schedules.tsx @@ -3,6 +3,10 @@ import React, { SyntheticEvent } from 'react'; import { cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, IconButton, LoadingPlaceholder, Stack, withTheme2 } from '@grafana/ui'; +import { LocationHelper } from 'helpers/LocationHelper'; +import { UserActions } from 'helpers/authorization/authorization'; +import { PAGE, PLUGIN_ROOT, StackSize, TEXT_ELLIPSIS_CLASS } from 'helpers/consts'; +import { PropsWithRouter, withRouter } from 'helpers/hoc'; import { observer } from 'mobx-react'; import qs from 'query-string'; import { getUtilStyles } from 'styles/utils.styles'; @@ -27,10 +31,6 @@ import { Schedule, ScheduleView } from 'models/schedule/schedule.types'; import { getSlackChannelName } from 'models/slack_channel/slack_channel.helpers'; import { WithStoreProps, PageProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { LocationHelper } from 'utils/LocationHelper'; -import { UserActions } from 'utils/authorization/authorization'; -import { PAGE, PLUGIN_ROOT, StackSize, TEXT_ELLIPSIS_CLASS } from 'utils/consts'; -import { PropsWithRouter, withRouter } from 'utils/hoc'; import { getSchedulesStyles } from './Schedules.styles'; diff --git a/grafana-plugin/src/pages/settings/SettingsPage.tsx b/grafana-plugin/src/pages/settings/SettingsPage.tsx index 758a84555d..ca13d2ded0 100644 --- a/grafana-plugin/src/pages/settings/SettingsPage.tsx +++ b/grafana-plugin/src/pages/settings/SettingsPage.tsx @@ -3,6 +3,8 @@ import React from 'react'; import { css } from '@emotion/css'; import { AppRootProps } from '@grafana/data'; import { Tab, TabsBar, useStyles2 } from '@grafana/ui'; +import { LocationHelper } from 'helpers/LocationHelper'; +import { isUserActionAllowed, UserActions } from 'helpers/authorization/authorization'; import { observer } from 'mobx-react'; import { ChatOpsPage } from 'pages/settings/tabs/ChatOps/ChatOps'; @@ -11,8 +13,6 @@ import { isTopNavbar } from 'plugin/GrafanaPluginRootPage.helpers'; import { AppFeature } from 'state/features'; import { WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { LocationHelper } from 'utils/LocationHelper'; -import { isUserActionAllowed, UserActions } from 'utils/authorization/authorization'; import { SettingsPageTab } from './SettingsPage.types'; import { CloudPage } from './tabs/Cloud/CloudPage'; diff --git a/grafana-plugin/src/pages/settings/SettingsPage.types.ts b/grafana-plugin/src/pages/settings/SettingsPage.types.ts index 940343d0ec..d88213085c 100644 --- a/grafana-plugin/src/pages/settings/SettingsPage.types.ts +++ b/grafana-plugin/src/pages/settings/SettingsPage.types.ts @@ -1,4 +1,4 @@ -import { KeyValuePair } from 'utils/utils'; +import { KeyValuePair } from 'helpers/helpers'; export const SettingsPageTab = { MainSettings: new KeyValuePair('MainSettings', 'Organization Settings'), diff --git a/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.helpers.ts b/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.helpers.ts index 4313ec5f7a..cbd88fe189 100644 --- a/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.helpers.ts +++ b/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.helpers.ts @@ -1,5 +1,5 @@ -import { LocationHelper } from 'utils/LocationHelper'; -import { openErrorNotification } from 'utils/utils'; +import { LocationHelper } from 'helpers/LocationHelper'; +import { openErrorNotification } from 'helpers/helpers'; export const handleChatOpsQueryParamError = () => { const error = LocationHelper.getQueryParam('error'); diff --git a/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.tsx b/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.tsx index 5ee30c39df..5434d48d6c 100644 --- a/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.tsx +++ b/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { AppRootProps } from '@grafana/data'; import { Alert, Icon, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { LocationHelper } from 'helpers/LocationHelper'; import { observer } from 'mobx-react'; import { VerticalTabsBar, VerticalTab } from 'components/VerticalTabsBar/VerticalTabsBar'; @@ -13,7 +14,6 @@ import { AppFeature } from 'state/features'; import { WithStoreProps } from 'state/types'; import { useStore } from 'state/useStore'; import { withMobXProviderContext } from 'state/withStore'; -import { LocationHelper } from 'utils/LocationHelper'; import { handleChatOpsQueryParamError } from './ChatOps.helpers'; diff --git a/grafana-plugin/src/pages/settings/tabs/ChatOps/tabs/SlackSettings/SlackSettings.tsx b/grafana-plugin/src/pages/settings/tabs/ChatOps/tabs/SlackSettings/SlackSettings.tsx index 4d5227c8e6..e5ea47c625 100644 --- a/grafana-plugin/src/pages/settings/tabs/ChatOps/tabs/SlackSettings/SlackSettings.tsx +++ b/grafana-plugin/src/pages/settings/tabs/ChatOps/tabs/SlackSettings/SlackSettings.tsx @@ -2,6 +2,10 @@ import React, { Component } from 'react'; import { Alert, LoadingPlaceholder, Icon, Button, InlineField, Input, Legend, ConfirmModal, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions } from 'helpers/authorization/authorization'; +import { DOCS_SLACK_SETUP, getPluginId, StackSize } from 'helpers/consts'; +import { showApiError } from 'helpers/helpers'; +import { useConfirmModal } from 'helpers/hooks'; import { observer } from 'mobx-react'; import { Block } from 'components/GBlock/Block'; @@ -19,10 +23,6 @@ import { AppFeature } from 'state/features'; import { WithStoreProps } from 'state/types'; import { useStore } from 'state/useStore'; import { withMobXProviderContext } from 'state/withStore'; -import { UserActions } from 'utils/authorization/authorization'; -import { DOCS_SLACK_SETUP, getPluginId, StackSize } from 'utils/consts'; -import { useConfirmModal } from 'utils/hooks'; -import { showApiError } from 'utils/utils'; import styles from './SlackSettings.module.css'; diff --git a/grafana-plugin/src/pages/settings/tabs/ChatOps/tabs/TelegramSettings/TelegramSettings.tsx b/grafana-plugin/src/pages/settings/tabs/ChatOps/tabs/TelegramSettings/TelegramSettings.tsx index eb7bf4d828..de07ef2c76 100644 --- a/grafana-plugin/src/pages/settings/tabs/ChatOps/tabs/TelegramSettings/TelegramSettings.tsx +++ b/grafana-plugin/src/pages/settings/tabs/ChatOps/tabs/TelegramSettings/TelegramSettings.tsx @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import { Badge, Button, Icon, LoadingPlaceholder, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { DOCS_TELEGRAM_SETUP, StackSize } from 'helpers/consts'; import { observer } from 'mobx-react'; import { Block } from 'components/GBlock/Block'; @@ -15,7 +16,6 @@ import { TelegramChannel } from 'models/telegram_channel/telegram_channel.types' import { AppFeature } from 'state/features'; import { WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { DOCS_TELEGRAM_SETUP, StackSize } from 'utils/consts'; import styles from './TelegramSettings.module.css'; diff --git a/grafana-plugin/src/pages/settings/tabs/Cloud/CloudPage.tsx b/grafana-plugin/src/pages/settings/tabs/Cloud/CloudPage.tsx index b64424cf1f..1f92fe3d5b 100644 --- a/grafana-plugin/src/pages/settings/tabs/Cloud/CloudPage.tsx +++ b/grafana-plugin/src/pages/settings/tabs/Cloud/CloudPage.tsx @@ -2,6 +2,10 @@ import React, { useCallback, useEffect, useState } from 'react'; import { Button, Field, Icon, Input, LoadingPlaceholder, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { UserActions, determineRequiredAuthString } from 'helpers/authorization/authorization'; +import { PLUGIN_ROOT, StackSize } from 'helpers/consts'; +import { openErrorNotification } from 'helpers/helpers'; +import { PropsWithRouter, withRouter } from 'helpers/hoc'; import { observer } from 'mobx-react'; import { Block } from 'components/GBlock/Block'; @@ -13,10 +17,6 @@ import { Cloud } from 'models/cloud/cloud.types'; import { WithStoreProps } from 'state/types'; import { useStore } from 'state/useStore'; import { withMobXProviderContext } from 'state/withStore'; -import { UserActions, determineRequiredAuthString } from 'utils/authorization/authorization'; -import { PLUGIN_ROOT, StackSize } from 'utils/consts'; -import { PropsWithRouter, withRouter } from 'utils/hoc'; -import { openErrorNotification } from 'utils/utils'; import styles from './CloudPage.module.css'; diff --git a/grafana-plugin/src/pages/settings/tabs/LiveSettings/LiveSettingsPage.tsx b/grafana-plugin/src/pages/settings/tabs/LiveSettings/LiveSettingsPage.tsx index d044ee7e20..123ca87bd6 100644 --- a/grafana-plugin/src/pages/settings/tabs/LiveSettings/LiveSettingsPage.tsx +++ b/grafana-plugin/src/pages/settings/tabs/LiveSettings/LiveSettingsPage.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Button, Checkbox, Icon, Stack } from '@grafana/ui'; import cn from 'classnames/bind'; +import { isUserActionAllowed, UserActions } from 'helpers/authorization/authorization'; import { Lambda, observe } from 'mobx'; import { observer } from 'mobx-react'; @@ -12,7 +13,6 @@ import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/W import { GlobalSetting } from 'models/global_setting/global_setting.types'; import { WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { isUserActionAllowed, UserActions } from 'utils/authorization/authorization'; import { PLACEHOLDER } from './LiveSettings.config'; import { normalizeValue, prepareForUpdate } from './LiveSettings.helpers'; diff --git a/grafana-plugin/src/pages/settings/tabs/MainSettings/MainSettings.tsx b/grafana-plugin/src/pages/settings/tabs/MainSettings/MainSettings.tsx index fea4751d3e..4eae4d7494 100644 --- a/grafana-plugin/src/pages/settings/tabs/MainSettings/MainSettings.tsx +++ b/grafana-plugin/src/pages/settings/tabs/MainSettings/MainSettings.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { css } from '@emotion/css'; import { Field, Input, Switch, useStyles2 } from '@grafana/ui'; +import { UserActions } from 'helpers/authorization/authorization'; import { observer } from 'mobx-react'; import { LegacyNavHeading } from 'navbar/LegacyNavHeading'; @@ -11,7 +12,6 @@ import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/W import { TeamsSettings } from 'pages/settings/tabs/TeamsSettings/TeamsSettings'; import { isTopNavbar } from 'plugin/GrafanaPluginRootPage.helpers'; import { useStore } from 'state/useStore'; -import { UserActions } from 'utils/authorization/authorization'; export const MainSettings = observer(() => { const styles = useStyles2(getStyles); diff --git a/grafana-plugin/src/pages/users/Users.tsx b/grafana-plugin/src/pages/users/Users.tsx index aecac4d977..a2fe503e98 100644 --- a/grafana-plugin/src/pages/users/Users.tsx +++ b/grafana-plugin/src/pages/users/Users.tsx @@ -3,6 +3,14 @@ import React from 'react'; import { cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Alert, Button, Stack, withTheme2 } from '@grafana/ui'; +import { LocationHelper } from 'helpers/LocationHelper'; +import { + UserActions, + generateMissingPermissionMessage, + isUserActionAllowed, +} from 'helpers/authorization/authorization'; +import { PAGE, PLUGIN_ROOT, StackSize } from 'helpers/consts'; +import { PropsWithRouter, withRouter } from 'helpers/hoc'; import { debounce } from 'lodash-es'; import { observer } from 'mobx-react'; import { LegacyNavHeading } from 'navbar/LegacyNavHeading'; @@ -25,10 +33,6 @@ import { ApiSchemas } from 'network/oncall-api/api.types'; import { AppFeature } from 'state/features'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { LocationHelper } from 'utils/LocationHelper'; -import { UserActions, generateMissingPermissionMessage, isUserActionAllowed } from 'utils/authorization/authorization'; -import { PAGE, PLUGIN_ROOT, StackSize } from 'utils/consts'; -import { PropsWithRouter, withRouter } from 'utils/hoc'; import { getUserRowClassNameFn } from './Users.helpers'; import { getUsersStyles } from './Users.styles'; diff --git a/grafana-plugin/src/plugin/GrafanaPluginRootPage.helpers.test.tsx b/grafana-plugin/src/plugin/GrafanaPluginRootPage.helpers.test.tsx index 3b35235080..611fbb80ed 100644 --- a/grafana-plugin/src/plugin/GrafanaPluginRootPage.helpers.test.tsx +++ b/grafana-plugin/src/plugin/GrafanaPluginRootPage.helpers.test.tsx @@ -1,6 +1,5 @@ import * as runtime from '@grafana/runtime'; - -import { getGrafanaVersion } from 'utils/utils'; +import { getGrafanaVersion } from 'helpers/helpers'; jest.mock('@grafana/runtime', () => ({ config: jest.fn(), diff --git a/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx b/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx index 064146f7d6..cc510fad04 100644 --- a/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx +++ b/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx @@ -1,12 +1,16 @@ import React, { useEffect } from 'react'; import { ErrorBoundary, LoadingPlaceholder } from '@grafana/ui'; +import { AppRootProps } from 'app-types'; import classnames from 'classnames'; +import { isUserActionAllowed } from 'helpers/authorization/authorization'; +import { DEFAULT_PAGE, getOnCallApiUrl } from 'helpers/consts'; +import { FaroHelper } from 'helpers/faro'; +import { useOnMount } from 'helpers/hooks'; import { observer, Provider } from 'mobx-react'; import { Header } from 'navbar/Header/Header'; import { LegacyNavTabsBar } from 'navbar/LegacyNavTabsBar'; import { Navigate, Route, Routes, useLocation } from 'react-router-dom-v5-compat'; -import { AppRootProps } from 'types'; import { RenderConditionally } from 'components/RenderConditionally/RenderConditionally'; import { Unauthorized } from 'components/Unauthorized/Unauthorized'; @@ -30,13 +34,9 @@ import LiveSettings from 'pages/settings/tabs/LiveSettings/LiveSettingsPage'; import { UsersPage } from 'pages/users/Users'; import { rootStore } from 'state/rootStore'; import { useStore } from 'state/useStore'; -import { isUserActionAllowed } from 'utils/authorization/authorization'; -import { DEFAULT_PAGE, getOnCallApiUrl } from 'utils/consts'; import 'assets/style/vars.css'; import 'assets/style/global.css'; import 'assets/style/utils.css'; -import { FaroHelper } from 'utils/faro'; -import { useOnMount } from 'utils/hooks'; import { getQueryParams, isTopNavbar } from './GrafanaPluginRootPage.helpers'; diff --git a/grafana-plugin/src/state/rootBaseStore/RootBaseStore.ts b/grafana-plugin/src/state/rootBaseStore/RootBaseStore.ts index 2839ad30d3..8087436c3f 100644 --- a/grafana-plugin/src/state/rootBaseStore/RootBaseStore.ts +++ b/grafana-plugin/src/state/rootBaseStore/RootBaseStore.ts @@ -1,6 +1,9 @@ +import { OnCallAppPluginMeta } from 'app-types'; +import { retryFailingPromises } from 'helpers/async'; +import { APP_VERSION, CLOUD_VERSION_REGEX, GRAFANA_LICENSE_CLOUD, GRAFANA_LICENSE_OSS } from 'helpers/consts'; +import { loadJs } from 'helpers/loadJs'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import qs from 'query-string'; -import { OnCallAppPluginMeta } from 'types'; import { AlertReceiveChannelStore } from 'models/alert_receive_channel/alert_receive_channel'; import { AlertReceiveChannelConnectedChannelsStore } from 'models/alert_receive_channel_connected_channels/alert_receive_channel_connected_channels'; @@ -33,9 +36,6 @@ import { UserGroupStore } from 'models/user_group/user_group'; import { makeRequest } from 'network/network'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { AppFeature } from 'state/features'; -import { retryFailingPromises } from 'utils/async'; -import { APP_VERSION, CLOUD_VERSION_REGEX, GRAFANA_LICENSE_CLOUD, GRAFANA_LICENSE_OSS } from 'utils/consts'; -import { loadJs } from 'utils/loadJs'; // ------ Dashboard ------ // diff --git a/grafana-plugin/src/state/types.ts b/grafana-plugin/src/state/types.ts index 4e0cf218c0..262eaa2ce0 100644 --- a/grafana-plugin/src/state/types.ts +++ b/grafana-plugin/src/state/types.ts @@ -1,7 +1,7 @@ import { AppPluginMeta, KeyValue } from '@grafana/data'; +import { useDrawer } from 'helpers/hooks'; import { RootStore } from 'state/rootStore'; -import { useDrawer } from 'utils/hooks'; export interface WithStoreProps { store: RootStore; From a0a5482a852c481e314ae72b37a78fd256324c89 Mon Sep 17 00:00:00 2001 From: Joey Orlando Date: Thu, 5 Sep 2024 13:50:08 -0400 Subject: [PATCH 2/5] [IRM] - changes to get incident working in IRM plugin (#4987) Required for https://github.com/grafana/irm/pull/69 --- grafana-plugin/.bra.toml | 7 +---- grafana-plugin/go.mod | 2 +- grafana-plugin/pkg/main.go | 4 +-- grafana-plugin/pkg/plugin/app.go | 27 ++++++++++++++----- grafana-plugin/pkg/plugin/debug.go | 5 ++-- grafana-plugin/pkg/plugin/install.go | 2 +- grafana-plugin/pkg/plugin/resources.go | 10 +++---- grafana-plugin/pkg/plugin/resources_test.go | 5 ++-- grafana-plugin/pkg/plugin/settings.go | 17 +++++------- grafana-plugin/pkg/plugin/status.go | 2 +- grafana-plugin/pkg/plugin/sync.go | 7 ++--- .../src/network/grafana-api/http-client.ts | 26 +++++++++++------- 12 files changed, 65 insertions(+), 49 deletions(-) diff --git a/grafana-plugin/.bra.toml b/grafana-plugin/.bra.toml index 24d10b6db9..eaa76a4d6d 100644 --- a/grafana-plugin/.bra.toml +++ b/grafana-plugin/.bra.toml @@ -4,19 +4,14 @@ [run] init_cmds = [ ["mage", "-v", "build:debug"], - ["mage", "-v" , "reloadPlugin"], ] watch_all = true follow_symlinks = false ignore = [".git", "node_modules", "dist"] ignore_files = ["mage_output_file.go"] -watch_dirs = [ - "pkg", - # "src", -] +watch_dirs = ["pkg"] watch_exts = [".go", ".json"] build_delay = 2000 cmds = [ ["mage", "-v", "build:debug"], - ["mage", "-v" , "reloadPlugin"], ] diff --git a/grafana-plugin/go.mod b/grafana-plugin/go.mod index f059ec7554..aecbb33233 100644 --- a/grafana-plugin/go.mod +++ b/grafana-plugin/go.mod @@ -1,4 +1,4 @@ -module github.com/grafana-labs/grafana-oncall-app +module github.com/grafana/grafana-oncall-app go 1.21.5 diff --git a/grafana-plugin/pkg/main.go b/grafana-plugin/pkg/main.go index 2d956202d4..948e5f08b5 100644 --- a/grafana-plugin/pkg/main.go +++ b/grafana-plugin/pkg/main.go @@ -3,7 +3,7 @@ package main import ( "os" - "github.com/grafana-labs/grafana-oncall-app/pkg/plugin" + "github.com/grafana/grafana-oncall-app/pkg/plugin" "github.com/grafana/grafana-plugin-sdk-go/backend/app" "github.com/grafana/grafana-plugin-sdk-go/backend/log" ) @@ -16,7 +16,7 @@ func main() { // argument. This factory will be automatically called on incoming request // from Grafana to create different instances of `App` (per plugin // ID). - if err := app.Manage("grafana-oncall-app", plugin.NewApp, app.ManageOpts{}); err != nil { + if err := app.Manage("grafana-oncall-app", plugin.NewInstance, app.ManageOpts{}); err != nil { log.DefaultLogger.Error(err.Error()) os.Exit(1) } diff --git a/grafana-plugin/pkg/plugin/app.go b/grafana-plugin/pkg/plugin/app.go index cd51cd4a10..84e03b93d4 100644 --- a/grafana-plugin/pkg/plugin/app.go +++ b/grafana-plugin/pkg/plugin/app.go @@ -38,15 +38,9 @@ type App struct { } // NewApp creates a new example *App instance. -func NewApp(ctx context.Context, settings backend.AppInstanceSettings) (instancemgmt.Instance, error) { +func NewApp(ctx context.Context, settings backend.AppInstanceSettings) (*App, error) { var app App - // Use a httpadapter (provided by the SDK) for resource calls. This allows us - // to use a *http.ServeMux for resource calls, so we can map multiple routes - // to CallResource without having to implement extra logic. - mux := http.NewServeMux() - app.registerRoutes(mux) - app.CallResourceHandler = httpadapter.New(mux) app.OnCallSyncCache = &OnCallSyncCache{} app.OnCallSettingsCache = &OnCallSettingsCache{} app.OnCallUserCache = NewOnCallUserCache() @@ -66,6 +60,25 @@ func NewApp(ctx context.Context, settings backend.AppInstanceSettings) (instance return &app, nil } +// NewInstance creates a new example *Instance instance. +func NewInstance(ctx context.Context, settings backend.AppInstanceSettings) (instancemgmt.Instance, error) { + app, err := NewApp(ctx, settings) + + if err != nil { + log.DefaultLogger.Error("Error creating new app", "error", err) + return nil, err + } + + // Use a httpadapter (provided by the SDK) for resource calls. This allows us + // to use a *http.ServeMux for resource calls, so we can map multiple routes + // to CallResource without having to implement extra logic. + mux := http.NewServeMux() + app.registerRoutes(mux) + app.CallResourceHandler = httpadapter.New(mux) + + return app, nil +} + // Dispose here tells plugin SDK that plugin wants to clean up resources when a new instance // created. func (a *App) Dispose() { diff --git a/grafana-plugin/pkg/plugin/debug.go b/grafana-plugin/pkg/plugin/debug.go index e362c9ccd1..7c429d4412 100644 --- a/grafana-plugin/pkg/plugin/debug.go +++ b/grafana-plugin/pkg/plugin/debug.go @@ -2,9 +2,10 @@ package plugin import ( "encoding/json" + "net/http" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter" - "net/http" ) type OnCallDebugStats struct { @@ -47,7 +48,7 @@ func (a *App) handleDebugSync(w http.ResponseWriter, req *http.Request) { return } - onCallSync, err := a.GetSyncData(req.Context(), onCallPluginSettings) + onCallSync, err := a.GetSyncData(onCallPluginSettings) if err != nil { log.DefaultLogger.Error("Error getting sync data", "error", err) return diff --git a/grafana-plugin/pkg/plugin/install.go b/grafana-plugin/pkg/plugin/install.go index da6f94b2cb..6f4d108d04 100644 --- a/grafana-plugin/pkg/plugin/install.go +++ b/grafana-plugin/pkg/plugin/install.go @@ -41,7 +41,7 @@ func (a *App) handleInstall(w http.ResponseWriter, req *http.Request) { return } - onCallSync, err := a.GetSyncData(req.Context(), onCallPluginSettings) + onCallSync, err := a.GetSyncData(onCallPluginSettings) if err != nil { log.DefaultLogger.Error("Error getting sync data", "error", err) return diff --git a/grafana-plugin/pkg/plugin/resources.go b/grafana-plugin/pkg/plugin/resources.go index 7dad9564e0..b43420844e 100644 --- a/grafana-plugin/pkg/plugin/resources.go +++ b/grafana-plugin/pkg/plugin/resources.go @@ -84,7 +84,7 @@ func afterRequest(handler http.Handler, afterFunc func(*responseWriter, *http.Re }) } -func (a *App) handleInternalApi(w http.ResponseWriter, req *http.Request) { +func (a *App) HandleInternalApi(w http.ResponseWriter, req *http.Request) { a.ProxyRequestToOnCall(w, req, "api/internal/v1/") } @@ -121,10 +121,10 @@ func (a *App) handleLegacyInstall(w *responseWriter, req *http.Request) { // registerRoutes takes a *http.ServeMux and registers some HTTP handlers. func (a *App) registerRoutes(mux *http.ServeMux) { mux.HandleFunc("/plugin/install", a.handleInstall) - mux.HandleFunc("/plugin/status", a.handleStatus) - mux.HandleFunc("/plugin/sync", a.handleSync) + mux.HandleFunc("/plugin/status", a.HandleStatus) + mux.HandleFunc("/plugin/sync", a.HandleSync) - mux.Handle("/plugin/self-hosted/install", afterRequest(http.HandlerFunc(a.handleInternalApi), a.handleLegacyInstall)) + mux.Handle("/plugin/self-hosted/install", afterRequest(http.HandlerFunc(a.HandleInternalApi), a.handleLegacyInstall)) // Disable debug endpoints //mux.HandleFunc("/debug/user", a.handleDebugUser) @@ -134,5 +134,5 @@ func (a *App) registerRoutes(mux *http.ServeMux) { //mux.HandleFunc("/debug/stats", a.handleDebugStats) //mux.HandleFunc("/debug/unlock", a.handleDebugUnlock) - mux.HandleFunc("/", a.handleInternalApi) + mux.HandleFunc("/", a.HandleInternalApi) } diff --git a/grafana-plugin/pkg/plugin/resources_test.go b/grafana-plugin/pkg/plugin/resources_test.go index 7506326a7e..e3a88e81b5 100644 --- a/grafana-plugin/pkg/plugin/resources_test.go +++ b/grafana-plugin/pkg/plugin/resources_test.go @@ -3,8 +3,9 @@ package plugin import ( "bytes" "context" - "github.com/grafana/grafana-plugin-sdk-go/backend" "testing" + + "github.com/grafana/grafana-plugin-sdk-go/backend" ) // mockCallResourceResponseSender implements backend.CallResourceResponseSender @@ -23,7 +24,7 @@ func (s *mockCallResourceResponseSender) Send(response *backend.CallResourceResp // This ensures the httpadapter for CallResource works correctly. func TestCallResource(t *testing.T) { // Initialize app - inst, err := NewApp(context.Background(), backend.AppInstanceSettings{}) + inst, err := NewInstance(context.Background(), backend.AppInstanceSettings{}) if err != nil { t.Fatalf("new app: %s", err) } diff --git a/grafana-plugin/pkg/plugin/settings.go b/grafana-plugin/pkg/plugin/settings.go index 700bc42383..8386c7dbee 100644 --- a/grafana-plugin/pkg/plugin/settings.go +++ b/grafana-plugin/pkg/plugin/settings.go @@ -317,32 +317,29 @@ func (a *App) SaveOnCallSettings(settings *OnCallPluginSettings) error { return nil } -func (a *App) GetSyncData(ctx context.Context, settings *OnCallPluginSettings) (*OnCallSync, error) { +func (a *App) GetSyncData(pluginSettings *OnCallPluginSettings) (*OnCallSync, error) { + var err error + startGetSyncData := time.Now() defer func() { elapsed := time.Since(startGetSyncData) log.DefaultLogger.Info("GetSyncData", "time", elapsed.Milliseconds()) }() - onCallPluginSettings, err := a.OnCallSettingsFromContext(ctx) - if err != nil { - return nil, fmt.Errorf("error getting settings from context = %v", err) - } - onCallSync := OnCallSync{ - Settings: *settings, + Settings: *pluginSettings, } - onCallSync.Users, err = a.GetAllUsersWithPermissions(onCallPluginSettings) + onCallSync.Users, err = a.GetAllUsersWithPermissions(pluginSettings) if err != nil { return nil, fmt.Errorf("error getting users = %v", err) } - onCallSync.Teams, err = a.GetAllTeams(onCallPluginSettings) + onCallSync.Teams, err = a.GetAllTeams(pluginSettings) if err != nil { return nil, fmt.Errorf("error getting teams = %v", err) } - teamMembers, err := a.GetAllTeamMembers(onCallPluginSettings, onCallSync.Teams) + teamMembers, err := a.GetAllTeamMembers(pluginSettings, onCallSync.Teams) if err != nil { return nil, fmt.Errorf("error getting team members = %v", err) } diff --git a/grafana-plugin/pkg/plugin/status.go b/grafana-plugin/pkg/plugin/status.go index 935e9bf387..ab93a742f8 100644 --- a/grafana-plugin/pkg/plugin/status.go +++ b/grafana-plugin/pkg/plugin/status.go @@ -244,7 +244,7 @@ func (a *App) ValidateOnCallStatus(ctx context.Context, settings *OnCallPluginSe return &status, nil } -func (a *App) handleStatus(w http.ResponseWriter, req *http.Request) { +func (a *App) HandleStatus(w http.ResponseWriter, req *http.Request) { if req.Method != http.MethodGet { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return diff --git a/grafana-plugin/pkg/plugin/sync.go b/grafana-plugin/pkg/plugin/sync.go index 72a1c6f636..d79e22fad3 100644 --- a/grafana-plugin/pkg/plugin/sync.go +++ b/grafana-plugin/pkg/plugin/sync.go @@ -7,13 +7,14 @@ import ( "encoding/json" "errors" "fmt" - "github.com/grafana/grafana-plugin-sdk-go/backend/log" "io" "net/http" "net/url" "strconv" "sync" "time" + + "github.com/grafana/grafana-plugin-sdk-go/backend/log" ) type OnCallSyncCache struct { @@ -38,7 +39,7 @@ func (oc *OnCallSyncCache) UnlockAfterDelay(delay time.Duration) { }) } -func (a *App) handleSync(w http.ResponseWriter, req *http.Request) { +func (a *App) HandleSync(w http.ResponseWriter, req *http.Request) { if req.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return @@ -122,7 +123,7 @@ func (a *App) makeSyncRequest(ctx context.Context, forceSend bool) error { return fmt.Errorf("error getting settings from context: %v ", err) } - onCallSync, err := a.GetSyncData(ctx, onCallPluginSettings) + onCallSync, err := a.GetSyncData(onCallPluginSettings) if err != nil { return fmt.Errorf("error getting sync data: %v", err) } diff --git a/grafana-plugin/src/network/grafana-api/http-client.ts b/grafana-plugin/src/network/grafana-api/http-client.ts index 44a9ead325..d0ff66f492 100644 --- a/grafana-plugin/src/network/grafana-api/http-client.ts +++ b/grafana-plugin/src/network/grafana-api/http-client.ts @@ -1,6 +1,6 @@ import { getBackendSrv } from '@grafana/runtime'; import { OnCallPluginMetaJSONData } from 'app-types'; -import { getPluginId } from 'helpers/consts'; +import { getPluginId, PluginId } from 'helpers/consts'; import { ApiAuthKeyDTO, @@ -11,18 +11,26 @@ import { UpdateGrafanaPluginSettingsProps, } from './api.types'; +const pluginId = getPluginId(); +const KEY_NAME = { + [PluginId.OnCall]: 'OnCall', + [PluginId.Irm]: 'IRM', +}[pluginId]; +const SERVICE_ACCOUNT_NAME = { + [PluginId.OnCall]: 'sa-autogen-OnCall', + [PluginId.Irm]: 'sa-autogen-IRM', +}[pluginId]; + const KEYS_BASE_URL = '/api/auth/keys'; const SERVICE_ACCOUNTS_BASE_URL = '/api/serviceaccounts'; -const ONCALL_KEY_NAME = 'OnCall'; -const ONCALL_SERVICE_ACCOUNT_NAME = 'sa-autogen-OnCall'; -const GRAFANA_PLUGIN_SETTINGS_URL = `/api/plugins/${getPluginId()}/settings`; +const GRAFANA_PLUGIN_SETTINGS_URL = `/api/plugins/${pluginId}/settings`; export class GrafanaApiClient { static grafanaBackend = getBackendSrv(); private static getServiceAccount = async () => { const serviceAccounts = await this.grafanaBackend.get( - `${SERVICE_ACCOUNTS_BASE_URL}/search?query=${ONCALL_SERVICE_ACCOUNT_NAME}` + `${SERVICE_ACCOUNTS_BASE_URL}/search?query=${SERVICE_ACCOUNT_NAME}` ); return serviceAccounts.serviceAccounts.length > 0 ? serviceAccounts.serviceAccounts[0] : null; }; @@ -34,7 +42,7 @@ export class GrafanaApiClient { } return await this.grafanaBackend.post(SERVICE_ACCOUNTS_BASE_URL, { - name: ONCALL_SERVICE_ACCOUNT_NAME, + name: SERVICE_ACCOUNT_NAME, role: 'Admin', isDisabled: false, }); @@ -44,7 +52,7 @@ export class GrafanaApiClient { const tokens = await this.grafanaBackend.get( `${SERVICE_ACCOUNTS_BASE_URL}/${serviceAccount.id}/tokens` ); - return tokens.find(({ name }) => name === ONCALL_KEY_NAME); + return tokens.find(({ name }) => name === KEY_NAME); }; private static getGrafanaToken = async () => { @@ -54,7 +62,7 @@ export class GrafanaApiClient { } const keys = await this.grafanaBackend.get(KEYS_BASE_URL); - return keys.find(({ name }) => name === ONCALL_KEY_NAME); + return keys.find(({ name }) => name === KEY_NAME); }; static updateGrafanaPluginSettings = async (data: UpdateGrafanaPluginSettingsProps, enabled = true) => @@ -79,7 +87,7 @@ export class GrafanaApiClient { const { key: grafanaToken } = await this.grafanaBackend.post( `${SERVICE_ACCOUNTS_BASE_URL}/${serviceAccount.id}/tokens`, { - name: ONCALL_KEY_NAME, + name: KEY_NAME, role: 'Admin', } ); From 0efe51d310124121d1dae8a8eeb2a792098a35dc Mon Sep 17 00:00:00 2001 From: Michael Derynck Date: Thu, 5 Sep 2024 12:18:07 -0600 Subject: [PATCH 3/5] Update helm chart for newer grafana + enable externalServiceAccounts (#4876) # What this PR does Updates the helm chart and docker compose files with the required changes to support the plugin initialization changes. Updated instructions on the README.md show how to setup & intialize OnCall without needing to go to the configuration page, this is currently the preferred method. ## Which issue(s) this PR closes Related to [issue link here] ## Checklist - [ ] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [ ] 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. --------- Co-authored-by: GitHub Actions --- README.md | 41 +++++++++++++++--- docker-compose-mysql-rabbitmq.yml | 1 + docker-compose.yml | 1 + helm/README.md | 3 +- helm/oncall/Chart.yaml | 6 +-- helm/oncall/charts/grafana-6.57.1.tgz | Bin 39048 -> 0 bytes helm/oncall/charts/grafana-8.4.6.tgz | Bin 0 -> 44404 bytes helm/oncall/templates/_env.tpl | 2 +- .../oncall/templates/plugin-provisioning.yaml | 16 +++++++ .../integrations_deployment_test.yaml.snap | 2 +- ...telegram_polling_deployment_test.yaml.snap | 4 +- .../__snapshot__/wait_for_db_test.yaml.snap | 12 ++--- helm/oncall/tests/telegram_env_test.yaml | 4 +- helm/oncall/values.yaml | 16 +++++-- helm/simple.yml | 4 +- 15 files changed, 84 insertions(+), 28 deletions(-) delete mode 100644 helm/oncall/charts/grafana-6.57.1.tgz create mode 100644 helm/oncall/charts/grafana-8.4.6.tgz create mode 100644 helm/oncall/templates/plugin-provisioning.yaml diff --git a/README.md b/README.md index e83f0f14db..510a4a628b 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,11 @@ Developer-friendly incident response with brilliant Slack integration. ## Getting Started +> [!IMPORTANT] +> These instructions are for using Grafana 11 or newer. You must enable the feature toggle for +> `externalServiceAccounts`. This is already done for the docker files and helm charts. If you are running Grafana +> separately see the Grafana documentation on how to enable this. + We prepared multiple environments: - [production](https://grafana.com/docs/oncall/latest/open-source/#production-environment) @@ -82,17 +87,41 @@ We prepared multiple environments: docker-compose pull && docker-compose up -d ``` -5. Go to [OnCall Plugin Configuration](http://localhost:3000/plugins/grafana-oncall-app), using log in credentials - as defined above: `admin`/`admin` (or find OnCall plugin in configuration->plugins) and connect OnCall _plugin_ - with OnCall _backend_: +5. Provision the plugin (If you run Grafana outside the included docker files install the plugin before these steps): + + If you are using the included docker compose file use `admin`/`admin` credentials and `localhost:3000` to + perform this task. If you have configured Grafana differently adjust your credentials and hostnames accordingly. - ```text - OnCall backend URL: http://engine:8080 + ```bash + # Note: onCallApiUrl 'engine' and grafanaUrl 'grafana' use the name from the docker compose file. If you are + # running your grafana or oncall engine instance with another hostname adjust accordingly. + curl -X POST 'http://admin:admin@localhost:3000/api/plugins/grafana-oncall-app/settings' -H "Content-Type: application/json" -d '{"enabled":true, "jsonData":{"stackId":5, "orgId":100, "onCallApiUrl":"http://engine:8080", "grafanaUrl":"http://grafana:3000"}}' + curl -X POST 'http://admin:admin@localhost:3000/api/plugins/grafana-oncall-app/resources/plugin/install' ``` -6. Enjoy! Check our [OSS docs](https://grafana.com/docs/oncall/latest/open-source/) if you want to set up +6. Start using OnCall, log in to Grafana with credentials + as defined above: `admin`/`admin` + +7. Enjoy! Check our [OSS docs](https://grafana.com/docs/oncall/latest/open-source/) if you want to set up Slack, Telegram, Twilio or SMS/calls through Grafana Cloud. +## Troubleshooting + +Here are some API calls that can be made to help if you are having difficulty connecting Grafana and OnCall. +(Modify parameters to match your credentials and environment) + + ```bash + # Use this to get more information about the connection between Grafana and OnCall + curl -X GET 'http://admin:admin@localhost:3000/api/plugins/grafana-oncall-app/resources/plugin/status' + ``` + + ```bash + # If you added a user or changed permissions and don't see it show up in OnCall you can manually trigger sync. + # Note: This is called automatically when the app is loaded (page load/refresh) but there is a 5 min timeout so + # that it does not generate unnecessary activity. + curl -X POST 'http://admin:admin@localhost:3000/api/plugins/grafana-oncall-app/resources/plugin/sync' + ``` + ## Update version To update your Grafana OnCall hobby environment: diff --git a/docker-compose-mysql-rabbitmq.yml b/docker-compose-mysql-rabbitmq.yml index f7687a8e8e..f587902e76 100644 --- a/docker-compose-mysql-rabbitmq.yml +++ b/docker-compose-mysql-rabbitmq.yml @@ -139,6 +139,7 @@ services: GF_DATABASE_HOST: ${MYSQL_HOST:-mysql} GF_DATABASE_USER: ${MYSQL_USER:-root} GF_DATABASE_PASSWORD: ${MYSQL_PASSWORD:?err} + GF_FEATURE_TOGGLES_ENABLE: externalServiceAccounts GF_SECURITY_ADMIN_USER: ${GRAFANA_USER:-admin} GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD:-admin} GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: grafana-oncall-app diff --git a/docker-compose.yml b/docker-compose.yml index 23bc9d3e1f..b115199f8c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -89,6 +89,7 @@ services: ports: - "3000:3000" environment: + GF_FEATURE_TOGGLES_ENABLE: externalServiceAccounts GF_SECURITY_ADMIN_USER: ${GRAFANA_USER:-admin} GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD:-admin} GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: grafana-oncall-app diff --git a/helm/README.md b/helm/README.md index f3fce70841..7e998340a3 100644 --- a/helm/README.md +++ b/helm/README.md @@ -2,8 +2,7 @@ 1. Create the cluster with [kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation) - > Make sure ports 30001, 30002 (Grafana, optional) and - > 30003 (detached integrations server, optional) are free on your machine + > Make sure ports 30001, 30002 (Grafana, optional) are available ```bash kind create cluster --image kindest/node:v1.24.7 --config kind.yml diff --git a/helm/oncall/Chart.yaml b/helm/oncall/Chart.yaml index 178455147b..08dc3664b5 100644 --- a/helm/oncall/Chart.yaml +++ b/helm/oncall/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: oncall description: Developer-friendly incident response with brilliant Slack integration type: application -version: 1.7.2 -appVersion: v1.7.2 +version: 1.9.20 +appVersion: v1.9.20 dependencies: - name: cert-manager version: v1.8.0 @@ -26,7 +26,7 @@ dependencies: repository: https://charts.bitnami.com/bitnami condition: redis.enabled - name: grafana - version: 6.57.1 + version: 8.4.6 repository: https://grafana.github.io/helm-charts condition: grafana.enabled - name: ingress-nginx diff --git a/helm/oncall/charts/grafana-6.57.1.tgz b/helm/oncall/charts/grafana-6.57.1.tgz deleted file mode 100644 index 25121669ba6fbd4f92187675be05101c96b0cc9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39048 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 Q00030|5${X&j6+a0E?(6GXMYp diff --git a/helm/oncall/charts/grafana-8.4.6.tgz b/helm/oncall/charts/grafana-8.4.6.tgz new file mode 100644 index 0000000000000000000000000000000000000000..3e185d3a1f199367271cd6f224cad3bc78f4144d GIT binary patch literal 44404 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 Q0{{U3|0D-pv;Zaq0M{(vRsaA1 literal 0 HcmV?d00001 diff --git a/helm/oncall/templates/_env.tpl b/helm/oncall/templates/_env.tpl index 6821d2a92b..20a52493e0 100644 --- a/helm/oncall/templates/_env.tpl +++ b/helm/oncall/templates/_env.tpl @@ -106,7 +106,7 @@ value: {{ .Values.telegramPolling.enabled | toString | title | quote }} {{- end }} - name: TELEGRAM_WEBHOOK_HOST - value: {{ .Values.oncall.telegram.webhookUrl | default (printf "https://%s" .Values.base_url) | quote }} + value: {{ .Values.oncall.telegram.webhookUrl | default (printf "%s://%s" .Values.base_url_protocol .Values.base_url) | quote }} {{- if .Values.oncall.telegram.existingSecret }} - name: TELEGRAM_TOKEN valueFrom: diff --git a/helm/oncall/templates/plugin-provisioning.yaml b/helm/oncall/templates/plugin-provisioning.yaml new file mode 100644 index 0000000000..511a4a39fd --- /dev/null +++ b/helm/oncall/templates/plugin-provisioning.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: helm-testing-grafana-plugin-provisioning + labels: + app: {{ include "oncall.name" . }} +data: + grafana-oncall-app-provisioning.yaml: | + apps: + - type: grafana-oncall-app + name: grafana-oncall-app + disabled: false + jsonData: + stackId: 5 + orgId: 100 + onCallApiUrl: http://helm-testing-oncall-engine:8080 diff --git a/helm/oncall/tests/__snapshot__/integrations_deployment_test.yaml.snap b/helm/oncall/tests/__snapshot__/integrations_deployment_test.yaml.snap index 89efb16bdc..d055982f49 100644 --- a/helm/oncall/tests/__snapshot__/integrations_deployment_test.yaml.snap +++ b/helm/oncall/tests/__snapshot__/integrations_deployment_test.yaml.snap @@ -2,7 +2,7 @@ detached_integrations.enabled=true -> should create integrations deployment: 1: | - env: - name: BASE_URL - value: https://example.com + value: http://example.com - name: SECRET_KEY valueFrom: secretKeyRef: diff --git a/helm/oncall/tests/__snapshot__/telegram_polling_deployment_test.yaml.snap b/helm/oncall/tests/__snapshot__/telegram_polling_deployment_test.yaml.snap index 341fa32d83..b3eb11b572 100644 --- a/helm/oncall/tests/__snapshot__/telegram_polling_deployment_test.yaml.snap +++ b/helm/oncall/tests/__snapshot__/telegram_polling_deployment_test.yaml.snap @@ -6,7 +6,7 @@ telegramPolling.enabled=true -> should create telegram polling deployment: - python manage.py start_telegram_polling env: - name: BASE_URL - value: https://example.com + value: http://example.com - name: SECRET_KEY valueFrom: secretKeyRef: @@ -38,7 +38,7 @@ telegramPolling.enabled=true -> should create telegram polling deployment: - name: FEATURE_TELEGRAM_LONG_POLLING_ENABLED value: "True" - name: TELEGRAM_WEBHOOK_HOST - value: https://example.com + value: http://example.com - name: TELEGRAM_TOKEN value: "" - name: MYSQL_HOST diff --git a/helm/oncall/tests/__snapshot__/wait_for_db_test.yaml.snap b/helm/oncall/tests/__snapshot__/wait_for_db_test.yaml.snap index 2cee70e776..5c6e4adb7e 100644 --- a/helm/oncall/tests/__snapshot__/wait_for_db_test.yaml.snap +++ b/helm/oncall/tests/__snapshot__/wait_for_db_test.yaml.snap @@ -6,7 +6,7 @@ database.type=mysql -> should create initContainer for MySQL database (default): - until (python manage.py migrate --check); do echo Waiting for database migrations; sleep 2; done env: - name: BASE_URL - value: https://example.com + value: http://example.com - name: SECRET_KEY valueFrom: secretKeyRef: @@ -94,7 +94,7 @@ database.type=mysql -> should create initContainer for MySQL database (default): - until (python manage.py migrate --check); do echo Waiting for database migrations; sleep 2; done env: - name: BASE_URL - value: https://example.com + value: http://example.com - name: SECRET_KEY valueFrom: secretKeyRef: @@ -182,7 +182,7 @@ database.type=mysql -> should create initContainer for MySQL database (default): - until (python manage.py migrate --check); do echo Waiting for database migrations; sleep 2; done env: - name: BASE_URL - value: https://example.com + value: http://example.com - name: SECRET_KEY valueFrom: secretKeyRef: @@ -271,7 +271,7 @@ database.type=postgresql -> should create initContainer for PostgreSQL database: - until (python manage.py migrate --check); do echo Waiting for database migrations; sleep 2; done env: - name: BASE_URL - value: https://example.com + value: http://example.com - name: SECRET_KEY valueFrom: secretKeyRef: @@ -361,7 +361,7 @@ database.type=postgresql -> should create initContainer for PostgreSQL database: - until (python manage.py migrate --check); do echo Waiting for database migrations; sleep 2; done env: - name: BASE_URL - value: https://example.com + value: http://example.com - name: SECRET_KEY valueFrom: secretKeyRef: @@ -451,7 +451,7 @@ database.type=postgresql -> should create initContainer for PostgreSQL database: - until (python manage.py migrate --check); do echo Waiting for database migrations; sleep 2; done env: - name: BASE_URL - value: https://example.com + value: http://example.com - name: SECRET_KEY valueFrom: secretKeyRef: diff --git a/helm/oncall/tests/telegram_env_test.yaml b/helm/oncall/tests/telegram_env_test.yaml index 288d5ee65a..f45d019ccb 100644 --- a/helm/oncall/tests/telegram_env_test.yaml +++ b/helm/oncall/tests/telegram_env_test.yaml @@ -24,7 +24,7 @@ tests: set: oncall.telegram: enabled: true - webhookUrl: https://example.com + webhookUrl: http://example.com token: "abcd:123" asserts: - contains: @@ -36,7 +36,7 @@ tests: path: spec.template.spec.containers[0].env content: name: TELEGRAM_WEBHOOK_HOST - value: "https://example.com" + value: "http://example.com" - contains: path: spec.template.spec.containers[0].env content: diff --git a/helm/oncall/values.yaml b/helm/oncall/values.yaml index f7d4c30b1d..8ca59a2664 100644 --- a/helm/oncall/values.yaml +++ b/helm/oncall/values.yaml @@ -3,7 +3,7 @@ # Set the domain name Grafana OnCall will be installed on. # If you want to install grafana as a part of this release make sure to configure grafana.grafana.ini.server.domain too base_url: example.com -base_url_protocol: https +base_url_protocol: http ## Optionally specify an array of imagePullSecrets. ## Secrets must be manually created in the namespace. @@ -634,9 +634,11 @@ grafana: enabled: true grafana.ini: server: - domain: example.com - root_url: "%(protocol)s://%(domain)s/grafana" + domain: helm-testing-grafana + root_url: "%(protocol)s://%(domain)s/grafana/" serve_from_sub_path: true + feature_toggles: + enable: externalServiceAccounts persistence: enabled: true # Disable psp as PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+ @@ -644,6 +646,14 @@ grafana: pspEnabled: false plugins: - grafana-oncall-app + extraVolumes: + - name: provisioning + configMap: + name: helm-testing-grafana-plugin-provisioning + extraVolumeMounts: + - name: provisioning + mountPath: /etc/grafana/provisioning/plugins/grafana-oncall-app-provisioning.yaml + subPath: grafana-oncall-app-provisioning.yaml externalGrafana: # Example: https://grafana.mydomain.com diff --git a/helm/simple.yml b/helm/simple.yml index 219c7a11c6..0dd9da4b96 100644 --- a/helm/simple.yml +++ b/helm/simple.yml @@ -15,9 +15,9 @@ grafana: type: NodePort nodePort: 30002 detached_integrations: - enabled: true + enabled: false detached_integrations_service: - enabled: true + enabled: false type: NodePort port: 8080 nodePort: 30003 From dbba664a1fb244b00ae16d43617c36137c0e19ce Mon Sep 17 00:00:00 2001 From: Matias Bordese Date: Fri, 6 Sep 2024 09:25:23 -0300 Subject: [PATCH 4/5] Check for `user.is_active` during mobile app auth (#4990) Related to https://github.com/grafana/support-escalations/issues/12253 --- engine/apps/auth_token/auth.py | 2 +- .../apps/auth_token/tests/test_plugin_auth.py | 20 +++++++++++++++++++ engine/apps/mobile_app/auth.py | 2 +- .../tests/test_mobile_app_auth_token.py | 15 ++++++++++++++ .../public_api/tests/test_alert_groups.py | 14 +++++++++++++ 5 files changed, 51 insertions(+), 2 deletions(-) diff --git a/engine/apps/auth_token/auth.py b/engine/apps/auth_token/auth.py index bb419eb3af..19374a7b16 100644 --- a/engine/apps/auth_token/auth.py +++ b/engine/apps/auth_token/auth.py @@ -52,7 +52,7 @@ def authenticate(self, request): auth = get_authorization_header(request).decode("utf-8") user, auth_token = self.authenticate_credentials(auth) - if not user_is_authorized(user, [RBACPermission.Permissions.API_KEYS_WRITE]): + if not user.is_active or not user_is_authorized(user, [RBACPermission.Permissions.API_KEYS_WRITE]): raise exceptions.AuthenticationFailed( "Only users with Admin permissions are allowed to perform this action." ) diff --git a/engine/apps/auth_token/tests/test_plugin_auth.py b/engine/apps/auth_token/tests/test_plugin_auth.py index 56fe56974a..d03c40b388 100644 --- a/engine/apps/auth_token/tests/test_plugin_auth.py +++ b/engine/apps/auth_token/tests/test_plugin_auth.py @@ -83,6 +83,26 @@ def test_plugin_authentication_fail(authorization, instance_context): PluginAuthentication().authenticate(request) +@pytest.mark.django_db +def test_plugin_authentication_inactive_user(make_organization, make_user, make_token_for_organization): + organization = make_organization(stack_id=42, org_id=24) + token, token_string = make_token_for_organization(organization) + user = make_user(organization=organization, user_id=12) + # user is set to inactive if deleted via queryset (ie. during sync) + user.is_active = False + user.save() + + headers = { + "HTTP_AUTHORIZATION": token_string, + "HTTP_X-Instance-Context": INSTANCE_CONTEXT, + "HTTP_X-Grafana-Context": '{"UserId": 12}', + } + request = APIRequestFactory().get("/", **headers) + + with pytest.raises(AuthenticationFailed): + PluginAuthentication().authenticate(request) + + @pytest.mark.django_db def test_plugin_authentication_gcom_setup_new_user(make_organization): # Setting gcom_token_org_last_time_synced to now, so it doesn't try to sync with gcom diff --git a/engine/apps/mobile_app/auth.py b/engine/apps/mobile_app/auth.py index 6363f9f254..8a5a7178b9 100644 --- a/engine/apps/mobile_app/auth.py +++ b/engine/apps/mobile_app/auth.py @@ -33,7 +33,7 @@ class MobileAppAuthTokenAuthentication(BaseAuthentication): def authenticate(self, request) -> Optional[Tuple[User, MobileAppAuthToken]]: auth = get_authorization_header(request).decode("utf-8") user, auth_token = self.authenticate_credentials(auth) - if user is None: + if user is None or not user.is_active: return None return user, auth_token diff --git a/engine/apps/mobile_app/tests/test_mobile_app_auth_token.py b/engine/apps/mobile_app/tests/test_mobile_app_auth_token.py index 2f5f2f4de9..34e7d9d011 100644 --- a/engine/apps/mobile_app/tests/test_mobile_app_auth_token.py +++ b/engine/apps/mobile_app/tests/test_mobile_app_auth_token.py @@ -4,6 +4,7 @@ from rest_framework.test import APIClient from apps.mobile_app.models import MobileAppAuthToken +from apps.user_management.models import User @pytest.mark.django_db @@ -76,3 +77,17 @@ def test_mobile_app_auth_token( response = client.get(url, HTTP_AUTHORIZATION=verification_token) assert response.status_code == status.HTTP_404_NOT_FOUND + + +@pytest.mark.django_db +def test_mobile_app_auth_token_deleted_user( + make_organization_and_user_with_mobile_app_auth_token, +): + _, user, auth_token = make_organization_and_user_with_mobile_app_auth_token() + # user is deleted via queryset (ie. setting it to inactive, during sync) + User.objects.filter(id=user.id).delete() + + client = APIClient() + url = reverse("api-internal:alertgroup-list") + response = client.get(url, HTTP_AUTHORIZATION=auth_token) + assert response.status_code == status.HTTP_403_FORBIDDEN diff --git a/engine/apps/public_api/tests/test_alert_groups.py b/engine/apps/public_api/tests/test_alert_groups.py index 9c88fec880..3cfc9c8537 100644 --- a/engine/apps/public_api/tests/test_alert_groups.py +++ b/engine/apps/public_api/tests/test_alert_groups.py @@ -180,6 +180,20 @@ def test_get_alert_groups(alert_group_public_api_setup): assert response.json() == expected_response +@pytest.mark.django_db +def test_get_alert_groups_inactive_user(make_organization_and_user_with_token): + _, user, token = make_organization_and_user_with_token() + # user is set to inactive if deleted via queryset (ie. during sync) + user.is_active = False + user.save() + + client = APIClient() + url = reverse("api-public:alert_groups-list") + response = client.get(url, format="json", HTTP_AUTHORIZATION=token) + + assert response.status_code == status.HTTP_403_FORBIDDEN + + @pytest.mark.django_db def test_get_alert_groups_include_labels(alert_group_public_api_setup, make_alert_group_label_association): token, _, _, _ = alert_group_public_api_setup From fc07a22c56609d2110ccf0dfefda9129c0877c3c Mon Sep 17 00:00:00 2001 From: Michael Derynck Date: Fri, 6 Sep 2024 09:26:03 -0600 Subject: [PATCH 5/5] Main to dev (#4995) # What this PR does ## Which issue(s) this PR closes Related to [issue link here] ## Checklist - [ ] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [ ] 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. --------- Co-authored-by: Joey Orlando Co-authored-by: GitHub Actions Co-authored-by: Joey Orlando Co-authored-by: Matias Bordese Co-authored-by: Vadim Stepanov Co-authored-by: Dominik Broj Co-authored-by: Yulya Artyukhina Co-authored-by: Innokentii Konstantinov --- helm/oncall/Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm/oncall/Chart.yaml b/helm/oncall/Chart.yaml index 08dc3664b5..c3836dbbd8 100644 --- a/helm/oncall/Chart.yaml +++ b/helm/oncall/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: oncall description: Developer-friendly incident response with brilliant Slack integration type: application -version: 1.9.20 -appVersion: v1.9.20 +version: 1.9.22 +appVersion: v1.9.22 dependencies: - name: cert-manager version: v1.8.0