Merge remote-tracking branch 'origin/main' into resource-store-bridge

This commit is contained in:
Ryan McKinley 2024-07-03 13:55:27 -07:00
commit 22d3f163df
28 changed files with 737 additions and 1329 deletions

View File

@ -1835,10 +1835,7 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "13"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "14"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "15"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "16"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "17"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "18"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "19"]
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "16"]
],
"public/app/features/alerting/unified/components/notification-policies/PromDurationDocs.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],

View File

@ -4,11 +4,6 @@ on:
workflow_dispatch:
pull_request:
branches: [main]
paths:
- '**/go.work'
- '**/go.work.sum'
- '**/go.mod'
- '**/go.sum'
jobs:
check:
@ -24,28 +19,15 @@ jobs:
with:
go-version-file: go.mod
- name: Workspace Sync
run: go work sync
- name: Update workspace
run: make update-workspace
- name: Check for go mod & workspace changes
run: |
if ! git diff --exit-code --quiet; then
echo "Changes detected:"
git diff
echo "Please run 'go work sync' and commit the changes."
echo "Please run 'make update-workspace' and commit the changes."
echo "If there is a change in enterprise dependencies, please update pkg/extensions/main.go."
exit 1
fi
- name: Build
run: make build-go
- name: Check for go workspace changes
run: |
if ! git diff --exit-code --quiet; then
echo "Changes detected:"
git diff
echo "Please run 'make build-go' and commit the changes."
echo "If there is a change in enterprise dependencies, please update pkg/extensions/main.go."
exit 1
fi
fi

View File

@ -169,8 +169,13 @@ fix-cue: $(CUE)
gen-jsonnet:
go generate ./devenv/jsonnet
.PHONY: update-workspace
update-workspace:
@echo "updating workspace"
$(GO) mod download
.PHONY: build-go
build-go: gen-go ## Build all Go binaries.
build-go: update-workspace gen-go ## Build all Go binaries.
@echo "build go files"
$(GO) run build.go $(GO_BUILD_FLAGS) build

View File

@ -209,6 +209,10 @@ If you have a field value that is an image URL or a base64 encoded image you can
{{< figure src="/static/img/docs/v73/table_hover.gif" max-width="900px" caption="Table hover" >}}
Use the **Alt text** option to set the alternative text of an image. The text will be available for screen readers and in cases when images can't be loaded.
Use the **Title text** option to set the text that's displayed when the image is hovered over with a cursor.
#### Sparkline
Shows values rendered as a sparkline. You can show sparklines using the [Time series to table transformation](ref:time-series-to-table-transformation) on data with multiple time series to process it into a format the table can show.

File diff suppressed because it is too large Load Diff

View File

@ -261,7 +261,7 @@
"@grafana/prometheus": "workspace:*",
"@grafana/runtime": "workspace:*",
"@grafana/saga-icons": "workspace:*",
"@grafana/scenes": "^5.3.4",
"@grafana/scenes": "5.3.5",
"@grafana/schema": "workspace:*",
"@grafana/sql": "workspace:*",
"@grafana/ui": "workspace:*",

View File

@ -771,6 +771,8 @@ export interface TableJsonViewCellOptions {
* Json view cell options
*/
export interface TableImageCellOptions {
alt?: string;
title?: string;
type: TableCellDisplayMode.Image;
}

View File

@ -48,6 +48,8 @@ TableJsonViewCellOptions: {
// Json view cell options
TableImageCellOptions: {
type: TableCellDisplayMode & "image"
alt?: string
title?: string
} @cuetsy(kind="interface")
// Show data links in the cell

View File

@ -3,41 +3,41 @@ import * as React from 'react';
import { getCellLinks } from '../../utils';
import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu';
import { TableCellProps } from './types';
import { TableCellDisplayMode, TableCellProps } from './types';
import { getCellOptions } from './utils';
const DATALINKS_HEIGHT_OFFSET = 10;
export const ImageCell = (props: TableCellProps) => {
const { field, cell, tableStyles, row, cellProps } = props;
const cellOptions = getCellOptions(field);
const { title, alt } =
cellOptions.type === TableCellDisplayMode.Image ? cellOptions : { title: undefined, alt: undefined };
const displayValue = field.display!(cell.value);
const hasLinks = Boolean(getCellLinks(field, row)?.length);
// The image element
const img = (
<img
style={{ height: tableStyles.cellHeight - DATALINKS_HEIGHT_OFFSET, width: 'auto' }}
src={displayValue.text}
className={tableStyles.imageCell}
alt={alt}
title={title}
/>
);
return (
<div {...cellProps} className={tableStyles.cellContainer}>
{!hasLinks && (
<img
style={{ height: tableStyles.cellHeight - DATALINKS_HEIGHT_OFFSET, width: 'auto' }}
src={displayValue.text}
className={tableStyles.imageCell}
alt=""
/>
)}
{/* If there are no links we simply render the image */}
{!hasLinks && img}
{/* Otherwise render data links with image */}
{hasLinks && (
<DataLinksContextMenu
style={{ height: tableStyles.cellHeight - DATALINKS_HEIGHT_OFFSET, width: 'auto' }}
links={() => getCellLinks(field, row) || []}
>
{(api) => {
const img = (
<img
style={{ height: tableStyles.cellHeight - DATALINKS_HEIGHT_OFFSET, width: 'auto' }}
src={displayValue.text}
className={tableStyles.imageCell}
alt=""
/>
);
if (api.openMenu) {
return (
<div

View File

@ -46,6 +46,9 @@ type FeatureSpec struct {
// Do not show the value in docs
HideFromDocs bool `json:"hideFromDocs,omitempty"`
// Expression to determine if the flag is enabled by default
Expression string `json:"expression,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@ -74,32 +74,40 @@ func RouteOperationName(req *http.Request) (string, bool) {
func RequestTracing(tracer tracing.Tracer) web.Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if strings.HasPrefix(req.URL.Path, "/public/") || req.URL.Path == "/robots.txt" || req.URL.Path == "/favicon.ico" {
// skip tracing for a few endpoints
if strings.HasPrefix(req.URL.Path, "/public/") ||
req.URL.Path == "/robots.txt" ||
req.URL.Path == "/favicon.ico" {
next.ServeHTTP(w, req)
return
}
rw := web.Rw(w, req)
// Extract the parent span context from the incoming request.
ctx := otel.GetTextMapPropagator().Extract(req.Context(), propagation.HeaderCarrier(req.Header))
wireContext := otel.GetTextMapPropagator().Extract(req.Context(), propagation.HeaderCarrier(req.Header))
ctx, span := tracer.Start(wireContext, fmt.Sprintf("HTTP %s %s", req.Method, req.URL.Path), trace.WithLinks(trace.LinkFromContext(wireContext)))
req = req.WithContext(ctx)
next.ServeHTTP(w, req)
// Only call span.Finish when a route operation name have been set,
// meaning that not set the span would not be reported.
// generic span name for requests where there's no route operation name
spanName := fmt.Sprintf("HTTP %s <unknown>", req.Method)
// TODO: do not depend on web.Context from the future
if routeOperation, exists := RouteOperationName(web.FromContext(req.Context()).Req); exists {
defer span.End()
span.SetName(fmt.Sprintf("HTTP %s %s", req.Method, routeOperation))
spanName = fmt.Sprintf("HTTP %s %s", req.Method, routeOperation)
}
ctx, span := tracer.Start(ctx, spanName, trace.WithAttributes(
semconv.HTTPURLKey.String(req.RequestURI),
semconv.HTTPMethodKey.String(req.Method),
), trace.WithSpanKind(trace.SpanKindServer))
defer span.End()
req = req.WithContext(ctx)
// Ensure the response writer's status can be captured.
rw := web.Rw(w, req)
next.ServeHTTP(rw, req)
status := rw.Status()
span.SetAttributes(semconv.HTTPStatusCode(status))
span.SetAttributes(semconv.HTTPURL(req.RequestURI))
span.SetAttributes(semconv.HTTPMethod(req.Method))
if status >= 400 {
span.SetStatus(codes.Error, fmt.Sprintf("error with HTTP status code %s", strconv.Itoa(status)))
}

View File

@ -145,15 +145,19 @@
{
"metadata": {
"name": "alertingInsights",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-09-14T12:58:04Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-09-14T12:58:04Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Show the new alerting insights landing page",
"stage": "GA",
"codeowner": "@grafana/alerting-squad",
"frontend": true,
"hideFromAdminPage": true
"hideFromAdminPage": true,
"expression": "true"
}
},
{
@ -172,14 +176,18 @@
{
"metadata": {
"name": "alertingNoDataErrorExecution",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-08-15T14:27:15Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-08-15T14:27:15Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Changes how Alerting state manager handles execution of NoData/Error",
"stage": "GA",
"codeowner": "@grafana/alerting-squad",
"requiresRestart": true
"requiresRestart": true,
"expression": "true"
}
},
{
@ -198,13 +206,17 @@
{
"metadata": {
"name": "alertingQueryOptimization",
"resourceVersion": "1718727528075",
"creationTimestamp": "2024-01-10T20:52:58Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2024-01-10T20:52:58Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Optimizes eligible queries in order to reduce load on datasources",
"stage": "GA",
"codeowner": "@grafana/alerting-squad"
"codeowner": "@grafana/alerting-squad",
"expression": "false"
}
},
{
@ -222,13 +234,17 @@
{
"metadata": {
"name": "alertingSimplifiedRouting",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-11-10T13:14:39Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-11-10T13:14:39Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables users to easily configure alert notifications by specifying a contact point directly when editing or creating an alert rule",
"stage": "GA",
"codeowner": "@grafana/alerting-squad"
"codeowner": "@grafana/alerting-squad",
"expression": "true"
}
},
{
@ -270,26 +286,34 @@
{
"metadata": {
"name": "angularDeprecationUI",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-08-29T14:05:47Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-08-29T14:05:47Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Display Angular warnings in dashboards and panels",
"stage": "GA",
"codeowner": "@grafana/plugins-platform-backend",
"frontend": true
"frontend": true,
"expression": "true"
}
},
{
"metadata": {
"name": "annotationPermissionUpdate",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-10-31T13:30:13Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-10-31T13:30:13Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Change the way annotation permissions work by scoping them to folders and dashboards.",
"stage": "GA",
"codeowner": "@grafana/identity-access-team"
"codeowner": "@grafana/identity-access-team",
"expression": "true"
}
},
{
@ -427,26 +451,34 @@
{
"metadata": {
"name": "awsAsyncQueryCaching",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-07-21T15:34:07Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-07-21T15:34:07Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enable caching for async queries for Redshift and Athena. Requires that the datasource has caching and async query support enabled",
"stage": "GA",
"codeowner": "@grafana/aws-datasources"
"codeowner": "@grafana/aws-datasources",
"expression": "true"
}
},
{
"metadata": {
"name": "awsDatasourcesNewFormStyling",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-10-12T08:59:10Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-10-12T08:59:10Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Applies new form styling for configuration and query editors in AWS plugins",
"stage": "GA",
"codeowner": "@grafana/aws-datasources",
"frontend": true
"frontend": true,
"expression": "true"
}
},
{
@ -476,8 +508,11 @@
{
"metadata": {
"name": "bodyScrolling",
"resourceVersion": "1719825052257",
"creationTimestamp": "2024-07-01T09:10:52Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2024-07-01T09:10:52Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Adjusts Page to make body the scrollable element",
@ -485,7 +520,8 @@
"codeowner": "@grafana/grafana-frontend-platform",
"frontend": true,
"hideFromAdminPage": true,
"hideFromDocs": true
"hideFromDocs": true,
"expression": "false"
}
},
{
@ -556,38 +592,50 @@
{
"metadata": {
"name": "cloudWatchCrossAccountQuerying",
"resourceVersion": "1718727528075",
"creationTimestamp": "2022-11-28T11:39:12Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2022-11-28T11:39:12Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables cross-account querying in CloudWatch datasources",
"stage": "GA",
"codeowner": "@grafana/aws-datasources",
"allowSelfServe": true
"allowSelfServe": true,
"expression": "true"
}
},
{
"metadata": {
"name": "cloudWatchNewLabelParsing",
"resourceVersion": "1718727528075",
"creationTimestamp": "2024-04-05T15:57:56Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2024-04-05T15:57:56Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Updates CloudWatch label parsing to be more accurate",
"stage": "GA",
"codeowner": "@grafana/aws-datasources"
"codeowner": "@grafana/aws-datasources",
"expression": "true"
}
},
{
"metadata": {
"name": "cloudWatchRoundUpEndTime",
"resourceVersion": "1719324143210",
"creationTimestamp": "2024-06-25T14:02:23Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2024-06-25T14:02:23Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Round up end time for metric queries to the next minute to avoid missing data",
"stage": "GA",
"codeowner": "@grafana/aws-datasources"
"codeowner": "@grafana/aws-datasources",
"expression": "true"
}
},
{
@ -620,14 +668,18 @@
{
"metadata": {
"name": "correlations",
"resourceVersion": "1718727528075",
"creationTimestamp": "2022-09-16T13:14:27Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2022-09-16T13:14:27Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Correlations page",
"stage": "GA",
"codeowner": "@grafana/explore-squad",
"allowSelfServe": true
"allowSelfServe": true,
"expression": "true"
}
},
{
@ -649,13 +701,17 @@
{
"metadata": {
"name": "dashboardRestoreUI",
"resourceVersion": "1719321338253",
"creationTimestamp": "2024-06-25T13:15:38Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2024-06-25T13:15:38Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables the frontend to be able to restore a recently deleted dashboard",
"stage": "experimental",
"codeowner": "@grafana/grafana-frontend-platform"
"codeowner": "@grafana/grafana-frontend-platform",
"expression": "false"
}
},
{
@ -700,53 +756,69 @@
{
"metadata": {
"name": "dashgpt",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-08-30T20:22:05Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-08-30T20:22:05Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enable AI powered features in dashboards",
"stage": "GA",
"codeowner": "@grafana/dashboards-squad",
"frontend": true
"frontend": true,
"expression": "true"
}
},
{
"metadata": {
"name": "databaseReadReplica",
"resourceVersion": "1718727528075",
"creationTimestamp": "2024-06-18T16:18:48Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2024-06-18T16:18:48Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Use a read replica for some database queries.",
"stage": "experimental",
"codeowner": "@grafana/grafana-backend-services-squad"
"codeowner": "@grafana/grafana-backend-services-squad",
"expression": "false"
}
},
{
"metadata": {
"name": "dataplaneFrontendFallback",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-04-07T21:13:19Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-04-07T21:13:19Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Support dataplane contract field name change for transformations and field name matchers where the name is different",
"stage": "GA",
"codeowner": "@grafana/observability-metrics",
"frontend": true,
"allowSelfServe": true
"allowSelfServe": true,
"expression": "true"
}
},
{
"metadata": {
"name": "datasourceProxyDisableRBAC",
"resourceVersion": "1718727528075",
"creationTimestamp": "2024-05-21T13:05:16Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2024-05-21T13:05:16Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Disables applying a plugin route's ReqAction field to authorization",
"stage": "GA",
"codeowner": "@grafana/identity-access-team",
"hideFromDocs": true
"hideFromDocs": true,
"expression": "false"
}
},
{
@ -805,14 +877,18 @@
{
"metadata": {
"name": "disableEnvelopeEncryption",
"resourceVersion": "1718727528075",
"creationTimestamp": "2022-05-24T08:34:47Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2022-05-24T08:34:47Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Disable envelope encryption (emergency only)",
"stage": "GA",
"codeowner": "@grafana/grafana-as-code",
"hideFromAdminPage": true
"hideFromAdminPage": true,
"expression": "false"
}
},
{
@ -911,14 +987,18 @@
{
"metadata": {
"name": "exploreMetrics",
"resourceVersion": "1718727528075",
"creationTimestamp": "2024-04-09T18:15:18Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2024-04-09T18:15:18Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables the new Explore Metrics core app",
"stage": "GA",
"codeowner": "@grafana/dashboards-squad",
"frontend": true
"frontend": true,
"expression": "true"
}
},
{
@ -1013,14 +1093,18 @@
{
"metadata": {
"name": "featureHighlights",
"resourceVersion": "1718727528075",
"creationTimestamp": "2022-02-03T11:53:23Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2022-02-03T11:53:23Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Highlight Grafana Enterprise features",
"stage": "GA",
"codeowner": "@grafana/grafana-as-code",
"allowSelfServe": true
"allowSelfServe": true,
"expression": "false"
}
},
{
@ -1185,15 +1269,19 @@
{
"metadata": {
"name": "influxdbBackendMigration",
"resourceVersion": "1718727528075",
"resourceVersion": "1720021873452",
"creationTimestamp": "2022-02-09T18:26:16Z",
"deletionTimestamp": "2023-01-17T14:11:26Z"
"deletionTimestamp": "2023-01-17T14:11:26Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Query InfluxDB InfluxQL without the proxy",
"stage": "GA",
"codeowner": "@grafana/observability-metrics",
"frontend": true
"frontend": true,
"expression": "true"
}
},
{
@ -1277,14 +1365,18 @@
{
"metadata": {
"name": "kubernetesPlaylists",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-10-05T19:00:36Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-10-05T19:00:36Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s",
"stage": "GA",
"codeowner": "@grafana/grafana-app-platform-squad",
"requiresRestart": true
"requiresRestart": true,
"expression": "true"
}
},
{
@ -1341,28 +1433,36 @@
{
"metadata": {
"name": "logRowsPopoverMenu",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-11-16T09:48:10Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-11-16T09:48:10Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enable filtering menu displayed when text of a log line is selected",
"stage": "GA",
"codeowner": "@grafana/observability-logs",
"frontend": true
"frontend": true,
"expression": "true"
}
},
{
"metadata": {
"name": "logsContextDatasourceUi",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-01-27T14:12:01Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-01-27T14:12:01Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Allow datasource to provide custom UI for context view",
"stage": "GA",
"codeowner": "@grafana/observability-logs",
"frontend": true,
"allowSelfServe": true
"allowSelfServe": true,
"expression": "true"
}
},
{
@ -1381,27 +1481,35 @@
{
"metadata": {
"name": "logsExploreTableVisualisation",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-07-12T13:52:42Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-07-12T13:52:42Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "A table visualisation for logs in Explore",
"stage": "GA",
"codeowner": "@grafana/observability-logs",
"frontend": true
"frontend": true,
"expression": "true"
}
},
{
"metadata": {
"name": "logsInfiniteScrolling",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-11-09T10:54:03Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-11-09T10:54:03Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables infinite scrolling for the Logs panel in Explore and Dashboards",
"stage": "GA",
"codeowner": "@grafana/observability-logs",
"frontend": true
"frontend": true,
"expression": "true"
}
},
{
@ -1431,14 +1539,18 @@
{
"metadata": {
"name": "lokiMetricDataplane",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-04-13T13:07:08Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-04-13T13:07:08Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Changes metric responses from Loki to be compliant with the dataplane specification.",
"stage": "GA",
"codeowner": "@grafana/observability-logs",
"allowSelfServe": true
"allowSelfServe": true,
"expression": "true"
}
},
{
@ -1457,28 +1569,36 @@
{
"metadata": {
"name": "lokiQueryHints",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-12-18T20:43:16Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-12-18T20:43:16Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables query hints for Loki",
"stage": "GA",
"codeowner": "@grafana/observability-logs",
"frontend": true
"frontend": true,
"expression": "true"
}
},
{
"metadata": {
"name": "lokiQuerySplitting",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-02-09T17:27:02Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-02-09T17:27:02Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Split large interval queries into subqueries with smaller time intervals",
"stage": "GA",
"codeowner": "@grafana/observability-logs",
"frontend": true,
"allowSelfServe": true
"allowSelfServe": true,
"expression": "true"
}
},
{
@ -1509,25 +1629,33 @@
{
"metadata": {
"name": "lokiStructuredMetadata",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-11-16T16:06:14Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-11-16T16:06:14Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables the loki data source to request structured metadata from the Loki server",
"stage": "GA",
"codeowner": "@grafana/observability-logs"
"codeowner": "@grafana/observability-logs",
"expression": "true"
}
},
{
"metadata": {
"name": "managedPluginsInstall",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-10-18T13:17:03Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-10-18T13:17:03Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Install managed plugins directly from plugins catalog",
"stage": "GA",
"codeowner": "@grafana/plugins-platform-backend"
"codeowner": "@grafana/plugins-platform-backend",
"expression": "true"
}
},
{
@ -1570,13 +1698,17 @@
{
"metadata": {
"name": "nestedFolders",
"resourceVersion": "1718727528075",
"creationTimestamp": "2022-10-26T14:15:14Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2022-10-26T14:15:14Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enable folder nesting",
"stage": "GA",
"codeowner": "@grafana/search-and-storage"
"codeowner": "@grafana/search-and-storage",
"expression": "true"
}
},
{
@ -1711,14 +1843,18 @@
{
"metadata": {
"name": "panelMonitoring",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-10-09T05:19:08Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-10-09T05:19:08Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables panel monitoring through logs and measurements",
"stage": "GA",
"codeowner": "@grafana/dataviz-squad",
"frontend": true
"frontend": true,
"expression": "true"
}
},
{
@ -1800,13 +1936,17 @@
{
"metadata": {
"name": "pluginProxyPreserveTrailingSlash",
"resourceVersion": "1718727528075",
"creationTimestamp": "2024-06-05T11:36:14Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2024-06-05T11:36:14Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Preserve plugin proxy trailing slash.",
"stage": "GA",
"codeowner": "@grafana/plugins-platform-backend"
"codeowner": "@grafana/plugins-platform-backend",
"expression": "false"
}
},
{
@ -1889,26 +2029,34 @@
{
"metadata": {
"name": "prometheusConfigOverhaulAuth",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-07-26T16:09:53Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-07-26T16:09:53Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Update the Prometheus configuration page with the new auth component",
"stage": "GA",
"codeowner": "@grafana/observability-metrics"
"codeowner": "@grafana/observability-metrics",
"expression": "true"
}
},
{
"metadata": {
"name": "prometheusDataplane",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-03-29T15:26:32Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-03-29T15:26:32Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Changes responses to from Prometheus to be compliant with the dataplane specification. In particular, when this feature toggle is active, the numeric `Field.Name` is set from 'Value' to the value of the `__name__` label.",
"stage": "GA",
"codeowner": "@grafana/observability-metrics",
"allowSelfServe": true
"allowSelfServe": true,
"expression": "true"
}
},
{
@ -1928,15 +2076,19 @@
{
"metadata": {
"name": "prometheusMetricEncyclopedia",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-03-07T18:41:05Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-03-07T18:41:05Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Adds the metrics explorer component to the Prometheus query builder as an option in metric select",
"stage": "GA",
"codeowner": "@grafana/observability-metrics",
"frontend": true,
"allowSelfServe": true
"allowSelfServe": true,
"expression": "true"
}
},
{
@ -1955,14 +2107,18 @@
{
"metadata": {
"name": "publicDashboards",
"resourceVersion": "1718727528075",
"creationTimestamp": "2022-04-07T18:30:19Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2022-04-07T18:30:19Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "[Deprecated] Public dashboards are now enabled by default; to disable them, use the configuration setting. This feature toggle will be removed in the next major version.",
"stage": "GA",
"codeowner": "@grafana/sharing-squad",
"allowSelfServe": true
"allowSelfServe": true,
"expression": "true"
}
},
{
@ -2060,26 +2216,34 @@
{
"metadata": {
"name": "recordedQueriesMulti",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-06-14T12:34:22Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-06-14T12:34:22Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables writing multiple items from a single query within Recorded Queries",
"stage": "GA",
"codeowner": "@grafana/observability-metrics"
"codeowner": "@grafana/observability-metrics",
"expression": "true"
}
},
{
"metadata": {
"name": "recoveryThreshold",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-10-10T14:51:50Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-10-10T14:51:50Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables feature recovery threshold (aka hysteresis) for threshold server-side expression",
"stage": "GA",
"codeowner": "@grafana/alerting-squad",
"requiresRestart": true
"requiresRestart": true,
"expression": "true"
}
},
{
@ -2215,14 +2379,18 @@
{
"metadata": {
"name": "ssoSettingsApi",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-11-08T09:50:01Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-11-08T09:50:01Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables the SSO settings API and the OAuth configuration UIs in Grafana",
"stage": "GA",
"codeowner": "@grafana/identity-access-team",
"allowSelfServe": true
"allowSelfServe": true,
"expression": "true"
}
},
{
@ -2305,53 +2473,69 @@
{
"metadata": {
"name": "topnav",
"resourceVersion": "1718727528075",
"creationTimestamp": "2022-06-20T14:25:43Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2022-06-20T14:25:43Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables topnav support in external plugins. The new Grafana navigation cannot be disabled.",
"stage": "deprecated",
"codeowner": "@grafana/grafana-frontend-platform"
"codeowner": "@grafana/grafana-frontend-platform",
"expression": "true"
}
},
{
"metadata": {
"name": "traceQLStreaming",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-07-26T13:33:16Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-07-26T13:33:16Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables response streaming of TraceQL queries of the Tempo data source",
"stage": "GA",
"codeowner": "@grafana/observability-traces-and-profiling",
"frontend": true
"frontend": true,
"expression": "false"
}
},
{
"metadata": {
"name": "transformationsRedesign",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-07-12T16:35:49Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-07-12T16:35:49Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Enables the transformations redesign",
"stage": "GA",
"codeowner": "@grafana/observability-metrics",
"frontend": true,
"allowSelfServe": true
"allowSelfServe": true,
"expression": "true"
}
},
{
"metadata": {
"name": "transformationsVariableSupport",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-10-04T14:28:46Z"
"resourceVersion": "1720021873452",
"creationTimestamp": "2023-10-04T14:28:46Z",
"annotations": {
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
}
},
"spec": {
"description": "Allows using variables in transformations",
"stage": "GA",
"codeowner": "@grafana/dataviz-squad",
"frontend": true
"frontend": true,
"expression": "true"
}
},
{

View File

@ -59,6 +59,7 @@ func TestFeatureToggleFiles(t *testing.T) {
AllowSelfServe: flag.AllowSelfServe,
HideFromAdminPage: flag.HideFromAdminPage,
HideFromDocs: flag.HideFromDocs,
Expression: flag.Expression,
// EnabledVersion: ???,
}

View File

@ -140,6 +140,7 @@ func (root *NavTreeRoot) ApplyCostManagementIA() {
orgAdminNode := root.FindById(NavIDCfg)
var costManagementApp *NavLink
var adaptiveMetricsApp *NavLink
var adaptiveLogsApp *NavLink
var attributionsApp *NavLink
var logVolumeExplorerApp *NavLink
@ -151,6 +152,8 @@ func (root *NavTreeRoot) ApplyCostManagementIA() {
costManagementApp = element
case "plugin-page-grafana-adaptive-metrics-app":
adaptiveMetricsApp = element
case "plugin-page-grafana-adaptivelogs-app":
adaptiveLogsApp = element
case "plugin-page-grafana-attributions-app":
attributionsApp = element
case "plugin-page-grafana-logvolumeexplorer-app":
@ -173,6 +176,9 @@ func (root *NavTreeRoot) ApplyCostManagementIA() {
costManagementLogsNode := FindByURL(costManagementApp.Children, "/a/grafana-costmanagementui-app/logs")
if costManagementLogsNode != nil {
if adaptiveLogsApp != nil {
costManagementLogsNode.Children = append(costManagementLogsNode.Children, adaptiveLogsApp)
}
if logVolumeExplorerApp != nil {
costManagementLogsNode.Children = append(costManagementLogsNode.Children, logVolumeExplorerApp)
}

View File

@ -298,6 +298,7 @@ func (s *ServiceImpl) readNavigationSettings() {
"grafana-cloud-link-app": {SectionID: navtree.NavIDCfgPlugins, SortWeight: 3},
"grafana-costmanagementui-app": {SectionID: navtree.NavIDCfg, Text: "Cost management"},
"grafana-adaptive-metrics-app": {SectionID: navtree.NavIDCfg, Text: "Adaptive Metrics"},
"grafana-adaptivelogs-app": {SectionID: navtree.NavIDCfg, Text: "Adaptive Logs"},
"grafana-attributions-app": {SectionID: navtree.NavIDCfg, Text: "Attributions"},
"grafana-logvolumeexplorer-app": {SectionID: navtree.NavIDCfg, Text: "Log Volume Explorer"},
"grafana-easystart-app": {SectionID: navtree.NavIDRoot, SortWeight: navtree.WeightApps + 1, Text: "Connections", Icon: "adjust-circle"},

View File

@ -183,7 +183,7 @@ func calculateState(ctx context.Context, log log.Logger, alertRule *ngModels.Ale
}
}
if len(dupes) > 0 {
log.Warn("Rule declares one or many reserved labels. Those rules labels will be ignored", "labels", dupes)
log.Debug("Rule declares one or many reserved labels. Those rules labels will be ignored", "labels", dupes)
}
dupes = make(data.Labels)
for key, val := range resultLabels {
@ -196,7 +196,7 @@ func calculateState(ctx context.Context, log log.Logger, alertRule *ngModels.Ale
}
}
if len(dupes) > 0 {
log.Warn("Evaluation result contains either reserved labels or labels declared in the rules. Those labels from the result will be ignored", "labels", dupes)
log.Debug("Evaluation result contains either reserved labels or labels declared in the rules. Those labels from the result will be ignored", "labels", dupes)
}
cacheID := lbs.Fingerprint()

View File

@ -1,6 +1,6 @@
import { render, renderHook, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { noop } from 'lodash';
import { first, noop } from 'lodash';
import { Router } from 'react-router-dom';
import { config, locationService } from '@grafana/runtime';
@ -21,6 +21,7 @@ import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
import {
AUTOGENERATED_ROOT_LABEL_NAME,
Policy,
TimingOptionsMeta,
isAutoGeneratedRootAndSimplifiedEnabled,
useCreateDropdownMenuActions,
} from './Policy';
@ -104,7 +105,7 @@ describe('Policy', () => {
// for timing options
expect(within(defaultPolicy).getByTestId('timing-options')).toHaveTextContent(
'Wait30s to group instances,5m before sending updates'
'Wait 30s to group instances · Wait 5m before sending updates · Repeated every 4h'
);
// should have custom policies
@ -485,3 +486,29 @@ describe('useCreateDropdownMenuActions', () => {
});
});
});
describe('TimingOptionsMeta', () => {
it('should render nothing without options', () => {
render(<TimingOptionsMeta timingOptions={{}} />);
expect(screen.queryByText(/wait/i)).not.toBeInTheDocument();
});
it('should render only repeat interval', () => {
render(<TimingOptionsMeta timingOptions={{ repeat_interval: '5h' }} />);
expect(screen.getByText(/repeated every/i)).toBeInTheDocument();
expect(screen.getByText('5h')).toBeInTheDocument();
});
it('should render all options', () => {
render(<TimingOptionsMeta timingOptions={{ group_wait: '30s', group_interval: '5m', repeat_interval: '4h' }} />);
expect(
first(
screen.getAllByText(
(_, element) =>
element?.textContent === 'Wait 30s to group instances · Wait 5m before sending updates · Repeated every 4h',
{ collapseWhitespace: false, trim: false, exact: true }
)
)
).toBeInTheDocument();
});
});

View File

@ -21,6 +21,7 @@ import {
getTagColorsFromName,
useStyles2,
} from '@grafana/ui';
import { t, Trans } from 'app/core/internationalization';
import ConditionalWrap from 'app/features/alerting/unified/components/ConditionalWrap';
import {
AlertmanagerGroup,
@ -720,40 +721,84 @@ const MuteTimings: FC<{ timings: string[]; alertManagerSourceName: string }> = (
);
};
const TimingOptionsMeta: FC<{ timingOptions: TimingOptions }> = ({ timingOptions }) => {
interface TimingOptionsMetaProps {
timingOptions: TimingOptions;
}
export const TimingOptionsMeta = ({ timingOptions }: TimingOptionsMetaProps) => {
const groupWait = timingOptions.group_wait;
const groupInterval = timingOptions.group_interval;
const repeatInterval = timingOptions.repeat_interval;
// we don't have any timing options to show we're inheriting everything from the parent
// and those show up in a separate "inherited properties" component
if (!groupWait && !groupInterval) {
if (!groupWait && !groupInterval && !repeatInterval) {
return null;
}
const metaOptions: ReactNode[] = [];
if (groupWait) {
metaOptions.push(
<Tooltip
placement="top"
content={t(
'alerting.policies.metadata.timingOptions.groupWait.description',
'How long to initially wait to send a notification for a group of alert instances.'
)}
>
<span>
<Trans i18nKey="alerting.policies.metadata.timingOptions.groupWait.label">
Wait <PrimaryText content={groupWait} /> to group instances
</Trans>
</span>
</Tooltip>
);
}
if (groupInterval) {
metaOptions.push(
<Tooltip
placement="top"
content={t(
'alerting.policies.metadata.timingOptions.groupInterval.description',
'How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent.'
)}
>
<span>
<Trans i18nKey="alerting.policies.metadata.timingOptions.groupInterval.label">
Wait <PrimaryText content={groupInterval} /> before sending updates
</Trans>
</span>
</Tooltip>
);
}
if (repeatInterval) {
metaOptions.push(
<Tooltip
placement="top"
content={t(
'alerting.policies.metadata.timingOptions.repeatInterval.description',
'How often notifications are sent if the group of alerts has not changed since the last notification.'
)}
>
<span>
<Trans i18nKey="alerting.policies.metadata.timingOptions.repeatInterval.label">
Repeated every <PrimaryText content={repeatInterval} />
</Trans>
</span>
</Tooltip>
);
}
return (
<MetaText icon="hourglass" data-testid="timing-options">
<span>Wait</span>
{groupWait && (
<Tooltip
placement="top"
content="How long to initially wait to send a notification for a group of alert instances."
>
<span>
<Text color="primary">{groupWait}</Text> to group instances
{groupWait && groupInterval && ','}
</span>
</Tooltip>
)}
{groupInterval && (
<Tooltip
placement="top"
content="How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent."
>
<span>
<Text color="primary">{groupInterval}</Text> before sending updates
</span>
</Tooltip>
)}
{metaOptions.map((meta, index) => (
<span key={uniqueId()}>
{meta}
{index < metaOptions.length - 1 && ' · '}
</span>
))}
</MetaText>
);
};
@ -976,4 +1021,8 @@ const getStyles = (theme: GrafanaTheme2) => ({
}),
});
// This is a convencience component to deal with I18n shenanigans
// see https://github.com/grafana/grafana/blob/main/contribute/internationalization.md#jsx
const PrimaryText = ({ content }: { content: string }) => <Text color="primary">{content}</Text>;
export { Policy };

View File

@ -14,6 +14,7 @@ import {
sceneGraph,
SceneObjectUrlSyncConfig,
SceneObjectUrlValues,
CancelActivationHandler,
} from '@grafana/scenes';
import { Box, Stack, useStyles2 } from '@grafana/ui';
@ -74,6 +75,20 @@ export class DashboardControls extends SceneObjectBase<DashboardControlsState> {
refreshPicker: state.refreshPicker ?? new SceneRefreshPicker({}),
...state,
});
this.addActivationHandler(() => {
let refreshPickerDeactivation: CancelActivationHandler | undefined;
if (this.state.hideTimeControls) {
refreshPickerDeactivation = this.state.refreshPicker.activate();
}
return () => {
if (refreshPickerDeactivation) {
refreshPickerDeactivation();
}
};
});
}
/**

View File

@ -68,7 +68,7 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS
>
{scopes && !meta.dashboardNotFound && <scopes.Component model={scopes} />}
<NavToolbarActions dashboard={model} />
{controls && hasControls && (
{controls && (
<div
className={cx(styles.controlsWrapper, scopes && !isScopesExpanded && styles.controlsWrapperWithScopes)}
>

View File

@ -2,13 +2,15 @@ import { useEffect, useMemo } from 'react';
import { SelectableValue, toOption } from '@grafana/data';
import { EditorField, EditorFieldGroup, EditorSwitch } from '@grafana/experimental';
import { config } from '@grafana/runtime';
import { Select } from '@grafana/ui';
import { CloudWatchDatasource } from '../../../../datasource';
import { useDimensionKeys, useMetrics, useNamespaces } from '../../../../hooks';
import { useAccountOptions, useDimensionKeys, useMetrics, useNamespaces } from '../../../../hooks';
import { STATISTICS } from '../../../../language/cloudwatch-sql/language';
import { CloudWatchMetricsQuery } from '../../../../types';
import { appendTemplateVariables } from '../../../../utils/utils';
import { Account } from '../../../shared/Account';
import {
getMetricNameFromExpression,
@ -48,13 +50,24 @@ const SQLBuilderSelectRow = ({ datasource, query, onQueryChange }: SQLBuilderSel
const withSchemaEnabled = isUsingWithSchema(sql.from);
const namespaceOptions = useNamespaces(datasource);
const metricOptions = useMetrics(datasource, { region: query.region, namespace });
const metricOptions = useMetrics(datasource, {
region: query.region,
namespace,
...(config.featureToggles.cloudWatchCrossAccountQuerying &&
config.featureToggles.cloudwatchMetricInsightsCrossAccount
? { accountId: query.accountId }
: {}),
});
const existingFilters = useMemo(() => stringArrayToDimensions(schemaLabels ?? []), [schemaLabels]);
const unusedDimensionKeys = useDimensionKeys(datasource, {
region: query.region,
namespace,
metricName,
dimensionFilters: existingFilters,
...(config.featureToggles.cloudWatchCrossAccountQuerying &&
config.featureToggles.cloudwatchMetricInsightsCrossAccount
? { accountId: query.accountId }
: {}),
});
const dimensionKeys = useMemo(
() => (schemaLabels?.length ? [...unusedDimensionKeys, ...schemaLabels.map(toOption)] : unusedDimensionKeys),
@ -76,9 +89,23 @@ const SQLBuilderSelectRow = ({ datasource, query, onQueryChange }: SQLBuilderSel
return { ...query, sql };
};
const accountState = useAccountOptions(datasource.resources, query.region);
return (
<>
<EditorFieldGroup>
{config.featureToggles.cloudWatchCrossAccountQuerying &&
config.featureToggles.cloudwatchMetricInsightsCrossAccount && (
<Account
accountId={query.accountId}
accountOptions={accountState.value || []}
onChange={(accountId) => {
onQueryChange({
...query,
accountId,
});
}}
/>
)}
<EditorField label="Namespace" width={16}>
<Select
aria-label="Namespace"

View File

@ -4,6 +4,7 @@ import { useAsyncFn } from 'react-use';
import { SelectableValue, toOption } from '@grafana/data';
import { AccessoryButton, EditorList, InputGroup } from '@grafana/experimental';
import { config } from '@grafana/runtime';
import { Alert, Select, useStyles2 } from '@grafana/ui';
import { CloudWatchDatasource } from '../../../../datasource';
@ -108,7 +109,15 @@ const FilterItem = (props: FilterItemProps) => {
const namespace = getNamespaceFromExpression(sql.from);
const metricName = getMetricNameFromExpression(sql.select);
const dimensionKeys = useDimensionKeys(datasource, { region: query.region, namespace, metricName });
const dimensionKeys = useDimensionKeys(datasource, {
region: query.region,
namespace,
metricName,
...(config.featureToggles.cloudWatchCrossAccountQuerying &&
config.featureToggles.cloudwatchMetricInsightsCrossAccount
? { accountId: query.accountId }
: {}),
});
const loadDimensionValues = async () => {
if (!filter.property?.name || !namespace) {
@ -116,7 +125,16 @@ const FilterItem = (props: FilterItemProps) => {
}
return datasource.resources
.getDimensionValues({ region: query.region, namespace, metricName, dimensionKey: filter.property.name })
.getDimensionValues({
region: query.region,
namespace,
metricName,
dimensionKey: filter.property.name,
...(config.featureToggles.cloudWatchCrossAccountQuerying &&
config.featureToggles.cloudwatchMetricInsightsCrossAccount
? { accountId: query.accountId }
: {}),
})
.then((result: Array<SelectableValue<string>>) => {
return appendTemplateVariables(datasource, result);
});

View File

@ -330,4 +330,25 @@ describe('QueryEditor should render right editor', () => {
expect(screen.queryByText('Are you sure?')).toBeNull();
});
});
describe('metric insights in builder mode', () => {
let originalValueCloudWatchCrossAccountQuerying: boolean | undefined;
let originalValueCloudwatchMetricInsightsCrossAccount: boolean | undefined;
beforeEach(() => {
originalValueCloudWatchCrossAccountQuerying = config.featureToggles.cloudWatchCrossAccountQuerying;
originalValueCloudwatchMetricInsightsCrossAccount = config.featureToggles.cloudwatchMetricInsightsCrossAccount;
});
afterEach(() => {
config.featureToggles.cloudWatchCrossAccountQuerying = originalValueCloudWatchCrossAccountQuerying;
config.featureToggles.cloudwatchMetricInsightsCrossAccount = originalValueCloudwatchMetricInsightsCrossAccount;
});
it('should have an account selector when the feature is enabled', async () => {
config.featureToggles.cloudWatchCrossAccountQuerying = true;
config.featureToggles.cloudwatchMetricInsightsCrossAccount = true;
props.datasource.resources.getAccounts = jest.fn().mockResolvedValue(['account123']);
render(<QueryEditor {...props} query={validMetricQueryBuilderQuery} />);
await screen.findByText('Metric Query');
expect(await screen.findByText('Account')).toBeInTheDocument();
});
});
});

View File

@ -9,6 +9,7 @@ import { Field, Select, TableCellDisplayMode, useStyles2 } from '@grafana/ui';
import { AutoCellOptionsEditor } from './cells/AutoCellOptionsEditor';
import { BarGaugeCellOptionsEditor } from './cells/BarGaugeCellOptionsEditor';
import { ColorBackgroundCellOptionsEditor } from './cells/ColorBackgroundCellOptionsEditor';
import { ImageCellOptionsEditor } from './cells/ImageCellOptionsEditor';
import { SparklineCellOptionsEditor } from './cells/SparklineCellOptionsEditor';
// The props that any cell type editor are expected
@ -73,6 +74,9 @@ export const TableCellOptionEditor = ({ value, onChange }: Props) => {
{cellType === TableCellDisplayMode.Sparkline && (
<SparklineCellOptionsEditor cellOptions={value} onChange={onCellOptionsChange} />
)}
{cellType === TableCellDisplayMode.Image && (
<ImageCellOptionsEditor cellOptions={value} onChange={onCellOptionsChange} />
)}
</div>
);
};

View File

@ -0,0 +1,33 @@
import { FormEvent } from 'react';
import { TableImageCellOptions } from '@grafana/schema';
import { Field, Input } from '@grafana/ui';
import { TableCellEditorProps } from '../TableCellOptionEditor';
export const ImageCellOptionsEditor = ({ cellOptions, onChange }: TableCellEditorProps<TableImageCellOptions>) => {
const onAltChange = (e: FormEvent<HTMLInputElement>) => {
cellOptions.alt = e.currentTarget.value;
onChange(cellOptions);
};
const onTitleChange = (e: FormEvent<HTMLInputElement>) => {
cellOptions.title = e.currentTarget.value;
onChange(cellOptions);
};
return (
<>
<Field
label="Alt text"
description="Alternative text that will be displayed if an image can't be displayed or for users who use a screen reader"
>
<Input onChange={onAltChange} defaultValue={cellOptions.alt} />
</Field>
<Field label="Title text" description="Text that will be displayed when the image is hovered by a cursor">
<Input onChange={onTitleChange} defaultValue={cellOptions.title} />
</Field>
</>
);
};

View File

@ -61,6 +61,24 @@
"parse-mode-warning-body": "If you use a <1>parse_mode</1> option other than <3>None</3>, truncation may result in an invalid message, causing the notification to fail. For longer messages, we recommend using an alternative contact method.",
"parse-mode-warning-title": "Telegram messages are limited to 4096 UTF-8 characters."
}
},
"policies": {
"metadata": {
"timingOptions": {
"groupInterval": {
"description": "How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent.",
"label": "Wait <1></1> before sending updates"
},
"groupWait": {
"description": "How long to initially wait to send a notification for a group of alert instances.",
"label": "Wait <1></1> to group instances"
},
"repeatInterval": {
"description": "How often notifications are sent if the group of alerts has not changed since the last notification.",
"label": "Repeated every <1></1>"
}
}
}
}
},
"annotations": {
@ -1638,7 +1656,7 @@
"restore-modal": {
"restore-button": "Restore",
"restore-loading": "Restoring...",
"text_one": "This action will restore {{numberOfDashboards}} dashboard.",
"text_one": "This action will restore {{numberOfDashboards}} dashboards.",
"text_other": "This action will restore {{numberOfDashboards}} dashboards.",
"title": "Restore Dashboards"
}

View File

@ -61,6 +61,24 @@
"parse-mode-warning-body": "Ĩƒ yőū ūşę ä <1>päřşę_mőđę</1> őpŧįőʼn őŧĥęř ŧĥäʼn <3>Ńőʼnę</3>, ŧřūʼnčäŧįőʼn mäy řęşūľŧ įʼn äʼn įʼnväľįđ męşşäģę, čäūşįʼnģ ŧĥę ʼnőŧįƒįčäŧįőʼn ŧő ƒäįľ. Főř ľőʼnģęř męşşäģęş, ŵę řęčőmmęʼnđ ūşįʼnģ äʼn äľŧęřʼnäŧįvę čőʼnŧäčŧ męŧĥőđ.",
"parse-mode-warning-title": "Ŧęľęģřäm męşşäģęş äřę ľįmįŧęđ ŧő 4096 ŮŦF-8 čĥäřäčŧęřş."
}
},
"policies": {
"metadata": {
"timingOptions": {
"groupInterval": {
"description": "Ħőŵ ľőʼnģ ŧő ŵäįŧ þęƒőřę şęʼnđįʼnģ ä ʼnőŧįƒįčäŧįőʼn äþőūŧ ʼnęŵ äľęřŧş ŧĥäŧ äřę äđđęđ ŧő ä ģřőūp őƒ äľęřŧş ƒőř ŵĥįčĥ äʼn įʼnįŧįäľ ʼnőŧįƒįčäŧįőʼn ĥäş äľřęäđy þęęʼn şęʼnŧ.",
"label": "Ŵäįŧ <1></1> þęƒőřę şęʼnđįʼnģ ūpđäŧęş"
},
"groupWait": {
"description": "Ħőŵ ľőʼnģ ŧő įʼnįŧįäľľy ŵäįŧ ŧő şęʼnđ ä ʼnőŧįƒįčäŧįőʼn ƒőř ä ģřőūp őƒ äľęřŧ įʼnşŧäʼnčęş.",
"label": "Ŵäįŧ <1></1> ŧő ģřőūp įʼnşŧäʼnčęş"
},
"repeatInterval": {
"description": "Ħőŵ őƒŧęʼn ʼnőŧįƒįčäŧįőʼnş äřę şęʼnŧ įƒ ŧĥę ģřőūp őƒ äľęřŧş ĥäş ʼnőŧ čĥäʼnģęđ şįʼnčę ŧĥę ľäşŧ ʼnőŧįƒįčäŧįőʼn.",
"label": "Ŗępęäŧęđ ęvęřy <1></1>"
}
}
}
}
},
"annotations": {
@ -1638,7 +1656,7 @@
"restore-modal": {
"restore-button": "Ŗęşŧőřę",
"restore-loading": "Ŗęşŧőřįʼnģ...",
"text_one": "Ŧĥįş äčŧįőʼn ŵįľľ řęşŧőřę {{numberOfDashboards}} đäşĥþőäřđ.",
"text_one": "Ŧĥįş äčŧįőʼn ŵįľľ řęşŧőřę {{numberOfDashboards}} đäşĥþőäřđş.",
"text_other": "Ŧĥįş äčŧįőʼn ŵįľľ řęşŧőřę {{numberOfDashboards}} đäşĥþőäřđş.",
"title": "Ŗęşŧőřę Đäşĥþőäřđş"
}

121
yarn.lock
View File

@ -285,7 +285,14 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.0, @babel/helper-plugin-utils@npm:^7.24.5, @babel/helper-plugin-utils@npm:^7.24.7, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3":
"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.0, @babel/helper-plugin-utils@npm:^7.24.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3":
version: 7.24.5
resolution: "@babel/helper-plugin-utils@npm:7.24.5"
checksum: 10/6e11ca5da73e6bd366848236568c311ac10e433fc2034a6fe6243af28419b07c93b4386f87bbc940aa058b7c83f370ef58f3b0fd598106be040d21a3d1c14276
languageName: node
linkType: hard
"@babel/helper-plugin-utils@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/helper-plugin-utils@npm:7.24.7"
checksum: 10/dad51622f0123fdba4e2d40a81a6b7d6ef4b1491b2f92fd9749447a36bde809106cf117358705057a2adc8fd73d5dc090222e0561b1213dae8601c8367f5aac8
@ -2450,7 +2457,16 @@ __metadata:
languageName: node
linkType: hard
"@floating-ui/dom@npm:^1.0.0, @floating-ui/dom@npm:^1.0.1":
"@floating-ui/core@npm:^1.6.0":
version: 1.6.0
resolution: "@floating-ui/core@npm:1.6.0"
dependencies:
"@floating-ui/utils": "npm:^0.2.1"
checksum: 10/d6a47cacde193cd8ccb4c268b91ccc4ca254dffaec6242b07fd9bcde526044cc976d27933a7917f9a671de0a0e27f8d358f46400677dbd0c8199de293e9746e1
languageName: node
linkType: hard
"@floating-ui/dom@npm:^1.0.0":
version: 1.6.5
resolution: "@floating-ui/dom@npm:1.6.5"
dependencies:
@ -2460,6 +2476,16 @@ __metadata:
languageName: node
linkType: hard
"@floating-ui/dom@npm:^1.0.1":
version: 1.6.1
resolution: "@floating-ui/dom@npm:1.6.1"
dependencies:
"@floating-ui/core": "npm:^1.6.0"
"@floating-ui/utils": "npm:^0.2.1"
checksum: 10/c010feb55be37662eb4cc8d0a22e21359c25247bbdcd9557617fd305cf08c8f020435b17e4b4f410201ba9abe3a0dd96b5c42d56e85f7a5e11e7d30b85afc116
languageName: node
linkType: hard
"@floating-ui/react-dom@npm:^2.1.0":
version: 2.1.0
resolution: "@floating-ui/react-dom@npm:2.1.0"
@ -2486,7 +2512,7 @@ __metadata:
languageName: node
linkType: hard
"@floating-ui/utils@npm:^0.2.0":
"@floating-ui/utils@npm:^0.2.0, @floating-ui/utils@npm:^0.2.1":
version: 0.2.1
resolution: "@floating-ui/utils@npm:0.2.1"
checksum: 10/33c9ab346e7b05c5a1e6a95bc902aafcfc2c9d513a147e2491468843bd5607531b06d0b9aa56aa491cbf22a6c2495c18ccfc4c0344baec54a689a7bb8e4898d6
@ -3100,13 +3126,13 @@ __metadata:
linkType: soft
"@grafana/e2e-selectors@npm:^11.0.0":
version: 11.0.0
resolution: "@grafana/e2e-selectors@npm:11.0.0"
version: 11.1.0
resolution: "@grafana/e2e-selectors@npm:11.1.0"
dependencies:
"@grafana/tsconfig": "npm:^1.3.0-rc1"
tslib: "npm:2.6.2"
typescript: "npm:5.3.3"
checksum: 10/0e327c5afc342bca9be46b0b3fb7b55d69e8b7b6e9912be3255ce52abcb8832265246a0e6b2f877112a752b6e44f6f7edf5c4d2a32b5edc84800d19c880dfc6c
tslib: "npm:2.6.3"
typescript: "npm:5.4.5"
checksum: 10/010a32e8b562d0da83b008646b9928a96a79957096eed713aa67b227d8ad6055d22cc0ec26f87fd9839cfb28344d0012f49c3c823defc6e91f4ab05ed7d8c465
languageName: node
linkType: hard
@ -3554,9 +3580,9 @@ __metadata:
languageName: unknown
linkType: soft
"@grafana/scenes@npm:^5.3.4":
version: 5.3.6
resolution: "@grafana/scenes@npm:5.3.6"
"@grafana/scenes@npm:5.3.5":
version: 5.3.5
resolution: "@grafana/scenes@npm:5.3.5"
dependencies:
"@grafana/e2e-selectors": "npm:^11.0.0"
"@leeoniya/ufuzzy": "npm:^1.0.14"
@ -3571,7 +3597,7 @@ __metadata:
"@grafana/ui": ^10.4.1
react: ^18.0.0
react-dom: ^18.0.0
checksum: 10/d487f6f1c53f4dba1562925f4d69c75b5882ce0cdb5cdf95eb6998c3f4fcf13e597200e44e47ff9105bee1e1be23007e568bbe52c5049f92e27f9a14abe00178
checksum: 10/2ffbbda4a90e9b0f17c6a4d9b210b97321cc531427c1b11c75b32c72631c4122950bc310bb103cb29f17a50a98844fd91ae689872625614b0284befc61081eb5
languageName: node
linkType: hard
@ -5719,7 +5745,7 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-portal@npm:1.0.4, @radix-ui/react-portal@npm:^1.0.1":
"@radix-ui/react-portal@npm:1.0.4":
version: 1.0.4
resolution: "@radix-ui/react-portal@npm:1.0.4"
dependencies:
@ -5739,6 +5765,26 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-portal@npm:^1.0.1":
version: 1.0.3
resolution: "@radix-ui/react-portal@npm:1.0.3"
dependencies:
"@babel/runtime": "npm:^7.13.10"
"@radix-ui/react-primitive": "npm:1.0.3"
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
checksum: 10/d352bcd6ad65eb43c9e0d72d0755c2aae85e03fb287770866262be3a2d5302b2885aee3cd99f2bbf62ecd14fcb1460703f1dcdc40351f77ad887b931c6f0012a
languageName: node
linkType: hard
"@radix-ui/react-presence@npm:1.0.1":
version: 1.0.1
resolution: "@radix-ui/react-presence@npm:1.0.1"
@ -17099,7 +17145,7 @@ __metadata:
"@grafana/prometheus": "workspace:*"
"@grafana/runtime": "workspace:*"
"@grafana/saga-icons": "workspace:*"
"@grafana/scenes": "npm:^5.3.4"
"@grafana/scenes": "npm:5.3.5"
"@grafana/schema": "workspace:*"
"@grafana/sql": "workspace:*"
"@grafana/tsconfig": "npm:^1.3.0-rc1"
@ -18148,7 +18194,16 @@ __metadata:
languageName: node
linkType: hard
"i18next@npm:^23.0.0, i18next@npm:^23.11.5, i18next@npm:^23.5.1":
"i18next@npm:^23.0.0, i18next@npm:^23.5.1":
version: 23.11.3
resolution: "i18next@npm:23.11.3"
dependencies:
"@babel/runtime": "npm:^7.23.2"
checksum: 10/9d562ade19d0beba16683ff94967a6dedc0a32ce335d203c5a160f075ac5a9a7a9adb164085a6b7b69328568bc932a65b92664834c2bf3e15d8f3bff90f15353
languageName: node
linkType: hard
"i18next@npm:^23.11.5":
version: 23.11.5
resolution: "i18next@npm:23.11.5"
dependencies:
@ -29377,13 +29432,6 @@ __metadata:
languageName: node
linkType: hard
"tslib@npm:2.6.2":
version: 2.6.2
resolution: "tslib@npm:2.6.2"
checksum: 10/bd26c22d36736513980091a1e356378e8b662ded04204453d353a7f34a4c21ed0afc59b5f90719d4ba756e581a162ecbf93118dc9c6be5acf70aa309188166ca
languageName: node
linkType: hard
"tslib@npm:2.6.3, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.4.1, tslib@npm:^2.6.2":
version: 2.6.3
resolution: "tslib@npm:2.6.3"
@ -29522,13 +29570,20 @@ __metadata:
languageName: node
linkType: hard
"type-fest@npm:^4.18.2, type-fest@npm:^4.9.0":
"type-fest@npm:^4.18.2":
version: 4.18.3
resolution: "type-fest@npm:4.18.3"
checksum: 10/eb750920d0ef3639177f581edd6489d972c5c5827abb602a9c9662889aad148a7d558257e36c563f1beb81a2e417faec52ecec9799b28531d8335856f91e6dff
languageName: node
linkType: hard
"type-fest@npm:^4.9.0":
version: 4.10.2
resolution: "type-fest@npm:4.10.2"
checksum: 10/2b1ad1270d9fabeeb506ba831d513caeb05bfc852e5e012511d785ce9dc68d773fe0a42bddf857a362c7f3406244809c5b8a698b743bb7617d4a8c470672087f
languageName: node
linkType: hard
"type-is@npm:~1.6.18":
version: 1.6.18
resolution: "type-is@npm:1.6.18"
@ -29622,16 +29677,6 @@ __metadata:
languageName: node
linkType: hard
"typescript@npm:5.3.3":
version: 5.3.3
resolution: "typescript@npm:5.3.3"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10/6e4e6a14a50c222b3d14d4ea2f729e79f972fa536ac1522b91202a9a65af3605c2928c4a790a4a50aa13694d461c479ba92cedaeb1e7b190aadaa4e4b96b8e18
languageName: node
linkType: hard
"typescript@npm:5.4.5, typescript@npm:>=2.7, typescript@npm:>=3 < 6, typescript@npm:^5.0.0, typescript@npm:^5.0.4, typescript@npm:^5.2.2":
version: 5.4.5
resolution: "typescript@npm:5.4.5"
@ -29652,16 +29697,6 @@ __metadata:
languageName: node
linkType: hard
"typescript@patch:typescript@npm%3A5.3.3#optional!builtin<compat/typescript>":
version: 5.3.3
resolution: "typescript@patch:typescript@npm%3A5.3.3#optional!builtin<compat/typescript>::version=5.3.3&hash=e012d7"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10/c93786fcc9a70718ba1e3819bab56064ead5817004d1b8186f8ca66165f3a2d0100fee91fa64c840dcd45f994ca5d615d8e1f566d39a7470fc1e014dbb4cf15d
languageName: node
linkType: hard
"typescript@patch:typescript@npm%3A5.4.5#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A>=2.7#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A>=3 < 6#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.0.0#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.0.4#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.2.2#optional!builtin<compat/typescript>":
version: 5.4.5
resolution: "typescript@patch:typescript@npm%3A5.4.5#optional!builtin<compat/typescript>::version=5.4.5&hash=5adc0c"