mirror of
https://github.com/grafana/grafana.git
synced 2025-12-21 12:04:45 +08:00
Compare commits
17 Commits
docs/add-t
...
v9.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efe95b4c21 | ||
|
|
a14c2d674a | ||
|
|
262d642d77 | ||
|
|
5e6893c425 | ||
|
|
ec109a913b | ||
|
|
7104dc3fef | ||
|
|
a6a9e4e0ed | ||
|
|
62e0b45067 | ||
|
|
0f0e2664cc | ||
|
|
1dc26665f3 | ||
|
|
b68716e9eb | ||
|
|
638a277364 | ||
|
|
c7cc68649d | ||
|
|
119fb76938 | ||
|
|
fbb66e6b9f | ||
|
|
ea8d17009f | ||
|
|
f34d0df862 |
@@ -8,7 +8,7 @@ weight: 5
|
||||
|
||||
# Grafana OSS
|
||||
|
||||
[Grafana open source software](https://grafana.com/oss/) enables you to query, visualize, alert on, and explore your metrics, logs, and traces wherever they are stored. Grafana OSS provides you with tools to turn your time-series database (TSDB) data into insightful graphs and visualizations.
|
||||
[Grafana open source software](https://grafana.com/oss/) enables you to query, visualize, alert on, and explore your metrics, logs, and traces wherever they are stored. Grafana OSS provides you with tools to turn your time-series database (TSDB) data into insightful graphs and visualizations. The Grafana OSS plugin framework also enables you to connect other data sources like NoSQL/SQL databases, ticketing tools like Jira or ServiceNow, and CI/CD tooling like GitLab.
|
||||
|
||||
After you have [installed Grafana]({{< relref "../setup-grafana/installation/" >}}) and set up your first dashboard using instructions in [Getting started with Grafana]({{< relref "../getting-started/build-first-dashboard.md" >}}), you will have many options to choose from depending on your requirements. For example, if you want to view weather data and statistics about your smart home, then you can create a [playlist]({{< relref "../dashboards/create-manage-playlists/" >}}). If you are the administrator for an enterprise and are managing Grafana for multiple teams, then you can set up [provisioning]({{< relref "../administration/provisioning/" >}}) and [authentication]({{< relref "../setup-grafana/configure-security/configure-authentication/" >}}).
|
||||
|
||||
@@ -71,3 +71,5 @@ In addition to Grafana, Grafana Labs also provides the following open source pro
|
||||
**Grafana Tempo:** Grafana Tempo is an open source, easy-to-use and high-volume distributed tracing backend. For more information, refer to [Grafana Tempo documentation](https://grafana.com/docs/tempo/latest/?pg=oss-tempo&plcmt=hero-txt/).
|
||||
|
||||
**Grafana Mimir:** Grafana Mimir is an open source software project that provides a scalable long-term storage for Prometheus. For more information about Grafana Mimir, refer to [Grafana Mimir documentation](https://grafana.com/docs/mimir/latest/).
|
||||
|
||||
**Grafana Phlare:** Grafana Phlare is an open-source software project for aggregating continuous profiling data. Continuous profiling is an observability signal that enables you to understand your workload's resource (CPU, memory, etc.) usage to the exact line number. For more information about using Grafana Phlare, refer to [Grafana Phlare documentation](https://grafana.com/docs/phlare/latest/).
|
||||
|
||||
@@ -18,7 +18,7 @@ weight: 900
|
||||
|
||||
The enhanced LDAP integration adds additional functionality on top of the [LDAP integration]({{< relref "ldap/" >}}) available in the open source edition of Grafana.
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Advanced](/docs/grafana-cloud).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Advanced](/docs/grafana-cloud). If you are a Grafana Cloud customer, please [open a support ticket in the Cloud Portal](https://grafana.com/orgs/$org/tickets) to request this feature.
|
||||
|
||||
> To control user access with role-based permissions, refer to [role-based access control]({{< relref "../../../../administration/roles-and-permissions/access-control/" >}}).
|
||||
|
||||
|
||||
2
go.mod
2
go.mod
@@ -400,7 +400,7 @@ require (
|
||||
)
|
||||
|
||||
// Use fork of crewjam/saml with fixes for some issues until changes get merged into upstream
|
||||
replace github.com/crewjam/saml => github.com/grafana/saml v0.4.13-0.20230203140620-5f476db5c00a
|
||||
replace github.com/crewjam/saml => github.com/grafana/saml v0.4.13-0.20230331080031-67cbfa09c7b6
|
||||
|
||||
// Thema's thema CLI requires cobra, which eventually works its way down to go-hclog@v1.0.0.
|
||||
// Upgrading affects backend plugins: https://github.com/grafana/grafana/pull/47653#discussion_r850508593
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1284,8 +1284,8 @@ github.com/grafana/phlare/api v0.1.3 h1:mYTaE9mLsAW/uzPXlW/PQSLsZ4ojBFA+oAMfR/PD
|
||||
github.com/grafana/phlare/api v0.1.3/go.mod h1:29vcLwFDmZBDce2jwFIMtzvof7fzPadT8VMKw9ks7FU=
|
||||
github.com/grafana/prometheus-alertmanager v0.25.1-0.20230308154952-78fedf89728b h1:VQOGGGJ2lKcVPANyzIESKYhSeA0QIvUQwfA3CbrkDfA=
|
||||
github.com/grafana/prometheus-alertmanager v0.25.1-0.20230308154952-78fedf89728b/go.mod h1:MnBfDPXJqXmmfPwQlCLvVUdqfnvrAw+hSPtDeaaFwj4=
|
||||
github.com/grafana/saml v0.4.13-0.20230203140620-5f476db5c00a h1:aWSTt/pTOI4uGY9DhBMG1l0GOnGjIYtaqxzYR3/q82o=
|
||||
github.com/grafana/saml v0.4.13-0.20230203140620-5f476db5c00a/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA=
|
||||
github.com/grafana/saml v0.4.13-0.20230331080031-67cbfa09c7b6 h1:oHn/OOUkECNX06DPHksS7R3UY5Qdye04b/sBj2/OJ5E=
|
||||
github.com/grafana/saml v0.4.13-0.20230331080031-67cbfa09c7b6/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA=
|
||||
github.com/grafana/sqlds/v2 v2.3.10 h1:HWKhE0vR6LoEiE+Is8CSZOgaB//D1yqb2ntkass9Fd4=
|
||||
github.com/grafana/sqlds/v2 v2.3.10/go.mod h1:c6ibxnxRVGxV/0YkEgvy7QpQH/lyifFyV7K/14xvdIs=
|
||||
github.com/grafana/thema v0.0.0-20230302221249-6952e4a999b7 h1:XOxaBjhozlleshff3mKNdp55ul74nXJEX3wz8ckjTpc=
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"packages": ["packages/*"],
|
||||
"version": "9.5.0-pre"
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "9.5.0"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"license": "AGPL-3.0-only",
|
||||
"private": true,
|
||||
"name": "grafana",
|
||||
"version": "9.5.0-pre",
|
||||
"version": "9.5.0",
|
||||
"repository": "github:grafana/grafana",
|
||||
"scripts": {
|
||||
"build": "yarn i18n:compile && NODE_ENV=production webpack --progress --config scripts/webpack/webpack.prod.js",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/data",
|
||||
"version": "9.5.0-pre",
|
||||
"version": "9.5.0",
|
||||
"description": "Grafana Data Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
@@ -36,7 +36,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "6.0.2",
|
||||
"@grafana/schema": "9.5.0-pre",
|
||||
"@grafana/schema": "9.5.0",
|
||||
"@types/d3-interpolate": "^3.0.0",
|
||||
"d3-interpolate": "3.0.1",
|
||||
"date-fns": "2.29.3",
|
||||
|
||||
@@ -441,6 +441,124 @@ describe('setFieldConfigDefaults', () => {
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('applies field config defaults correctly when links property exist in field config and no links are defined in panel', () => {
|
||||
const dsFieldConfig: FieldConfig = {
|
||||
links: [
|
||||
{
|
||||
title: 'Google link',
|
||||
url: 'https://google.com',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const panelFieldConfig: FieldConfig = {};
|
||||
|
||||
const context: FieldOverrideEnv = {
|
||||
data: [],
|
||||
field: { type: FieldType.number } as Field,
|
||||
dataFrameIndex: 0,
|
||||
fieldConfigRegistry: customFieldRegistry,
|
||||
};
|
||||
|
||||
// we mutate dsFieldConfig
|
||||
// @ts-ignore
|
||||
setFieldConfigDefaults(dsFieldConfig, panelFieldConfig, context);
|
||||
|
||||
expect(dsFieldConfig).toMatchInlineSnapshot(`
|
||||
{
|
||||
"custom": {},
|
||||
"links": [
|
||||
{
|
||||
"title": "Google link",
|
||||
"url": "https://google.com",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('applies field config defaults correctly when links property exist in panel config and no links are defined in ds field config', () => {
|
||||
const dsFieldConfig: FieldConfig = {};
|
||||
|
||||
const panelFieldConfig: FieldConfig = {
|
||||
links: [
|
||||
{
|
||||
title: 'Google link',
|
||||
url: 'https://google.com',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const context: FieldOverrideEnv = {
|
||||
data: [],
|
||||
field: { type: FieldType.number } as Field,
|
||||
dataFrameIndex: 0,
|
||||
fieldConfigRegistry: customFieldRegistry,
|
||||
};
|
||||
|
||||
// we mutate dsFieldConfig
|
||||
// @ts-ignore
|
||||
setFieldConfigDefaults(dsFieldConfig, panelFieldConfig, context);
|
||||
|
||||
expect(dsFieldConfig).toMatchInlineSnapshot(`
|
||||
{
|
||||
"custom": {},
|
||||
"links": [
|
||||
{
|
||||
"title": "Google link",
|
||||
"url": "https://google.com",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('applies a merge strategy for links when they exist in ds config and panel', () => {
|
||||
const dsFieldConfig: FieldConfig = {
|
||||
links: [
|
||||
{
|
||||
title: 'Google link',
|
||||
url: 'https://google.com',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const panelFieldConfig: FieldConfig = {
|
||||
links: [
|
||||
{
|
||||
title: 'Grafana',
|
||||
url: 'https://grafana.com',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const context: FieldOverrideEnv = {
|
||||
data: [],
|
||||
field: { type: FieldType.number } as Field,
|
||||
dataFrameIndex: 0,
|
||||
fieldConfigRegistry: customFieldRegistry,
|
||||
};
|
||||
|
||||
// we mutate dsFieldConfig
|
||||
setFieldConfigDefaults(dsFieldConfig, panelFieldConfig, context);
|
||||
|
||||
expect(dsFieldConfig).toMatchInlineSnapshot(`
|
||||
{
|
||||
"custom": {},
|
||||
"links": [
|
||||
{
|
||||
"title": "Google link",
|
||||
"url": "https://google.com",
|
||||
},
|
||||
{
|
||||
"title": "Grafana",
|
||||
"url": "https://grafana.com",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setDynamicConfigValue', () => {
|
||||
|
||||
@@ -293,6 +293,11 @@ export function setDynamicConfigValue(config: FieldConfig, value: DynamicConfigV
|
||||
// config -> from DS
|
||||
// defaults -> from Panel config
|
||||
export function setFieldConfigDefaults(config: FieldConfig, defaults: FieldConfig, context: FieldOverrideEnv) {
|
||||
// For cases where we have links on the datasource config and the panel config, we need to merge them
|
||||
if (config.links && defaults.links) {
|
||||
// Combine the data source links and the panel default config links
|
||||
config.links = [...config.links, ...defaults.links];
|
||||
}
|
||||
for (const fieldConfigProperty of context.fieldConfigRegistry.list()) {
|
||||
if (fieldConfigProperty.isCustom && !config.custom) {
|
||||
config.custom = {};
|
||||
|
||||
@@ -97,7 +97,7 @@ export class AppPlugin<T extends KeyValue = KeyValue> extends GrafanaPlugin<AppP
|
||||
return this._extensionConfigs;
|
||||
}
|
||||
|
||||
configureExtensionLink<Context extends object>(extension: Exclude<PluginExtensionLinkConfig<Context>, 'type'>) {
|
||||
configureExtensionLink<Context extends object>(extension: Omit<PluginExtensionLinkConfig<Context>, 'type'>) {
|
||||
this._extensionConfigs.push({
|
||||
...extension,
|
||||
type: PluginExtensionTypes.link,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
|
||||
import { RawTimeRange, TimeZone } from './time';
|
||||
|
||||
// Plugin Extensions types
|
||||
@@ -18,7 +20,7 @@ export type PluginExtension = {
|
||||
export type PluginExtensionLink = PluginExtension & {
|
||||
type: PluginExtensionTypes.link;
|
||||
path?: string;
|
||||
onClick?: (event: React.MouseEvent) => void;
|
||||
onClick?: (event?: React.MouseEvent) => void;
|
||||
};
|
||||
|
||||
// Objects used for registering extensions (in app plugins)
|
||||
@@ -43,7 +45,7 @@ export type PluginExtensionLinkConfig<Context extends object = object> = PluginE
|
||||
Context,
|
||||
Pick<PluginExtensionLink, 'path'> & {
|
||||
type: PluginExtensionTypes.link;
|
||||
onClick?: (event: React.MouseEvent, helpers: PluginExtensionEventHelpers<Context>) => void;
|
||||
onClick?: (event: React.MouseEvent | undefined, helpers: PluginExtensionEventHelpers<Context>) => void;
|
||||
}
|
||||
>;
|
||||
|
||||
@@ -73,7 +75,7 @@ export type PluginExtensionPanelContext = {
|
||||
timeRange: RawTimeRange;
|
||||
timeZone: TimeZone;
|
||||
dashboard: Dashboard;
|
||||
targets: Target[];
|
||||
targets: DataQuery[];
|
||||
};
|
||||
|
||||
type Dashboard = {
|
||||
@@ -81,8 +83,3 @@ type Dashboard = {
|
||||
title: string;
|
||||
tags: string[];
|
||||
};
|
||||
|
||||
type Target = {
|
||||
pluginId: string;
|
||||
refId: string;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/e2e-selectors",
|
||||
"version": "9.5.0-pre",
|
||||
"version": "9.5.0",
|
||||
"description": "Grafana End-to-End Test Selectors Library",
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/e2e",
|
||||
"version": "9.5.0-pre",
|
||||
"version": "9.5.0",
|
||||
"description": "Grafana End-to-End Test Library",
|
||||
"keywords": [
|
||||
"cli",
|
||||
@@ -63,7 +63,7 @@
|
||||
"@babel/core": "7.20.5",
|
||||
"@babel/preset-env": "7.20.2",
|
||||
"@cypress/webpack-preprocessor": "5.17.0",
|
||||
"@grafana/e2e-selectors": "9.5.0-pre",
|
||||
"@grafana/e2e-selectors": "9.5.0",
|
||||
"@grafana/tsconfig": "^1.2.0-rc1",
|
||||
"@mochajs/json-file-reporter": "^1.2.0",
|
||||
"babel-loader": "9.1.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@grafana/eslint-plugin",
|
||||
"description": "ESLint rules for use within the Grafana repo. Not suitable (or supported) for external use.",
|
||||
"version": "9.5.0-pre",
|
||||
"version": "9.5.0",
|
||||
"main": "./index.cjs",
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/runtime",
|
||||
"version": "9.5.0-pre",
|
||||
"version": "9.5.0",
|
||||
"description": "Grafana Runtime Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -37,10 +37,10 @@
|
||||
"postpack": "mv package.json.bak package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grafana/data": "9.5.0-pre",
|
||||
"@grafana/e2e-selectors": "9.5.0-pre",
|
||||
"@grafana/data": "9.5.0",
|
||||
"@grafana/e2e-selectors": "9.5.0",
|
||||
"@grafana/faro-web-sdk": "1.0.2",
|
||||
"@grafana/ui": "9.5.0-pre",
|
||||
"@grafana/ui": "9.5.0",
|
||||
"@sentry/browser": "6.19.7",
|
||||
"history": "4.10.1",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/schema",
|
||||
"version": "9.5.0-pre",
|
||||
"version": "9.5.0",
|
||||
"description": "Grafana Schema Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/toolkit",
|
||||
"version": "9.5.0-pre",
|
||||
"version": "9.5.0",
|
||||
"description": "Grafana Toolkit",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -51,10 +51,10 @@
|
||||
"@babel/preset-env": "7.18.9",
|
||||
"@babel/preset-react": "7.18.6",
|
||||
"@babel/preset-typescript": "7.18.6",
|
||||
"@grafana/data": "9.5.0-pre",
|
||||
"@grafana/data": "9.5.0",
|
||||
"@grafana/eslint-config": "5.1.0",
|
||||
"@grafana/tsconfig": "^1.2.0-rc1",
|
||||
"@grafana/ui": "9.5.0-pre",
|
||||
"@grafana/ui": "9.5.0",
|
||||
"@jest/core": "27.5.1",
|
||||
"@types/command-exists": "^1.2.0",
|
||||
"@types/eslint": "8.4.1",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/ui",
|
||||
"version": "9.5.0-pre",
|
||||
"version": "9.5.0",
|
||||
"description": "Grafana Components Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -49,10 +49,10 @@
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.10.6",
|
||||
"@emotion/react": "11.10.6",
|
||||
"@grafana/data": "9.5.0-pre",
|
||||
"@grafana/e2e-selectors": "9.5.0-pre",
|
||||
"@grafana/data": "9.5.0",
|
||||
"@grafana/e2e-selectors": "9.5.0",
|
||||
"@grafana/faro-web-sdk": "1.0.2",
|
||||
"@grafana/schema": "9.5.0-pre",
|
||||
"@grafana/schema": "9.5.0",
|
||||
"@leeoniya/ufuzzy": "1.0.6",
|
||||
"@monaco-editor/react": "4.4.6",
|
||||
"@popperjs/core": "2.11.6",
|
||||
|
||||
@@ -206,6 +206,14 @@ var Versions = VersionMap{
|
||||
},
|
||||
CloudMode: {
|
||||
Variants: []Variant{
|
||||
VariantArmV6,
|
||||
VariantArmV7,
|
||||
VariantArmV7Musl,
|
||||
VariantArm64,
|
||||
VariantArm64Musl,
|
||||
VariantDarwinAmd64,
|
||||
VariantWindowsAmd64,
|
||||
VariantLinuxAmd64,
|
||||
VariantLinuxAmd64Musl,
|
||||
},
|
||||
PluginSignature: PluginSignature{
|
||||
@@ -216,9 +224,12 @@ var Versions = VersionMap{
|
||||
ShouldSave: true,
|
||||
Architectures: []Architecture{
|
||||
ArchAMD64,
|
||||
ArchARM64,
|
||||
ArchARMv7,
|
||||
},
|
||||
Distribution: []Distribution{
|
||||
Alpine,
|
||||
Ubuntu,
|
||||
},
|
||||
PrereleaseBucket: "grafana-prerelease/artifacts/docker",
|
||||
},
|
||||
|
||||
@@ -320,3 +320,15 @@ func (s OpentelemetrySpan) AddEvents(keys []string, values []EventValue) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s OpentelemetrySpan) contextWithSpan(ctx context.Context) context.Context {
|
||||
if s.span != nil {
|
||||
ctx = trace.ContextWithSpan(ctx, s.span)
|
||||
// Grafana also manages its own separate traceID in the context in addition to what opentracing handles.
|
||||
// It's derived from the span. Ensure that we propagate this too.
|
||||
if traceID := s.span.SpanContext().TraceID(); traceID.IsValid() {
|
||||
ctx = context.WithValue(ctx, traceKey{}, traceValue{traceID.String(), s.span.SpanContext().IsSampled()})
|
||||
}
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@@ -92,6 +92,10 @@ func (t *FakeSpan) AddEvents(keys []string, values []EventValue) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *FakeSpan) contextWithSpan(ctx context.Context) context.Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
type FakeTracer struct {
|
||||
Spans []*FakeSpan
|
||||
}
|
||||
|
||||
@@ -76,6 +76,10 @@ type Span interface {
|
||||
//
|
||||
// Panics if the length of keys is shorter than the length of values.
|
||||
AddEvents(keys []string, values []EventValue)
|
||||
|
||||
// contextWithSpan returns a context.Context that holds the parent
|
||||
// context plus a reference to this span.
|
||||
contextWithSpan(ctx context.Context) context.Context
|
||||
}
|
||||
|
||||
func ProvideService(cfg *setting.Cfg) (Tracer, error) {
|
||||
@@ -146,6 +150,29 @@ func TraceIDFromContext(c context.Context, requireSampled bool) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// SpanFromContext returns the Span previously associated with ctx, or nil, if no such span could be found.
|
||||
// It is the equivalent of opentracing.SpanFromContext and trace.SpanFromContext.
|
||||
func SpanFromContext(ctx context.Context) Span {
|
||||
// Look for both opentracing and opentelemetry spans.
|
||||
if span := opentracing.SpanFromContext(ctx); span != nil {
|
||||
return OpentracingSpan{span: span}
|
||||
}
|
||||
if span := trace.SpanFromContext(ctx); span != nil {
|
||||
return OpentelemetrySpan{span: span}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextWithSpan returns a new context.Context that holds a reference to the given span.
|
||||
// If span is nil, a new context without an active span is returned.
|
||||
// It is the equivalent of opentracing.ContextWithSpan and trace.ContextWithSpan.
|
||||
func ContextWithSpan(ctx context.Context, span Span) context.Context {
|
||||
if span != nil {
|
||||
return span.contextWithSpan(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
type Opentracing struct {
|
||||
enabled bool
|
||||
address string
|
||||
@@ -324,6 +351,18 @@ func (s OpentracingSpan) AddEvents(keys []string, values []EventValue) {
|
||||
s.span.LogFields(fields...)
|
||||
}
|
||||
|
||||
func (s OpentracingSpan) contextWithSpan(ctx context.Context) context.Context {
|
||||
if s.span != nil {
|
||||
ctx = opentracing.ContextWithSpan(ctx, s.span)
|
||||
// Grafana also manages its own separate traceID in the context in addition to what opentracing handles.
|
||||
// It's derived from the span. Ensure that we propagate this too.
|
||||
if sctx, ok := s.span.Context().(jaeger.SpanContext); ok {
|
||||
ctx = context.WithValue(ctx, traceKey{}, traceValue{sctx.TraceID().String(), sctx.IsSampled()})
|
||||
}
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func splitTagSettings(input string) map[string]string {
|
||||
res := map[string]string{}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
|
||||
const authQueryParamName = "auth_token"
|
||||
|
||||
var _ authn.ContextAwareClient = new(JWT)
|
||||
|
||||
var (
|
||||
@@ -50,6 +52,7 @@ func (s *JWT) Name() string {
|
||||
|
||||
func (s *JWT) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identity, error) {
|
||||
jwtToken := s.retrieveToken(r.HTTPRequest)
|
||||
s.stripSensitiveParam(r.HTTPRequest)
|
||||
|
||||
claims, err := s.jwtService.Verify(ctx, jwtToken)
|
||||
if err != nil {
|
||||
@@ -120,6 +123,18 @@ func (s *JWT) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identi
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// remove sensitive query param
|
||||
// avoid JWT URL login passing auth_token in URL
|
||||
func (s *JWT) stripSensitiveParam(httpRequest *http.Request) {
|
||||
if s.cfg.JWTAuthURLLogin {
|
||||
params := httpRequest.URL.Query()
|
||||
if params.Has(authQueryParamName) {
|
||||
params.Del(authQueryParamName)
|
||||
httpRequest.URL.RawQuery = params.Encode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// retrieveToken retrieves the JWT token from the request.
|
||||
func (s *JWT) retrieveToken(httpRequest *http.Request) string {
|
||||
jwtToken := httpRequest.Header.Get(s.cfg.JWTAuthHeaderName)
|
||||
|
||||
@@ -307,3 +307,47 @@ func TestJWTTest(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJWTStripParam(t *testing.T) {
|
||||
jwtService := &jwt.FakeJWTService{
|
||||
VerifyProvider: func(context.Context, string) (jwt.JWTClaims, error) {
|
||||
return jwt.JWTClaims{
|
||||
"sub": "1234567890",
|
||||
"email": "eai.doe@cor.po",
|
||||
"preferred_username": "eai-doe",
|
||||
"name": "Eai Doe",
|
||||
"roles": "Admin",
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
jwtHeaderName := "X-Forwarded-User"
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
JWTAuthEnabled: true,
|
||||
JWTAuthHeaderName: jwtHeaderName,
|
||||
JWTAuthAutoSignUp: true,
|
||||
JWTAuthAllowAssignGrafanaAdmin: true,
|
||||
JWTAuthURLLogin: true,
|
||||
JWTAuthRoleAttributeStrict: false,
|
||||
JWTAuthRoleAttributePath: "roles",
|
||||
JWTAuthEmailClaim: "email",
|
||||
JWTAuthUsernameClaim: "preferred_username",
|
||||
}
|
||||
|
||||
// #nosec G101 -- This is a dummy/test token
|
||||
token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o"
|
||||
|
||||
httpReq := &http.Request{
|
||||
URL: &url.URL{RawQuery: "auth_token=" + token + "&other_param=other_value"},
|
||||
}
|
||||
jwtClient := ProvideJWT(jwtService, cfg)
|
||||
_, err := jwtClient.Authenticate(context.Background(), &authn.Request{
|
||||
OrgID: 1,
|
||||
HTTPRequest: httpReq,
|
||||
Resp: nil,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// auth_token should be removed from the query string
|
||||
assert.Equal(t, "other_param=other_value", httpReq.URL.RawQuery)
|
||||
}
|
||||
|
||||
@@ -15,12 +15,14 @@ import (
|
||||
loginsvc "github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
const (
|
||||
InvalidJWT = "Invalid JWT"
|
||||
InvalidRole = "Invalid Role"
|
||||
UserNotFound = "User not found"
|
||||
InvalidJWT = "Invalid JWT"
|
||||
InvalidRole = "Invalid Role"
|
||||
UserNotFound = "User not found"
|
||||
authQueryParamName = "auth_token"
|
||||
)
|
||||
|
||||
func (h *ContextHandler) initContextWithJWT(ctx *contextmodel.ReqContext, orgId int64) bool {
|
||||
@@ -30,13 +32,15 @@ func (h *ContextHandler) initContextWithJWT(ctx *contextmodel.ReqContext, orgId
|
||||
|
||||
jwtToken := ctx.Req.Header.Get(h.Cfg.JWTAuthHeaderName)
|
||||
if jwtToken == "" && h.Cfg.JWTAuthURLLogin {
|
||||
jwtToken = ctx.Req.URL.Query().Get("auth_token")
|
||||
jwtToken = ctx.Req.URL.Query().Get(authQueryParamName)
|
||||
}
|
||||
|
||||
if jwtToken == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
stripSensitiveParam(h.Cfg, ctx.Req)
|
||||
|
||||
// Strip the 'Bearer' prefix if it exists.
|
||||
jwtToken = strings.TrimPrefix(jwtToken, "Bearer ")
|
||||
|
||||
@@ -204,3 +208,15 @@ func searchClaimsForStringAttr(attributePath string, claims map[string]interface
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// remove sensitive query params
|
||||
// avoid JWT URL login passing auth_token in URL
|
||||
func stripSensitiveParam(cfg *setting.Cfg, httpRequest *http.Request) {
|
||||
if cfg.JWTAuthURLLogin {
|
||||
params := httpRequest.URL.Query()
|
||||
if params.Has(authQueryParamName) {
|
||||
params.Del(authQueryParamName)
|
||||
httpRequest.URL.RawQuery = params.Encode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/annotations"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
@@ -67,10 +68,23 @@ func (h *AnnotationBackend) Record(ctx context.Context, rule history_model.RuleM
|
||||
return errCh
|
||||
}
|
||||
|
||||
go func() {
|
||||
// This is a new background job, so let's create a brand new context for it.
|
||||
// We want it to be isolated, i.e. we don't want grafana shutdowns to interrupt this work
|
||||
// immediately but rather try to flush writes.
|
||||
// This also prevents timeouts or other lingering objects (like transactions) from being
|
||||
// incorrectly propagated here from other areas.
|
||||
writeCtx := context.Background()
|
||||
writeCtx, cancel := context.WithTimeout(writeCtx, StateHistoryWriteTimeout)
|
||||
writeCtx = history_model.WithRuleData(writeCtx, rule)
|
||||
writeCtx = tracing.ContextWithSpan(writeCtx, tracing.SpanFromContext(ctx))
|
||||
|
||||
go func(ctx context.Context) {
|
||||
defer cancel()
|
||||
defer close(errCh)
|
||||
logger := h.log.FromContext(ctx)
|
||||
|
||||
errCh <- h.recordAnnotations(ctx, panel, annotations, rule.OrgID, logger)
|
||||
}()
|
||||
}(writeCtx)
|
||||
return errCh
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package historian
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
|
||||
@@ -12,6 +13,8 @@ import (
|
||||
history_model "github.com/grafana/grafana/pkg/services/ngalert/state/historian/model"
|
||||
)
|
||||
|
||||
const StateHistoryWriteTimeout = time.Minute
|
||||
|
||||
func shouldRecord(transition state.StateTransition) bool {
|
||||
if !transition.Changed() {
|
||||
return false
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
@@ -80,8 +81,20 @@ func (h *RemoteLokiBackend) Record(ctx context.Context, rule history_model.RuleM
|
||||
return errCh
|
||||
}
|
||||
|
||||
go func() {
|
||||
// This is a new background job, so let's create a brand new context for it.
|
||||
// We want it to be isolated, i.e. we don't want grafana shutdowns to interrupt this work
|
||||
// immediately but rather try to flush writes.
|
||||
// This also prevents timeouts or other lingering objects (like transactions) from being
|
||||
// incorrectly propagated here from other areas.
|
||||
writeCtx := context.Background()
|
||||
writeCtx, cancel := context.WithTimeout(writeCtx, StateHistoryWriteTimeout)
|
||||
writeCtx = history_model.WithRuleData(writeCtx, rule)
|
||||
writeCtx = tracing.ContextWithSpan(writeCtx, tracing.SpanFromContext(ctx))
|
||||
|
||||
go func(ctx context.Context) {
|
||||
defer cancel()
|
||||
defer close(errCh)
|
||||
logger := h.log.FromContext(ctx)
|
||||
|
||||
org := fmt.Sprint(rule.OrgID)
|
||||
h.metrics.WritesTotal.WithLabelValues(org, "loki").Inc()
|
||||
@@ -93,7 +106,7 @@ func (h *RemoteLokiBackend) Record(ctx context.Context, rule history_model.RuleM
|
||||
h.metrics.TransitionsFailed.WithLabelValues(org).Add(float64(len(logStream.Values)))
|
||||
errCh <- fmt.Errorf("failed to save alert state history batch: %w", err)
|
||||
}
|
||||
}()
|
||||
}(writeCtx)
|
||||
return errCh
|
||||
}
|
||||
|
||||
|
||||
@@ -17,12 +17,8 @@ import (
|
||||
"github.com/weaveworks/common/http/client"
|
||||
)
|
||||
|
||||
const defaultClientTimeout = 30 * time.Second
|
||||
|
||||
func NewRequester() client.Requester {
|
||||
return &http.Client{
|
||||
Timeout: defaultClientTimeout,
|
||||
}
|
||||
return &http.Client{}
|
||||
}
|
||||
|
||||
// encoder serializes log streams to some byte format.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
|
||||
@@ -44,3 +45,7 @@ func NewRuleMeta(r *models.AlertRule, log log.Logger) RuleMeta {
|
||||
PanelID: panelID,
|
||||
}
|
||||
}
|
||||
|
||||
func WithRuleData(ctx context.Context, rule RuleMeta) context.Context {
|
||||
return models.WithRuleKey(ctx, models.AlertRuleKey{OrgID: rule.OrgID, UID: rule.UID})
|
||||
}
|
||||
|
||||
@@ -213,7 +213,10 @@ func getUniqueDashboardDatasourceUids(dashboard *simplejson.Json) []string {
|
||||
var datasourceUids []string
|
||||
exists := map[string]bool{}
|
||||
|
||||
for _, panelObj := range dashboard.Get("panels").MustArray() {
|
||||
// collapsed rows contain panels in a nested structure, so we need to flatten them before calculate unique uids
|
||||
flattenedPanels := getFlattenedPanels(dashboard)
|
||||
|
||||
for _, panelObj := range flattenedPanels {
|
||||
panel := simplejson.NewFromAny(panelObj)
|
||||
uid := getDataSourceUidFromJson(panel)
|
||||
|
||||
@@ -238,6 +241,23 @@ func getUniqueDashboardDatasourceUids(dashboard *simplejson.Json) []string {
|
||||
return datasourceUids
|
||||
}
|
||||
|
||||
func getFlattenedPanels(dashboard *simplejson.Json) []interface{} {
|
||||
var flatPanels []interface{}
|
||||
for _, panelObj := range dashboard.Get("panels").MustArray() {
|
||||
panel := simplejson.NewFromAny(panelObj)
|
||||
// if the panel is a row and it is collapsed, get the queries from the panels inside the row
|
||||
// if it is not collapsed, the row does not have any panels
|
||||
if panel.Get("type").MustString() == "row" {
|
||||
if panel.Get("collapsed").MustBool() {
|
||||
flatPanels = append(flatPanels, panel.Get("panels").MustArray()...)
|
||||
}
|
||||
} else {
|
||||
flatPanels = append(flatPanels, panelObj)
|
||||
}
|
||||
}
|
||||
return flatPanels
|
||||
}
|
||||
|
||||
func groupQueriesByPanelId(dashboard *simplejson.Json) map[int64][]*simplejson.Json {
|
||||
result := make(map[int64][]*simplejson.Json)
|
||||
|
||||
|
||||
@@ -432,6 +432,218 @@ const (
|
||||
],
|
||||
"schemaVersion": 35
|
||||
}`
|
||||
|
||||
dashboardWithCollapsedRows = `
|
||||
{
|
||||
"panels": [
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 12,
|
||||
"title": "Row title",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "qCbTUC37k"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 0,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "auto",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 1
|
||||
},
|
||||
"id": 11,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "qCbTUC37k"
|
||||
},
|
||||
"editorMode": "builder",
|
||||
"expr": "access_evaluation_duration_bucket",
|
||||
"legendFormat": "__auto",
|
||||
"range": true,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Panel Title",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"collapsed": true,
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 9
|
||||
},
|
||||
"id": 10,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "P49A45DF074423DFB"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 0,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "auto",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 10
|
||||
},
|
||||
"id": 8,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "9.4.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "P49A45DF074423DFB"
|
||||
},
|
||||
"query": "// v.bucket, v.timeRangeStart, and v.timeRange stop are all variables supported by the flux plugin and influxdb\nfrom(bucket: v.bucket)\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_value\"] >= 10 and r[\"_value\"] <= 20)",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Panel Title",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"title": "Row title 1",
|
||||
"type": "row"
|
||||
}
|
||||
]
|
||||
}`
|
||||
)
|
||||
|
||||
func TestGetQueryDataResponse(t *testing.T) {
|
||||
@@ -951,6 +1163,16 @@ func TestGetUniqueDashboardDatasourceUids(t *testing.T) {
|
||||
uids := getUniqueDashboardDatasourceUids(json)
|
||||
require.Len(t, uids, 0)
|
||||
})
|
||||
|
||||
t.Run("can get unique datasource ids from dashboard with rows", func(t *testing.T) {
|
||||
json, err := simplejson.NewJson([]byte(dashboardWithCollapsedRows))
|
||||
require.NoError(t, err)
|
||||
|
||||
uids := getUniqueDashboardDatasourceUids(json)
|
||||
require.Len(t, uids, 2)
|
||||
require.Equal(t, "qCbTUC37k", uids[0])
|
||||
require.Equal(t, "P49A45DF074423DFB", uids[1])
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildMetricRequest(t *testing.T) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@grafana-plugins/input-datasource",
|
||||
"version": "9.5.0-pre",
|
||||
"version": "9.5.0",
|
||||
"description": "Input Datasource",
|
||||
"private": true,
|
||||
"repository": {
|
||||
@@ -15,15 +15,15 @@
|
||||
},
|
||||
"author": "Grafana Labs",
|
||||
"devDependencies": {
|
||||
"@grafana/toolkit": "9.5.0-pre",
|
||||
"@grafana/toolkit": "9.5.0",
|
||||
"@types/jest": "26.0.15",
|
||||
"@types/lodash": "4.14.149",
|
||||
"@types/react": "17.0.30",
|
||||
"lodash": "4.17.21"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grafana/data": "9.5.0-pre",
|
||||
"@grafana/ui": "9.5.0-pre",
|
||||
"@grafana/data": "9.5.0",
|
||||
"@grafana/ui": "9.5.0",
|
||||
"jquery": "3.5.1",
|
||||
"react": "17.0.1",
|
||||
"react-dom": "17.0.1",
|
||||
|
||||
@@ -228,7 +228,9 @@ describe('getPanelMenu()', () => {
|
||||
targets: [
|
||||
{
|
||||
refId: 'A',
|
||||
pluginId: 'testdata',
|
||||
datasource: {
|
||||
type: 'testdata',
|
||||
},
|
||||
},
|
||||
],
|
||||
dashboard: {
|
||||
|
||||
@@ -332,16 +332,13 @@ function createExtensionContext(panel: PanelModel, dashboard: DashboardModel): P
|
||||
id: panel.id,
|
||||
pluginId: panel.type,
|
||||
title: panel.title,
|
||||
timeRange: Object.assign({}, dashboard.time),
|
||||
timeRange: dashboard.time,
|
||||
timeZone: dashboard.timezone,
|
||||
dashboard: {
|
||||
uid: dashboard.uid,
|
||||
title: dashboard.title,
|
||||
tags: Array.from<string>(dashboard.tags),
|
||||
},
|
||||
targets: panel.targets.map((t) => ({
|
||||
refId: t.refId,
|
||||
pluginId: t.datasource?.type ?? 'unknown',
|
||||
})),
|
||||
targets: panel.targets,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -246,4 +246,43 @@ describe('getPluginExtensions()', () => {
|
||||
expect(global.console.warn).toHaveBeenCalledTimes(1);
|
||||
expect(global.console.warn).toHaveBeenCalledWith('[Plugin Extensions] Something went wrong!');
|
||||
});
|
||||
|
||||
test('should pass a frozen copy of the context to the onClick() function', () => {
|
||||
const context = { title: 'New title from the context!' };
|
||||
|
||||
link2.path = undefined;
|
||||
link2.onClick = jest.fn();
|
||||
|
||||
const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]);
|
||||
const { extensions } = getPluginExtensions({ registry, context, extensionPointId: extensionPoint2 });
|
||||
const [extension] = extensions;
|
||||
|
||||
assertPluginExtensionLink(extension);
|
||||
extension.onClick?.({} as React.MouseEvent);
|
||||
|
||||
const helpers = (link2.onClick as jest.Mock).mock.calls[0][1];
|
||||
|
||||
expect(link2.configure).toHaveBeenCalledTimes(1);
|
||||
expect(Object.isFrozen(helpers.context)).toBe(true);
|
||||
expect(() => {
|
||||
helpers.context.title = 'New title';
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test('should should not freeze the original context', () => {
|
||||
const context = {
|
||||
title: 'New title from the context!',
|
||||
nested: { title: 'title' },
|
||||
array: ['a'],
|
||||
};
|
||||
|
||||
const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]);
|
||||
getPluginExtensions({ registry, context, extensionPointId: extensionPoint2 });
|
||||
|
||||
expect(() => {
|
||||
context.title = 'Updating the title';
|
||||
context.nested.title = 'new title';
|
||||
context.array.push('b');
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -108,14 +108,14 @@ function getLinkExtensionOverrides(pluginId: string, config: PluginExtensionLink
|
||||
function getLinkExtensionOnClick(
|
||||
config: PluginExtensionLinkConfig,
|
||||
context?: object
|
||||
): ((event: React.MouseEvent) => void) | undefined {
|
||||
): ((event?: React.MouseEvent) => void) | undefined {
|
||||
const { onClick } = config;
|
||||
|
||||
if (!onClick) {
|
||||
return;
|
||||
}
|
||||
|
||||
return function onClickExtensionLink(event: React.MouseEvent) {
|
||||
return function onClickExtensionLink(event?: React.MouseEvent) {
|
||||
try {
|
||||
const result = onClick(event, getEventHelpers(context));
|
||||
|
||||
|
||||
@@ -246,6 +246,20 @@ describe('runSplitQuery()', () => {
|
||||
expect(datasource.runQuery).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
||||
test('with mixed splitDuration runs the expected amount of queries', async () => {
|
||||
const request = getQueryOptions<LokiQuery>({
|
||||
targets: [
|
||||
{ expr: 'count_over_time({c="d"}[1m])', refId: 'A', splitDuration: '15m' },
|
||||
{ expr: '{a="b"}', refId: 'B', splitDuration: '15m' },
|
||||
{ expr: '{a="b"}', refId: 'C', splitDuration: '1h' },
|
||||
],
|
||||
range: range1h,
|
||||
});
|
||||
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
|
||||
// 4 * 15m + 4 * 15m + 1 * 1h
|
||||
expect(datasource.runQuery).toHaveBeenCalledTimes(9);
|
||||
});
|
||||
});
|
||||
test('with 1h/30m splitDuration and 1 log and 2 metric target runs 3 queries', async () => {
|
||||
const request = getQueryOptions<LokiQuery>({
|
||||
targets: [
|
||||
|
||||
@@ -154,11 +154,14 @@ export function runSplitGroupedQueries(datasource: LokiDatasource, requests: Lok
|
||||
|
||||
function getNextRequestPointers(requests: LokiGroupedRequest, requestGroup: number, requestN: number) {
|
||||
// There's a pending request from the next group:
|
||||
if (requests[requestGroup + 1]?.partition[requestN - 1]) {
|
||||
return {
|
||||
nextRequestGroup: requestGroup + 1,
|
||||
nextRequestN: requestN,
|
||||
};
|
||||
for (let i = requestGroup + 1; i < requests.length; i++) {
|
||||
const group = requests[i];
|
||||
if (group.partition[requestN - 1]) {
|
||||
return {
|
||||
nextRequestGroup: i,
|
||||
nextRequestN: requestN,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
// Find the first group where `[requestN - 1]` is defined
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Map as OpenLayersMap } from 'ol';
|
||||
import { FeatureLike } from 'ol/Feature';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import { getFrameMatchers, MapLayerHandler, MapLayerOptions, PanelData } from '@grafana/data/src';
|
||||
import { getFrameMatchers, MapLayerHandler, MapLayerOptions, PanelData, textUtil } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime/src';
|
||||
|
||||
import { GeomapPanel } from '../GeomapPanel';
|
||||
@@ -114,6 +114,10 @@ export async function initLayer(
|
||||
return Promise.reject('unknown layer: ' + options.type);
|
||||
}
|
||||
|
||||
if (options.config?.attribution) {
|
||||
options.config.attribution = textUtil.sanitizeTextPanelContent(options.config.attribution);
|
||||
}
|
||||
|
||||
const handler = await item.create(map, options, panel.props.eventBus, config.theme2);
|
||||
const layer = handler.init(); // eslint-disable-line
|
||||
if (options.opacity != null) {
|
||||
|
||||
@@ -84,7 +84,7 @@ export const PieChart = ({
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<svg width={layout.size} height={layout.size} ref={containerRef}>
|
||||
<svg width={layout.size} height={layout.size} ref={containerRef} style={{ overflow: 'visible' }}>
|
||||
<Group top={layout.position} left={layout.position}>
|
||||
{colors.map((color) => {
|
||||
return (
|
||||
|
||||
@@ -1,142 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Table Migrations migrates styles to field config overrides and defaults 1`] = `
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"align": "right",
|
||||
"displayMode": undefined,
|
||||
},
|
||||
"decimals": 2,
|
||||
"displayName": "",
|
||||
"unit": "short",
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Time",
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "displayName",
|
||||
"value": "Time",
|
||||
},
|
||||
{
|
||||
"id": "unit",
|
||||
"value": "time: YYYY-MM-DD HH:mm:ss",
|
||||
},
|
||||
{
|
||||
"id": "custom.align",
|
||||
"value": null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "ColorCell",
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "unit",
|
||||
"value": "currencyUSD",
|
||||
},
|
||||
{
|
||||
"id": "decimals",
|
||||
"value": 2,
|
||||
},
|
||||
{
|
||||
"id": "custom.displayMode",
|
||||
"value": "color-background",
|
||||
},
|
||||
{
|
||||
"id": "custom.align",
|
||||
"value": "left",
|
||||
},
|
||||
{
|
||||
"id": "thresholds",
|
||||
"value": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "rgba(245, 54, 54, 0.9)",
|
||||
"value": -Infinity,
|
||||
},
|
||||
{
|
||||
"color": "rgba(237, 129, 40, 0.89)",
|
||||
"value": 5,
|
||||
},
|
||||
{
|
||||
"color": "rgba(50, 172, 45, 0.97)",
|
||||
"value": 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "ColorValue",
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "unit",
|
||||
"value": "Bps",
|
||||
},
|
||||
{
|
||||
"id": "decimals",
|
||||
"value": 2,
|
||||
},
|
||||
{
|
||||
"id": "links",
|
||||
"value": [
|
||||
{
|
||||
"targetBlank": true,
|
||||
"title": "",
|
||||
"url": "http://www.grafana.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "custom.displayMode",
|
||||
"value": "color-text",
|
||||
},
|
||||
{
|
||||
"id": "custom.align",
|
||||
"value": null,
|
||||
},
|
||||
{
|
||||
"id": "thresholds",
|
||||
"value": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "rgba(245, 54, 54, 0.9)",
|
||||
"value": -Infinity,
|
||||
},
|
||||
{
|
||||
"color": "rgba(237, 129, 40, 0.89)",
|
||||
"value": 5,
|
||||
},
|
||||
{
|
||||
"color": "rgba(50, 172, 45, 0.97)",
|
||||
"value": 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
"transformations": [],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Table Migrations migrates transform out to core transforms 1`] = `
|
||||
{
|
||||
"fieldConfig": {
|
||||
|
||||
@@ -128,6 +128,144 @@ describe('Table Migrations', () => {
|
||||
};
|
||||
const panel = {} as PanelModel;
|
||||
tablePanelChangedHandler(panel, 'table-old', oldStyles);
|
||||
expect(panel).toMatchSnapshot();
|
||||
expect(panel).toMatchInlineSnapshot(`
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"align": "right",
|
||||
},
|
||||
"decimals": 2,
|
||||
"displayName": "",
|
||||
"unit": "short",
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Time",
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "displayName",
|
||||
"value": "Time",
|
||||
},
|
||||
{
|
||||
"id": "unit",
|
||||
"value": "time: YYYY-MM-DD HH:mm:ss",
|
||||
},
|
||||
{
|
||||
"id": "custom.align",
|
||||
"value": null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "ColorCell",
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "unit",
|
||||
"value": "currencyUSD",
|
||||
},
|
||||
{
|
||||
"id": "decimals",
|
||||
"value": 2,
|
||||
},
|
||||
{
|
||||
"id": "custom.cellOptions",
|
||||
"value": {
|
||||
"type": "color-background",
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "custom.align",
|
||||
"value": "left",
|
||||
},
|
||||
{
|
||||
"id": "thresholds",
|
||||
"value": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "rgba(245, 54, 54, 0.9)",
|
||||
"value": -Infinity,
|
||||
},
|
||||
{
|
||||
"color": "rgba(237, 129, 40, 0.89)",
|
||||
"value": 5,
|
||||
},
|
||||
{
|
||||
"color": "rgba(50, 172, 45, 0.97)",
|
||||
"value": 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "ColorValue",
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "unit",
|
||||
"value": "Bps",
|
||||
},
|
||||
{
|
||||
"id": "decimals",
|
||||
"value": 2,
|
||||
},
|
||||
{
|
||||
"id": "links",
|
||||
"value": [
|
||||
{
|
||||
"targetBlank": true,
|
||||
"title": "",
|
||||
"url": "http://www.grafana.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "custom.cellOptions",
|
||||
"value": {
|
||||
"type": "color-text",
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "custom.align",
|
||||
"value": null,
|
||||
},
|
||||
{
|
||||
"id": "thresholds",
|
||||
"value": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "rgba(245, 54, 54, 0.9)",
|
||||
"value": -Infinity,
|
||||
},
|
||||
{
|
||||
"color": "rgba(237, 129, 40, 0.89)",
|
||||
"value": 5,
|
||||
},
|
||||
{
|
||||
"color": "rgba(50, 172, 45, 0.97)",
|
||||
"value": 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
"transformations": [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -163,8 +163,10 @@ const migrateTableStyleToOverride = (style: Style) => {
|
||||
|
||||
if (style.colorMode) {
|
||||
override.properties.push({
|
||||
id: 'custom.displayMode',
|
||||
value: colorModeMap[style.colorMode],
|
||||
id: 'custom.cellOptions',
|
||||
value: {
|
||||
type: colorModeMap[style.colorMode],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -200,11 +202,11 @@ const migrateDefaults = (prevDefaults: Style) => {
|
||||
displayName: prevDefaults.alias,
|
||||
custom: {
|
||||
align: prevDefaults.align === 'auto' ? null : prevDefaults.align,
|
||||
displayMode: colorModeMap[prevDefaults.colorMode],
|
||||
},
|
||||
},
|
||||
isNil
|
||||
);
|
||||
|
||||
if (prevDefaults.thresholds.length) {
|
||||
const thresholds: ThresholdsConfig = {
|
||||
mode: ThresholdsMode.Absolute,
|
||||
@@ -212,6 +214,12 @@ const migrateDefaults = (prevDefaults: Style) => {
|
||||
};
|
||||
defaults.thresholds = thresholds;
|
||||
}
|
||||
|
||||
if (prevDefaults.colorMode) {
|
||||
defaults.custom.cellOptions = {
|
||||
type: colorModeMap[prevDefaults.colorMode],
|
||||
};
|
||||
}
|
||||
}
|
||||
return defaults;
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
TimeRange,
|
||||
} from '@grafana/data';
|
||||
import { LinkButton, usePanelContext, useStyles2, VerticalGroup, VizTooltipOptions } from '@grafana/ui';
|
||||
import { findField } from 'app/features/dimensions';
|
||||
import { getFieldLinksForExplore } from 'app/features/explore/utils/links';
|
||||
|
||||
import { ScatterSeriesConfig, SeriesMapping } from './models.gen';
|
||||
@@ -69,14 +70,16 @@ export const TooltipView = ({
|
||||
range,
|
||||
});
|
||||
|
||||
let extraFields: Field[] = frame.fields.filter((f) => f !== xField && f !== yField);
|
||||
|
||||
let yValue: YValue | null = null;
|
||||
let extraFacets: ExtraFacets | null = null;
|
||||
if (seriesMapping === SeriesMapping.Manual && manualSeriesConfigs) {
|
||||
const colorFacetFieldName = manualSeriesConfigs[hoveredPointIndex].pointColor?.field ?? '';
|
||||
const sizeFacetFieldName = manualSeriesConfigs[hoveredPointIndex].pointSize?.field ?? '';
|
||||
|
||||
const colorFacet = colorFacetFieldName ? frame.fields.find((f) => f.name === colorFacetFieldName) : undefined;
|
||||
const sizeFacet = sizeFacetFieldName ? frame.fields.find((f) => f.name === sizeFacetFieldName) : undefined;
|
||||
const colorFacet = colorFacetFieldName ? findField(frame, colorFacetFieldName) : undefined;
|
||||
const sizeFacet = sizeFacetFieldName ? findField(frame, sizeFacetFieldName) : undefined;
|
||||
|
||||
extraFacets = {
|
||||
colorFacetFieldName,
|
||||
@@ -84,6 +87,8 @@ export const TooltipView = ({
|
||||
colorFacetValue: colorFacet?.values.get(rowIndex),
|
||||
sizeFacetValue: sizeFacet?.values.get(rowIndex),
|
||||
};
|
||||
|
||||
extraFields = extraFields.filter((f) => f !== colorFacet && f !== sizeFacet);
|
||||
}
|
||||
|
||||
yValue = {
|
||||
@@ -101,7 +106,7 @@ export const TooltipView = ({
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{xField.name}</th>
|
||||
<th>{getFieldDisplayName(xField, frame)}</th>
|
||||
<td>{fmt(xField, xField.values.get(rowIndex))}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -120,6 +125,12 @@ export const TooltipView = ({
|
||||
<td>{extraFacets.sizeFacetValue}</td>
|
||||
</tr>
|
||||
)}
|
||||
{extraFields.map((field, i) => (
|
||||
<tr key={i}>
|
||||
<th>{getFieldDisplayName(field, frame)}:</th>
|
||||
<td>{fmt(field, field.values.get(rowIndex))}</td>
|
||||
</tr>
|
||||
))}
|
||||
{links.length > 0 && (
|
||||
<tr>
|
||||
<td colSpan={2}>
|
||||
|
||||
54
yarn.lock
54
yarn.lock
@@ -2988,9 +2988,9 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana-plugins/input-datasource@workspace:plugins-bundled/internal/input-datasource"
|
||||
dependencies:
|
||||
"@grafana/data": 9.5.0-pre
|
||||
"@grafana/toolkit": 9.5.0-pre
|
||||
"@grafana/ui": 9.5.0-pre
|
||||
"@grafana/data": 9.5.0
|
||||
"@grafana/toolkit": 9.5.0
|
||||
"@grafana/ui": 9.5.0
|
||||
"@types/jest": 26.0.15
|
||||
"@types/lodash": 4.14.149
|
||||
"@types/react": 17.0.30
|
||||
@@ -3023,12 +3023,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/data@9.5.0-pre, @grafana/data@workspace:*, @grafana/data@workspace:packages/grafana-data":
|
||||
"@grafana/data@9.5.0, @grafana/data@workspace:*, @grafana/data@workspace:packages/grafana-data":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/data@workspace:packages/grafana-data"
|
||||
dependencies:
|
||||
"@braintree/sanitize-url": 6.0.2
|
||||
"@grafana/schema": 9.5.0-pre
|
||||
"@grafana/schema": 9.5.0
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@rollup/plugin-commonjs": 23.0.2
|
||||
"@rollup/plugin-json": 5.0.1
|
||||
@@ -3088,7 +3088,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@grafana/e2e-selectors@9.5.0-pre, @grafana/e2e-selectors@workspace:*, @grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors":
|
||||
"@grafana/e2e-selectors@9.5.0, @grafana/e2e-selectors@^9.4.3, @grafana/e2e-selectors@workspace:*, @grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors"
|
||||
dependencies:
|
||||
@@ -3107,17 +3107,6 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@grafana/e2e-selectors@npm:^9.4.3":
|
||||
version: 9.4.3
|
||||
resolution: "@grafana/e2e-selectors@npm:9.4.3"
|
||||
dependencies:
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
tslib: 2.4.1
|
||||
typescript: 4.8.4
|
||||
checksum: 85a88cdf4adb643ff863b15f96fc6c04ecb7567c27cc526a00c157eb02575e55adc1e7701d58e1b48f00f24951c332fbb191cd2b5a8e74cd0c545543777e82af
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/e2e@workspace:*, @grafana/e2e@workspace:packages/grafana-e2e":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/e2e@workspace:packages/grafana-e2e"
|
||||
@@ -3125,7 +3114,7 @@ __metadata:
|
||||
"@babel/core": 7.20.5
|
||||
"@babel/preset-env": 7.20.2
|
||||
"@cypress/webpack-preprocessor": 5.17.0
|
||||
"@grafana/e2e-selectors": 9.5.0-pre
|
||||
"@grafana/e2e-selectors": 9.5.0
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@mochajs/json-file-reporter": ^1.2.0
|
||||
"@rollup/plugin-node-resolve": 15.0.1
|
||||
@@ -3285,11 +3274,11 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/runtime@workspace:packages/grafana-runtime"
|
||||
dependencies:
|
||||
"@grafana/data": 9.5.0-pre
|
||||
"@grafana/e2e-selectors": 9.5.0-pre
|
||||
"@grafana/data": 9.5.0
|
||||
"@grafana/e2e-selectors": 9.5.0
|
||||
"@grafana/faro-web-sdk": 1.0.2
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@grafana/ui": 9.5.0-pre
|
||||
"@grafana/ui": 9.5.0
|
||||
"@rollup/plugin-commonjs": 23.0.2
|
||||
"@rollup/plugin-node-resolve": 15.0.1
|
||||
"@sentry/browser": 6.19.7
|
||||
@@ -3340,7 +3329,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/schema@9.5.0-pre, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema":
|
||||
"@grafana/schema@9.5.0, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/schema@workspace:packages/grafana-schema"
|
||||
dependencies:
|
||||
@@ -3359,7 +3348,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@grafana/toolkit@9.5.0-pre, @grafana/toolkit@workspace:*, @grafana/toolkit@workspace:packages/grafana-toolkit":
|
||||
"@grafana/toolkit@9.5.0, @grafana/toolkit@workspace:*, @grafana/toolkit@workspace:packages/grafana-toolkit":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/toolkit@workspace:packages/grafana-toolkit"
|
||||
dependencies:
|
||||
@@ -3375,10 +3364,10 @@ __metadata:
|
||||
"@babel/preset-env": 7.18.9
|
||||
"@babel/preset-react": 7.18.6
|
||||
"@babel/preset-typescript": 7.18.6
|
||||
"@grafana/data": 9.5.0-pre
|
||||
"@grafana/data": 9.5.0
|
||||
"@grafana/eslint-config": 5.1.0
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@grafana/ui": 9.5.0-pre
|
||||
"@grafana/ui": 9.5.0
|
||||
"@jest/core": 27.5.1
|
||||
"@types/command-exists": ^1.2.0
|
||||
"@types/eslint": 8.4.1
|
||||
@@ -3459,17 +3448,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/ui@9.5.0-pre, @grafana/ui@workspace:*, @grafana/ui@workspace:packages/grafana-ui":
|
||||
"@grafana/ui@9.5.0, @grafana/ui@workspace:*, @grafana/ui@workspace:packages/grafana-ui":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/ui@workspace:packages/grafana-ui"
|
||||
dependencies:
|
||||
"@babel/core": 7.20.5
|
||||
"@emotion/css": 11.10.6
|
||||
"@emotion/react": 11.10.6
|
||||
"@grafana/data": 9.5.0-pre
|
||||
"@grafana/e2e-selectors": 9.5.0-pre
|
||||
"@grafana/data": 9.5.0
|
||||
"@grafana/e2e-selectors": 9.5.0
|
||||
"@grafana/faro-web-sdk": 1.0.2
|
||||
"@grafana/schema": 9.5.0-pre
|
||||
"@grafana/schema": 9.5.0
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@leeoniya/ufuzzy": 1.0.6
|
||||
"@mdx-js/react": 1.6.22
|
||||
@@ -35667,13 +35656,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:2.4.1":
|
||||
version: 2.4.1
|
||||
resolution: "tslib@npm:2.4.1"
|
||||
checksum: 19480d6e0313292bd6505d4efe096a6b31c70e21cf08b5febf4da62e95c265c8f571f7b36fcc3d1a17e068032f59c269fab3459d6cd3ed6949eafecf64315fca
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:2.5.0, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.4.1":
|
||||
version: 2.5.0
|
||||
resolution: "tslib@npm:2.5.0"
|
||||
|
||||
Reference in New Issue
Block a user