Merge branch 'main' into serge/prevent-panel-hovel-header-crop-with-scenes
This commit is contained in:
@@ -21,10 +21,12 @@ If you install via RPM or the `.tar.gz` file, then you must manually update Graf
|
||||
|
||||
If you install from the RPM repository, then Grafana is automatically updated every time you update your applications.
|
||||
|
||||
| Grafana Version | Package | Repository |
|
||||
| ------------------ | ------------------ | ------------------------- |
|
||||
| Grafana Enterprise | grafana-enterprise | `https://rpm.grafana.com` |
|
||||
| Grafana OSS | grafana | `https://rpm.grafana.com` |
|
||||
| Grafana Version | Package | Repository |
|
||||
| ------------------------- | ------------------ | ------------------------------ |
|
||||
| Grafana Enterprise | grafana-enterprise | `https://rpm.grafana.com` |
|
||||
| Grafana Enterprise (Beta) | grafana-enterprise | `https://rpm-beta.grafana.com` |
|
||||
| Grafana OSS | grafana | `https://rpm.grafana.com` |
|
||||
| Grafana OSS (Beta) | grafana | `https://rpm-beta.grafana.com` |
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Grafana Enterprise is the recommended and default edition. It is available for free and includes all the features of the OSS edition. You can also upgrade to the [full Enterprise feature set](/products/enterprise/?utm_source=grafana-install-page), which has support for [Enterprise plugins](/grafana/plugins/?enterprise=1&utcm_source=grafana-install-page).
|
||||
@@ -32,6 +34,10 @@ Grafana Enterprise is the recommended and default edition. It is available for f
|
||||
|
||||
To install Grafana from the RPM repository, complete the following steps:
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
If you wish to install beta versions of Grafana, substitute the repository URL for the beta URL listed above.
|
||||
{{% /admonition %}}
|
||||
|
||||
1. Import the GPG key:
|
||||
|
||||
```bash
|
||||
@@ -53,12 +59,6 @@ To install Grafana from the RPM repository, complete the following steps:
|
||||
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
|
||||
```
|
||||
|
||||
1. To prevent beta versions from being installed, add the following exclude line to your `.repo` file.
|
||||
|
||||
```bash
|
||||
exclude=*beta*
|
||||
```
|
||||
|
||||
1. To install Grafana OSS, run the following command:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -113,7 +113,7 @@ We moved the time picker into the dashboard canvas and now, together with templa
|
||||
|
||||
If you want to learn more, in detail, about all the improvements we've made, don't miss our blog post.
|
||||
|
||||
<!-- {{< youtube id="ECahW01W5gs" >}} commented out until team decides if there will be changes to the video -->
|
||||
{{< youtube id="kcKwBhvrsHc" >}}
|
||||
|
||||
### Scenes for viewers
|
||||
|
||||
|
||||
@@ -51,9 +51,9 @@ require (
|
||||
github.com/grafana/cuetsy v0.1.11 // @grafana/grafana-as-code
|
||||
github.com/grafana/grafana-aws-sdk v0.25.0 // @grafana/aws-datasources
|
||||
github.com/grafana/grafana-azure-sdk-go/v2 v2.0.1 // @grafana/partner-datasources
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.220.0 // @grafana/plugins-platform-backend
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.221.0 // @grafana/plugins-platform-backend
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // @grafana/backend-platform
|
||||
github.com/hashicorp/go-hclog v1.6.2 // @grafana/plugins-platform-backend
|
||||
github.com/hashicorp/go-hclog v1.6.3 // @grafana/plugins-platform-backend
|
||||
github.com/hashicorp/go-plugin v1.6.0 // @grafana/plugins-platform-backend
|
||||
github.com/hashicorp/go-version v1.6.0 // @grafana/backend-platform
|
||||
github.com/hashicorp/hcl/v2 v2.17.0 // @grafana/alerting-squad-backend
|
||||
@@ -161,7 +161,7 @@ require (
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-msgpack v0.5.5 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect; @grafana/alerting-squad
|
||||
github.com/hashicorp/go-multierror v1.1.1 // @grafana/alerting-squad
|
||||
github.com/hashicorp/go-sockaddr v1.0.6 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/igm/sockjs-go/v3 v3.0.2 // indirect
|
||||
@@ -316,7 +316,7 @@ require (
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/grafana/regexp v0.0.0-20221123153739-15dc172cd2db // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect; @grafana/alerting-squad-backend
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // @grafana/alerting-squad-backend
|
||||
github.com/hashicorp/memberlist v0.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/invopop/yaml v0.2.0 // indirect
|
||||
@@ -407,7 +407,7 @@ require (
|
||||
github.com/docker/docker v24.0.7+incompatible // @grafana/grafana-release-guild
|
||||
github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027 // indirect
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // @grafana/grafana-app-platform-squad
|
||||
github.com/go-logr/logr v1.4.1 // indirect; @grafana/grafana-app-platform-squad
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
|
||||
@@ -2186,8 +2186,8 @@ github.com/grafana/grafana-google-sdk-go v0.1.0/go.mod h1:Vo2TKWfDVmNTELBUM+3lkr
|
||||
github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 h1:r+mU5bGMzcXCRVAuOrTn54S80qbfVkvTdUJZfSfTNbs=
|
||||
github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79/go.mod h1:wc6Hbh3K2TgCUSfBC/BOzabItujtHMESZeFk5ZhdxhQ=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.114.0/go.mod h1:D7x3ah+1d4phNXpbnOaxa/osSaZlwh9/ZUnGGzegRbk=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.220.0 h1:0UgyATMH9em+qk1YVqplzZpDSMFcRjx6YBjnl2cIe+E=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.220.0/go.mod h1:epJ5s1UeH/mzBpRrkiO8iz9xc6uuQ8f4zwdmCA4kYmo=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.221.0 h1:gHrZZNyBW7jcLFKd3cCiIcjsp/z77iUZijNct5fMa+g=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.221.0/go.mod h1:0lYONo3rJrjIoJNOQP0h2NXmOaF5rFIuH/rwSVSeIrQ=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240226124929-648abdbd0ea4 h1:hpyusz8c3yRFoJPlA0o34rWnsLbaOOBZleqRhFBi5Lg=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240226124929-648abdbd0ea4/go.mod h1:vrRQJuNprTWqwm6JPxHf3BoTJhvO15QMEjQ7Q/YUOnI=
|
||||
github.com/grafana/grafana/pkg/apiserver v0.0.0-20240226124929-648abdbd0ea4 h1:tIbI5zgos92vwJ8lV3zwHwuxkV03GR3FGLkFW9V5LxY=
|
||||
|
||||
@@ -289,6 +289,9 @@ export const Pages = {
|
||||
table: 'Explore Table',
|
||||
scrollView: 'data-testid explorer scroll view',
|
||||
},
|
||||
QueryHistory: {
|
||||
container: 'data-testid QueryHistory',
|
||||
},
|
||||
},
|
||||
SoloPanel: {
|
||||
url: (page: string) => `/d-solo/${page}`,
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { CSSTransition } from 'react-transition-group';
|
||||
|
||||
import { ReactUtils } from '../../utils';
|
||||
import { ReactUtils, handleReducedMotion } from '../../utils';
|
||||
import { getPlacement } from '../../utils/tooltipUtils';
|
||||
import { Portal } from '../Portal/Portal';
|
||||
import { TooltipPlacement } from '../Tooltip/types';
|
||||
@@ -114,13 +114,17 @@ const getStyles = (duration: number) => {
|
||||
appear: css({
|
||||
opacity: '0',
|
||||
position: 'relative',
|
||||
transform: 'scaleY(0.5)',
|
||||
transformOrigin: 'top',
|
||||
...handleReducedMotion({
|
||||
transform: 'scaleY(0.5)',
|
||||
}),
|
||||
}),
|
||||
appearActive: css({
|
||||
opacity: '1',
|
||||
transform: 'scaleY(1)',
|
||||
transition: `transform ${duration}ms cubic-bezier(0.2, 0, 0.2, 1), opacity ${duration}ms cubic-bezier(0.2, 0, 0.2, 1)`,
|
||||
...handleReducedMotion({
|
||||
transform: 'scaleY(1)',
|
||||
transition: `transform ${duration}ms cubic-bezier(0.2, 0, 0.2, 1), opacity ${duration}ms cubic-bezier(0.2, 0, 0.2, 1)`,
|
||||
}),
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useStyles2 } from '../../themes';
|
||||
import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins';
|
||||
import { ComponentSize } from '../../types';
|
||||
import { IconName, IconSize, IconType } from '../../types/icon';
|
||||
import { handleReducedMotion } from '../../utils/handleReducedMotion';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { getSvgSize } from '../Icon/utils';
|
||||
import { TooltipPlacement, PopoverContent, Tooltip } from '../Tooltip';
|
||||
@@ -143,9 +144,11 @@ const getStyles = (theme: GrafanaTheme2, size: IconSize, variant: IconButtonVari
|
||||
height: `${hoverSize}px`,
|
||||
borderRadius: theme.shape.radius.default,
|
||||
content: '""',
|
||||
transitionDuration: '0.2s',
|
||||
transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
transitionProperty: 'opacity',
|
||||
...handleReducedMotion({
|
||||
transitionDuration: '0.2s',
|
||||
transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
transitionProperty: 'opacity',
|
||||
}),
|
||||
},
|
||||
|
||||
'&:focus, &:focus-visible': getFocusStyles(theme),
|
||||
|
||||
@@ -21,9 +21,10 @@ export interface TabbedContainerProps {
|
||||
defaultTab?: string;
|
||||
closeIconTooltip?: string;
|
||||
onClose: () => void;
|
||||
testId?: string;
|
||||
}
|
||||
|
||||
export function TabbedContainer({ tabs, defaultTab, closeIconTooltip, onClose }: TabbedContainerProps) {
|
||||
export function TabbedContainer({ tabs, defaultTab, closeIconTooltip, onClose, testId }: TabbedContainerProps) {
|
||||
const [activeTab, setActiveTab] = useState(tabs.some((tab) => tab.value === defaultTab) ? defaultTab : tabs[0].value);
|
||||
const styles = useStyles2(getStyles);
|
||||
const theme = useTheme2();
|
||||
@@ -35,7 +36,7 @@ export function TabbedContainer({ tabs, defaultTab, closeIconTooltip, onClose }:
|
||||
const autoHeight = `calc(100% - (${theme.components.menuTabs.height}px + ${theme.spacing(1)}))`;
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.container} data-testid={testId}>
|
||||
<TabsBar className={styles.tabs}>
|
||||
{tabs.map((t) => (
|
||||
<Tab
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { CSSInterpolation } from '@emotion/css';
|
||||
|
||||
/**
|
||||
* @param styles - Styles to apply when no `prefers-reduced-motion` preference is set.
|
||||
* @param reducedMotionStyles - Styles to apply when `prefers-reduced-motion` is enabled.
|
||||
* Applies one of `styles` or `reducedMotionStyles` depending on a users `prefers-reduced-motion` setting. Omitting `reducedMotionStyles` entirely will result in no styles being applied when `prefers-reduced-motion` is enabled. In most cases this is a reasonable default.
|
||||
*/
|
||||
export const handleReducedMotion = (styles: CSSInterpolation, reducedMotionStyles?: CSSInterpolation) => {
|
||||
const result: Record<string, CSSInterpolation> = {
|
||||
'@media (prefers-reduced-motion: no-preference)': styles,
|
||||
};
|
||||
if (reducedMotionStyles) {
|
||||
result['@media (prefers-reduced-motion: reduce)'] = reducedMotionStyles;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -19,5 +19,6 @@ export { createLogger } from './logger';
|
||||
export { attachDebugger } from './debug';
|
||||
export * from './nodeGraph';
|
||||
export { fuzzyMatch } from './fuzzy';
|
||||
export { handleReducedMotion } from './handleReducedMotion';
|
||||
|
||||
export { ReactUtils };
|
||||
|
||||
@@ -1035,7 +1035,7 @@ func (m *mockLibraryPanelService) ConnectLibraryPanelsForDashboard(c context.Con
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockLibraryPanelService) ImportLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
|
||||
func (m *mockLibraryPanelService) ImportLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64, folderUID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
|
||||
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
|
||||
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
|
||||
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
|
||||
github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE=
|
||||
github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
@@ -39,12 +45,15 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -57,6 +66,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -78,6 +88,7 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -91,6 +102,7 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
|
||||
k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
|
||||
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
|
||||
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20240220201932-37d671a357a5 h1:QSpdNrZ9uRlV0VkqLvVO0Rqg8ioKi3oSw7O5P7pJV8M=
|
||||
k8s.io/kube-openapi v0.0.0-20240220201932-37d671a357a5/go.mod h1:Pa1PvrP7ACSkuX6I7KYomY6cmMA0Tx86waBhDUgoKPw=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
|
||||
|
||||
@@ -5,8 +5,10 @@ go 1.21.0
|
||||
require (
|
||||
github.com/bwmarrin/snowflake v0.3.0
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.220.0
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.221.0
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240409140820-518d3341d58f
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/zeebo/assert v1.3.0
|
||||
golang.org/x/mod v0.14.0
|
||||
k8s.io/apimachinery v0.29.2
|
||||
k8s.io/apiserver v0.29.2
|
||||
@@ -60,7 +62,7 @@ require (
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
|
||||
+231
-3
@@ -1,69 +1,121 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18=
|
||||
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
|
||||
github.com/apache/arrow/go/v15 v15.0.2 h1:60IliRbiyTWCWjERBCkO1W4Qun9svcYoZrSLcyOsMLE=
|
||||
github.com/apache/arrow/go/v15 v15.0.2/go.mod h1:DGXsR3ajT524njufqf95822i+KTh+yea1jass9YXgjA=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
|
||||
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
|
||||
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
|
||||
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89 h1:aPflPkRFkVwbW6dmcVqfgwp1i+UWGFH6VgR1Jim5Ygc=
|
||||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
||||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027 h1:1L0aalTpPz7YlMxETKpmQoWMBkeiuorElZIXoNmgiPE=
|
||||
github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20220115173737-adb46da277ac h1:9yrT5tmn9Zc0ytWPASlaPwQfQMQYnRf0RSDe1XvHw0Q=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20220115173737-adb46da277ac/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg=
|
||||
github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=
|
||||
github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4=
|
||||
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
|
||||
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
|
||||
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
|
||||
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
|
||||
github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE=
|
||||
github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/cel-go v0.17.7 h1:6ebJFzu1xO2n7TLtN+UBqShGBhlD85bhvglh5DpcfqQ=
|
||||
github.com/google/cel-go v0.17.7/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
|
||||
github.com/google/flatbuffers v23.5.26+incompatible h1:M9dgRyhJemaM4Sw8+66GHBu8ioaQmyPLg1b8VwK5WJg=
|
||||
github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
@@ -72,35 +124,58 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08 h1:PxlBVtIFHR/mtWk2i0gTEdCz+jBnqiuHNSki0epDbVs=
|
||||
github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.220.0 h1:0UgyATMH9em+qk1YVqplzZpDSMFcRjx6YBjnl2cIe+E=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.221.0 h1:gHrZZNyBW7jcLFKd3cCiIcjsp/z77iUZijNct5fMa+g=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.221.0/go.mod h1:0lYONo3rJrjIoJNOQP0h2NXmOaF5rFIuH/rwSVSeIrQ=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240409140820-518d3341d58f h1:+CK3tH3XrAAqx5urmVqpgSxMrL2MlpTOnLVSU4w4IjY=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240409140820-518d3341d58f/go.mod h1:ZxIaCOlDmFupiL55aLU+Qp7O1dgwkDMBAQBK7wnEVBg=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 h1:uGoIog/wiQHI9GAxXO5TJbT0wWKH3O9HhOJW1F9c3fY=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340/go.mod h1:3bDW6wMZJB7tiONtC/1Xpicra6Wp5GgbTbQWCbI5fkc=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
|
||||
github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I=
|
||||
github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A=
|
||||
github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
|
||||
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
|
||||
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
|
||||
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
|
||||
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
|
||||
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
@@ -109,75 +184,134 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
|
||||
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattetti/filebuffer v1.0.1 h1:gG7pyfnSIZCxdoKq+cPa8T0hhYtD9NxCdI4D7PTjRLM=
|
||||
github.com/mattetti/filebuffer v1.0.1/go.mod h1:YdMURNDOttIiruleeVr6f56OrMc+MydEnTcXwtkxNVs=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
|
||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
||||
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
|
||||
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y=
|
||||
github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw=
|
||||
github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
|
||||
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
||||
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 h1:aVGB3YnaS/JNfOW3tiHIlmNmTDg618va+eT0mVomgyI=
|
||||
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8/go.mod h1:fVle4kNr08ydeohzYafr20oZzbAkhQT39gKK/pFQ5M4=
|
||||
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
|
||||
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||
github.com/unknwon/log v0.0.0-20150304194804-e617c87089d3 h1:4EYQaWAatQokdji3zqZloVIW/Ke1RQjYw2zHULyrHJg=
|
||||
github.com/unknwon/log v0.0.0-20150304194804-e617c87089d3/go.mod h1:1xEUf2abjfP92w2GZTV+GgaRxXErwRXcClbUwrNJffU=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
|
||||
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
|
||||
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k=
|
||||
@@ -195,80 +329,168 @@ go.etcd.io/etcd/raft/v3 v3.5.10/go.mod h1:odD6kr8XQXTy9oQnyMPBOr0TVe+gT0neQhElQ6
|
||||
go.etcd.io/etcd/server/v3 v3.5.10 h1:4NOGyOwD5sUZ22PiWYKmfxqoeh72z6EhYjNosKGLmZg=
|
||||
go.etcd.io/etcd/server/v3 v3.5.10/go.mod h1:gBplPHfs6YI0L+RpGkTQO7buDbHv5HJGG/Bst0/zIPo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.49.0 h1:RtcvQ4iw3w9NBB5yRwgA4sSa82rfId7n4atVpvKx3bY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.49.0/go.mod h1:f/PbKbRd4cdUICWell6DmzvVJ7QrmBgFrRHjXmAXbK4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.22.0 h1:bAHX+zN/inu+Rbqk51REmC8oXLl+Dw6pp9ldQf/onaY=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.22.0/go.mod h1:bH9GkgkN21mscXcQP6lQJYI8XnEPDxlTN/ZOBuHDjqE=
|
||||
go.opentelemetry.io/contrib/samplers/jaegerremote v0.18.0 h1:Q9PrD94WoMolBx44ef5UWWvufpVSME0MiSymXZfedso=
|
||||
go.opentelemetry.io/contrib/samplers/jaegerremote v0.18.0/go.mod h1:tjp49JHNvreAAoWjdCHIVD7NXMjuJ3Dp/9iNOuPPlC8=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
|
||||
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8=
|
||||
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
||||
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o=
|
||||
gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
||||
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 h1:XNNYLJHt73EyYiCZi6+xjupS9CpvmiDgjPTAjrBlQbo=
|
||||
gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
@@ -279,8 +501,12 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
|
||||
k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0=
|
||||
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
|
||||
@@ -292,9 +518,11 @@ k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA=
|
||||
k8s.io/component-base v0.29.2 h1:lpiLyuvPA9yV1aQwGLENYyK7n/8t6l3nn3zAtFTJYe8=
|
||||
k8s.io/component-base v0.29.2/go.mod h1:BfB3SLrefbZXiBfbM+2H1dlat21Uewg/5qtKOl8degM=
|
||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
|
||||
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20240220201932-37d671a357a5 h1:QSpdNrZ9uRlV0VkqLvVO0Rqg8ioKi3oSw7O5P7pJV8M=
|
||||
k8s.io/kube-openapi v0.0.0-20240220201932-37d671a357a5/go.mod h1:Pa1PvrP7ACSkuX6I7KYomY6cmMA0Tx86waBhDUgoKPw=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0 h1:TgtAeesdhpm2SGwkQasmbeqDo8th5wOBA5h/AjTKA4I=
|
||||
|
||||
@@ -32,3 +32,8 @@ func (d *DualWriterMode1) Create(ctx context.Context, obj runtime.Object, create
|
||||
|
||||
return legacy.Create(ctx, obj, createValidation, options)
|
||||
}
|
||||
|
||||
// Get overrides the default behavior of the DualWriter and reads only to LegacyStorage.
|
||||
func (d *DualWriterMode1) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
return d.Legacy.Get(ctx, name, options)
|
||||
}
|
||||
|
||||
@@ -80,3 +80,8 @@ func enrichObject(orig, copy runtime.Object) (runtime.Object, error) {
|
||||
|
||||
return copy, nil
|
||||
}
|
||||
|
||||
// Get overrides the default behavior of the Storage and retrieves an object from LegacyStorage
|
||||
func (d *DualWriterMode2) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
return d.Legacy.Get(ctx, name, &metav1.GetOptions{})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
@@ -38,3 +39,24 @@ func (d *DualWriterMode3) Create(ctx context.Context, obj runtime.Object, create
|
||||
}
|
||||
return created, nil
|
||||
}
|
||||
|
||||
// Get overrides the default behavior of the Storage and retrieves an object from Unified Storage
|
||||
// the object is still fetched from Legacy Storage if it's not found in Unified Storage
|
||||
func (d *DualWriterMode3) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
legacy, ok := d.Legacy.(rest.Getter)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("legacy storage rest.Getter is missing")
|
||||
}
|
||||
|
||||
s, err := d.Storage.Get(ctx, name, &metav1.GetOptions{})
|
||||
if err == nil {
|
||||
return s, err
|
||||
}
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
klog.Info("object not found in unified storage. Getting it from legacy", "name", name)
|
||||
|
||||
return legacy.Get(ctx, name, &metav1.GetOptions{})
|
||||
}
|
||||
|
||||
@@ -23,3 +23,8 @@ func NewDualWriterMode4(legacy LegacyStorage, storage Storage) *DualWriterMode4
|
||||
func (d *DualWriterMode4) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
|
||||
return d.Storage.Create(ctx, obj, createValidation, options)
|
||||
}
|
||||
|
||||
// Get overrides the default behavior of the Storage and retrieves an object from Unified Storage
|
||||
func (d *DualWriterMode4) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
return d.Storage.Get(ctx, name, &metav1.GetOptions{})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/zeebo/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const kind = "dummy"
|
||||
|
||||
func Test_Mode1(t *testing.T) {
|
||||
var ls = (LegacyStorage)(nil)
|
||||
var s = (Storage)(nil)
|
||||
lsSpy := NewLegacyStorageSpyClient(ls)
|
||||
sSpy := NewStorageSpyClient(s)
|
||||
|
||||
dw := NewDualWriterMode1(lsSpy, sSpy)
|
||||
|
||||
_, err := dw.Get(context.Background(), kind, &metav1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// it should use the Legacy Get implementation
|
||||
assert.Equal(t, 1, lsSpy.Counts("LegacyStorage.Get"))
|
||||
assert.Equal(t, 0, sSpy.Counts("Storage.Get"))
|
||||
}
|
||||
|
||||
func Test_Mode2(t *testing.T) {
|
||||
var ls = (LegacyStorage)(nil)
|
||||
var s = (Storage)(nil)
|
||||
lsSpy := NewLegacyStorageSpyClient(ls)
|
||||
sSpy := NewStorageSpyClient(s)
|
||||
|
||||
dw := NewDualWriterMode2(lsSpy, sSpy)
|
||||
|
||||
_, err := dw.Get(context.Background(), kind, &metav1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// it should use the Legacy Get implementation
|
||||
assert.Equal(t, 1, lsSpy.Counts("LegacyStorage.Get"))
|
||||
assert.Equal(t, 0, sSpy.Counts("Storage.Get"))
|
||||
}
|
||||
|
||||
func Mode3_Test(t *testing.T) {
|
||||
var ls = (LegacyStorage)(nil)
|
||||
var s = (Storage)(nil)
|
||||
lsSpy := NewLegacyStorageSpyClient(ls)
|
||||
sSpy := NewStorageSpyClient(s)
|
||||
|
||||
dw := NewDualWriterMode3(lsSpy, sSpy)
|
||||
|
||||
_, err := dw.Get(context.Background(), kind, &metav1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// it should use the Unified Storage Get implementation
|
||||
assert.Equal(t, 0, lsSpy.Counts("LegacyStorage.Get"))
|
||||
assert.Equal(t, 1, sSpy.Counts("Storage.Get"))
|
||||
}
|
||||
|
||||
func Test_Mode4(t *testing.T) {
|
||||
var ls = (LegacyStorage)(nil)
|
||||
var s = (Storage)(nil)
|
||||
lsSpy := NewLegacyStorageSpyClient(ls)
|
||||
sSpy := NewStorageSpyClient(s)
|
||||
|
||||
dw := NewDualWriterMode3(lsSpy, sSpy)
|
||||
|
||||
_, err := dw.Get(context.Background(), kind, &metav1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// it should use the Unified Storage Get implementation
|
||||
assert.Equal(t, 0, lsSpy.Counts("LegacyStorage.Get"))
|
||||
assert.Equal(t, 1, sSpy.Counts("Storage.Get"))
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
|
||||
@@ -19,6 +19,8 @@ import (
|
||||
_ "github.com/grafana/dskit/flagext"
|
||||
_ "github.com/grafana/gofpdf"
|
||||
_ "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
_ "github.com/hashicorp/go-multierror"
|
||||
_ "github.com/hashicorp/golang-lru/v2"
|
||||
_ "github.com/linkedin/goavro/v2"
|
||||
_ "github.com/m3db/prometheus_remote_client_golang/promremote"
|
||||
_ "github.com/robfig/cron/v3"
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package slogadapter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
)
|
||||
|
||||
var _ slog.Handler = &slogHandler{}
|
||||
|
||||
type slogHandler struct {
|
||||
log.Logger
|
||||
}
|
||||
|
||||
// NewSLogHandler returns a new slog.Handler that logs to the given log.Logger.
|
||||
func New(logger log.Logger) *slogHandler {
|
||||
return &slogHandler{logger}
|
||||
}
|
||||
|
||||
// Enabled implements slog.Handler.Enabled.
|
||||
func (h *slogHandler) Enabled(_ context.Context, _ slog.Level) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Handle implements slog.Handler.Handle.
|
||||
func (h *slogHandler) Handle(ctx context.Context, r slog.Record) error {
|
||||
attrs := make([]any, 0, 2*r.NumAttrs())
|
||||
fn := func(attr slog.Attr) bool {
|
||||
attrs = append(attrs, attr.Key, attr.Value)
|
||||
return true
|
||||
}
|
||||
r.Attrs(fn)
|
||||
|
||||
switch level := r.Level; {
|
||||
case level < slog.LevelInfo:
|
||||
h.Debug(r.Message, attrs...)
|
||||
case level < slog.LevelWarn:
|
||||
h.Info(r.Message, attrs...)
|
||||
case level < slog.LevelError:
|
||||
h.Warn(r.Message, attrs...)
|
||||
default:
|
||||
h.Error(r.Message, attrs...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithAttrs implements slog.Handler.WithAttrs.
|
||||
func (h *slogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
out := make([]any, 0, 2*len(attrs))
|
||||
for _, attr := range attrs {
|
||||
out = append(out, attr.Key, attr.Value)
|
||||
}
|
||||
return New(h.Logger.New(out...))
|
||||
}
|
||||
|
||||
// WithGroup implements slog.Handler.WithGroup.
|
||||
func (h *slogHandler) WithGroup(name string) slog.Handler {
|
||||
if name == "" {
|
||||
return h
|
||||
}
|
||||
return New(h.Logger.New("group", name))
|
||||
}
|
||||
+2
-2
@@ -3,7 +3,7 @@ module github.com/grafana/grafana/pkg/promlib
|
||||
go 1.21.0
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.220.0
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.221.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/prometheus/client_golang v1.18.0
|
||||
@@ -56,7 +56,7 @@ require (
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/invopop/jsonschema v0.12.0 // indirect
|
||||
|
||||
+302
-3
@@ -1,141 +1,440 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs=
|
||||
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||
github.com/apache/arrow/go/v15 v15.0.2 h1:60IliRbiyTWCWjERBCkO1W4Qun9svcYoZrSLcyOsMLE=
|
||||
github.com/apache/arrow/go/v15 v15.0.2/go.mod h1:DGXsR3ajT524njufqf95822i+KTh+yea1jass9YXgjA=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/aws/aws-sdk-go v1.50.8 h1:gY0WoOW+/Wz6XmYSgDH9ge3wnAevYDSQWPxxJvqAkP4=
|
||||
github.com/aws/aws-sdk-go v1.50.8/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
|
||||
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89 h1:aPflPkRFkVwbW6dmcVqfgwp1i+UWGFH6VgR1Jim5Ygc=
|
||||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE=
|
||||
github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
|
||||
github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027 h1:1L0aalTpPz7YlMxETKpmQoWMBkeiuorElZIXoNmgiPE=
|
||||
github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20220115173737-adb46da277ac h1:9yrT5tmn9Zc0ytWPASlaPwQfQMQYnRf0RSDe1XvHw0Q=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20220115173737-adb46da277ac/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg=
|
||||
github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
|
||||
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
|
||||
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
|
||||
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
|
||||
github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE=
|
||||
github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/flatbuffers v23.5.26+incompatible h1:M9dgRyhJemaM4Sw8+66GHBu8ioaQmyPLg1b8VwK5WJg=
|
||||
github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.220.0 h1:0UgyATMH9em+qk1YVqplzZpDSMFcRjx6YBjnl2cIe+E=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.221.0 h1:gHrZZNyBW7jcLFKd3cCiIcjsp/z77iUZijNct5fMa+g=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.221.0/go.mod h1:0lYONo3rJrjIoJNOQP0h2NXmOaF5rFIuH/rwSVSeIrQ=
|
||||
github.com/grafana/regexp v0.0.0-20221123153739-15dc172cd2db h1:7aN5cccjIqCLTzedH7MZzRZt5/lsAHch6Z3L2ZGn5FA=
|
||||
github.com/grafana/regexp v0.0.0-20221123153739-15dc172cd2db/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 h1:uGoIog/wiQHI9GAxXO5TJbT0wWKH3O9HhOJW1F9c3fY=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340/go.mod h1:3bDW6wMZJB7tiONtC/1Xpicra6Wp5GgbTbQWCbI5fkc=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
|
||||
github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I=
|
||||
github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A=
|
||||
github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI=
|
||||
github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
|
||||
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
|
||||
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
|
||||
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
|
||||
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattetti/filebuffer v1.0.1 h1:gG7pyfnSIZCxdoKq+cPa8T0hhYtD9NxCdI4D7PTjRLM=
|
||||
github.com/mattetti/filebuffer v1.0.1/go.mod h1:YdMURNDOttIiruleeVr6f56OrMc+MydEnTcXwtkxNVs=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
||||
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
|
||||
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y=
|
||||
github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ=
|
||||
github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4=
|
||||
github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/prometheus/prometheus v1.8.2-0.20221021121301-51a44e6657c3 h1:etRZv4bJf9YAuyPWbyFufjkijfeoPSmyA5xNcd4DoyI=
|
||||
github.com/prometheus/prometheus v1.8.2-0.20221021121301-51a44e6657c3/go.mod h1:plwr4+63Q1xL8oIdBDeU854um7Cct0Av8dhP44lutMw=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw=
|
||||
github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 h1:aVGB3YnaS/JNfOW3tiHIlmNmTDg618va+eT0mVomgyI=
|
||||
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8/go.mod h1:fVle4kNr08ydeohzYafr20oZzbAkhQT39gKK/pFQ5M4=
|
||||
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
|
||||
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||
github.com/unknwon/log v0.0.0-20150304194804-e617c87089d3 h1:4EYQaWAatQokdji3zqZloVIW/Ke1RQjYw2zHULyrHJg=
|
||||
github.com/unknwon/log v0.0.0-20150304194804-e617c87089d3/go.mod h1:1xEUf2abjfP92w2GZTV+GgaRxXErwRXcClbUwrNJffU=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
|
||||
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.49.0 h1:RtcvQ4iw3w9NBB5yRwgA4sSa82rfId7n4atVpvKx3bY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.49.0/go.mod h1:f/PbKbRd4cdUICWell6DmzvVJ7QrmBgFrRHjXmAXbK4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.22.0 h1:bAHX+zN/inu+Rbqk51REmC8oXLl+Dw6pp9ldQf/onaY=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.22.0/go.mod h1:bH9GkgkN21mscXcQP6lQJYI8XnEPDxlTN/ZOBuHDjqE=
|
||||
go.opentelemetry.io/contrib/samplers/jaegerremote v0.18.0 h1:Q9PrD94WoMolBx44ef5UWWvufpVSME0MiSymXZfedso=
|
||||
go.opentelemetry.io/contrib/samplers/jaegerremote v0.18.0/go.mod h1:tjp49JHNvreAAoWjdCHIVD7NXMjuJ3Dp/9iNOuPPlC8=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
|
||||
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8=
|
||||
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
||||
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o=
|
||||
gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
||||
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 h1:XNNYLJHt73EyYiCZi6+xjupS9CpvmiDgjPTAjrBlQbo=
|
||||
gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/kube-openapi v0.0.0-20240220201932-37d671a357a5 h1:QSpdNrZ9uRlV0VkqLvVO0Rqg8ioKi3oSw7O5P7pJV8M=
|
||||
k8s.io/kube-openapi v0.0.0-20240220201932-37d671a357a5/go.mod h1:Pa1PvrP7ACSkuX6I7KYomY6cmMA0Tx86waBhDUgoKPw=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/log/slogadapter"
|
||||
"github.com/spf13/pflag"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/component-base/logs"
|
||||
@@ -36,10 +38,12 @@ func (o *ExtraOptions) Validate() []error {
|
||||
}
|
||||
|
||||
func (o *ExtraOptions) ApplyTo(c *genericapiserver.RecommendedConfig) error {
|
||||
logger := logr.New(newLogAdapter(o.Verbosity))
|
||||
klog.SetLoggerWithOptions(logger, klog.ContextualLogger(true))
|
||||
handler := slogadapter.New(log.New("grafana-apiserver"))
|
||||
logger := slog.New(handler)
|
||||
|
||||
klog.SetSlogLogger(logger)
|
||||
if _, err := logs.GlogSetter(strconv.Itoa(o.Verbosity)); err != nil {
|
||||
logger.Error(err, "failed to set log level")
|
||||
logger.Error("failed to set log level", "error", err)
|
||||
}
|
||||
c.ExternalAddress = o.ExternalAddress
|
||||
return nil
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
)
|
||||
|
||||
var _ logr.LogSink = (*logAdapter)(nil)
|
||||
|
||||
type logAdapter struct {
|
||||
level int
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func newLogAdapter(level int) *logAdapter {
|
||||
return &logAdapter{log: log.New("grafana-apiserver"), level: level}
|
||||
}
|
||||
|
||||
func (l *logAdapter) WithName(name string) logr.LogSink {
|
||||
l.log = l.log.New("name", name)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *logAdapter) WithValues(keysAndValues ...any) logr.LogSink {
|
||||
l.log = l.log.New(keysAndValues...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *logAdapter) Init(_ logr.RuntimeInfo) {
|
||||
// we aren't using the logr library for logging, so this is a no-op
|
||||
}
|
||||
|
||||
func (l *logAdapter) Enabled(level int) bool {
|
||||
return level <= l.level
|
||||
}
|
||||
|
||||
func (l *logAdapter) Info(level int, msg string, keysAndValues ...any) {
|
||||
msg = strings.TrimSpace(msg)
|
||||
// kubernetes uses level 0 for critical messages, so map that to Info
|
||||
if level == 0 {
|
||||
l.log.Info(msg, keysAndValues...)
|
||||
return
|
||||
}
|
||||
// every other level is mapped to Debug
|
||||
l.log.Debug(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
func (l *logAdapter) Error(err error, msg string, keysAndValues ...any) {
|
||||
msg = strings.TrimSpace(msg)
|
||||
l.log.Error(msg, keysAndValues...)
|
||||
}
|
||||
@@ -141,7 +141,7 @@ func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashb
|
||||
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.DashboardImport).Inc()
|
||||
// nolint:staticcheck
|
||||
err = s.libraryPanelService.ImportLibraryPanelsForDashboard(ctx, req.User, libraryElements, generatedDash.Get("panels").MustArray(), req.FolderId)
|
||||
err = s.libraryPanelService.ImportLibraryPanelsForDashboard(ctx, req.User, libraryElements, generatedDash.Get("panels").MustArray(), req.FolderId, req.FolderUid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestImportDashboardService(t *testing.T) {
|
||||
importLibraryPanelsForDashboard := false
|
||||
connectLibraryPanelsForDashboardCalled := false
|
||||
libraryPanelService := &libraryPanelServiceMock{
|
||||
importLibraryPanelsForDashboardFunc: func(ctx context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
|
||||
importLibraryPanelsForDashboardFunc: func(ctx context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64, folderUID string) error {
|
||||
importLibraryPanelsForDashboard = true
|
||||
return nil
|
||||
},
|
||||
@@ -75,9 +75,8 @@ func TestImportDashboardService(t *testing.T) {
|
||||
Inputs: []dashboardimport.ImportDashboardInput{
|
||||
{Name: "*", Type: "datasource", Value: "prom"},
|
||||
},
|
||||
User: &user.SignedInUser{UserID: 2, OrgRole: org.RoleAdmin, OrgID: 3},
|
||||
// FolderId: 5,
|
||||
FolderUid: "123",
|
||||
User: &user.SignedInUser{UserID: 2, OrgRole: org.RoleAdmin, OrgID: 3},
|
||||
FolderUid: "folderUID",
|
||||
}
|
||||
resp, err := s.ImportDashboard(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
@@ -91,7 +90,7 @@ func TestImportDashboardService(t *testing.T) {
|
||||
require.Equal(t, int64(3), importDashboardArg.OrgID)
|
||||
require.Equal(t, int64(2), userID)
|
||||
require.Equal(t, "prometheus", importDashboardArg.Dashboard.PluginID)
|
||||
require.Equal(t, "123", importDashboardArg.Dashboard.FolderUID)
|
||||
require.Equal(t, "folderUID", importDashboardArg.Dashboard.FolderUID)
|
||||
|
||||
panel := importDashboardArg.Dashboard.Data.Get("panels").GetIndex(0)
|
||||
require.Equal(t, "prom", panel.Get("datasource").MustString())
|
||||
@@ -143,7 +142,7 @@ func TestImportDashboardService(t *testing.T) {
|
||||
{Name: "*", Type: "datasource", Value: "prom"},
|
||||
},
|
||||
User: &user.SignedInUser{UserID: 2, OrgRole: org.RoleAdmin, OrgID: 3},
|
||||
FolderUid: "123",
|
||||
FolderUid: "folderUID",
|
||||
}
|
||||
resp, err := s.ImportDashboard(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
@@ -157,7 +156,7 @@ func TestImportDashboardService(t *testing.T) {
|
||||
require.Equal(t, int64(3), importDashboardArg.OrgID)
|
||||
require.Equal(t, int64(2), userID)
|
||||
require.Equal(t, "", importDashboardArg.Dashboard.PluginID)
|
||||
require.Equal(t, "123", importDashboardArg.Dashboard.FolderUID)
|
||||
require.Equal(t, "folderUID", importDashboardArg.Dashboard.FolderUID)
|
||||
|
||||
panel := importDashboardArg.Dashboard.Data.Get("panels").GetIndex(0)
|
||||
require.Equal(t, "prom", panel.Get("datasource").MustString())
|
||||
@@ -211,7 +210,7 @@ func (s *dashboardServiceMock) ImportDashboard(ctx context.Context, dto *dashboa
|
||||
type libraryPanelServiceMock struct {
|
||||
librarypanels.Service
|
||||
connectLibraryPanelsForDashboardFunc func(c context.Context, signedInUser identity.Requester, dash *dashboards.Dashboard) error
|
||||
importLibraryPanelsForDashboardFunc func(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error
|
||||
importLibraryPanelsForDashboardFunc func(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64, folderUID string) error
|
||||
}
|
||||
|
||||
var _ librarypanels.Service = (*libraryPanelServiceMock)(nil)
|
||||
@@ -224,9 +223,9 @@ func (s *libraryPanelServiceMock) ConnectLibraryPanelsForDashboard(ctx context.C
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *libraryPanelServiceMock) ImportLibraryPanelsForDashboard(ctx context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
|
||||
func (s *libraryPanelServiceMock) ImportLibraryPanelsForDashboard(ctx context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64, folderUID string) error {
|
||||
if s.importLibraryPanelsForDashboardFunc != nil {
|
||||
return s.importLibraryPanelsForDashboardFunc(ctx, signedInUser, libraryPanels, panels, folderID)
|
||||
return s.importLibraryPanelsForDashboardFunc(ctx, signedInUser, libraryPanels, panels, folderID, folderUID)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -448,10 +448,12 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
||||
|
||||
// nolint:staticcheck
|
||||
libraryElementCmd.FolderID = parent.ID
|
||||
libraryElementCmd.FolderUID = &parent.UID
|
||||
_, err = lps.LibraryElementService.CreateElement(context.Background(), &signedInUser, libraryElementCmd)
|
||||
require.NoError(t, err)
|
||||
// nolint:staticcheck
|
||||
libraryElementCmd.FolderID = subfolder.ID
|
||||
libraryElementCmd.FolderUID = &subfolder.UID
|
||||
_, err = lps.LibraryElementService.CreateElement(context.Background(), &signedInUser, libraryElementCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -528,10 +530,12 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
||||
|
||||
// nolint:staticcheck
|
||||
libraryElementCmd.FolderID = parent.ID
|
||||
libraryElementCmd.FolderUID = &parent.UID
|
||||
_, err = lps.LibraryElementService.CreateElement(context.Background(), &signedInUser, libraryElementCmd)
|
||||
require.NoError(t, err)
|
||||
// nolint:staticcheck
|
||||
libraryElementCmd.FolderID = subfolder.ID
|
||||
libraryElementCmd.FolderUID = &subfolder.UID
|
||||
_, err = lps.LibraryElementService.CreateElement(context.Background(), &signedInUser, libraryElementCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -667,11 +671,13 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
||||
_ = createRule(t, alertStore, subfolder.UID, "sub alert")
|
||||
// nolint:staticcheck
|
||||
libraryElementCmd.FolderID = subfolder.ID
|
||||
libraryElementCmd.FolderUID = &subPanel.FolderUID
|
||||
subPanel, err = lps.LibraryElementService.CreateElement(context.Background(), &signedInUser, libraryElementCmd)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// nolint:staticcheck
|
||||
libraryElementCmd.FolderID = parent.ID
|
||||
libraryElementCmd.FolderUID = &parent.UID
|
||||
parentPanel, err := lps.LibraryElementService.CreateElement(context.Background(), &signedInUser, libraryElementCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -149,14 +149,20 @@ func (l *LibraryElementService) createLibraryElement(c context.Context, signedIn
|
||||
}
|
||||
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.LibraryElements).Inc()
|
||||
// folderUID *string will be changed to string
|
||||
var folderUID string
|
||||
if cmd.FolderUID != nil {
|
||||
folderUID = *cmd.FolderUID
|
||||
}
|
||||
element := model.LibraryElement{
|
||||
OrgID: signedInUser.GetOrgID(),
|
||||
FolderID: cmd.FolderID, // nolint:staticcheck
|
||||
UID: createUID,
|
||||
Name: cmd.Name,
|
||||
Model: updatedModel,
|
||||
Version: 1,
|
||||
Kind: cmd.Kind,
|
||||
OrgID: signedInUser.GetOrgID(),
|
||||
FolderID: cmd.FolderID, // nolint:staticcheck
|
||||
FolderUID: folderUID,
|
||||
UID: createUID,
|
||||
Name: cmd.Name,
|
||||
Model: updatedModel,
|
||||
Version: 1,
|
||||
Kind: cmd.Kind,
|
||||
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
|
||||
@@ -15,7 +15,7 @@ func TestCreateLibraryElement(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to create a library panel that already exists, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Text - Library Panel")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 400, resp.Status())
|
||||
@@ -28,6 +28,7 @@ func TestCreateLibraryElement(t *testing.T) {
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: sc.initialResult.Result.UID,
|
||||
Name: "Text - Library Panel",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -68,7 +69,7 @@ func TestCreateLibraryElement(t *testing.T) {
|
||||
testScenario(t, "When an admin tries to create a library panel that does not exists using an nonexistent UID, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Nonexistent UID")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Nonexistent UID")
|
||||
command.UID = util.GenerateShortUID()
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
@@ -78,6 +79,7 @@ func TestCreateLibraryElement(t *testing.T) {
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: command.UID,
|
||||
Name: "Nonexistent UID",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -118,7 +120,7 @@ func TestCreateLibraryElement(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to create a library panel that does not exists using an existent UID, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Existing UID")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Existing UID")
|
||||
command.UID = sc.initialResult.Result.UID
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
@@ -128,7 +130,7 @@ func TestCreateLibraryElement(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to create a library panel that does not exists using an invalid UID, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Invalid UID")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Invalid UID")
|
||||
command.UID = "Testing an invalid UID"
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
@@ -138,7 +140,7 @@ func TestCreateLibraryElement(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to create a library panel that does not exists using an UID that is too long, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Invalid UID")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Invalid UID")
|
||||
command.UID = "j6T00KRZzj6T00KRZzj6T00KRZzj6T00KRZzj6T00K"
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
@@ -147,7 +149,7 @@ func TestCreateLibraryElement(t *testing.T) {
|
||||
|
||||
testScenario(t, "When an admin tries to create a library panel where name and panel title differ, it should not update panel title",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := getCreatePanelCommand(1, "Library Panel Name")
|
||||
command := getCreatePanelCommand(1, sc.folder.UID, "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
@@ -156,6 +158,7 @@ func TestCreateLibraryElement(t *testing.T) {
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.UID,
|
||||
Name: "Library Panel Name",
|
||||
Kind: int64(model.PanelElement),
|
||||
|
||||
@@ -74,7 +74,7 @@ func TestDeleteLibraryElement(t *testing.T) {
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
// nolint:staticcheck
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID)
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID, sc.folder.UID)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all panel elements and both panels and variables exist, it should only return panels",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreateVariableCommand(sc.folder.ID, "query0")
|
||||
command := getCreateVariableCommand(sc.folder.ID, sc.folder.UID, "query0")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
@@ -64,6 +64,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "Text - Library Panel",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -106,7 +107,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all variable elements and both panels and variables exist, it should only return panels",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreateVariableCommand(sc.folder.ID, "query0")
|
||||
command := getCreateVariableCommand(sc.folder.ID, sc.folder.UID, "query0")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
@@ -131,6 +132,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "query0",
|
||||
Kind: int64(model.VariableElement),
|
||||
@@ -172,7 +174,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
@@ -193,6 +195,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "Text - Library Panel",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -228,6 +231,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[1].UID,
|
||||
Name: "Text - Library Panel2",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -270,7 +274,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and sort desc is set, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
@@ -294,6 +298,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "Text - Library Panel2",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -329,6 +334,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[1].UID,
|
||||
Name: "Text - Library Panel",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -371,7 +377,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and typeFilter is set to existing types, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreateCommandWithModel(sc.folder.ID, "Gauge - Library Panel", model.PanelElement, []byte(`
|
||||
command := getCreateCommandWithModel(sc.folder.ID, sc.folder.UID, "Gauge - Library Panel", model.PanelElement, []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": 1,
|
||||
@@ -385,7 +391,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
// nolint:staticcheck
|
||||
command = getCreateCommandWithModel(sc.folder.ID, "BarGauge - Library Panel", model.PanelElement, []byte(`
|
||||
command = getCreateCommandWithModel(sc.folder.ID, sc.folder.UID, "BarGauge - Library Panel", model.PanelElement, []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": 1,
|
||||
@@ -417,6 +423,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 3,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "BarGauge - Library Panel",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -452,6 +459,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[1].UID,
|
||||
Name: "Gauge - Library Panel",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -494,7 +502,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and typeFilter is set to a nonexistent type, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreateCommandWithModel(sc.folder.ID, "Gauge - Library Panel", model.PanelElement, []byte(`
|
||||
command := getCreateCommandWithModel(sc.folder.ID, sc.folder.UID, "Gauge - Library Panel", model.PanelElement, []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": 1,
|
||||
@@ -529,25 +537,24 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and folderFilter is set to existing folders, it should succeed and the result should be correct",
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and folderFilterUIDs is set to existing folders, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolder(t, sc, "NewFolder")
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(newFolder.ID, "Text - Library Panel2")
|
||||
command := getCreatePanelCommand(newFolder.ID, newFolder.UID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
// nolint:staticcheck
|
||||
folderFilter := strconv.FormatInt(newFolder.ID, 10)
|
||||
|
||||
folderFilterUID := newFolder.UID
|
||||
err := sc.reqContext.Req.ParseForm()
|
||||
require.NoError(t, err)
|
||||
sc.reqContext.Req.Form.Add("folderFilter", folderFilter)
|
||||
sc.reqContext.Req.Form.Add("folderFilterUIDs", folderFilterUID)
|
||||
resp = sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var result libraryElementsSearch
|
||||
err = json.Unmarshal(resp.Body(), &result)
|
||||
|
||||
require.NoError(t, err)
|
||||
var expected = libraryElementsSearch{
|
||||
Result: libraryElementsSearchResult{
|
||||
@@ -559,6 +566,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
FolderID: newFolder.ID, // nolint:staticcheck
|
||||
FolderUID: newFolder.UID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "Text - Library Panel2",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -602,15 +610,15 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolder(t, sc, "NewFolder")
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(newFolder.ID, "Text - Library Panel2")
|
||||
command := getCreatePanelCommand(newFolder.ID, sc.folder.UID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
folderFilter := "2020,2021"
|
||||
folderFilterUIDs := "2020,2021"
|
||||
|
||||
err := sc.reqContext.Req.ParseForm()
|
||||
require.NoError(t, err)
|
||||
sc.reqContext.Req.Form.Add("folderFilter", folderFilter)
|
||||
sc.reqContext.Req.Form.Add("folderFilterUIDs", folderFilterUIDs)
|
||||
resp = sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
@@ -633,7 +641,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and folderFilter is set to General folder, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
@@ -658,6 +666,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "Text - Library Panel",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -693,6 +702,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[1].UID,
|
||||
Name: "Text - Library Panel2",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -735,7 +745,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and excludeUID is set, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
@@ -759,6 +769,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "Text - Library Panel2",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -801,7 +812,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
@@ -825,6 +836,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "Text - Library Panel",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -867,7 +879,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 2, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
@@ -892,6 +904,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "Text - Library Panel2",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -934,7 +947,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and searchString exists in the description, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreateCommandWithModel(sc.folder.ID, "Text - Library Panel2", model.PanelElement, []byte(`
|
||||
command := getCreateCommandWithModel(sc.folder.ID, sc.folder.UID, "Text - Library Panel2", model.PanelElement, []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": 1,
|
||||
@@ -968,6 +981,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "Text - Library Panel",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -1010,7 +1024,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and searchString exists in both name and description, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreateCommandWithModel(sc.folder.ID, "Some Other", model.PanelElement, []byte(`
|
||||
command := getCreateCommandWithModel(sc.folder.ID, sc.folder.UID, "Some Other", model.PanelElement, []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": 1,
|
||||
@@ -1042,6 +1056,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "Some Other",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -1077,6 +1092,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[1].UID,
|
||||
Name: "Text - Library Panel",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -1119,7 +1135,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 1 and searchString is panel2, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
@@ -1145,6 +1161,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "Text - Library Panel2",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -1187,7 +1204,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 3 and searchString is panel, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
@@ -1219,7 +1236,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 3 and searchString does not exist, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
@@ -35,6 +35,7 @@ func TestGetLibraryElement(t *testing.T) {
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: res.Result.UID,
|
||||
Name: "Text - Library Panel",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -123,7 +124,7 @@ func TestGetLibraryElement(t *testing.T) {
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
// nolint:staticcheck
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID)
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID, sc.folder.UID)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -133,6 +134,7 @@ func TestGetLibraryElement(t *testing.T) {
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: sc.folder.UID,
|
||||
UID: res.Result.UID,
|
||||
Name: "Text - Library Panel",
|
||||
Kind: int64(model.PanelElement),
|
||||
|
||||
@@ -26,8 +26,9 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolder(t, sc, "NewFolder")
|
||||
cmd := model.PatchLibraryElementCommand{
|
||||
FolderID: newFolder.ID, // nolint:staticcheck
|
||||
Name: "Panel - New name",
|
||||
FolderID: newFolder.ID, // nolint:staticcheck
|
||||
FolderUID: &newFolder.UID,
|
||||
Name: "Panel - New name",
|
||||
Model: []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
@@ -50,6 +51,7 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: newFolder.ID, // nolint:staticcheck
|
||||
FolderUID: newFolder.UID,
|
||||
UID: sc.initialResult.Result.UID,
|
||||
Name: "Panel - New name",
|
||||
Kind: int64(model.PanelElement),
|
||||
@@ -91,9 +93,10 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolder(t, sc, "NewFolder")
|
||||
cmd := model.PatchLibraryElementCommand{
|
||||
FolderID: newFolder.ID, // nolint:staticcheck
|
||||
Kind: int64(model.PanelElement),
|
||||
Version: 1,
|
||||
FolderID: newFolder.ID, // nolint:staticcheck
|
||||
FolderUID: &newFolder.UID,
|
||||
Kind: int64(model.PanelElement),
|
||||
Version: 1,
|
||||
}
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
sc.reqContext.Req.Body = mockRequestBody(cmd)
|
||||
@@ -102,6 +105,7 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
// nolint:staticcheck
|
||||
sc.initialResult.Result.FolderID = newFolder.ID
|
||||
sc.initialResult.Result.FolderUID = newFolder.UID
|
||||
sc.initialResult.Result.Meta.CreatedBy.Name = userInDbName
|
||||
sc.initialResult.Result.Meta.CreatedBy.AvatarUrl = userInDbAvatar
|
||||
sc.initialResult.Result.Meta.Updated = result.Result.Meta.Updated
|
||||
@@ -176,10 +180,11 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to patch a library panel with an UID that is too long, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := model.PatchLibraryElementCommand{
|
||||
FolderID: -1, // nolint:staticcheck
|
||||
UID: "j6T00KRZzj6T00KRZzj6T00KRZzj6T00KRZzj6T00K",
|
||||
Kind: int64(model.PanelElement),
|
||||
Version: 1,
|
||||
FolderID: -1, // nolint:staticcheck
|
||||
FolderUID: &sc.folder.UID,
|
||||
UID: "j6T00KRZzj6T00KRZzj6T00KRZzj6T00KRZzj6T00K",
|
||||
Kind: int64(model.PanelElement),
|
||||
Version: 1,
|
||||
}
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
sc.ctx.Req.Body = mockRequestBody(cmd)
|
||||
@@ -190,16 +195,17 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to patch a library panel with an existing UID, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Existing UID")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Existing UID")
|
||||
command.UID = util.GenerateShortUID()
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
cmd := model.PatchLibraryElementCommand{
|
||||
FolderID: -1, // nolint:staticcheck
|
||||
UID: command.UID,
|
||||
Kind: int64(model.PanelElement),
|
||||
Version: 1,
|
||||
FolderID: -1, // nolint:staticcheck
|
||||
FolderUID: &sc.folder.UID,
|
||||
UID: command.UID,
|
||||
Kind: int64(model.PanelElement),
|
||||
Version: 1,
|
||||
}
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
sc.ctx.Req.Body = mockRequestBody(cmd)
|
||||
@@ -312,7 +318,7 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to patch a library panel with a name that already exists, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Another Panel")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Another Panel")
|
||||
sc.ctx.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
@@ -331,14 +337,15 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolder(t, sc, "NewFolder")
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(newFolder.ID, "Text - Library Panel")
|
||||
command := getCreatePanelCommand(newFolder.ID, newFolder.UID, "Text - Library Panel")
|
||||
sc.ctx.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
cmd := model.PatchLibraryElementCommand{
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
Version: 1,
|
||||
Kind: int64(model.PanelElement),
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: &sc.folder.UID,
|
||||
Version: 1,
|
||||
Kind: int64(model.PanelElement),
|
||||
}
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
|
||||
sc.ctx.Req.Body = mockRequestBody(cmd)
|
||||
@@ -349,23 +356,25 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to patch a library panel in another org, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := model.PatchLibraryElementCommand{
|
||||
FolderID: sc.folder.ID, // nolint:staticcheck
|
||||
Version: 1,
|
||||
Kind: int64(model.PanelElement),
|
||||
FolderID: sc.folder.ID, // nolint:staticcheck
|
||||
FolderUID: &sc.folder.UID,
|
||||
Version: 1,
|
||||
Kind: int64(model.PanelElement),
|
||||
}
|
||||
sc.reqContext.OrgID = 2
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
sc.ctx.Req.Body = mockRequestBody(cmd)
|
||||
resp := sc.service.patchHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
require.Equal(t, 400, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithPanel(t, "When an admin tries to patch a library panel with an old version number, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := model.PatchLibraryElementCommand{
|
||||
FolderID: sc.folder.ID, // nolint:staticcheck
|
||||
Version: 1,
|
||||
Kind: int64(model.PanelElement),
|
||||
FolderID: sc.folder.ID, // nolint:staticcheck
|
||||
FolderUID: &sc.folder.UID,
|
||||
Version: 1,
|
||||
Kind: int64(model.PanelElement),
|
||||
}
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
sc.ctx.Req.Body = mockRequestBody(cmd)
|
||||
@@ -379,9 +388,10 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to patch a library panel with an other kind, it should succeed but panel should not change",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := model.PatchLibraryElementCommand{
|
||||
FolderID: sc.folder.ID, // nolint:staticcheck
|
||||
Version: 1,
|
||||
Kind: int64(model.VariableElement),
|
||||
FolderID: sc.folder.ID, // nolint:staticcheck
|
||||
FolderUID: &sc.folder.UID,
|
||||
Version: 1,
|
||||
Kind: int64(model.VariableElement),
|
||||
}
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
sc.ctx.Req.Body = mockRequestBody(cmd)
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestLibraryElementPermissionsGeneralFolder(t *testing.T) {
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
command := getCreatePanelCommand(0, "Library Panel Name")
|
||||
command := getCreatePanelCommand(0, "", "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
@@ -40,7 +40,7 @@ func TestLibraryElementPermissionsGeneralFolder(t *testing.T) {
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolder(t, sc, "Folder")
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(folder.ID, "Library Panel Name")
|
||||
command := getCreatePanelCommand(folder.ID, folder.UID, "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -57,7 +57,7 @@ func TestLibraryElementPermissionsGeneralFolder(t *testing.T) {
|
||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it from the General folder, it should return correct status", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolder(t, sc, "Folder")
|
||||
command := getCreatePanelCommand(0, "Library Panel Name")
|
||||
command := getCreatePanelCommand(0, "", "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -73,7 +73,7 @@ func TestLibraryElementPermissionsGeneralFolder(t *testing.T) {
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to delete a library panel in the General folder, it should return correct status", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := getCreatePanelCommand(0, "Library Panel Name")
|
||||
cmd := getCreatePanelCommand(0, "", "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(cmd)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -86,7 +86,7 @@ func TestLibraryElementPermissionsGeneralFolder(t *testing.T) {
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to get a library panel from General folder, it should return correct response", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := getCreatePanelCommand(0, "Library Panel in General Folder")
|
||||
cmd := getCreatePanelCommand(0, "", "Library Panel in General Folder")
|
||||
sc.reqContext.Req.Body = mockRequestBody(cmd)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -96,6 +96,7 @@ func TestLibraryElementPermissionsGeneralFolder(t *testing.T) {
|
||||
result.Result.Meta.UpdatedBy.AvatarUrl = userInDbAvatar
|
||||
result.Result.Meta.FolderName = "General"
|
||||
result.Result.Meta.FolderUID = ""
|
||||
result.Result.FolderUID = "general"
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
|
||||
@@ -111,7 +112,7 @@ func TestLibraryElementPermissionsGeneralFolder(t *testing.T) {
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to get all library panels from General folder, it should return correct response", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := getCreatePanelCommand(0, "Library Panel in General Folder")
|
||||
cmd := getCreatePanelCommand(0, "", "Library Panel in General Folder")
|
||||
sc.reqContext.Req.Body = mockRequestBody(cmd)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -183,7 +184,7 @@ func TestLibraryElementCreatePermissions(t *testing.T) {
|
||||
}
|
||||
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(folder.ID, "Library Panel Name")
|
||||
command := getCreatePanelCommand(folder.ID, folder.UID, "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
@@ -236,7 +237,7 @@ func TestLibraryElementPatchPermissions(t *testing.T) {
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
fromFolder := createFolder(t, sc, "FromFolder")
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(fromFolder.ID, "Library Panel Name")
|
||||
command := getCreatePanelCommand(fromFolder.ID, fromFolder.UID, "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -248,7 +249,7 @@ func TestLibraryElementPatchPermissions(t *testing.T) {
|
||||
}
|
||||
|
||||
// nolint:staticcheck
|
||||
cmd := model.PatchLibraryElementCommand{FolderID: toFolder.ID, Version: 1, Kind: int64(model.PanelElement)}
|
||||
cmd := model.PatchLibraryElementCommand{FolderID: toFolder.ID, FolderUID: &toFolder.UID, Version: 1, Kind: int64(model.PanelElement)}
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
|
||||
sc.reqContext.Req.Body = mockRequestBody(cmd)
|
||||
resp = sc.service.patchHandler(sc.reqContext)
|
||||
@@ -298,7 +299,7 @@ func TestLibraryElementDeletePermissions(t *testing.T) {
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolder(t, sc, "Folder")
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(folder.ID, "Library Panel Name")
|
||||
command := getCreatePanelCommand(folder.ID, folder.UID, "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -317,27 +318,29 @@ func TestLibraryElementDeletePermissions(t *testing.T) {
|
||||
func TestLibraryElementsWithMissingFolders(t *testing.T) {
|
||||
testScenario(t, "When a user tries to create a library panel in a folder that doesn't exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := getCreatePanelCommand(-100, "Library Panel Name")
|
||||
command := getCreatePanelCommand(0, "badFolderUID", "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
fmt.Println(string(resp.Body()))
|
||||
require.Equal(t, 400, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, "When a user tries to patch a library panel by moving it to a folder that doesn't exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolder(t, sc, "Folder")
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(folder.ID, "Library Panel Name")
|
||||
command := getCreatePanelCommand(folder.ID, folder.UID, "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
|
||||
folderUID := "badFolderUID"
|
||||
// nolint:staticcheck
|
||||
cmd := model.PatchLibraryElementCommand{FolderID: -100, Version: 1, Kind: int64(model.PanelElement)}
|
||||
cmd := model.PatchLibraryElementCommand{FolderID: -100, FolderUID: &folderUID, Version: 1, Kind: int64(model.PanelElement)}
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
|
||||
sc.reqContext.Req.Body = mockRequestBody(cmd)
|
||||
resp = sc.service.patchHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
require.Equal(t, 400, resp.Status())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -367,7 +370,7 @@ func TestLibraryElementsGetPermissions(t *testing.T) {
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolder(t, sc, "Folder")
|
||||
// nolint:staticcheck
|
||||
cmd := getCreatePanelCommand(folder.ID, "Library Panel")
|
||||
cmd := getCreatePanelCommand(folder.ID, folder.UID, "Library Panel")
|
||||
sc.reqContext.Req.Body = mockRequestBody(cmd)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -418,7 +421,7 @@ func TestLibraryElementsGetAllPermissions(t *testing.T) {
|
||||
for i := 1; i <= 2; i++ {
|
||||
folder := createFolder(t, sc, fmt.Sprintf("Folder%d", i))
|
||||
// nolint:staticcheck
|
||||
cmd := getCreatePanelCommand(folder.ID, fmt.Sprintf("Library Panel %d", i))
|
||||
cmd := getCreatePanelCommand(folder.ID, folder.UID, fmt.Sprintf("Library Panel %d", i))
|
||||
sc.reqContext.Req.Body = mockRequestBody(cmd)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
|
||||
@@ -88,7 +88,7 @@ func TestDeleteLibraryPanelsInFolder(t *testing.T) {
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
// nolint:staticcheck
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID)
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID, sc.folder.UID)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -105,7 +105,7 @@ func TestDeleteLibraryPanelsInFolder(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to delete a folder that contains disconnected elements, it should delete all disconnected elements too",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreateVariableCommand(sc.folder.ID, "query0")
|
||||
command := getCreateVariableCommand(sc.folder.ID, sc.folder.UID, "query0")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
@@ -163,7 +163,7 @@ func TestGetLibraryPanelConnections(t *testing.T) {
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
// nolint:staticcheck
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID)
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID, sc.folder.UID)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -202,6 +202,7 @@ type libraryElement struct {
|
||||
OrgID int64 `json:"orgId"`
|
||||
// Deprecated: use FolderUID instead
|
||||
FolderID int64 `json:"folderId"`
|
||||
FolderUID string `json:"folderUid"`
|
||||
UID string `json:"uid"`
|
||||
Name string `json:"name"`
|
||||
Kind int64 `json:"kind"`
|
||||
@@ -231,8 +232,8 @@ type libraryElementsSearchResult struct {
|
||||
PerPage int `json:"perPage"`
|
||||
}
|
||||
|
||||
func getCreatePanelCommand(folderID int64, name string) model.CreateLibraryElementCommand {
|
||||
command := getCreateCommandWithModel(folderID, name, model.PanelElement, []byte(`
|
||||
func getCreatePanelCommand(folderID int64, folderUID string, name string) model.CreateLibraryElementCommand {
|
||||
command := getCreateCommandWithModel(folderID, folderUID, name, model.PanelElement, []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": 1,
|
||||
@@ -245,8 +246,8 @@ func getCreatePanelCommand(folderID int64, name string) model.CreateLibraryEleme
|
||||
return command
|
||||
}
|
||||
|
||||
func getCreateVariableCommand(folderID int64, name string) model.CreateLibraryElementCommand {
|
||||
command := getCreateCommandWithModel(folderID, name, model.VariableElement, []byte(`
|
||||
func getCreateVariableCommand(folderID int64, folderUID, name string) model.CreateLibraryElementCommand {
|
||||
command := getCreateCommandWithModel(folderID, folderUID, name, model.VariableElement, []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"name": "query0",
|
||||
@@ -258,12 +259,12 @@ func getCreateVariableCommand(folderID int64, name string) model.CreateLibraryEl
|
||||
return command
|
||||
}
|
||||
|
||||
func getCreateCommandWithModel(folderID int64, name string, kind model.LibraryElementKind, byteModel []byte) model.CreateLibraryElementCommand {
|
||||
func getCreateCommandWithModel(folderID int64, folderUID, name string, kind model.LibraryElementKind, byteModel []byte) model.CreateLibraryElementCommand {
|
||||
command := model.CreateLibraryElementCommand{
|
||||
FolderID: folderID, // nolint:staticcheck
|
||||
Name: name,
|
||||
Model: byteModel,
|
||||
Kind: int64(kind),
|
||||
FolderUID: &folderUID,
|
||||
Name: name,
|
||||
Model: byteModel,
|
||||
Kind: int64(kind),
|
||||
}
|
||||
|
||||
return command
|
||||
@@ -280,9 +281,10 @@ type scenarioContext struct {
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func createDashboard(t *testing.T, sqlStore db.DB, user user.SignedInUser, dash *dashboards.Dashboard, folderID int64) *dashboards.Dashboard {
|
||||
func createDashboard(t *testing.T, sqlStore db.DB, user user.SignedInUser, dash *dashboards.Dashboard, folderID int64, folderUID string) *dashboards.Dashboard {
|
||||
// nolint:staticcheck
|
||||
dash.FolderID = folderID
|
||||
dash.FolderUID = folderUID
|
||||
dashItem := &dashboards.SaveDashboardDTO{
|
||||
Dashboard: dash,
|
||||
Message: "",
|
||||
@@ -396,7 +398,7 @@ func scenarioWithPanel(t *testing.T, desc string, fn func(t *testing.T, sc scena
|
||||
|
||||
testScenario(t, desc, func(t *testing.T, sc scenarioContext) {
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel")
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Text - Library Panel")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
sc.initialResult = validateAndUnMarshalResponse(t, resp)
|
||||
|
||||
@@ -20,6 +20,7 @@ type LibraryElement struct {
|
||||
OrgID int64 `xorm:"org_id"`
|
||||
// Deprecated: use FolderUID instead
|
||||
FolderID int64 `xorm:"folder_id"`
|
||||
FolderUID string `xorm:"folder_uid"`
|
||||
UID string `xorm:"uid"`
|
||||
Name string
|
||||
Kind int64
|
||||
@@ -41,6 +42,7 @@ type LibraryElementWithMeta struct {
|
||||
OrgID int64 `xorm:"org_id"`
|
||||
// Deprecated: use FolderUID instead
|
||||
FolderID int64 `xorm:"folder_id"`
|
||||
FolderUID string `xorm:"folder_uid"`
|
||||
UID string `xorm:"uid"`
|
||||
Name string
|
||||
Kind int64
|
||||
@@ -53,7 +55,6 @@ type LibraryElementWithMeta struct {
|
||||
Updated time.Time
|
||||
|
||||
FolderName string
|
||||
FolderUID string `xorm:"folder_uid"`
|
||||
ConnectedDashboards int64
|
||||
CreatedBy int64
|
||||
UpdatedBy int64
|
||||
@@ -217,13 +218,14 @@ type GetLibraryElementCommand struct {
|
||||
|
||||
// SearchLibraryElementsQuery is the query used for searching for Elements
|
||||
type SearchLibraryElementsQuery struct {
|
||||
PerPage int
|
||||
Page int
|
||||
SearchString string
|
||||
SortDirection string
|
||||
Kind int
|
||||
TypeFilter string
|
||||
ExcludeUID string
|
||||
PerPage int
|
||||
Page int
|
||||
SearchString string
|
||||
SortDirection string
|
||||
Kind int
|
||||
TypeFilter string
|
||||
ExcludeUID string
|
||||
// Deprecated: use FolderFilterUIDs instead
|
||||
FolderFilter string
|
||||
FolderFilterUIDs string
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ type FolderFilter struct {
|
||||
func parseFolderFilter(query model.SearchLibraryElementsQuery) FolderFilter {
|
||||
folderIDs := make([]string, 0)
|
||||
folderUIDs := make([]string, 0)
|
||||
// nolint:staticcheck
|
||||
hasFolderFilter := len(strings.TrimSpace(query.FolderFilter)) > 0
|
||||
hasFolderFilterUID := len(strings.TrimSpace(query.FolderFilterUIDs)) > 0
|
||||
|
||||
@@ -100,6 +101,7 @@ func parseFolderFilter(query model.SearchLibraryElementsQuery) FolderFilter {
|
||||
|
||||
if hasFolderFilter {
|
||||
result.includeGeneralFolder = false
|
||||
// nolint:staticcheck
|
||||
folderIDs = strings.Split(query.FolderFilter, ",")
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.LibraryElements).Inc()
|
||||
// nolint:staticcheck
|
||||
|
||||
@@ -42,7 +42,7 @@ func ProvideService(cfg *setting.Cfg, sqlStore db.DB, routeRegister routing.Rout
|
||||
// Service is a service for operating on library panels.
|
||||
type Service interface {
|
||||
ConnectLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, dash *dashboards.Dashboard) error
|
||||
ImportLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error
|
||||
ImportLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64, folderUID string) error
|
||||
}
|
||||
|
||||
type LibraryInfo struct {
|
||||
@@ -117,11 +117,11 @@ func connectLibraryPanelsRecursively(c context.Context, panels []any, libraryPan
|
||||
}
|
||||
|
||||
// ImportLibraryPanelsForDashboard loops through all panels in dashboard JSON and creates any missing library panels in the database.
|
||||
func (lps *LibraryPanelService) ImportLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
|
||||
return importLibraryPanelsRecursively(c, lps.LibraryElementService, signedInUser, libraryPanels, panels, folderID)
|
||||
func (lps *LibraryPanelService) ImportLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64, folderUID string) error {
|
||||
return importLibraryPanelsRecursively(c, lps.LibraryElementService, signedInUser, libraryPanels, panels, folderID, folderUID)
|
||||
}
|
||||
|
||||
func importLibraryPanelsRecursively(c context.Context, service libraryelements.Service, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
|
||||
func importLibraryPanelsRecursively(c context.Context, service libraryelements.Service, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64, folderUID string) error {
|
||||
for _, panel := range panels {
|
||||
panelAsJSON := simplejson.NewFromAny(panel)
|
||||
libraryPanel := panelAsJSON.Get("libraryPanel")
|
||||
@@ -132,7 +132,7 @@ func importLibraryPanelsRecursively(c context.Context, service libraryelements.S
|
||||
|
||||
// we have a row
|
||||
if panelType == "row" {
|
||||
err := importLibraryPanelsRecursively(c, service, signedInUser, libraryPanels, panelAsJSON.Get("panels").MustArray(), folderID)
|
||||
err := importLibraryPanelsRecursively(c, service, signedInUser, libraryPanels, panelAsJSON.Get("panels").MustArray(), folderID, folderUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -168,11 +168,12 @@ func importLibraryPanelsRecursively(c context.Context, service libraryelements.S
|
||||
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.LibraryPanels).Inc()
|
||||
var cmd = model.CreateLibraryElementCommand{
|
||||
FolderID: folderID, // nolint:staticcheck
|
||||
Name: name,
|
||||
Model: Model,
|
||||
Kind: int64(model.PanelElement),
|
||||
UID: UID,
|
||||
FolderID: folderID, // nolint:staticcheck
|
||||
FolderUID: &folderUID,
|
||||
Name: name,
|
||||
Model: Model,
|
||||
Kind: int64(model.PanelElement),
|
||||
UID: UID,
|
||||
}
|
||||
_, err = service.CreateElement(c, signedInUser, cmd)
|
||||
if err != nil {
|
||||
|
||||
@@ -85,8 +85,7 @@ func TestConnectLibraryPanelsForDashboard(t *testing.T) {
|
||||
Title: "Testing ConnectLibraryPanelsForDashboard",
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
// nolint:staticcheck
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID)
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash)
|
||||
|
||||
err := sc.service.ConnectLibraryPanelsForDashboard(sc.ctx, sc.user, dashInDB)
|
||||
require.NoError(t, err)
|
||||
@@ -100,8 +99,7 @@ func TestConnectLibraryPanelsForDashboard(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to store a dashboard with library panels inside and outside of rows, it should connect all",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := model.CreateLibraryElementCommand{
|
||||
FolderID: sc.initialResult.Result.FolderID, // nolint:staticcheck
|
||||
Name: "Outside row",
|
||||
Name: "Outside row",
|
||||
Model: []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
@@ -111,7 +109,8 @@ func TestConnectLibraryPanelsForDashboard(t *testing.T) {
|
||||
"description": "A description"
|
||||
}
|
||||
`),
|
||||
Kind: int64(model.PanelElement),
|
||||
Kind: int64(model.PanelElement),
|
||||
FolderUID: &sc.folder.UID,
|
||||
}
|
||||
outsidePanel, err := sc.elementService.CreateElement(sc.ctx, sc.user, cmd)
|
||||
require.NoError(t, err)
|
||||
@@ -184,8 +183,7 @@ func TestConnectLibraryPanelsForDashboard(t *testing.T) {
|
||||
Title: "Testing ConnectLibraryPanelsForDashboard",
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
// nolint:staticcheck
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID)
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash)
|
||||
|
||||
err = sc.service.ConnectLibraryPanelsForDashboard(sc.ctx, sc.user, dashInDB)
|
||||
require.NoError(t, err)
|
||||
@@ -231,8 +229,7 @@ func TestConnectLibraryPanelsForDashboard(t *testing.T) {
|
||||
Title: "Testing ConnectLibraryPanelsForDashboard",
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
// nolint:staticcheck
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID)
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash)
|
||||
|
||||
err := sc.service.ConnectLibraryPanelsForDashboard(sc.ctx, sc.user, dashInDB)
|
||||
require.EqualError(t, err, errLibraryPanelHeaderUIDMissing.Error())
|
||||
@@ -241,8 +238,7 @@ func TestConnectLibraryPanelsForDashboard(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to store a dashboard with unused/removed library panels, it should disconnect unused/removed library panels",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
unused, err := sc.elementService.CreateElement(sc.ctx, sc.user, model.CreateLibraryElementCommand{
|
||||
FolderID: sc.folder.ID, // nolint:staticcheck
|
||||
Name: "Unused Libray Panel",
|
||||
Name: "Unused Libray Panel",
|
||||
Model: []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
@@ -252,7 +248,8 @@ func TestConnectLibraryPanelsForDashboard(t *testing.T) {
|
||||
"description": "Unused description"
|
||||
}
|
||||
`),
|
||||
Kind: int64(model.PanelElement),
|
||||
Kind: int64(model.PanelElement),
|
||||
FolderUID: &sc.folder.UID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
dashJSON := map[string]any{
|
||||
@@ -288,8 +285,7 @@ func TestConnectLibraryPanelsForDashboard(t *testing.T) {
|
||||
Title: "Testing ConnectLibraryPanelsForDashboard",
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
// nolint:staticcheck
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID)
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash)
|
||||
err = sc.elementService.ConnectElementsToDashboard(sc.ctx, sc.user, []string{sc.initialResult.Result.UID}, dashInDB.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -398,7 +394,7 @@ func TestImportLibraryPanelsForDashboard(t *testing.T) {
|
||||
|
||||
require.EqualError(t, err, model.ErrLibraryElementNotFound.Error())
|
||||
|
||||
err = sc.service.ImportLibraryPanelsForDashboard(sc.ctx, sc.user, simplejson.NewFromAny(libraryElements), panels, 0)
|
||||
err = sc.service.ImportLibraryPanelsForDashboard(sc.ctx, sc.user, simplejson.NewFromAny(libraryElements), panels, 0, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
element, err := sc.elementService.GetElement(sc.ctx, sc.user,
|
||||
@@ -439,14 +435,14 @@ func TestImportLibraryPanelsForDashboard(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// nolint:staticcheck
|
||||
err = sc.service.ImportLibraryPanelsForDashboard(sc.ctx, sc.user, simplejson.New(), panels, sc.folder.ID)
|
||||
err = sc.service.ImportLibraryPanelsForDashboard(sc.ctx, sc.user, simplejson.New(), panels, sc.folder.ID, sc.folder.UID)
|
||||
require.NoError(t, err)
|
||||
|
||||
element, err := sc.elementService.GetElement(sc.ctx, sc.user,
|
||||
model.GetLibraryElementCommand{UID: existingUID, FolderName: dashboards.RootFolderName})
|
||||
require.NoError(t, err)
|
||||
var expected = getExpected(t, element, existingUID, existingName, sc.initialResult.Result.Model)
|
||||
expected.FolderID = sc.initialResult.Result.FolderID
|
||||
expected.FolderUID = sc.initialResult.Result.FolderUID
|
||||
expected.Description = sc.initialResult.Result.Description
|
||||
expected.Meta.FolderUID = sc.folder.UID
|
||||
expected.Meta.FolderName = sc.folder.Title
|
||||
@@ -555,7 +551,7 @@ func TestImportLibraryPanelsForDashboard(t *testing.T) {
|
||||
_, err = sc.elementService.GetElement(sc.ctx, sc.user, model.GetLibraryElementCommand{UID: insideUID, FolderName: dashboards.RootFolderName})
|
||||
require.EqualError(t, err, model.ErrLibraryElementNotFound.Error())
|
||||
|
||||
err = sc.service.ImportLibraryPanelsForDashboard(sc.ctx, sc.user, simplejson.NewFromAny(libraryElements), panels, 0)
|
||||
err = sc.service.ImportLibraryPanelsForDashboard(sc.ctx, sc.user, simplejson.NewFromAny(libraryElements), panels, 0, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
element, err := sc.elementService.GetElement(sc.ctx, sc.user, model.GetLibraryElementCommand{UID: outsideUID, FolderName: dashboards.RootFolderName})
|
||||
@@ -577,9 +573,11 @@ func TestImportLibraryPanelsForDashboard(t *testing.T) {
|
||||
}
|
||||
|
||||
type libraryPanel struct {
|
||||
ID int64
|
||||
OrgID int64
|
||||
ID int64
|
||||
OrgID int64
|
||||
// Deprecated: use FolderUID instead
|
||||
FolderID int64
|
||||
FolderUID string
|
||||
UID string
|
||||
Name string
|
||||
Type string
|
||||
@@ -615,6 +613,7 @@ type libraryElement struct {
|
||||
ID int64 `json:"id"`
|
||||
OrgID int64 `json:"orgId"`
|
||||
FolderID int64 `json:"folderId"`
|
||||
FolderUID string `json:"folderUid"`
|
||||
UID string `json:"uid"`
|
||||
Name string `json:"name"`
|
||||
Kind int64 `json:"kind"`
|
||||
@@ -648,7 +647,6 @@ func toLibraryElement(t *testing.T, res model.LibraryElementDTO) libraryElement
|
||||
return libraryElement{
|
||||
ID: res.ID,
|
||||
OrgID: res.OrgID,
|
||||
FolderID: res.FolderID, // nolint:staticcheck
|
||||
UID: res.UID,
|
||||
Name: res.Name,
|
||||
Type: res.Type,
|
||||
@@ -714,9 +712,7 @@ func getExpected(t *testing.T, res model.LibraryElementDTO, UID string, name str
|
||||
}
|
||||
}
|
||||
|
||||
func createDashboard(t *testing.T, sqlStore db.DB, user *user.SignedInUser, dash *dashboards.Dashboard, folderID int64) *dashboards.Dashboard {
|
||||
// nolint:staticcheck
|
||||
dash.FolderID = folderID
|
||||
func createDashboard(t *testing.T, sqlStore db.DB, user *user.SignedInUser, dash *dashboards.Dashboard) *dashboards.Dashboard {
|
||||
dashItem := &dashboards.SaveDashboardDTO{
|
||||
Dashboard: dash,
|
||||
Message: "",
|
||||
@@ -772,8 +768,9 @@ func scenarioWithLibraryPanel(t *testing.T, desc string, fn func(t *testing.T, s
|
||||
|
||||
testScenario(t, desc, func(t *testing.T, sc scenarioContext) {
|
||||
command := model.CreateLibraryElementCommand{
|
||||
FolderID: sc.folder.ID, // nolint:staticcheck
|
||||
Name: "Text - Library Panel",
|
||||
FolderID: sc.folder.ID, // nolint:staticcheck
|
||||
FolderUID: &sc.folder.UID,
|
||||
Name: "Text - Library Panel",
|
||||
Model: []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
@@ -795,7 +792,7 @@ func scenarioWithLibraryPanel(t *testing.T, desc string, fn func(t *testing.T, s
|
||||
Result: libraryPanel{
|
||||
ID: resp.ID,
|
||||
OrgID: resp.OrgID,
|
||||
FolderID: resp.FolderID, // nolint:staticcheck
|
||||
FolderUID: resp.FolderUID,
|
||||
UID: resp.UID,
|
||||
Name: resp.Name,
|
||||
Type: resp.Type,
|
||||
|
||||
@@ -417,9 +417,6 @@ func (am *alertmanager) PutAlerts(_ context.Context, postableAlerts apimodels.Po
|
||||
return am.Base.PutAlerts(alerts)
|
||||
}
|
||||
|
||||
// CleanUp no-ops as no files are stored on disk.
|
||||
func (am *alertmanager) CleanUp() {}
|
||||
|
||||
// AlertValidationError is the error capturing the validation errors
|
||||
// faced on the alerts.
|
||||
type AlertValidationError struct {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by mockery v2.34.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.42.1. DO NOT EDIT.
|
||||
|
||||
package alertmanager_mock
|
||||
|
||||
@@ -34,6 +34,10 @@ func (_m *AlertmanagerMock) EXPECT() *AlertmanagerMock_Expecter {
|
||||
func (_m *AlertmanagerMock) ApplyConfig(_a0 context.Context, _a1 *models.AlertConfiguration) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ApplyConfig")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.AlertConfiguration) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
@@ -73,42 +77,14 @@ func (_c *AlertmanagerMock_ApplyConfig_Call) RunAndReturn(run func(context.Conte
|
||||
return _c
|
||||
}
|
||||
|
||||
// CleanUp provides a mock function with given fields:
|
||||
func (_m *AlertmanagerMock) CleanUp() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// AlertmanagerMock_CleanUp_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CleanUp'
|
||||
type AlertmanagerMock_CleanUp_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CleanUp is a helper method to define mock.On call
|
||||
func (_e *AlertmanagerMock_Expecter) CleanUp() *AlertmanagerMock_CleanUp_Call {
|
||||
return &AlertmanagerMock_CleanUp_Call{Call: _e.mock.On("CleanUp")}
|
||||
}
|
||||
|
||||
func (_c *AlertmanagerMock_CleanUp_Call) Run(run func()) *AlertmanagerMock_CleanUp_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AlertmanagerMock_CleanUp_Call) Return() *AlertmanagerMock_CleanUp_Call {
|
||||
_c.Call.Return()
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AlertmanagerMock_CleanUp_Call) RunAndReturn(run func()) *AlertmanagerMock_CleanUp_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// CreateSilence provides a mock function with given fields: _a0, _a1
|
||||
func (_m *AlertmanagerMock) CreateSilence(_a0 context.Context, _a1 *v2models.PostableSilence) (string, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreateSilence")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *v2models.PostableSilence) (string, error)); ok {
|
||||
@@ -162,6 +138,10 @@ func (_c *AlertmanagerMock_CreateSilence_Call) RunAndReturn(run func(context.Con
|
||||
func (_m *AlertmanagerMock) DeleteSilence(_a0 context.Context, _a1 string) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteSilence")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
@@ -205,6 +185,10 @@ func (_c *AlertmanagerMock_DeleteSilence_Call) RunAndReturn(run func(context.Con
|
||||
func (_m *AlertmanagerMock) GetAlertGroups(ctx context.Context, active bool, silenced bool, inhibited bool, filter []string, receiver string) (v2models.AlertGroups, error) {
|
||||
ret := _m.Called(ctx, active, silenced, inhibited, filter, receiver)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetAlertGroups")
|
||||
}
|
||||
|
||||
var r0 v2models.AlertGroups
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, bool, bool, bool, []string, string) (v2models.AlertGroups, error)); ok {
|
||||
@@ -264,6 +248,10 @@ func (_c *AlertmanagerMock_GetAlertGroups_Call) RunAndReturn(run func(context.Co
|
||||
func (_m *AlertmanagerMock) GetAlerts(ctx context.Context, active bool, silenced bool, inhibited bool, filter []string, receiver string) (v2models.GettableAlerts, error) {
|
||||
ret := _m.Called(ctx, active, silenced, inhibited, filter, receiver)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetAlerts")
|
||||
}
|
||||
|
||||
var r0 v2models.GettableAlerts
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, bool, bool, bool, []string, string) (v2models.GettableAlerts, error)); ok {
|
||||
@@ -323,6 +311,10 @@ func (_c *AlertmanagerMock_GetAlerts_Call) RunAndReturn(run func(context.Context
|
||||
func (_m *AlertmanagerMock) GetReceivers(ctx context.Context) ([]v2models.Receiver, error) {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetReceivers")
|
||||
}
|
||||
|
||||
var r0 []v2models.Receiver
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) ([]v2models.Receiver, error)); ok {
|
||||
@@ -377,6 +369,10 @@ func (_c *AlertmanagerMock_GetReceivers_Call) RunAndReturn(run func(context.Cont
|
||||
func (_m *AlertmanagerMock) GetSilence(_a0 context.Context, _a1 string) (v2models.GettableSilence, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetSilence")
|
||||
}
|
||||
|
||||
var r0 v2models.GettableSilence
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (v2models.GettableSilence, error)); ok {
|
||||
@@ -430,6 +426,10 @@ func (_c *AlertmanagerMock_GetSilence_Call) RunAndReturn(run func(context.Contex
|
||||
func (_m *AlertmanagerMock) GetStatus() definitions.GettableStatus {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetStatus")
|
||||
}
|
||||
|
||||
var r0 definitions.GettableStatus
|
||||
if rf, ok := ret.Get(0).(func() definitions.GettableStatus); ok {
|
||||
r0 = rf()
|
||||
@@ -471,6 +471,10 @@ func (_c *AlertmanagerMock_GetStatus_Call) RunAndReturn(run func() definitions.G
|
||||
func (_m *AlertmanagerMock) ListSilences(_a0 context.Context, _a1 []string) (v2models.GettableSilences, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListSilences")
|
||||
}
|
||||
|
||||
var r0 v2models.GettableSilences
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []string) (v2models.GettableSilences, error)); ok {
|
||||
@@ -526,6 +530,10 @@ func (_c *AlertmanagerMock_ListSilences_Call) RunAndReturn(run func(context.Cont
|
||||
func (_m *AlertmanagerMock) PutAlerts(_a0 context.Context, _a1 definitions.PostableAlerts) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for PutAlerts")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, definitions.PostableAlerts) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
@@ -569,6 +577,10 @@ func (_c *AlertmanagerMock_PutAlerts_Call) RunAndReturn(run func(context.Context
|
||||
func (_m *AlertmanagerMock) Ready() bool {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Ready")
|
||||
}
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func() bool); ok {
|
||||
r0 = rf()
|
||||
@@ -610,6 +622,10 @@ func (_c *AlertmanagerMock_Ready_Call) RunAndReturn(run func() bool) *Alertmanag
|
||||
func (_m *AlertmanagerMock) SaveAndApplyConfig(ctx context.Context, config *definitions.PostableUserConfig) error {
|
||||
ret := _m.Called(ctx, config)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SaveAndApplyConfig")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *definitions.PostableUserConfig) error); ok {
|
||||
r0 = rf(ctx, config)
|
||||
@@ -653,6 +669,10 @@ func (_c *AlertmanagerMock_SaveAndApplyConfig_Call) RunAndReturn(run func(contex
|
||||
func (_m *AlertmanagerMock) SaveAndApplyDefaultConfig(ctx context.Context) error {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SaveAndApplyDefaultConfig")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
|
||||
r0 = rf(ctx)
|
||||
@@ -727,6 +747,10 @@ func (_c *AlertmanagerMock_StopAndWait_Call) RunAndReturn(run func()) *Alertmana
|
||||
func (_m *AlertmanagerMock) TestReceivers(ctx context.Context, c definitions.TestReceiversConfigBodyParams) (*notifier.TestReceiversResult, error) {
|
||||
ret := _m.Called(ctx, c)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for TestReceivers")
|
||||
}
|
||||
|
||||
var r0 *notifier.TestReceiversResult
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, definitions.TestReceiversConfigBodyParams) (*notifier.TestReceiversResult, error)); ok {
|
||||
@@ -782,6 +806,10 @@ func (_c *AlertmanagerMock_TestReceivers_Call) RunAndReturn(run func(context.Con
|
||||
func (_m *AlertmanagerMock) TestTemplate(ctx context.Context, c definitions.TestTemplatesConfigBodyParams) (*notify.TestTemplatesResults, error) {
|
||||
ret := _m.Called(ctx, c)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for TestTemplate")
|
||||
}
|
||||
|
||||
var r0 *notify.TestTemplatesResults
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, definitions.TestTemplatesConfigBodyParams) (*notify.TestTemplatesResults, error)); ok {
|
||||
|
||||
@@ -52,8 +52,7 @@ type Alertmanager interface {
|
||||
TestReceivers(ctx context.Context, c apimodels.TestReceiversConfigBodyParams) (*TestReceiversResult, error)
|
||||
TestTemplate(ctx context.Context, c apimodels.TestTemplatesConfigBodyParams) (*TestTemplatesResults, error)
|
||||
|
||||
// State
|
||||
CleanUp()
|
||||
// Lifecycle
|
||||
StopAndWait()
|
||||
Ready() bool
|
||||
}
|
||||
@@ -325,8 +324,6 @@ func (moa *MultiOrgAlertmanager) SyncAlertmanagersForOrgs(ctx context.Context, o
|
||||
moa.logger.Info("Stopping Alertmanager", "org", orgID)
|
||||
am.StopAndWait()
|
||||
moa.logger.Info("Stopped Alertmanager", "org", orgID)
|
||||
// Clean up all the remaining resources from this alertmanager.
|
||||
am.CleanUp()
|
||||
}
|
||||
|
||||
moa.cleanupOrphanLocalOrgState(ctx, orgsFound)
|
||||
|
||||
@@ -401,9 +401,6 @@ func (am *Alertmanager) Ready() bool {
|
||||
return am.ready
|
||||
}
|
||||
|
||||
// CleanUp does not have an equivalent in a "remote Alertmanager" context, we don't have files on disk, no-op.
|
||||
func (am *Alertmanager) CleanUp() {}
|
||||
|
||||
// getFullState returns a base64-encoded protobuf message representing the Alertmanager's internal state.
|
||||
func (am *Alertmanager) getFullState(ctx context.Context) (string, error) {
|
||||
var parts []alertingClusterPB.Part
|
||||
|
||||
@@ -314,14 +314,6 @@ func TestForkedAlertmanager_ModeRemoteSecondary(t *testing.T) {
|
||||
require.ErrorIs(tt, expErr, err)
|
||||
})
|
||||
|
||||
t.Run("CleanUp", func(tt *testing.T) {
|
||||
// CleanUp should be called only in the internal Alertmanager,
|
||||
// there's no cleanup to do in the remote one.
|
||||
internal, _, forked := genTestAlertmanagers(tt, modeRemoteSecondary)
|
||||
internal.EXPECT().CleanUp().Once()
|
||||
forked.CleanUp()
|
||||
})
|
||||
|
||||
t.Run("StopAndWait", func(tt *testing.T) {
|
||||
{
|
||||
// StopAndWait should be called in both Alertmanagers.
|
||||
@@ -589,14 +581,6 @@ func TestForkedAlertmanager_ModeRemotePrimary(t *testing.T) {
|
||||
require.ErrorIs(tt, expErr, err)
|
||||
})
|
||||
|
||||
t.Run("CleanUp", func(tt *testing.T) {
|
||||
// CleanUp should be called only in the internal Alertmanager,
|
||||
// there's no cleanup to do in the remote one.
|
||||
internal, _, forked := genTestAlertmanagers(tt, modeRemotePrimary)
|
||||
internal.EXPECT().CleanUp().Once()
|
||||
forked.CleanUp()
|
||||
})
|
||||
|
||||
t.Run("StopAndWait", func(tt *testing.T) {
|
||||
// StopAndWait should be called on both Alertmanagers.
|
||||
internal, remote, forked := genTestAlertmanagers(tt, modeRemotePrimary)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by mockery v2.34.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.42.1. DO NOT EDIT.
|
||||
|
||||
package alertmanager_mock
|
||||
|
||||
@@ -34,6 +34,10 @@ func (_m *RemoteAlertmanagerMock) EXPECT() *RemoteAlertmanagerMock_Expecter {
|
||||
func (_m *RemoteAlertmanagerMock) ApplyConfig(_a0 context.Context, _a1 *models.AlertConfiguration) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ApplyConfig")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.AlertConfiguration) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
@@ -73,42 +77,14 @@ func (_c *RemoteAlertmanagerMock_ApplyConfig_Call) RunAndReturn(run func(context
|
||||
return _c
|
||||
}
|
||||
|
||||
// CleanUp provides a mock function with given fields:
|
||||
func (_m *RemoteAlertmanagerMock) CleanUp() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// RemoteAlertmanagerMock_CleanUp_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CleanUp'
|
||||
type RemoteAlertmanagerMock_CleanUp_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CleanUp is a helper method to define mock.On call
|
||||
func (_e *RemoteAlertmanagerMock_Expecter) CleanUp() *RemoteAlertmanagerMock_CleanUp_Call {
|
||||
return &RemoteAlertmanagerMock_CleanUp_Call{Call: _e.mock.On("CleanUp")}
|
||||
}
|
||||
|
||||
func (_c *RemoteAlertmanagerMock_CleanUp_Call) Run(run func()) *RemoteAlertmanagerMock_CleanUp_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *RemoteAlertmanagerMock_CleanUp_Call) Return() *RemoteAlertmanagerMock_CleanUp_Call {
|
||||
_c.Call.Return()
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *RemoteAlertmanagerMock_CleanUp_Call) RunAndReturn(run func()) *RemoteAlertmanagerMock_CleanUp_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// CompareAndSendConfiguration provides a mock function with given fields: _a0, _a1
|
||||
func (_m *RemoteAlertmanagerMock) CompareAndSendConfiguration(_a0 context.Context, _a1 *models.AlertConfiguration) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CompareAndSendConfiguration")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.AlertConfiguration) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
@@ -152,6 +128,10 @@ func (_c *RemoteAlertmanagerMock_CompareAndSendConfiguration_Call) RunAndReturn(
|
||||
func (_m *RemoteAlertmanagerMock) CompareAndSendState(_a0 context.Context) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CompareAndSendState")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
|
||||
r0 = rf(_a0)
|
||||
@@ -194,6 +174,10 @@ func (_c *RemoteAlertmanagerMock_CompareAndSendState_Call) RunAndReturn(run func
|
||||
func (_m *RemoteAlertmanagerMock) CreateSilence(_a0 context.Context, _a1 *v2models.PostableSilence) (string, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreateSilence")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *v2models.PostableSilence) (string, error)); ok {
|
||||
@@ -247,6 +231,10 @@ func (_c *RemoteAlertmanagerMock_CreateSilence_Call) RunAndReturn(run func(conte
|
||||
func (_m *RemoteAlertmanagerMock) DeleteSilence(_a0 context.Context, _a1 string) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteSilence")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
@@ -290,6 +278,10 @@ func (_c *RemoteAlertmanagerMock_DeleteSilence_Call) RunAndReturn(run func(conte
|
||||
func (_m *RemoteAlertmanagerMock) GetAlertGroups(ctx context.Context, active bool, silenced bool, inhibited bool, filter []string, receiver string) (v2models.AlertGroups, error) {
|
||||
ret := _m.Called(ctx, active, silenced, inhibited, filter, receiver)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetAlertGroups")
|
||||
}
|
||||
|
||||
var r0 v2models.AlertGroups
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, bool, bool, bool, []string, string) (v2models.AlertGroups, error)); ok {
|
||||
@@ -349,6 +341,10 @@ func (_c *RemoteAlertmanagerMock_GetAlertGroups_Call) RunAndReturn(run func(cont
|
||||
func (_m *RemoteAlertmanagerMock) GetAlerts(ctx context.Context, active bool, silenced bool, inhibited bool, filter []string, receiver string) (v2models.GettableAlerts, error) {
|
||||
ret := _m.Called(ctx, active, silenced, inhibited, filter, receiver)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetAlerts")
|
||||
}
|
||||
|
||||
var r0 v2models.GettableAlerts
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, bool, bool, bool, []string, string) (v2models.GettableAlerts, error)); ok {
|
||||
@@ -408,6 +404,10 @@ func (_c *RemoteAlertmanagerMock_GetAlerts_Call) RunAndReturn(run func(context.C
|
||||
func (_m *RemoteAlertmanagerMock) GetReceivers(ctx context.Context) ([]v2models.Receiver, error) {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetReceivers")
|
||||
}
|
||||
|
||||
var r0 []v2models.Receiver
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) ([]v2models.Receiver, error)); ok {
|
||||
@@ -462,6 +462,10 @@ func (_c *RemoteAlertmanagerMock_GetReceivers_Call) RunAndReturn(run func(contex
|
||||
func (_m *RemoteAlertmanagerMock) GetSilence(_a0 context.Context, _a1 string) (v2models.GettableSilence, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetSilence")
|
||||
}
|
||||
|
||||
var r0 v2models.GettableSilence
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (v2models.GettableSilence, error)); ok {
|
||||
@@ -515,6 +519,10 @@ func (_c *RemoteAlertmanagerMock_GetSilence_Call) RunAndReturn(run func(context.
|
||||
func (_m *RemoteAlertmanagerMock) GetStatus() definitions.GettableStatus {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetStatus")
|
||||
}
|
||||
|
||||
var r0 definitions.GettableStatus
|
||||
if rf, ok := ret.Get(0).(func() definitions.GettableStatus); ok {
|
||||
r0 = rf()
|
||||
@@ -556,6 +564,10 @@ func (_c *RemoteAlertmanagerMock_GetStatus_Call) RunAndReturn(run func() definit
|
||||
func (_m *RemoteAlertmanagerMock) ListSilences(_a0 context.Context, _a1 []string) (v2models.GettableSilences, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListSilences")
|
||||
}
|
||||
|
||||
var r0 v2models.GettableSilences
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []string) (v2models.GettableSilences, error)); ok {
|
||||
@@ -611,6 +623,10 @@ func (_c *RemoteAlertmanagerMock_ListSilences_Call) RunAndReturn(run func(contex
|
||||
func (_m *RemoteAlertmanagerMock) PutAlerts(_a0 context.Context, _a1 definitions.PostableAlerts) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for PutAlerts")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, definitions.PostableAlerts) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
@@ -654,6 +670,10 @@ func (_c *RemoteAlertmanagerMock_PutAlerts_Call) RunAndReturn(run func(context.C
|
||||
func (_m *RemoteAlertmanagerMock) Ready() bool {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Ready")
|
||||
}
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func() bool); ok {
|
||||
r0 = rf()
|
||||
@@ -695,6 +715,10 @@ func (_c *RemoteAlertmanagerMock_Ready_Call) RunAndReturn(run func() bool) *Remo
|
||||
func (_m *RemoteAlertmanagerMock) SaveAndApplyConfig(ctx context.Context, config *definitions.PostableUserConfig) error {
|
||||
ret := _m.Called(ctx, config)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SaveAndApplyConfig")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *definitions.PostableUserConfig) error); ok {
|
||||
r0 = rf(ctx, config)
|
||||
@@ -738,6 +762,10 @@ func (_c *RemoteAlertmanagerMock_SaveAndApplyConfig_Call) RunAndReturn(run func(
|
||||
func (_m *RemoteAlertmanagerMock) SaveAndApplyDefaultConfig(ctx context.Context) error {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SaveAndApplyDefaultConfig")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
|
||||
r0 = rf(ctx)
|
||||
@@ -812,6 +840,10 @@ func (_c *RemoteAlertmanagerMock_StopAndWait_Call) RunAndReturn(run func()) *Rem
|
||||
func (_m *RemoteAlertmanagerMock) TestReceivers(ctx context.Context, c definitions.TestReceiversConfigBodyParams) (*notifier.TestReceiversResult, error) {
|
||||
ret := _m.Called(ctx, c)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for TestReceivers")
|
||||
}
|
||||
|
||||
var r0 *notifier.TestReceiversResult
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, definitions.TestReceiversConfigBodyParams) (*notifier.TestReceiversResult, error)); ok {
|
||||
@@ -867,6 +899,10 @@ func (_c *RemoteAlertmanagerMock_TestReceivers_Call) RunAndReturn(run func(conte
|
||||
func (_m *RemoteAlertmanagerMock) TestTemplate(ctx context.Context, c definitions.TestTemplatesConfigBodyParams) (*notify.TestTemplatesResults, error) {
|
||||
ret := _m.Called(ctx, c)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for TestTemplate")
|
||||
}
|
||||
|
||||
var r0 *notify.TestTemplatesResults
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, definitions.TestTemplatesConfigBodyParams) (*notify.TestTemplatesResults, error)); ok {
|
||||
|
||||
@@ -91,11 +91,6 @@ func (fam *RemotePrimaryForkedAlertmanager) TestTemplate(ctx context.Context, c
|
||||
return fam.remote.TestTemplate(ctx, c)
|
||||
}
|
||||
|
||||
func (fam *RemotePrimaryForkedAlertmanager) CleanUp() {
|
||||
// No cleanup to do in the remote Alertmanager.
|
||||
fam.internal.CleanUp()
|
||||
}
|
||||
|
||||
func (fam *RemotePrimaryForkedAlertmanager) StopAndWait() {
|
||||
fam.internal.StopAndWait()
|
||||
fam.remote.StopAndWait()
|
||||
|
||||
@@ -165,11 +165,6 @@ func (fam *RemoteSecondaryForkedAlertmanager) TestTemplate(ctx context.Context,
|
||||
return fam.internal.TestTemplate(ctx, c)
|
||||
}
|
||||
|
||||
func (fam *RemoteSecondaryForkedAlertmanager) CleanUp() {
|
||||
// No cleanup to do in the remote Alertmanager.
|
||||
fam.internal.CleanUp()
|
||||
}
|
||||
|
||||
func (fam *RemoteSecondaryForkedAlertmanager) StopAndWait() {
|
||||
// Stop the internal Alertmanager.
|
||||
fam.internal.StopAndWait()
|
||||
|
||||
@@ -61,4 +61,26 @@ func addLibraryElementsMigrations(mg *migrator.Migrator) {
|
||||
|
||||
mg.AddMigration("alter library_element model to mediumtext", migrator.NewRawSQLMigration("").
|
||||
Mysql("ALTER TABLE library_element MODIFY model MEDIUMTEXT NOT NULL;"))
|
||||
|
||||
q := `UPDATE library_element
|
||||
SET folder_uid = dashboard.uid
|
||||
FROM dashboard
|
||||
WHERE library_element.folder_id = dashboard.id AND library_element.org_id = dashboard.org_id`
|
||||
|
||||
if mg.Dialect.DriverName() == migrator.MySQL {
|
||||
q = `UPDATE library_element
|
||||
SET folder_uid = (
|
||||
SELECT dashboard.uid
|
||||
FROM dashboard
|
||||
WHERE library_element.folder_id = dashboard.id AND library_element.org_id = dashboard.org_id
|
||||
)`
|
||||
}
|
||||
|
||||
mg.AddMigration("add library_element folder uid", migrator.NewAddColumnMigration(libraryElementsV1, &migrator.Column{
|
||||
Name: "folder_uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: true,
|
||||
}))
|
||||
|
||||
mg.AddMigration("populate library_element folder_uid", migrator.NewRawSQLMigration(q))
|
||||
|
||||
mg.AddMigration("add index library_element org_id-folder_uid-name-kind", migrator.NewAddIndexMigration(libraryElementsV1, &migrator.Index{Cols: []string{"org_id", "folder_uid", "name", "kind"}, Type: migrator.UniqueIndex}))
|
||||
}
|
||||
|
||||
@@ -1,28 +1,45 @@
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
|
||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
|
||||
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
|
||||
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
|
||||
xorm.io/core v0.7.3 h1:W8ws1PlrnkS1CZU1YWaYLMQcQilwAmQXU0BJDJon+H0=
|
||||
xorm.io/core v0.7.3/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
||||
|
||||
@@ -6,7 +6,7 @@ import React, { useRef } from 'react';
|
||||
import CSSTransition from 'react-transition-group/CSSTransition';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { handleReducedMotion, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
import { KioskMode } from 'app/types';
|
||||
|
||||
@@ -125,8 +125,10 @@ const getStyles = (theme: GrafanaTheme2, searchBarHidden?: boolean) => {
|
||||
|
||||
const getAnimStyles = (theme: GrafanaTheme2, animationDuration: number) => {
|
||||
const commonTransition = {
|
||||
transitionDuration: `${animationDuration}ms`,
|
||||
transitionTimingFunction: theme.transitions.easing.easeInOut,
|
||||
...handleReducedMotion({
|
||||
transitionDuration: `${animationDuration}ms`,
|
||||
transitionTimingFunction: theme.transitions.easing.easeInOut,
|
||||
}),
|
||||
[theme.breakpoints.down('md')]: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@ import { cx, css, keyframes } from '@emotion/css';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { handleReducedMotion, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { Branding } from '../Branding/Branding';
|
||||
import { BrandingSettings } from '../Branding/types';
|
||||
@@ -174,7 +174,9 @@ export const getLoginStyles = (theme: GrafanaTheme2) => {
|
||||
transition: '0.25s ease',
|
||||
}),
|
||||
enterAnimation: css({
|
||||
animation: `${flyInAnimation} ease-out 0.2s`,
|
||||
...handleReducedMotion({
|
||||
animation: `${flyInAnimation} ease-out 0.2s`,
|
||||
}),
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -120,7 +120,7 @@ describe('RichHistoryLocalStorage', () => {
|
||||
const settings: RichHistorySettings = {
|
||||
retentionPeriod: 2,
|
||||
starredTabAsFirstTab: true,
|
||||
activeDatasourceOnly: true,
|
||||
activeDatasourcesOnly: true,
|
||||
lastUsedDatasourceFilters: ['foobar'],
|
||||
};
|
||||
await storage.updateSettings(settings);
|
||||
|
||||
@@ -124,8 +124,11 @@ export default class RichHistoryLocalStorage implements RichHistoryStorage {
|
||||
}
|
||||
|
||||
async getSettings() {
|
||||
// get the new key without a default. If undefined, use the legacy key, or false as the default
|
||||
const activeDatasource: boolean | undefined = store.getObject(RICH_HISTORY_SETTING_KEYS.activeDatasourcesOnly);
|
||||
return {
|
||||
activeDatasourceOnly: store.getObject(RICH_HISTORY_SETTING_KEYS.activeDatasourceOnly, false),
|
||||
activeDatasourcesOnly:
|
||||
activeDatasource ?? store.getObject(RICH_HISTORY_SETTING_KEYS.legacyActiveDatasourceOnly, false),
|
||||
retentionPeriod: store.getObject(RICH_HISTORY_SETTING_KEYS.retentionPeriod, 7),
|
||||
starredTabAsFirstTab: store.getBool(RICH_HISTORY_SETTING_KEYS.starredTabAsFirstTab, false),
|
||||
lastUsedDatasourceFilters: store
|
||||
@@ -135,7 +138,7 @@ export default class RichHistoryLocalStorage implements RichHistoryStorage {
|
||||
}
|
||||
|
||||
async updateSettings(settings: RichHistorySettings) {
|
||||
store.set(RICH_HISTORY_SETTING_KEYS.activeDatasourceOnly, settings.activeDatasourceOnly);
|
||||
store.set(RICH_HISTORY_SETTING_KEYS.activeDatasourcesOnly, settings.activeDatasourcesOnly);
|
||||
store.set(RICH_HISTORY_SETTING_KEYS.retentionPeriod, settings.retentionPeriod);
|
||||
store.set(RICH_HISTORY_SETTING_KEYS.starredTabAsFirstTab, settings.starredTabAsFirstTab);
|
||||
store.setObject(
|
||||
|
||||
@@ -188,7 +188,7 @@ describe('RichHistoryRemoteStorage', () => {
|
||||
} as UserPreferencesDTO);
|
||||
const settings = await storage.getSettings();
|
||||
expect(settings).toMatchObject({
|
||||
activeDatasourceOnly: false,
|
||||
activeDatasourcesOnly: false,
|
||||
lastUsedDatasourceFilters: undefined,
|
||||
retentionPeriod: 14,
|
||||
starredTabAsFirstTab: true,
|
||||
@@ -203,7 +203,7 @@ describe('RichHistoryRemoteStorage', () => {
|
||||
} as UserPreferencesDTO);
|
||||
const settings = await storage.getSettings();
|
||||
expect(settings).toMatchObject({
|
||||
activeDatasourceOnly: false,
|
||||
activeDatasourcesOnly: false,
|
||||
lastUsedDatasourceFilters: undefined,
|
||||
retentionPeriod: 14,
|
||||
starredTabAsFirstTab: false,
|
||||
@@ -212,7 +212,7 @@ describe('RichHistoryRemoteStorage', () => {
|
||||
|
||||
it('updates user settings', async () => {
|
||||
await storage.updateSettings({
|
||||
activeDatasourceOnly: false,
|
||||
activeDatasourcesOnly: false,
|
||||
lastUsedDatasourceFilters: undefined,
|
||||
retentionPeriod: 14,
|
||||
starredTabAsFirstTab: false,
|
||||
@@ -222,7 +222,7 @@ describe('RichHistoryRemoteStorage', () => {
|
||||
} as Partial<UserPreferencesDTO>);
|
||||
|
||||
await storage.updateSettings({
|
||||
activeDatasourceOnly: false,
|
||||
activeDatasourcesOnly: false,
|
||||
lastUsedDatasourceFilters: undefined,
|
||||
retentionPeriod: 14,
|
||||
starredTabAsFirstTab: true,
|
||||
|
||||
@@ -85,7 +85,7 @@ export default class RichHistoryRemoteStorage implements RichHistoryStorage {
|
||||
async getSettings(): Promise<RichHistorySettings> {
|
||||
const preferences = await this.preferenceService.load();
|
||||
return {
|
||||
activeDatasourceOnly: false,
|
||||
activeDatasourcesOnly: false,
|
||||
lastUsedDatasourceFilters: undefined,
|
||||
retentionPeriod: 14,
|
||||
starredTabAsFirstTab: preferences.queryHistory?.homeTab === 'starred',
|
||||
|
||||
@@ -94,6 +94,7 @@ export const sortQueries = (array: RichHistoryQuery[], sortOrder: SortOrder) =>
|
||||
export const RICH_HISTORY_SETTING_KEYS = {
|
||||
retentionPeriod: 'grafana.explore.richHistory.retentionPeriod',
|
||||
starredTabAsFirstTab: 'grafana.explore.richHistory.starredTabAsFirstTab',
|
||||
activeDatasourceOnly: 'grafana.explore.richHistory.activeDatasourceOnly',
|
||||
legacyActiveDatasourceOnly: 'grafana.explore.richHistory.activeDatasourceOnly',
|
||||
activeDatasourcesOnly: 'grafana.explore.richHistory.activeDatasourcesOnly',
|
||||
datasourceFilters: 'grafana.explore.richHistory.datasourceFilters',
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ export enum SortOrder {
|
||||
export interface RichHistorySettings {
|
||||
retentionPeriod: number;
|
||||
starredTabAsFirstTab: boolean;
|
||||
activeDatasourceOnly: boolean;
|
||||
activeDatasourcesOnly: boolean;
|
||||
lastUsedDatasourceFilters?: string[];
|
||||
}
|
||||
|
||||
|
||||
@@ -2,19 +2,26 @@ import { Unsubscribable } from 'rxjs';
|
||||
|
||||
import {
|
||||
SceneDataLayerSet,
|
||||
SceneDataTransformer,
|
||||
SceneGridLayout,
|
||||
SceneObjectStateChangedEvent,
|
||||
SceneQueryRunner,
|
||||
SceneRefreshPicker,
|
||||
SceneTimeRange,
|
||||
SceneVariableSet,
|
||||
VizPanel,
|
||||
behaviors,
|
||||
} from '@grafana/scenes';
|
||||
import { createWorker } from 'app/features/dashboard-scene/saving/createDetectChangesWorker';
|
||||
|
||||
import { VizPanelManager } from '../panel-edit/VizPanelManager';
|
||||
import { DashboardAnnotationsDataLayer } from '../scene/DashboardAnnotationsDataLayer';
|
||||
import { DashboardControls } from '../scene/DashboardControls';
|
||||
import { DashboardGridItem } from '../scene/DashboardGridItem';
|
||||
import { DashboardScene, PERSISTED_PROPS } from '../scene/DashboardScene';
|
||||
import { LibraryVizPanel } from '../scene/LibraryVizPanel';
|
||||
import { VizPanelLinks } from '../scene/PanelLinks';
|
||||
import { PanelTimeRange } from '../scene/PanelTimeRange';
|
||||
import { transformSceneToSaveModel } from '../serialization/transformSceneToSaveModel';
|
||||
import { isSceneVariableInstance } from '../settings/variables/utils';
|
||||
|
||||
@@ -30,52 +37,97 @@ export class DashboardSceneChangeTracker {
|
||||
}
|
||||
|
||||
private onStateChanged({ payload }: SceneObjectStateChangedEvent) {
|
||||
// If there are no changes in the state, the check is not needed
|
||||
if (Object.keys(payload.partialUpdate).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Any change in the panel should trigger a change detection
|
||||
// The VizPanelManager includes configuration for the panel like repeat
|
||||
// The PanelTimeRange includes the overrides configuration
|
||||
if (
|
||||
payload.changedObject instanceof VizPanel ||
|
||||
payload.changedObject instanceof DashboardGridItem ||
|
||||
payload.changedObject instanceof PanelTimeRange
|
||||
) {
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
// VizPanelManager includes the repeat configuration
|
||||
if (payload.changedObject instanceof VizPanelManager) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'repeat') ||
|
||||
Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'repeatDirection') ||
|
||||
Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'maxPerRow')
|
||||
) {
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
}
|
||||
// SceneQueryRunner includes the DS configuration
|
||||
if (payload.changedObject instanceof SceneQueryRunner) {
|
||||
if (!Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'data')) {
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
}
|
||||
// SceneDataTransformer includes the transformation configuration
|
||||
if (payload.changedObject instanceof SceneDataTransformer) {
|
||||
if (!Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'data')) {
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
}
|
||||
if (payload.changedObject instanceof VizPanelLinks) {
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
if (payload.changedObject instanceof LibraryVizPanel) {
|
||||
if (Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'name')) {
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
}
|
||||
if (payload.changedObject instanceof SceneRefreshPicker) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'intervals') ||
|
||||
Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'refresh')
|
||||
) {
|
||||
this.detectSaveModelChanges();
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
}
|
||||
if (payload.changedObject instanceof behaviors.CursorSync) {
|
||||
this.detectSaveModelChanges();
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
if (payload.changedObject instanceof SceneDataLayerSet) {
|
||||
this.detectSaveModelChanges();
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
if (payload.changedObject instanceof DashboardGridItem) {
|
||||
this.detectSaveModelChanges();
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
if (payload.changedObject instanceof SceneGridLayout) {
|
||||
this.detectSaveModelChanges();
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
if (payload.changedObject instanceof DashboardScene) {
|
||||
if (Object.keys(payload.partialUpdate).some((key) => PERSISTED_PROPS.includes(key))) {
|
||||
this.detectSaveModelChanges();
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
}
|
||||
if (payload.changedObject instanceof SceneTimeRange) {
|
||||
this.detectSaveModelChanges();
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
if (payload.changedObject instanceof DashboardControls) {
|
||||
if (Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'hideTimeControls')) {
|
||||
this.detectSaveModelChanges();
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
}
|
||||
if (payload.changedObject instanceof SceneVariableSet) {
|
||||
this.detectSaveModelChanges();
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
if (payload.changedObject instanceof DashboardAnnotationsDataLayer) {
|
||||
if (!Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'data')) {
|
||||
this.detectSaveModelChanges();
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
}
|
||||
if (payload.changedObject instanceof behaviors.LiveNowTimer) {
|
||||
this.detectSaveModelChanges();
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
if (isSceneVariableInstance(payload.changedObject)) {
|
||||
this.detectSaveModelChanges();
|
||||
return this.detectSaveModelChanges();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CoreApp } from '@grafana/data';
|
||||
import { CoreApp, LoadingState, getDefaultTimeRange } from '@grafana/data';
|
||||
import {
|
||||
sceneGraph,
|
||||
SceneGridLayout,
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
VizPanel,
|
||||
SceneGridRow,
|
||||
behaviors,
|
||||
SceneDataTransformer,
|
||||
} from '@grafana/scenes';
|
||||
import { Dashboard, DashboardCursorSync } from '@grafana/schema';
|
||||
import appEvents from 'app/core/app_events';
|
||||
@@ -30,6 +31,7 @@ import { DashboardControls } from './DashboardControls';
|
||||
import { DashboardGridItem } from './DashboardGridItem';
|
||||
import { DashboardScene, DashboardSceneState } from './DashboardScene';
|
||||
import { LibraryVizPanel } from './LibraryVizPanel';
|
||||
import { PanelTimeRange } from './PanelTimeRange';
|
||||
import { RowActions } from './row-actions/RowActions';
|
||||
|
||||
jest.mock('../settings/version-history/HistorySrv');
|
||||
@@ -270,6 +272,125 @@ describe('DashboardScene', () => {
|
||||
expect(dashboardSceneGraph.getCursorSync(scene)!.state).toEqual(initialState);
|
||||
});
|
||||
|
||||
it('A change to a any VizPanel state should set isDirty true', () => {
|
||||
const panel = sceneGraph.findObject(scene, (p) => p instanceof VizPanel) as VizPanel;
|
||||
const prevTitle = panel.state.title;
|
||||
panel.setState({ title: 'new title' });
|
||||
|
||||
expect(scene.state.isDirty).toBe(true);
|
||||
|
||||
scene.exitEditMode({ skipConfirm: true });
|
||||
const restoredPanel = sceneGraph.findObject(scene, (p) => p instanceof VizPanel) as VizPanel;
|
||||
expect(restoredPanel.state.title).toBe(prevTitle);
|
||||
});
|
||||
|
||||
it('A change to any DashboardGridItem state should set isDirty true', () => {
|
||||
const dashboardGridItem = sceneGraph.findObject(
|
||||
scene,
|
||||
(p) => p instanceof DashboardGridItem
|
||||
) as DashboardGridItem;
|
||||
const prevValue = dashboardGridItem.state.variableName;
|
||||
|
||||
dashboardGridItem.setState({ variableName: 'var1', repeatDirection: 'h', maxPerRow: 2 });
|
||||
|
||||
expect(scene.state.isDirty).toBe(true);
|
||||
|
||||
scene.exitEditMode({ skipConfirm: true });
|
||||
const restoredDashboardGridItem = sceneGraph.findObject(
|
||||
scene,
|
||||
(p) => p instanceof DashboardGridItem
|
||||
) as DashboardGridItem;
|
||||
expect(restoredDashboardGridItem.state.variableName).toBe(prevValue);
|
||||
});
|
||||
|
||||
it('A change to any LibraryVizPanel name should set isDirty true', () => {
|
||||
const libraryVizPanel = sceneGraph.findObject(scene, (p) => p instanceof LibraryVizPanel) as LibraryVizPanel;
|
||||
const prevValue = libraryVizPanel.state.name;
|
||||
|
||||
libraryVizPanel.setState({ name: 'new name' });
|
||||
|
||||
expect(scene.state.isDirty).toBe(true);
|
||||
|
||||
scene.exitEditMode({ skipConfirm: true });
|
||||
const restoredLibraryVizPanel = sceneGraph.findObject(
|
||||
scene,
|
||||
(p) => p instanceof LibraryVizPanel
|
||||
) as LibraryVizPanel;
|
||||
expect(restoredLibraryVizPanel.state.name).toBe(prevValue);
|
||||
});
|
||||
|
||||
it('A change to any PanelTimeRange state should set isDirty true', () => {
|
||||
const panelTimeRange = sceneGraph.findObject(scene, (p) => p instanceof PanelTimeRange) as PanelTimeRange;
|
||||
const prevValue = panelTimeRange.state.from;
|
||||
|
||||
panelTimeRange.setState({ from: 'now-1h', to: 'now' });
|
||||
|
||||
expect(scene.state.isDirty).toBe(true);
|
||||
|
||||
scene.exitEditMode({ skipConfirm: true });
|
||||
const restoredPanelTimeRange = sceneGraph.findObject(
|
||||
scene,
|
||||
(p) => p instanceof PanelTimeRange
|
||||
) as PanelTimeRange;
|
||||
expect(restoredPanelTimeRange.state.from).toEqual(prevValue);
|
||||
});
|
||||
|
||||
it('A change to any SceneQueryRunner state should set isDirty true', () => {
|
||||
const queryRunner = sceneGraph.findObject(scene, (p) => p instanceof SceneQueryRunner) as SceneQueryRunner;
|
||||
const prevValue = queryRunner.state.queries;
|
||||
|
||||
queryRunner.setState({ queries: [{ refId: 'A', datasource: { uid: 'fake-uid', type: 'test' } }] });
|
||||
|
||||
expect(scene.state.isDirty).toBe(true);
|
||||
|
||||
scene.exitEditMode({ skipConfirm: true });
|
||||
const restoredQueryRunner = sceneGraph.findObject(
|
||||
scene,
|
||||
(p) => p instanceof SceneQueryRunner
|
||||
) as SceneQueryRunner;
|
||||
expect(restoredQueryRunner.state.queries).toEqual(prevValue);
|
||||
});
|
||||
|
||||
it('A change to any SceneDataTransformer state should set isDirty true', () => {
|
||||
const dataTransformer = sceneGraph.findObject(
|
||||
scene,
|
||||
(p) => p instanceof SceneDataTransformer
|
||||
) as SceneDataTransformer;
|
||||
const prevValue = dataTransformer.state.transformations;
|
||||
|
||||
dataTransformer.setState({ transformations: [{ id: 'fake-transformation', options: {} }] });
|
||||
|
||||
expect(scene.state.isDirty).toBe(true);
|
||||
|
||||
scene.exitEditMode({ skipConfirm: true });
|
||||
const restoredDataTransformer = sceneGraph.findObject(
|
||||
scene,
|
||||
(p) => p instanceof SceneDataTransformer
|
||||
) as SceneDataTransformer;
|
||||
expect(restoredDataTransformer.state.transformations).toEqual(prevValue);
|
||||
});
|
||||
|
||||
it('A change to any SceneDataTransformer data should NOT set isDirty true', () => {
|
||||
const dataTransformer = sceneGraph.findObject(
|
||||
scene,
|
||||
(p) => p instanceof SceneDataTransformer
|
||||
) as SceneDataTransformer;
|
||||
const prevValue = dataTransformer.state.data;
|
||||
const newData = { state: LoadingState.Done, timeRange: getDefaultTimeRange(), series: [] };
|
||||
|
||||
dataTransformer.setState({ data: newData });
|
||||
|
||||
expect(scene.state.isDirty).toBeFalsy();
|
||||
|
||||
scene.exitEditMode({ skipConfirm: true });
|
||||
|
||||
const restoredDataTransformer = sceneGraph.findObject(
|
||||
scene,
|
||||
(p) => p instanceof SceneDataTransformer
|
||||
) as SceneDataTransformer;
|
||||
expect(restoredDataTransformer.state.data).toEqual(prevValue);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ hasChanges: true, hasTimeChanges: false, hasVariableValueChanges: false },
|
||||
{ hasChanges: true, hasTimeChanges: true, hasVariableValueChanges: false },
|
||||
@@ -967,7 +1088,15 @@ function buildTestScene(overrides?: Partial<DashboardSceneState>) {
|
||||
title: 'Panel A',
|
||||
key: 'panel-1',
|
||||
pluginId: 'table',
|
||||
$data: new SceneQueryRunner({ key: 'data-query-runner', queries: [{ refId: 'A' }] }),
|
||||
$timeRange: new PanelTimeRange({
|
||||
from: 'now-12h',
|
||||
to: 'now',
|
||||
timeZone: 'browser',
|
||||
}),
|
||||
$data: new SceneDataTransformer({
|
||||
transformations: [],
|
||||
$data: new SceneQueryRunner({ key: 'data-query-runner', queries: [{ refId: 'A' }] }),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
new DashboardGridItem({
|
||||
|
||||
@@ -98,6 +98,7 @@ const mustKeepProps: { [str: string]: boolean } = {
|
||||
links: true,
|
||||
fullscreen: true,
|
||||
isEditing: true,
|
||||
isViewing: true,
|
||||
hasRefreshed: true,
|
||||
events: true,
|
||||
cacheTimeout: true,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { configureStore } from 'app/store/configureStore';
|
||||
|
||||
import { ContentOutlineContextProvider } from './ContentOutline/ContentOutlineContext';
|
||||
import { Explore, Props } from './Explore';
|
||||
import { initialExploreState } from './state/main';
|
||||
import { changeShowQueryHistory, initialExploreState } from './state/main';
|
||||
import { scanStopAction } from './state/query';
|
||||
import { createEmptyQueryResponse, makeExplorePaneState } from './state/utils';
|
||||
|
||||
@@ -51,6 +51,8 @@ const makeEmptyQueryResponse = (loadingState: LoadingState) => {
|
||||
};
|
||||
|
||||
const dummyProps: Props = {
|
||||
setShowQueryInspector: (value: boolean) => {},
|
||||
showQueryInspector: false,
|
||||
logsResult: undefined,
|
||||
changeSize: jest.fn(),
|
||||
datasourceInstance: {
|
||||
@@ -98,6 +100,8 @@ const dummyProps: Props = {
|
||||
setSupplementaryQueryEnabled: jest.fn(),
|
||||
correlationEditorDetails: undefined,
|
||||
correlationEditorHelperData: undefined,
|
||||
showQueryHistory: false,
|
||||
changeShowQueryHistory: changeShowQueryHistory,
|
||||
};
|
||||
|
||||
jest.mock('@grafana/runtime/src/services/dataSourceSrv', () => {
|
||||
|
||||
@@ -41,7 +41,6 @@ import { ContentOutlineContextProvider } from './ContentOutline/ContentOutlineCo
|
||||
import { ContentOutlineItem } from './ContentOutline/ContentOutlineItem';
|
||||
import { CorrelationHelper } from './CorrelationHelper';
|
||||
import { CustomContainer } from './CustomContainer';
|
||||
import ExploreQueryInspector from './ExploreQueryInspector';
|
||||
import { ExploreToolbar } from './ExploreToolbar';
|
||||
import { FlameGraphExploreContainer } from './FlameGraph/FlameGraphExploreContainer';
|
||||
import { GraphContainer } from './Graph/GraphContainer';
|
||||
@@ -53,12 +52,11 @@ import { NodeGraphContainer } from './NodeGraph/NodeGraphContainer';
|
||||
import { QueryRows } from './QueryRows';
|
||||
import RawPrometheusContainer from './RawPrometheus/RawPrometheusContainer';
|
||||
import { ResponseErrorContainer } from './ResponseErrorContainer';
|
||||
import RichHistoryContainer from './RichHistory/RichHistoryContainer';
|
||||
import { SecondaryActions } from './SecondaryActions';
|
||||
import TableContainer from './Table/TableContainer';
|
||||
import { TraceViewContainer } from './TraceView/TraceViewContainer';
|
||||
import { changeSize } from './state/explorePane';
|
||||
import { splitOpen } from './state/main';
|
||||
import { changeShowQueryHistory, splitOpen } from './state/main';
|
||||
import {
|
||||
addQueryRow,
|
||||
modifyQueries,
|
||||
@@ -108,15 +106,11 @@ export interface ExploreProps extends Themeable2 {
|
||||
exploreId: string;
|
||||
theme: GrafanaTheme2;
|
||||
eventBus: EventBus;
|
||||
}
|
||||
|
||||
enum ExploreDrawer {
|
||||
RichHistory,
|
||||
QueryInspector,
|
||||
setShowQueryInspector: (value: boolean) => void;
|
||||
showQueryInspector: boolean;
|
||||
}
|
||||
|
||||
interface ExploreState {
|
||||
openDrawer?: ExploreDrawer;
|
||||
contentOutlineVisible: boolean;
|
||||
}
|
||||
|
||||
@@ -156,7 +150,6 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
openDrawer: undefined,
|
||||
contentOutlineVisible: false,
|
||||
};
|
||||
this.graphEventBus = props.eventBus.newScopedBus('graph', { onlyLocal: false });
|
||||
@@ -311,20 +304,8 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
updateTimeRange({ exploreId, absoluteRange });
|
||||
};
|
||||
|
||||
toggleShowRichHistory = () => {
|
||||
this.setState((state) => {
|
||||
return {
|
||||
openDrawer: state.openDrawer === ExploreDrawer.RichHistory ? undefined : ExploreDrawer.RichHistory,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
toggleShowQueryInspector = () => {
|
||||
this.setState((state) => {
|
||||
return {
|
||||
openDrawer: state.openDrawer === ExploreDrawer.QueryInspector ? undefined : ExploreDrawer.QueryInspector,
|
||||
};
|
||||
});
|
||||
toggleShowQueryHistory = () => {
|
||||
this.props.changeShowQueryHistory(!this.props.showQueryHistory);
|
||||
};
|
||||
|
||||
onSplitOpen = (panelType: string) => {
|
||||
@@ -551,17 +532,17 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
showCustom,
|
||||
showNodeGraph,
|
||||
showFlameGraph,
|
||||
timeZone,
|
||||
showLogsSample,
|
||||
correlationEditorDetails,
|
||||
correlationEditorHelperData,
|
||||
showQueryHistory,
|
||||
showQueryInspector,
|
||||
setShowQueryInspector,
|
||||
} = this.props;
|
||||
const { openDrawer, contentOutlineVisible } = this.state;
|
||||
const { contentOutlineVisible } = this.state;
|
||||
const styles = getStyles(theme);
|
||||
const showPanels = queryResponse && queryResponse.state !== LoadingState.NotStarted;
|
||||
const showRichHistory = openDrawer === ExploreDrawer.RichHistory;
|
||||
const richHistoryRowButtonHidden = !supportedFeatures().queryHistoryAvailable;
|
||||
const showQueryInspector = openDrawer === ExploreDrawer.QueryInspector;
|
||||
const showNoData =
|
||||
queryResponse.state === LoadingState.Done &&
|
||||
[
|
||||
@@ -622,11 +603,11 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
//TODO:unification
|
||||
addQueryRowButtonHidden={false}
|
||||
richHistoryRowButtonHidden={richHistoryRowButtonHidden}
|
||||
richHistoryButtonActive={showRichHistory}
|
||||
richHistoryButtonActive={showQueryHistory}
|
||||
queryInspectorButtonActive={showQueryInspector}
|
||||
onClickAddQueryRowButton={this.onClickAddQueryRowButton}
|
||||
onClickRichHistoryButton={this.toggleShowRichHistory}
|
||||
onClickQueryInspectorButton={this.toggleShowQueryInspector}
|
||||
onClickRichHistoryButton={this.toggleShowQueryHistory}
|
||||
onClickQueryInspectorButton={() => setShowQueryInspector(!showQueryInspector)}
|
||||
/>
|
||||
<ResponseErrorContainer exploreId={exploreId} />
|
||||
</PanelContainer>
|
||||
@@ -664,22 +645,6 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
{showNoData && <ErrorBoundaryAlert>{this.renderNoData()}</ErrorBoundaryAlert>}
|
||||
</>
|
||||
)}
|
||||
{showRichHistory && (
|
||||
<RichHistoryContainer
|
||||
width={width}
|
||||
exploreId={exploreId}
|
||||
onClose={this.toggleShowRichHistory}
|
||||
/>
|
||||
)}
|
||||
{showQueryInspector && (
|
||||
<ExploreQueryInspector
|
||||
exploreId={exploreId}
|
||||
width={width}
|
||||
onClose={this.toggleShowQueryInspector}
|
||||
timeZone={timeZone}
|
||||
isMixed={datasourceInstance.meta.mixed || false}
|
||||
/>
|
||||
)}
|
||||
</ErrorBoundaryAlert>
|
||||
</main>
|
||||
);
|
||||
@@ -756,6 +721,7 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
||||
showLogsSample,
|
||||
correlationEditorHelperData,
|
||||
correlationEditorDetails: explore.correlationEditorDetails,
|
||||
showQueryHistory: explore.showQueryHistory,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -769,6 +735,7 @@ const mapDispatchToProps = {
|
||||
addQueryRow,
|
||||
splitOpen,
|
||||
setSupplementaryQueryEnabled,
|
||||
changeShowQueryHistory,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
||||
@@ -8,22 +8,20 @@ import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { getDragStyles, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
|
||||
export interface Props {
|
||||
width: number;
|
||||
children: React.ReactNode;
|
||||
onResize?: ResizeCallback;
|
||||
}
|
||||
|
||||
export function ExploreDrawer(props: Props) {
|
||||
const { width, children, onResize } = props;
|
||||
const { children, onResize } = props;
|
||||
const theme = useTheme2();
|
||||
const styles = useStyles2(getStyles);
|
||||
const dragStyles = getDragStyles(theme);
|
||||
const drawerWidth = `${width + 31.5}px`;
|
||||
|
||||
return (
|
||||
<Resizable
|
||||
className={cx(styles.fixed, styles.container, styles.drawerActive)}
|
||||
defaultSize={{ width: drawerWidth, height: `${theme.components.horizontalDrawer.defaultHeight}px` }}
|
||||
defaultSize={{ width: '100%', height: `${theme.components.horizontalDrawer.defaultHeight}px` }}
|
||||
handleClasses={{ top: dragStyles.dragHandleHorizontal }}
|
||||
enable={{
|
||||
top: true,
|
||||
@@ -36,8 +34,6 @@ export function ExploreDrawer(props: Props) {
|
||||
topLeft: false,
|
||||
}}
|
||||
maxHeight="100vh"
|
||||
maxWidth={drawerWidth}
|
||||
minWidth={drawerWidth}
|
||||
onResize={onResize}
|
||||
>
|
||||
{children}
|
||||
@@ -58,13 +54,12 @@ const drawerSlide = (theme: GrafanaTheme2) => keyframes`
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
// @ts-expect-error csstype doesn't allow !important. see https://github.com/frenic/csstype/issues/114
|
||||
fixed: css({
|
||||
position: 'fixed !important',
|
||||
position: 'absolute !important',
|
||||
}),
|
||||
container: css({
|
||||
bottom: 0,
|
||||
background: theme.colors.background.primary,
|
||||
borderTop: `1px solid ${theme.colors.border.weak}`,
|
||||
margin: theme.spacing(0, -2, 0, -2),
|
||||
boxShadow: theme.shadows.z3,
|
||||
zIndex: theme.zIndex.navbarFixed,
|
||||
}),
|
||||
|
||||
@@ -8,18 +8,21 @@ import { SplitPaneWrapper } from 'app/core/components/SplitPaneWrapper/SplitPane
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
import { useNavModel } from 'app/core/hooks/useNavModel';
|
||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||
import { useSelector } from 'app/types';
|
||||
import { useDispatch, useSelector } from 'app/types';
|
||||
import { ExploreQueryParams } from 'app/types/explore';
|
||||
|
||||
import { CorrelationEditorModeBar } from './CorrelationEditorModeBar';
|
||||
import { ExploreActions } from './ExploreActions';
|
||||
import { ExploreDrawer } from './ExploreDrawer';
|
||||
import { ExplorePaneContainer } from './ExplorePaneContainer';
|
||||
import RichHistoryContainer from './RichHistory/RichHistoryContainer';
|
||||
import { useExplorePageTitle } from './hooks/useExplorePageTitle';
|
||||
import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts';
|
||||
import { useSplitSizeUpdater } from './hooks/useSplitSizeUpdater';
|
||||
import { useStateSync } from './hooks/useStateSync';
|
||||
import { useTimeSrvFix } from './hooks/useTimeSrvFix';
|
||||
import { isSplit, selectCorrelationDetails, selectPanesEntries } from './state/selectors';
|
||||
import { changeShowQueryHistory } from './state/main';
|
||||
import { isSplit, selectCorrelationDetails, selectPanesEntries, selectShowQueryHistory } from './state/selectors';
|
||||
|
||||
const MIN_PANE_WIDTH = 200;
|
||||
|
||||
@@ -36,11 +39,13 @@ export default function ExplorePage(props: GrafanaRouteComponentProps<{}, Explor
|
||||
useExplorePageTitle(props.queryParams);
|
||||
const { chrome } = useGrafana();
|
||||
const navModel = useNavModel('explore');
|
||||
const dispatch = useDispatch();
|
||||
const { updateSplitSize, widthCalc } = useSplitSizeUpdater(MIN_PANE_WIDTH);
|
||||
|
||||
const panes = useSelector(selectPanesEntries);
|
||||
const hasSplit = useSelector(isSplit);
|
||||
const correlationDetails = useSelector(selectCorrelationDetails);
|
||||
const showQueryHistory = useSelector(selectShowQueryHistory);
|
||||
const showCorrelationEditorBar = config.featureToggles.correlations && (correlationDetails?.editorMode || false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -80,6 +85,15 @@ export default function ExplorePage(props: GrafanaRouteComponentProps<{}, Explor
|
||||
);
|
||||
})}
|
||||
</SplitPaneWrapper>
|
||||
{showQueryHistory && (
|
||||
<ExploreDrawer>
|
||||
<RichHistoryContainer
|
||||
onClose={() => {
|
||||
dispatch(changeShowQueryHistory(false));
|
||||
}}
|
||||
/>
|
||||
</ExploreDrawer>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -92,6 +106,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
minHeight: 0,
|
||||
height: '100%',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
}),
|
||||
correlationsEditorIndicator: css({
|
||||
borderLeft: `4px solid ${theme.colors.primary.main}`,
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { useEffect, useMemo, useRef } from 'react';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { EventBusSrv } from '@grafana/data';
|
||||
import { EventBusSrv, getTimeZone } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { CustomScrollbar } from '@grafana/ui';
|
||||
import { stopQueryState } from 'app/core/utils/explore';
|
||||
import { StoreState, useSelector } from 'app/types';
|
||||
|
||||
import Explore from './Explore';
|
||||
import ExploreQueryInspector from './ExploreQueryInspector';
|
||||
import { getExploreItemSelector } from './state/selectors';
|
||||
|
||||
const containerStyles = css({
|
||||
@@ -27,7 +28,7 @@ interface Props {
|
||||
Connected components subscribe to the store before function components (using hooks) and can react to store changes. Thus, this connector function is called before the parent component (ExplorePage) is rerendered.
|
||||
This means that child components' mapStateToProps will be executed with a zombie `exploreId` that is not present anymore in the store if the pane gets closed.
|
||||
By connecting this component and returning the pane we workaround the zombie children issue here instead of modifying every children.
|
||||
This is definitely not the ideal solution and we should in the future invest more time in exploring other approaches to better handle this scenario, potentially by refactoring panels to be function components
|
||||
This is definitely not the ideal solution and we should in the future invest more time in exploring other approaches to better handle this scenario, potentially by refactoring panels to be function components
|
||||
(therefore immune to this behaviour), or by forbidding them to access the store directly and instead pass them all the data they need via props or context.
|
||||
|
||||
You can read more about this issue here: https://react-redux.js.org/api/hooks#stale-props-and-zombie-children
|
||||
@@ -36,6 +37,7 @@ function ExplorePaneContainerUnconnected({ exploreId }: Props) {
|
||||
useStopQueries(exploreId);
|
||||
const eventBus = useRef(new EventBusSrv());
|
||||
const ref = useRef(null);
|
||||
const [showQueryInspector, setShowQueryInspector] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const bus = eventBus.current;
|
||||
@@ -45,7 +47,19 @@ function ExplorePaneContainerUnconnected({ exploreId }: Props) {
|
||||
return (
|
||||
<CustomScrollbar hideVerticalTrack>
|
||||
<div className={containerStyles} ref={ref} data-testid={selectors.pages.Explore.General.container}>
|
||||
<Explore exploreId={exploreId} eventBus={eventBus.current} />
|
||||
<Explore
|
||||
exploreId={exploreId}
|
||||
eventBus={eventBus.current}
|
||||
showQueryInspector={showQueryInspector}
|
||||
setShowQueryInspector={setShowQueryInspector}
|
||||
/>
|
||||
{showQueryInspector && (
|
||||
<ExploreQueryInspector
|
||||
exploreId={exploreId}
|
||||
onClose={() => setShowQueryInspector(false)}
|
||||
timeZone={getTimeZone()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</CustomScrollbar>
|
||||
);
|
||||
|
||||
@@ -47,7 +47,6 @@ jest.mock('react-virtualized-auto-sizer', () => {
|
||||
|
||||
const setup = (propOverrides = {}) => {
|
||||
const props: ExploreQueryInspectorProps = {
|
||||
width: 100,
|
||||
exploreId: 'left',
|
||||
onClose: jest.fn(),
|
||||
timeZone: InternalTimeZones.utc,
|
||||
|
||||
@@ -21,17 +21,15 @@ import { GetDataOptions } from '../query/state/PanelQueryRunner';
|
||||
import { runQueries } from './state/query';
|
||||
|
||||
interface DispatchProps {
|
||||
width: number;
|
||||
exploreId: string;
|
||||
timeZone: TimeZone;
|
||||
onClose: () => void;
|
||||
isMixed: boolean;
|
||||
}
|
||||
|
||||
type Props = DispatchProps & ConnectedProps<typeof connector>;
|
||||
|
||||
export function ExploreQueryInspector(props: Props) {
|
||||
const { width, onClose, queryResponse, timeZone, isMixed, exploreId } = props;
|
||||
const { onClose, queryResponse, timeZone, isMixed, exploreId } = props;
|
||||
const [dataOptions, setDataOptions] = useState<GetDataOptions>({
|
||||
withTransforms: false,
|
||||
withFieldConfig: true,
|
||||
@@ -105,7 +103,7 @@ export function ExploreQueryInspector(props: Props) {
|
||||
tabs.push(errorTab);
|
||||
}
|
||||
return (
|
||||
<ExploreDrawer width={width}>
|
||||
<ExploreDrawer>
|
||||
<TabbedContainer tabs={tabs} onClose={onClose} closeIconTooltip="Close query inspector" />
|
||||
</ExploreDrawer>
|
||||
);
|
||||
@@ -118,6 +116,7 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }
|
||||
|
||||
return {
|
||||
queryResponse,
|
||||
isMixed: item.datasourceInstance?.meta.mixed || false,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -51,10 +51,11 @@ function setup(queries: DataQuery[]) {
|
||||
|
||||
const leftState = makeExplorePaneState();
|
||||
const initialState: ExploreState = {
|
||||
richHistory: [],
|
||||
showQueryHistory: false,
|
||||
panes: {
|
||||
left: {
|
||||
...leftState,
|
||||
richHistory: [],
|
||||
datasourceInstance: datasources['someDs-uid'],
|
||||
queries,
|
||||
correlations: [],
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { TestProvider } from 'test/helpers/TestProvider';
|
||||
|
||||
import { SortOrder } from 'app/core/utils/richHistory';
|
||||
|
||||
import { RichHistory, RichHistoryProps, Tabs } from './RichHistory';
|
||||
|
||||
jest.mock('../state/selectors', () => ({ getExploreDatasources: jest.fn() }));
|
||||
jest.mock('../state/selectors', () => ({ selectExploreDSMaps: jest.fn().mockReturnValue({ dsToExplore: [] }) }));
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
@@ -18,11 +19,14 @@ jest.mock('@grafana/runtime', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('react-use', () => ({
|
||||
...jest.requireActual('react-use'),
|
||||
useAsync: () => ({ loading: false, value: [] }),
|
||||
}));
|
||||
|
||||
const setup = (propOverrides?: Partial<RichHistoryProps>) => {
|
||||
const props: RichHistoryProps = {
|
||||
exploreId: 'left',
|
||||
height: 100,
|
||||
activeDatasourceInstance: 'Test datasource',
|
||||
richHistory: [],
|
||||
richHistoryTotal: 0,
|
||||
firstTab: Tabs.RichHistory,
|
||||
@@ -42,7 +46,7 @@ const setup = (propOverrides?: Partial<RichHistoryProps>) => {
|
||||
richHistorySettings: {
|
||||
retentionPeriod: 0,
|
||||
starredTabAsFirstTab: false,
|
||||
activeDatasourceOnly: true,
|
||||
activeDatasourcesOnly: true,
|
||||
lastUsedDatasourceFilters: [],
|
||||
},
|
||||
updateHistorySearchFilters: jest.fn(),
|
||||
@@ -51,7 +55,11 @@ const setup = (propOverrides?: Partial<RichHistoryProps>) => {
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
||||
render(<RichHistory {...props} />);
|
||||
render(
|
||||
<TestProvider>
|
||||
<RichHistory {...props} />
|
||||
</TestProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('RichHistory', () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { debounce } from 'lodash';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { TabbedContainer, TabConfig } from '@grafana/ui';
|
||||
import { t } from 'app/core/internationalization';
|
||||
import { SortOrder, RichHistorySearchFilters, RichHistorySettings } from 'app/core/utils/richHistory';
|
||||
@@ -33,29 +34,18 @@ export interface RichHistoryProps {
|
||||
richHistorySettings: RichHistorySettings;
|
||||
richHistorySearchFilters?: RichHistorySearchFilters;
|
||||
updateHistorySettings: (settings: RichHistorySettings) => void;
|
||||
updateHistorySearchFilters: (exploreId: string, filters: RichHistorySearchFilters) => void;
|
||||
loadRichHistory: (exploreId: string) => void;
|
||||
loadMoreRichHistory: (exploreId: string) => void;
|
||||
clearRichHistoryResults: (exploreId: string) => void;
|
||||
updateHistorySearchFilters: (filters: RichHistorySearchFilters) => void;
|
||||
loadRichHistory: () => void;
|
||||
loadMoreRichHistory: () => void;
|
||||
clearRichHistoryResults: () => void;
|
||||
deleteRichHistory: () => void;
|
||||
activeDatasourceInstance: string;
|
||||
firstTab: Tabs;
|
||||
exploreId: string;
|
||||
height: number;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function RichHistory(props: RichHistoryProps) {
|
||||
const {
|
||||
richHistory,
|
||||
richHistoryTotal,
|
||||
height,
|
||||
exploreId,
|
||||
deleteRichHistory,
|
||||
onClose,
|
||||
firstTab,
|
||||
activeDatasourceInstance,
|
||||
} = props;
|
||||
const { richHistory, richHistoryTotal, height, deleteRichHistory, onClose, firstTab } = props;
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -69,12 +59,12 @@ export function RichHistory(props: RichHistoryProps) {
|
||||
...filtersToUpdate,
|
||||
page: 1, // always load fresh results when updating filters
|
||||
};
|
||||
props.updateHistorySearchFilters(props.exploreId, filters);
|
||||
props.updateHistorySearchFilters(filters);
|
||||
loadRichHistory();
|
||||
};
|
||||
|
||||
const loadRichHistory = debounce(() => {
|
||||
props.loadRichHistory(props.exploreId);
|
||||
props.loadRichHistory();
|
||||
setLoading(true);
|
||||
}, 300);
|
||||
|
||||
@@ -87,8 +77,8 @@ export function RichHistory(props: RichHistoryProps) {
|
||||
const toggleStarredTabAsFirstTab = () =>
|
||||
updateSettings({ starredTabAsFirstTab: !props.richHistorySettings.starredTabAsFirstTab });
|
||||
|
||||
const toggleActiveDatasourceOnly = () =>
|
||||
updateSettings({ activeDatasourceOnly: !props.richHistorySettings.activeDatasourceOnly });
|
||||
const toggleActiveDatasourcesOnly = () =>
|
||||
updateSettings({ activeDatasourcesOnly: !props.richHistorySettings.activeDatasourcesOnly });
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(false);
|
||||
@@ -103,12 +93,10 @@ export function RichHistory(props: RichHistoryProps) {
|
||||
totalQueries={richHistoryTotal || 0}
|
||||
loading={loading}
|
||||
updateFilters={updateFilters}
|
||||
clearRichHistoryResults={() => props.clearRichHistoryResults(props.exploreId)}
|
||||
loadMoreRichHistory={() => props.loadMoreRichHistory(props.exploreId)}
|
||||
activeDatasourceInstance={activeDatasourceInstance}
|
||||
clearRichHistoryResults={() => props.clearRichHistoryResults()}
|
||||
loadMoreRichHistory={() => props.loadMoreRichHistory()}
|
||||
richHistorySettings={props.richHistorySettings}
|
||||
richHistorySearchFilters={props.richHistorySearchFilters}
|
||||
exploreId={exploreId}
|
||||
height={height}
|
||||
/>
|
||||
),
|
||||
@@ -123,13 +111,11 @@ export function RichHistory(props: RichHistoryProps) {
|
||||
queries={richHistory}
|
||||
totalQueries={richHistoryTotal || 0}
|
||||
loading={loading}
|
||||
activeDatasourceInstance={activeDatasourceInstance}
|
||||
updateFilters={updateFilters}
|
||||
clearRichHistoryResults={() => props.clearRichHistoryResults(props.exploreId)}
|
||||
loadMoreRichHistory={() => props.loadMoreRichHistory(props.exploreId)}
|
||||
clearRichHistoryResults={() => props.clearRichHistoryResults()}
|
||||
loadMoreRichHistory={() => props.loadMoreRichHistory()}
|
||||
richHistorySettings={props.richHistorySettings}
|
||||
richHistorySearchFilters={props.richHistorySearchFilters}
|
||||
exploreId={exploreId}
|
||||
/>
|
||||
),
|
||||
icon: 'star',
|
||||
@@ -142,10 +128,10 @@ export function RichHistory(props: RichHistoryProps) {
|
||||
<RichHistorySettingsTab
|
||||
retentionPeriod={props.richHistorySettings.retentionPeriod}
|
||||
starredTabAsFirstTab={props.richHistorySettings.starredTabAsFirstTab}
|
||||
activeDatasourceOnly={props.richHistorySettings.activeDatasourceOnly}
|
||||
activeDatasourcesOnly={props.richHistorySettings.activeDatasourcesOnly}
|
||||
onChangeRetentionPeriod={onChangeRetentionPeriod}
|
||||
toggleStarredTabAsFirstTab={toggleStarredTabAsFirstTab}
|
||||
toggleactiveDatasourceOnly={toggleActiveDatasourceOnly}
|
||||
toggleActiveDatasourcesOnly={toggleActiveDatasourcesOnly}
|
||||
deleteRichHistory={deleteRichHistory}
|
||||
/>
|
||||
),
|
||||
@@ -159,6 +145,7 @@ export function RichHistory(props: RichHistoryProps) {
|
||||
onClose={onClose}
|
||||
defaultTab={firstTab}
|
||||
closeIconTooltip={t('explore.rich-history.close-tooltip', 'Close query history')}
|
||||
testId={selectors.pages.Explore.QueryHistory.container}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { fireEvent, render, screen, getByText, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { TestProvider } from 'test/helpers/TestProvider';
|
||||
|
||||
import { DataSourceApi, DataSourceInstanceSettings, DataSourcePluginMeta } from '@grafana/data';
|
||||
import { DataQuery, DataSourceRef } from '@grafana/schema';
|
||||
import { MixedDatasource } from 'app/plugins/datasource/mixed/MixedDataSource';
|
||||
import { RichHistoryQuery } from 'app/types';
|
||||
import { configureStore } from 'app/store/configureStore';
|
||||
import { ExploreState, RichHistoryQuery } from 'app/types';
|
||||
import { ShowConfirmModalEvent } from 'app/types/events';
|
||||
|
||||
import { RichHistoryCard, Props } from './RichHistoryCard';
|
||||
@@ -47,7 +49,7 @@ class MockDatasourceApi<T extends DataQuery> implements DataSourceApi<T> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getRef(): DataSourceRef {
|
||||
throw new Error('Method not implemented.');
|
||||
return { uid: this.uid, type: this.type };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +57,7 @@ const dsStore: Record<string, DataSourceApi> = {
|
||||
alertmanager: new MockDatasourceApi('Alertmanager', 3, 'alertmanager', 'alertmanager'),
|
||||
loki: new MockDatasourceApi('Loki', 2, 'loki', 'loki'),
|
||||
prometheus: new MockDatasourceApi<MockQuery>('Prometheus', 1, 'prometheus', 'prometheus', {
|
||||
getQueryDisplayText: (query: MockQuery) => query.queryText || 'Unknwon query',
|
||||
getQueryDisplayText: (query: MockQuery) => query.queryText || 'Unknown query',
|
||||
}),
|
||||
mixed: new MixedDatasource({
|
||||
id: 4,
|
||||
@@ -97,6 +99,7 @@ jest.mock('app/core/utils/explore', () => ({
|
||||
|
||||
jest.mock('app/core/app_events', () => ({
|
||||
publish: jest.fn(),
|
||||
subscribe: jest.fn(),
|
||||
}));
|
||||
|
||||
interface MockQuery extends DataQuery {
|
||||
@@ -124,13 +127,31 @@ const setup = (propOverrides?: Partial<Props<MockQuery>>) => {
|
||||
deleteHistoryItem: deleteRichHistoryMock,
|
||||
commentHistoryItem: jest.fn(),
|
||||
setQueries: jest.fn(),
|
||||
exploreId: 'left',
|
||||
datasourceInstance: dsStore.loki,
|
||||
datasourceInstances: [dsStore.loki],
|
||||
};
|
||||
|
||||
const store = configureStore({
|
||||
explore: {
|
||||
panes: {
|
||||
left: {
|
||||
queries: [{ query: 'query1', refId: 'A' }],
|
||||
datasourceInstance: dsStore.loki,
|
||||
queryResponse: {},
|
||||
range: {
|
||||
raw: { from: 'now-1h', to: 'now' },
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as ExploreState,
|
||||
});
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
||||
render(<RichHistoryCard {...props} />);
|
||||
render(
|
||||
<TestProvider store={store}>
|
||||
<RichHistoryCard {...props} />
|
||||
</TestProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const starredQueryWithComment: RichHistoryQuery<MockQuery> = {
|
||||
@@ -236,8 +257,11 @@ describe('RichHistoryCard', () => {
|
||||
datasourceName: 'Test datasource',
|
||||
starred: false,
|
||||
comment: '',
|
||||
queries: [{ query: 'query1', refId: 'A', queryText: 'query1' }],
|
||||
queries: [
|
||||
{ query: 'query1', refId: 'A', queryText: 'query1', datasource: { uid: 'prometheus', type: 'prometheus' } },
|
||||
],
|
||||
},
|
||||
datasourceInstances: [dsStore.prometheus],
|
||||
});
|
||||
const copyQueriesButton = await screen.findByRole('button', { name: 'Copy query to clipboard' });
|
||||
expect(copyQueriesButton).toBeInTheDocument();
|
||||
@@ -260,6 +284,7 @@ describe('RichHistoryCard', () => {
|
||||
{ query: 'query2', refId: 'B', datasource: { uid: 'loki' } },
|
||||
],
|
||||
},
|
||||
datasourceInstances: [dsStore.loki, dsStore.prometheus, dsStore.mixed],
|
||||
});
|
||||
const copyQueriesButton = await screen.findByRole('button', { name: 'Copy query to clipboard' });
|
||||
expect(copyQueriesButton).toBeInTheDocument();
|
||||
@@ -367,6 +392,7 @@ describe('RichHistoryCard', () => {
|
||||
comment: '',
|
||||
queries,
|
||||
},
|
||||
datasourceInstances: [dsStore.loki, dsStore.prometheus, dsStore.mixed],
|
||||
});
|
||||
|
||||
const runQueryButton = await screen.findByRole('button', { name: /run query/i });
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2, DataSourceApi } from '@grafana/data';
|
||||
import { config, getDataSourceSrv, reportInteraction, getAppEvents } from '@grafana/runtime';
|
||||
import { config, reportInteraction, getAppEvents } from '@grafana/runtime';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import { TextArea, Button, IconButton, useStyles2, LoadingPlaceholder } from '@grafana/ui';
|
||||
import { TextArea, Button, IconButton, useStyles2, ToolbarButton, Dropdown, Menu } from '@grafana/ui';
|
||||
import { notifyApp } from 'app/core/actions';
|
||||
import { createSuccessNotification } from 'app/core/copy/appNotification';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
@@ -17,18 +16,11 @@ import { changeDatasource } from 'app/features/explore/state/datasource';
|
||||
import { starHistoryItem, commentHistoryItem, deleteHistoryItem } from 'app/features/explore/state/history';
|
||||
import { setQueries } from 'app/features/explore/state/query';
|
||||
import { dispatch } from 'app/store/store';
|
||||
import { StoreState } from 'app/types';
|
||||
import { useSelector } from 'app/types';
|
||||
import { ShowConfirmModalEvent } from 'app/types/events';
|
||||
import { RichHistoryQuery } from 'app/types/explore';
|
||||
|
||||
function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }) {
|
||||
const explore = state.explore;
|
||||
const { datasourceInstance } = explore.panes[exploreId]!;
|
||||
return {
|
||||
exploreId,
|
||||
datasourceInstance,
|
||||
};
|
||||
}
|
||||
import { isSplit, selectExploreDSMaps, selectPanesEntries } from '../state/selectors';
|
||||
|
||||
const mapDispatchToProps = {
|
||||
changeDatasource,
|
||||
@@ -38,9 +30,10 @@ const mapDispatchToProps = {
|
||||
setQueries,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
const connector = connect(undefined, mapDispatchToProps);
|
||||
|
||||
interface OwnProps<T extends DataQuery = DataQuery> {
|
||||
datasourceInstances?: DataSourceApi[];
|
||||
queryHistoryItem: RichHistoryQuery<T>;
|
||||
}
|
||||
|
||||
@@ -147,46 +140,29 @@ export function RichHistoryCard(props: Props) {
|
||||
starHistoryItem,
|
||||
deleteHistoryItem,
|
||||
changeDatasource,
|
||||
exploreId,
|
||||
datasourceInstance,
|
||||
setQueries,
|
||||
datasourceInstances,
|
||||
} = props;
|
||||
|
||||
const [activeUpdateComment, setActiveUpdateComment] = useState(false);
|
||||
const [openRunQueryButton, setOpenRunQueryButton] = useState(false);
|
||||
const [comment, setComment] = useState<string | undefined>(queryHistoryItem.comment);
|
||||
const { value: historyCardData, loading } = useAsync(async () => {
|
||||
let datasourceInstance: DataSourceApi | undefined;
|
||||
try {
|
||||
datasourceInstance = await getDataSourceSrv().get(queryHistoryItem.datasourceUid);
|
||||
} catch (e) {}
|
||||
|
||||
return {
|
||||
datasourceInstance,
|
||||
queries: await Promise.all(
|
||||
queryHistoryItem.queries.map(async (query) => {
|
||||
let datasource;
|
||||
if (datasourceInstance?.meta.mixed) {
|
||||
try {
|
||||
datasource = await getDataSourceSrv().get(query.datasource);
|
||||
} catch (e) {}
|
||||
} else {
|
||||
datasource = datasourceInstance;
|
||||
}
|
||||
|
||||
return {
|
||||
query,
|
||||
datasource,
|
||||
};
|
||||
})
|
||||
),
|
||||
};
|
||||
}, [queryHistoryItem.datasourceUid, queryHistoryItem.queries]);
|
||||
const panesEntries = useSelector(selectPanesEntries);
|
||||
const exploreActiveDS = useSelector(selectExploreDSMaps);
|
||||
const isPaneSplit = useSelector(isSplit);
|
||||
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const onRunQuery = async () => {
|
||||
const cardRootDatasource = datasourceInstances
|
||||
? datasourceInstances.find((di) => di.uid === queryHistoryItem.datasourceUid)
|
||||
: undefined;
|
||||
|
||||
const isDifferentDatasource = (uid: string, exploreId: string) =>
|
||||
!exploreActiveDS.dsToExplore.find((di) => di.datasource.uid === uid)?.exploreIds.includes(exploreId);
|
||||
|
||||
const onRunQuery = async (exploreId: string) => {
|
||||
const queriesToRun = queryHistoryItem.queries;
|
||||
const differentDataSource = queryHistoryItem.datasourceUid !== datasourceInstance?.uid;
|
||||
const differentDataSource = isDifferentDatasource(queryHistoryItem.datasourceUid, exploreId);
|
||||
if (differentDataSource) {
|
||||
await changeDatasource({ exploreId, datasource: queryHistoryItem.datasourceUid });
|
||||
}
|
||||
@@ -202,16 +178,16 @@ export function RichHistoryCard(props: Props) {
|
||||
const datasources = [...queryHistoryItem.queries.map((query) => query.datasource?.type || 'unknown')];
|
||||
reportInteraction('grafana_explore_query_history_copy_query', {
|
||||
datasources,
|
||||
mixed: Boolean(historyCardData?.datasourceInstance?.meta.mixed),
|
||||
mixed: Boolean(cardRootDatasource?.meta.mixed),
|
||||
});
|
||||
|
||||
if (loading || !historyCardData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const queriesText = historyCardData.queries
|
||||
const queriesText = queryHistoryItem.queries
|
||||
.map((query) => {
|
||||
return createQueryText(query.query, query.datasource);
|
||||
let queryDS = datasourceInstances?.find((di) => di.uid === queryHistoryItem.datasourceUid);
|
||||
if (queryDS?.meta.mixed) {
|
||||
queryDS = datasourceInstances?.find((di) => di.uid === query.datasource?.uid);
|
||||
}
|
||||
return createQueryText(query, queryDS);
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
@@ -258,7 +234,7 @@ export function RichHistoryCard(props: Props) {
|
||||
}
|
||||
};
|
||||
|
||||
const onStarrQuery = () => {
|
||||
const onStarQuery = () => {
|
||||
starHistoryItem(queryHistoryItem.id, !queryHistoryItem.starred);
|
||||
reportInteraction('grafana_explore_query_history_starred', {
|
||||
queryHistoryEnabled: config.queryHistoryEnabled,
|
||||
@@ -338,7 +314,7 @@ export function RichHistoryCard(props: Props) {
|
||||
onClick={onCopyQuery}
|
||||
tooltip={t('explore.rich-history-card.copy-query-tooltip', 'Copy query to clipboard')}
|
||||
/>
|
||||
{historyCardData?.datasourceInstance && (
|
||||
{cardRootDatasource && (
|
||||
<IconButton
|
||||
name="share-alt"
|
||||
onClick={onCreateShortLink}
|
||||
@@ -358,7 +334,7 @@ export function RichHistoryCard(props: Props) {
|
||||
<IconButton
|
||||
name={queryHistoryItem.starred ? 'favorite' : 'star'}
|
||||
iconType={queryHistoryItem.starred ? 'mono' : 'default'}
|
||||
onClick={onStarrQuery}
|
||||
onClick={onStarQuery}
|
||||
tooltip={
|
||||
queryHistoryItem.starred
|
||||
? t('explore.rich-history-card.unstar-query-tooltip', 'Unstar query')
|
||||
@@ -368,17 +344,86 @@ export function RichHistoryCard(props: Props) {
|
||||
</div>
|
||||
);
|
||||
|
||||
// exploreId on where the query will be ran, and the datasource ID for the item's DS
|
||||
const runQueryText = (exploreId: string, dsUid: string) => {
|
||||
return dsUid !== undefined && isDifferentDatasource(dsUid, exploreId)
|
||||
? {
|
||||
fallbackText: 'Switch data source and run query',
|
||||
translation: t('explore.rich-history-card.switch-datasource-button', 'Switch data source and run query'),
|
||||
}
|
||||
: {
|
||||
fallbackText: 'Run query',
|
||||
translation: t('explore.rich-history-card.run-query-button', 'Run query'),
|
||||
};
|
||||
};
|
||||
|
||||
const runButton = () => {
|
||||
const disabled = cardRootDatasource?.uid === undefined;
|
||||
if (!isPaneSplit) {
|
||||
const exploreId = exploreActiveDS.exploreToDS[0].exploreId;
|
||||
const buttonText = runQueryText(exploreId, props.queryHistoryItem.datasourceUid);
|
||||
return (
|
||||
<Button
|
||||
variant="secondary"
|
||||
aria-label={buttonText.translation}
|
||||
onClick={() => onRunQuery(exploreId)}
|
||||
disabled={disabled}
|
||||
>
|
||||
{buttonText.translation}
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
const menu = (
|
||||
<Menu>
|
||||
{panesEntries.map((pane, i) => {
|
||||
const buttonText = runQueryText(pane[0], props.queryHistoryItem.datasourceUid);
|
||||
const paneLabel =
|
||||
i === 0
|
||||
? t('explore.rich-history-card.left-pane', 'Left pane')
|
||||
: t('explore.rich-history-card.right-pane', 'Right pane');
|
||||
return (
|
||||
<Menu.Item
|
||||
key={i}
|
||||
ariaLabel={buttonText.fallbackText}
|
||||
onClick={() => {
|
||||
onRunQuery(pane[0]);
|
||||
}}
|
||||
label={`${paneLabel}: ${buttonText.translation}`}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dropdown onVisibleChange={(state) => setOpenRunQueryButton(state)} placement="bottom-start" overlay={menu}>
|
||||
<ToolbarButton aria-label="run query options" variant="canvas" isOpen={openRunQueryButton}>
|
||||
{t('explore.rich-history-card.run-query-button', 'Run query')}
|
||||
</ToolbarButton>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.queryCard}>
|
||||
<div className={styles.cardRow}>
|
||||
<DatasourceInfo dsApi={historyCardData?.datasourceInstance} size="sm" />
|
||||
<DatasourceInfo dsApi={cardRootDatasource} size="sm" />
|
||||
|
||||
{queryActionButtons}
|
||||
</div>
|
||||
<div className={cx(styles.cardRow)}>
|
||||
<div className={styles.queryContainer}>
|
||||
{historyCardData?.queries.map((q, i) => {
|
||||
return <Query query={q} key={`${q}-${i}`} showDsInfo={historyCardData?.datasourceInstance?.meta.mixed} />;
|
||||
{queryHistoryItem?.queries.map((q, i) => {
|
||||
const queryDs = datasourceInstances?.find((ds) => ds.uid === q.datasource?.uid);
|
||||
return (
|
||||
<Query
|
||||
query={{ query: q, datasource: queryDs }}
|
||||
key={`${q}-${i}`}
|
||||
showDsInfo={cardRootDatasource?.meta.mixed}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{!activeUpdateComment && queryHistoryItem.comment && (
|
||||
<div
|
||||
@@ -390,32 +435,8 @@ export function RichHistoryCard(props: Props) {
|
||||
)}
|
||||
{activeUpdateComment && updateComment}
|
||||
</div>
|
||||
{!activeUpdateComment && (
|
||||
<div className={styles.runButton}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={onRunQuery}
|
||||
disabled={
|
||||
!historyCardData?.datasourceInstance || historyCardData.queries.some((query) => !query.datasource)
|
||||
}
|
||||
>
|
||||
{datasourceInstance?.uid === queryHistoryItem.datasourceUid ? (
|
||||
<Trans i18nKey="explore.rich-history-card.run-query-button">Run query</Trans>
|
||||
) : (
|
||||
<Trans i18nKey="explore.rich-history-card.switch-datasource-button">
|
||||
Switch data source and run query
|
||||
</Trans>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{!activeUpdateComment && <div className={styles.runButton}>{runButton()}</div>}
|
||||
</div>
|
||||
{loading && (
|
||||
<LoadingPlaceholder
|
||||
text={t('explore.rich-history-card.loading-text', 'loading...')}
|
||||
className={styles.loader}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { TestProvider } from 'test/helpers/TestProvider';
|
||||
|
||||
import { SortOrder } from 'app/core/utils/richHistory';
|
||||
|
||||
import { Tabs } from './RichHistory';
|
||||
import { RichHistoryContainer, Props } from './RichHistoryContainer';
|
||||
|
||||
jest.mock('../state/selectors', () => ({ getExploreDatasources: jest.fn() }));
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getDataSourceSrv: () => {
|
||||
@@ -18,11 +17,15 @@ jest.mock('@grafana/runtime', () => ({
|
||||
reportInteraction: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('react-use', () => ({
|
||||
...jest.requireActual('react-use'),
|
||||
useAsync: () => ({ loading: false, value: [] }),
|
||||
}));
|
||||
|
||||
jest.mock('../state/selectors', () => ({ selectExploreDSMaps: jest.fn().mockReturnValue({ dsToExplore: [] }) }));
|
||||
|
||||
const setup = (propOverrides?: Partial<Props>) => {
|
||||
const props: Props = {
|
||||
width: 500,
|
||||
exploreId: 'left',
|
||||
activeDatasourceInstance: 'Test datasource',
|
||||
richHistory: [],
|
||||
firstTab: Tabs.RichHistory,
|
||||
deleteRichHistory: jest.fn(),
|
||||
@@ -44,7 +47,7 @@ const setup = (propOverrides?: Partial<Props>) => {
|
||||
richHistorySettings: {
|
||||
retentionPeriod: 0,
|
||||
starredTabAsFirstTab: false,
|
||||
activeDatasourceOnly: true,
|
||||
activeDatasourcesOnly: true,
|
||||
lastUsedDatasourceFilters: [],
|
||||
},
|
||||
richHistoryTotal: 0,
|
||||
@@ -52,7 +55,7 @@ const setup = (propOverrides?: Partial<Props>) => {
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
||||
return render(<RichHistoryContainer {...props} />);
|
||||
return render(<RichHistoryContainer {...props} />, { wrapper: TestProvider });
|
||||
};
|
||||
|
||||
describe('RichHistoryContainer', () => {
|
||||
@@ -60,23 +63,15 @@ describe('RichHistoryContainer', () => {
|
||||
const { container } = setup({ richHistorySettings: undefined });
|
||||
expect(container).toHaveTextContent('Loading...');
|
||||
});
|
||||
it('should render component with correct width', () => {
|
||||
const { container } = setup();
|
||||
expect(container.firstElementChild!.getAttribute('style')).toContain('width: 531.5px');
|
||||
});
|
||||
it('should render component with correct height', () => {
|
||||
const { container } = setup();
|
||||
expect(container.firstElementChild!.getAttribute('style')).toContain('height: 400px');
|
||||
});
|
||||
it('should re-request rich history every time the component is mounted', () => {
|
||||
const initRichHistory = jest.fn();
|
||||
const { unmount } = setup({ initRichHistory });
|
||||
expect(initRichHistory).toBeCalledTimes(1);
|
||||
expect(initRichHistory).toHaveBeenCalledTimes(1);
|
||||
|
||||
unmount();
|
||||
expect(initRichHistory).toBeCalledTimes(1);
|
||||
expect(initRichHistory).toHaveBeenCalledTimes(1);
|
||||
|
||||
setup({ initRichHistory });
|
||||
expect(initRichHistory).toBeCalledTimes(2);
|
||||
expect(initRichHistory).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
// Libraries
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
import { useTheme2 } from '@grafana/ui';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
// Types
|
||||
import { ExploreItemState, StoreState } from 'app/types';
|
||||
import { StoreState } from 'app/types';
|
||||
|
||||
// Components, enums
|
||||
import { ExploreDrawer } from '../ExploreDrawer';
|
||||
import {
|
||||
deleteRichHistory,
|
||||
initRichHistory,
|
||||
@@ -24,19 +23,16 @@ import { RichHistory, Tabs } from './RichHistory';
|
||||
|
||||
//Actions
|
||||
|
||||
function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }) {
|
||||
function mapStateToProps(state: StoreState) {
|
||||
const explore = state.explore;
|
||||
const item: ExploreItemState = explore.panes[exploreId]!;
|
||||
const richHistorySearchFilters = item.richHistorySearchFilters;
|
||||
const richHistorySettings = explore.richHistorySettings;
|
||||
const { datasourceInstance } = item;
|
||||
const richHistorySearchFilters = explore.richHistorySearchFilters;
|
||||
const { richHistorySettings, richHistory, richHistoryTotal } = explore;
|
||||
|
||||
const firstTab = richHistorySettings?.starredTabAsFirstTab ? Tabs.Starred : Tabs.RichHistory;
|
||||
const { richHistory, richHistoryTotal } = item;
|
||||
return {
|
||||
richHistory,
|
||||
richHistoryTotal,
|
||||
firstTab,
|
||||
activeDatasourceInstance: datasourceInstance!.name,
|
||||
richHistorySettings,
|
||||
richHistorySearchFilters,
|
||||
};
|
||||
@@ -55,23 +51,17 @@ const mapDispatchToProps = {
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
||||
interface OwnProps {
|
||||
width: number;
|
||||
exploreId: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
export type Props = ConnectedProps<typeof connector> & OwnProps;
|
||||
|
||||
export function RichHistoryContainer(props: Props) {
|
||||
const theme = useTheme2();
|
||||
const [height, setHeight] = useState(theme.components.horizontalDrawer.defaultHeight);
|
||||
|
||||
const {
|
||||
richHistory,
|
||||
richHistoryTotal,
|
||||
width,
|
||||
firstTab,
|
||||
activeDatasourceInstance,
|
||||
exploreId,
|
||||
deleteRichHistory,
|
||||
initRichHistory,
|
||||
loadRichHistory,
|
||||
@@ -100,30 +90,21 @@ export function RichHistoryContainer(props: Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<ExploreDrawer
|
||||
width={width}
|
||||
onResize={(_e, _dir, ref) => {
|
||||
setHeight(Number(ref.style.height.slice(0, -2)));
|
||||
}}
|
||||
>
|
||||
<RichHistory
|
||||
richHistory={richHistory}
|
||||
richHistoryTotal={richHistoryTotal}
|
||||
firstTab={firstTab}
|
||||
activeDatasourceInstance={activeDatasourceInstance}
|
||||
exploreId={exploreId}
|
||||
onClose={onClose}
|
||||
height={height}
|
||||
deleteRichHistory={deleteRichHistory}
|
||||
richHistorySettings={richHistorySettings}
|
||||
richHistorySearchFilters={richHistorySearchFilters}
|
||||
updateHistorySettings={updateHistorySettings}
|
||||
updateHistorySearchFilters={updateHistorySearchFilters}
|
||||
loadRichHistory={loadRichHistory}
|
||||
loadMoreRichHistory={loadMoreRichHistory}
|
||||
clearRichHistoryResults={clearRichHistoryResults}
|
||||
/>
|
||||
</ExploreDrawer>
|
||||
<RichHistory
|
||||
richHistory={richHistory}
|
||||
richHistoryTotal={richHistoryTotal}
|
||||
firstTab={firstTab}
|
||||
onClose={onClose}
|
||||
height={theme.components.horizontalDrawer.defaultHeight}
|
||||
deleteRichHistory={deleteRichHistory}
|
||||
richHistorySettings={richHistorySettings}
|
||||
richHistorySearchFilters={richHistorySearchFilters}
|
||||
updateHistorySettings={updateHistorySettings}
|
||||
updateHistorySearchFilters={updateHistorySearchFilters}
|
||||
loadRichHistory={loadRichHistory}
|
||||
loadMoreRichHistory={loadMoreRichHistory}
|
||||
clearRichHistoryResults={clearRichHistoryResults}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import * as reactUse from 'react-use';
|
||||
import { TestProvider } from 'test/helpers/TestProvider';
|
||||
import { MockDataSourceApi } from 'test/mocks/datasource_srv';
|
||||
|
||||
import { DataSourceSrv, setDataSourceSrv } from '@grafana/runtime';
|
||||
import { SortOrder } from 'app/core/utils/richHistoryTypes';
|
||||
|
||||
import { RichHistoryQueriesTab, RichHistoryQueriesTabProps } from './RichHistoryQueriesTab';
|
||||
|
||||
const asyncSpy = jest
|
||||
.spyOn(reactUse, 'useAsync')
|
||||
.mockReturnValue({ loading: false, value: [new MockDataSourceApi('test-ds')] });
|
||||
|
||||
const setup = (propOverrides?: Partial<RichHistoryQueriesTabProps>) => {
|
||||
const props: RichHistoryQueriesTabProps = {
|
||||
queries: [],
|
||||
totalQueries: 0,
|
||||
loading: false,
|
||||
activeDatasourceInstance: 'test-ds',
|
||||
updateFilters: jest.fn(),
|
||||
clearRichHistoryResults: jest.fn(),
|
||||
loadMoreRichHistory: jest.fn(),
|
||||
@@ -25,39 +31,52 @@ const setup = (propOverrides?: Partial<RichHistoryQueriesTabProps>) => {
|
||||
},
|
||||
richHistorySettings: {
|
||||
retentionPeriod: 30,
|
||||
activeDatasourceOnly: false,
|
||||
lastUsedDatasourceFilters: [],
|
||||
activeDatasourcesOnly: false,
|
||||
lastUsedDatasourceFilters: ['test-ds'],
|
||||
starredTabAsFirstTab: false,
|
||||
},
|
||||
exploreId: 'left',
|
||||
height: 100,
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
||||
return render(<RichHistoryQueriesTab {...props} />);
|
||||
return render(<RichHistoryQueriesTab {...props} />, { wrapper: TestProvider });
|
||||
};
|
||||
|
||||
describe('RichHistoryQueriesTab', () => {
|
||||
beforeAll(() => {
|
||||
const testDS = new MockDataSourceApi('test-ds');
|
||||
setDataSourceSrv({
|
||||
getList() {
|
||||
return [];
|
||||
return [testDS];
|
||||
},
|
||||
} as unknown as DataSourceSrv);
|
||||
});
|
||||
|
||||
it('should render', () => {
|
||||
afterEach(() => {
|
||||
asyncSpy.mockClear();
|
||||
});
|
||||
it('should render', async () => {
|
||||
setup();
|
||||
expect(screen.queryByText('Filter history')).toBeInTheDocument();
|
||||
const filterHistory = await screen.findByText('Filter history');
|
||||
expect(filterHistory).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not regex escape filter input', () => {
|
||||
it('should not regex escape filter input', async () => {
|
||||
const updateFiltersSpy = jest.fn();
|
||||
setup({ updateFilters: updateFiltersSpy });
|
||||
const input = screen.getByPlaceholderText(/search queries/i);
|
||||
const input = await screen.findByPlaceholderText(/search queries/i);
|
||||
fireEvent.change(input, { target: { value: '|=' } });
|
||||
|
||||
expect(updateFiltersSpy).toHaveBeenCalledWith(expect.objectContaining({ search: '|=' }));
|
||||
});
|
||||
|
||||
it('should update the filter and get data once on mount, and update the filter when the it changes', async () => {
|
||||
const updateFiltersSpy = jest.fn();
|
||||
setup({ updateFilters: updateFiltersSpy });
|
||||
expect(updateFiltersSpy).toHaveBeenCalledTimes(1);
|
||||
expect(asyncSpy).toHaveBeenCalledTimes(1);
|
||||
const input = await screen.findByLabelText(/remove/i);
|
||||
fireEvent.click(input);
|
||||
expect(updateFiltersSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { DataSourceApi, GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
import { config, getDataSourceSrv } from '@grafana/runtime';
|
||||
import { Button, FilterInput, MultiSelect, RangeSlider, Select, useStyles2 } from '@grafana/ui';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
import {
|
||||
@@ -13,8 +14,11 @@ import {
|
||||
RichHistorySearchFilters,
|
||||
RichHistorySettings,
|
||||
} from 'app/core/utils/richHistory';
|
||||
import { useSelector } from 'app/types';
|
||||
import { RichHistoryQuery } from 'app/types/explore';
|
||||
|
||||
import { selectExploreDSMaps } from '../state/selectors';
|
||||
|
||||
import { getSortOrderOptions } from './RichHistory';
|
||||
import RichHistoryCard from './RichHistoryCard';
|
||||
|
||||
@@ -22,13 +26,11 @@ export interface RichHistoryQueriesTabProps {
|
||||
queries: RichHistoryQuery[];
|
||||
totalQueries: number;
|
||||
loading: boolean;
|
||||
activeDatasourceInstance: string;
|
||||
updateFilters: (filtersToUpdate?: Partial<RichHistorySearchFilters>) => void;
|
||||
clearRichHistoryResults: () => void;
|
||||
loadMoreRichHistory: () => void;
|
||||
richHistorySettings: RichHistorySettings;
|
||||
richHistorySearchFilters?: RichHistorySearchFilters;
|
||||
exploreId: string;
|
||||
height: number;
|
||||
}
|
||||
|
||||
@@ -122,20 +124,22 @@ export function RichHistoryQueriesTab(props: RichHistoryQueriesTabProps) {
|
||||
clearRichHistoryResults,
|
||||
loadMoreRichHistory,
|
||||
richHistorySettings,
|
||||
exploreId,
|
||||
height,
|
||||
activeDatasourceInstance,
|
||||
} = props;
|
||||
|
||||
const exploreActiveDS = useSelector(selectExploreDSMaps);
|
||||
const styles = useStyles2(getStyles, height);
|
||||
|
||||
const listOfDatasources = createDatasourcesList();
|
||||
|
||||
// on mount, set filter to either active datasource or all datasources
|
||||
useEffect(() => {
|
||||
const datasourceFilters =
|
||||
!richHistorySettings.activeDatasourceOnly && richHistorySettings.lastUsedDatasourceFilters
|
||||
!richHistorySettings.activeDatasourcesOnly && richHistorySettings.lastUsedDatasourceFilters
|
||||
? richHistorySettings.lastUsedDatasourceFilters
|
||||
: [activeDatasourceInstance];
|
||||
: exploreActiveDS.dsToExplore
|
||||
.map((eDs) => listOfDatasources.find((ds) => ds.uid === eDs.datasource?.uid)?.name)
|
||||
.filter((name): name is string => !!name);
|
||||
const filters: RichHistorySearchFilters = {
|
||||
search: '',
|
||||
sortOrder: SortOrder.Descending,
|
||||
@@ -152,10 +156,33 @@ export function RichHistoryQueriesTab(props: RichHistoryQueriesTabProps) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// whenever the filter changes, get all datasource information for the filtered datasources
|
||||
const { value: datasourceFilterApis, loading: loadingDs } = useAsync(async () => {
|
||||
const datasourcesToGet =
|
||||
richHistorySearchFilters?.datasourceFilters && richHistorySearchFilters?.datasourceFilters.length > 0
|
||||
? richHistorySearchFilters?.datasourceFilters
|
||||
: listOfDatasources.map((ds) => ds.uid);
|
||||
const dsGetProm = await datasourcesToGet.map(async (dsf) => {
|
||||
try {
|
||||
// this get works off datasource names
|
||||
return getDataSourceSrv().get(dsf);
|
||||
} catch (e) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
if (dsGetProm !== undefined) {
|
||||
const enhancedDatasourceData = (await Promise.all(dsGetProm)).filter((dsi): dsi is DataSourceApi => !!dsi);
|
||||
return enhancedDatasourceData;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}, [richHistorySearchFilters?.datasourceFilters]);
|
||||
|
||||
if (!richHistorySearchFilters) {
|
||||
return (
|
||||
<span>
|
||||
<Trans i18nKey="explore.rich-history-queries-tab.loading">Loading...</Trans>;
|
||||
<Trans i18nKey="explore.rich-history-queries-tab.loading">Loading...</Trans>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -199,7 +226,7 @@ export function RichHistoryQueriesTab(props: RichHistoryQueriesTabProps) {
|
||||
|
||||
<div className={styles.containerContent} data-testid="query-history-queries-tab">
|
||||
<div className={styles.selectors}>
|
||||
{!richHistorySettings.activeDatasourceOnly && (
|
||||
{!richHistorySettings.activeDatasourcesOnly && (
|
||||
<MultiSelect
|
||||
className={styles.multiselect}
|
||||
options={listOfDatasources.map((ds) => {
|
||||
@@ -237,13 +264,13 @@ export function RichHistoryQueriesTab(props: RichHistoryQueriesTabProps) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{loading && (
|
||||
{(loading || loadingDs) && (
|
||||
<span>
|
||||
<Trans i18nKey="explore.rich-history-queries-tab.loading-results">Loading results...</Trans>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{!loading &&
|
||||
{!(loading || loadingDs) &&
|
||||
Object.keys(mappedQueriesToHeadings).map((heading) => {
|
||||
return (
|
||||
<div key={heading}>
|
||||
@@ -266,7 +293,7 @@ export function RichHistoryQueriesTab(props: RichHistoryQueriesTabProps) {
|
||||
</span>
|
||||
</div>
|
||||
{mappedQueriesToHeadings[heading].map((q) => {
|
||||
return <RichHistoryCard queryHistoryItem={q} key={q.id} exploreId={exploreId} />;
|
||||
return <RichHistoryCard datasourceInstances={datasourceFilterApis} queryHistoryItem={q} key={q.id} />;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -7,10 +7,10 @@ const setup = (propOverrides?: Partial<RichHistorySettingsProps>) => {
|
||||
const props: RichHistorySettingsProps = {
|
||||
retentionPeriod: 14,
|
||||
starredTabAsFirstTab: true,
|
||||
activeDatasourceOnly: false,
|
||||
activeDatasourcesOnly: false,
|
||||
onChangeRetentionPeriod: jest.fn(),
|
||||
toggleStarredTabAsFirstTab: jest.fn(),
|
||||
toggleactiveDatasourceOnly: jest.fn(),
|
||||
toggleActiveDatasourcesOnly: jest.fn(),
|
||||
deleteRichHistory: jest.fn(),
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ describe('RichHistorySettings', () => {
|
||||
setup();
|
||||
expect(screen.queryByText('2 weeks')).toBeInTheDocument();
|
||||
});
|
||||
it('should render component with correctly checked starredTabAsFirstTab and uncheched toggleActiveDatasourceOnly settings', () => {
|
||||
it('should render component with correctly checked starredTabAsFirstTab and uncheched toggleactiveDatasourcesOnly settings', () => {
|
||||
setup();
|
||||
const checkboxes = screen.getAllByRole('checkbox');
|
||||
expect(checkboxes.length).toBe(2);
|
||||
|
||||
@@ -16,10 +16,10 @@ import { ShowConfirmModalEvent } from '../../../types/events';
|
||||
export interface RichHistorySettingsProps {
|
||||
retentionPeriod: number;
|
||||
starredTabAsFirstTab: boolean;
|
||||
activeDatasourceOnly: boolean;
|
||||
activeDatasourcesOnly: boolean;
|
||||
onChangeRetentionPeriod: (option: SelectableValue<number>) => void;
|
||||
toggleStarredTabAsFirstTab: () => void;
|
||||
toggleactiveDatasourceOnly: () => void;
|
||||
toggleActiveDatasourcesOnly: () => void;
|
||||
deleteRichHistory: () => void;
|
||||
}
|
||||
|
||||
@@ -54,10 +54,10 @@ export function RichHistorySettingsTab(props: RichHistorySettingsProps) {
|
||||
const {
|
||||
retentionPeriod,
|
||||
starredTabAsFirstTab,
|
||||
activeDatasourceOnly,
|
||||
activeDatasourcesOnly,
|
||||
onChangeRetentionPeriod,
|
||||
toggleStarredTabAsFirstTab,
|
||||
toggleactiveDatasourceOnly,
|
||||
toggleActiveDatasourcesOnly,
|
||||
deleteRichHistory,
|
||||
} = props;
|
||||
const styles = useStyles2(getStyles);
|
||||
@@ -136,8 +136,8 @@ export function RichHistorySettingsTab(props: RichHistorySettingsProps) {
|
||||
>
|
||||
<InlineSwitch
|
||||
id="explore-query-history-settings-data-source-behavior"
|
||||
value={activeDatasourceOnly}
|
||||
onChange={toggleactiveDatasourceOnly}
|
||||
value={activeDatasourcesOnly}
|
||||
onChange={toggleActiveDatasourcesOnly}
|
||||
/>
|
||||
</InlineField>
|
||||
)}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { TestProvider } from 'test/helpers/TestProvider';
|
||||
|
||||
import { SortOrder } from 'app/core/utils/richHistory';
|
||||
|
||||
import { RichHistoryStarredTab, RichHistoryStarredTabProps } from './RichHistoryStarredTab';
|
||||
|
||||
jest.mock('../state/selectors', () => ({ getExploreDatasources: jest.fn() }));
|
||||
jest.mock('../state/selectors', () => ({ selectExploreDSMaps: jest.fn().mockReturnValue({ dsToExplore: [] }) }));
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
@@ -21,15 +22,13 @@ const setup = (propOverrides?: Partial<RichHistoryStarredTabProps>) => {
|
||||
queries: [],
|
||||
loading: false,
|
||||
totalQueries: 0,
|
||||
activeDatasourceInstance: '',
|
||||
updateFilters: jest.fn(),
|
||||
loadMoreRichHistory: jest.fn(),
|
||||
clearRichHistoryResults: jest.fn(),
|
||||
exploreId: 'left',
|
||||
richHistorySettings: {
|
||||
retentionPeriod: 7,
|
||||
starredTabAsFirstTab: false,
|
||||
activeDatasourceOnly: false,
|
||||
activeDatasourcesOnly: false,
|
||||
lastUsedDatasourceFilters: [],
|
||||
},
|
||||
richHistorySearchFilters: {
|
||||
@@ -44,41 +43,46 @@ const setup = (propOverrides?: Partial<RichHistoryStarredTabProps>) => {
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
||||
const container = render(<RichHistoryStarredTab {...props} />);
|
||||
const container = render(<RichHistoryStarredTab {...props} />, { wrapper: TestProvider });
|
||||
return container;
|
||||
};
|
||||
|
||||
describe('RichHistoryStarredTab', () => {
|
||||
describe('sorter', () => {
|
||||
it('should render sorter', () => {
|
||||
it('should render sorter', async () => {
|
||||
const container = setup();
|
||||
expect(container.queryByLabelText('Sort queries')).toBeInTheDocument();
|
||||
const sortText = await container.findByLabelText('Sort queries');
|
||||
expect(sortText).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('select datasource', () => {
|
||||
it('should render select datasource if activeDatasourceOnly is false', () => {
|
||||
it('should render select datasource if activeDatasourcesOnly is false', async () => {
|
||||
const container = setup();
|
||||
expect(container.queryByLabelText('Filter queries for data sources(s)')).toBeInTheDocument();
|
||||
const filterText = await container.findByLabelText('Filter queries for data sources(s)');
|
||||
expect(filterText).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render select datasource if activeDatasourceOnly is true', () => {
|
||||
it('should not render select datasource if activeDatasourcesOnly is true', async () => {
|
||||
const container = setup({
|
||||
richHistorySettings: {
|
||||
retentionPeriod: 7,
|
||||
starredTabAsFirstTab: false,
|
||||
activeDatasourceOnly: true,
|
||||
activeDatasourcesOnly: true,
|
||||
lastUsedDatasourceFilters: [],
|
||||
},
|
||||
});
|
||||
expect(container.queryByLabelText('Filter queries for data sources(s)')).not.toBeInTheDocument();
|
||||
// trying to wait for placeholder text to render before proceeding does not work
|
||||
await container.findByPlaceholderText(/search queries/i);
|
||||
const filterText = container.queryByLabelText('Filter queries for data sources(s)');
|
||||
expect(filterText).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not regex escape filter input', () => {
|
||||
it('should not regex escape filter input', async () => {
|
||||
const updateFiltersSpy = jest.fn();
|
||||
setup({ updateFilters: updateFiltersSpy });
|
||||
const input = screen.getByPlaceholderText(/search queries/i);
|
||||
const input = await screen.findByPlaceholderText(/search queries/i);
|
||||
fireEvent.change(input, { target: { value: '|=' } });
|
||||
|
||||
expect(updateFiltersSpy).toHaveBeenCalledWith(expect.objectContaining({ search: '|=' }));
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { DataSourceApi, GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
import { config, getDataSourceSrv } from '@grafana/runtime';
|
||||
import { useStyles2, Select, MultiSelect, FilterInput, Button } from '@grafana/ui';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
import {
|
||||
@@ -11,8 +12,11 @@ import {
|
||||
RichHistorySearchFilters,
|
||||
RichHistorySettings,
|
||||
} from 'app/core/utils/richHistory';
|
||||
import { useSelector } from 'app/types';
|
||||
import { RichHistoryQuery } from 'app/types/explore';
|
||||
|
||||
import { selectExploreDSMaps } from '../state/selectors';
|
||||
|
||||
import { getSortOrderOptions } from './RichHistory';
|
||||
import RichHistoryCard from './RichHistoryCard';
|
||||
|
||||
@@ -20,13 +24,11 @@ export interface RichHistoryStarredTabProps {
|
||||
queries: RichHistoryQuery[];
|
||||
totalQueries: number;
|
||||
loading: boolean;
|
||||
activeDatasourceInstance: string;
|
||||
updateFilters: (filtersToUpdate: Partial<RichHistorySearchFilters>) => void;
|
||||
clearRichHistoryResults: () => void;
|
||||
loadMoreRichHistory: () => void;
|
||||
richHistorySearchFilters?: RichHistorySearchFilters;
|
||||
richHistorySettings: RichHistorySettings;
|
||||
exploreId: string;
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
@@ -72,24 +74,25 @@ export function RichHistoryStarredTab(props: RichHistoryStarredTabProps) {
|
||||
updateFilters,
|
||||
clearRichHistoryResults,
|
||||
loadMoreRichHistory,
|
||||
activeDatasourceInstance,
|
||||
richHistorySettings,
|
||||
queries,
|
||||
totalQueries,
|
||||
loading,
|
||||
richHistorySearchFilters,
|
||||
exploreId,
|
||||
} = props;
|
||||
|
||||
const styles = useStyles2(getStyles);
|
||||
const exploreActiveDS = useSelector(selectExploreDSMaps);
|
||||
|
||||
const listOfDatasources = createDatasourcesList();
|
||||
|
||||
useEffect(() => {
|
||||
const datasourceFilters =
|
||||
richHistorySettings.activeDatasourceOnly && richHistorySettings.lastUsedDatasourceFilters
|
||||
richHistorySettings.activeDatasourcesOnly && richHistorySettings.lastUsedDatasourceFilters
|
||||
? richHistorySettings.lastUsedDatasourceFilters
|
||||
: [activeDatasourceInstance];
|
||||
: exploreActiveDS.dsToExplore
|
||||
.map((eDs) => listOfDatasources.find((ds) => ds.uid === eDs.datasource?.uid)?.name)
|
||||
.filter((name): name is string => !!name);
|
||||
const filters: RichHistorySearchFilters = {
|
||||
search: '',
|
||||
sortOrder: SortOrder.Descending,
|
||||
@@ -105,6 +108,29 @@ export function RichHistoryStarredTab(props: RichHistoryStarredTabProps) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const { value: datasourceFilterApis, loading: loadingDs } = useAsync(async () => {
|
||||
const datasourcesToGet =
|
||||
richHistorySearchFilters?.datasourceFilters && richHistorySearchFilters?.datasourceFilters.length > 0
|
||||
? richHistorySearchFilters?.datasourceFilters
|
||||
: listOfDatasources.map((ds) => ds.uid);
|
||||
const dsGetProm = await datasourcesToGet.map(async (dsf) => {
|
||||
try {
|
||||
// this get works off datasource names
|
||||
return getDataSourceSrv().get(dsf);
|
||||
} catch (e) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
if (dsGetProm !== undefined) {
|
||||
const enhancedDatasourceData = (await Promise.all(dsGetProm)).filter((dsi): dsi is DataSourceApi => !!dsi);
|
||||
//setDatasourceFilterApiList(enhancedDatasourceData)
|
||||
return enhancedDatasourceData;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}, [richHistorySearchFilters?.datasourceFilters]);
|
||||
|
||||
if (!richHistorySearchFilters) {
|
||||
return (
|
||||
<span>
|
||||
@@ -119,7 +145,7 @@ export function RichHistoryStarredTab(props: RichHistoryStarredTabProps) {
|
||||
<div className={styles.container}>
|
||||
<div className={styles.containerContent}>
|
||||
<div className={styles.selectors}>
|
||||
{!richHistorySettings.activeDatasourceOnly && (
|
||||
{!richHistorySettings.activeDatasourcesOnly && (
|
||||
<MultiSelect
|
||||
className={styles.multiselect}
|
||||
options={listOfDatasources.map((ds) => {
|
||||
@@ -159,14 +185,14 @@ export function RichHistoryStarredTab(props: RichHistoryStarredTabProps) {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{loading && (
|
||||
{loading && loadingDs && (
|
||||
<span>
|
||||
<Trans i18nKey="explore.rich-history-starred-tab.loading-results">Loading results...</Trans>
|
||||
</span>
|
||||
)}
|
||||
{!loading &&
|
||||
{!(loading && loadingDs) &&
|
||||
queries.map((q) => {
|
||||
return <RichHistoryCard queryHistoryItem={q} key={q.id} exploreId={exploreId} />;
|
||||
return <RichHistoryCard queryHistoryItem={q} key={q.id} datasourceInstances={datasourceFilterApis} />;
|
||||
})}
|
||||
{queries.length && queries.length !== totalQueries ? (
|
||||
<div>
|
||||
|
||||
@@ -1,26 +1,18 @@
|
||||
import { waitFor } from '@testing-library/react';
|
||||
|
||||
import { getAllByRoleInQueryHistoryTab, withinExplore } from './setup';
|
||||
import { withinQueryHistory } from './setup';
|
||||
|
||||
export const assertQueryHistoryExists = async (query: string, exploreId = 'left') => {
|
||||
const selector = withinExplore(exploreId);
|
||||
export const assertQueryHistoryExists = async (query: string) => {
|
||||
const selector = withinQueryHistory();
|
||||
|
||||
expect(await selector.findByText('1 queries')).toBeInTheDocument();
|
||||
const queryItem = selector.getByLabelText('Query text');
|
||||
expect(queryItem).toHaveTextContent(query);
|
||||
};
|
||||
|
||||
export const assertQueryHistoryContains = async (query: string, exploreId = 'left') => {
|
||||
const selector = withinExplore(exploreId);
|
||||
export const assertQueryHistory = async (expectedQueryTexts: string[]) => {
|
||||
const selector = withinQueryHistory();
|
||||
|
||||
await waitFor(() => {
|
||||
const containsQuery = selector.getAllByLabelText('Query text').map((e) => (e.textContent || '').includes(query));
|
||||
expect(containsQuery).toContain(true);
|
||||
});
|
||||
};
|
||||
|
||||
export const assertQueryHistory = async (expectedQueryTexts: string[], exploreId = 'left') => {
|
||||
const selector = withinExplore(exploreId);
|
||||
await waitFor(() => {
|
||||
expect(selector.getByText(new RegExp(`${expectedQueryTexts.length} queries`))).toBeInTheDocument();
|
||||
const queryTexts = selector.getAllByLabelText('Query text');
|
||||
@@ -30,15 +22,15 @@ export const assertQueryHistory = async (expectedQueryTexts: string[], exploreId
|
||||
});
|
||||
};
|
||||
|
||||
export const assertQueryHistoryIsEmpty = async (exploreId = 'left') => {
|
||||
const selector = withinExplore(exploreId);
|
||||
export const assertQueryHistoryIsEmpty = async () => {
|
||||
const selector = withinQueryHistory();
|
||||
const queryTexts = selector.queryAllByLabelText('Query text');
|
||||
|
||||
expect(await queryTexts).toHaveLength(0);
|
||||
};
|
||||
|
||||
export const assertQueryHistoryComment = async (expectedQueryComments: string[], exploreId = 'left') => {
|
||||
const selector = withinExplore(exploreId);
|
||||
export const assertQueryHistoryComment = async (expectedQueryComments: string[]) => {
|
||||
const selector = withinQueryHistory();
|
||||
await waitFor(() => {
|
||||
expect(selector.getByText(new RegExp(`${expectedQueryComments.length} queries`))).toBeInTheDocument();
|
||||
const queryComments = selector.getAllByLabelText('Query comment');
|
||||
@@ -48,25 +40,12 @@ export const assertQueryHistoryComment = async (expectedQueryComments: string[],
|
||||
});
|
||||
};
|
||||
|
||||
export const assertQueryHistoryIsStarred = async (expectedStars: boolean[], exploreId = 'left') => {
|
||||
const starButtons = getAllByRoleInQueryHistoryTab(exploreId, 'button', /Star query|Unstar query/);
|
||||
|
||||
await waitFor(() =>
|
||||
expectedStars.forEach((starred, queryIndex) => {
|
||||
expect(starButtons[queryIndex]).toHaveAccessibleName(starred ? 'Unstar query' : 'Star query');
|
||||
})
|
||||
);
|
||||
export const assertQueryHistoryTabIsSelected = (tabName: 'Query history' | 'Starred' | 'Settings') => {
|
||||
expect(withinQueryHistory().getByRole('tab', { name: `Tab ${tabName}`, selected: true })).toBeInTheDocument();
|
||||
};
|
||||
|
||||
export const assertQueryHistoryTabIsSelected = (
|
||||
tabName: 'Query history' | 'Starred' | 'Settings',
|
||||
exploreId = 'left'
|
||||
) => {
|
||||
expect(withinExplore(exploreId).getByRole('tab', { name: `Tab ${tabName}`, selected: true })).toBeInTheDocument();
|
||||
};
|
||||
|
||||
export const assertDataSourceFilterVisibility = (visible: boolean, exploreId = 'left') => {
|
||||
const filterInput = withinExplore(exploreId).queryByLabelText('Filter queries for data sources(s)');
|
||||
export const assertDataSourceFilterVisibility = (visible: boolean) => {
|
||||
const filterInput = withinQueryHistory().queryByLabelText('Filter queries for data sources(s)');
|
||||
if (visible) {
|
||||
expect(filterInput).toBeInTheDocument();
|
||||
} else {
|
||||
@@ -74,10 +53,10 @@ export const assertDataSourceFilterVisibility = (visible: boolean, exploreId = '
|
||||
}
|
||||
};
|
||||
|
||||
export const assertQueryHistoryElementsShown = (shown: number, total: number, exploreId = 'left') => {
|
||||
expect(withinExplore(exploreId).queryByText(`Showing ${shown} of ${total}`)).toBeInTheDocument();
|
||||
export const assertQueryHistoryElementsShown = (shown: number, total: number) => {
|
||||
expect(withinQueryHistory().queryByText(`Showing ${shown} of ${total}`)).toBeInTheDocument();
|
||||
};
|
||||
|
||||
export const assertLoadMoreQueryHistoryNotVisible = (exploreId = 'left') => {
|
||||
expect(withinExplore(exploreId).queryByRole('button', { name: 'Load more' })).not.toBeInTheDocument();
|
||||
export const assertLoadMoreQueryHistoryNotVisible = () => {
|
||||
expect(withinQueryHistory().queryByRole('button', { name: 'Load more' })).not.toBeInTheDocument();
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { getAllByRoleInQueryHistoryTab, withinExplore } from './setup';
|
||||
import { getAllByRoleInQueryHistoryTab, withinExplore, withinQueryHistory } from './setup';
|
||||
|
||||
export const changeDatasource = async (name: string) => {
|
||||
const datasourcePicker = (await screen.findByTestId(selectors.components.DataSourcePicker.container)).children[0];
|
||||
@@ -25,56 +25,57 @@ export const runQuery = async (exploreId = 'left') => {
|
||||
await userEvent.click(button);
|
||||
};
|
||||
|
||||
export const openQueryHistory = async (exploreId = 'left') => {
|
||||
const selector = withinExplore(exploreId);
|
||||
const button = selector.getByRole('button', { name: 'Query history' });
|
||||
export const openQueryHistory = async () => {
|
||||
const explore = withinExplore('left');
|
||||
const button = explore.getByRole('button', { name: 'Query history' });
|
||||
await userEvent.click(button);
|
||||
expect(await selector.findByPlaceholderText('Search queries')).toBeInTheDocument();
|
||||
expect(await screen.findByPlaceholderText('Search queries')).toBeInTheDocument();
|
||||
};
|
||||
|
||||
export const closeQueryHistory = async (exploreId = 'left') => {
|
||||
const closeButton = withinExplore(exploreId).getByRole('button', { name: 'Close query history' });
|
||||
export const closeQueryHistory = async () => {
|
||||
const selector = withinQueryHistory();
|
||||
const closeButton = selector.getByRole('button', { name: 'Close query history' });
|
||||
await userEvent.click(closeButton);
|
||||
};
|
||||
|
||||
export const switchToQueryHistoryTab = async (name: 'Settings' | 'Query History', exploreId = 'left') => {
|
||||
await userEvent.click(withinExplore(exploreId).getByRole('tab', { name: `Tab ${name}` }));
|
||||
export const switchToQueryHistoryTab = async (name: 'Settings' | 'Query History') => {
|
||||
await userEvent.click(withinQueryHistory().getByRole('tab', { name: `Tab ${name}` }));
|
||||
};
|
||||
|
||||
export const selectStarredTabFirst = async (exploreId = 'left') => {
|
||||
const checkbox = withinExplore(exploreId).getByRole('checkbox', {
|
||||
export const selectStarredTabFirst = async () => {
|
||||
const checkbox = withinQueryHistory().getByRole('checkbox', {
|
||||
name: /Change the default active tab from “Query history” to “Starred”/,
|
||||
});
|
||||
await userEvent.click(checkbox);
|
||||
};
|
||||
|
||||
export const selectOnlyActiveDataSource = async (exploreId = 'left') => {
|
||||
const checkbox = withinExplore(exploreId).getByLabelText(/Only show queries for data source currently active.*/);
|
||||
export const selectOnlyActiveDataSource = async () => {
|
||||
const checkbox = withinQueryHistory().getByLabelText(/Only show queries for data source currently active.*/);
|
||||
await userEvent.click(checkbox);
|
||||
};
|
||||
|
||||
export const starQueryHistory = async (queryIndex: number, exploreId = 'left') => {
|
||||
await invokeAction(queryIndex, 'Star query', exploreId);
|
||||
export const starQueryHistory = async (queryIndex: number) => {
|
||||
await invokeAction(queryIndex, 'Star query');
|
||||
};
|
||||
|
||||
export const commentQueryHistory = async (queryIndex: number, comment: string, exploreId = 'left') => {
|
||||
await invokeAction(queryIndex, 'Add comment', exploreId);
|
||||
const input = withinExplore(exploreId).getByPlaceholderText('An optional description of what the query does.');
|
||||
export const commentQueryHistory = async (queryIndex: number, comment: string) => {
|
||||
await invokeAction(queryIndex, 'Add comment');
|
||||
const input = withinQueryHistory().getByPlaceholderText('An optional description of what the query does.');
|
||||
await userEvent.clear(input);
|
||||
await userEvent.type(input, comment);
|
||||
await invokeAction(queryIndex, 'Save comment', exploreId);
|
||||
await invokeAction(queryIndex, 'Save comment');
|
||||
};
|
||||
|
||||
export const deleteQueryHistory = async (queryIndex: number, exploreId = 'left') => {
|
||||
await invokeAction(queryIndex, 'Delete query', exploreId);
|
||||
export const deleteQueryHistory = async (queryIndex: number) => {
|
||||
await invokeAction(queryIndex, 'Delete query');
|
||||
};
|
||||
|
||||
export const loadMoreQueryHistory = async (exploreId = 'left') => {
|
||||
const button = withinExplore(exploreId).getByRole('button', { name: 'Load more' });
|
||||
export const loadMoreQueryHistory = async () => {
|
||||
const button = withinQueryHistory().getByRole('button', { name: 'Load more' });
|
||||
await userEvent.click(button);
|
||||
};
|
||||
|
||||
const invokeAction = async (queryIndex: number, actionAccessibleName: string | RegExp, exploreId: string) => {
|
||||
const buttons = getAllByRoleInQueryHistoryTab(exploreId, 'button', actionAccessibleName);
|
||||
const invokeAction = async (queryIndex: number, actionAccessibleName: string | RegExp) => {
|
||||
const buttons = getAllByRoleInQueryHistoryTab('button', actionAccessibleName);
|
||||
await userEvent.click(buttons[queryIndex]);
|
||||
};
|
||||
|
||||
@@ -282,6 +282,11 @@ export const withinExplore = (exploreId: string) => {
|
||||
return within(container[exploreId === 'left' ? 0 : 1]);
|
||||
};
|
||||
|
||||
export const withinQueryHistory = () => {
|
||||
const container = screen.getByTestId('data-testid QueryHistory');
|
||||
return within(container);
|
||||
};
|
||||
|
||||
const exploreTestsHelper: { setupExplore: typeof setupExplore; tearDownExplore?: (options?: TearDownOptions) => void } =
|
||||
{
|
||||
setupExplore,
|
||||
@@ -291,8 +296,8 @@ const exploreTestsHelper: { setupExplore: typeof setupExplore; tearDownExplore?:
|
||||
/**
|
||||
* Optimized version of getAllByRole to avoid timeouts in tests. Please check #70158, #59116 and #47635, #78236.
|
||||
*/
|
||||
export const getAllByRoleInQueryHistoryTab = (exploreId: string, role: ByRoleMatcher, name: string | RegExp) => {
|
||||
const selector = withinExplore(exploreId);
|
||||
export const getAllByRoleInQueryHistoryTab = (role: ByRoleMatcher, name: string | RegExp) => {
|
||||
const selector = withinQueryHistory();
|
||||
// Test ID is used to avoid test timeouts reported in
|
||||
const queriesContainer = selector.getByTestId('query-history-queries-tab');
|
||||
return within(queriesContainer).getAllByRole(role, { name });
|
||||
|
||||
@@ -12,11 +12,9 @@ import {
|
||||
assertLoadMoreQueryHistoryNotVisible,
|
||||
assertQueryHistory,
|
||||
assertQueryHistoryComment,
|
||||
assertQueryHistoryContains,
|
||||
assertQueryHistoryElementsShown,
|
||||
assertQueryHistoryExists,
|
||||
assertQueryHistoryIsEmpty,
|
||||
assertQueryHistoryIsStarred,
|
||||
assertQueryHistoryTabIsSelected,
|
||||
} from './helper/assert';
|
||||
import {
|
||||
@@ -29,7 +27,6 @@ import {
|
||||
runQuery,
|
||||
selectOnlyActiveDataSource,
|
||||
selectStarredTabFirst,
|
||||
starQueryHistory,
|
||||
switchToQueryHistoryTab,
|
||||
} from './helper/interactions';
|
||||
import { makeLogsQueryResponse } from './helper/query';
|
||||
@@ -141,58 +138,6 @@ describe('Explore: Query History', () => {
|
||||
await assertQueryHistory(['{"expr":"query #2"}', '{"expr":"query #1"}']);
|
||||
});
|
||||
|
||||
describe('updates the state in both Explore panes', () => {
|
||||
beforeEach(async () => {
|
||||
const urlParams = {
|
||||
left: serializeStateToUrlParam({
|
||||
datasource: 'loki',
|
||||
queries: [{ refId: 'A', expr: 'query #1' }],
|
||||
range: { from: 'now-1h', to: 'now' },
|
||||
}),
|
||||
right: serializeStateToUrlParam({
|
||||
datasource: 'loki',
|
||||
queries: [{ refId: 'A', expr: 'query #2' }],
|
||||
range: { from: 'now-1h', to: 'now' },
|
||||
}),
|
||||
};
|
||||
|
||||
const { datasources } = setupExplore({ urlParams });
|
||||
jest.mocked(datasources.loki.query).mockReturnValue(makeLogsQueryResponse());
|
||||
await waitForExplore();
|
||||
await waitForExplore('right');
|
||||
|
||||
await openQueryHistory('left');
|
||||
await openQueryHistory('right');
|
||||
});
|
||||
|
||||
it('initial state is in sync', async () => {
|
||||
await assertQueryHistoryContains('{"expr":"query #1"}', 'left');
|
||||
await assertQueryHistoryContains('{"expr":"query #2"}', 'left');
|
||||
await assertQueryHistoryContains('{"expr":"query #1"}', 'right');
|
||||
await assertQueryHistoryContains('{"expr":"query #2"}', 'right');
|
||||
});
|
||||
|
||||
it('starred queries are synced', async () => {
|
||||
// star one one query
|
||||
await starQueryHistory(1, 'left');
|
||||
await assertQueryHistoryIsStarred([false, true], 'left');
|
||||
await assertQueryHistoryIsStarred([false, true], 'right');
|
||||
expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_starred', {
|
||||
queryHistoryEnabled: false,
|
||||
newValue: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('deleted queries are synced', async () => {
|
||||
await deleteQueryHistory(0, 'left');
|
||||
await assertQueryHistory(['{"expr":"query #1"}'], 'left');
|
||||
await assertQueryHistory(['{"expr":"query #1"}'], 'right');
|
||||
expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_deleted', {
|
||||
queryHistoryEnabled: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('add comments to query history', async () => {
|
||||
const urlParams = {
|
||||
left: serializeStateToUrlParam({
|
||||
@@ -206,9 +151,9 @@ describe('Explore: Query History', () => {
|
||||
jest.mocked(datasources.loki.query).mockReturnValueOnce(makeLogsQueryResponse());
|
||||
await waitForExplore();
|
||||
await openQueryHistory();
|
||||
await assertQueryHistory(['{"expr":"query #1"}'], 'left');
|
||||
await assertQueryHistory(['{"expr":"query #1"}']);
|
||||
await commentQueryHistory(0, 'test comment');
|
||||
await assertQueryHistoryComment(['test comment'], 'left');
|
||||
await assertQueryHistoryComment(['test comment']);
|
||||
});
|
||||
|
||||
it('removes the query item from the history panel when user deletes a regular query', async () => {
|
||||
@@ -227,13 +172,13 @@ describe('Explore: Query History', () => {
|
||||
await openQueryHistory();
|
||||
|
||||
// queries in history
|
||||
await assertQueryHistory(['{"expr":"query #1"}'], 'left');
|
||||
await assertQueryHistory(['{"expr":"query #1"}']);
|
||||
|
||||
// delete query
|
||||
await deleteQueryHistory(0, 'left');
|
||||
await deleteQueryHistory(0);
|
||||
|
||||
// there was only one query in history so assert that query history is empty
|
||||
await assertQueryHistoryIsEmpty('left');
|
||||
await assertQueryHistoryIsEmpty();
|
||||
});
|
||||
|
||||
it('updates query history settings', async () => {
|
||||
|
||||
@@ -20,7 +20,6 @@ import { createAsyncThunk, ThunkResult } from 'app/types';
|
||||
import { ExploreItemState } from 'app/types/explore';
|
||||
|
||||
import { datasourceReducer } from './datasource';
|
||||
import { richHistorySearchFiltersUpdatedAction, richHistoryUpdatedAction } from './main';
|
||||
import { queryReducer, runQueries } from './query';
|
||||
import { timeReducer, updateTime } from './time';
|
||||
import {
|
||||
@@ -214,23 +213,6 @@ export const paneReducer = (state: ExploreItemState = makeExplorePaneState(), ac
|
||||
state = datasourceReducer(state, action);
|
||||
state = timeReducer(state, action);
|
||||
|
||||
if (richHistoryUpdatedAction.match(action)) {
|
||||
const { richHistory, total } = action.payload.richHistoryResults;
|
||||
return {
|
||||
...state,
|
||||
richHistory,
|
||||
richHistoryTotal: total,
|
||||
};
|
||||
}
|
||||
|
||||
if (richHistorySearchFiltersUpdatedAction.match(action)) {
|
||||
const richHistorySearchFilters = action.payload.filters;
|
||||
return {
|
||||
...state,
|
||||
richHistorySearchFilters,
|
||||
};
|
||||
}
|
||||
|
||||
if (changeSizeAction.match(action)) {
|
||||
const containerWidth = action.payload.width;
|
||||
return { ...state, containerWidth };
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { HistoryItem } from '@grafana/data';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import {
|
||||
addToRichHistory,
|
||||
@@ -9,7 +12,7 @@ import {
|
||||
updateRichHistorySettings,
|
||||
updateStarredInRichHistory,
|
||||
} from 'app/core/utils/richHistory';
|
||||
import { ExploreItemState, ExploreState, RichHistoryQuery, ThunkResult } from 'app/types';
|
||||
import { RichHistoryQuery, ThunkResult } from 'app/types';
|
||||
|
||||
import { supportedFeatures } from '../../../core/history/richHistoryStorageProvider';
|
||||
import { RichHistorySearchFilters, RichHistorySettings } from '../../../core/utils/richHistoryTypes';
|
||||
@@ -21,7 +24,15 @@ import {
|
||||
richHistoryStorageFullAction,
|
||||
richHistoryUpdatedAction,
|
||||
} from './main';
|
||||
import { selectPanesEntries } from './selectors';
|
||||
|
||||
//
|
||||
// Actions and Payloads
|
||||
//
|
||||
|
||||
export interface HistoryUpdatedPayload {
|
||||
history: HistoryItem[];
|
||||
}
|
||||
export const historyUpdatedAction = createAction<HistoryUpdatedPayload>('explore/historyUpdated');
|
||||
|
||||
//
|
||||
// Action creators
|
||||
@@ -37,27 +48,23 @@ type SyncHistoryUpdatesOptions = {
|
||||
*/
|
||||
const updateRichHistoryState = ({ updatedQuery, deletedId }: SyncHistoryUpdatesOptions): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
forEachExplorePane(getState().explore, (item, exploreId) => {
|
||||
const newRichHistory = item.richHistory
|
||||
// update
|
||||
.map((query) => (query.id === updatedQuery?.id ? updatedQuery : query))
|
||||
// or remove
|
||||
.filter((query) => query.id !== deletedId);
|
||||
const deletedItems = item.richHistory.length - newRichHistory.length;
|
||||
dispatch(
|
||||
richHistoryUpdatedAction({
|
||||
richHistoryResults: { richHistory: newRichHistory, total: item.richHistoryTotal! - deletedItems },
|
||||
exploreId,
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
};
|
||||
const richHistory = getState().explore.richHistory;
|
||||
|
||||
const forEachExplorePane = (state: ExploreState, callback: (item: ExploreItemState, exploreId: string) => void) => {
|
||||
Object.entries(state.panes).forEach(([exploreId, item]) => {
|
||||
item && callback(item, exploreId);
|
||||
});
|
||||
// update or remove entries
|
||||
const newRichHistory = richHistory
|
||||
.map((query) => (query.id === updatedQuery?.id ? updatedQuery : query))
|
||||
.filter((query) => query.id !== deletedId);
|
||||
|
||||
const deletedItems = richHistory.length - newRichHistory.length;
|
||||
dispatch(
|
||||
richHistoryUpdatedAction({
|
||||
richHistoryResults: {
|
||||
richHistory: newRichHistory,
|
||||
total: getState().explore.richHistoryTotal! - deletedItems,
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export const addHistoryItem = (
|
||||
@@ -114,45 +121,41 @@ export const deleteHistoryItem = (id: string): ThunkResult<void> => {
|
||||
};
|
||||
|
||||
export const deleteRichHistory = (): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
return async (dispatch) => {
|
||||
await deleteAllFromRichHistory();
|
||||
selectPanesEntries(getState()).forEach(([exploreId]) => {
|
||||
dispatch(richHistoryUpdatedAction({ richHistoryResults: { richHistory: [], total: 0 }, exploreId }));
|
||||
dispatch(richHistoryUpdatedAction({ richHistoryResults: { richHistory: [], total: 0 }, exploreId }));
|
||||
});
|
||||
dispatch(richHistoryUpdatedAction({ richHistoryResults: { richHistory: [], total: 0 } }));
|
||||
dispatch(richHistoryUpdatedAction({ richHistoryResults: { richHistory: [], total: 0 } }));
|
||||
};
|
||||
};
|
||||
|
||||
export const loadRichHistory = (exploreId: string): ThunkResult<void> => {
|
||||
export const loadRichHistory = (): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const filters = getState().explore.panes[exploreId]!.richHistorySearchFilters;
|
||||
const filters = getState().explore.richHistorySearchFilters;
|
||||
if (filters) {
|
||||
const richHistoryResults = await getRichHistory(filters);
|
||||
dispatch(richHistoryUpdatedAction({ richHistoryResults, exploreId }));
|
||||
dispatch(richHistoryUpdatedAction({ richHistoryResults }));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const loadMoreRichHistory = (exploreId: string): ThunkResult<void> => {
|
||||
export const loadMoreRichHistory = (): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const currentFilters = getState().explore.panes[exploreId]?.richHistorySearchFilters;
|
||||
const currentRichHistory = getState().explore.panes[exploreId]?.richHistory;
|
||||
const currentFilters = getState().explore.richHistorySearchFilters;
|
||||
const currentRichHistory = getState().explore.richHistory;
|
||||
if (currentFilters && currentRichHistory) {
|
||||
const nextFilters = { ...currentFilters, page: (currentFilters?.page || 1) + 1 };
|
||||
const moreRichHistory = await getRichHistory(nextFilters);
|
||||
const richHistory = [...currentRichHistory, ...moreRichHistory.richHistory];
|
||||
dispatch(richHistorySearchFiltersUpdatedAction({ filters: nextFilters, exploreId }));
|
||||
dispatch(
|
||||
richHistoryUpdatedAction({ richHistoryResults: { richHistory, total: moreRichHistory.total }, exploreId })
|
||||
);
|
||||
dispatch(richHistorySearchFiltersUpdatedAction({ filters: nextFilters }));
|
||||
dispatch(richHistoryUpdatedAction({ richHistoryResults: { richHistory, total: moreRichHistory.total } }));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const clearRichHistoryResults = (exploreId: string): ThunkResult<void> => {
|
||||
export const clearRichHistoryResults = (): ThunkResult<void> => {
|
||||
return async (dispatch) => {
|
||||
dispatch(richHistorySearchFiltersUpdatedAction({ filters: undefined, exploreId }));
|
||||
dispatch(richHistoryUpdatedAction({ richHistoryResults: { richHistory: [], total: 0 }, exploreId }));
|
||||
dispatch(richHistorySearchFiltersUpdatedAction({ filters: undefined }));
|
||||
dispatch(richHistoryUpdatedAction({ richHistoryResults: { richHistory: [], total: 0 } }));
|
||||
};
|
||||
};
|
||||
|
||||
@@ -180,9 +183,9 @@ export const updateHistorySettings = (settings: RichHistorySettings): ThunkResul
|
||||
/**
|
||||
* Assumed this can be called only when settings and filters are initialised
|
||||
*/
|
||||
export const updateHistorySearchFilters = (exploreId: string, filters: RichHistorySearchFilters): ThunkResult<void> => {
|
||||
export const updateHistorySearchFilters = (filters: RichHistorySearchFilters): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
await dispatch(richHistorySearchFiltersUpdatedAction({ exploreId, filters: { ...filters } }));
|
||||
await dispatch(richHistorySearchFiltersUpdatedAction({ filters: { ...filters } }));
|
||||
const currentSettings = getState().explore.richHistorySettings!;
|
||||
if (supportedFeatures().lastUsedDataSourcesAvailable) {
|
||||
await dispatch(
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface SyncTimesPayload {
|
||||
}
|
||||
export const syncTimesAction = createAction<SyncTimesPayload>('explore/syncTimes');
|
||||
|
||||
export const richHistoryUpdatedAction = createAction<{ richHistoryResults: RichHistoryResults; exploreId: string }>(
|
||||
export const richHistoryUpdatedAction = createAction<{ richHistoryResults: RichHistoryResults }>(
|
||||
'explore/richHistoryUpdated'
|
||||
);
|
||||
export const richHistoryStorageFullAction = createAction('explore/richHistoryStorageFullAction');
|
||||
@@ -34,7 +34,6 @@ export const richHistoryLimitExceededAction = createAction('explore/richHistoryL
|
||||
|
||||
export const richHistorySettingsUpdatedAction = createAction<RichHistorySettings>('explore/richHistorySettingsUpdated');
|
||||
export const richHistorySearchFiltersUpdatedAction = createAction<{
|
||||
exploreId: string;
|
||||
filters?: RichHistorySearchFilters;
|
||||
}>('explore/richHistorySearchFiltersUpdatedAction');
|
||||
|
||||
@@ -125,6 +124,8 @@ export const changeCorrelationEditorDetails = createAction<CorrelationEditorDeta
|
||||
'explore/changeCorrelationEditorDetails'
|
||||
);
|
||||
|
||||
export const changeShowQueryHistory = createAction<boolean>('explore/changeShowQueryHistory');
|
||||
|
||||
export interface NavigateToExploreDependencies {
|
||||
timeRange: TimeRange;
|
||||
getExploreUrl: (args: GetExploreUrlArguments) => Promise<string | undefined>;
|
||||
@@ -168,6 +169,8 @@ export const initialExploreState: ExploreState = {
|
||||
largerExploreId: undefined,
|
||||
maxedExploreId: undefined,
|
||||
evenSplitPanes: true,
|
||||
showQueryHistory: false,
|
||||
richHistory: [],
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -243,6 +246,23 @@ export const exploreReducer = (state = initialExploreState, action: AnyAction):
|
||||
};
|
||||
}
|
||||
|
||||
if (richHistoryUpdatedAction.match(action)) {
|
||||
const { richHistory, total } = action.payload.richHistoryResults;
|
||||
return {
|
||||
...state,
|
||||
richHistory,
|
||||
richHistoryTotal: total,
|
||||
};
|
||||
}
|
||||
|
||||
if (richHistorySearchFiltersUpdatedAction.match(action)) {
|
||||
const richHistorySearchFilters = action.payload.filters;
|
||||
return {
|
||||
...state,
|
||||
richHistorySearchFilters,
|
||||
};
|
||||
}
|
||||
|
||||
if (createNewSplitOpenPane.pending.match(action)) {
|
||||
return {
|
||||
...state,
|
||||
@@ -303,6 +323,13 @@ export const exploreReducer = (state = initialExploreState, action: AnyAction):
|
||||
};
|
||||
}
|
||||
|
||||
if (changeShowQueryHistory.match(action)) {
|
||||
return {
|
||||
...state,
|
||||
showQueryHistory: action.payload,
|
||||
};
|
||||
}
|
||||
|
||||
const exploreId: string | undefined = action.payload?.exploreId;
|
||||
if (typeof exploreId === 'string') {
|
||||
return {
|
||||
|
||||
@@ -494,10 +494,7 @@ async function handleHistory(
|
||||
|
||||
// Because filtering happens in the backend we cannot add a new entry without checking if it matches currently
|
||||
// used filters. Instead, we refresh the query history list.
|
||||
// TODO: run only if Query History list is opened (#47252)
|
||||
for (const exploreId in state.panes) {
|
||||
await dispatch(loadRichHistory(exploreId));
|
||||
}
|
||||
await dispatch(loadRichHistory());
|
||||
}
|
||||
|
||||
interface RunQueriesOptions {
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
import { DataSourceApi, DataSourceJsonData } from '@grafana/data';
|
||||
import { DataQuery } from '@grafana/schema/dist/esm/index';
|
||||
import { configureStore } from 'app/store/configureStore';
|
||||
import { StoreState, ThunkDispatch } from 'app/types';
|
||||
|
||||
import { createDefaultInitialState } from './helpers';
|
||||
import { selectExploreDSMaps } from './selectors';
|
||||
|
||||
const { defaultInitialState } = createDefaultInitialState();
|
||||
|
||||
const datasources: DataSourceApi[] = [
|
||||
{
|
||||
name: 'testDs',
|
||||
type: 'postgres',
|
||||
uid: 'ds1',
|
||||
getRef: () => {
|
||||
return { type: 'postgres', uid: 'ds1' };
|
||||
},
|
||||
} as DataSourceApi<DataQuery, DataSourceJsonData, {}>,
|
||||
{
|
||||
name: 'testDs2',
|
||||
type: 'mysql',
|
||||
uid: 'ds2',
|
||||
getRef: () => {
|
||||
return { type: 'mysql', uid: 'ds2' };
|
||||
},
|
||||
} as DataSourceApi<DataQuery, DataSourceJsonData, {}>,
|
||||
];
|
||||
|
||||
describe('selectExploreDSMaps', () => {
|
||||
it('returns datasource information as empty with empty state', () => {
|
||||
const store: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore();
|
||||
|
||||
const dsMaps = selectExploreDSMaps(store.getState());
|
||||
expect(dsMaps.dsToExplore).toEqual([]);
|
||||
expect(dsMaps.exploreToDS).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns root datasources from 2 panes with empty queries', () => {
|
||||
const store: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
panes: {
|
||||
left: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
datasourceInstance: datasources[0],
|
||||
queries: [],
|
||||
},
|
||||
right: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
datasourceInstance: datasources[1],
|
||||
queries: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
|
||||
const dsMaps = selectExploreDSMaps(store.getState());
|
||||
expect(dsMaps.dsToExplore.length).toEqual(2);
|
||||
|
||||
// ds 1
|
||||
expect(dsMaps.dsToExplore[0].datasource.uid).toEqual('ds1');
|
||||
expect(dsMaps.dsToExplore[0].exploreIds.length).toEqual(1);
|
||||
expect(dsMaps.dsToExplore[0].exploreIds[0]).toEqual('left');
|
||||
// ds 2
|
||||
expect(dsMaps.dsToExplore[1].datasource.uid).toEqual('ds2');
|
||||
expect(dsMaps.dsToExplore[1].exploreIds.length).toEqual(1);
|
||||
expect(dsMaps.dsToExplore[1].exploreIds[0]).toEqual('right');
|
||||
|
||||
expect(dsMaps.exploreToDS.length).toEqual(2);
|
||||
// pane 1
|
||||
expect(dsMaps.exploreToDS[0].exploreId).toEqual('left');
|
||||
expect(dsMaps.exploreToDS[0].datasources.length).toEqual(1);
|
||||
expect(dsMaps.exploreToDS[0].datasources[0].uid).toEqual('ds1');
|
||||
//pane 2
|
||||
expect(dsMaps.exploreToDS[1].exploreId).toEqual('right');
|
||||
expect(dsMaps.exploreToDS[1].datasources.length).toEqual(1);
|
||||
expect(dsMaps.exploreToDS[1].datasources[0].uid).toEqual('ds2');
|
||||
});
|
||||
|
||||
it('returns all datasources from 2 panes with queries', () => {
|
||||
const store: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
panes: {
|
||||
different: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
datasourceInstance: datasources[0],
|
||||
queries: [{ datasource: datasources[1] }],
|
||||
},
|
||||
match: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
datasourceInstance: datasources[1],
|
||||
queries: [{ datasource: datasources[1] }],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
|
||||
const dsMaps = selectExploreDSMaps(store.getState());
|
||||
expect(dsMaps.dsToExplore.length).toEqual(2);
|
||||
// ds 1
|
||||
expect(dsMaps.dsToExplore[0].datasource.uid).toEqual('ds1');
|
||||
expect(dsMaps.dsToExplore[0].exploreIds.length).toEqual(1);
|
||||
expect(dsMaps.dsToExplore[0].exploreIds[0]).toEqual('different');
|
||||
// ds2
|
||||
expect(dsMaps.dsToExplore[1].datasource.uid).toEqual('ds2');
|
||||
expect(dsMaps.dsToExplore[1].exploreIds.length).toEqual(2);
|
||||
expect(dsMaps.dsToExplore[1].exploreIds[0]).toEqual('different');
|
||||
expect(dsMaps.dsToExplore[1].exploreIds[1]).toEqual('match');
|
||||
|
||||
expect(dsMaps.exploreToDS.length).toEqual(2);
|
||||
// pane 1
|
||||
expect(dsMaps.exploreToDS[0].exploreId).toEqual('different');
|
||||
expect(dsMaps.exploreToDS[0].datasources.length).toEqual(2);
|
||||
expect(dsMaps.exploreToDS[0].datasources[0].uid).toEqual('ds1');
|
||||
expect(dsMaps.exploreToDS[0].datasources[1].uid).toEqual('ds2');
|
||||
// pane 2
|
||||
expect(dsMaps.exploreToDS[1].exploreId).toEqual('match');
|
||||
expect(dsMaps.exploreToDS[1].datasources.length).toEqual(1);
|
||||
expect(dsMaps.exploreToDS[1].datasources[0].uid).toEqual('ds2');
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,7 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { flatten, uniqBy } from 'lodash';
|
||||
|
||||
import { DataSourceRef } from '@grafana/schema';
|
||||
import { ExploreItemState, StoreState } from 'app/types';
|
||||
|
||||
export const selectPanes = (state: Pick<StoreState, 'explore'>) => state.explore.panes;
|
||||
@@ -23,3 +25,43 @@ export const isLeftPaneSelector = (exploreId: string) =>
|
||||
export const getExploreItemSelector = (exploreId: string) => createSelector(selectPanes, (panes) => panes[exploreId]);
|
||||
|
||||
export const selectCorrelationDetails = createSelector(selectExploreRoot, (state) => state.correlationEditorDetails);
|
||||
|
||||
export const selectShowQueryHistory = createSelector(selectExploreRoot, (state) => state.showQueryHistory);
|
||||
|
||||
export const selectExploreDSMaps = createSelector(selectPanesEntries, (panes) => {
|
||||
const exploreDSMap = panes
|
||||
.map(([exploreId, pane]) => {
|
||||
const rootDatasource = [pane?.datasourceInstance?.getRef()];
|
||||
const queryDatasources = pane?.queries.map((q) => q.datasource) || [];
|
||||
const datasources = [...rootDatasource, ...queryDatasources].filter(
|
||||
(datasource): datasource is DataSourceRef => !!datasource
|
||||
);
|
||||
|
||||
if (datasources === undefined || datasources.length === 0) {
|
||||
return undefined;
|
||||
} else {
|
||||
return {
|
||||
exploreId,
|
||||
datasources: uniqBy(datasources, (ds) => ds.uid),
|
||||
};
|
||||
}
|
||||
})
|
||||
.filter((pane): pane is { exploreId: string; datasources: DataSourceRef[] } => !!pane);
|
||||
|
||||
const uniqueDataSources = uniqBy(flatten(exploreDSMap.map((pane) => pane.datasources)), (ds) => ds.uid);
|
||||
|
||||
const dsToExploreMap = uniqueDataSources.map((ds) => {
|
||||
let exploreIds: string[] = [];
|
||||
exploreDSMap.forEach((eds) => {
|
||||
if (eds.datasources.some((edsDs) => edsDs.uid === ds.uid)) {
|
||||
exploreIds.push(eds.exploreId);
|
||||
}
|
||||
});
|
||||
return {
|
||||
datasource: ds,
|
||||
exploreIds: exploreIds,
|
||||
};
|
||||
});
|
||||
|
||||
return { exploreToDS: exploreDSMap, dsToExplore: dsToExploreMap };
|
||||
});
|
||||
|
||||
@@ -75,7 +75,6 @@ export const makeExplorePaneState = (overrides?: Partial<ExploreItemState>): Exp
|
||||
rawPrometheusResult: null,
|
||||
eventBridge: null as unknown as EventBusExtended,
|
||||
cache: [],
|
||||
richHistory: [],
|
||||
supplementaryQueries: loadSupplementaryQueries(),
|
||||
panelsState: {},
|
||||
correlations: undefined,
|
||||
|
||||
@@ -63,6 +63,18 @@ export interface ExploreState {
|
||||
|
||||
panes: Record<string, ExploreItemState | undefined>;
|
||||
|
||||
/**
|
||||
* Is the drawer for query history showing
|
||||
*/
|
||||
showQueryHistory: boolean;
|
||||
|
||||
/**
|
||||
* History of all queries
|
||||
*/
|
||||
richHistory: RichHistoryQuery[];
|
||||
richHistorySearchFilters?: RichHistorySearchFilters;
|
||||
richHistoryTotal?: number;
|
||||
|
||||
/**
|
||||
* Settings for rich history (note: filters are stored per each pane separately)
|
||||
*/
|
||||
@@ -206,13 +218,6 @@ export interface ExploreItemState {
|
||||
showFlameGraph?: boolean;
|
||||
showCustom?: boolean;
|
||||
|
||||
/**
|
||||
* History of all queries
|
||||
*/
|
||||
richHistory: RichHistoryQuery[];
|
||||
richHistorySearchFilters?: RichHistorySearchFilters;
|
||||
richHistoryTotal?: number;
|
||||
|
||||
/**
|
||||
* We are using caching to store query responses of queries run from logs navigation.
|
||||
* In logs navigation, we do pagination and we don't want our users to unnecessarily run the same queries that they've run just moments before.
|
||||
|
||||
@@ -446,10 +446,11 @@
|
||||
"delete-query-tooltip": "Delete query",
|
||||
"delete-starred-query-confirmation-text": "Are you sure you want to permanently delete your starred query?",
|
||||
"edit-comment-tooltip": "Edit comment",
|
||||
"loading-text": "loading...",
|
||||
"left-pane": "Left pane",
|
||||
"optional-description": "An optional description of what the query does.",
|
||||
"query-comment-label": "Query comment",
|
||||
"query-text-label": "Query text",
|
||||
"right-pane": "Right pane",
|
||||
"run-query-button": "Run query",
|
||||
"save-comment": "Save comment",
|
||||
"star-query-tooltip": "Star query",
|
||||
|
||||
@@ -446,10 +446,11 @@
|
||||
"delete-query-tooltip": "Đęľęŧę qūęřy",
|
||||
"delete-starred-query-confirmation-text": "Åřę yőū şūřę yőū ŵäʼnŧ ŧő pęřmäʼnęʼnŧľy đęľęŧę yőūř şŧäřřęđ qūęřy?",
|
||||
"edit-comment-tooltip": "Ēđįŧ čőmmęʼnŧ",
|
||||
"loading-text": "ľőäđįʼnģ...",
|
||||
"left-pane": "Ŀęƒŧ päʼnę",
|
||||
"optional-description": "Åʼn őpŧįőʼnäľ đęşčřįpŧįőʼn őƒ ŵĥäŧ ŧĥę qūęřy đőęş.",
|
||||
"query-comment-label": "Qūęřy čőmmęʼnŧ",
|
||||
"query-text-label": "Qūęřy ŧęχŧ",
|
||||
"right-pane": "Ŗįģĥŧ päʼnę",
|
||||
"run-query-button": "Ŗūʼn qūęřy",
|
||||
"save-comment": "Ŝävę čőmmęʼnŧ",
|
||||
"star-query-tooltip": "Ŝŧäř qūęřy",
|
||||
|
||||
Reference in New Issue
Block a user