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 | 30 +++++++++---------- .../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, 400 insertions(+), 407 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 1c66b63b..aecf5845 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 774c1840..44725482 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 4e0bfa64..95b7b548 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 3d8cdbef..3eb32a8b 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 895a1905..70020c93 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 44547883..497d3488 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 43d0da61..102a49db 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 bb28a07b..b60354a9 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 2bbfa34d..a474da16 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 a24eb49a..124c0220 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 16d077f8..29b0545e 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 f271a00c..f8f530f8 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 8b440d7c..129c7f6b 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 bd84f8cf..9c36e97d 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 25abaf35..da9ef142 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 aa4f3ed7..a3a7295d 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 8b013023..c79fdac4 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 203bb241..037229cc 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 eac18542..fefd973b 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 d25ea77a..65c47112 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 77eea524..d42e5109 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 0321db15..09511065 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 b108eb6b..4dcbbca9 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 2b0a4e16..5a4ca430 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 bf9598f5..cca9d052 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 a6d5d2cd..412366d5 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 719c9bc0..c9a145d4 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 b6fb4a9f..c2ad3722 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 fd0c71e5..68724896 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 ec360318..a31cd567 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 96158c52..fb43d7a3 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 19e2e68e..715e60d2 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 b62540b4..ba8c5ee8 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 8aaae76d..bb70c857 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 9678d17d..b3d85a06 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 31929e65..83295b37 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 5478b956..0f35269b 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 f6725b6f..8b11021c 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 0606becb..a908b622 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 f72b0cd7..52aca97b 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 da2fb3d9..6a7f65a5 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 c4ff5548..401caf62 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 f887cb18..9eb037a9 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 b53cdba2..5ed5b18a 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 5b6ccde8..3f1a179a 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 c5d32a7d..09b6352d 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 e2dbd9f3..f91b5363 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 37c65138..39019223 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 3758f197..848ad810 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 25e4809c..8648d6c9 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 84b2d7b0..c7a9bada 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 3b367162..3a13a3f5 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 2b3a4df2..e16ce0f0 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 855bfcb4..4dca5d91 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 402eba69..b72dee67 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 f3e32426..b657796e 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 fe18d5da..3d87aaf2 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 6ac090f7..d3f96bad 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 69083106..be5f9e59 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 83140b9e..5785c763 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 7602ea99..3dff84fe 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 85b68d93..71015714 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 e61eb91a..c30f538f 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 8ec381f4..be17c17e 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 8e3ea458..68751867 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 6481b64e..0cce3b48 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 02625f46..78555a78 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 e546a5fe..30d71291 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 f6920b3a..a2ecea4c 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 f1f367ba..3b009a47 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 932cc876..124fde2a 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 1ab887b2..b672f53b 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 076c6370..5d39a256 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 a6b5cde5..55d51554 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 7de7a09d..05db8418 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 0250c06b..e7a4ac97 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 82a6592d..fa8fd2e2 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 d62156e1..ee7dbf18 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 0758740f..58b408e7 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 a421a137..722a16e9 100644 --- a/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx +++ b/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx @@ -3,17 +3,7 @@ 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 { 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'; -import { RenderConditionally } from 'components/RenderConditionally/RenderConditionally'; -import { Text } from 'components/Text/Text'; -import { ActionKey } from 'models/loader/action-keys'; -import { rootStore } from 'state/rootStore'; +import { OnCallPluginMetaJSONData } from 'app-types'; import { DEFAULT_PAGE, DOCS_ONCALL_OSS_INSTALL, @@ -21,10 +11,20 @@ import { 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'; +} 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 { Button } from 'components/Button/Button'; +import { CollapsibleTreeView } from 'components/CollapsibleTreeView/CollapsibleTreeView'; +import { RenderConditionally } from 'components/RenderConditionally/RenderConditionally'; +import { Text } from 'components/Text/Text'; +import { ActionKey } from 'models/loader/action-keys'; +import { rootStore } from 'state/rootStore'; type PluginConfigFormValues = { onCallApiUrl: string; diff --git a/grafana-plugin/src/containers/PluginInitializer/PluginInitializer.tsx b/grafana-plugin/src/containers/PluginInitializer/PluginInitializer.tsx index 2ba9aa66..ffdb6b3b 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 0c35a0fe..92474fdc 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 94ec2fbc..f33ab4c2 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 37671a36..7043f614 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 8e5d38c6..e3f10fd6 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 12a69d5a..9a23727a 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 12176718..f8e01084 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 5132a69a..d5cef9b6 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 0c6dfb18..5ee43823 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 a769c76e..9034d7b7 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 9effe9a6..a2e7485f 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 eb09c17b..db490348 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 2d18d994..91b360e8 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 5674438f..e2e3d5ff 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 d3364ff6..4161e7aa 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 2f71a05c..111a68ed 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 8e0f8fb5..2cd97350 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 20569cd8..d9e71d30 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 ce2a7ae7..3f84bb6b 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 d479942e..0b395bf0 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 4110a869..6ff772df 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 131319d4..01fbfd2b 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 7f1a7411..67aa8617 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 a780574d..8242780c 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 50c19963..513d4498 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 4ce1f76e..d993af33 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 e7746c46..ac5538df 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 e05344e3..c48fda5e 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 25e63e71..23846774 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 a6262e58..d50b8e84 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 9f5b8f44..29e9ebee 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 1782b0b1..45928791 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 072c8afe..9b1c4379 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 8abe4284..877e68a9 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 742d8afb..6c6917a0 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 46cb91a1..27d7191f 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 43359ea1..c583f4c8 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 53988ecc..4cbd8ad2 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 13add812..1a3dcd27 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 e14c7b22..20489da2 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 567ba82f..4090c40b 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 bfb5e1ac..83610a6e 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 20921c56..60f76063 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 21335c8b..6cb9a826 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 846a2791..6a559618 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 c9c3d52d..753e4fd5 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 149a8962..4019e7a3 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 2aec11fb..d11503e0 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 c1d10a75..8ebb865f 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 bf0f6976..f9e8a56e 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 0accc916..bed4aac3 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 c920b009..298fb564 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 4173e8b7..bf17bfb8 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 e5e0dab8..1eaf46e8 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 ce01395a..9d85ca62 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 72a9d450..7fa206a8 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 bc22ed4b..5f3619a1 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 b3dc76fe..47b036b6 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 531055a6..f4ffc790 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 dd452dcb..4bbdf3c1 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 a102973a..987a214a 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 7559632a..d7236266 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 a063bb10..661404ef 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 c8ffe32d..db183739 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 d4446904..520338a7 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 6a4b6ce8..20aa1968 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 fecb1db2..7f02e2cf 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 d18f4d77..7f905ea7 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 8a36c224..f73db2ac 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 e501e06c..d69837c8 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 918bef67..fcc75ba8 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 e4173c33..44a9ead3 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 f111126f..6f926845 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 fda14302..fd910e07 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 ab08d3ac..795718f2 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 414c9fd0..fc861c9b 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 ee13ac1b..883fefe8 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 a3925429..b52ba753 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 aa4a5772..bedd9368 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 0460a460..7507aac6 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 66580ead..070b77b4 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 0a046057..d38532e4 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 4f0c0b15..57d26a4d 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 3657834f..b40cc735 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 042b2606..0812cc8f 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 f4b87ec2..33ea7b1e 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 d364ded1..115ce7dd 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 d56bfa57..e794eaf6 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 de89b5dd..7e144f66 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 691d5b02..aa71a6fa 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 086f522a..015ace29 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 90c61c06..ffcb8862 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 2efbaa83..020a5e38 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 25dc51fd..667b76e7 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 900ac884..4a8cac51 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 b040b749..b8e8d073 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 8a5e6e4c..f1f9a0da 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 44baa96f..39e66c4d 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 980497a4..8379ba9b 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 a08a4856..4f5a861a 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 a0db5d5a..bc3ff0a6 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 758a8455..ca13d2de 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 940343d0..d8821308 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 4313ec5f..cbd88fe1 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 5ee30c39..5434d48d 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 4d5227c8..e5ea47c6 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 eb7bf4d8..de07ef2c 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 b64424cf..1f92fe3d 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 d044ee7e..123ca87b 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 fea4751d..4eae4d74 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 aecac4d9..a2fe503e 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 3b352350..611fbb80 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 064146f7..cc510fad 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 2839ad30..8087436c 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 4e0cf218..262eaa2c 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 24d10b6d..eaa76a4d 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 f059ec75..aecbb332 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 2d956202..948e5f08 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 cd51cd4a..84e03b93 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 e362c9cc..7c429d44 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 da6f94b2..6f4d108d 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 7dad9564..b4342084 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 7506326a..e3a88e81 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 700bc423..8386c7db 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 935e9bf3..ab93a742 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 72a1c6f6..d79e22fa 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 44a9ead3..d0ff66f4 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 e83f0f14..510a4a62 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): - ```text - OnCall backend URL: http://engine:8080 + 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. + + ```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 f7687a8e..f587902e 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 23bc9d3e..b115199f 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 f3fce708..7e998340 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 17845514..08dc3664 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 6821d2a9..20a52493 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 00000000..511a4a39 --- /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 89efb16b..d055982f 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 341fa32d..b3eb11b5 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 2cee70e7..5c6e4adb 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 288d5ee6..f45d019c 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 f7d4c30b..8ca59a26 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 219c7a11..0dd9da4b 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 bb419eb3..19374a7b 100644 --- a/engine/apps/auth_token/auth.py +++ b/engine/apps/auth_token/auth.py @@ -52,7 +52,7 @@ class ApiTokenAuthentication(BaseAuthentication): 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 56fe5697..d03c40b3 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 6363f9f2..8a5a7178 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 2f5f2f4d..34e7d9d0 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 import status 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 9c88fec8..3cfc9c85 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 08dc3664..c3836dbb 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