From 7e2870b7bb5c0f1be9f83429ac6c97ba896fa91d Mon Sep 17 00:00:00 2001 From: brendamuir <100768211+brendamuir@users.noreply.github.com> Date: Thu, 9 Jun 2022 20:12:41 +0200 Subject: [PATCH 01/79] Docs: Updates Grafana Alerting upgrade topics (#50533) * Updates Grafana Alerting upgrade topics * Run yarn prettier:write Co-authored-by: Armand Grillet --- docs/sources/alerting/_index.md | 2 +- .../alerting/migrating-alerts/_index.md | 32 +++++---- .../migrating-alerts/disable-alerting.md | 23 +++++++ .../migrating-legacy-alerts.md | 11 +-- .../alerting/migrating-alerts/opt-in.md | 25 +++++++ .../alerting/migrating-alerts/opt-out.md | 68 ++++++++++--------- .../alerting/migrating-alerts/roll-back.md | 29 ++++++++ docs/sources/alerting/performance.md | 2 +- .../setup-grafana/configure-grafana/_index.md | 2 +- 9 files changed, 141 insertions(+), 53 deletions(-) create mode 100644 docs/sources/alerting/migrating-alerts/disable-alerting.md create mode 100644 docs/sources/alerting/migrating-alerts/opt-in.md create mode 100644 docs/sources/alerting/migrating-alerts/roll-back.md diff --git a/docs/sources/alerting/_index.md b/docs/sources/alerting/_index.md index 9803ae16ed7..b8bf41fc564 100644 --- a/docs/sources/alerting/_index.md +++ b/docs/sources/alerting/_index.md @@ -8,7 +8,7 @@ weight: 114 # Grafana Alerting -Grafana alerts allow you to learn about problems in your systems moments after they occur. Robust and actionable alerts help you identify and resolve issues quickly, minimizing disruption to your services. It centralizes alerting information in a single, searchable view that allows you to: +Grafana Alerting allows you to learn about problems in your systems moments after they occur. Robust and actionable alerts help you identify and resolve issues quickly, minimizing disruption to your services. It centralizes alerting information in a single, searchable view that allows you to: - Create and manage Grafana alerts - Create and manage Grafana Mimir and Loki managed alerts diff --git a/docs/sources/alerting/migrating-alerts/_index.md b/docs/sources/alerting/migrating-alerts/_index.md index c2b1a057a0f..094038351c0 100644 --- a/docs/sources/alerting/migrating-alerts/_index.md +++ b/docs/sources/alerting/migrating-alerts/_index.md @@ -1,27 +1,31 @@ --- aliases: - /docs/grafana/latest/alerting/migrating-alerts/ -description: Migrate Grafana alerts -title: Migrate to Grafana Alerting -weight: 113 +description: Upgrade Grafana alerts +title: Upgrade to Grafana Alerting +weight: 101 --- -# Migrate to Grafana Alerting +# Upgrade to Grafana Alerting -Grafana Alerting is the default for new Cloud, Enterprise, and OSS installations. The new installations will only show the Grafana Alerting icon in the left navigation panel. +Grafana Alerting is enabled by default for new installations or existing installations whether or not legacy alerting is configured. -Existing installations that upgrade to v9.0 will have Grafana Alerting enabled by default. +Existing installations that do not use legacy alerting will have Grafana Alerting enabled by default unless alerting is disabled in the configuration. -| Grafana instance upgraded to v 90 | | -| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Cloud | Existing Cloud installations with legacy dashboard alerting will have two alerting icons in the left navigation panel - the old alerting plugin icon and the new Grafana Alerting icon. During upgrade, existing alerts from the Cloud alerting plugin are migrated to Grafana Alerting. Once migration is complete, you can access aman manage the older alerts from the new alerting Grafana Alerting icon in the navigation panel. The (older) Cloud alerting plugin is uninstalled from your cloud instance. Contact customer support if you **do not wish** to migrate to Grafana Alerting for your Cloud stack. If you choose to use legacy alerting, use the You will see the new Grafana Alerting icon as well as the old Cloud alerting plugin in the left navigation panel. | -| Enterprise | Existing Enterprise instances using legacy alerting will have both the old (marked as legacy) and the new alerting icons in the navigation panel. During upgrade, existing legacy alerts are migrated to Grafana Alerting. If you wish, you can [opt-out]({{< relref "opt-out/" >}}) of Grafana Alerting and roll back to legacy alerting. In that case, you can manage your legacy alerts from the alerting icon marked as legacy. | -| OSS | Existing OSS installations with legacy dashboard alerting will have two alerting icons in the left navigation panel - the old alerting icon (marked as legacy) and the new Grafana Alerting icon. During upgrade, existing legacy alerts are migrated to Grafana Alerting. If you wish, you can [opt-out]({{< relref "opt-out/" >}}) of Grafana Alerting and roll back to legacy alerting. In that case, you can manage your legacy alerts from the alerting icon marked as legacy. | +Likewise, existing installations that use legacy alerting will be automatically upgraded to Grafana Alerting unless you have [opted out]({{< relref "opt-out/" >}})of Grafana Alerting before migration takes place. During the upgrade, legacy alerts are migrated to the new alerts type and no alerts or alerting data are lost. -During migration from legacy alerting to unified alerting, the legacy alerts are updated to the new alerts type, as a result, the user does not lose alerts or alerting data. However, if a user rolls back to legacy alerting after having migrated to unified alerting, they will only get the legacy alerts they had right before migration. +Once the upgrade has taken place, you still have the option to roll back [roll back]({{< relref "roll-back/" >}}) to legacy alerting. However, we do not recommend choosing this option. If you do choose to roll back, Grafana will restore your alerts to the alerts you had at the point in time when the upgrade took place. All new alerts and changes made exclusively in Grafana Alerting will be deleted. -## Roll back to legacy alerting +> **Note**: Cloud customers, who do not want to upgrade to Grafana Alerting, should contact customer support. -Although we encourage you to use Grafana Alerting, roll back to legacy alerting is supported in Grafana 9. Rolling back can result in data loss (you will loose all alerts that you created using Grafana Alerting). This is applicable to the fresh installation as well as upgraded setups. +If you have opted out or rolled back, you can always choose to opt in [opt in]({{< relref "opt-in/" >}}) to Grafana Alerting at a later point in time. + +The following table provides details on the upgrade for Cloud, Enterprise, and OSS installations and the new Grafana Alerting UI. + +| Grafana instance upgraded to 9.0 | | +| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Cloud | Existing Cloud installations with legacy dashboard alerting will have two alerting icons in the left navigation panel - the old alerting plugin icon and the new Grafana Alerting icon. During upgrade, existing alerts from the Cloud alerting plugin are migrated to Grafana Alerting. Once migration is complete, you can access and manage the older alerts from the new alerting Grafana Alerting icon in the navigation panel. The (older) Cloud alerting plugin is uninstalled from your cloud instance. Contact customer support if you **do not wish** to migrate to Grafana Alerting for your Cloud stack. If you choose to use legacy alerting, use the You will see the new Grafana Alerting icon as well as the old Cloud alerting plugin in the left navigation panel. | +| Enterprise | Existing Enterprise instances using legacy alerting will have both the old (marked as legacy) and the new alerting icons in the navigation panel. During upgrade, existing legacy alerts are migrated to Grafana Alerting. If you wish, you can [opt-out]({{< relref "opt-out/" >}}) of Grafana Alerting and roll back to legacy alerting. In that case, you can manage your legacy alerts from the alerting icon marked as legacy. | +| OSS | Existing OSS installations with legacy dashboard alerting will have two alerting icons in the left navigation panel - the old alerting icon (marked as legacy) and the new Grafana Alerting icon. During upgrade, existing legacy alerts are migrated to Grafana Alerting. If you wish, you can [opt-out]({{< relref "opt-out/" >}}) of Grafana Alerting and roll back to legacy alerting. In that case, you can manage your legacy alerts from the alerting icon marked as legacy. | > **Note:** Legacy alerting will be deprecated in a future release (v10). diff --git a/docs/sources/alerting/migrating-alerts/disable-alerting.md b/docs/sources/alerting/migrating-alerts/disable-alerting.md new file mode 100644 index 00000000000..0f759504417 --- /dev/null +++ b/docs/sources/alerting/migrating-alerts/disable-alerting.md @@ -0,0 +1,23 @@ +--- +aliases: + - /docs/grafana/latest/alerting/migrating-alerts/disable-alerting/ + - /docs/grafana/latest/alerting/disable-alerting/ + - /docs/grafana/latest/alerting/unified-alerting/disable-alerting/ +description: Disable alerting in Grafana +title: Disable alerting in Grafana +weight: 105 +--- + +# Disable alerting in Grafana + +To disable alerting in Grafana entirely (including both legacy and Grafana Alerting), enter the following in your configuration: + +``` +[alerting] +enabled = false + +[unified_alerting] +enabled = false +``` + +If at any time you want to turn alerting back on, you can opt in. diff --git a/docs/sources/alerting/migrating-alerts/migrating-legacy-alerts.md b/docs/sources/alerting/migrating-alerts/migrating-legacy-alerts.md index e517e3e90fa..6a94dd9a459 100644 --- a/docs/sources/alerting/migrating-alerts/migrating-legacy-alerts.md +++ b/docs/sources/alerting/migrating-alerts/migrating-legacy-alerts.md @@ -4,13 +4,14 @@ aliases: - /docs/grafana/latest/alerting/migrating-legacy-alerts/ - /docs/grafana/latest/alerting/unified-alerting/opt-in/ description: Migrate legacy dashboard alerts -title: Migrating legacy dashboard alerts -weight: 114 +title: Differences and limitations +weight: 106 --- -# Migrating legacy dashboard alerts +# Differences and limitations -When Grafana Alerting is enabled or Grafana is upgraded to the latest version, existing legacy dashboard alerts migrate in a format compatible with the Grafana Alerting. In the Alerting page of your Grafana instance, you can view the migrated alerts alongside any new alerts. This topic explains how legacy dashboard alerts are migrated and some limitations. +When Grafana Alerting is enabled or upgraded to Grafana 9.0 or later, existing legacy dashboard alerts migrate in a format compatible with the Grafana Alerting. In the Alerting page of your Grafana instance, you can view the migrated alerts alongside any new alerts. +This topic explains how legacy dashboard alerts are migrated and some limitations of the migration. > **Note:** This topic is only relevant for OSS and Enterprise customers. Contact customer support to enable or disable Grafana Alerting for your Cloud stack. @@ -24,5 +25,7 @@ Read and write access to legacy dashboard alerts and Grafana alerts are governed Notification channels are migrated to an Alertmanager configuration with the appropriate routes and receivers. Default notification channels are added as contact points to the default route. Notification channels not associated with any Dashboard alert go to the `autogen-unlinked-channel-recv` route. +## Limitations + Since `Hipchat` and `Sensu` notification channels are no longer supported, legacy alerts associated with these channels are not automatically migrated to Grafana Alerting. Assign the legacy alerts to a supported notification channel so that you continue to receive notifications for those alerts. Silences (expiring after one year) are created for all paused dashboard alerts. diff --git a/docs/sources/alerting/migrating-alerts/opt-in.md b/docs/sources/alerting/migrating-alerts/opt-in.md new file mode 100644 index 00000000000..00a62c2385b --- /dev/null +++ b/docs/sources/alerting/migrating-alerts/opt-in.md @@ -0,0 +1,25 @@ +--- +aliases: + - /docs/grafana/latest/alerting/migrating-alerts/opt-in/ + - /docs/grafana/latest/alerting/opt-in/ + - /docs/grafana/latest/alerting/unified-alerting/opt-in/ +description: Opt in to Grafana Alerting +title: Opt in to Grafana Alerting +weight: 104 +--- + +# Opt in to Grafana Alerting + +If you have previously disabled alerting in Grafana, or opted out of Grafana Alerting and have decided that you would now like to use Grafana Alerting, you can choose to opt in at any time. + +If you have been using legacy alerting up until now your existing alerts will be migrated to the new alerts type and no alerts or alerting data are lost. Even if you choose to opt in to Grafana Alerting, you can roll back to legacy alerting at any time. + +To opt in to Grafana Alerting, enter the following in your configuration: + +``` +[alerting] +enabled = false + +[unified_alerting] +enabled = true +``` diff --git a/docs/sources/alerting/migrating-alerts/opt-out.md b/docs/sources/alerting/migrating-alerts/opt-out.md index 72edb664d98..3c2ef5f0b1f 100644 --- a/docs/sources/alerting/migrating-alerts/opt-out.md +++ b/docs/sources/alerting/migrating-alerts/opt-out.md @@ -3,41 +3,45 @@ aliases: - /docs/grafana/latest/alerting/migrating-alerts/opt-out/ - /docs/grafana/latest/alerting/opt-in/ - /docs/grafana/latest/alerting/unified-alerting/opt-in/ -description: Disable Grafana alerts -title: Opt-out of Grafana Alerting -weight: 113 +description: Opt out of Grafana Alerting +title: Opt out of Grafana Alerting +weight: 102 --- -# Opt-out to Grafana Alerting in OSS +# Opt out of Grafana Alerting -This topic discusses how to disable Grafana Alerting and migrate to legacy dashboard alerting. It also provides guidance on how to enable Grafana Alerting once you are ready to migrate to Grafana Alerting. +If you have an existing installation, you can opt out of alerting in its entirety or opt out of Grafana Alerting in favor of using legacy alerting. + +Existing installations that do not use legacy alerting will have Grafana Alerting enabled by default unless alerting is disabled in the configuration. To keep alerting disabled: + +1. Go to your custom configuration file ($WORKING_DIR/conf/custom.ini). +1. Enter the following in your configuration: + +``` +[alerting] +enabled = false + +[unified_alerting] +enabled = false +``` + +3. Restart Grafana for the configuration changes to take effect. + +If at any time you want to turn alerting back on, you can do so. + +Existing installations that use legacy alerting will automatically be upgraded to Grafana Alerting unless you have opted-out of Grafana Alerting before migration takes place. During the upgrade, legacy alerts are migrated to the new alerts type and no alerts or alerting data are lost. To keep using legacy alerting and disable Grafana Alerting: + +1. Go to your custom configuration file ($WORKING_DIR/conf/custom.ini). +2. Enter the following in your configuration: + +``` +[alerting] +enabled = false + +[unified_alerting] +enabled = true +``` > **Note:** This topic is only relevant for OSS and Enterprise customers. Contact customer support to enable or disable Grafana Alerting for your Grafana Cloud stack. -## Before you begin - -We recommend that you backup Grafana's database. If you are using PostgreSQL as the backend database, then the minimum required version is 9.5. - -## Opt-out of Grafana alerts - -To opt-out of Grafana alerts and roll back to legacy dashboard alerting: - -1. In your custom configuration file ($WORKING_DIR/conf/custom.ini), go to the [Grafana Alerting]({{< relref "../../setup-grafana/configure-grafana/#unified_alerting" >}}) section. -1. Set the `enabled` property to `false`. -1. For [legacy dashboard alerting]({{< relref "../../setup-grafana/configure-grafana/#alerting" >}}), set the `enabled` flag to `true`. -1. Restart Grafana for the configuration changes to take effect. - -> **Note:** Rolling back from Grafana to legacy alerting can result in data loss. This is applicable to the fresh installation as well as upgraded setups. - -## Opt-in to Grafana Alerting - -When you are ready to make the switch, the following procedure will help you migrate to Grafana Alerting. - -To opt-in to Grafana alerts: - -1. In your custom configuration file ($WORKING_DIR/conf/custom.ini), go to the [unified alerts]({{< relref "../../setup-grafana/configure-grafana/#unified_alerting" >}}) section. -1. Set the `enabled` property to `true`. -1. Next, for [legacy dashboard alerting]({{< relref "../../setup-grafana/configure-grafana/#alerting" >}}), set the `enabled` flag to `false`. -1. Restart Grafana for the configuration changes to take effect. - -> **Note:** The `ngalert` toggle previously used to enable or disable Grafana Alerting is no longer available. +The `ngalert` toggle previously used to enable or disable Grafana Alerting is no longer available. diff --git a/docs/sources/alerting/migrating-alerts/roll-back.md b/docs/sources/alerting/migrating-alerts/roll-back.md new file mode 100644 index 00000000000..319db9b3921 --- /dev/null +++ b/docs/sources/alerting/migrating-alerts/roll-back.md @@ -0,0 +1,29 @@ +--- +aliases: + - /docs/grafana/latest/alerting/migrating-alerts/roll-back/ + - /docs/grafana/latest/alerting/opt-in/ + - /docs/grafana/latest/alerting/unified-alerting/roll-back/ +description: Roll back to legacy alerting +title: Roll back to legacy alerting +weight: 103 +--- + +# Roll back to legacy alerting + +Once the upgrade has taken place, you still have the option to roll back to legacy alerting. If you choose to roll back, Grafana will restore your alerts to the alerts you had at the point in time when the upgrade took place. + +All new alerts and changes made exclusively in Grafana Alerting will be deleted. + +To roll back to legacy alerting, enter the following in your configuration: + +``` +force_migration = true + +[alerting] +enabled = true + +[unified_alerting] +enabled = false +``` + +> **Note**: We do not recommend this option. If you choose to roll back, Grafana will restore your alerts to the alerts you had at the point in time when the upgrade took place. All new alerts and changes made exclusively in Grafana Alerting will be deleted. diff --git a/docs/sources/alerting/performance.md b/docs/sources/alerting/performance.md index c4a2802e90f..3c1f4279034 100644 --- a/docs/sources/alerting/performance.md +++ b/docs/sources/alerting/performance.md @@ -2,7 +2,7 @@ title = "Performance considerations" description = "Understanding alerting performance" keywords = ["grafana", "alerting", "performance"] -weight = 100 +weight = 555 +++ # Alerting performance considerations diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index 55f95c700ca..ed821e6eddc 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -1189,7 +1189,7 @@ For more information about the Grafana alerts, refer to [About Grafana Alerting] ### enabled -Enable the Unified Alerting sub-system and interface. When enabled we'll migrate all of your alert rules and notification channels to the new system. New alert rules will be created and your notification channels will be converted into an Alertmanager configuration. Previous data is preserved to enable backwards compatibility but new data is removed. The default value is `false`. +Enable or disable Grafana Alerting. If enabled, we’ll migrate all your alert rules and notification channels to the new system as alert rules and notification channels you had previously defined will be converted into an Alertmanager configuration. Legacy alerting data is preserved to enable backwards compatibility. If disabled, all your legacy alerting data will be available again, but the data you created using Grafana Alerting will be deleted. Set force_migration=true to avoid deletion of data. The default value is `true`. Alerting Rules migrated from dashboards and panels will include a link back via the `annotations`. From 5df694b924d24905756ed2aa5844a3e88672c6db Mon Sep 17 00:00:00 2001 From: Adam Simpson Date: Thu, 9 Jun 2022 18:24:48 +0000 Subject: [PATCH 02/79] AzureMonitor: changes to azureMonitorExperimentalUI after feedback (#50455) * AzureMonitor: tweak experimental-ui after UX discussions. - move metrics up to resource row - allow resources field to take an inlineField prop and a labelWidth prop. Eventually once the feature toggle is removed the inlineField prop will be the only code branch in Field.tsx. * AzureMonitor: remove devmode req for azureMonitorExperimentalUI flag * refactor: consolidate logic branch for inline Field --- pkg/services/featuremgmt/registry.go | 9 ++++----- .../components/Field.tsx | 11 ++++++++--- .../components/LogsQueryEditor/LogsQueryEditor.tsx | 2 ++ .../MetricsQueryEditor/MetricsQueryEditor.tsx | 5 ----- .../components/ResourceField/ResourceField.tsx | 7 +++++-- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 5a95129680b..623ea09178f 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -198,11 +198,10 @@ var ( State: FeatureStateAlpha, }, { - Name: "azureMonitorExperimentalUI", - Description: "Use grafana-experimental UI in Azure Monitor", - State: FeatureStateAlpha, - RequiresDevMode: true, - FrontendOnly: true, + Name: "azureMonitorExperimentalUI", + Description: "Use grafana-experimental UI in Azure Monitor", + State: FeatureStateAlpha, + FrontendOnly: true, }, { Name: "traceToMetrics", diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/Field.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/Field.tsx index 52e2fa818bc..61318cf8527 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/Field.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/Field.tsx @@ -7,13 +7,18 @@ import { Props as InlineFieldProps } from '@grafana/ui/src/components/Forms/Inli interface Props extends InlineFieldProps { label: string; + inlineField?: boolean; + labelWidth?: number; } const DEFAULT_LABEL_WIDTH = 18; export const Field = (props: Props) => { - if (config.featureToggles.azureMonitorExperimentalUI) { - return ; + const { labelWidth, inlineField, ...remainingProps } = props; + + if (config.featureToggles.azureMonitorExperimentalUI && !inlineField) { + return ; + } else { + return ; } - return ; }; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx index 83d858ac5b6..6879724dc17 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx @@ -44,6 +44,8 @@ const LogsQueryEditor: React.FC = ({ = ({ setResource={setResource} resourceUri={query.azureMonitor?.resourceUri} /> - - - - - AzureMonitorQuery; selectableEntryTypes: ResourceRowType[]; resourceUri?: string; + inlineField?: boolean; + labelWidth?: number; } const ResourceField: React.FC = ({ @@ -38,6 +40,8 @@ const ResourceField: React.FC = ({ setResource, selectableEntryTypes, resourceUri, + inlineField, + labelWidth, }) => { const styles = useStyles2(getStyles); const [pickerIsOpen, setPickerIsOpen] = useState(false); @@ -77,8 +81,7 @@ const ResourceField: React.FC = ({ selectableEntryTypes={selectableEntryTypes} /> - - + From be7518c97beff67905d2cd14a7940f24841dba60 Mon Sep 17 00:00:00 2001 From: idafurjes <36131195+idafurjes@users.noreply.github.com> Date: Thu, 9 Jun 2022 21:17:03 +0200 Subject: [PATCH 03/79] Bug: Fix List dashboard version (#50544) * Fix List dashboard version * Fix * Roll back tag for dahboard version id --- pkg/services/dashboardversion/model.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/services/dashboardversion/model.go b/pkg/services/dashboardversion/model.go index 6de84dd4d53..fd087921269 100644 --- a/pkg/services/dashboardversion/model.go +++ b/pkg/services/dashboardversion/model.go @@ -45,9 +45,9 @@ type ListDashboardVersionsQuery struct { } type DashboardVersionDTO struct { - ID int64 `json:"id"` - DashboardID int64 `json:"dashboardId"` - DashboardUID string `json:"dashboardUid"` + ID int64 `json:"id" xorm:"id"` + DashboardID int64 `json:"dashboardId" xorm:"dashboard_id"` + DashboardUID string `json:"dashboardUid" xorm:"dashboard_uid"` ParentVersion int `json:"parentVersion"` RestoredFrom int `json:"restoredFrom"` Version int `json:"version"` From fd239f94e8041e246445b7b8cadbf7e754887933 Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Fri, 10 Jun 2022 09:50:53 +0200 Subject: [PATCH 04/79] TeamSettings: Convert tests to RTL (#50513) --- .betterer.results | 3 -- .../app/features/teams/TeamSettings.test.tsx | 46 ++++++++++++++----- public/app/features/teams/TeamSettings.tsx | 10 +++- .../__snapshots__/TeamSettings.test.tsx.snap | 30 ------------ 4 files changed, 42 insertions(+), 47 deletions(-) delete mode 100644 public/app/features/teams/__snapshots__/TeamSettings.test.tsx.snap diff --git a/.betterer.results b/.betterer.results index 9d094e27732..cccbb6fad1b 100644 --- a/.betterer.results +++ b/.betterer.results @@ -146,9 +146,6 @@ exports[`no enzyme tests`] = { "public/app/features/org/OrgDetailsPage.test.tsx:3835042085": [ [0, 19, 13, "RegExp match", "2409514259"] ], - "public/app/features/teams/TeamSettings.test.tsx:2043271249": [ - [0, 19, 13, "RegExp match", "2409514259"] - ], "public/app/features/users/UsersListPage.test.tsx:2518052139": [ [0, 19, 13, "RegExp match", "2409514259"] ], diff --git a/public/app/features/teams/TeamSettings.test.tsx b/public/app/features/teams/TeamSettings.test.tsx index 2b6bb754ca3..1092aefede7 100644 --- a/public/app/features/teams/TeamSettings.test.tsx +++ b/public/app/features/teams/TeamSettings.test.tsx @@ -1,15 +1,20 @@ -import { shallow } from 'enzyme'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import React from 'react'; import { Props, TeamSettings } from './TeamSettings'; import { getMockTeam } from './__mocks__/teamMocks'; -jest.mock('app/core/core', () => ({ +jest.mock('app/core/services/context_srv', () => ({ contextSrv: { hasPermissionInMetadata: () => true, }, })); +jest.mock('app/core/components/SharedPreferences/SharedPreferences', () => { + return { SharedPreferences: () =>
}; +}); + const setup = (propOverrides?: object) => { const props: Props = { team: getMockTeam(), @@ -18,19 +23,36 @@ const setup = (propOverrides?: object) => { Object.assign(props, propOverrides); - const wrapper = shallow(); - const instance = wrapper.instance() as any; - - return { - wrapper, - instance, - }; + render(); }; -describe('Render', () => { +describe('Team settings', () => { it('should render component', () => { - const { wrapper } = setup(); + setup(); - expect(wrapper).toMatchSnapshot(); + expect(screen.getByText('Team settings')).toBeInTheDocument(); + }); + + it('should validate required fields', async () => { + const mockUpdate = jest.fn(); + setup({ updateTeam: mockUpdate }); + await userEvent.clear(screen.getByRole('textbox', { name: /Name/ })); + await userEvent.type(screen.getByLabelText(/Email/i), 'team@test.com'); + // Submitting with userEvent doesn't work here + fireEvent.submit(screen.getByRole('button', { name: 'Update' })); + expect(await screen.findByText('Name is required')).toBeInTheDocument(); + await waitFor(() => expect(mockUpdate).not.toHaveBeenCalled()); + }); + + it('should submit form with correct values', async () => { + const mockUpdate = jest.fn(); + setup({ updateTeam: mockUpdate }); + await userEvent.clear(screen.getByRole('textbox', { name: /Name/ })); + await userEvent.clear(screen.getByLabelText(/Email/i)); + await userEvent.type(screen.getByRole('textbox', { name: /Name/ }), 'New team'); + await userEvent.type(screen.getByLabelText(/Email/i), 'team@test.com'); + // Submitting with userEvent doesn't work here + fireEvent.submit(screen.getByRole('button', { name: 'Update' })); + await waitFor(() => expect(mockUpdate).toHaveBeenCalledWith('New team', 'team@test.com')); }); }); diff --git a/public/app/features/teams/TeamSettings.tsx b/public/app/features/teams/TeamSettings.tsx index d4fb9016887..7a828f13064 100644 --- a/public/app/features/teams/TeamSettings.tsx +++ b/public/app/features/teams/TeamSettings.tsx @@ -32,9 +32,15 @@ export const TeamSettings: FC = ({ team, updateTeam }) => { }} disabled={!canWriteTeamSettings} > - {({ register }) => ( + {({ register, errors }) => ( <> - + diff --git a/public/app/features/teams/__snapshots__/TeamSettings.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamSettings.test.tsx.snap deleted file mode 100644 index 1fe40abc971..00000000000 --- a/public/app/features/teams/__snapshots__/TeamSettings.test.tsx.snap +++ /dev/null @@ -1,30 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Render should render component 1`] = ` - -
-
- - -
- -
-`; From ce6a73a623d05e7d4506f1aabcf345cbfa0d0756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Bedi?= Date: Fri, 10 Jun 2022 09:55:46 +0200 Subject: [PATCH 05/79] TimeSeries: Add missing legend width migration (#50551) --- .../plugins/panel/timeseries/migrations.test.ts | 17 +++++++++++++++++ .../app/plugins/panel/timeseries/migrations.ts | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/public/app/plugins/panel/timeseries/migrations.test.ts b/public/app/plugins/panel/timeseries/migrations.test.ts index 14310ce9fb6..6daee162a78 100644 --- a/public/app/plugins/panel/timeseries/migrations.test.ts +++ b/public/app/plugins/panel/timeseries/migrations.test.ts @@ -127,6 +127,23 @@ describe('Graph Migrations', () => { panel.options = graphPanelChangedHandler(panel, 'graph', old, prevFieldConfig); expect(panel).toMatchSnapshot(); }); + test('with sideWidth', () => { + const old: any = { + angular: { + legend: { + alignAsTable: true, + rightSide: true, + show: true, + sideWidth: 200, + total: true, + values: true, + }, + }, + }; + const panel = {} as PanelModel; + panel.options = graphPanelChangedHandler(panel, 'graph', old, prevFieldConfig); + expect(panel.options.legend.width).toBe(200); + }); }); describe('stacking', () => { diff --git a/public/app/plugins/panel/timeseries/migrations.ts b/public/app/plugins/panel/timeseries/migrations.ts index e27b75fc8b1..d49cca16649 100644 --- a/public/app/plugins/panel/timeseries/migrations.ts +++ b/public/app/plugins/panel/timeseries/migrations.ts @@ -345,6 +345,10 @@ export function flotToGraphOptions(angular: any): { fieldConfig: FieldConfigSour const enabledLegendValues = pickBy(angular.legend); options.legend.calcs = getReducersFromLegend(enabledLegendValues); } + + if (angular.legend.sideWidth) { + options.legend.width = angular.legend.sideWidth; + } } const tooltipConfig = angular.tooltip; From bda47df4adc00aa22808e4ab325b87cb696af21d Mon Sep 17 00:00:00 2001 From: George Robinson Date: Fri, 10 Jun 2022 08:59:58 +0100 Subject: [PATCH 06/79] Alerting: Invalid setting of enabled for unified alerting should return error (#49876) --- pkg/setting/setting_test.go | 118 ++++++++++++++++++++++-- pkg/setting/setting_unified_alerting.go | 20 ++-- 2 files changed, 124 insertions(+), 14 deletions(-) diff --git a/pkg/setting/setting_test.go b/pkg/setting/setting_test.go index 826e6ad4e3b..92f2a42a54d 100644 --- a/pkg/setting/setting_test.go +++ b/pkg/setting/setting_test.go @@ -489,7 +489,7 @@ func TestAlertingEnabled(t *testing.T) { }, }, { - desc: "when legacy alerting is enabled and unified is invalid (or not defined) [OSS]", + desc: "when legacy alerting is enabled and unified is not defined [OSS]", legacyAlertingEnabled: "true", unifiedAlertingEnabled: "", isEnterprise: false, @@ -507,7 +507,24 @@ func TestAlertingEnabled(t *testing.T) { }, }, { - desc: "when legacy alerting is enabled and unified is invalid (or not defined) [Enterprise]", + desc: "when legacy alerting is enabled and unified is invalid [OSS]", + legacyAlertingEnabled: "true", + unifiedAlertingEnabled: "invalid", + isEnterprise: false, + verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) { + err := readAlertingSettings(f) + require.NoError(t, err) + err = cfg.readFeatureToggles(f) + require.NoError(t, err) + err = cfg.ReadUnifiedAlertingSettings(f) + assert.EqualError(t, err, "failed to read unified alerting enabled setting: invalid value invalid, should be either true or false") + assert.Nil(t, cfg.UnifiedAlerting.Enabled) + assert.NotNil(t, AlertingEnabled) + assert.Equal(t, false, *AlertingEnabled) + }, + }, + { + desc: "when legacy alerting is enabled and unified is not defined [Enterprise]", legacyAlertingEnabled: "true", unifiedAlertingEnabled: "", isEnterprise: true, @@ -525,10 +542,27 @@ func TestAlertingEnabled(t *testing.T) { }, }, { - desc: "when legacy alerting is disabled and unified is invalid (or not defined) [OSS]", - legacyAlertingEnabled: "false", + desc: "when legacy alerting is enabled and unified is invalid [Enterprise]", + legacyAlertingEnabled: "true", unifiedAlertingEnabled: "invalid", isEnterprise: false, + verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) { + err := readAlertingSettings(f) + require.NoError(t, err) + err = cfg.readFeatureToggles(f) + require.NoError(t, err) + err = cfg.ReadUnifiedAlertingSettings(f) + assert.EqualError(t, err, "failed to read unified alerting enabled setting: invalid value invalid, should be either true or false") + assert.Nil(t, cfg.UnifiedAlerting.Enabled) + assert.NotNil(t, AlertingEnabled) + assert.Equal(t, false, *AlertingEnabled) + }, + }, + { + desc: "when legacy alerting is disabled and unified is not defined [OSS]", + legacyAlertingEnabled: "false", + unifiedAlertingEnabled: "", + isEnterprise: false, verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) { err := readAlertingSettings(f) require.NoError(t, err) @@ -543,9 +577,26 @@ func TestAlertingEnabled(t *testing.T) { }, }, { - desc: "when legacy alerting is disabled and unified is invalid (or not defined) [Enterprise]", + desc: "when legacy alerting is disabled and unified is invalid [OSS]", legacyAlertingEnabled: "false", unifiedAlertingEnabled: "invalid", + isEnterprise: false, + verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) { + err := readAlertingSettings(f) + require.NoError(t, err) + err = cfg.readFeatureToggles(f) + require.NoError(t, err) + err = cfg.ReadUnifiedAlertingSettings(f) + assert.EqualError(t, err, "failed to read unified alerting enabled setting: invalid value invalid, should be either true or false") + assert.Nil(t, cfg.UnifiedAlerting.Enabled) + assert.NotNil(t, AlertingEnabled) + assert.Equal(t, false, *AlertingEnabled) + }, + }, + { + desc: "when legacy alerting is disabled and unified is not defined [Enterprise]", + legacyAlertingEnabled: "false", + unifiedAlertingEnabled: "", isEnterprise: true, verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) { err := readAlertingSettings(f) @@ -561,10 +612,27 @@ func TestAlertingEnabled(t *testing.T) { }, }, { - desc: "when both are invalid (or not defined) [OSS]", - legacyAlertingEnabled: "invalid", + desc: "when legacy alerting is disabled and unified is invalid [Enterprise]", + legacyAlertingEnabled: "false", unifiedAlertingEnabled: "invalid", isEnterprise: false, + verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) { + err := readAlertingSettings(f) + require.NoError(t, err) + err = cfg.readFeatureToggles(f) + require.NoError(t, err) + err = cfg.ReadUnifiedAlertingSettings(f) + assert.EqualError(t, err, "failed to read unified alerting enabled setting: invalid value invalid, should be either true or false") + assert.Nil(t, cfg.UnifiedAlerting.Enabled) + assert.NotNil(t, AlertingEnabled) + assert.Equal(t, false, *AlertingEnabled) + }, + }, + { + desc: "when both are not defined [OSS]", + legacyAlertingEnabled: "", + unifiedAlertingEnabled: "", + isEnterprise: false, verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) { err := readAlertingSettings(f) require.NoError(t, err) @@ -578,9 +646,26 @@ func TestAlertingEnabled(t *testing.T) { }, }, { - desc: "when both are invalid (or not defined) [Enterprise]", + desc: "when both are not invalid [OSS]", legacyAlertingEnabled: "invalid", unifiedAlertingEnabled: "invalid", + isEnterprise: false, + verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) { + err := readAlertingSettings(f) + require.NoError(t, err) + err = cfg.readFeatureToggles(f) + require.NoError(t, err) + err = cfg.ReadUnifiedAlertingSettings(f) + assert.EqualError(t, err, "failed to read unified alerting enabled setting: invalid value invalid, should be either true or false") + assert.Nil(t, cfg.UnifiedAlerting.Enabled) + assert.NotNil(t, AlertingEnabled) + assert.Equal(t, false, *AlertingEnabled) + }, + }, + { + desc: "when both are not defined [Enterprise]", + legacyAlertingEnabled: "", + unifiedAlertingEnabled: "", isEnterprise: true, verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) { err := readAlertingSettings(f) @@ -594,6 +679,23 @@ func TestAlertingEnabled(t *testing.T) { assert.Nil(t, AlertingEnabled) }, }, + { + desc: "when both are not invalid [Enterprise]", + legacyAlertingEnabled: "invalid", + unifiedAlertingEnabled: "invalid", + isEnterprise: false, + verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) { + err := readAlertingSettings(f) + require.NoError(t, err) + err = cfg.readFeatureToggles(f) + require.NoError(t, err) + err = cfg.ReadUnifiedAlertingSettings(f) + assert.EqualError(t, err, "failed to read unified alerting enabled setting: invalid value invalid, should be either true or false") + assert.Nil(t, cfg.UnifiedAlerting.Enabled) + assert.NotNil(t, AlertingEnabled) + assert.Equal(t, false, *AlertingEnabled) + }, + }, { desc: "when both are false", legacyAlertingEnabled: "false", diff --git a/pkg/setting/setting_unified_alerting.go b/pkg/setting/setting_unified_alerting.go index e0bcc3da0e3..5762760c38d 100644 --- a/pkg/setting/setting_unified_alerting.go +++ b/pkg/setting/setting_unified_alerting.go @@ -103,31 +103,39 @@ func (cfg *Cfg) readUnifiedAlertingEnabledSetting(section *ini.Section) (*bool, // At present an invalid value is considered the same as no value. This means that a // spelling mistake in the string "false" could enable unified alerting rather // than disable it. This issue can be found here - unifiedAlerting, err := section.Key("enabled").Bool() - if err != nil { + hasEnabled := section.Key("enabled").Value() != "" + if !hasEnabled { // TODO: Remove in Grafana v9 if cfg.IsFeatureToggleEnabled("ngalert") { cfg.Logger.Warn("ngalert feature flag is deprecated: use unified alerting enabled setting instead") // feature flag overrides the legacy alerting setting legacyAlerting := false AlertingEnabled = &legacyAlerting - unifiedAlerting = true + unifiedAlerting := true return &unifiedAlerting, nil } // if legacy alerting has not been configured then enable unified alerting if AlertingEnabled == nil { - unifiedAlerting = true + unifiedAlerting := true return &unifiedAlerting, nil } // enable unified alerting and disable legacy alerting legacyAlerting := false AlertingEnabled = &legacyAlerting - unifiedAlerting = true + unifiedAlerting := true return &unifiedAlerting, nil } + unifiedAlerting, err := section.Key("enabled").Bool() + if err != nil { + // the value for unified alerting is invalid so disable all alerting + legacyAlerting := false + AlertingEnabled = &legacyAlerting + return nil, fmt.Errorf("invalid value %s, should be either true or false", section.Key("enabled")) + } + // If both legacy and unified alerting are enabled then return an error if AlertingEnabled != nil && *AlertingEnabled && unifiedAlerting { return nil, errors.New("legacy and unified alerting cannot both be enabled at the same time, please disable one of them and restart Grafana") @@ -149,7 +157,7 @@ func (cfg *Cfg) ReadUnifiedAlertingSettings(iniFile *ini.File) error { ua := iniFile.Section("unified_alerting") uaCfg.Enabled, err = cfg.readUnifiedAlertingEnabledSetting(ua) if err != nil { - return err + return fmt.Errorf("failed to read unified alerting enabled setting: %w", err) } uaCfg.DisabledOrgs = make(map[int64]struct{}) From 6a4906ef7eedee975a1a928e2284a981dff77ef0 Mon Sep 17 00:00:00 2001 From: Jack Westbrook Date: Fri, 10 Jun 2022 10:37:40 +0200 Subject: [PATCH 07/79] Grafana UI: Fix nonexistent indexof module for plugin tests (#50529) * fix(grafana-ui): prevent module resolution failure where component-indexof appears as indexof * chore(grafana-ui): update comment explaining need for alias plugin --- packages/grafana-ui/package.json | 2 ++ packages/grafana-ui/rollup.config.ts | 6 +++++- yarn.lock | 31 +++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index c580a71a296..124445a3bc5 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -97,6 +97,7 @@ "@babel/core": "7.18.2", "@grafana/tsconfig": "^1.2.0-rc1", "@mdx-js/react": "1.6.22", + "@rollup/plugin-alias": "^3.1.9", "@rollup/plugin-commonjs": "22.0.0", "@rollup/plugin-node-resolve": "13.3.0", "@storybook/addon-a11y": "6.4.21", @@ -169,6 +170,7 @@ "rollup-plugin-sourcemaps": "0.6.3", "rollup-plugin-svg-import": "^1.6.0", "rollup-plugin-terser": "7.0.2", + "rollup-plugin-visualizer": "^5.6.0", "sass-loader": "13.0.0", "storybook-dark-mode": "1.1.0", "style-loader": "3.3.1", diff --git a/packages/grafana-ui/rollup.config.ts b/packages/grafana-ui/rollup.config.ts index 9a1d647c43c..1979de79239 100644 --- a/packages/grafana-ui/rollup.config.ts +++ b/packages/grafana-ui/rollup.config.ts @@ -1,3 +1,4 @@ +import alias from '@rollup/plugin-alias'; import commonjs from '@rollup/plugin-commonjs'; import resolve from '@rollup/plugin-node-resolve'; import svg from 'rollup-plugin-svg-import'; @@ -39,6 +40,9 @@ const buildCjsPackage = ({ env }) => { '@emotion/css', ], plugins: [ + // rc-time-picker has a transitive dependency on component-indexof which + // when bundled via `component-classes` imports a nonexistent `indexof` module. + alias({ entries: [{ find: 'indexof', replacement: 'component-indexof' }] }), commonjs({ include: /node_modules/, ignoreTryCatch: false, @@ -49,4 +53,4 @@ const buildCjsPackage = ({ env }) => { ], }; }; -export default [buildCjsPackage({ env: 'development' }), buildCjsPackage({ env: 'production' })]; +export default [buildCjsPackage({ env: 'development' })]; diff --git a/yarn.lock b/yarn.lock index 3ff6306cc98..6ee0af96e06 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4684,6 +4684,7 @@ __metadata: "@react-aria/menu": 3.4.4 "@react-aria/overlays": 3.8.2 "@react-stately/menu": 3.2.7 + "@rollup/plugin-alias": ^3.1.9 "@rollup/plugin-commonjs": 22.0.0 "@rollup/plugin-node-resolve": 13.3.0 "@sentry/browser": 6.19.7 @@ -4796,6 +4797,7 @@ __metadata: rollup-plugin-sourcemaps: 0.6.3 rollup-plugin-svg-import: ^1.6.0 rollup-plugin-terser: 7.0.2 + rollup-plugin-visualizer: ^5.6.0 rxjs: 7.5.5 sass-loader: 13.0.0 slate: 0.47.8 @@ -7652,6 +7654,17 @@ __metadata: languageName: node linkType: hard +"@rollup/plugin-alias@npm:^3.1.9": + version: 3.1.9 + resolution: "@rollup/plugin-alias@npm:3.1.9" + dependencies: + slash: ^3.0.0 + peerDependencies: + rollup: ^1.20.0||^2.0.0 + checksum: cefae9dfb7c30f0dc78d24f4ad9ccb8a0878397b313c0fa9d0f519667394941c58a930d968d841e25aee43b0fb892d1e3f7edbb55e8197f191cce7da6a50b882 + languageName: node + linkType: hard + "@rollup/plugin-commonjs@npm:22.0.0": version: 22.0.0 resolution: "@rollup/plugin-commonjs@npm:22.0.0" @@ -26412,7 +26425,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.3, nanoid@npm:^3.3.4": +"nanoid@npm:^3.1.32, nanoid@npm:^3.3.3, nanoid@npm:^3.3.4": version: 3.3.4 resolution: "nanoid@npm:3.3.4" bin: @@ -32205,6 +32218,22 @@ __metadata: languageName: node linkType: hard +"rollup-plugin-visualizer@npm:^5.6.0": + version: 5.6.0 + resolution: "rollup-plugin-visualizer@npm:5.6.0" + dependencies: + nanoid: ^3.1.32 + open: ^8.4.0 + source-map: ^0.7.3 + yargs: ^17.3.1 + peerDependencies: + rollup: ^2.0.0 + bin: + rollup-plugin-visualizer: dist/bin/cli.js + checksum: 1036a3873f3b6a0c46d692538aaac5dc9f7a7b2a5aec9e7ab816aaf8a31576ae750f415e1019e401707d18490b634a982fbe8c22d9761cafbd9b12c756e7fd99 + languageName: node + linkType: hard + "rollup@npm:2.74.1": version: 2.74.1 resolution: "rollup@npm:2.74.1" From 2d6e69226be20a7f001c7792bda8df68d90287c6 Mon Sep 17 00:00:00 2001 From: Carl Bergquist Date: Fri, 10 Jun 2022 10:56:55 +0200 Subject: [PATCH 08/79] Instrumentation: Add feature toggle for logging requests instrumented as unknown (#50566) Signed-off-by: bergquist --- .../src/types/featureToggles.gen.ts | 1 + pkg/middleware/request_metrics.go | 18 ++++++++++++------ pkg/services/featuremgmt/registry.go | 5 +++++ pkg/services/featuremgmt/toggles_gen.go | 4 ++++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index e3b794c181a..3cc6fa27b0b 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -58,4 +58,5 @@ export interface FeatureToggles { prometheusWideSeries?: boolean; canvasPanelNesting?: boolean; cloudMonitoringExperimentalUI?: boolean; + logRequestsInstrumentedAsUnknown?: boolean; } diff --git a/pkg/middleware/request_metrics.go b/pkg/middleware/request_metrics.go index 654271e92cd..fb6c7a57117 100644 --- a/pkg/middleware/request_metrics.go +++ b/pkg/middleware/request_metrics.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/featuremgmt" @@ -46,6 +47,8 @@ func init() { // RequestMetrics is a middleware handler that instruments the request. func RequestMetrics(features featuremgmt.FeatureToggles) web.Handler { + log := log.New("middleware.request-metrics") + return func(res http.ResponseWriter, req *http.Request, c *web.Context) { if strings.HasPrefix(c.Req.URL.Path, "/public/") || c.Req.URL.Path == "robots.txt" || c.Req.URL.Path == "/metrics" { c.Next() @@ -58,15 +61,18 @@ func RequestMetrics(features featuremgmt.FeatureToggles) web.Handler { defer httpRequestsInFlight.Dec() c.Next() - handler := "unknown" - - if routeOperation, exists := RouteOperationNameFromContext(c.Req.Context()); exists { - handler = routeOperation - } - status := rw.Status() code := sanitizeCode(status) + handler := "unknown" + if routeOperation, exists := RouteOperationNameFromContext(c.Req.Context()); exists { + handler = routeOperation + } else { + if features.IsEnabled(featuremgmt.FlagLogRequestsInstrumentedAsUnknown) { + log.Warn("request instrumented as unknown", "path", c.Req.URL.Path, "status_code", status) + } + } + // avoiding the sanitize functions for in the new instrumentation // since they dont make much sense. We should remove them later. histogram := httpRequestDurationHistogram. diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 623ea09178f..1490b6be669 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -237,5 +237,10 @@ var ( State: FeatureStateAlpha, FrontendOnly: true, }, + { + Name: "logRequestsInstrumentedAsUnknown", + Description: "Logs the path for requests that are instrumented as unknown", + State: FeatureStateAlpha, + }, } ) diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index a911fcbacae..bbc6b55dfd7 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -174,4 +174,8 @@ const ( // FlagCloudMonitoringExperimentalUI // Use grafana-experimental UI in Cloud Monitoring FlagCloudMonitoringExperimentalUI = "cloudMonitoringExperimentalUI" + + // FlagLogRequestsInstrumentedAsUnknown + // Logs the path for requests that are instrumented as unknown + FlagLogRequestsInstrumentedAsUnknown = "logRequestsInstrumentedAsUnknown" ) From c8914ecd8aa68987d8875e74bb37bb0e2b342dee Mon Sep 17 00:00:00 2001 From: brendamuir <100768211+brendamuir@users.noreply.github.com> Date: Fri, 10 Jun 2022 11:27:08 +0200 Subject: [PATCH 09/79] Docs: Fixes typos and adds enterprise note (#50564) * Fixes typos and space errors * Docs: adds note for enterprise users * Run yarn prettier:write Co-authored-by: Armand Grillet --- docs/sources/alerting/migrating-alerts/_index.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/sources/alerting/migrating-alerts/_index.md b/docs/sources/alerting/migrating-alerts/_index.md index 094038351c0..12a1710194d 100644 --- a/docs/sources/alerting/migrating-alerts/_index.md +++ b/docs/sources/alerting/migrating-alerts/_index.md @@ -10,15 +10,17 @@ weight: 101 Grafana Alerting is enabled by default for new installations or existing installations whether or not legacy alerting is configured. +> **Note**: We recommend that Grafana Enterprise customers with more than a dozen Grafana dashboard alert rules do not upgrade and remain on legacy alerting for now by [opting out]({{< relref "opt-out/" >}}). If you do want to upgrade to Grafana Alerting, contact customer support. + Existing installations that do not use legacy alerting will have Grafana Alerting enabled by default unless alerting is disabled in the configuration. -Likewise, existing installations that use legacy alerting will be automatically upgraded to Grafana Alerting unless you have [opted out]({{< relref "opt-out/" >}})of Grafana Alerting before migration takes place. During the upgrade, legacy alerts are migrated to the new alerts type and no alerts or alerting data are lost. +Likewise, existing installations that use legacy alerting will be automatically upgraded to Grafana Alerting unless you have [opted out]({{< relref "opt-out/" >}}) of Grafana Alerting before migration takes place. During the upgrade, legacy alerts are migrated to the new alerts type and no alerts or alerting data are lost. -Once the upgrade has taken place, you still have the option to roll back [roll back]({{< relref "roll-back/" >}}) to legacy alerting. However, we do not recommend choosing this option. If you do choose to roll back, Grafana will restore your alerts to the alerts you had at the point in time when the upgrade took place. All new alerts and changes made exclusively in Grafana Alerting will be deleted. +Once the upgrade has taken place, you still have the option to [roll back]({{< relref "roll-back/" >}}) to legacy alerting. However, we do not recommend choosing this option. If you do choose to roll back, Grafana will restore your alerts to the alerts you had at the point in time when the upgrade took place. All new alerts and changes made exclusively in Grafana Alerting will be deleted. > **Note**: Cloud customers, who do not want to upgrade to Grafana Alerting, should contact customer support. -If you have opted out or rolled back, you can always choose to opt in [opt in]({{< relref "opt-in/" >}}) to Grafana Alerting at a later point in time. +If you have opted out or rolled back, you can always choose to [opt in]({{< relref "opt-in/" >}}) to Grafana Alerting at a later point in time. The following table provides details on the upgrade for Cloud, Enterprise, and OSS installations and the new Grafana Alerting UI. From c1d405a200b6fc99ee4d7dbcabb83670fcef7930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 10 Jun 2022 11:30:52 +0200 Subject: [PATCH 10/79] Search: Fixes margins and paddings and left position (#50570) --- packages/grafana-data/src/themes/createComponents.ts | 2 +- public/app/core/components/NavBar/Next/NavBarNext.tsx | 2 +- public/app/features/search/components/DashboardSearch.tsx | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/grafana-data/src/themes/createComponents.ts b/packages/grafana-data/src/themes/createComponents.ts index 314b9ba218e..7646160521d 100644 --- a/packages/grafana-data/src/themes/createComponents.ts +++ b/packages/grafana-data/src/themes/createComponents.ts @@ -90,7 +90,7 @@ export function createComponents(colors: ThemeColors, shadows: ThemeShadows): Th background: colors.mode === 'dark' ? 'rgba(63, 62, 62, 0.45)' : 'rgba(208, 209, 211, 0.24)', }, sidemenu: { - width: 48, + width: 57, }, menuTabs: { height: 41, diff --git a/public/app/core/components/NavBar/Next/NavBarNext.tsx b/public/app/core/components/NavBar/Next/NavBarNext.tsx index 6fbaee70c0f..fa1031aca37 100644 --- a/public/app/core/components/NavBar/Next/NavBarNext.tsx +++ b/public/app/core/components/NavBar/Next/NavBarNext.tsx @@ -205,7 +205,7 @@ const getStyles = (theme: GrafanaTheme2) => ({ zIndex: theme.zIndex.sidemenu, padding: `${theme.spacing(1)} 0`, position: 'relative', - width: `calc(${theme.spacing(7)} + 1px)`, + width: theme.components.sidemenu.width, borderRight: `1px solid ${theme.colors.border.weak}`, [theme.breakpoints.down('md')]: { diff --git a/public/app/features/search/components/DashboardSearch.tsx b/public/app/features/search/components/DashboardSearch.tsx index d283e0e8c9a..03bc8058289 100644 --- a/public/app/features/search/components/DashboardSearch.tsx +++ b/public/app/features/search/components/DashboardSearch.tsx @@ -144,10 +144,12 @@ const getStyles = stylesFactory((theme: GrafanaTheme2) => { z-index: ${theme.zIndex.sidemenu}; position: fixed; background: ${theme.colors.background.canvas}; + padding: ${theme.spacing(1)}; ${theme.breakpoints.up('md')} { left: ${theme.components.sidemenu.width}px; z-index: ${theme.zIndex.navbarFixed + 1}; + padding: ${theme.spacing(2)}; } `, container: css` @@ -155,11 +157,9 @@ const getStyles = stylesFactory((theme: GrafanaTheme2) => { flex-direction: column; max-width: 1400px; margin: 0 auto; - padding: ${theme.spacing(2)}; + padding: ${theme.spacing(1)}; background: ${theme.colors.background.primary}; border: 1px solid ${theme.components.panel.borderColor}; - margin-top: ${theme.spacing(4)}; - height: 100%; ${theme.breakpoints.up('md')} { From f132cf33884b441ccaae1c358ca04a340021cc74 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Jun 2022 09:37:54 +0000 Subject: [PATCH 11/79] Update dependency @react-aria/focus to v3.6.0 (#50506) Co-authored-by: Renovate Bot --- package.json | 2 +- packages/grafana-ui/package.json | 2 +- yarn.lock | 80 ++++++++++++++++++++++++++++++-- 3 files changed, 79 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1df77b45723..cbfcaab8b6f 100644 --- a/package.json +++ b/package.json @@ -271,7 +271,7 @@ "@popperjs/core": "2.11.5", "@react-aria/button": "3.4.4", "@react-aria/dialog": "3.1.9", - "@react-aria/focus": "3.5.5", + "@react-aria/focus": "3.6.0", "@react-aria/interactions": "3.8.4", "@react-aria/menu": "3.4.4", "@react-aria/overlays": "3.8.2", diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 124445a3bc5..667d75b9922 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -41,7 +41,7 @@ "@popperjs/core": "2.11.5", "@react-aria/button": "3.4.4", "@react-aria/dialog": "3.1.9", - "@react-aria/focus": "3.5.5", + "@react-aria/focus": "3.6.0", "@react-aria/menu": "3.4.4", "@react-aria/overlays": "3.8.2", "@react-stately/menu": "3.2.7", diff --git a/yarn.lock b/yarn.lock index 6ee0af96e06..9b295f86b84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4680,7 +4680,7 @@ __metadata: "@popperjs/core": 2.11.5 "@react-aria/button": 3.4.4 "@react-aria/dialog": 3.1.9 - "@react-aria/focus": 3.5.5 + "@react-aria/focus": 3.6.0 "@react-aria/menu": 3.4.4 "@react-aria/overlays": 3.8.2 "@react-stately/menu": 3.2.7 @@ -7327,7 +7327,22 @@ __metadata: languageName: node linkType: hard -"@react-aria/focus@npm:3.5.5, @react-aria/focus@npm:^3.5.5": +"@react-aria/focus@npm:3.6.0": + version: 3.6.0 + resolution: "@react-aria/focus@npm:3.6.0" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-aria/interactions": ^3.9.0 + "@react-aria/utils": ^3.13.0 + "@react-types/shared": ^3.13.0 + clsx: ^1.1.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 5b459ec9e7150c58bc515cc70db87d3fc10df42b171b69f021dee339e71fecafe4055f08c423f1e006643aa678b08e975312c5f02e474d7342c1ad24d6e26429 + languageName: node + linkType: hard + +"@react-aria/focus@npm:^3.5.5": version: 3.5.5 resolution: "@react-aria/focus@npm:3.5.5" dependencies: @@ -7372,6 +7387,19 @@ __metadata: languageName: node linkType: hard +"@react-aria/interactions@npm:^3.9.0": + version: 3.9.0 + resolution: "@react-aria/interactions@npm:3.9.0" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-aria/utils": ^3.13.0 + "@react-types/shared": ^3.13.0 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 783d4fa59bda9329f4977d8e83c88555c3376c489e40d97c974c7725ebf286849ed149f8e20226fbab070bd45356e5ba01ace328d2ed785f0f40e28790e7b0d5 + languageName: node + linkType: hard + "@react-aria/menu@npm:3.4.4": version: 3.4.4 resolution: "@react-aria/menu@npm:3.4.4" @@ -7445,6 +7473,17 @@ __metadata: languageName: node linkType: hard +"@react-aria/ssr@npm:^3.2.0": + version: 3.2.0 + resolution: "@react-aria/ssr@npm:3.2.0" + dependencies: + "@babel/runtime": ^7.6.2 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 30468b436f9d56636eb3fb89cbbbf861c55ad841e255e529d225a0efd9c23628badaee72c42ab3870b5f17a9d5b5163564a64e9b928565230a090cb4f7de23bf + languageName: node + linkType: hard + "@react-aria/utils@npm:3.12.0, @react-aria/utils@npm:^3.12.0": version: 3.12.0 resolution: "@react-aria/utils@npm:3.12.0" @@ -7460,6 +7499,21 @@ __metadata: languageName: node linkType: hard +"@react-aria/utils@npm:^3.13.0": + version: 3.13.0 + resolution: "@react-aria/utils@npm:3.13.0" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-aria/ssr": ^3.2.0 + "@react-stately/utils": ^3.5.0 + "@react-types/shared": ^3.13.0 + clsx: ^1.1.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 7a154d6258adf6205ac367def0fcaf1b3fd0db3644c43481b2cb6eb9bc0f35ebddab9ecbddbec8dee92bbf5e38b63222137e1df5c26e5c8c1775ffa0e0b172fa + languageName: node + linkType: hard + "@react-aria/visually-hidden@npm:^3.2.8": version: 3.2.8 resolution: "@react-aria/visually-hidden@npm:3.2.8" @@ -7568,6 +7622,17 @@ __metadata: languageName: node linkType: hard +"@react-stately/utils@npm:^3.5.0": + version: 3.5.0 + resolution: "@react-stately/utils@npm:3.5.0" + dependencies: + "@babel/runtime": ^7.6.2 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: e948b783a01325a5396c46915508981152fcc02a8738c848342c8ecfbc64bdefc14530616d8ccfb6680fc85f24d0546f3df3686b189a9fbaa86ccbbe29e2fe1b + languageName: node + linkType: hard + "@react-types/button@npm:3.4.5, @react-types/button@npm:^3.4.5": version: 3.4.5 resolution: "@react-types/button@npm:3.4.5" @@ -7634,6 +7699,15 @@ __metadata: languageName: node linkType: hard +"@react-types/shared@npm:^3.13.0": + version: 3.13.0 + resolution: "@react-types/shared@npm:3.13.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 59b94fd8ef8fbb8edeb44e233113450a15fee107f231eaced043a1acb820c7fb8bf690623a0f85e2a638a870ee074919d6f89263ad2395c26e363ae25b102bdf + languageName: node + linkType: hard + "@reduxjs/toolkit@npm:1.8.1": version: 1.8.1 resolution: "@reduxjs/toolkit@npm:1.8.1" @@ -20633,7 +20707,7 @@ __metadata: "@popperjs/core": 2.11.5 "@react-aria/button": 3.4.4 "@react-aria/dialog": 3.1.9 - "@react-aria/focus": 3.5.5 + "@react-aria/focus": 3.6.0 "@react-aria/interactions": 3.8.4 "@react-aria/menu": 3.4.4 "@react-aria/overlays": 3.8.2 From 2d1ab276360133233eb47b7e0590ba26d9c31933 Mon Sep 17 00:00:00 2001 From: Jack Westbrook Date: Fri, 10 Jun 2022 11:47:29 +0200 Subject: [PATCH 12/79] Grafana UI: Put back production build in rollup config (#50575) --- packages/grafana-ui/package.json | 1 - packages/grafana-ui/rollup.config.ts | 2 +- yarn.lock | 19 +------------------ 3 files changed, 2 insertions(+), 20 deletions(-) diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 667d75b9922..4fa504c4180 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -170,7 +170,6 @@ "rollup-plugin-sourcemaps": "0.6.3", "rollup-plugin-svg-import": "^1.6.0", "rollup-plugin-terser": "7.0.2", - "rollup-plugin-visualizer": "^5.6.0", "sass-loader": "13.0.0", "storybook-dark-mode": "1.1.0", "style-loader": "3.3.1", diff --git a/packages/grafana-ui/rollup.config.ts b/packages/grafana-ui/rollup.config.ts index 1979de79239..b5751895103 100644 --- a/packages/grafana-ui/rollup.config.ts +++ b/packages/grafana-ui/rollup.config.ts @@ -53,4 +53,4 @@ const buildCjsPackage = ({ env }) => { ], }; }; -export default [buildCjsPackage({ env: 'development' })]; +export default [buildCjsPackage({ env: 'development' }), buildCjsPackage({ env: 'production' })]; diff --git a/yarn.lock b/yarn.lock index 9b295f86b84..e0b445bc62a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4797,7 +4797,6 @@ __metadata: rollup-plugin-sourcemaps: 0.6.3 rollup-plugin-svg-import: ^1.6.0 rollup-plugin-terser: 7.0.2 - rollup-plugin-visualizer: ^5.6.0 rxjs: 7.5.5 sass-loader: 13.0.0 slate: 0.47.8 @@ -26499,7 +26498,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.1.32, nanoid@npm:^3.3.3, nanoid@npm:^3.3.4": +"nanoid@npm:^3.3.3, nanoid@npm:^3.3.4": version: 3.3.4 resolution: "nanoid@npm:3.3.4" bin: @@ -32292,22 +32291,6 @@ __metadata: languageName: node linkType: hard -"rollup-plugin-visualizer@npm:^5.6.0": - version: 5.6.0 - resolution: "rollup-plugin-visualizer@npm:5.6.0" - dependencies: - nanoid: ^3.1.32 - open: ^8.4.0 - source-map: ^0.7.3 - yargs: ^17.3.1 - peerDependencies: - rollup: ^2.0.0 - bin: - rollup-plugin-visualizer: dist/bin/cli.js - checksum: 1036a3873f3b6a0c46d692538aaac5dc9f7a7b2a5aec9e7ab816aaf8a31576ae750f415e1019e401707d18490b634a982fbe8c22d9761cafbd9b12c756e7fd99 - languageName: node - linkType: hard - "rollup@npm:2.74.1": version: 2.74.1 resolution: "rollup@npm:2.74.1" From 363f11156bca9cdeabdcfc29f51aa020e8f94f3f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Jun 2022 11:54:20 +0200 Subject: [PATCH 13/79] Update dependency @react-aria/dialog to v3.2.0 (#50505) Co-authored-by: Renovate Bot --- package.json | 2 +- packages/grafana-ui/package.json | 2 +- yarn.lock | 62 ++++++++++++++++++++++---------- 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index cbfcaab8b6f..9e96bd945fa 100644 --- a/package.json +++ b/package.json @@ -270,7 +270,7 @@ "@opentelemetry/semantic-conventions": "1.3.1", "@popperjs/core": "2.11.5", "@react-aria/button": "3.4.4", - "@react-aria/dialog": "3.1.9", + "@react-aria/dialog": "3.2.0", "@react-aria/focus": "3.6.0", "@react-aria/interactions": "3.8.4", "@react-aria/menu": "3.4.4", diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 4fa504c4180..819039f6293 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -40,7 +40,7 @@ "@monaco-editor/react": "4.3.1", "@popperjs/core": "2.11.5", "@react-aria/button": "3.4.4", - "@react-aria/dialog": "3.1.9", + "@react-aria/dialog": "3.2.0", "@react-aria/focus": "3.6.0", "@react-aria/menu": "3.4.4", "@react-aria/overlays": "3.8.2", diff --git a/yarn.lock b/yarn.lock index e0b445bc62a..1c8577a1ebe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4679,7 +4679,7 @@ __metadata: "@monaco-editor/react": 4.3.1 "@popperjs/core": 2.11.5 "@react-aria/button": 3.4.4 - "@react-aria/dialog": 3.1.9 + "@react-aria/dialog": 3.2.0 "@react-aria/focus": 3.6.0 "@react-aria/menu": 3.4.4 "@react-aria/overlays": 3.8.2 @@ -7311,22 +7311,22 @@ __metadata: languageName: node linkType: hard -"@react-aria/dialog@npm:3.1.9": - version: 3.1.9 - resolution: "@react-aria/dialog@npm:3.1.9" +"@react-aria/dialog@npm:3.2.0": + version: 3.2.0 + resolution: "@react-aria/dialog@npm:3.2.0" dependencies: "@babel/runtime": ^7.6.2 - "@react-aria/focus": ^3.5.5 - "@react-aria/utils": ^3.12.0 - "@react-stately/overlays": ^3.2.0 - "@react-types/dialog": ^3.3.5 + "@react-aria/focus": ^3.6.0 + "@react-aria/utils": ^3.13.0 + "@react-stately/overlays": ^3.3.0 + "@react-types/dialog": ^3.4.0 peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 - checksum: 6ce0f22d0804bb4cb86521c0da81024a52605490f0deb12162cc56f416544f4e4015d38c401dfed906d3101310a5b6c122d96ad2d506d444c671a29011c6df98 + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: a62a7c71bf65fc28f7e863bd1f250977f16043097b0863583cebd235d8d5abfed40789a0d00af06fb398afe5d16219e1f694d017d0bd0ff0d8479fa041409f19 languageName: node linkType: hard -"@react-aria/focus@npm:3.6.0": +"@react-aria/focus@npm:3.6.0, @react-aria/focus@npm:^3.6.0": version: 3.6.0 resolution: "@react-aria/focus@npm:3.6.0" dependencies: @@ -7567,6 +7567,19 @@ __metadata: languageName: node linkType: hard +"@react-stately/overlays@npm:^3.3.0": + version: 3.3.0 + resolution: "@react-stately/overlays@npm:3.3.0" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/utils": ^3.5.0 + "@react-types/overlays": ^3.6.0 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 19d4e9f2c9fb745704f9400d54a7b78766bd3f7592df9253ddf11a9231b2a21f6412166d714d7cea8ace28db33791989c092ac1ab7f566a8b53f3c9d17e9b18c + languageName: node + linkType: hard + "@react-stately/selection@npm:^3.9.4": version: 3.9.4 resolution: "@react-stately/selection@npm:3.9.4" @@ -7654,15 +7667,15 @@ __metadata: languageName: node linkType: hard -"@react-types/dialog@npm:^3.3.5": - version: 3.3.5 - resolution: "@react-types/dialog@npm:3.3.5" +"@react-types/dialog@npm:^3.4.0": + version: 3.4.0 + resolution: "@react-types/dialog@npm:3.4.0" dependencies: - "@react-types/overlays": ^3.5.5 - "@react-types/shared": ^3.12.0 + "@react-types/overlays": ^3.6.0 + "@react-types/shared": ^3.13.0 peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 - checksum: 50847ec5ccb6b4cffb62fb19fb7d026d3965cd02db293b247f415ace1e394e61291637dcd83159ec7de99749994a3a67ab45ec927a810e4677c2f4bdb7ed54bd + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 88d6ea76b345474e17d20e61d716151a254bcce7df5c0e86d23219db0dc4e970cc3a3c29488910bf48ec907282bbb67de99256082017aa97971dffe33f0b8ff4 languageName: node linkType: hard @@ -7689,6 +7702,17 @@ __metadata: languageName: node linkType: hard +"@react-types/overlays@npm:^3.6.0": + version: 3.6.0 + resolution: "@react-types/overlays@npm:3.6.0" + dependencies: + "@react-types/shared": ^3.13.0 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 9f6c773bcb4877c9847d79946e90286d335f5d0306e4f656be068bfeaa395bb685231614155282395356bf9acffc7a9086fc4aca8b117b4231e5985332cbe8fc + languageName: node + linkType: hard + "@react-types/shared@npm:3.12.0, @react-types/shared@npm:^3.12.0": version: 3.12.0 resolution: "@react-types/shared@npm:3.12.0" @@ -20705,7 +20729,7 @@ __metadata: "@pmmmwh/react-refresh-webpack-plugin": 0.5.6 "@popperjs/core": 2.11.5 "@react-aria/button": 3.4.4 - "@react-aria/dialog": 3.1.9 + "@react-aria/dialog": 3.2.0 "@react-aria/focus": 3.6.0 "@react-aria/interactions": 3.8.4 "@react-aria/menu": 3.4.4 From 502c6e4e6b0e160a9d5bb49d5f432d4bc940f60c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Jun 2022 09:59:37 +0000 Subject: [PATCH 14/79] Update dependency @react-aria/interactions to v3.9.0 (#50579) Co-authored-by: Renovate Bot --- package.json | 2 +- yarn.lock | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 9e96bd945fa..add10c6848f 100644 --- a/package.json +++ b/package.json @@ -272,7 +272,7 @@ "@react-aria/button": "3.4.4", "@react-aria/dialog": "3.2.0", "@react-aria/focus": "3.6.0", - "@react-aria/interactions": "3.8.4", + "@react-aria/interactions": "3.9.0", "@react-aria/menu": "3.4.4", "@react-aria/overlays": "3.8.2", "@react-aria/utils": "3.12.0", diff --git a/yarn.lock b/yarn.lock index 1c8577a1ebe..a539999587b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7373,20 +7373,7 @@ __metadata: languageName: node linkType: hard -"@react-aria/interactions@npm:3.8.4, @react-aria/interactions@npm:^3.8.4": - version: 3.8.4 - resolution: "@react-aria/interactions@npm:3.8.4" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-aria/utils": ^3.12.0 - "@react-types/shared": ^3.12.0 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 - checksum: 0fae18cfd47e73fb92355312ff3fe54bc469e9b67b97e6d7d566b894433c95232128352c2ff439472a449f3097c866732ba6a7c658efa6f9391f02d3e0e977ef - languageName: node - linkType: hard - -"@react-aria/interactions@npm:^3.9.0": +"@react-aria/interactions@npm:3.9.0, @react-aria/interactions@npm:^3.9.0": version: 3.9.0 resolution: "@react-aria/interactions@npm:3.9.0" dependencies: @@ -7399,6 +7386,19 @@ __metadata: languageName: node linkType: hard +"@react-aria/interactions@npm:^3.8.4": + version: 3.8.4 + resolution: "@react-aria/interactions@npm:3.8.4" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-aria/utils": ^3.12.0 + "@react-types/shared": ^3.12.0 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 + checksum: 0fae18cfd47e73fb92355312ff3fe54bc469e9b67b97e6d7d566b894433c95232128352c2ff439472a449f3097c866732ba6a7c658efa6f9391f02d3e0e977ef + languageName: node + linkType: hard + "@react-aria/menu@npm:3.4.4": version: 3.4.4 resolution: "@react-aria/menu@npm:3.4.4" @@ -20731,7 +20731,7 @@ __metadata: "@react-aria/button": 3.4.4 "@react-aria/dialog": 3.2.0 "@react-aria/focus": 3.6.0 - "@react-aria/interactions": 3.8.4 + "@react-aria/interactions": 3.9.0 "@react-aria/menu": 3.4.4 "@react-aria/overlays": 3.8.2 "@react-aria/utils": 3.12.0 From 9a85a2e4410266270a1cd59db1937c7ba248966e Mon Sep 17 00:00:00 2001 From: Levente Balogh Date: Fri, 10 Jun 2022 12:13:31 +0200 Subject: [PATCH 15/79] Data Connections: Create a new top-level page (#50018) * Feature Flags: introduce a flag for enabling the Data Connections page * Feature Flags: generate schemas * Navigation: add navigation weight for the Data Connections page * NavLink: add a comment pointing out where icon names can be looked up * NavTree: add a new page called Data Connections * fix(Api): prefix the navigation IDs with the parent ("data-connections") * feat(Frontend): add a basic page with four tabs * feat(Plugins): add a hook for importing an app plugin * feat(Plugins): add a component for loading app plugins anywhere * feat(Data Connections): load the cloud-onboarding app under the "Cloud onboarding" tab * feat(Data Connections): generate a proper nav model to highlight active tabs * test(Data Connections): add tests * refactor(Data Connections): update temporary text content This is only used as a placeholder until the tabs are under development. * refactor(Data Cnnnections): move /pages to /tabs * refactor(Data Connections): remove the `types.ts` file as it is not referenced by any module * feat(Data Connections): only register routes if feature is enabled --- .../src/types/featureToggles.gen.ts | 1 + pkg/api/dtos/index.go | 3 +- pkg/api/index.go | 56 + pkg/services/featuremgmt/registry.go | 4 + pkg/services/featuremgmt/toggles_gen.go | 4 + .../DataConnectionsPage.test.tsx | 42 + .../data-connections/DataConnectionsPage.tsx | 30 + .../__mocks__/store.navIndex.mock.ts | 2519 +++++++++++++++++ .../features/data-connections/constants.ts | 12 + .../data-connections/hooks/useNavModel.ts | 26 + .../app/features/data-connections/routes.tsx | 21 + .../CloudIntegrations/CloudIntegrations.tsx | 25 + .../tabs/CloudIntegrations/index.tsx | 1 + .../tabs/DataSources/DataSources.tsx | 5 + .../tabs/DataSources/index.tsx | 1 + .../data-connections/tabs/Plugins/Plugins.tsx | 5 + .../data-connections/tabs/Plugins/index.tsx | 1 + .../tabs/RecordedQueries/RecordedQueries.tsx | 5 + .../tabs/RecordedQueries/index.tsx | 1 + .../components/AppPluginLoader.test.tsx | 132 + .../plugins/components/AppPluginLoader.tsx | 44 + .../hooks/tests/useImportAppPlugin.test.tsx | 150 + .../plugins/hooks/useImportAppPlugin.ts | 26 + public/app/routes/routes.tsx | 2 + 24 files changed, 3115 insertions(+), 1 deletion(-) create mode 100644 public/app/features/data-connections/DataConnectionsPage.test.tsx create mode 100644 public/app/features/data-connections/DataConnectionsPage.tsx create mode 100644 public/app/features/data-connections/__mocks__/store.navIndex.mock.ts create mode 100644 public/app/features/data-connections/constants.ts create mode 100644 public/app/features/data-connections/hooks/useNavModel.ts create mode 100644 public/app/features/data-connections/routes.tsx create mode 100644 public/app/features/data-connections/tabs/CloudIntegrations/CloudIntegrations.tsx create mode 100644 public/app/features/data-connections/tabs/CloudIntegrations/index.tsx create mode 100644 public/app/features/data-connections/tabs/DataSources/DataSources.tsx create mode 100644 public/app/features/data-connections/tabs/DataSources/index.tsx create mode 100644 public/app/features/data-connections/tabs/Plugins/Plugins.tsx create mode 100644 public/app/features/data-connections/tabs/Plugins/index.tsx create mode 100644 public/app/features/data-connections/tabs/RecordedQueries/RecordedQueries.tsx create mode 100644 public/app/features/data-connections/tabs/RecordedQueries/index.tsx create mode 100644 public/app/features/plugins/components/AppPluginLoader.test.tsx create mode 100644 public/app/features/plugins/components/AppPluginLoader.tsx create mode 100644 public/app/features/plugins/hooks/tests/useImportAppPlugin.test.tsx create mode 100644 public/app/features/plugins/hooks/useImportAppPlugin.ts diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 3cc6fa27b0b..e140056e6ee 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -59,4 +59,5 @@ export interface FeatureToggles { canvasPanelNesting?: boolean; cloudMonitoringExperimentalUI?: boolean; logRequestsInstrumentedAsUnknown?: boolean; + dataConnectionsConsole?: boolean; } diff --git a/pkg/api/dtos/index.go b/pkg/api/dtos/index.go index caba326a2a8..d951bcfc87a 100644 --- a/pkg/api/dtos/index.go +++ b/pkg/api/dtos/index.go @@ -42,6 +42,7 @@ const ( WeightDashboard WeightExplore WeightAlerting + WeightDataConnections WeightPlugin WeightConfig WeightAdmin @@ -61,7 +62,7 @@ type NavLink struct { Description string `json:"description,omitempty"` Section string `json:"section,omitempty"` SubTitle string `json:"subTitle,omitempty"` - Icon string `json:"icon,omitempty"` + Icon string `json:"icon,omitempty"` // Available icons can be browsed in Storybook: https://developers.grafana.com/ui/latest/index.html?path=/story/docs-overview-icon--icons-overview Img string `json:"img,omitempty"` Url string `json:"url,omitempty"` Target string `json:"target,omitempty"` diff --git a/pkg/api/index.go b/pkg/api/index.go index 8aa9a327461..c288c653817 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -245,6 +245,10 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool, prefs * navTree = append(navTree, hs.buildAlertNavLinks(c)...) } + if hs.Features.IsEnabled(featuremgmt.FlagDataConnectionsConsole) { + navTree = append(navTree, hs.buildDataConnectionsNavLink(c)) + } + appLinks, err := hs.getAppLinks(c) if err != nil { return nil, err @@ -630,6 +634,58 @@ func (hs *HTTPServer) buildCreateNavLinks(c *models.ReqContext) []*dtos.NavLink return children } +func (hs *HTTPServer) buildDataConnectionsNavLink(c *models.ReqContext) *dtos.NavLink { + var children []*dtos.NavLink + var navLink *dtos.NavLink + + baseId := "data-connections" + baseUrl := hs.Cfg.AppSubURL + "/" + baseId + + children = append(children, &dtos.NavLink{ + Id: baseId + "-datasources", + Text: "Data sources", + Icon: "database", + Description: "Add and configure data sources", + Url: baseUrl + "/datasources", + }) + + children = append(children, &dtos.NavLink{ + Id: baseId + "-plugins", + Text: "Plugins", + Icon: "plug", + Description: "Manage plugins", + Url: baseUrl + "/plugins", + }) + + children = append(children, &dtos.NavLink{ + Id: baseId + "-cloud-integrations", + Text: "Cloud integrations", + Icon: "bolt", + Description: "Manage your cloud integrations", + Url: baseUrl + "/cloud-integrations", + }) + + children = append(children, &dtos.NavLink{ + Id: baseId + "-recorded-queries", + Text: "Recorded queries", + Icon: "record-audio", + Description: "Manage your recorded queries", + Url: baseUrl + "/recorded-queries", + }) + + navLink = &dtos.NavLink{ + Text: "Data Connections", + Icon: "link", + Id: baseId, + Url: baseUrl, + Children: children, + Section: dtos.NavSectionCore, + SortWeight: dtos.WeightDataConnections, + } + + return navLink +} + func (hs *HTTPServer) buildAdminNavLinks(c *models.ReqContext) []*dtos.NavLink { hasAccess := ac.HasAccess(hs.AccessControl, c) hasGlobalAccess := ac.HasGlobalAccess(hs.AccessControl, c) diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 1490b6be669..416f5da8ef8 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -240,6 +240,10 @@ var ( { Name: "logRequestsInstrumentedAsUnknown", Description: "Logs the path for requests that are instrumented as unknown", + }, + { + Name: "dataConnectionsConsole", + Description: "Enables a new top-level page called Data Connections. This page is an experiment for better grouping of installing / configuring data sources and other plugins.", State: FeatureStateAlpha, }, } diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index bbc6b55dfd7..ec83161bcf8 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -178,4 +178,8 @@ const ( // FlagLogRequestsInstrumentedAsUnknown // Logs the path for requests that are instrumented as unknown FlagLogRequestsInstrumentedAsUnknown = "logRequestsInstrumentedAsUnknown" + + // FlagDataConnectionsConsole + // Enables a new top-level page called Data Connections. This page is an experiment for better grouping of installing / configuring data sources and other plugins. + FlagDataConnectionsConsole = "dataConnectionsConsole" ) diff --git a/public/app/features/data-connections/DataConnectionsPage.test.tsx b/public/app/features/data-connections/DataConnectionsPage.test.tsx new file mode 100644 index 00000000000..3498905301f --- /dev/null +++ b/public/app/features/data-connections/DataConnectionsPage.test.tsx @@ -0,0 +1,42 @@ +import { render, RenderResult, screen } from '@testing-library/react'; +import React from 'react'; +import { Provider } from 'react-redux'; +import { Router } from 'react-router-dom'; + +import { locationService } from '@grafana/runtime'; +import { configureStore } from 'app/store/configureStore'; + +import DataConnectionsPage from './DataConnectionsPage'; +import navIndex from './__mocks__/store.navIndex.mock'; +import { ROUTE_BASE_ID } from './constants'; + +const renderPage = (path = `/${ROUTE_BASE_ID}`): RenderResult => { + // @ts-ignore + const store = configureStore({ navIndex }); + locationService.push(path); + + return render( + + + + + + ); +}; + +describe('Data Connections Page', () => { + test('shows all the four tabs', async () => { + renderPage(); + + expect(await screen.findByLabelText('Tab Data sources')).toBeVisible(); + expect(await screen.findByLabelText('Tab Plugins')).toBeVisible(); + expect(await screen.findByLabelText('Tab Cloud integrations')).toBeVisible(); + expect(await screen.findByLabelText('Tab Recorded queries')).toBeVisible(); + }); + + test('shows the "Data sources" tab by default', async () => { + renderPage(); + + expect(await screen.findByText('The list of data sources is under development.')).toBeVisible(); + }); +}); diff --git a/public/app/features/data-connections/DataConnectionsPage.tsx b/public/app/features/data-connections/DataConnectionsPage.tsx new file mode 100644 index 00000000000..0cf34e69871 --- /dev/null +++ b/public/app/features/data-connections/DataConnectionsPage.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { Route, Switch } from 'react-router-dom'; + +import { Page } from 'app/core/components/Page/Page'; + +import { ROUTES } from './constants'; +import { useNavModel } from './hooks/useNavModel'; +import { CloudIntegrations } from './tabs/CloudIntegrations'; +import { DataSources } from './tabs/DataSources'; +import { Plugins } from './tabs/Plugins'; +import { RecordedQueries } from './tabs/RecordedQueries'; + +export default function DataConnectionsPage(): React.ReactElement | null { + const navModel = useNavModel(); + + return ( + + + + + + + + {/* Default page */} + + + + + ); +} diff --git a/public/app/features/data-connections/__mocks__/store.navIndex.mock.ts b/public/app/features/data-connections/__mocks__/store.navIndex.mock.ts new file mode 100644 index 00000000000..4546a29ec7c --- /dev/null +++ b/public/app/features/data-connections/__mocks__/store.navIndex.mock.ts @@ -0,0 +1,2519 @@ +export default { + dashboards: { + id: 'dashboards', + text: 'Dashboards', + section: 'core', + subTitle: 'Manage dashboards and folders', + icon: 'apps', + url: '/dashboards', + sortWeight: -1800, + children: [ + { + id: 'manage-dashboards', + text: 'Browse', + icon: 'sitemap', + url: '/dashboards', + }, + { + id: 'playlists', + text: 'Playlists', + icon: 'presentation-play', + url: '/playlists', + }, + { + id: 'snapshots', + text: 'Snapshots', + icon: 'camera', + url: '/dashboard/snapshots', + }, + { + id: 'library-panels', + text: 'Library panels', + icon: 'library-panel', + url: '/library-panels', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'new-dashboard', + text: 'New dashboard', + icon: 'plus', + url: '/dashboard/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'new-folder', + text: 'New folder', + subTitle: 'Create a new folder to organize your dashboards', + icon: 'plus', + url: '/dashboards/folder/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'import', + text: 'Import', + subTitle: 'Import dashboard from file or Grafana.com', + icon: 'plus', + url: '/dashboard/import', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + 'manage-dashboards': { + id: 'manage-dashboards', + text: 'Browse', + icon: 'sitemap', + url: '/dashboards', + parentItem: { + id: 'dashboards', + text: 'Dashboards', + section: 'core', + subTitle: 'Manage dashboards and folders', + icon: 'apps', + url: '/dashboards', + sortWeight: -1800, + children: [ + { + id: 'manage-dashboards', + text: 'Browse', + icon: 'sitemap', + url: '/dashboards', + }, + { + id: 'playlists', + text: 'Playlists', + icon: 'presentation-play', + url: '/playlists', + }, + { + id: 'snapshots', + text: 'Snapshots', + icon: 'camera', + url: '/dashboard/snapshots', + }, + { + id: 'library-panels', + text: 'Library panels', + icon: 'library-panel', + url: '/library-panels', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'new-dashboard', + text: 'New dashboard', + icon: 'plus', + url: '/dashboard/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'new-folder', + text: 'New folder', + subTitle: 'Create a new folder to organize your dashboards', + icon: 'plus', + url: '/dashboards/folder/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'import', + text: 'Import', + subTitle: 'Import dashboard from file or Grafana.com', + icon: 'plus', + url: '/dashboard/import', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + playlists: { + id: 'playlists', + text: 'Playlists', + icon: 'presentation-play', + url: '/playlists', + parentItem: { + id: 'dashboards', + text: 'Dashboards', + section: 'core', + subTitle: 'Manage dashboards and folders', + icon: 'apps', + url: '/dashboards', + sortWeight: -1800, + children: [ + { + id: 'manage-dashboards', + text: 'Browse', + icon: 'sitemap', + url: '/dashboards', + }, + { + id: 'playlists', + text: 'Playlists', + icon: 'presentation-play', + url: '/playlists', + }, + { + id: 'snapshots', + text: 'Snapshots', + icon: 'camera', + url: '/dashboard/snapshots', + }, + { + id: 'library-panels', + text: 'Library panels', + icon: 'library-panel', + url: '/library-panels', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'new-dashboard', + text: 'New dashboard', + icon: 'plus', + url: '/dashboard/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'new-folder', + text: 'New folder', + subTitle: 'Create a new folder to organize your dashboards', + icon: 'plus', + url: '/dashboards/folder/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'import', + text: 'Import', + subTitle: 'Import dashboard from file or Grafana.com', + icon: 'plus', + url: '/dashboard/import', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + snapshots: { + id: 'snapshots', + text: 'Snapshots', + icon: 'camera', + url: '/dashboard/snapshots', + parentItem: { + id: 'dashboards', + text: 'Dashboards', + section: 'core', + subTitle: 'Manage dashboards and folders', + icon: 'apps', + url: '/dashboards', + sortWeight: -1800, + children: [ + { + id: 'manage-dashboards', + text: 'Browse', + icon: 'sitemap', + url: '/dashboards', + }, + { + id: 'playlists', + text: 'Playlists', + icon: 'presentation-play', + url: '/playlists', + }, + { + id: 'snapshots', + text: 'Snapshots', + icon: 'camera', + url: '/dashboard/snapshots', + }, + { + id: 'library-panels', + text: 'Library panels', + icon: 'library-panel', + url: '/library-panels', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'new-dashboard', + text: 'New dashboard', + icon: 'plus', + url: '/dashboard/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'new-folder', + text: 'New folder', + subTitle: 'Create a new folder to organize your dashboards', + icon: 'plus', + url: '/dashboards/folder/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'import', + text: 'Import', + subTitle: 'Import dashboard from file or Grafana.com', + icon: 'plus', + url: '/dashboard/import', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + 'library-panels': { + id: 'library-panels', + text: 'Library panels', + icon: 'library-panel', + url: '/library-panels', + parentItem: { + id: 'dashboards', + text: 'Dashboards', + section: 'core', + subTitle: 'Manage dashboards and folders', + icon: 'apps', + url: '/dashboards', + sortWeight: -1800, + children: [ + { + id: 'manage-dashboards', + text: 'Browse', + icon: 'sitemap', + url: '/dashboards', + }, + { + id: 'playlists', + text: 'Playlists', + icon: 'presentation-play', + url: '/playlists', + }, + { + id: 'snapshots', + text: 'Snapshots', + icon: 'camera', + url: '/dashboard/snapshots', + }, + { + id: 'library-panels', + text: 'Library panels', + icon: 'library-panel', + url: '/library-panels', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'new-dashboard', + text: 'New dashboard', + icon: 'plus', + url: '/dashboard/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'new-folder', + text: 'New folder', + subTitle: 'Create a new folder to organize your dashboards', + icon: 'plus', + url: '/dashboards/folder/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'import', + text: 'Import', + subTitle: 'Import dashboard from file or Grafana.com', + icon: 'plus', + url: '/dashboard/import', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + divider: { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + parentItem: { + id: 'alerting', + text: 'Alerting', + section: 'core', + subTitle: 'Alert rules and notifications', + icon: 'bell', + url: '/alerting/list', + sortWeight: -1600, + children: [ + { + id: 'alert-list', + text: 'Alert rules', + icon: 'list-ul', + url: '/alerting/list', + }, + { + id: 'receivers', + text: 'Contact points', + icon: 'comment-alt-share', + url: '/alerting/notifications', + }, + { + id: 'am-routes', + text: 'Notification policies', + icon: 'sitemap', + url: '/alerting/routes', + }, + { + id: 'silences', + text: 'Silences', + icon: 'bell-slash', + url: '/alerting/silences', + }, + { + id: 'groups', + text: 'Alert groups', + icon: 'layer-group', + url: '/alerting/groups', + }, + { + id: 'alerting-admin', + text: 'Admin', + icon: 'cog', + url: '/alerting/admin', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'alert', + text: 'New alert rule', + subTitle: 'Create an alert rule', + icon: 'plus', + url: '/alerting/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + 'new-dashboard': { + id: 'new-dashboard', + text: 'New dashboard', + icon: 'plus', + url: '/dashboard/new', + hideFromTabs: true, + showIconInNavbar: true, + parentItem: { + id: 'dashboards', + text: 'Dashboards', + section: 'core', + subTitle: 'Manage dashboards and folders', + icon: 'apps', + url: '/dashboards', + sortWeight: -1800, + children: [ + { + id: 'manage-dashboards', + text: 'Browse', + icon: 'sitemap', + url: '/dashboards', + }, + { + id: 'playlists', + text: 'Playlists', + icon: 'presentation-play', + url: '/playlists', + }, + { + id: 'snapshots', + text: 'Snapshots', + icon: 'camera', + url: '/dashboard/snapshots', + }, + { + id: 'library-panels', + text: 'Library panels', + icon: 'library-panel', + url: '/library-panels', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'new-dashboard', + text: 'New dashboard', + icon: 'plus', + url: '/dashboard/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'new-folder', + text: 'New folder', + subTitle: 'Create a new folder to organize your dashboards', + icon: 'plus', + url: '/dashboards/folder/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'import', + text: 'Import', + subTitle: 'Import dashboard from file or Grafana.com', + icon: 'plus', + url: '/dashboard/import', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + 'new-folder': { + id: 'new-folder', + text: 'New folder', + subTitle: 'Create a new folder to organize your dashboards', + icon: 'plus', + url: '/dashboards/folder/new', + hideFromTabs: true, + showIconInNavbar: true, + parentItem: { + id: 'dashboards', + text: 'Dashboards', + section: 'core', + subTitle: 'Manage dashboards and folders', + icon: 'apps', + url: '/dashboards', + sortWeight: -1800, + children: [ + { + id: 'manage-dashboards', + text: 'Browse', + icon: 'sitemap', + url: '/dashboards', + }, + { + id: 'playlists', + text: 'Playlists', + icon: 'presentation-play', + url: '/playlists', + }, + { + id: 'snapshots', + text: 'Snapshots', + icon: 'camera', + url: '/dashboard/snapshots', + }, + { + id: 'library-panels', + text: 'Library panels', + icon: 'library-panel', + url: '/library-panels', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'new-dashboard', + text: 'New dashboard', + icon: 'plus', + url: '/dashboard/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'new-folder', + text: 'New folder', + subTitle: 'Create a new folder to organize your dashboards', + icon: 'plus', + url: '/dashboards/folder/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'import', + text: 'Import', + subTitle: 'Import dashboard from file or Grafana.com', + icon: 'plus', + url: '/dashboard/import', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + import: { + id: 'import', + text: 'Import', + subTitle: 'Import dashboard from file or Grafana.com', + icon: 'plus', + url: '/dashboard/import', + hideFromTabs: true, + showIconInNavbar: true, + parentItem: { + id: 'dashboards', + text: 'Dashboards', + section: 'core', + subTitle: 'Manage dashboards and folders', + icon: 'apps', + url: '/dashboards', + sortWeight: -1800, + children: [ + { + id: 'manage-dashboards', + text: 'Browse', + icon: 'sitemap', + url: '/dashboards', + }, + { + id: 'playlists', + text: 'Playlists', + icon: 'presentation-play', + url: '/playlists', + }, + { + id: 'snapshots', + text: 'Snapshots', + icon: 'camera', + url: '/dashboard/snapshots', + }, + { + id: 'library-panels', + text: 'Library panels', + icon: 'library-panel', + url: '/library-panels', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'new-dashboard', + text: 'New dashboard', + icon: 'plus', + url: '/dashboard/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'new-folder', + text: 'New folder', + subTitle: 'Create a new folder to organize your dashboards', + icon: 'plus', + url: '/dashboards/folder/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + { + id: 'import', + text: 'Import', + subTitle: 'Import dashboard from file or Grafana.com', + icon: 'plus', + url: '/dashboard/import', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + 'not-found': { + text: 'Page not found', + subTitle: '404 Error', + icon: 'exclamation-triangle', + }, + explore: { + id: 'explore', + text: 'Explore', + section: 'core', + subTitle: 'Explore your data', + icon: 'compass', + url: '/explore', + sortWeight: -1700, + }, + alerting: { + id: 'alerting', + text: 'Alerting', + section: 'core', + subTitle: 'Alert rules and notifications', + icon: 'bell', + url: '/alerting/list', + sortWeight: -1600, + children: [ + { + id: 'alert-list', + text: 'Alert rules', + icon: 'list-ul', + url: '/alerting/list', + }, + { + id: 'receivers', + text: 'Contact points', + icon: 'comment-alt-share', + url: '/alerting/notifications', + }, + { + id: 'am-routes', + text: 'Notification policies', + icon: 'sitemap', + url: '/alerting/routes', + }, + { + id: 'silences', + text: 'Silences', + icon: 'bell-slash', + url: '/alerting/silences', + }, + { + id: 'groups', + text: 'Alert groups', + icon: 'layer-group', + url: '/alerting/groups', + }, + { + id: 'alerting-admin', + text: 'Admin', + icon: 'cog', + url: '/alerting/admin', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'alert', + text: 'New alert rule', + subTitle: 'Create an alert rule', + icon: 'plus', + url: '/alerting/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + 'alert-list': { + id: 'alert-list', + text: 'Alert rules', + icon: 'list-ul', + url: '/alerting/list', + parentItem: { + id: 'alerting', + text: 'Alerting', + section: 'core', + subTitle: 'Alert rules and notifications', + icon: 'bell', + url: '/alerting/list', + sortWeight: -1600, + children: [ + { + id: 'alert-list', + text: 'Alert rules', + icon: 'list-ul', + url: '/alerting/list', + }, + { + id: 'receivers', + text: 'Contact points', + icon: 'comment-alt-share', + url: '/alerting/notifications', + }, + { + id: 'am-routes', + text: 'Notification policies', + icon: 'sitemap', + url: '/alerting/routes', + }, + { + id: 'silences', + text: 'Silences', + icon: 'bell-slash', + url: '/alerting/silences', + }, + { + id: 'groups', + text: 'Alert groups', + icon: 'layer-group', + url: '/alerting/groups', + }, + { + id: 'alerting-admin', + text: 'Admin', + icon: 'cog', + url: '/alerting/admin', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'alert', + text: 'New alert rule', + subTitle: 'Create an alert rule', + icon: 'plus', + url: '/alerting/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + receivers: { + id: 'receivers', + text: 'Contact points', + icon: 'comment-alt-share', + url: '/alerting/notifications', + parentItem: { + id: 'alerting', + text: 'Alerting', + section: 'core', + subTitle: 'Alert rules and notifications', + icon: 'bell', + url: '/alerting/list', + sortWeight: -1600, + children: [ + { + id: 'alert-list', + text: 'Alert rules', + icon: 'list-ul', + url: '/alerting/list', + }, + { + id: 'receivers', + text: 'Contact points', + icon: 'comment-alt-share', + url: '/alerting/notifications', + }, + { + id: 'am-routes', + text: 'Notification policies', + icon: 'sitemap', + url: '/alerting/routes', + }, + { + id: 'silences', + text: 'Silences', + icon: 'bell-slash', + url: '/alerting/silences', + }, + { + id: 'groups', + text: 'Alert groups', + icon: 'layer-group', + url: '/alerting/groups', + }, + { + id: 'alerting-admin', + text: 'Admin', + icon: 'cog', + url: '/alerting/admin', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'alert', + text: 'New alert rule', + subTitle: 'Create an alert rule', + icon: 'plus', + url: '/alerting/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + 'am-routes': { + id: 'am-routes', + text: 'Notification policies', + icon: 'sitemap', + url: '/alerting/routes', + parentItem: { + id: 'alerting', + text: 'Alerting', + section: 'core', + subTitle: 'Alert rules and notifications', + icon: 'bell', + url: '/alerting/list', + sortWeight: -1600, + children: [ + { + id: 'alert-list', + text: 'Alert rules', + icon: 'list-ul', + url: '/alerting/list', + }, + { + id: 'receivers', + text: 'Contact points', + icon: 'comment-alt-share', + url: '/alerting/notifications', + }, + { + id: 'am-routes', + text: 'Notification policies', + icon: 'sitemap', + url: '/alerting/routes', + }, + { + id: 'silences', + text: 'Silences', + icon: 'bell-slash', + url: '/alerting/silences', + }, + { + id: 'groups', + text: 'Alert groups', + icon: 'layer-group', + url: '/alerting/groups', + }, + { + id: 'alerting-admin', + text: 'Admin', + icon: 'cog', + url: '/alerting/admin', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'alert', + text: 'New alert rule', + subTitle: 'Create an alert rule', + icon: 'plus', + url: '/alerting/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + silences: { + id: 'silences', + text: 'Silences', + icon: 'bell-slash', + url: '/alerting/silences', + parentItem: { + id: 'alerting', + text: 'Alerting', + section: 'core', + subTitle: 'Alert rules and notifications', + icon: 'bell', + url: '/alerting/list', + sortWeight: -1600, + children: [ + { + id: 'alert-list', + text: 'Alert rules', + icon: 'list-ul', + url: '/alerting/list', + }, + { + id: 'receivers', + text: 'Contact points', + icon: 'comment-alt-share', + url: '/alerting/notifications', + }, + { + id: 'am-routes', + text: 'Notification policies', + icon: 'sitemap', + url: '/alerting/routes', + }, + { + id: 'silences', + text: 'Silences', + icon: 'bell-slash', + url: '/alerting/silences', + }, + { + id: 'groups', + text: 'Alert groups', + icon: 'layer-group', + url: '/alerting/groups', + }, + { + id: 'alerting-admin', + text: 'Admin', + icon: 'cog', + url: '/alerting/admin', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'alert', + text: 'New alert rule', + subTitle: 'Create an alert rule', + icon: 'plus', + url: '/alerting/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + groups: { + id: 'groups', + text: 'Alert groups', + icon: 'layer-group', + url: '/alerting/groups', + parentItem: { + id: 'alerting', + text: 'Alerting', + section: 'core', + subTitle: 'Alert rules and notifications', + icon: 'bell', + url: '/alerting/list', + sortWeight: -1600, + children: [ + { + id: 'alert-list', + text: 'Alert rules', + icon: 'list-ul', + url: '/alerting/list', + }, + { + id: 'receivers', + text: 'Contact points', + icon: 'comment-alt-share', + url: '/alerting/notifications', + }, + { + id: 'am-routes', + text: 'Notification policies', + icon: 'sitemap', + url: '/alerting/routes', + }, + { + id: 'silences', + text: 'Silences', + icon: 'bell-slash', + url: '/alerting/silences', + }, + { + id: 'groups', + text: 'Alert groups', + icon: 'layer-group', + url: '/alerting/groups', + }, + { + id: 'alerting-admin', + text: 'Admin', + icon: 'cog', + url: '/alerting/admin', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'alert', + text: 'New alert rule', + subTitle: 'Create an alert rule', + icon: 'plus', + url: '/alerting/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + 'alerting-admin': { + id: 'alerting-admin', + text: 'Admin', + icon: 'cog', + url: '/alerting/admin', + parentItem: { + id: 'alerting', + text: 'Alerting', + section: 'core', + subTitle: 'Alert rules and notifications', + icon: 'bell', + url: '/alerting/list', + sortWeight: -1600, + children: [ + { + id: 'alert-list', + text: 'Alert rules', + icon: 'list-ul', + url: '/alerting/list', + }, + { + id: 'receivers', + text: 'Contact points', + icon: 'comment-alt-share', + url: '/alerting/notifications', + }, + { + id: 'am-routes', + text: 'Notification policies', + icon: 'sitemap', + url: '/alerting/routes', + }, + { + id: 'silences', + text: 'Silences', + icon: 'bell-slash', + url: '/alerting/silences', + }, + { + id: 'groups', + text: 'Alert groups', + icon: 'layer-group', + url: '/alerting/groups', + }, + { + id: 'alerting-admin', + text: 'Admin', + icon: 'cog', + url: '/alerting/admin', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'alert', + text: 'New alert rule', + subTitle: 'Create an alert rule', + icon: 'plus', + url: '/alerting/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + alert: { + id: 'alert', + text: 'New alert rule', + subTitle: 'Create an alert rule', + icon: 'plus', + url: '/alerting/new', + hideFromTabs: true, + showIconInNavbar: true, + parentItem: { + id: 'alerting', + text: 'Alerting', + section: 'core', + subTitle: 'Alert rules and notifications', + icon: 'bell', + url: '/alerting/list', + sortWeight: -1600, + children: [ + { + id: 'alert-list', + text: 'Alert rules', + icon: 'list-ul', + url: '/alerting/list', + }, + { + id: 'receivers', + text: 'Contact points', + icon: 'comment-alt-share', + url: '/alerting/notifications', + }, + { + id: 'am-routes', + text: 'Notification policies', + icon: 'sitemap', + url: '/alerting/routes', + }, + { + id: 'silences', + text: 'Silences', + icon: 'bell-slash', + url: '/alerting/silences', + }, + { + id: 'groups', + text: 'Alert groups', + icon: 'layer-group', + url: '/alerting/groups', + }, + { + id: 'alerting-admin', + text: 'Admin', + icon: 'cog', + url: '/alerting/admin', + }, + { + id: 'divider', + text: 'Divider', + divider: true, + hideFromTabs: true, + }, + { + id: 'alert', + text: 'New alert rule', + subTitle: 'Create an alert rule', + icon: 'plus', + url: '/alerting/new', + hideFromTabs: true, + showIconInNavbar: true, + }, + ], + }, + }, + 'data-connections': { + id: 'data-connections', + text: 'Data Connections', + section: 'core', + icon: 'link', + url: '/data-connections', + sortWeight: -1500, + children: [ + { + id: 'data-connections-datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/data-connections/datasources', + active: false, + }, + { + id: 'data-connections-plugins', + text: 'Plugins', + description: 'Manage plugins', + icon: 'plug', + url: '/data-connections/plugins', + active: false, + }, + { + id: 'data-connections-cloud-integrations', + text: 'Cloud integrations', + description: 'Manage your cloud integrations', + icon: 'bolt', + url: '/data-connections/cloud-integrations', + active: true, + }, + { + id: 'data-connections-recorded-queries', + text: 'Recorded queries', + description: 'Manage your recorded queries', + icon: 'record-audio', + url: '/data-connections/recorded-queries', + active: false, + }, + ], + }, + 'data-connections-datasources': { + id: 'data-connections-datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/data-connections/datasources', + parentItem: { + id: 'data-connections', + text: 'Data Connections', + section: 'core', + icon: 'link', + url: '/data-connections', + sortWeight: -1500, + children: [ + { + id: 'data-connections-datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/data-connections/datasources', + }, + { + id: 'data-connections-plugins', + text: 'Plugins', + description: 'Manage plugins', + icon: 'plug', + url: '/data-connections/plugins', + }, + { + id: 'data-connections-cloud-integrations', + text: 'Cloud integrations', + description: 'Manage your cloud integrations', + icon: 'bolt', + url: '/data-connections/cloud-integrations', + }, + { + id: 'data-connections-recorded-queries', + text: 'Recorded queries', + description: 'Manage your recorded queries', + icon: 'record-audio', + url: '/data-connections/recorded-queries', + }, + ], + }, + }, + 'data-connections-plugins': { + id: 'data-connections-plugins', + text: 'Plugins', + description: 'Manage plugins', + icon: 'plug', + url: '/data-connections/plugins', + parentItem: { + id: 'data-connections', + text: 'Data Connections', + section: 'core', + icon: 'link', + url: '/data-connections', + sortWeight: -1500, + children: [ + { + id: 'data-connections-datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/data-connections/datasources', + }, + { + id: 'data-connections-plugins', + text: 'Plugins', + description: 'Manage plugins', + icon: 'plug', + url: '/data-connections/plugins', + }, + { + id: 'data-connections-cloud-integrations', + text: 'Cloud integrations', + description: 'Manage your cloud integrations', + icon: 'bolt', + url: '/data-connections/cloud-integrations', + }, + { + id: 'data-connections-recorded-queries', + text: 'Recorded queries', + description: 'Manage your recorded queries', + icon: 'record-audio', + url: '/data-connections/recorded-queries', + }, + ], + }, + }, + 'data-connections-cloud-integrations': { + id: 'data-connections-cloud-integrations', + text: 'Cloud integrations', + description: 'Manage your cloud integrations', + icon: 'bolt', + url: '/data-connections/cloud-integrations', + parentItem: { + id: 'data-connections', + text: 'Data Connections', + section: 'core', + icon: 'link', + url: '/data-connections', + sortWeight: -1500, + children: [ + { + id: 'data-connections-datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/data-connections/datasources', + }, + { + id: 'data-connections-plugins', + text: 'Plugins', + description: 'Manage plugins', + icon: 'plug', + url: '/data-connections/plugins', + }, + { + id: 'data-connections-cloud-integrations', + text: 'Cloud integrations', + description: 'Manage your cloud integrations', + icon: 'bolt', + url: '/data-connections/cloud-integrations', + }, + { + id: 'data-connections-recorded-queries', + text: 'Recorded queries', + description: 'Manage your recorded queries', + icon: 'record-audio', + url: '/data-connections/recorded-queries', + }, + ], + }, + }, + 'data-connections-recorded-queries': { + id: 'data-connections-recorded-queries', + text: 'Recorded queries', + description: 'Manage your recorded queries', + icon: 'record-audio', + url: '/data-connections/recorded-queries', + parentItem: { + id: 'data-connections', + text: 'Data Connections', + section: 'core', + icon: 'link', + url: '/data-connections', + sortWeight: -1500, + children: [ + { + id: 'data-connections-datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/data-connections/datasources', + }, + { + id: 'data-connections-plugins', + text: 'Plugins', + description: 'Manage plugins', + icon: 'plug', + url: '/data-connections/plugins', + }, + { + id: 'data-connections-cloud-integrations', + text: 'Cloud integrations', + description: 'Manage your cloud integrations', + icon: 'bolt', + url: '/data-connections/cloud-integrations', + }, + { + id: 'data-connections-recorded-queries', + text: 'Recorded queries', + description: 'Manage your recorded queries', + icon: 'record-audio', + url: '/data-connections/recorded-queries', + }, + ], + }, + }, + 'plugin-page-basic-app': { + id: 'plugin-page-basic-app', + text: 'Basic App', + section: 'plugin', + img: 'public/plugins/basic-app/img/logo.svg', + url: '/a/basic-app/one', + sortWeight: -1400, + children: [ + { + text: 'Page One', + url: '/a/basic-app/one', + }, + { + text: 'Page Two', + url: '/a/basic-app/two', + }, + { + text: 'Page Three', + url: '/a/basic-app/three', + }, + { + text: 'Page Four', + url: '/a/basic-app/four', + }, + { + text: 'Configuration', + icon: 'fa fa-cog', + url: '/plugins/basic-app', + }, + ], + }, + undefined: { + text: 'Config', + url: '/plugins/grafana-synthetic-monitoring-app', + parentItem: { + id: 'plugin-page-grafana-synthetic-monitoring-app', + text: 'Synthetic Monitoring', + section: 'plugin', + img: 'public/plugins/grafana-synthetic-monitoring-app/img/logo.svg', + url: '/a/grafana-synthetic-monitoring-app/home', + sortWeight: -1400, + children: [ + { + text: 'Home', + url: '/a/grafana-synthetic-monitoring-app/home', + }, + { + text: 'Summary', + url: '/a/grafana-synthetic-monitoring-app/redirect?dashboard=summary', + }, + { + text: 'Checks', + url: '/a/grafana-synthetic-monitoring-app/checks', + }, + { + text: 'Probes', + url: '/a/grafana-synthetic-monitoring-app/probes', + }, + { + text: 'Alerts', + url: '/a/grafana-synthetic-monitoring-app/alerts', + }, + { + text: 'Config', + url: '/plugins/grafana-synthetic-monitoring-app', + }, + ], + }, + }, + 'plugin-page-cloudflare-app': { + id: 'plugin-page-cloudflare-app', + text: 'Cloudflare Grafana App', + section: 'plugin', + img: 'public/plugins/cloudflare-app/img/cf_icon.png', + sortWeight: -1400, + children: [ + { + text: 'Zones', + url: '/d/KAVdMAw9k', + }, + { + text: 'DNS Firewall', + url: '/d/QrKttDVqu', + }, + ], + }, + 'plugin-page-grafana-easystart-app': { + id: 'plugin-page-grafana-easystart-app', + text: 'Integrations and Connections', + section: 'plugin', + img: 'public/plugins/grafana-easystart-app/img/logo.svg', + url: '/a/grafana-easystart-app', + sortWeight: -1400, + }, + 'plugin-page-redis-explorer-app': { + id: 'plugin-page-redis-explorer-app', + text: 'Redis Explorer', + section: 'plugin', + img: 'public/plugins/redis-explorer-app/img/logo.svg', + url: '/a/redis-explorer-app/', + sortWeight: -1400, + children: [ + { + text: 'Home', + icon: 'home-alt', + url: '/a/redis-explorer-app/', + }, + { + text: 'Enterprise Clusters', + icon: 'apps', + url: '/d/1dKhTjtGk', + }, + { + text: 'Cluster Overview', + icon: 'monitor', + url: '/d/viroIzSGz', + }, + { + text: 'Cluster Nodes', + icon: 'sitemap', + url: '/d/hqze6rtGz', + }, + { + text: 'Cluster Databases', + icon: 'database', + url: '/d/k_A8MjtMk', + }, + { + text: 'Cluster Alerts', + icon: 'info-circle', + url: '/d/xESAiFcnk', + }, + ], + }, + 'plugin-page-grafana-synthetic-monitoring-app': { + id: 'plugin-page-grafana-synthetic-monitoring-app', + text: 'Synthetic Monitoring', + section: 'plugin', + img: 'public/plugins/grafana-synthetic-monitoring-app/img/logo.svg', + url: '/a/grafana-synthetic-monitoring-app/home', + sortWeight: -1400, + children: [ + { + text: 'Home', + url: '/a/grafana-synthetic-monitoring-app/home', + }, + { + text: 'Summary', + url: '/a/grafana-synthetic-monitoring-app/redirect?dashboard=summary', + }, + { + text: 'Checks', + url: '/a/grafana-synthetic-monitoring-app/checks', + }, + { + text: 'Probes', + url: '/a/grafana-synthetic-monitoring-app/probes', + }, + { + text: 'Alerts', + url: '/a/grafana-synthetic-monitoring-app/alerts', + }, + { + text: 'Config', + url: '/plugins/grafana-synthetic-monitoring-app', + }, + ], + }, + 'plugin-page-grafana-k6-app': { + id: 'plugin-page-grafana-k6-app', + text: 'k6 Cloud App', + section: 'plugin', + img: 'public/plugins/grafana-k6-app/img/logo.svg', + url: '/a/grafana-k6-app', + sortWeight: -1400, + }, + cfg: { + id: 'cfg', + text: 'Configuration', + section: 'config', + subTitle: 'Organization: Main Org.', + icon: 'cog', + url: '/datasources', + sortWeight: -1300, + children: [ + { + id: 'datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/datasources', + }, + { + id: 'users', + text: 'Users', + description: 'Manage org members', + icon: 'user', + url: '/org/users', + }, + { + id: 'teams', + text: 'Teams', + description: 'Manage org groups', + icon: 'users-alt', + url: '/org/teams', + }, + { + id: 'plugins', + text: 'Plugins', + description: 'View and configure plugins', + icon: 'plug', + url: '/plugins', + }, + { + id: 'org-settings', + text: 'Preferences', + description: 'Organization preferences', + icon: 'sliders-v-alt', + url: '/org', + }, + { + id: 'apikeys', + text: 'API keys', + description: 'Create & manage API keys', + icon: 'key-skeleton-alt', + url: '/org/apikeys', + }, + ], + }, + datasources: { + id: 'datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/datasources', + parentItem: { + id: 'cfg', + text: 'Configuration', + section: 'config', + subTitle: 'Organization: Main Org.', + icon: 'cog', + url: '/datasources', + sortWeight: -1300, + children: [ + { + id: 'datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/datasources', + }, + { + id: 'users', + text: 'Users', + description: 'Manage org members', + icon: 'user', + url: '/org/users', + }, + { + id: 'teams', + text: 'Teams', + description: 'Manage org groups', + icon: 'users-alt', + url: '/org/teams', + }, + { + id: 'plugins', + text: 'Plugins', + description: 'View and configure plugins', + icon: 'plug', + url: '/plugins', + }, + { + id: 'org-settings', + text: 'Preferences', + description: 'Organization preferences', + icon: 'sliders-v-alt', + url: '/org', + }, + { + id: 'apikeys', + text: 'API keys', + description: 'Create & manage API keys', + icon: 'key-skeleton-alt', + url: '/org/apikeys', + }, + ], + }, + }, + users: { + id: 'users', + text: 'Users', + description: 'Manage org members', + icon: 'user', + url: '/org/users', + parentItem: { + id: 'cfg', + text: 'Configuration', + section: 'config', + subTitle: 'Organization: Main Org.', + icon: 'cog', + url: '/datasources', + sortWeight: -1300, + children: [ + { + id: 'datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/datasources', + }, + { + id: 'users', + text: 'Users', + description: 'Manage org members', + icon: 'user', + url: '/org/users', + }, + { + id: 'teams', + text: 'Teams', + description: 'Manage org groups', + icon: 'users-alt', + url: '/org/teams', + }, + { + id: 'plugins', + text: 'Plugins', + description: 'View and configure plugins', + icon: 'plug', + url: '/plugins', + }, + { + id: 'org-settings', + text: 'Preferences', + description: 'Organization preferences', + icon: 'sliders-v-alt', + url: '/org', + }, + { + id: 'apikeys', + text: 'API keys', + description: 'Create & manage API keys', + icon: 'key-skeleton-alt', + url: '/org/apikeys', + }, + ], + }, + }, + teams: { + id: 'teams', + text: 'Teams', + description: 'Manage org groups', + icon: 'users-alt', + url: '/org/teams', + parentItem: { + id: 'cfg', + text: 'Configuration', + section: 'config', + subTitle: 'Organization: Main Org.', + icon: 'cog', + url: '/datasources', + sortWeight: -1300, + children: [ + { + id: 'datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/datasources', + }, + { + id: 'users', + text: 'Users', + description: 'Manage org members', + icon: 'user', + url: '/org/users', + }, + { + id: 'teams', + text: 'Teams', + description: 'Manage org groups', + icon: 'users-alt', + url: '/org/teams', + }, + { + id: 'plugins', + text: 'Plugins', + description: 'View and configure plugins', + icon: 'plug', + url: '/plugins', + }, + { + id: 'org-settings', + text: 'Preferences', + description: 'Organization preferences', + icon: 'sliders-v-alt', + url: '/org', + }, + { + id: 'apikeys', + text: 'API keys', + description: 'Create & manage API keys', + icon: 'key-skeleton-alt', + url: '/org/apikeys', + }, + ], + }, + }, + plugins: { + id: 'plugins', + text: 'Plugins', + description: 'View and configure plugins', + icon: 'plug', + url: '/plugins', + parentItem: { + id: 'cfg', + text: 'Configuration', + section: 'config', + subTitle: 'Organization: Main Org.', + icon: 'cog', + url: '/datasources', + sortWeight: -1300, + children: [ + { + id: 'datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/datasources', + }, + { + id: 'users', + text: 'Users', + description: 'Manage org members', + icon: 'user', + url: '/org/users', + }, + { + id: 'teams', + text: 'Teams', + description: 'Manage org groups', + icon: 'users-alt', + url: '/org/teams', + }, + { + id: 'plugins', + text: 'Plugins', + description: 'View and configure plugins', + icon: 'plug', + url: '/plugins', + }, + { + id: 'org-settings', + text: 'Preferences', + description: 'Organization preferences', + icon: 'sliders-v-alt', + url: '/org', + }, + { + id: 'apikeys', + text: 'API keys', + description: 'Create & manage API keys', + icon: 'key-skeleton-alt', + url: '/org/apikeys', + }, + ], + }, + }, + 'org-settings': { + id: 'org-settings', + text: 'Preferences', + description: 'Organization preferences', + icon: 'sliders-v-alt', + url: '/org', + parentItem: { + id: 'cfg', + text: 'Configuration', + section: 'config', + subTitle: 'Organization: Main Org.', + icon: 'cog', + url: '/datasources', + sortWeight: -1300, + children: [ + { + id: 'datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/datasources', + }, + { + id: 'users', + text: 'Users', + description: 'Manage org members', + icon: 'user', + url: '/org/users', + }, + { + id: 'teams', + text: 'Teams', + description: 'Manage org groups', + icon: 'users-alt', + url: '/org/teams', + }, + { + id: 'plugins', + text: 'Plugins', + description: 'View and configure plugins', + icon: 'plug', + url: '/plugins', + }, + { + id: 'org-settings', + text: 'Preferences', + description: 'Organization preferences', + icon: 'sliders-v-alt', + url: '/org', + }, + { + id: 'apikeys', + text: 'API keys', + description: 'Create & manage API keys', + icon: 'key-skeleton-alt', + url: '/org/apikeys', + }, + ], + }, + }, + apikeys: { + id: 'apikeys', + text: 'API keys', + description: 'Create & manage API keys', + icon: 'key-skeleton-alt', + url: '/org/apikeys', + parentItem: { + id: 'cfg', + text: 'Configuration', + section: 'config', + subTitle: 'Organization: Main Org.', + icon: 'cog', + url: '/datasources', + sortWeight: -1300, + children: [ + { + id: 'datasources', + text: 'Data sources', + description: 'Add and configure data sources', + icon: 'database', + url: '/datasources', + }, + { + id: 'users', + text: 'Users', + description: 'Manage org members', + icon: 'user', + url: '/org/users', + }, + { + id: 'teams', + text: 'Teams', + description: 'Manage org groups', + icon: 'users-alt', + url: '/org/teams', + }, + { + id: 'plugins', + text: 'Plugins', + description: 'View and configure plugins', + icon: 'plug', + url: '/plugins', + }, + { + id: 'org-settings', + text: 'Preferences', + description: 'Organization preferences', + icon: 'sliders-v-alt', + url: '/org', + }, + { + id: 'apikeys', + text: 'API keys', + description: 'Create & manage API keys', + icon: 'key-skeleton-alt', + url: '/org/apikeys', + }, + ], + }, + }, + admin: { + id: 'admin', + text: 'Server Admin', + section: 'config', + subTitle: 'Manage all users and orgs', + icon: 'shield', + url: '/admin/users', + sortWeight: -1200, + hideFromTabs: true, + children: [ + { + id: 'global-users', + text: 'Users', + icon: 'user', + url: '/admin/users', + }, + { + id: 'global-orgs', + text: 'Orgs', + icon: 'building', + url: '/admin/orgs', + }, + { + id: 'server-settings', + text: 'Settings', + icon: 'sliders-v-alt', + url: '/admin/settings', + }, + { + id: 'admin-plugins', + text: 'Plugins', + icon: 'plug', + url: '/admin/plugins', + }, + { + id: 'upgrading', + text: 'Stats and license', + icon: 'unlock', + url: '/admin/upgrading', + }, + ], + }, + 'global-users': { + id: 'global-users', + text: 'Users', + icon: 'user', + url: '/admin/users', + parentItem: { + id: 'admin', + text: 'Server Admin', + section: 'config', + subTitle: 'Manage all users and orgs', + icon: 'shield', + url: '/admin/users', + sortWeight: -1200, + hideFromTabs: true, + children: [ + { + id: 'global-users', + text: 'Users', + icon: 'user', + url: '/admin/users', + }, + { + id: 'global-orgs', + text: 'Orgs', + icon: 'building', + url: '/admin/orgs', + }, + { + id: 'server-settings', + text: 'Settings', + icon: 'sliders-v-alt', + url: '/admin/settings', + }, + { + id: 'admin-plugins', + text: 'Plugins', + icon: 'plug', + url: '/admin/plugins', + }, + { + id: 'upgrading', + text: 'Stats and license', + icon: 'unlock', + url: '/admin/upgrading', + }, + ], + }, + }, + 'global-orgs': { + id: 'global-orgs', + text: 'Orgs', + icon: 'building', + url: '/admin/orgs', + parentItem: { + id: 'admin', + text: 'Server Admin', + section: 'config', + subTitle: 'Manage all users and orgs', + icon: 'shield', + url: '/admin/users', + sortWeight: -1200, + hideFromTabs: true, + children: [ + { + id: 'global-users', + text: 'Users', + icon: 'user', + url: '/admin/users', + }, + { + id: 'global-orgs', + text: 'Orgs', + icon: 'building', + url: '/admin/orgs', + }, + { + id: 'server-settings', + text: 'Settings', + icon: 'sliders-v-alt', + url: '/admin/settings', + }, + { + id: 'admin-plugins', + text: 'Plugins', + icon: 'plug', + url: '/admin/plugins', + }, + { + id: 'upgrading', + text: 'Stats and license', + icon: 'unlock', + url: '/admin/upgrading', + }, + ], + }, + }, + 'server-settings': { + id: 'server-settings', + text: 'Settings', + icon: 'sliders-v-alt', + url: '/admin/settings', + parentItem: { + id: 'admin', + text: 'Server Admin', + section: 'config', + subTitle: 'Manage all users and orgs', + icon: 'shield', + url: '/admin/users', + sortWeight: -1200, + hideFromTabs: true, + children: [ + { + id: 'global-users', + text: 'Users', + icon: 'user', + url: '/admin/users', + }, + { + id: 'global-orgs', + text: 'Orgs', + icon: 'building', + url: '/admin/orgs', + }, + { + id: 'server-settings', + text: 'Settings', + icon: 'sliders-v-alt', + url: '/admin/settings', + }, + { + id: 'admin-plugins', + text: 'Plugins', + icon: 'plug', + url: '/admin/plugins', + }, + { + id: 'upgrading', + text: 'Stats and license', + icon: 'unlock', + url: '/admin/upgrading', + }, + ], + }, + }, + 'admin-plugins': { + id: 'admin-plugins', + text: 'Plugins', + icon: 'plug', + url: '/admin/plugins', + parentItem: { + id: 'admin', + text: 'Server Admin', + section: 'config', + subTitle: 'Manage all users and orgs', + icon: 'shield', + url: '/admin/users', + sortWeight: -1200, + hideFromTabs: true, + children: [ + { + id: 'global-users', + text: 'Users', + icon: 'user', + url: '/admin/users', + }, + { + id: 'global-orgs', + text: 'Orgs', + icon: 'building', + url: '/admin/orgs', + }, + { + id: 'server-settings', + text: 'Settings', + icon: 'sliders-v-alt', + url: '/admin/settings', + }, + { + id: 'admin-plugins', + text: 'Plugins', + icon: 'plug', + url: '/admin/plugins', + }, + { + id: 'upgrading', + text: 'Stats and license', + icon: 'unlock', + url: '/admin/upgrading', + }, + ], + }, + }, + upgrading: { + id: 'upgrading', + text: 'Stats and license', + icon: 'unlock', + url: '/admin/upgrading', + parentItem: { + id: 'admin', + text: 'Server Admin', + section: 'config', + subTitle: 'Manage all users and orgs', + icon: 'shield', + url: '/admin/users', + sortWeight: -1200, + hideFromTabs: true, + children: [ + { + id: 'global-users', + text: 'Users', + icon: 'user', + url: '/admin/users', + }, + { + id: 'global-orgs', + text: 'Orgs', + icon: 'building', + url: '/admin/orgs', + }, + { + id: 'server-settings', + text: 'Settings', + icon: 'sliders-v-alt', + url: '/admin/settings', + }, + { + id: 'admin-plugins', + text: 'Plugins', + icon: 'plug', + url: '/admin/plugins', + }, + { + id: 'upgrading', + text: 'Stats and license', + icon: 'unlock', + url: '/admin/upgrading', + }, + ], + }, + }, + profile: { + id: 'profile', + text: 'admin', + section: 'config', + img: '/avatar/46d229b033af06a191ff2267bca9ae56', + url: '/profile', + sortWeight: -1100, + children: [ + { + id: 'profile-settings', + text: 'Preferences', + icon: 'sliders-v-alt', + url: '/profile', + }, + { + id: 'notifications', + text: 'Notification history', + icon: 'bell', + url: '/notifications', + }, + { + id: 'change-password', + text: 'Change password', + icon: 'lock', + url: '/profile/password', + }, + { + id: 'sign-out', + text: 'Sign out', + icon: 'arrow-from-right', + url: '/logout', + target: '_self', + hideFromTabs: true, + }, + ], + }, + 'profile-settings': { + id: 'profile-settings', + text: 'Preferences', + icon: 'sliders-v-alt', + url: '/profile', + parentItem: { + id: 'profile', + text: 'admin', + section: 'config', + img: '/avatar/46d229b033af06a191ff2267bca9ae56', + url: '/profile', + sortWeight: -1100, + children: [ + { + id: 'profile-settings', + text: 'Preferences', + icon: 'sliders-v-alt', + url: '/profile', + }, + { + id: 'notifications', + text: 'Notification history', + icon: 'bell', + url: '/notifications', + }, + { + id: 'change-password', + text: 'Change password', + icon: 'lock', + url: '/profile/password', + }, + { + id: 'sign-out', + text: 'Sign out', + icon: 'arrow-from-right', + url: '/logout', + target: '_self', + hideFromTabs: true, + }, + ], + }, + }, + notifications: { + id: 'notifications', + text: 'Notification history', + icon: 'bell', + url: '/notifications', + parentItem: { + id: 'profile', + text: 'admin', + section: 'config', + img: '/avatar/46d229b033af06a191ff2267bca9ae56', + url: '/profile', + sortWeight: -1100, + children: [ + { + id: 'profile-settings', + text: 'Preferences', + icon: 'sliders-v-alt', + url: '/profile', + }, + { + id: 'notifications', + text: 'Notification history', + icon: 'bell', + url: '/notifications', + }, + { + id: 'change-password', + text: 'Change password', + icon: 'lock', + url: '/profile/password', + }, + { + id: 'sign-out', + text: 'Sign out', + icon: 'arrow-from-right', + url: '/logout', + target: '_self', + hideFromTabs: true, + }, + ], + }, + }, + 'change-password': { + id: 'change-password', + text: 'Change password', + icon: 'lock', + url: '/profile/password', + parentItem: { + id: 'profile', + text: 'admin', + section: 'config', + img: '/avatar/46d229b033af06a191ff2267bca9ae56', + url: '/profile', + sortWeight: -1100, + children: [ + { + id: 'profile-settings', + text: 'Preferences', + icon: 'sliders-v-alt', + url: '/profile', + }, + { + id: 'notifications', + text: 'Notification history', + icon: 'bell', + url: '/notifications', + }, + { + id: 'change-password', + text: 'Change password', + icon: 'lock', + url: '/profile/password', + }, + { + id: 'sign-out', + text: 'Sign out', + icon: 'arrow-from-right', + url: '/logout', + target: '_self', + hideFromTabs: true, + }, + ], + }, + }, + 'sign-out': { + id: 'sign-out', + text: 'Sign out', + icon: 'arrow-from-right', + url: '/logout', + target: '_self', + hideFromTabs: true, + parentItem: { + id: 'profile', + text: 'admin', + section: 'config', + img: '/avatar/46d229b033af06a191ff2267bca9ae56', + url: '/profile', + sortWeight: -1100, + children: [ + { + id: 'profile-settings', + text: 'Preferences', + icon: 'sliders-v-alt', + url: '/profile', + }, + { + id: 'notifications', + text: 'Notification history', + icon: 'bell', + url: '/notifications', + }, + { + id: 'change-password', + text: 'Change password', + icon: 'lock', + url: '/profile/password', + }, + { + id: 'sign-out', + text: 'Sign out', + icon: 'arrow-from-right', + url: '/logout', + target: '_self', + hideFromTabs: true, + }, + ], + }, + }, + help: { + id: 'help', + text: 'Help', + section: 'config', + subTitle: 'Grafana v9.0.0-pre (abb5c6109a)', + icon: 'question-circle', + url: '#', + sortWeight: -1000, + }, +}; diff --git a/public/app/features/data-connections/constants.ts b/public/app/features/data-connections/constants.ts new file mode 100644 index 00000000000..c35a30aba1b --- /dev/null +++ b/public/app/features/data-connections/constants.ts @@ -0,0 +1,12 @@ +// The ID of the app plugin that we render under that "Cloud Integrations" tab +export const CLOUD_ONBOARDING_APP_ID = 'grafana-easystart-app'; + +// The ID of the main nav-tree item (the main item in the NavIndex) +export const ROUTE_BASE_ID = 'data-connections'; + +export enum ROUTES { + DataSources = '/data-connections/data-sources', + Plugins = '/data-connections/plugins', + CloudIntegrations = '/data-connections/cloud-integrations', + RecordedQueries = '/data-connections/recorded-queries', +} diff --git a/public/app/features/data-connections/hooks/useNavModel.ts b/public/app/features/data-connections/hooks/useNavModel.ts new file mode 100644 index 00000000000..b030c3e0b66 --- /dev/null +++ b/public/app/features/data-connections/hooks/useNavModel.ts @@ -0,0 +1,26 @@ +import { useSelector } from 'react-redux'; +import { useLocation } from 'react-router-dom'; + +import { StoreState } from 'app/types/store'; + +import { ROUTE_BASE_ID } from '../constants'; + +// We need this utility logic to make sure that the tab with the current URL is marked as active. +// (In case we were using `getNavModel()` from app/core/selectors/navModel, then we would need to set +// the child nav-model-item's ID on the call-site.) +export const useNavModel = () => { + const { pathname } = useLocation(); + const navIndex = useSelector((state: StoreState) => state.navIndex); + const node = navIndex[ROUTE_BASE_ID]; + const main = node; + + main.children = main.children?.map((item) => ({ + ...item, + active: item.url === pathname, + })); + + return { + node, + main, + }; +}; diff --git a/public/app/features/data-connections/routes.tsx b/public/app/features/data-connections/routes.tsx new file mode 100644 index 00000000000..5366ec53caa --- /dev/null +++ b/public/app/features/data-connections/routes.tsx @@ -0,0 +1,21 @@ +import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport'; +import { config } from 'app/core/config'; +import { RouteDescriptor } from 'app/core/navigation/types'; + +import { ROUTE_BASE_ID } from './constants'; + +export function getRoutes(): RouteDescriptor[] { + if (config.featureToggles.dataConnectionsConsole) { + return [ + { + path: `/${ROUTE_BASE_ID}`, + exact: false, + component: SafeDynamicImport( + () => import(/* webpackChunkName: "DataConnectionsPage"*/ 'app/features/data-connections/DataConnectionsPage') + ), + }, + ]; + } + + return []; +} diff --git a/public/app/features/data-connections/tabs/CloudIntegrations/CloudIntegrations.tsx b/public/app/features/data-connections/tabs/CloudIntegrations/CloudIntegrations.tsx new file mode 100644 index 00000000000..c912fb8d9b7 --- /dev/null +++ b/public/app/features/data-connections/tabs/CloudIntegrations/CloudIntegrations.tsx @@ -0,0 +1,25 @@ +import { css } from '@emotion/css'; +import React, { ReactElement } from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { useStyles2 } from '@grafana/ui'; +import { AppPluginLoader } from 'app/features/plugins/components/AppPluginLoader'; + +import { CLOUD_ONBOARDING_APP_ID } from '../../constants'; + +export function CloudIntegrations(): ReactElement | null { + const s = useStyles2(getStyles); + + return ( +
+ +
+ ); +} + +const getStyles = (theme: GrafanaTheme2) => ({ + // We would like to force the app to stay inside the provided tab + container: css` + position: relative; + `, +}); diff --git a/public/app/features/data-connections/tabs/CloudIntegrations/index.tsx b/public/app/features/data-connections/tabs/CloudIntegrations/index.tsx new file mode 100644 index 00000000000..41c904956aa --- /dev/null +++ b/public/app/features/data-connections/tabs/CloudIntegrations/index.tsx @@ -0,0 +1 @@ +export * from './CloudIntegrations'; diff --git a/public/app/features/data-connections/tabs/DataSources/DataSources.tsx b/public/app/features/data-connections/tabs/DataSources/DataSources.tsx new file mode 100644 index 00000000000..a809c21511e --- /dev/null +++ b/public/app/features/data-connections/tabs/DataSources/DataSources.tsx @@ -0,0 +1,5 @@ +import React, { ReactElement } from 'react'; + +export function DataSources(): ReactElement | null { + return
The list of data sources is under development.
; +} diff --git a/public/app/features/data-connections/tabs/DataSources/index.tsx b/public/app/features/data-connections/tabs/DataSources/index.tsx new file mode 100644 index 00000000000..5d681837186 --- /dev/null +++ b/public/app/features/data-connections/tabs/DataSources/index.tsx @@ -0,0 +1 @@ +export * from './DataSources'; diff --git a/public/app/features/data-connections/tabs/Plugins/Plugins.tsx b/public/app/features/data-connections/tabs/Plugins/Plugins.tsx new file mode 100644 index 00000000000..e1befaab13f --- /dev/null +++ b/public/app/features/data-connections/tabs/Plugins/Plugins.tsx @@ -0,0 +1,5 @@ +import React, { ReactElement } from 'react'; + +export function Plugins(): ReactElement | null { + return
The list of plugins is under development
; +} diff --git a/public/app/features/data-connections/tabs/Plugins/index.tsx b/public/app/features/data-connections/tabs/Plugins/index.tsx new file mode 100644 index 00000000000..20c2507f6d8 --- /dev/null +++ b/public/app/features/data-connections/tabs/Plugins/index.tsx @@ -0,0 +1 @@ +export * from './Plugins'; diff --git a/public/app/features/data-connections/tabs/RecordedQueries/RecordedQueries.tsx b/public/app/features/data-connections/tabs/RecordedQueries/RecordedQueries.tsx new file mode 100644 index 00000000000..90ec2c24f76 --- /dev/null +++ b/public/app/features/data-connections/tabs/RecordedQueries/RecordedQueries.tsx @@ -0,0 +1,5 @@ +import React, { ReactElement } from 'react'; + +export function RecordedQueries(): ReactElement | null { + return
The recorded queries tab is under development.
; +} diff --git a/public/app/features/data-connections/tabs/RecordedQueries/index.tsx b/public/app/features/data-connections/tabs/RecordedQueries/index.tsx new file mode 100644 index 00000000000..fc4ff37714f --- /dev/null +++ b/public/app/features/data-connections/tabs/RecordedQueries/index.tsx @@ -0,0 +1 @@ +export * from './RecordedQueries'; diff --git a/public/app/features/plugins/components/AppPluginLoader.test.tsx b/public/app/features/plugins/components/AppPluginLoader.test.tsx new file mode 100644 index 00000000000..4c1430c7372 --- /dev/null +++ b/public/app/features/plugins/components/AppPluginLoader.test.tsx @@ -0,0 +1,132 @@ +import { render, screen } from '@testing-library/react'; +import React, { Component } from 'react'; +import { Router } from 'react-router-dom'; + +import { AppPlugin, PluginType, AppRootProps, NavModelItem } from '@grafana/data'; +import { locationService, setEchoSrv } from '@grafana/runtime'; +import { Echo } from 'app/core/services/echo/Echo'; + +import { getMockPlugin } from '../__mocks__/pluginMocks'; +import { useImportAppPlugin } from '../hooks/useImportAppPlugin'; + +import { AppPluginLoader } from './AppPluginLoader'; + +jest.mock('../hooks/useImportAppPlugin', () => ({ + useImportAppPlugin: jest.fn(), +})); + +const useImportAppPluginMock = useImportAppPlugin as jest.Mock< + ReturnType, + Parameters +>; + +const TEXTS = { + PLUGIN_TITLE: 'Amazing App', + PLUGIN_CONTENT: 'This is my amazing app plugin!', + PLUGIN_TAB_TITLE_A: 'Tab (A)', + PLUGIN_TAB_TITLE_B: 'Tab (B)', +}; + +describe('AppPluginLoader', () => { + beforeEach(() => { + jest.resetAllMocks(); + AppPluginComponent.timesMounted = 0; + setEchoSrv(new Echo()); + }); + + test('renders the app plugin correctly', async () => { + useImportAppPluginMock.mockReturnValue({ value: getAppPluginMock(), loading: false, error: undefined }); + + renderAppPlugin(); + + expect(await screen.findByText(TEXTS.PLUGIN_TITLE)).toBeVisible(); + expect(await screen.findByText(TEXTS.PLUGIN_CONTENT)).toBeVisible(); + expect(await screen.findByLabelText(`Tab ${TEXTS.PLUGIN_TAB_TITLE_A}`)).toBeVisible(); + expect(await screen.findByLabelText(`Tab ${TEXTS.PLUGIN_TAB_TITLE_B}`)).toBeVisible(); + expect(screen.queryByText('Loading ...')).not.toBeInTheDocument(); + }); + + test('renders the app plugin only once', async () => { + useImportAppPluginMock.mockReturnValue({ value: getAppPluginMock(), loading: false, error: undefined }); + + renderAppPlugin(); + + expect(await screen.findByText(TEXTS.PLUGIN_TITLE)).toBeVisible(); + expect(AppPluginComponent.timesMounted).toEqual(1); + }); + + test('renders a loader while the plugin is loading', async () => { + useImportAppPluginMock.mockReturnValue({ value: undefined, loading: true, error: undefined }); + + renderAppPlugin(); + + expect(await screen.findByText('Loading ...')).toBeVisible(); + expect(screen.queryByText(TEXTS.PLUGIN_TITLE)).not.toBeInTheDocument(); + }); + + test('renders an error message if there are any errors while importing the plugin', async () => { + const errorMsg = 'Unable to find plugin'; + useImportAppPluginMock.mockReturnValue({ value: undefined, loading: false, error: new Error(errorMsg) }); + + renderAppPlugin(); + + expect(await screen.findByText(errorMsg)).toBeVisible(); + expect(screen.queryByText(TEXTS.PLUGIN_TITLE)).not.toBeInTheDocument(); + }); +}); + +function renderAppPlugin() { + render( + + ; + + ); +} +class AppPluginComponent extends Component { + static timesMounted = 0; + + componentDidMount() { + AppPluginComponent.timesMounted += 1; + + const node: NavModelItem = { + text: TEXTS.PLUGIN_TITLE, + children: [ + { + text: TEXTS.PLUGIN_TAB_TITLE_A, + url: '/tab-a', + id: 'a', + }, + { + text: TEXTS.PLUGIN_TAB_TITLE_B, + url: '/tab-b', + id: 'b', + }, + ], + }; + + this.props.onNavChanged({ + main: node, + node, + }); + } + + render() { + return

{TEXTS.PLUGIN_CONTENT}

; + } +} + +function getAppPluginMeta() { + return getMockPlugin({ + type: PluginType.app, + enabled: true, + }); +} + +function getAppPluginMock() { + const plugin = new AppPlugin(); + + plugin.root = AppPluginComponent; + plugin.init(getAppPluginMeta()); + + return plugin; +} diff --git a/public/app/features/plugins/components/AppPluginLoader.tsx b/public/app/features/plugins/components/AppPluginLoader.tsx new file mode 100644 index 00000000000..fe263276930 --- /dev/null +++ b/public/app/features/plugins/components/AppPluginLoader.tsx @@ -0,0 +1,44 @@ +import React, { useState } from 'react'; +import { useLocation, useParams } from 'react-router-dom'; + +import { NavModel } from '@grafana/data'; +import { getWarningNav } from 'app/angular/services/nav_model_srv'; +import Page from 'app/core/components/Page/Page'; +import PageLoader from 'app/core/components/PageLoader/PageLoader'; + +import { useImportAppPlugin } from '../hooks/useImportAppPlugin'; + +type AppPluginLoaderProps = { + // The id of the app plugin to be loaded + id: string; + // The base URL path - defaults to the current path + basePath?: string; +}; + +// This component can be used to render an app-plugin based on its plugin ID. +export const AppPluginLoader = ({ id, basePath }: AppPluginLoaderProps) => { + const [nav, setNav] = useState(null); + const { value: plugin, error, loading } = useImportAppPlugin(id); + const queryParams = useParams(); + const { pathname } = useLocation(); + + if (error) { + return ; + } + + return ( + <> + {loading && } + {nav && } + {!loading && plugin && plugin.root && ( + + )} + + ); +}; diff --git a/public/app/features/plugins/hooks/tests/useImportAppPlugin.test.tsx b/public/app/features/plugins/hooks/tests/useImportAppPlugin.test.tsx new file mode 100644 index 00000000000..5d90c9700ce --- /dev/null +++ b/public/app/features/plugins/hooks/tests/useImportAppPlugin.test.tsx @@ -0,0 +1,150 @@ +import { render, act, waitFor } from '@testing-library/react'; +import React from 'react'; + +import { AppPlugin, PluginType } from '@grafana/data'; + +import { getMockPlugin } from '../../__mocks__/pluginMocks'; +import { getPluginSettings } from '../../pluginSettings'; +import { importAppPlugin } from '../../plugin_loader'; +import { useImportAppPlugin } from '../useImportAppPlugin'; + +jest.mock('../../pluginSettings', () => ({ + getPluginSettings: jest.fn(), +})); +jest.mock('../../plugin_loader', () => ({ + importAppPlugin: jest.fn(), +})); + +const importAppPluginMock = importAppPlugin as jest.Mock< + ReturnType, + Parameters +>; + +const getPluginSettingsMock = getPluginSettings as jest.Mock< + ReturnType, + Parameters +>; + +const PLUGIN_ID = 'sample-plugin'; + +describe('useImportAppPlugin()', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + test('returns the imported plugin in case it exists', async () => { + let response: any; + getPluginSettingsMock.mockResolvedValue(getAppPluginMeta()); + importAppPluginMock.mockResolvedValue(getAppPluginMock()); + + act(() => { + response = runHook(PLUGIN_ID); + }); + + await waitFor(() => expect(response.value).not.toBeUndefined()); + await waitFor(() => expect(response.error).toBeUndefined()); + await waitFor(() => expect(response.loading).toBe(false)); + }); + + test('returns an error if the plugin does not exist', async () => { + let response: any; + + act(() => { + response = runHook(PLUGIN_ID); + }); + + await waitFor(() => expect(response.value).toBeUndefined()); + await waitFor(() => expect(response.error).not.toBeUndefined()); + await waitFor(() => expect(response.error.message).toMatch(/unknown plugin/i)); + await waitFor(() => expect(response.loading).toBe(false)); + }); + + test('returns an error if the plugin is not an app', async () => { + let response: any; + getPluginSettingsMock.mockResolvedValue(getAppPluginMeta({ type: PluginType.panel })); + importAppPluginMock.mockResolvedValue(getAppPluginMock()); + + act(() => { + response = runHook(PLUGIN_ID); + }); + + await waitFor(() => expect(response.value).toBeUndefined()); + await waitFor(() => expect(response.error).not.toBeUndefined()); + await waitFor(() => expect(response.error.message).toMatch(/plugin must be an app/i)); + await waitFor(() => expect(response.loading).toBe(false)); + }); + + test('returns an error if the plugin is not enabled', async () => { + let response: any; + getPluginSettingsMock.mockResolvedValue(getAppPluginMeta({ enabled: false })); + importAppPluginMock.mockResolvedValue(getAppPluginMock()); + + act(() => { + response = runHook(PLUGIN_ID); + }); + + await waitFor(() => expect(response.value).toBeUndefined()); + await waitFor(() => expect(response.error).not.toBeUndefined()); + await waitFor(() => expect(response.error.message).toMatch(/is not enabled/i)); + await waitFor(() => expect(response.loading).toBe(false)); + }); + + test('returns errors that happen during fetching plugin settings', async () => { + let response: any; + const errorMsg = 'Error while fetching plugin data'; + getPluginSettingsMock.mockRejectedValue(new Error(errorMsg)); + importAppPluginMock.mockResolvedValue(getAppPluginMock()); + + act(() => { + response = runHook(PLUGIN_ID); + }); + + await waitFor(() => expect(response.value).toBeUndefined()); + await waitFor(() => expect(response.error).not.toBeUndefined()); + await waitFor(() => expect(response.error.message).toBe(errorMsg)); + await waitFor(() => expect(response.loading).toBe(false)); + }); + + test('returns errors that happen during importing a plugin', async () => { + let response: any; + const errorMsg = 'Error while importing the plugin'; + getPluginSettingsMock.mockResolvedValue(getAppPluginMeta()); + importAppPluginMock.mockRejectedValue(new Error(errorMsg)); + + act(() => { + response = runHook(PLUGIN_ID); + }); + + await waitFor(() => expect(response.value).toBeUndefined()); + await waitFor(() => expect(response.error).not.toBeUndefined()); + await waitFor(() => expect(response.error.message).toBe(errorMsg)); + await waitFor(() => expect(response.loading).toBe(false)); + }); +}); + +function runHook(id: string): any { + const returnVal = {}; + function TestComponent() { + Object.assign(returnVal, useImportAppPlugin(id)); + return null; + } + render(); + return returnVal; +} + +function getAppPluginMeta(overrides?: Record) { + return getMockPlugin({ + id: PLUGIN_ID, + type: PluginType.app, + enabled: true, + ...overrides, + }); +} + +function getAppPluginMock() { + const plugin = new AppPlugin(); + + plugin.init(getAppPluginMeta()); + + return plugin; +} diff --git a/public/app/features/plugins/hooks/useImportAppPlugin.ts b/public/app/features/plugins/hooks/useImportAppPlugin.ts new file mode 100644 index 00000000000..cd48d9ce22f --- /dev/null +++ b/public/app/features/plugins/hooks/useImportAppPlugin.ts @@ -0,0 +1,26 @@ +import useAsync from 'react-use/lib/useAsync'; + +import { PluginType } from '@grafana/data'; + +import { getPluginSettings } from '../pluginSettings'; +import { importAppPlugin } from '../plugin_loader'; + +export const useImportAppPlugin = (id: string) => { + return useAsync(async () => { + const pluginMeta = await getPluginSettings(id); + + if (!pluginMeta) { + throw new Error(`Unknown plugin: "${id}"`); + } + + if (pluginMeta.type !== PluginType.app) { + throw new Error(`Plugin must be an app (currently "${pluginMeta.type}")`); + } + + if (!pluginMeta.enabled) { + throw new Error(`Application "${id}" is not enabled`); + } + + return await importAppPlugin(pluginMeta); + }); +}; diff --git a/public/app/routes/routes.tsx b/public/app/routes/routes.tsx index 420d887f8af..e69f684807a 100644 --- a/public/app/routes/routes.tsx +++ b/public/app/routes/routes.tsx @@ -8,6 +8,7 @@ import { contextSrv } from 'app/core/services/context_srv'; import UserAdminPage from 'app/features/admin/UserAdminPage'; import LdapPage from 'app/features/admin/ldap/LdapPage'; import { getAlertingRoutes } from 'app/features/alerting/routes'; +import { getRoutes as getDataConnectionsRoutes } from 'app/features/data-connections/routes'; import { getLiveRoutes } from 'app/features/live/pages/routes'; import { getRoutes as getPluginCatalogRoutes } from 'app/features/plugins/admin/routes'; import { getProfileRoutes } from 'app/features/profile/routes'; @@ -431,6 +432,7 @@ export function getAppRoutes(): RouteDescriptor[] { ...getProfileRoutes(), ...extraRoutes, ...getPublicDashboardRoutes(), + ...getDataConnectionsRoutes(), { path: '/*', component: ErrorPage, From 06da129210fae46fb439ba3b2ec8c421a8de049e Mon Sep 17 00:00:00 2001 From: George Robinson Date: Fri, 10 Jun 2022 11:19:20 +0100 Subject: [PATCH 16/79] Docs: Add paragraph about rolling back to opt-out (#50583) --- docs/sources/alerting/migrating-alerts/opt-out.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/sources/alerting/migrating-alerts/opt-out.md b/docs/sources/alerting/migrating-alerts/opt-out.md index 3c2ef5f0b1f..49cf9e64fb7 100644 --- a/docs/sources/alerting/migrating-alerts/opt-out.md +++ b/docs/sources/alerting/migrating-alerts/opt-out.md @@ -42,6 +42,8 @@ enabled = false enabled = true ``` +Installations that have been migrated to Grafana Alerting can [roll back]({{< relref "roll-back/" >}}) to legacy alerting at any time. + > **Note:** This topic is only relevant for OSS and Enterprise customers. Contact customer support to enable or disable Grafana Alerting for your Grafana Cloud stack. The `ngalert` toggle previously used to enable or disable Grafana Alerting is no longer available. From c4f7cb2a556d3b50e00a05bd648b33e710775257 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Jun 2022 12:21:49 +0200 Subject: [PATCH 17/79] Update dependency @react-aria/button to v3.5.0 (#50391) Co-authored-by: Renovate Bot --- package.json | 2 +- packages/grafana-ui/package.json | 2 +- yarn.lock | 63 +++++++++++++++++++------------- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index add10c6848f..0c26753e683 100644 --- a/package.json +++ b/package.json @@ -269,7 +269,7 @@ "@opentelemetry/exporter-collector": "0.25.0", "@opentelemetry/semantic-conventions": "1.3.1", "@popperjs/core": "2.11.5", - "@react-aria/button": "3.4.4", + "@react-aria/button": "3.5.0", "@react-aria/dialog": "3.2.0", "@react-aria/focus": "3.6.0", "@react-aria/interactions": "3.9.0", diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 819039f6293..e3925d07e39 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -39,7 +39,7 @@ "@grafana/slate-react": "0.22.10-grafana", "@monaco-editor/react": "4.3.1", "@popperjs/core": "2.11.5", - "@react-aria/button": "3.4.4", + "@react-aria/button": "3.5.0", "@react-aria/dialog": "3.2.0", "@react-aria/focus": "3.6.0", "@react-aria/menu": "3.4.4", diff --git a/yarn.lock b/yarn.lock index a539999587b..b3d2063d683 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4678,7 +4678,7 @@ __metadata: "@mdx-js/react": 1.6.22 "@monaco-editor/react": 4.3.1 "@popperjs/core": 2.11.5 - "@react-aria/button": 3.4.4 + "@react-aria/button": 3.5.0 "@react-aria/dialog": 3.2.0 "@react-aria/focus": 3.6.0 "@react-aria/menu": 3.4.4 @@ -7295,19 +7295,19 @@ __metadata: languageName: node linkType: hard -"@react-aria/button@npm:3.4.4": - version: 3.4.4 - resolution: "@react-aria/button@npm:3.4.4" +"@react-aria/button@npm:3.5.0": + version: 3.5.0 + resolution: "@react-aria/button@npm:3.5.0" dependencies: "@babel/runtime": ^7.6.2 - "@react-aria/focus": ^3.5.5 - "@react-aria/interactions": ^3.8.4 - "@react-aria/utils": ^3.12.0 - "@react-stately/toggle": ^3.2.7 - "@react-types/button": ^3.4.5 + "@react-aria/focus": ^3.6.0 + "@react-aria/interactions": ^3.9.0 + "@react-aria/utils": ^3.13.0 + "@react-stately/toggle": ^3.3.0 + "@react-types/button": ^3.5.0 peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 - checksum: 8320dfd1ead1eb2097025edc347615ac020b59f05581b5bdf14815bb7518d399ab280525242852122bb864af3318ef5d2bc3bcd3431063c53f6a2fd2cca45fa1 + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: f6960c91b073ea68f82dd7751f8c77ccdc4d62e227f190ae0030811a15bf943998e87708ceadaded2d7fbd45015b3c68ea74618d7157121c01c6126cc06de3ee languageName: node linkType: hard @@ -7594,17 +7594,17 @@ __metadata: languageName: node linkType: hard -"@react-stately/toggle@npm:^3.2.7": - version: 3.2.7 - resolution: "@react-stately/toggle@npm:3.2.7" +"@react-stately/toggle@npm:^3.3.0": + version: 3.3.0 + resolution: "@react-stately/toggle@npm:3.3.0" dependencies: "@babel/runtime": ^7.6.2 - "@react-stately/utils": ^3.4.1 - "@react-types/checkbox": ^3.2.7 - "@react-types/shared": ^3.12.0 + "@react-stately/utils": ^3.5.0 + "@react-types/checkbox": ^3.3.0 + "@react-types/shared": ^3.13.0 peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 - checksum: 47119dac74d5da5fca3cdc1579eb28c5367e8610c55c27a69a78102ad3aa6518623fd422820f3d49362c3da6a3d738f4900d8f6660b0b7a47ca6adaff1e81f84 + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 3c6e721acb8c7527d44f82dbf1edb219bccbf84006c908b8ba66c7e044d4d81115227f84b781f3dcdf60d835986d964526ff653564f15889001499f6c22c6e85 languageName: node linkType: hard @@ -7656,14 +7656,25 @@ __metadata: languageName: node linkType: hard -"@react-types/checkbox@npm:^3.2.7": - version: 3.2.7 - resolution: "@react-types/checkbox@npm:3.2.7" +"@react-types/button@npm:^3.5.0": + version: 3.5.0 + resolution: "@react-types/button@npm:3.5.0" dependencies: - "@react-types/shared": ^3.12.0 + "@react-types/shared": ^3.13.0 peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 - checksum: 02214780b7efe2863cdd9cf97048fabbd0ad3abcaa5c4f1f073e39ede8541fd4a7345cd88f3ad4ad25f5acedfe52120e6a1a4470508f29c641a91d5493915060 + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 85c6e278c5e0da8a9f4595b8d2943dcb28ecd2ffe56eab0c9387daf6a25823045cb432964c88a8729cf036cf5058a077e2b33c63636412af2e9f14ec8f70d534 + languageName: node + linkType: hard + +"@react-types/checkbox@npm:^3.3.0": + version: 3.3.0 + resolution: "@react-types/checkbox@npm:3.3.0" + dependencies: + "@react-types/shared": ^3.13.0 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 9fb34839fca2c2e46b3b68ca492eee8a9db76b532eb363717529cb28970b72880b54f80eacbbf1a5607b6a0749b1b1fa68fe010a6c0a381fe58ee50ffb7ce276 languageName: node linkType: hard @@ -20728,7 +20739,7 @@ __metadata: "@opentelemetry/semantic-conventions": 1.3.1 "@pmmmwh/react-refresh-webpack-plugin": 0.5.6 "@popperjs/core": 2.11.5 - "@react-aria/button": 3.4.4 + "@react-aria/button": 3.5.0 "@react-aria/dialog": 3.2.0 "@react-aria/focus": 3.6.0 "@react-aria/interactions": 3.9.0 From 5633648383a3e41ee75f80766b907436562d808f Mon Sep 17 00:00:00 2001 From: Dimitris Sotirakis Date: Fri, 10 Jun 2022 13:28:50 +0300 Subject: [PATCH 18/79] Rename release pipelines (#50585) --- .drone.yml | 58 ++++++++++++++-------------- scripts/drone/pipelines/release.star | 28 +++++++------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/.drone.yml b/.drone.yml index afd93a2702e..59af3003bb4 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1505,7 +1505,7 @@ type: docker --- depends_on: [] kind: pipeline -name: oss-build-e2e-publish-release +name: release-oss-build-e2e-publish node: type: no-parallel platform: @@ -1802,7 +1802,7 @@ volumes: --- depends_on: [] kind: pipeline -name: oss-test-release +name: release-oss-test node: type: no-parallel platform: @@ -1920,7 +1920,7 @@ volumes: --- depends_on: [] kind: pipeline -name: oss-integration-tests-release +name: release-oss-integration-tests node: type: no-parallel platform: @@ -2010,11 +2010,11 @@ volumes: medium: memory --- depends_on: -- oss-build-e2e-publish-release -- oss-test-release -- oss-integration-tests-release +- release-oss-build-e2e-publish +- release-oss-test +- release-oss-integration-tests kind: pipeline -name: oss-windows-release +name: release-oss-windows platform: arch: amd64 os: windows @@ -2076,7 +2076,7 @@ depends_on: [] image_pull_secrets: - dockerconfigjson kind: pipeline -name: enterprise-build-e2e-publish-release +name: release-enterprise-build-e2e-publish node: type: no-parallel platform: @@ -2424,7 +2424,7 @@ depends_on: [] image_pull_secrets: - dockerconfigjson kind: pipeline -name: enterprise-test-release +name: release-enterprise-test node: type: no-parallel platform: @@ -2587,7 +2587,7 @@ depends_on: [] image_pull_secrets: - dockerconfigjson kind: pipeline -name: enterprise-integration-tests-release +name: release-enterprise-integration-tests node: type: no-parallel platform: @@ -2728,13 +2728,13 @@ volumes: clone: disable: true depends_on: -- enterprise-build-e2e-publish-release -- enterprise-test-release -- enterprise-integration-tests-release +- release-enterprise-build-e2e-publish +- release-enterprise-test +- release-enterprise-integration-tests image_pull_secrets: - dockerconfigjson kind: pipeline -name: enterprise-windows-release +name: release-enterprise-windows platform: arch: amd64 os: windows @@ -3317,7 +3317,7 @@ volumes: --- depends_on: [] kind: pipeline -name: oss-build-e2e-publish-release-branch +name: release-branch-oss-build-e2e-publish node: type: no-parallel platform: @@ -3584,7 +3584,7 @@ volumes: --- depends_on: [] kind: pipeline -name: oss-test-release-branch +name: release-branch-oss-test node: type: no-parallel platform: @@ -3696,7 +3696,7 @@ volumes: --- depends_on: [] kind: pipeline -name: oss-integration-tests-release-branch +name: release-branch-oss-integration-tests node: type: no-parallel platform: @@ -3780,11 +3780,11 @@ volumes: medium: memory --- depends_on: -- oss-build-e2e-publish-release-branch -- oss-test-release-branch -- oss-integration-tests-release-branch +- release-branch-oss-build-e2e-publish +- release-branch-oss-test +- release-branch-oss-integration-tests kind: pipeline -name: oss-windows-release-branch +name: release-branch-oss-windows platform: arch: amd64 os: windows @@ -3835,7 +3835,7 @@ depends_on: [] image_pull_secrets: - dockerconfigjson kind: pipeline -name: enterprise-build-e2e-publish-release-branch +name: release-branch-enterprise-build-e2e-publish node: type: no-parallel platform: @@ -4181,7 +4181,7 @@ depends_on: [] image_pull_secrets: - dockerconfigjson kind: pipeline -name: enterprise-test-release-branch +name: release-branch-enterprise-test node: type: no-parallel platform: @@ -4335,7 +4335,7 @@ depends_on: [] image_pull_secrets: - dockerconfigjson kind: pipeline -name: enterprise-integration-tests-release-branch +name: release-branch-enterprise-integration-tests node: type: no-parallel platform: @@ -4467,13 +4467,13 @@ volumes: clone: disable: true depends_on: -- enterprise-build-e2e-publish-release-branch -- enterprise-test-release-branch -- enterprise-integration-tests-release-branch +- release-branch-enterprise-build-e2e-publish +- release-branch-enterprise-test +- release-branch-enterprise-integration-tests image_pull_secrets: - dockerconfigjson kind: pipeline -name: enterprise-windows-release-branch +name: release-branch-enterprise-windows platform: arch: amd64 os: windows @@ -4683,6 +4683,6 @@ kind: secret name: gcp_upload_artifacts_key --- kind: signature -hmac: 378147a306d077b0566f5353c9e5c236c40dbb8d81392091a1346ed275b86496 +hmac: 0abf901971b403a4cf5887d1051ec4a656335a23d9a92809a71312d115b4862f ... diff --git a/scripts/drone/pipelines/release.star b/scripts/drone/pipelines/release.star index 8b3f3af3e80..38c81d3c129 100644 --- a/scripts/drone/pipelines/release.star +++ b/scripts/drone/pipelines/release.star @@ -253,7 +253,7 @@ def get_oss_pipelines(trigger, ver_mode): volumes = integration_test_services_volumes() init_steps, test_steps, build_steps, integration_test_steps, package_steps, windows_package_steps, publish_steps = get_steps(edition=edition, ver_mode=ver_mode) windows_pipeline = pipeline( - name='oss-windows-{}'.format(ver_mode), edition=edition, trigger=trigger, + name='{}-oss-windows'.format(ver_mode), edition=edition, trigger=trigger, steps=[identify_runner_step('windows')] + windows_package_steps, platform='windows', depends_on=[ 'oss-build{}-publish-{}'.format(get_e2e_suffix(), ver_mode), @@ -261,7 +261,7 @@ def get_oss_pipelines(trigger, ver_mode): ) pipelines = [ pipeline( - name='oss-build{}-publish-{}'.format(get_e2e_suffix(), ver_mode), edition=edition, trigger=trigger, services=[], + name='{}-oss-build{}-publish'.format(ver_mode, get_e2e_suffix()), edition=edition, trigger=trigger, services=[], steps=init_steps + build_steps + package_steps + publish_steps, volumes=volumes, ), @@ -269,21 +269,21 @@ def get_oss_pipelines(trigger, ver_mode): if not disable_tests: pipelines.extend([ pipeline( - name='oss-test-{}'.format(ver_mode), edition=edition, trigger=trigger, services=[], + name='{}-oss-test'.format(ver_mode), edition=edition, trigger=trigger, services=[], steps=init_steps + test_steps, volumes=[], ), pipeline( - name='oss-integration-tests-{}'.format(ver_mode), edition=edition, trigger=trigger, services=services, + name='{}-oss-integration-tests'.format(ver_mode), edition=edition, trigger=trigger, services=services, steps=[download_grabpl_step(), identify_runner_step(),] + integration_test_steps, volumes=volumes, ) ]) deps = { 'depends_on': [ - 'oss-build{}-publish-{}'.format(get_e2e_suffix(), ver_mode), - 'oss-test-{}'.format(ver_mode), - 'oss-integration-tests-{}'.format(ver_mode) + '{}-oss-build{}-publish'.format(ver_mode, get_e2e_suffix()), + '{}-oss-test'.format(ver_mode), + '{}-oss-integration-tests'.format(ver_mode) ] } windows_pipeline.update(deps) @@ -315,7 +315,7 @@ def get_enterprise_pipelines(trigger, ver_mode): step.update(deps_on_clone_enterprise_step) windows_pipeline = pipeline( - name='enterprise-windows-{}'.format(ver_mode), edition=edition, trigger=trigger, + name='{}-enterprise-windows'.format(ver_mode), edition=edition, trigger=trigger, steps=[identify_runner_step('windows')] + windows_package_steps, platform='windows', depends_on=[ 'enterprise-build{}-publish-{}'.format(get_e2e_suffix(), ver_mode), @@ -323,7 +323,7 @@ def get_enterprise_pipelines(trigger, ver_mode): ) pipelines = [ pipeline( - name='enterprise-build{}-publish-{}'.format(get_e2e_suffix(), ver_mode), edition=edition, trigger=trigger, services=[], + name='{}-enterprise-build{}-publish'.format(ver_mode, get_e2e_suffix()), edition=edition, trigger=trigger, services=[], steps=init_steps + build_steps + package_steps + publish_steps, volumes=volumes, ), @@ -331,21 +331,21 @@ def get_enterprise_pipelines(trigger, ver_mode): if not disable_tests: pipelines.extend([ pipeline( - name='enterprise-test-{}'.format(ver_mode), edition=edition, trigger=trigger, services=[], + name='{}-enterprise-test'.format(ver_mode), edition=edition, trigger=trigger, services=[], steps=init_steps + test_steps, volumes=[], ), pipeline( - name='enterprise-integration-tests-{}'.format(ver_mode), edition=edition, trigger=trigger, services=services, + name='{}-enterprise-integration-tests'.format(ver_mode), edition=edition, trigger=trigger, services=services, steps=[download_grabpl_step(), identify_runner_step(), clone_enterprise_step(ver_mode), init_enterprise_step(ver_mode),] + integration_test_steps, volumes=volumes, ), ]) deps = { 'depends_on': [ - 'enterprise-build{}-publish-{}'.format(get_e2e_suffix(), ver_mode), - 'enterprise-test-{}'.format(ver_mode), - 'enterprise-integration-tests-{}'.format(ver_mode) + '{}-enterprise-build{}-publish'.format(ver_mode, get_e2e_suffix()), + '{}-enterprise-test'.format(ver_mode), + '{}-enterprise-integration-tests'.format(ver_mode) ] } windows_pipeline.update(deps) From 9a62849dc39e960640b5762cc5eace1baf29c0ee Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Fri, 10 Jun 2022 12:02:52 +0100 Subject: [PATCH 19/79] Add i18n feature flag (#50590) --- packages/grafana-data/src/types/featureToggles.gen.ts | 1 + pkg/services/featuremgmt/registry.go | 6 ++++++ pkg/services/featuremgmt/toggles_gen.go | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index e140056e6ee..ef1da04661a 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -60,4 +60,5 @@ export interface FeatureToggles { cloudMonitoringExperimentalUI?: boolean; logRequestsInstrumentedAsUnknown?: boolean; dataConnectionsConsole?: boolean; + internationalization?: boolean; } diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 416f5da8ef8..b913a3384e8 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -2,6 +2,7 @@ // pkg/services/featuremgmt/registry.go // Then run tests in: // pkg/services/featuremgmt/toggles_gen_test.go +// twice to generate and validate the feature flag files package featuremgmt @@ -246,5 +247,10 @@ var ( Description: "Enables a new top-level page called Data Connections. This page is an experiment for better grouping of installing / configuring data sources and other plugins.", State: FeatureStateAlpha, }, + { + Name: "internationalization", + Description: "Enables work-in-progress internationalization", + State: FeatureStateAlpha, + }, } ) diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index ec83161bcf8..f73b679a30d 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -182,4 +182,8 @@ const ( // FlagDataConnectionsConsole // Enables a new top-level page called Data Connections. This page is an experiment for better grouping of installing / configuring data sources and other plugins. FlagDataConnectionsConsole = "dataConnectionsConsole" + + // FlagInternationalization + // Enables work-in-progress internationalization + FlagInternationalization = "internationalization" ) From 5a9b62270893ee93029d3297ef9f271a655b6055 Mon Sep 17 00:00:00 2001 From: idafurjes <36131195+idafurjes@users.noreply.github.com> Date: Fri, 10 Jun 2022 14:33:12 +0200 Subject: [PATCH 20/79] Add xorm tag to dashboard version id (#50599) --- pkg/services/dashboardversion/model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/dashboardversion/model.go b/pkg/services/dashboardversion/model.go index fd087921269..4141430f529 100644 --- a/pkg/services/dashboardversion/model.go +++ b/pkg/services/dashboardversion/model.go @@ -13,7 +13,7 @@ var ( ) type DashboardVersion struct { - ID int64 `json:"id"` + ID int64 `json:"id" xorm:"pk autoincr 'id'"` DashboardID int64 `json:"dashboardId" xorm:"dashboard_id"` ParentVersion int `json:"parentVersion"` RestoredFrom int `json:"restoredFrom"` From 99db588919000165d726fd9466fb10ba0445dde6 Mon Sep 17 00:00:00 2001 From: Todd Treece <360020+toddtreece@users.noreply.github.com> Date: Fri, 10 Jun 2022 08:41:29 -0400 Subject: [PATCH 21/79] Data Sources: Add QueryData OAuth & cookie forwarding middleware (#50466) --- .../forwarded_cookie_middleware_test.go | 65 +++++++++++++++ .../forwarded_cookies_middleware.go | 24 ++++++ .../forwarded_oauth_identity_middleware.go | 31 +++++++ ...orwarded_oauth_identity_middleware_test.go | 82 +++++++++++++++++++ .../http_client_provider.go | 3 +- pkg/services/query/query.go | 12 +++ 6 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 pkg/infra/httpclient/httpclientprovider/forwarded_cookie_middleware_test.go create mode 100644 pkg/infra/httpclient/httpclientprovider/forwarded_cookies_middleware.go create mode 100644 pkg/infra/httpclient/httpclientprovider/forwarded_oauth_identity_middleware.go create mode 100644 pkg/infra/httpclient/httpclientprovider/forwarded_oauth_identity_middleware_test.go diff --git a/pkg/infra/httpclient/httpclientprovider/forwarded_cookie_middleware_test.go b/pkg/infra/httpclient/httpclientprovider/forwarded_cookie_middleware_test.go new file mode 100644 index 00000000000..07753e0c0f5 --- /dev/null +++ b/pkg/infra/httpclient/httpclientprovider/forwarded_cookie_middleware_test.go @@ -0,0 +1,65 @@ +package httpclientprovider_test + +import ( + "net/http" + "testing" + + "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "github.com/grafana/grafana/pkg/infra/httpclient/httpclientprovider" + "github.com/stretchr/testify/require" +) + +func TestForwardedCookiesMiddleware(t *testing.T) { + tcs := []struct { + desc string + allowedCookies []string + expectedCookieHeader string + }{ + { + desc: "With nil allowedCookies should not populate Cookie header", + allowedCookies: nil, + expectedCookieHeader: "", + }, + { + desc: "With empty allowed cookies should not populate Cookie header", + allowedCookies: []string{}, + expectedCookieHeader: "", + }, + { + desc: "When provided with allowed cookies should populate Cookie header", + allowedCookies: []string{"c1", "c3"}, + expectedCookieHeader: "c1=1; c3=3", + }, + } + + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + ctx := &testContext{} + finalRoundTripper := ctx.createRoundTripper() + forwarded := []*http.Cookie{ + {Name: "c1", Value: "1"}, + {Name: "c2", Value: "2"}, + {Name: "c3", Value: "3"}, + } + mw := httpclientprovider.ForwardedCookiesMiddleware(forwarded, tc.allowedCookies) + opts := httpclient.Options{} + rt := mw.CreateMiddleware(opts, finalRoundTripper) + require.NotNil(t, rt) + middlewareName, ok := mw.(httpclient.MiddlewareName) + require.True(t, ok) + require.Equal(t, "forwarded-cookies", middlewareName.MiddlewareName()) + + req, err := http.NewRequest(http.MethodGet, "http://", nil) + require.NoError(t, err) + res, err := rt.RoundTrip(req) + require.NoError(t, err) + require.NotNil(t, res) + if res.Body != nil { + require.NoError(t, res.Body.Close()) + } + require.Len(t, ctx.callChain, 1) + require.ElementsMatch(t, []string{"final"}, ctx.callChain) + require.Equal(t, tc.expectedCookieHeader, ctx.req.Header.Get("Cookie")) + }) + } +} diff --git a/pkg/infra/httpclient/httpclientprovider/forwarded_cookies_middleware.go b/pkg/infra/httpclient/httpclientprovider/forwarded_cookies_middleware.go new file mode 100644 index 00000000000..7d0ef6f2ef5 --- /dev/null +++ b/pkg/infra/httpclient/httpclientprovider/forwarded_cookies_middleware.go @@ -0,0 +1,24 @@ +package httpclientprovider + +import ( + "net/http" + + "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "github.com/grafana/grafana/pkg/util/proxyutil" +) + +const ForwardedCookiesMiddlewareName = "forwarded-cookies" + +// ForwardedCookiesMiddleware middleware that sets Cookie header on the +// outgoing request, if forwarded cookies configured/provided. +func ForwardedCookiesMiddleware(forwardedCookies []*http.Cookie, allowedCookies []string) httpclient.Middleware { + return httpclient.NamedMiddlewareFunc(ForwardedCookiesMiddlewareName, func(opts httpclient.Options, next http.RoundTripper) http.RoundTripper { + return httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) { + for _, cookie := range forwardedCookies { + req.AddCookie(cookie) + } + proxyutil.ClearCookieHeader(req, allowedCookies) + return next.RoundTrip(req) + }) + }) +} diff --git a/pkg/infra/httpclient/httpclientprovider/forwarded_oauth_identity_middleware.go b/pkg/infra/httpclient/httpclientprovider/forwarded_oauth_identity_middleware.go new file mode 100644 index 00000000000..d5c78e794d7 --- /dev/null +++ b/pkg/infra/httpclient/httpclientprovider/forwarded_oauth_identity_middleware.go @@ -0,0 +1,31 @@ +package httpclientprovider + +import ( + "fmt" + "net/http" + + "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "golang.org/x/oauth2" +) + +const ForwardedOAuthIdentityMiddlewareName = "forwarded-oauth-identity" + +// ForwardedOAuthIdentityMiddleware middleware that sets Authorization/X-ID-Token +// headers on the outgoing request if an OAuth Token is provided +func ForwardedOAuthIdentityMiddleware(token *oauth2.Token) httpclient.Middleware { + return httpclient.NamedMiddlewareFunc(ForwardedOAuthIdentityMiddlewareName, func(opts httpclient.Options, next http.RoundTripper) http.RoundTripper { + if token == nil { + return next + } + return httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) { + req.Header.Set("Authorization", fmt.Sprintf("%s %s", token.Type(), token.AccessToken)) + + idToken, ok := token.Extra("id_token").(string) + if ok && idToken != "" { + req.Header.Set("X-ID-Token", idToken) + } + + return next.RoundTrip(req) + }) + }) +} diff --git a/pkg/infra/httpclient/httpclientprovider/forwarded_oauth_identity_middleware_test.go b/pkg/infra/httpclient/httpclientprovider/forwarded_oauth_identity_middleware_test.go new file mode 100644 index 00000000000..1cd65d1bf09 --- /dev/null +++ b/pkg/infra/httpclient/httpclientprovider/forwarded_oauth_identity_middleware_test.go @@ -0,0 +1,82 @@ +package httpclientprovider_test + +import ( + "net/http" + "testing" + + "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "github.com/grafana/grafana/pkg/infra/httpclient/httpclientprovider" + "github.com/stretchr/testify/require" + "golang.org/x/oauth2" +) + +func TestForwardedOAuthIdentityMiddleware(t *testing.T) { + at := &oauth2.Token{ + AccessToken: "access-token", + } + tcs := []struct { + desc string + token *oauth2.Token + expectedAuthorizationHeader string + expectedIDTokenHeader string + }{ + { + desc: "With nil token should not populate Cookie headers", + token: nil, + expectedAuthorizationHeader: "", + expectedIDTokenHeader: "", + }, + { + desc: "With access token set should populate Authorization header", + token: at, + expectedAuthorizationHeader: "Bearer access-token", + expectedIDTokenHeader: "", + }, + { + desc: "With Authorization and X-ID-Token header set should populate Authorization and X-Id-Token header", + token: at.WithExtra(map[string]interface{}{"id_token": "id-token"}), + expectedAuthorizationHeader: "Bearer access-token", + expectedIDTokenHeader: "id-token", + }, + } + + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + ctx := &testContext{} + finalRoundTripper := ctx.createRoundTripper() + mw := httpclientprovider.ForwardedOAuthIdentityMiddleware(tc.token) + opts := httpclient.Options{} + rt := mw.CreateMiddleware(opts, finalRoundTripper) + require.NotNil(t, rt) + middlewareName, ok := mw.(httpclient.MiddlewareName) + require.True(t, ok) + require.Equal(t, "forwarded-oauth-identity", middlewareName.MiddlewareName()) + + req, err := http.NewRequest(http.MethodGet, "http://", nil) + require.NoError(t, err) + res, err := rt.RoundTrip(req) + require.NoError(t, err) + require.NotNil(t, res) + if res.Body != nil { + require.NoError(t, res.Body.Close()) + } + require.Len(t, ctx.callChain, 1) + require.ElementsMatch(t, []string{"final"}, ctx.callChain) + require.Equal(t, tc.expectedAuthorizationHeader, ctx.req.Header.Get("Authorization")) + require.Equal(t, tc.expectedIDTokenHeader, ctx.req.Header.Get("X-ID-Token")) + }) + } +} + +type testContext struct { + callChain []string + req *http.Request +} + +func (c *testContext) createRoundTripper() http.RoundTripper { + return httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) { + c.callChain = append(c.callChain, "final") + c.req = req + return &http.Response{StatusCode: http.StatusOK}, nil + }) +} diff --git a/pkg/infra/httpclient/httpclientprovider/http_client_provider.go b/pkg/infra/httpclient/httpclientprovider/http_client_provider.go index 0c6944cffba..b09b50c7b8f 100644 --- a/pkg/infra/httpclient/httpclientprovider/http_client_provider.go +++ b/pkg/infra/httpclient/httpclientprovider/http_client_provider.go @@ -5,12 +5,11 @@ import ( "net/http" "time" - "github.com/grafana/grafana/pkg/models" - sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/metrics/metricutil" "github.com/grafana/grafana/pkg/infra/tracing" + "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" "github.com/mwitkow/go-conntrack" ) diff --git a/pkg/services/query/query.go b/pkg/services/query/query.go index 082696f6691..206d06ed39e 100644 --- a/pkg/services/query/query.go +++ b/pkg/services/query/query.go @@ -10,6 +10,7 @@ import ( "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/expr" + "github.com/grafana/grafana/pkg/infra/httpclient/httpclientprovider" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" @@ -22,6 +23,7 @@ import ( "github.com/grafana/grafana/pkg/util/proxyutil" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" ) const ( @@ -136,6 +138,13 @@ func (s *Service) handleQueryData(ctx context.Context, user *models.SignedInUser Queries: []backend.DataQuery{}, } + middlewares := []httpclient.Middleware{} + if parsedReq.httpRequest != nil { + middlewares = append(middlewares, + httpclientprovider.ForwardedCookiesMiddleware(parsedReq.httpRequest.Cookies(), ds.AllowedCookies()), + ) + } + if s.oAuthTokenService.IsOAuthPassThruEnabled(ds) { if token := s.oAuthTokenService.GetCurrentOAuthToken(ctx, user); token != nil { req.Headers["Authorization"] = fmt.Sprintf("%s %s", token.Type(), token.AccessToken) @@ -144,6 +153,7 @@ func (s *Service) handleQueryData(ctx context.Context, user *models.SignedInUser if ok && idToken != "" { req.Headers["X-ID-Token"] = idToken } + middlewares = append(middlewares, httpclientprovider.ForwardedOAuthIdentityMiddleware(token)) } } @@ -162,6 +172,8 @@ func (s *Service) handleQueryData(ctx context.Context, user *models.SignedInUser req.Queries = append(req.Queries, q.query) } + ctx = httpclient.WithContextualMiddleware(ctx, middlewares...) + return s.pluginClient.QueryData(ctx, req) } From 5539bb4578fb9489a882507403cf28893b165c92 Mon Sep 17 00:00:00 2001 From: Gilles De Mey Date: Fri, 10 Jun 2022 14:58:15 +0200 Subject: [PATCH 22/79] Alerting: use "find" to wait for expression editor to load (#50602) --- .../features/alerting/unified/RuleEditor.test.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/public/app/features/alerting/unified/RuleEditor.test.tsx b/public/app/features/alerting/unified/RuleEditor.test.tsx index e5dd5cf7959..8efe878f59b 100644 --- a/public/app/features/alerting/unified/RuleEditor.test.tsx +++ b/public/app/features/alerting/unified/RuleEditor.test.tsx @@ -148,19 +148,21 @@ describe('RuleEditor', () => { await renderRuleEditor(); await waitFor(() => expect(mocks.searchFolders).toHaveBeenCalled()); - await waitFor(() => expect(mocks.api.discoverFeatures).toHaveBeenCalled()); - await userEvent.type(await ui.inputs.name.find(), 'my great new rule'); - await userEvent.click(await ui.buttons.lotexAlert.get()); + + await userEvent.click(await ui.buttons.lotexAlert.find()); + const dataSourceSelect = ui.inputs.dataSource.get(); await userEvent.click(byRole('combobox').get(dataSourceSelect)); await clickSelectOption(dataSourceSelect, 'Prom (default)'); await waitFor(() => expect(mocks.api.fetchRulerRules).toHaveBeenCalled()); + + await userEvent.type(await ui.inputs.expr.find(), 'up == 1'); + + await userEvent.type(ui.inputs.name.get(), 'my great new rule'); await clickSelectOption(ui.inputs.namespace.get(), 'namespace2'); await clickSelectOption(ui.inputs.group.get(), 'group2'); - await userEvent.type(ui.inputs.expr.get(), 'up == 1'); - await userEvent.type(ui.inputs.annotationValue(0).get(), 'some summary'); await userEvent.type(ui.inputs.annotationValue(1).get(), 'some description'); @@ -353,7 +355,7 @@ describe('RuleEditor', () => { await clickSelectOption(ui.inputs.namespace.get(), 'namespace2'); await clickSelectOption(ui.inputs.group.get(), 'group2'); - await userEvent.type(ui.inputs.expr.get(), 'up == 1'); + await userEvent.type(await ui.inputs.expr.find(), 'up == 1'); // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed await userEvent.click(ui.buttons.addLabel.get(), { pointerEventsCheck: PointerEventsCheckLevel.Never }); From b4559b41c297795e9bb98c0077b53db0e61db644 Mon Sep 17 00:00:00 2001 From: Karl Persson Date: Fri, 10 Jun 2022 15:26:00 +0200 Subject: [PATCH 23/79] RBAC: fix access control check in both new navivation and new search view (#50596) * check access control permissions for new navigation * check access control actions in new search view --- pkg/api/index.go | 28 +++++++++++++------ .../search/components/DashboardActions.tsx | 12 ++++---- .../search/components/ManageDashboards.tsx | 13 +++++++-- .../search/components/ManageDashboardsNew.tsx | 11 ++++++-- 4 files changed, 45 insertions(+), 19 deletions(-) diff --git a/pkg/api/index.go b/pkg/api/index.go index c288c653817..945840a2936 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -460,6 +460,11 @@ func (hs *HTTPServer) buildStarredItemsNavLinks(c *models.ReqContext, prefs *pre } func (hs *HTTPServer) buildDashboardNavLinks(c *models.ReqContext, hasEditPerm bool) []*dtos.NavLink { + hasAccess := ac.HasAccess(hs.AccessControl, c) + hasEditPermInAnyFolder := func(c *models.ReqContext) bool { + return hasEditPerm + } + dashboardChildNavs := []*dtos.NavLink{} if !hs.Features.IsEnabled(featuremgmt.FlagNewNavigation) { dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{ @@ -496,19 +501,26 @@ func (hs *HTTPServer) buildDashboardNavLinks(c *models.ReqContext, hasEditPerm b dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{ Text: "Divider", Divider: true, Id: "divider", HideFromTabs: true, }) - dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{ - Text: "New dashboard", Icon: "plus", Url: hs.Cfg.AppSubURL + "/dashboard/new", HideFromTabs: true, Id: "new-dashboard", ShowIconInNavbar: true, - }) - if c.OrgRole == models.ROLE_ADMIN || c.OrgRole == models.ROLE_EDITOR { + + if hasAccess(hasEditPermInAnyFolder, ac.EvalPermission(dashboards.ActionDashboardsCreate)) { + dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{ + Text: "New dashboard", Icon: "plus", Url: hs.Cfg.AppSubURL + "/dashboard/new", HideFromTabs: true, Id: "new-dashboard", ShowIconInNavbar: true, + }) + } + + if hasAccess(ac.ReqOrgAdminOrEditor, ac.EvalPermission(dashboards.ActionFoldersCreate)) { dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{ Text: "New folder", SubTitle: "Create a new folder to organize your dashboards", Id: "new-folder", Icon: "plus", Url: hs.Cfg.AppSubURL + "/dashboards/folder/new", HideFromTabs: true, ShowIconInNavbar: true, }) } - dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{ - Text: "Import", SubTitle: "Import dashboard from file or Grafana.com", Id: "import", Icon: "plus", - Url: hs.Cfg.AppSubURL + "/dashboard/import", HideFromTabs: true, ShowIconInNavbar: true, - }) + + if hasAccess(hasEditPermInAnyFolder, ac.EvalPermission(dashboards.ActionDashboardsCreate)) { + dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{ + Text: "Import", SubTitle: "Import dashboard from file or Grafana.com", Id: "import", Icon: "plus", + Url: hs.Cfg.AppSubURL + "/dashboard/import", HideFromTabs: true, ShowIconInNavbar: true, + }) + } } return dashboardChildNavs } diff --git a/public/app/features/search/components/DashboardActions.tsx b/public/app/features/search/components/DashboardActions.tsx index 95b8f6e04b9..945cf39e67d 100644 --- a/public/app/features/search/components/DashboardActions.tsx +++ b/public/app/features/search/components/DashboardActions.tsx @@ -4,11 +4,11 @@ import { HorizontalGroup, LinkButton } from '@grafana/ui'; export interface Props { folderId?: number; - isEditor: boolean; - canEdit?: boolean; + canCreateFolders?: boolean; + canCreateDashboards?: boolean; } -export const DashboardActions: FC = ({ folderId, isEditor, canEdit }) => { +export const DashboardActions: FC = ({ folderId, canCreateFolders = false, canCreateDashboards = false }) => { const actionUrl = (type: string) => { let url = `dashboard/${type}`; @@ -22,9 +22,9 @@ export const DashboardActions: FC = ({ folderId, isEditor, canEdit }) => return (
- {canEdit && New Dashboard} - {!folderId && isEditor && New Folder} - {canEdit && Import} + {canCreateDashboards && New Dashboard} + {!folderId && canCreateFolders && New Folder} + {canCreateDashboards && Import}
); diff --git a/public/app/features/search/components/ManageDashboards.tsx b/public/app/features/search/components/ManageDashboards.tsx index 0a5737ae657..01b201f98bc 100644 --- a/public/app/features/search/components/ManageDashboards.tsx +++ b/public/app/features/search/components/ManageDashboards.tsx @@ -5,7 +5,7 @@ import { GrafanaTheme } from '@grafana/data'; import { FilterInput, Spinner, stylesFactory, useTheme } from '@grafana/ui'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import { contextSrv } from 'app/core/services/context_srv'; -import { FolderDTO } from 'app/types'; +import { FolderDTO, AccessControlAction } from 'app/types'; import { useManageDashboards } from '../hooks/useManageDashboards'; import { useSearchQuery } from '../hooks/useSearchQuery'; @@ -53,10 +53,10 @@ export const ManageDashboards: FC = memo(({ folder }) => { results, loading, initialLoading, - canSave, allChecked, hasEditPermissionInFolders, canMove, + canSave, canDelete, onToggleSection, onToggleChecked, @@ -101,7 +101,14 @@ export const ManageDashboards: FC = memo(({ folder }) => {
- +
diff --git a/public/app/features/search/components/ManageDashboardsNew.tsx b/public/app/features/search/components/ManageDashboardsNew.tsx index 8c40c37f389..8bb968176e3 100644 --- a/public/app/features/search/components/ManageDashboardsNew.tsx +++ b/public/app/features/search/components/ManageDashboardsNew.tsx @@ -6,7 +6,7 @@ import { GrafanaTheme2 } from '@grafana/data'; import { config } from '@grafana/runtime'; import { Input, useStyles2, Spinner } from '@grafana/ui'; import { contextSrv } from 'app/core/services/context_srv'; -import { FolderDTO } from 'app/types'; +import { FolderDTO, AccessControlAction } from 'app/types'; import { SEARCH_PANELS_LOCAL_STORAGE_KEY } from '../constants'; import { useSearchQuery } from '../hooks/useSearchQuery'; @@ -57,7 +57,14 @@ export const ManageDashboardsNew = React.memo(({ folder }: Props) => { suffix={false ? : null} />
- + Date: Fri, 10 Jun 2022 14:26:56 +0100 Subject: [PATCH 24/79] No longer generate theme json as it's not compatible with the right tooling (#50605) --- public/sass/theme.dark.generated.json | 963 ----------------------- public/sass/theme.light.generated.json | 963 ----------------------- scripts/cli/generateSassVariableFiles.ts | 13 - 3 files changed, 1939 deletions(-) delete mode 100644 public/sass/theme.dark.generated.json delete mode 100644 public/sass/theme.light.generated.json diff --git a/public/sass/theme.dark.generated.json b/public/sass/theme.dark.generated.json deleted file mode 100644 index 178b38b2614..00000000000 --- a/public/sass/theme.dark.generated.json +++ /dev/null @@ -1,963 +0,0 @@ -{ - "name": "Dark", - "isDark": true, - "isLight": false, - "colors": { - "mode": "dark", - "whiteBase": "204, 204, 220", - "border": { - "weak": "rgba(204, 204, 220, 0.07)", - "medium": "rgba(204, 204, 220, 0.15)", - "strong": "rgba(204, 204, 220, 0.25)" - }, - "text": { - "primary": "rgb(204, 204, 220)", - "secondary": "rgba(204, 204, 220, 0.65)", - "disabled": "rgba(204, 204, 220, 0.6)", - "link": "#6E9FFF", - "maxContrast": "#FFFFFF" - }, - "primary": { - "main": "#3D71D9", - "text": "#6E9FFF", - "border": "#6E9FFF", - "name": "primary", - "shade": "rgb(90, 134, 222)", - "transparent": "#3D71D926", - "contrastText": "#FFFFFF" - }, - "secondary": { - "main": "rgba(204, 204, 220, 0.16)", - "shade": "rgba(204, 204, 220, 0.20)", - "text": "rgb(204, 204, 220)", - "contrastText": "rgb(204, 204, 220)", - "border": "rgba(204, 204, 220, 0.25)", - "name": "secondary", - "transparent": "rgba(204, 204, 220, 0.15)" - }, - "info": { - "main": "#3D71D9", - "text": "#6E9FFF", - "border": "#6E9FFF", - "name": "info", - "shade": "rgb(90, 134, 222)", - "transparent": "#3D71D926", - "contrastText": "#FFFFFF" - }, - "error": { - "main": "#D10E5C", - "text": "#FF5286", - "name": "error", - "border": "#FF5286", - "shade": "rgb(215, 50, 116)", - "transparent": "#D10E5C26", - "contrastText": "#FFFFFF" - }, - "success": { - "main": "#1A7F4B", - "text": "#6CCF8E", - "name": "success", - "border": "#6CCF8E", - "shade": "rgb(60, 146, 102)", - "transparent": "#1A7F4B26", - "contrastText": "#FFFFFF" - }, - "warning": { - "main": "#F5B73D", - "text": "#F8D06B", - "name": "warning", - "border": "#F8D06B", - "shade": "rgb(246, 193, 90)", - "transparent": "#F5B73D26", - "contrastText": "#000000" - }, - "background": { - "canvas": "#111217", - "primary": "#181b1f", - "secondary": "#22252b" - }, - "action": { - "hover": "rgba(204, 204, 220, 0.16)", - "selected": "rgba(204, 204, 220, 0.12)", - "focus": "rgba(204, 204, 220, 0.16)", - "hoverOpacity": 0.08, - "disabledText": "rgba(204, 204, 220, 0.6)", - "disabledBackground": "rgba(204, 204, 220, 0.04)", - "disabledOpacity": 0.38 - }, - "gradients": { - "brandHorizontal": "linear-gradient(270deg, #F55F3E 0%, #FF8833 100%)", - "brandVertical": "linear-gradient(0.01deg, #F55F3E 0.01%, #FF8833 99.99%)" - }, - "contrastThreshold": 3, - "hoverFactor": 0.03, - "tonalOffset": 0.15 - }, - "breakpoints": { - "values": { - "xs": 0, - "sm": 544, - "md": 769, - "lg": 992, - "xl": 1200, - "xxl": 1440 - }, - "keys": [ - "xs", - "sm", - "md", - "lg", - "xl", - "xxl" - ], - "unit": "px" - }, - "shape": {}, - "components": { - "height": { - "sm": 3, - "md": 4, - "lg": 6 - }, - "input": { - "borderColor": "rgba(204, 204, 220, 0.15)", - "borderHover": "rgba(204, 204, 220, 0.25)", - "text": "rgb(204, 204, 220)", - "background": "#111217" - }, - "panel": { - "padding": 1, - "headerHeight": 4, - "background": "#181b1f", - "borderColor": "rgba(204, 204, 220, 0.07)", - "boxShadow": "none" - }, - "dropdown": { - "background": "#111217" - }, - "tooltip": { - "background": "#22252b", - "text": "rgb(204, 204, 220)" - }, - "dashboard": { - "background": "#111217", - "padding": 1 - }, - "overlay": { - "background": "rgba(63, 62, 62, 0.45)" - }, - "sidemenu": { - "width": 48 - }, - "menuTabs": { - "height": 41 - }, - "textHighlight": { - "text": "#000000", - "background": "#F5B73D" - }, - "horizontalDrawer": { - "defaultHeight": 400 - } - }, - "typography": { - "htmlFontSize": 14, - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontFamilyMonospace": "'Roboto Mono', monospace", - "fontSize": 14, - "fontWeightLight": 300, - "fontWeightRegular": 400, - "fontWeightMedium": 500, - "fontWeightBold": 500, - "size": { - "base": "14px", - "xs": "10px", - "sm": "12px", - "md": "14px", - "lg": "18px" - }, - "h1": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 300, - "fontSize": "2rem", - "lineHeight": 1.167, - "letterSpacing": "-0.00893em" - }, - "h2": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 300, - "fontSize": "1.7142857142857142rem", - "lineHeight": 1.2, - "letterSpacing": "0em" - }, - "h3": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 400, - "fontSize": "1.5rem", - "lineHeight": 1.167, - "letterSpacing": "0em" - }, - "h4": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 400, - "fontSize": "1.2857142857142858rem", - "lineHeight": 1.235, - "letterSpacing": "0.01389em" - }, - "h5": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 400, - "fontSize": "1.1428571428571428rem", - "lineHeight": 1.334, - "letterSpacing": "0em" - }, - "h6": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 500, - "fontSize": "1rem", - "lineHeight": 1.6, - "letterSpacing": "0.01071em" - }, - "body": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 400, - "fontSize": "1rem", - "lineHeight": 1.5, - "letterSpacing": "0.01071em" - }, - "bodySmall": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 400, - "fontSize": "0.8571428571428571rem", - "lineHeight": 1.5, - "letterSpacing": "0.0125em" - } - }, - "shadows": { - "z1": "0px 1px 2px rgba(24, 26, 27, 0.75)", - "z2": "0px 4px 8px rgba(24, 26, 27, 0.75)", - "z3": "0px 8px 24px rgb(1,4,9)" - }, - "transitions": { - "duration": { - "shortest": 150, - "shorter": 200, - "short": 250, - "standard": 300, - "complex": 375, - "enteringScreen": 225, - "leavingScreen": 195 - }, - "easing": { - "easeInOut": "cubic-bezier(0.4, 0, 0.2, 1)", - "easeOut": "cubic-bezier(0.0, 0, 0.2, 1)", - "easeIn": "cubic-bezier(0.4, 0, 1, 1)", - "sharp": "cubic-bezier(0.4, 0, 0.6, 1)" - } - }, - "visualization": { - "hues": [ - { - "name": "red", - "shades": [ - { - "color": "#FFA6B0", - "name": "super-light-red" - }, - { - "color": "#FF7383", - "name": "light-red" - }, - { - "color": "#F2495C", - "name": "red", - "primary": true - }, - { - "color": "#E02F44", - "name": "semi-dark-red" - }, - { - "color": "#C4162A", - "name": "dark-red" - } - ] - }, - { - "name": "orange", - "shades": [ - { - "color": "#FFCB7D", - "name": "super-light-orange", - "aliases": [] - }, - { - "color": "#FFB357", - "name": "light-orange", - "aliases": [] - }, - { - "color": "#FF9830", - "name": "orange", - "aliases": [], - "primary": true - }, - { - "color": "#FF780A", - "name": "semi-dark-orange", - "aliases": [] - }, - { - "color": "#FA6400", - "name": "dark-orange", - "aliases": [] - } - ] - }, - { - "name": "yellow", - "shades": [ - { - "color": "#FFF899", - "name": "super-light-yellow", - "aliases": [] - }, - { - "color": "#FFEE52", - "name": "light-yellow", - "aliases": [] - }, - { - "color": "#FADE2A", - "name": "yellow", - "aliases": [], - "primary": true - }, - { - "color": "#F2CC0C", - "name": "semi-dark-yellow", - "aliases": [] - }, - { - "color": "#E0B400", - "name": "dark-yellow", - "aliases": [] - } - ] - }, - { - "name": "green", - "shades": [ - { - "color": "#C8F2C2", - "name": "super-light-green", - "aliases": [] - }, - { - "color": "#96D98D", - "name": "light-green", - "aliases": [] - }, - { - "color": "#73BF69", - "name": "green", - "aliases": [], - "primary": true - }, - { - "color": "#56A64B", - "name": "semi-dark-green", - "aliases": [] - }, - { - "color": "#37872D", - "name": "dark-green", - "aliases": [] - } - ] - }, - { - "name": "blue", - "shades": [ - { - "color": "#C0D8FF", - "name": "super-light-blue", - "aliases": [] - }, - { - "color": "#8AB8FF", - "name": "light-blue", - "aliases": [] - }, - { - "color": "#5794F2", - "name": "blue", - "aliases": [], - "primary": true - }, - { - "color": "#3274D9", - "name": "semi-dark-blue", - "aliases": [] - }, - { - "color": "#1F60C4", - "name": "dark-blue", - "aliases": [] - } - ] - }, - { - "name": "purple", - "shades": [ - { - "color": "#DEB6F2", - "name": "super-light-purple", - "aliases": [] - }, - { - "color": "#CA95E5", - "name": "light-purple", - "aliases": [] - }, - { - "color": "#B877D9", - "name": "purple", - "aliases": [], - "primary": true - }, - { - "color": "#A352CC", - "name": "semi-dark-purple", - "aliases": [] - }, - { - "color": "#8F3BB8", - "name": "dark-purple", - "aliases": [] - } - ] - } - ], - "palette": [ - "green", - "semi-dark-yellow", - "light-blue", - "semi-dark-orange", - "red", - "blue", - "purple", - "#705DA0", - "dark-green", - "yellow", - "#447EBC", - "#C15C17", - "#890F02", - "#0A437C", - "#6D1F62", - "#584477", - "#B7DBAB", - "#F4D598", - "#70DBED", - "#F9BA8F", - "#F29191", - "#82B5D8", - "#E5A8E2", - "#AEA2E0", - "#629E51", - "#E5AC0E", - "#64B0C8", - "#E0752D", - "#BF1B00", - "#0A50A1", - "#962D82", - "#614D93", - "#9AC48A", - "#F2C96D", - "#65C5DB", - "#F9934E", - "#EA6460", - "#5195CE", - "#D683CE", - "#806EB7", - "#3F6833", - "#967302", - "#2F575E", - "#99440A", - "#58140C", - "#052B51", - "#511749", - "#3F2B5B", - "#E0F9D7", - "#FCEACA", - "#CFFAFF", - "#F9E2D2", - "#FCE2DE", - "#BADFF4", - "#F9D9F9", - "#DEDAF7" - ] - }, - "zIndex": { - "navbarFixed": 1000, - "sidemenu": 1020, - "dropdown": 1030, - "typeahead": 1030, - "tooltip": 1040, - "modalBackdrop": 1050, - "modal": 1060, - "portal": 1061 - }, - "v1": { - "name": "Dark", - "typography": { - "fontFamily": { - "sansSerif": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "monospace": "'Roboto Mono', monospace" - }, - "size": { - "base": "14px", - "xs": "10px", - "sm": "12px", - "md": "14px", - "lg": "18px" - }, - "heading": { - "h1": "2rem", - "h2": "1.7142857142857142rem", - "h3": "1.5rem", - "h4": "1.2857142857142858rem", - "h5": "1.1428571428571428rem", - "h6": "1rem" - }, - "weight": { - "light": 300, - "regular": 400, - "semibold": 500, - "bold": 500 - }, - "lineHeight": { - "xs": 1.5, - "sm": 1.5, - "md": 1.5, - "lg": 1.2 - }, - "link": { - "decoration": "none", - "hoverDecoration": "none" - } - }, - "breakpoints": { - "xs": "0px", - "sm": "544px", - "md": "769px", - "lg": "992px", - "xl": "1200px", - "xxl": "1440px" - }, - "spacing": { - "base": 8, - "insetSquishMd": "4px 8px", - "d": "16px", - "xxs": "2px", - "xs": "4px", - "sm": "8px", - "md": "16px", - "lg": "24px", - "xl": "32px", - "gutter": "32px", - "formSpacingBase": 8, - "formMargin": "32px", - "formFieldsetMargin": "16px", - "formInputHeight": 32, - "formButtonHeight": 32, - "formInputPaddingHorizontal": "8px", - "formInputAffixPaddingHorizontal": "4px", - "formInputMargin": "16px", - "formLabelPadding": "0 0 0 2px", - "formLabelMargin": "0 0 4px 0", - "formValidationMessagePadding": "4px 8px", - "formValidationMessageMargin": "4px 0 0 0", - "inlineFormMargin": "4px" - }, - "border": { - "radius": { - "sm": "2px", - "md": "4px", - "lg": "6px" - }, - "width": { - "sm": "1px" - } - }, - "height": { - "sm": 24, - "md": 32, - "lg": 48 - }, - "panelPadding": 8, - "panelHeaderHeight": 32, - "zIndex": { - "navbarFixed": 1000, - "sidemenu": 1020, - "dropdown": 1030, - "typeahead": 1030, - "tooltip": 1040, - "modalBackdrop": 1050, - "modal": 1060, - "portal": 1061 - }, - "type": "dark", - "isDark": true, - "isLight": false, - "palette": { - "gray98": "#f7f8fa", - "gray97": "#f1f5f9", - "gray95": "#e9edf2", - "gray90": "#dce1e6", - "gray85": "#c7d0d9", - "gray70": "#9fa7b3", - "gray60": "#7b8087", - "gray33": "#464c54", - "gray25": "#2c3235", - "gray15": "#202226", - "gray10": "#141619", - "gray05": "#0b0c0e", - "blue95": "#5794f2", - "blue85": "#33a2e5", - "blue80": "#3274d9", - "blue77": "#1f60c4", - "red88": "#e02f44", - "black": "#000000", - "white": "#ffffff", - "dark1": "#141414", - "dark2": "#161719", - "dark3": "#1f1f20", - "dark4": "#212124", - "dark5": "#222426", - "dark6": "#262628", - "dark7": "#292a2d", - "dark8": "#2f2f32", - "dark9": "#343436", - "dark10": "#424345", - "gray1": "#555555", - "gray2": "#8e8e8e", - "gray3": "#b3b3b3", - "gray4": "#d8d9da", - "gray5": "#ececec", - "gray6": "#f4f5f8", - "gray7": "#fbfbfb", - "redBase": "#e02f44", - "redShade": "#c4162a", - "greenBase": "#299c46", - "greenShade": "#23843b", - "red": "#d44a3a", - "yellow": "#ecbb13", - "purple": "#9933cc", - "variable": "#32d1df", - "orange": "#eb7b18", - "orangeDark": "#ff780a", - "brandPrimary": "#eb7b18", - "brandSuccess": "#1A7F4B", - "brandWarning": "#F5B73D", - "brandDanger": "#D10E5C", - "queryRed": "#FF5286", - "queryGreen": "#6CCF8E", - "queryPurple": "#fe85fc", - "queryOrange": "#eb7b18", - "online": "#1A7F4B", - "warn": "#1A7F4B", - "critical": "#1A7F4B" - }, - "colors": { - "bg1": "#181b1f", - "bg2": "#22252b", - "bg3": "rgba(204, 204, 220, 0.16)", - "dashboardBg": "#111217", - "bgBlue1": "#3D71D9", - "bgBlue2": "rgb(90, 134, 222)", - "border1": "rgba(204, 204, 220, 0.07)", - "border2": "rgba(204, 204, 220, 0.15)", - "border3": "rgba(204, 204, 220, 0.25)", - "formLabel": "rgb(204, 204, 220)", - "formDescription": "rgba(204, 204, 220, 0.65)", - "formInputBg": "#111217", - "formInputBgDisabled": "rgba(204, 204, 220, 0.04)", - "formInputBorder": "rgba(204, 204, 220, 0.15)", - "formInputBorderHover": "rgba(204, 204, 220, 0.25)", - "formInputBorderActive": "#6E9FFF", - "formInputBorderInvalid": "#FF5286", - "formInputPlaceholderText": "rgba(204, 204, 220, 0.6)", - "formInputText": "rgb(204, 204, 220)", - "formInputDisabledText": "rgba(204, 204, 220, 0.6)", - "formFocusOutline": "#3D71D9", - "formValidationMessageText": "#FFFFFF", - "formValidationMessageBg": "#D10E5C", - "textStrong": "#FFFFFF", - "textHeading": "rgb(204, 204, 220)", - "text": "rgb(204, 204, 220)", - "textSemiWeak": "rgba(204, 204, 220, 0.65)", - "textWeak": "rgba(204, 204, 220, 0.65)", - "textFaint": "rgba(204, 204, 220, 0.6)", - "textBlue": "#6E9FFF", - "bodyBg": "#111217", - "panelBg": "#181b1f", - "panelBorder": "rgba(204, 204, 220, 0.07)", - "pageHeaderBg": "#111217", - "pageHeaderBorder": "#111217", - "dropdownBg": "#111217", - "dropdownShadow": "#000000", - "dropdownOptionHoverBg": "#22252b", - "link": "rgb(204, 204, 220)", - "linkDisabled": "rgba(204, 204, 220, 0.6)", - "linkHover": "#FFFFFF", - "linkExternal": "#6E9FFF" - }, - "shadows": { - "listItem": "none" - }, - "visualization": { - "hues": [ - { - "name": "red", - "shades": [ - { - "color": "#FFA6B0", - "name": "super-light-red" - }, - { - "color": "#FF7383", - "name": "light-red" - }, - { - "color": "#F2495C", - "name": "red", - "primary": true - }, - { - "color": "#E02F44", - "name": "semi-dark-red" - }, - { - "color": "#C4162A", - "name": "dark-red" - } - ] - }, - { - "name": "orange", - "shades": [ - { - "color": "#FFCB7D", - "name": "super-light-orange", - "aliases": [] - }, - { - "color": "#FFB357", - "name": "light-orange", - "aliases": [] - }, - { - "color": "#FF9830", - "name": "orange", - "aliases": [], - "primary": true - }, - { - "color": "#FF780A", - "name": "semi-dark-orange", - "aliases": [] - }, - { - "color": "#FA6400", - "name": "dark-orange", - "aliases": [] - } - ] - }, - { - "name": "yellow", - "shades": [ - { - "color": "#FFF899", - "name": "super-light-yellow", - "aliases": [] - }, - { - "color": "#FFEE52", - "name": "light-yellow", - "aliases": [] - }, - { - "color": "#FADE2A", - "name": "yellow", - "aliases": [], - "primary": true - }, - { - "color": "#F2CC0C", - "name": "semi-dark-yellow", - "aliases": [] - }, - { - "color": "#E0B400", - "name": "dark-yellow", - "aliases": [] - } - ] - }, - { - "name": "green", - "shades": [ - { - "color": "#C8F2C2", - "name": "super-light-green", - "aliases": [] - }, - { - "color": "#96D98D", - "name": "light-green", - "aliases": [] - }, - { - "color": "#73BF69", - "name": "green", - "aliases": [], - "primary": true - }, - { - "color": "#56A64B", - "name": "semi-dark-green", - "aliases": [] - }, - { - "color": "#37872D", - "name": "dark-green", - "aliases": [] - } - ] - }, - { - "name": "blue", - "shades": [ - { - "color": "#C0D8FF", - "name": "super-light-blue", - "aliases": [] - }, - { - "color": "#8AB8FF", - "name": "light-blue", - "aliases": [] - }, - { - "color": "#5794F2", - "name": "blue", - "aliases": [], - "primary": true - }, - { - "color": "#3274D9", - "name": "semi-dark-blue", - "aliases": [] - }, - { - "color": "#1F60C4", - "name": "dark-blue", - "aliases": [] - } - ] - }, - { - "name": "purple", - "shades": [ - { - "color": "#DEB6F2", - "name": "super-light-purple", - "aliases": [] - }, - { - "color": "#CA95E5", - "name": "light-purple", - "aliases": [] - }, - { - "color": "#B877D9", - "name": "purple", - "aliases": [], - "primary": true - }, - { - "color": "#A352CC", - "name": "semi-dark-purple", - "aliases": [] - }, - { - "color": "#8F3BB8", - "name": "dark-purple", - "aliases": [] - } - ] - } - ], - "palette": [ - "green", - "semi-dark-yellow", - "light-blue", - "semi-dark-orange", - "red", - "blue", - "purple", - "#705DA0", - "dark-green", - "yellow", - "#447EBC", - "#C15C17", - "#890F02", - "#0A437C", - "#6D1F62", - "#584477", - "#B7DBAB", - "#F4D598", - "#70DBED", - "#F9BA8F", - "#F29191", - "#82B5D8", - "#E5A8E2", - "#AEA2E0", - "#629E51", - "#E5AC0E", - "#64B0C8", - "#E0752D", - "#BF1B00", - "#0A50A1", - "#962D82", - "#614D93", - "#9AC48A", - "#F2C96D", - "#65C5DB", - "#F9934E", - "#EA6460", - "#5195CE", - "#D683CE", - "#806EB7", - "#3F6833", - "#967302", - "#2F575E", - "#99440A", - "#58140C", - "#052B51", - "#511749", - "#3F2B5B", - "#E0F9D7", - "#FCEACA", - "#CFFAFF", - "#F9E2D2", - "#FCE2DE", - "#BADFF4", - "#F9D9F9", - "#DEDAF7" - ] - } - } -} \ No newline at end of file diff --git a/public/sass/theme.light.generated.json b/public/sass/theme.light.generated.json deleted file mode 100644 index ddd149cd45c..00000000000 --- a/public/sass/theme.light.generated.json +++ /dev/null @@ -1,963 +0,0 @@ -{ - "name": "Light", - "isDark": false, - "isLight": true, - "colors": { - "mode": "light", - "blackBase": "36, 41, 46", - "primary": { - "main": "#3871DC", - "border": "#1F62E0", - "text": "#1F62E0", - "name": "primary", - "shade": "rgb(44, 90, 176)", - "transparent": "#3871DC14", - "contrastText": "#FFFFFF" - }, - "text": { - "primary": "rgba(36, 41, 46, 1)", - "secondary": "rgba(36, 41, 46, 0.75)", - "disabled": "rgba(36, 41, 46, 0.50)", - "link": "#1F62E0", - "maxContrast": "#000000" - }, - "border": { - "weak": "rgba(36, 41, 46, 0.12)", - "medium": "rgba(36, 41, 46, 0.30)", - "strong": "rgba(36, 41, 46, 0.40)" - }, - "secondary": { - "main": "rgba(36, 41, 46, 0.16)", - "shade": "rgba(36, 41, 46, 0.20)", - "contrastText": "rgba(36, 41, 46, 1)", - "text": "rgba(36, 41, 46, 1)", - "border": "rgba(36, 41, 46, 0.40)", - "name": "secondary", - "transparent": "rgba(36, 41, 46, 0.08)" - }, - "info": { - "main": "#3871DC", - "text": "#1F62E0", - "name": "info", - "border": "#1F62E0", - "shade": "rgb(44, 90, 176)", - "transparent": "#3871DC14", - "contrastText": "#FFFFFF" - }, - "error": { - "main": "#E0226E", - "text": "#CF0E5B", - "border": "#CF0E5B", - "name": "error", - "shade": "rgb(179, 27, 88)", - "transparent": "#E0226E14", - "contrastText": "#FFFFFF" - }, - "success": { - "main": "#1B855E", - "text": "#0A764E", - "name": "success", - "border": "#0A764E", - "shade": "rgb(21, 106, 75)", - "transparent": "#1B855E14", - "contrastText": "#FFFFFF" - }, - "warning": { - "main": "#FAD34A", - "text": "#8A6C00", - "name": "warning", - "border": "#8A6C00", - "shade": "rgb(200, 168, 59)", - "transparent": "#FAD34A14", - "contrastText": "#000000" - }, - "background": { - "canvas": "#F4F5F5", - "primary": "#FFFFFF", - "secondary": "#F4F5F5" - }, - "action": { - "hover": "rgba(36, 41, 46, 0.12)", - "selected": "rgba(36, 41, 46, 0.08)", - "hoverOpacity": 0.08, - "focus": "rgba(36, 41, 46, 0.12)", - "disabledBackground": "rgba(36, 41, 46, 0.04)", - "disabledText": "rgba(36, 41, 46, 0.50)", - "disabledOpacity": 0.38 - }, - "gradients": { - "brandHorizontal": "linear-gradient(90deg, #FF8833 0%, #F53E4C 100%)", - "brandVertical": "linear-gradient(0.01deg, #F53E4C -31.2%, #FF8833 113.07%)" - }, - "contrastThreshold": 3, - "hoverFactor": 0.03, - "tonalOffset": 0.2 - }, - "breakpoints": { - "values": { - "xs": 0, - "sm": 544, - "md": 769, - "lg": 992, - "xl": 1200, - "xxl": 1440 - }, - "keys": [ - "xs", - "sm", - "md", - "lg", - "xl", - "xxl" - ], - "unit": "px" - }, - "shape": {}, - "components": { - "height": { - "sm": 3, - "md": 4, - "lg": 6 - }, - "input": { - "borderColor": "rgba(36, 41, 46, 0.30)", - "borderHover": "rgba(36, 41, 46, 0.40)", - "text": "rgba(36, 41, 46, 1)", - "background": "#FFFFFF" - }, - "panel": { - "padding": 1, - "headerHeight": 4, - "background": "#FFFFFF", - "borderColor": "rgba(36, 41, 46, 0.12)", - "boxShadow": "none" - }, - "dropdown": { - "background": "#FFFFFF" - }, - "tooltip": { - "background": "#F4F5F5", - "text": "rgba(36, 41, 46, 1)" - }, - "dashboard": { - "background": "#F4F5F5", - "padding": 1 - }, - "overlay": { - "background": "rgba(208, 209, 211, 0.24)" - }, - "sidemenu": { - "width": 48 - }, - "menuTabs": { - "height": 41 - }, - "textHighlight": { - "text": "#000000", - "background": "#FAD34A" - }, - "horizontalDrawer": { - "defaultHeight": 400 - } - }, - "typography": { - "htmlFontSize": 14, - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontFamilyMonospace": "'Roboto Mono', monospace", - "fontSize": 14, - "fontWeightLight": 300, - "fontWeightRegular": 400, - "fontWeightMedium": 500, - "fontWeightBold": 500, - "size": { - "base": "14px", - "xs": "10px", - "sm": "12px", - "md": "14px", - "lg": "18px" - }, - "h1": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 300, - "fontSize": "2rem", - "lineHeight": 1.167, - "letterSpacing": "-0.00893em" - }, - "h2": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 300, - "fontSize": "1.7142857142857142rem", - "lineHeight": 1.2, - "letterSpacing": "0em" - }, - "h3": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 400, - "fontSize": "1.5rem", - "lineHeight": 1.167, - "letterSpacing": "0em" - }, - "h4": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 400, - "fontSize": "1.2857142857142858rem", - "lineHeight": 1.235, - "letterSpacing": "0.01389em" - }, - "h5": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 400, - "fontSize": "1.1428571428571428rem", - "lineHeight": 1.334, - "letterSpacing": "0em" - }, - "h6": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 500, - "fontSize": "1rem", - "lineHeight": 1.6, - "letterSpacing": "0.01071em" - }, - "body": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 400, - "fontSize": "1rem", - "lineHeight": 1.5, - "letterSpacing": "0.01071em" - }, - "bodySmall": { - "fontFamily": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "fontWeight": 400, - "fontSize": "0.8571428571428571rem", - "lineHeight": 1.5, - "letterSpacing": "0.0125em" - } - }, - "shadows": { - "z1": "0px 1px 2px rgba(24, 26, 27, 0.2)", - "z2": "0px 4px 8px rgba(24, 26, 27, 0.2)", - "z3": "0px 13px 20px 1px rgba(24, 26, 27, 0.18)" - }, - "transitions": { - "duration": { - "shortest": 150, - "shorter": 200, - "short": 250, - "standard": 300, - "complex": 375, - "enteringScreen": 225, - "leavingScreen": 195 - }, - "easing": { - "easeInOut": "cubic-bezier(0.4, 0, 0.2, 1)", - "easeOut": "cubic-bezier(0.0, 0, 0.2, 1)", - "easeIn": "cubic-bezier(0.4, 0, 1, 1)", - "sharp": "cubic-bezier(0.4, 0, 0.6, 1)" - } - }, - "visualization": { - "hues": [ - { - "name": "red", - "shades": [ - { - "color": "#FF7383", - "name": "super-light-red" - }, - { - "color": "#F2495C", - "name": "light-red" - }, - { - "color": "#E02F44", - "name": "red", - "primary": true - }, - { - "color": "#C4162A", - "name": "semi-dark-red" - }, - { - "color": "#AD0317", - "name": "dark-red" - } - ] - }, - { - "name": "orange", - "shades": [ - { - "color": "#FFB357", - "name": "super-light-orange", - "aliases": [] - }, - { - "color": "#FF9830", - "name": "light-orange", - "aliases": [] - }, - { - "color": "#FF780A", - "name": "orange", - "aliases": [], - "primary": true - }, - { - "color": "#FA6400", - "name": "semi-dark-orange", - "aliases": [] - }, - { - "color": "#E55400", - "name": "dark-orange", - "aliases": [] - } - ] - }, - { - "name": "yellow", - "shades": [ - { - "color": "#FFEE52", - "name": "super-light-yellow", - "aliases": [] - }, - { - "color": "#FADE2A", - "name": "light-yellow", - "aliases": [] - }, - { - "color": "#F2CC0C", - "name": "yellow", - "aliases": [], - "primary": true - }, - { - "color": "#E0B400", - "name": "semi-dark-yellow", - "aliases": [] - }, - { - "color": "#CC9D00", - "name": "dark-yellow", - "aliases": [] - } - ] - }, - { - "name": "green", - "shades": [ - { - "color": "#96D98D", - "name": "super-light-green", - "aliases": [] - }, - { - "color": "#73BF69", - "name": "light-green", - "aliases": [] - }, - { - "color": "#56A64B", - "name": "green", - "aliases": [], - "primary": true - }, - { - "color": "#37872D", - "name": "semi-dark-green", - "aliases": [] - }, - { - "color": "#19730E", - "name": "dark-green", - "aliases": [] - } - ] - }, - { - "name": "blue", - "shades": [ - { - "color": "#8AB8FF", - "name": "super-light-blue", - "aliases": [] - }, - { - "color": "#5794F2", - "name": "light-blue", - "aliases": [] - }, - { - "color": "#3274D9", - "name": "blue", - "aliases": [], - "primary": true - }, - { - "color": "#1F60C4", - "name": "semi-dark-blue", - "aliases": [] - }, - { - "color": "#1250B0", - "name": "dark-blue", - "aliases": [] - } - ] - }, - { - "name": "purple", - "shades": [ - { - "color": "#CA95E5", - "name": "super-light-purple", - "aliases": [] - }, - { - "color": "#B877D9", - "name": "light-purple", - "aliases": [] - }, - { - "color": "#A352CC", - "name": "purple", - "aliases": [], - "primary": true - }, - { - "color": "#8F3BB8", - "name": "semi-dark-purple", - "aliases": [] - }, - { - "color": "#7C2EA3", - "name": "dark-purple", - "aliases": [] - } - ] - } - ], - "palette": [ - "green", - "semi-dark-yellow", - "light-blue", - "semi-dark-orange", - "red", - "blue", - "purple", - "#705DA0", - "dark-green", - "yellow", - "#447EBC", - "#C15C17", - "#890F02", - "#0A437C", - "#6D1F62", - "#584477", - "#B7DBAB", - "#F4D598", - "#70DBED", - "#F9BA8F", - "#F29191", - "#82B5D8", - "#E5A8E2", - "#AEA2E0", - "#629E51", - "#E5AC0E", - "#64B0C8", - "#E0752D", - "#BF1B00", - "#0A50A1", - "#962D82", - "#614D93", - "#9AC48A", - "#F2C96D", - "#65C5DB", - "#F9934E", - "#EA6460", - "#5195CE", - "#D683CE", - "#806EB7", - "#3F6833", - "#967302", - "#2F575E", - "#99440A", - "#58140C", - "#052B51", - "#511749", - "#3F2B5B", - "#E0F9D7", - "#FCEACA", - "#CFFAFF", - "#F9E2D2", - "#FCE2DE", - "#BADFF4", - "#F9D9F9", - "#DEDAF7" - ] - }, - "zIndex": { - "navbarFixed": 1000, - "sidemenu": 1020, - "dropdown": 1030, - "typeahead": 1030, - "tooltip": 1040, - "modalBackdrop": 1050, - "modal": 1060, - "portal": 1061 - }, - "v1": { - "name": "Light", - "typography": { - "fontFamily": { - "sansSerif": "\"Roboto\", \"Helvetica\", \"Arial\", sans-serif", - "monospace": "'Roboto Mono', monospace" - }, - "size": { - "base": "14px", - "xs": "10px", - "sm": "12px", - "md": "14px", - "lg": "18px" - }, - "heading": { - "h1": "2rem", - "h2": "1.7142857142857142rem", - "h3": "1.5rem", - "h4": "1.2857142857142858rem", - "h5": "1.1428571428571428rem", - "h6": "1rem" - }, - "weight": { - "light": 300, - "regular": 400, - "semibold": 500, - "bold": 500 - }, - "lineHeight": { - "xs": 1.5, - "sm": 1.5, - "md": 1.5, - "lg": 1.2 - }, - "link": { - "decoration": "none", - "hoverDecoration": "none" - } - }, - "breakpoints": { - "xs": "0px", - "sm": "544px", - "md": "769px", - "lg": "992px", - "xl": "1200px", - "xxl": "1440px" - }, - "spacing": { - "base": 8, - "insetSquishMd": "4px 8px", - "d": "16px", - "xxs": "2px", - "xs": "4px", - "sm": "8px", - "md": "16px", - "lg": "24px", - "xl": "32px", - "gutter": "32px", - "formSpacingBase": 8, - "formMargin": "32px", - "formFieldsetMargin": "16px", - "formInputHeight": 32, - "formButtonHeight": 32, - "formInputPaddingHorizontal": "8px", - "formInputAffixPaddingHorizontal": "4px", - "formInputMargin": "16px", - "formLabelPadding": "0 0 0 2px", - "formLabelMargin": "0 0 4px 0", - "formValidationMessagePadding": "4px 8px", - "formValidationMessageMargin": "4px 0 0 0", - "inlineFormMargin": "4px" - }, - "border": { - "radius": { - "sm": "2px", - "md": "4px", - "lg": "6px" - }, - "width": { - "sm": "1px" - } - }, - "height": { - "sm": 24, - "md": 32, - "lg": 48 - }, - "panelPadding": 8, - "panelHeaderHeight": 32, - "zIndex": { - "navbarFixed": 1000, - "sidemenu": 1020, - "dropdown": 1030, - "typeahead": 1030, - "tooltip": 1040, - "modalBackdrop": 1050, - "modal": 1060, - "portal": 1061 - }, - "type": "light", - "isDark": false, - "isLight": true, - "palette": { - "gray98": "#f7f8fa", - "gray97": "#f1f5f9", - "gray95": "#e9edf2", - "gray90": "#dce1e6", - "gray85": "#c7d0d9", - "gray70": "#9fa7b3", - "gray60": "#7b8087", - "gray33": "#464c54", - "gray25": "#2c3235", - "gray15": "#202226", - "gray10": "#141619", - "gray05": "#0b0c0e", - "blue95": "#5794f2", - "blue85": "#33a2e5", - "blue80": "#3274d9", - "blue77": "#1f60c4", - "red88": "#e02f44", - "black": "#000000", - "white": "#ffffff", - "dark1": "#141414", - "dark2": "#161719", - "dark3": "#1f1f20", - "dark4": "#212124", - "dark5": "#222426", - "dark6": "#262628", - "dark7": "#292a2d", - "dark8": "#2f2f32", - "dark9": "#343436", - "dark10": "#424345", - "gray1": "#555555", - "gray2": "#8e8e8e", - "gray3": "#b3b3b3", - "gray4": "#d8d9da", - "gray5": "#ececec", - "gray6": "#f4f5f8", - "gray7": "#fbfbfb", - "redBase": "#e02f44", - "redShade": "#c4162a", - "greenBase": "#299c46", - "greenShade": "#23843b", - "red": "#d44a3a", - "yellow": "#ecbb13", - "purple": "#9933cc", - "variable": "#32d1df", - "orange": "#eb7b18", - "orangeDark": "#ff780a", - "brandPrimary": "#eb7b18", - "brandSuccess": "#1B855E", - "brandWarning": "#FAD34A", - "brandDanger": "#E0226E", - "queryRed": "#CF0E5B", - "queryGreen": "#0A764E", - "queryPurple": "#fe85fc", - "queryOrange": "#eb7b18", - "online": "#1B855E", - "warn": "#1B855E", - "critical": "#1B855E" - }, - "colors": { - "bg1": "#FFFFFF", - "bg2": "#F4F5F5", - "bg3": "rgba(36, 41, 46, 0.12)", - "dashboardBg": "#F4F5F5", - "bgBlue1": "#3871DC", - "bgBlue2": "rgb(44, 90, 176)", - "border1": "rgba(36, 41, 46, 0.12)", - "border2": "rgba(36, 41, 46, 0.30)", - "border3": "rgba(36, 41, 46, 0.40)", - "formLabel": "rgba(36, 41, 46, 1)", - "formDescription": "rgba(36, 41, 46, 0.75)", - "formInputBg": "#FFFFFF", - "formInputBgDisabled": "rgba(36, 41, 46, 0.04)", - "formInputBorder": "rgba(36, 41, 46, 0.30)", - "formInputBorderHover": "rgba(36, 41, 46, 0.40)", - "formInputBorderActive": "#1F62E0", - "formInputBorderInvalid": "#CF0E5B", - "formInputPlaceholderText": "rgba(36, 41, 46, 0.50)", - "formInputText": "rgba(36, 41, 46, 1)", - "formInputDisabledText": "rgba(36, 41, 46, 0.50)", - "formFocusOutline": "#3871DC", - "formValidationMessageText": "#FFFFFF", - "formValidationMessageBg": "#E0226E", - "textStrong": "#000000", - "textHeading": "rgba(36, 41, 46, 1)", - "text": "rgba(36, 41, 46, 1)", - "textSemiWeak": "rgba(36, 41, 46, 0.75)", - "textWeak": "rgba(36, 41, 46, 0.75)", - "textFaint": "rgba(36, 41, 46, 0.50)", - "textBlue": "#1F62E0", - "bodyBg": "#F4F5F5", - "panelBg": "#FFFFFF", - "panelBorder": "rgba(36, 41, 46, 0.12)", - "pageHeaderBg": "#F4F5F5", - "pageHeaderBorder": "#F4F5F5", - "dropdownBg": "#FFFFFF", - "dropdownShadow": "#000000", - "dropdownOptionHoverBg": "#F4F5F5", - "link": "rgba(36, 41, 46, 1)", - "linkDisabled": "rgba(36, 41, 46, 0.50)", - "linkHover": "#000000", - "linkExternal": "#1F62E0" - }, - "shadows": { - "listItem": "none" - }, - "visualization": { - "hues": [ - { - "name": "red", - "shades": [ - { - "color": "#FF7383", - "name": "super-light-red" - }, - { - "color": "#F2495C", - "name": "light-red" - }, - { - "color": "#E02F44", - "name": "red", - "primary": true - }, - { - "color": "#C4162A", - "name": "semi-dark-red" - }, - { - "color": "#AD0317", - "name": "dark-red" - } - ] - }, - { - "name": "orange", - "shades": [ - { - "color": "#FFB357", - "name": "super-light-orange", - "aliases": [] - }, - { - "color": "#FF9830", - "name": "light-orange", - "aliases": [] - }, - { - "color": "#FF780A", - "name": "orange", - "aliases": [], - "primary": true - }, - { - "color": "#FA6400", - "name": "semi-dark-orange", - "aliases": [] - }, - { - "color": "#E55400", - "name": "dark-orange", - "aliases": [] - } - ] - }, - { - "name": "yellow", - "shades": [ - { - "color": "#FFEE52", - "name": "super-light-yellow", - "aliases": [] - }, - { - "color": "#FADE2A", - "name": "light-yellow", - "aliases": [] - }, - { - "color": "#F2CC0C", - "name": "yellow", - "aliases": [], - "primary": true - }, - { - "color": "#E0B400", - "name": "semi-dark-yellow", - "aliases": [] - }, - { - "color": "#CC9D00", - "name": "dark-yellow", - "aliases": [] - } - ] - }, - { - "name": "green", - "shades": [ - { - "color": "#96D98D", - "name": "super-light-green", - "aliases": [] - }, - { - "color": "#73BF69", - "name": "light-green", - "aliases": [] - }, - { - "color": "#56A64B", - "name": "green", - "aliases": [], - "primary": true - }, - { - "color": "#37872D", - "name": "semi-dark-green", - "aliases": [] - }, - { - "color": "#19730E", - "name": "dark-green", - "aliases": [] - } - ] - }, - { - "name": "blue", - "shades": [ - { - "color": "#8AB8FF", - "name": "super-light-blue", - "aliases": [] - }, - { - "color": "#5794F2", - "name": "light-blue", - "aliases": [] - }, - { - "color": "#3274D9", - "name": "blue", - "aliases": [], - "primary": true - }, - { - "color": "#1F60C4", - "name": "semi-dark-blue", - "aliases": [] - }, - { - "color": "#1250B0", - "name": "dark-blue", - "aliases": [] - } - ] - }, - { - "name": "purple", - "shades": [ - { - "color": "#CA95E5", - "name": "super-light-purple", - "aliases": [] - }, - { - "color": "#B877D9", - "name": "light-purple", - "aliases": [] - }, - { - "color": "#A352CC", - "name": "purple", - "aliases": [], - "primary": true - }, - { - "color": "#8F3BB8", - "name": "semi-dark-purple", - "aliases": [] - }, - { - "color": "#7C2EA3", - "name": "dark-purple", - "aliases": [] - } - ] - } - ], - "palette": [ - "green", - "semi-dark-yellow", - "light-blue", - "semi-dark-orange", - "red", - "blue", - "purple", - "#705DA0", - "dark-green", - "yellow", - "#447EBC", - "#C15C17", - "#890F02", - "#0A437C", - "#6D1F62", - "#584477", - "#B7DBAB", - "#F4D598", - "#70DBED", - "#F9BA8F", - "#F29191", - "#82B5D8", - "#E5A8E2", - "#AEA2E0", - "#629E51", - "#E5AC0E", - "#64B0C8", - "#E0752D", - "#BF1B00", - "#0A50A1", - "#962D82", - "#614D93", - "#9AC48A", - "#F2C96D", - "#65C5DB", - "#F9934E", - "#EA6460", - "#5195CE", - "#D683CE", - "#806EB7", - "#3F6833", - "#967302", - "#2F575E", - "#99440A", - "#58140C", - "#052B51", - "#511749", - "#3F2B5B", - "#E0F9D7", - "#FCEACA", - "#CFFAFF", - "#F9E2D2", - "#FCE2DE", - "#BADFF4", - "#F9D9F9", - "#DEDAF7" - ] - } - } -} \ No newline at end of file diff --git a/scripts/cli/generateSassVariableFiles.ts b/scripts/cli/generateSassVariableFiles.ts index 8a08a69aaf4..4e7cb99d5b7 100644 --- a/scripts/cli/generateSassVariableFiles.ts +++ b/scripts/cli/generateSassVariableFiles.ts @@ -9,9 +9,6 @@ const darkThemeVariablesPath = __dirname + '/../../public/sass/_variables.dark.g const lightThemeVariablesPath = __dirname + '/../../public/sass/_variables.light.generated.scss'; const defaultThemeVariablesPath = __dirname + '/../../public/sass/_variables.generated.scss'; -const darkThemeJsonPath = __dirname + '/../../public/sass/theme.dark.generated.json'; -const lightThemeJsonPath = __dirname + '/../../public/sass/theme.light.generated.json'; - const writeVariablesFile = async (path: string, data: string) => { return new Promise((resolve, reject) => { fs.writeFile(path, data, (e) => { @@ -39,16 +36,6 @@ const generateSassVariableFiles = async () => { console.error('\nWriting SASS variable files failed', error); process.exit(1); } - - try { - const darkJson = JSON.stringify(darkTheme, null, 2); - const lightJson = JSON.stringify(lightTheme, null, 2); - - writeVariablesFile(darkThemeJsonPath, darkJson); - writeVariablesFile(lightThemeJsonPath, lightJson); - } catch (error) { - console.error('\nWriting JSON variable files failed', error); - } }; generateSassVariableFiles(); From 85ecf997be4ad723413248ccf03d70c3840649a2 Mon Sep 17 00:00:00 2001 From: Laura <48948963+L-M-K-B@users.noreply.github.com> Date: Fri, 10 Jun 2022 16:01:28 +0200 Subject: [PATCH 25/79] Explore: Convert SASS styles of explore-active-button to emotion (#50061) * Chore: replace sass styling by emotion incl. new theme * Chore: remove sass styling * Chore: change colour * Chore: change colour * Chore: clean up Co-authored-by: Giordano Ricci Co-authored-by: Giordano Ricci --- public/app/features/explore/SecondaryActions.tsx | 9 +++++++-- public/sass/pages/_explore.scss | 6 ------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/public/app/features/explore/SecondaryActions.tsx b/public/app/features/explore/SecondaryActions.tsx index 97cf5f21bd4..84f610c2072 100644 --- a/public/app/features/explore/SecondaryActions.tsx +++ b/public/app/features/explore/SecondaryActions.tsx @@ -21,6 +21,11 @@ const getStyles = (theme: GrafanaTheme2) => { containerMargin: css` margin-top: ${theme.spacing(2)}; `, + exploreActiveButton: css` + box-shadow: ${theme.shadows.z1}; + border: 1px solid ${theme.colors.warning.border}; + color: ${theme.colors.warning.text}; + `, }; }; export function SecondaryActions(props: Props) { @@ -44,7 +49,7 @@ export function SecondaryActions(props: Props) {