Merge branch 'main' into giuseppe/enable-tracing-feature-flag

This commit is contained in:
Giuseppe Guerra
2024-01-09 16:53:33 +01:00
74 changed files with 2709 additions and 1068 deletions
+24 -76
View File
@@ -4374,12 +4374,9 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
[0, 0, 0, "Do not use any type assertions.", "12"],
[0, 0, 0, "Do not use any type assertions.", "13"],
[0, 0, 0, "Unexpected any. Specify a different type.", "14"],
[0, 0, 0, "Do not use any type assertions.", "15"]
[0, 0, 0, "Do not use any type assertions.", "10"],
[0, 0, 0, "Do not use any type assertions.", "11"],
[0, 0, 0, "Do not use any type assertions.", "12"]
],
"public/app/features/trails/SelectMetricTrailView.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
@@ -5947,24 +5944,16 @@ exports[`better eslint`] = {
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"]
],
"public/app/plugins/datasource/tempo/traceql/autocomplete.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/plugins/datasource/zipkin/ConfigEditor.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"]
],
"public/app/plugins/datasource/zipkin/QueryField.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/plugins/datasource/zipkin/datasource.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/plugins/datasource/zipkin/utils/testResponse.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@@ -6003,8 +5992,7 @@ exports[`better eslint`] = {
],
"public/app/plugins/panel/alertlist/AlertListMigrationHandler.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/plugins/panel/alertlist/UnifiedAlertList.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
@@ -6036,18 +6024,11 @@ exports[`better eslint`] = {
],
"public/app/plugins/panel/annolist/AnnoListPanel.test.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/plugins/panel/annolist/AnnoListPanel.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Styles should be written using objects.", "3"]
],
"public/app/plugins/panel/annolist/module.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"]
],
"public/app/plugins/panel/barchart/BarChartPanel.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
@@ -6094,9 +6075,6 @@ exports[`better eslint`] = {
"public/app/plugins/panel/canvas/globalStyles.ts:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"]
],
"public/app/plugins/panel/dashlist/migrations.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/plugins/panel/dashlist/styles.ts:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"],
@@ -6117,8 +6095,7 @@ exports[`better eslint`] = {
],
"public/app/plugins/panel/debug/EventBusLogger.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/plugins/panel/gauge/GaugeMigrations.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
@@ -6168,8 +6145,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/plugins/panel/geomap/utils/layers.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/plugins/panel/geomap/utils/tooltip.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
@@ -6246,9 +6222,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "16"]
],
"public/app/plugins/panel/histogram/Histogram.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/plugins/panel/live/LiveChannelEditor.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@@ -6330,13 +6304,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"]
],
"public/app/plugins/panel/nodeGraph/usePanning.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/plugins/panel/nodeGraph/utils.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/plugins/panel/piechart/PieChart.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"],
@@ -6349,8 +6316,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/plugins/panel/stat/StatMigrations.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/plugins/panel/state-timeline/migrations.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@@ -6403,14 +6369,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
[0, 0, 0, "Unexpected any. Specify a different type.", "12"],
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
[0, 0, 0, "Unexpected any. Specify a different type.", "14"],
[0, 0, 0, "Unexpected any. Specify a different type.", "15"],
[0, 0, 0, "Unexpected any. Specify a different type.", "16"],
[0, 0, 0, "Unexpected any. Specify a different type.", "17"],
[0, 0, 0, "Unexpected any. Specify a different type.", "18"]
[0, 0, 0, "Unexpected any. Specify a different type.", "11"]
],
"public/app/plugins/panel/table-old/renderer.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@@ -6485,17 +6444,15 @@ exports[`better eslint`] = {
],
"public/app/plugins/panel/timeseries/migrations.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Do not use any type assertions.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Do not use any type assertions.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"]
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Do not use any type assertions.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Do not use any type assertions.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Do not use any type assertions.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"]
],
"public/app/plugins/panel/timeseries/plugins/ThresholdDragHandle.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
@@ -6569,14 +6526,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
],
"public/app/plugins/panel/xychart/dims.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
],
"public/app/plugins/panel/xychart/scatter.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
@@ -6693,8 +6642,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"]
[0, 0, 0, "Unexpected any. Specify a different type.", "10"]
]
}`
};
+3 -4
View File
@@ -7,7 +7,6 @@
"history", // we should bump this together with react-router-dom (see https://github.com/grafana/grafana/issues/76744)
"react-router-dom", // we should bump this together with history (see https://github.com/grafana/grafana/issues/76744)
"monaco-editor", // due to us exposing this via @grafana/ui/CodeEditor's props bumping can break plugins
"react-hook-form", // due to us exposing these hooks via @grafana/ui form components bumping can break plugins
],
"includePaths": ["package.json", "packages/**", "public/app/plugins/**"],
"ignorePaths": ["emails/**", "plugins-bundled/**", "**/mocks/**", "packages/grafana-e2e/**"],
@@ -15,10 +14,10 @@
"postUpdateOptions": ["yarnDedupeHighest"],
"packageRules": [
{
"automerge": true,
"matchCurrentVersion": "!/^0/",
"matchUpdateTypes": ["patch"],
"excludePackagePatterns": ["^@?storybook", "^@locker"],
"extends": ["schedule:monthly"],
"groupName": "Monthly patch updates"
"excludePackagePatterns": ["^@?storybook", "^@locker"]
},
{
"matchPackagePatterns": ["^@?storybook"],
@@ -91,9 +91,9 @@ To troubleshoot configuration and other issues, check the log file located at `/
### Derived fields
Derived Fields are used to extract new fields from a log message and create a link from the value of the field.
Derived Fields are used to extract new fields from your logs and create a link from the value of the field.
For example, you can link to your tracing backend directly from your logs, or link to a user profile page if the log line contains a corresponding userId.
For example, you can link to your tracing backend directly from your logs, or link to a user profile page if the log line contains a corresponding `userId`.
These links appear in the [log details][].
You can add multiple derived fields.
@@ -106,7 +106,11 @@ Each derived field consists of the following:
- **Name** - Sets the field name. Displayed as a label in the log details.
- **Regex** - Defines a regular expression to parse a part of the log message and capture it as the value of the new field. Can contain only one capture group.
- **Type** - Defines the type of the derived field. It can be either:
- **Regex**: A regular expression to parse a part of the log message and capture it as the value of the new field. Can contain only one capture group.
- **Label**: A label from the selected log line. This can be any type of label - indexed, parsed or structured metadata. The label's value will be used as the value of the derived field.
- **URL/query** Sets the full link URL if the link is external, or a query for the target data source if the link is internal. You can interpolate the value from the field with the `${__value.raw}` macro.
+1 -1
View File
@@ -81,7 +81,7 @@
"@betterer/betterer": "5.4.0",
"@betterer/cli": "5.4.0",
"@betterer/eslint": "5.4.0",
"@cypress/webpack-preprocessor": "6.0.0",
"@cypress/webpack-preprocessor": "6.0.1",
"@emotion/eslint-plugin": "11.11.0",
"@grafana/eslint-config": "6.0.1",
"@grafana/eslint-plugin": "link:./packages/grafana-eslint-rules",
@@ -1,4 +1,4 @@
import { css, CSSObject, cx } from '@emotion/css';
import { css, cx } from '@emotion/css';
import React, { AnchorHTMLAttributes, ButtonHTMLAttributes } from 'react';
import { GrafanaTheme2, ThemeRichColor } from '@grafana/data';
@@ -224,7 +224,7 @@ export const getButtonStyles = (props: StyleProps) => {
};
};
function getButtonVariantStyles(theme: GrafanaTheme2, color: ThemeRichColor, fill: ButtonFill): CSSObject {
function getButtonVariantStyles(theme: GrafanaTheme2, color: ThemeRichColor, fill: ButtonFill) {
let outlineBorderColor = color.border;
let borderColor = 'transparent';
let hoverBorderColor = 'transparent';
@@ -293,7 +293,7 @@ function getButtonVariantStyles(theme: GrafanaTheme2, color: ThemeRichColor, fil
}
function getPropertiesForDisabled(theme: GrafanaTheme2, variant: ButtonVariant, fill: ButtonFill) {
const disabledStyles: CSSObject = {
const disabledStyles = {
cursor: 'not-allowed',
boxShadow: 'none',
color: theme.colors.text.disabled,
@@ -13,7 +13,7 @@ export default function resetSelectStyles(theme: GrafanaTheme2) {
groupHeading: () => ({}),
indicatorsContainer: () => ({}),
indicatorSeparator: () => ({}),
input: function (originalStyles: CSSObjectWithLabel): CSSObjectWithLabel {
input: function (originalStyles: CSSObjectWithLabel) {
return {
...originalStyles,
color: 'inherit',
@@ -37,7 +37,7 @@ export default function resetSelectStyles(theme: GrafanaTheme2) {
multiValueRemove: () => ({}),
noOptionsMessage: () => ({}),
option: () => ({}),
placeholder: (originalStyles: CSSObjectWithLabel): CSSObjectWithLabel => ({
placeholder: (originalStyles: CSSObjectWithLabel) => ({
...originalStyles,
color: theme.colors.text.secondary,
}),
@@ -1,4 +1,4 @@
import { css, CSSObject } from '@emotion/react';
import { css } from '@emotion/react';
import { GrafanaTheme2, ThemeTypographyVariant } from '@grafana/data';
@@ -144,7 +144,7 @@ export function getElementStyles(theme: GrafanaTheme2) {
});
}
export function getVariantStyles(variant: ThemeTypographyVariant): CSSObject {
export function getVariantStyles(variant: ThemeTypographyVariant) {
return {
margin: 0,
fontSize: variant.fontSize,
+3 -4
View File
@@ -1,4 +1,3 @@
import { CSSObject } from '@emotion/css';
import tinycolor from 'tinycolor2';
import { GrafanaTheme, GrafanaTheme2 } from '@grafana/data';
@@ -55,14 +54,14 @@ export const focusCss = (theme: GrafanaTheme | GrafanaTheme2) => {
transition-timing-function: cubic-bezier(0.19, 1, 0.22, 1);`;
};
export function getMouseFocusStyles(theme: GrafanaTheme | GrafanaTheme2): CSSObject {
export function getMouseFocusStyles(theme: GrafanaTheme | GrafanaTheme2) {
return {
outline: 'none',
boxShadow: `none`,
};
}
export function getFocusStyles(theme: GrafanaTheme2): CSSObject {
export function getFocusStyles(theme: GrafanaTheme2) {
return {
outline: '2px dotted transparent',
outlineOffset: '2px',
@@ -74,7 +73,7 @@ export function getFocusStyles(theme: GrafanaTheme2): CSSObject {
}
// max-width is set up based on .grafana-tooltip class that's used in dashboard
export const getTooltipContainerStyles = (theme: GrafanaTheme2): CSSObject => ({
export const getTooltipContainerStyles = (theme: GrafanaTheme2) => ({
overflow: 'hidden',
background: theme.colors.background.secondary,
boxShadow: theme.shadows.z2,
+1 -1
View File
@@ -51,7 +51,7 @@ func (s *Service) externalPluginSources() []plugins.PluginSource {
var pluginDirs []string
for _, dir := range d {
if dir.IsDir() {
if dir.IsDir() || dir.Type()&os.ModeSymlink == os.ModeSymlink {
pluginDirs = append(pluginDirs, filepath.Join(pluginsPath, dir.Name()))
}
}
@@ -78,4 +78,41 @@ func TestSources_List(t *testing.T) {
require.False(t, exists)
require.Equal(t, plugins.Signature{}, sig)
})
t.Run("Plugin sources are populated with symbolic links", func(t *testing.T) {
testdata, err := filepath.Abs("../testdata")
require.NoError(t, err)
cfg := &setting.Cfg{
StaticRootPath: testdata,
PluginsPath: filepath.Join(testdata, "symbolic-plugin-dirs"),
BundledPluginsPath: filepath.Join(testdata, "unsigned-panel"),
}
s := ProvideService(cfg)
ctx := context.Background()
srcs := s.List(ctx)
uris := map[plugins.Class]map[string]struct{}{}
for _, s := range srcs {
class := s.PluginClass(ctx)
if _, exists := uris[class]; !exists {
uris[class] = map[string]struct{}{}
}
for _, uri := range s.PluginURIs(ctx) {
uris[class][uri] = struct{}{}
}
}
require.Equal(t, uris[plugins.ClassCore], map[string]struct{}{
filepath.Join(testdata, "app", "plugins", "datasource"): {},
filepath.Join(testdata, "app", "plugins", "panel"): {},
}, "should include core plugins")
require.Equal(t, uris[plugins.ClassBundled], map[string]struct{}{
filepath.Join(testdata, "unsigned-panel"): {},
}, "should include bundle plugin")
require.Equal(t, uris[plugins.ClassExternal], map[string]struct{}{
filepath.Join(testdata, "symbolic-plugin-dirs", "plugin"): {},
}, "should include external symlinked plugin")
})
}
+2 -2
View File
@@ -177,7 +177,7 @@ func (api *Api) updateProviderSettings(c *contextmodel.ReqContext) response.Resp
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to update provider settings", err)
}
return response.JSON(http.StatusNoContent, nil)
return response.Empty(http.StatusNoContent)
}
// swagger:route DELETE /v1/sso-settings/{key} sso_settings removeProviderSettings
@@ -209,7 +209,7 @@ func (api *Api) removeProviderSettings(c *contextmodel.ReqContext) response.Resp
return response.Error(http.StatusInternalServerError, "Failed to delete provider settings", err)
}
return response.JSON(http.StatusNoContent, nil)
return response.Empty(http.StatusNoContent)
}
// swagger:parameters getProviderSettings
@@ -0,0 +1,205 @@
package postgres
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/experimental"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb/sqleng"
_ "github.com/lib/pq"
)
var updateGoldenFiles = false
// These tests require a real postgres database:
// - make devenv sources=potgres_tests
// - either set the env variable GRAFANA_TEST_DB = postgres
// - or set `forceRun := true` below
//
// The tests require a PostgreSQL db named grafanadstest and a user/password grafanatest/grafanatest!
// Use the docker/blocks/postgres_tests/docker-compose.yaml to spin up a
// preconfigured Postgres server suitable for running these tests.
func TestIntegrationPostgresSnapshots(t *testing.T) {
// the logic in this function is copied from postgres_tests.go
shouldRunTest := func() bool {
if testing.Short() {
return false
}
testDbName, present := os.LookupEnv("GRAFANA_TEST_DB")
if present && testDbName == "postgres" {
return true
}
return false
}
if !shouldRunTest() {
t.Skip()
}
openDB := func() *sql.DB {
host := os.Getenv("POSTGRES_HOST")
if host == "" {
host = "localhost"
}
port := os.Getenv("POSTGRES_PORT")
if port == "" {
port = "5432"
}
connStr := fmt.Sprintf("user=grafanatest password=grafanatest host=%s port=%s dbname=grafanadstest sslmode=disable",
host, port)
db, err := sql.Open("postgres", connStr)
require.NoError(t, err)
return db
}
sqlQueryCommentRe := regexp.MustCompile(`^-- (.+)\n`)
readSqlFile := func(path string) (string, string) {
// the file-path is not coming from the outside,
// it is hardcoded in this file.
//nolint:gosec
sqlBytes, err := os.ReadFile(path)
require.NoError(t, err)
sql := string(sqlBytes)
// first line of the file contains the sql query to run, commented out
match := sqlQueryCommentRe.FindStringSubmatch(sql)
require.Len(t, match, 2)
rawSQL := strings.TrimSpace(match[1])
return rawSQL, sql
}
makeQuery := func(rawSQL string, format string) backend.QueryDataRequest {
queryData := map[string]string{
"rawSql": rawSQL,
"format": format,
}
queryBytes, err := json.Marshal(queryData)
require.NoError(t, err)
return backend.QueryDataRequest{
Queries: []backend.DataQuery{
{
JSON: queryBytes,
RefID: "A",
TimeRange: backend.TimeRange{
From: time.Date(2023, 12, 24, 14, 15, 0, 0, time.UTC),
To: time.Date(2023, 12, 24, 14, 45, 0, 0, time.UTC),
},
},
},
}
}
tt := []struct {
name string
format string
}{
{format: "time_series", name: "simple"},
{format: "time_series", name: "7x_compat_metric_label"},
{format: "time_series", name: "convert_to_float64"},
{format: "time_series", name: "fill_null"},
{format: "time_series", name: "fill_previous"},
{format: "time_series", name: "fill_value"},
{format: "table", name: "simple"},
{format: "table", name: "types_numeric"},
{format: "table", name: "types_char"},
{format: "table", name: "types_datetime"},
{format: "table", name: "types_other"},
{format: "table", name: "timestamp_convert_bigint"},
{format: "table", name: "timestamp_convert_integer"},
}
for _, test := range tt {
require.True(t, test.format == "table" || test.format == "time_series")
t.Run(test.name, func(t *testing.T) {
origInterpolate := sqleng.Interpolate
t.Cleanup(func() {
sqleng.Interpolate = origInterpolate
})
sqleng.Interpolate = func(query backend.DataQuery, timeRange backend.TimeRange, timeInterval string, sql string) (string, error) {
return sql, nil
}
db := openDB()
t.Cleanup((func() {
_, err := db.Exec("DROP TABLE tbl")
require.NoError(t, err)
err = db.Close()
require.NoError(t, err)
}))
cfg := setting.NewCfg()
cfg.DataPath = t.TempDir()
jsonData := sqleng.JsonData{
MaxOpenConns: 0,
MaxIdleConns: 2,
ConnMaxLifetime: 14400,
Timescaledb: false,
ConfigurationMethod: "file-path",
}
dsInfo := sqleng.DataSourceInfo{
JsonData: jsonData,
DecryptedSecureJSONData: map[string]string{},
}
config := sqleng.DataPluginConfiguration{
DSInfo: dsInfo,
MetricColumnTypes: []string{"UNKNOWN", "TEXT", "VARCHAR", "CHAR"},
RowLimit: 1000000,
}
queryResultTransformer := postgresQueryResultTransformer{}
logger := log.New()
handler, err := sqleng.NewQueryDataHandler(cfg, db, config, &queryResultTransformer, newPostgresMacroEngine(dsInfo.JsonData.Timescaledb),
logger)
require.NoError(t, err)
sqlFilePath := filepath.Join("testdata", test.format, test.name+".sql")
goldenFileName := filepath.Join(test.format, test.name+".golden")
rawSQL, sql := readSqlFile(sqlFilePath)
_, err = db.Exec(sql)
require.NoError(t, err)
query := makeQuery(rawSQL, test.format)
result, err := handler.QueryData(context.Background(), &query)
require.Len(t, result.Responses, 1)
response, found := result.Responses["A"]
require.True(t, found)
require.NoError(t, err)
experimental.CheckGoldenJSONResponse(t, "testdata", goldenFileName, &response, updateGoldenFiles)
})
}
}
@@ -0,0 +1,112 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "typeVersion": [
// 0,
// 0
// ],
// "executedQueryString": "SELECT * FROM tbl"
// }
// Name:
// Dimensions: 3 Fields by 10 Rows
// +-------------------------------+------------------+-----------------+
// | Name: time | Name: v | Name: c |
// | Labels: | Labels: | Labels: |
// | Type: []*time.Time | Type: []*float64 | Type: []*string |
// +-------------------------------+------------------+-----------------+
// | 2023-12-24 14:30:03 +0000 UTC | 10 | a |
// | 2023-12-24 14:30:03 +0000 UTC | 110 | b |
// | 2023-12-24 14:31:03 +0000 UTC | 20 | a |
// | 2023-12-24 14:31:03 +0000 UTC | 120 | b |
// | 2023-12-24 14:32:03 +0000 UTC | 30 | a |
// | 2023-12-24 14:32:03 +0000 UTC | 130 | b |
// | 2023-12-24 14:33:03 +0000 UTC | 40 | a |
// | 2023-12-24 14:33:03 +0000 UTC | 140 | b |
// | 2023-12-24 14:34:03 +0000 UTC | 50 | a |
// | 2023-12-24 14:34:03 +0000 UTC | 150 | b |
// +-------------------------------+------------------+-----------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"meta": {
"typeVersion": [
0,
0
],
"executedQueryString": "SELECT * FROM tbl"
},
"fields": [
{
"name": "time",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "v",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
}
},
{
"name": "c",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
}
]
},
"data": {
"values": [
[
1703428203000,
1703428203000,
1703428263000,
1703428263000,
1703428323000,
1703428323000,
1703428383000,
1703428383000,
1703428443000,
1703428443000
],
[
10,
110,
20,
120,
30,
130,
40,
140,
50,
150
],
[
"a",
"b",
"a",
"b",
"a",
"b",
"a",
"b",
"a",
"b"
]
]
}
}
]
}
@@ -0,0 +1,18 @@
-- SELECT * FROM tbl
CREATE TEMPORARY TABLE tbl (
"time" timestamp with time zone,
v double precision,
c text
);
INSERT INTO tbl ("time", v, c) VALUES
('2023-12-24 14:30:03 UTC', 10, 'a'),
('2023-12-24 14:30:03 UTC', 110, 'b'),
('2023-12-24 14:31:03 UTC', 20, 'a'),
('2023-12-24 14:31:03 UTC', 120, 'b'),
('2023-12-24 14:32:03 UTC', 30, 'a'),
('2023-12-24 14:32:03 UTC', 130, 'b'),
('2023-12-24 14:33:03 UTC', 40, 'a'),
('2023-12-24 14:33:03 UTC', 140, 'b'),
('2023-12-24 14:34:03 UTC', 50, 'a'),
('2023-12-24 14:34:03 UTC', 150, 'b');
@@ -0,0 +1,113 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "typeVersion": [
// 0,
// 0
// ],
// "executedQueryString": "SELECT * FROM tbl"
// }
// Name:
// Dimensions: 4 Fields by 4 Rows
// +--------------------------------------+-----------------------------------+---------------------+-----------------------------------+
// | Name: reallyt | Name: time | Name: n | Name: timeend |
// | Labels: | Labels: | Labels: | Labels: |
// | Type: []*time.Time | Type: []*time.Time | Type: []*int64 | Type: []*time.Time |
// +--------------------------------------+-----------------------------------+---------------------+-----------------------------------+
// | 2023-12-24 14:21:27 +0000 UTC | 2023-12-21 13:21:27 +0100 CET | 1703161287 | 2023-12-21 13:21:52 +0100 CET |
// | 2023-12-24 14:21:27.724 +0000 UTC | 2023-12-21 13:21:27.724 +0100 CET | 1703161287724 | 2023-12-21 13:21:52.522 +0100 CET |
// | 2023-12-24 14:21:27.724919 +0000 UTC | 2023-12-21 13:21:27.724 +0100 CET | 1703161287724919000 | 2023-12-21 13:21:52.522 +0100 CET |
// | null | null | null | null |
// +--------------------------------------+-----------------------------------+---------------------+-----------------------------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"meta": {
"typeVersion": [
0,
0
],
"executedQueryString": "SELECT * FROM tbl"
},
"fields": [
{
"name": "reallyt",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "time",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "n",
"type": "number",
"typeInfo": {
"frame": "int64",
"nullable": true
}
},
{
"name": "timeend",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
}
]
},
"data": {
"values": [
[
1703427687000,
1703427687724,
1703427687724,
null
],
[
1703161287000,
1703161287724,
1703161287724,
null
],
[
1703161287,
1703161287724,
1703161287724919000,
null
],
[
1703161312000,
1703161312522,
1703161312522,
null
]
],
"nanos": [
[
0,
0,
919000,
0
],
null,
null,
null
]
}
}
]
}
@@ -0,0 +1,14 @@
-- SELECT * FROM tbl
-- the time-field and time-end field gets converted to time.Time
CREATE TEMPORARY TABLE tbl (
reallyt timestamp with time zone, -- reference real timestamp
"time" bigint,
n bigint, -- normal number, it should not get converted to a timestamp
timeend bigint
);
INSERT INTO tbl (reallyt, "time", n, timeend) VALUES
('2023-12-24T14:21:27 UTC', 1703161287, 1703161287, 1703161312),
('2023-12-24T14:21:27.724 UTC', 1703161287724, 1703161287724, 1703161312522),
('2023-12-24T14:21:27.724919 UTC', 1703161287724919000, 1703161287724919000, 1703161312522186000),
(NULL, NULL, NULL, NULL);
@@ -0,0 +1,92 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "typeVersion": [
// 0,
// 0
// ],
// "executedQueryString": "SELECT * FROM tbl"
// }
// Name:
// Dimensions: 4 Fields by 2 Rows
// +-------------------------------+-------------------------------+----------------+-------------------------------+
// | Name: reallyt | Name: time | Name: n | Name: timeend |
// | Labels: | Labels: | Labels: | Labels: |
// | Type: []*time.Time | Type: []*time.Time | Type: []*int32 | Type: []*time.Time |
// +-------------------------------+-------------------------------+----------------+-------------------------------+
// | 2023-12-24 14:21:27 +0000 UTC | 2023-12-21 13:21:27 +0100 CET | 1703161287 | 2023-12-21 13:21:52 +0100 CET |
// | null | null | null | null |
// +-------------------------------+-------------------------------+----------------+-------------------------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"meta": {
"typeVersion": [
0,
0
],
"executedQueryString": "SELECT * FROM tbl"
},
"fields": [
{
"name": "reallyt",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "time",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "n",
"type": "number",
"typeInfo": {
"frame": "int32",
"nullable": true
}
},
{
"name": "timeend",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
}
]
},
"data": {
"values": [
[
1703427687000,
null
],
[
1703161287000,
null
],
[
1703161287,
null
],
[
1703161312000,
null
]
]
}
}
]
}
@@ -0,0 +1,12 @@
-- SELECT * FROM tbl
-- the time-field and time-end field gets converted to time.Time
CREATE TEMPORARY TABLE tbl (
reallyt timestamp with time zone, -- reference real timestamp
"time" integer, -- 32bits, seconds-as-number is highest we can go, milliseconds-as-number does not fit
n integer, -- normal number, it should not get converted to a timestamp
timeend integer
);
INSERT INTO tbl (reallyt, "time", n, timeend) VALUES
('2023-12-24T14:21:27 UTC', 1703161287, 1703161287, 1703161312),
(NULL, NULL, NULL, NULL);
@@ -0,0 +1,140 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "typeVersion": [
// 0,
// 0
// ],
// "executedQueryString": "SELECT * FROM tbl"
// }
// Name:
// Dimensions: 8 Fields by 2 Rows
// +-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+
// | Name: cv | Name: cvnn | Name: c | Name: cnn | Name: bpc | Name: bpcnn | Name: t | Name: tnn |
// | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: |
// | Type: []*string | Type: []*string | Type: []*string | Type: []*string | Type: []*string | Type: []*string | Type: []*string | Type: []*string |
// +-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+
// | one | two | three | four | five | six | seven | eight |
// | null | xtwo | null | xfour | null | xsix | null | xeight |
// +-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"meta": {
"typeVersion": [
0,
0
],
"executedQueryString": "SELECT * FROM tbl"
},
"fields": [
{
"name": "cv",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
},
{
"name": "cvnn",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
},
{
"name": "c",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
},
{
"name": "cnn",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
},
{
"name": "bpc",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
},
{
"name": "bpcnn",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
},
{
"name": "t",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
},
{
"name": "tnn",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
}
]
},
"data": {
"values": [
[
"one",
null
],
[
"two",
"xtwo"
],
[
"three ",
null
],
[
"four ",
"xfour "
],
[
"five",
null
],
[
"six",
"xsix"
],
[
"seven",
null
],
[
"eight",
"xeight"
]
]
}
}
]
}
@@ -0,0 +1,16 @@
-- SELECT * FROM tbl
-- test all character-based postgres data types
CREATE TEMPORARY TABLE tbl (
cv character varying(10),
cvnn character varying(10) NOT NULL,
c character(10),
cnn character(10) NOT NULL,
bpc bpchar,
bpcnn bpchar NOT NULL,
t text,
tnn text NOT NULL
);
INSERT INTO tbl (cv, cvnn, c, cnn, bpc, bpcnn, t, tnn) VALUES
('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight'),
(NULL, 'xtwo', NULL, 'xfour', NULL, 'xsix', NULL, 'xeight');
@@ -0,0 +1,226 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "typeVersion": [
// 0,
// 0
// ],
// "executedQueryString": "SELECT * FROM tbl"
// }
// Name:
// Dimensions: 12 Fields by 2 Rows
// +----------------------------------------+----------------------------------------+--------------------------------------+--------------------------------------+---------------------------------+---------------------------------+--------------------------------------+--------------------------------------+----------------------------------------+----------------------------------------+-----------------+-----------------+
// | Name: ts | Name: tsnn | Name: tsz | Name: tsznn | Name: d | Name: dnn | Name: t | Name: tnn | Name: tz | Name: tznn | Name: i | Name: inn |
// | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: |
// | Type: []*time.Time | Type: []*time.Time | Type: []*time.Time | Type: []*time.Time | Type: []*time.Time | Type: []*time.Time | Type: []*time.Time | Type: []*time.Time | Type: []*time.Time | Type: []*time.Time | Type: []*string | Type: []*string |
// +----------------------------------------+----------------------------------------+--------------------------------------+--------------------------------------+---------------------------------+---------------------------------+--------------------------------------+--------------------------------------+----------------------------------------+----------------------------------------+-----------------+-----------------+
// | 2023-11-15 05:06:07.123456 +0000 +0000 | 2023-11-15 05:06:08.123456 +0000 +0000 | 2021-07-22 11:22:33.654321 +0000 UTC | 2021-07-22 11:22:34.654321 +0000 UTC | 2023-12-20 00:00:00 +0000 +0000 | 2023-12-21 00:00:00 +0000 +0000 | 0000-01-01 12:34:56.234567 +0000 UTC | 0000-01-01 12:34:57.234567 +0000 UTC | 0000-01-01 23:12:36.765432 +0100 +0100 | 0000-01-01 23:12:37.765432 +0100 +0100 | 00:00:00.987654 | 00:00:00.887654 |
// | null | 2023-11-15 05:06:09.123456 +0000 +0000 | null | 2021-07-22 11:22:35.654321 +0000 UTC | null | 2023-12-22 00:00:00 +0000 +0000 | null | 0000-01-01 12:34:58.234567 +0000 UTC | null | 0000-01-01 23:12:38.765432 +0100 +0100 | null | 00:00:00.787654 |
// +----------------------------------------+----------------------------------------+--------------------------------------+--------------------------------------+---------------------------------+---------------------------------+--------------------------------------+--------------------------------------+----------------------------------------+----------------------------------------+-----------------+-----------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"meta": {
"typeVersion": [
0,
0
],
"executedQueryString": "SELECT * FROM tbl"
},
"fields": [
{
"name": "ts",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "tsnn",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "tsz",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "tsznn",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "d",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "dnn",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "t",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "tnn",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "tz",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "tznn",
"type": "time",
"typeInfo": {
"frame": "time.Time",
"nullable": true
}
},
{
"name": "i",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
},
{
"name": "inn",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
}
]
},
"data": {
"values": [
[
1700024767123,
null
],
[
1700024768123,
1700024769123
],
[
1626952953654,
null
],
[
1626952954654,
1626952955654
],
[
1703030400000,
null
],
[
1703116800000,
1703203200000
],
[
-62167173903766,
null
],
[
-62167173902766,
-62167173901766
],
[
-62167139243235,
null
],
[
-62167139242235,
-62167139241235
],
[
"00:00:00.987654",
null
],
[
"00:00:00.887654",
"00:00:00.787654"
]
],
"nanos": [
[
456000,
0
],
[
456000,
456000
],
[
321000,
0
],
[
321000,
321000
],
null,
null,
[
567000,
0
],
[
567000,
567000
],
[
432000,
0
],
[
432000,
432000
],
null,
null
]
}
}
]
}
@@ -0,0 +1,44 @@
-- SELECT * FROM tbl
-- test all date/time-based postgres data types
CREATE TEMPORARY TABLE tbl (
ts timestamp,
tsnn timestamp NOT NULL,
tsz timestamp with time zone,
tsznn timestamp with time zone NOT NULL,
d date,
dnn date NOT NULL,
t time,
tnn time NOT NULL,
tz time with time zone,
tznn time with time zone NOT NULL,
i interval,
inn interval NOT NULL
);
INSERT INTO tbl (ts, tsnn, tsz, tsznn, d, dnn, t, tnn, tz, tznn, i, inn) VALUES (
'2023-11-15 05:06:07.123456',
'2023-11-15 05:06:08.123456',
'2021-07-22 13:22:33.654321 Europe/Berlin',
'2021-07-22 13:22:34.654321 Europe/Berlin',
'2023-12-20',
'2023-12-21',
'12:34:56.234567',
'12:34:57.234567',
'23:12:36.765432+1',
'23:12:37.765432+1',
'987654 microsecond',
'887654 microsecond'
), (
NULL,
'2023-11-15 05:06:09.123456',
NULL,
'2021-07-22 13:22:35.654321 Europe/Berlin',
NULL,
'2023-12-22',
NULL,
'12:34:58.234567',
NULL,
'23:12:38.765432+1',
NULL,
'787654 microsecond'
);
@@ -0,0 +1,188 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "typeVersion": [
// 0,
// 0
// ],
// "executedQueryString": "SELECT * FROM tbl"
// }
// Name:
// Dimensions: 12 Fields by 2 Rows
// +----------------+----------------+----------------+----------------+----------------+----------------+------------------+------------------+------------------+------------------+------------------+------------------+
// | Name: i16 | Name: i16nn | Name: i32 | Name: i32nn | Name: i64 | Name: i64nn | Name: n | Name: nnn | Name: f32 | Name: f32nn | Name: f64 | Name: f64nn |
// | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: |
// | Type: []*int16 | Type: []*int16 | Type: []*int32 | Type: []*int32 | Type: []*int64 | Type: []*int64 | Type: []*float64 | Type: []*float64 | Type: []*float64 | Type: []*float64 | Type: []*float64 | Type: []*float64 |
// +----------------+----------------+----------------+----------------+----------------+----------------+------------------+------------------+------------------+------------------+------------------+------------------+
// | 1 | 2 | 3 | 4 | 5 | 6 | 81.75 | 7065.25 | 30.75 | 14.625 | 21.5625 | 14.25 |
// | null | 22 | null | 44 | null | 66 | null | 169.75 | null | 77.125 | null | 215.8125 |
// +----------------+----------------+----------------+----------------+----------------+----------------+------------------+------------------+------------------+------------------+------------------+------------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"meta": {
"typeVersion": [
0,
0
],
"executedQueryString": "SELECT * FROM tbl"
},
"fields": [
{
"name": "i16",
"type": "number",
"typeInfo": {
"frame": "int16",
"nullable": true
}
},
{
"name": "i16nn",
"type": "number",
"typeInfo": {
"frame": "int16",
"nullable": true
}
},
{
"name": "i32",
"type": "number",
"typeInfo": {
"frame": "int32",
"nullable": true
}
},
{
"name": "i32nn",
"type": "number",
"typeInfo": {
"frame": "int32",
"nullable": true
}
},
{
"name": "i64",
"type": "number",
"typeInfo": {
"frame": "int64",
"nullable": true
}
},
{
"name": "i64nn",
"type": "number",
"typeInfo": {
"frame": "int64",
"nullable": true
}
},
{
"name": "n",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
}
},
{
"name": "nnn",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
}
},
{
"name": "f32",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
}
},
{
"name": "f32nn",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
}
},
{
"name": "f64",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
}
},
{
"name": "f64nn",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
}
}
]
},
"data": {
"values": [
[
1,
null
],
[
2,
22
],
[
3,
null
],
[
4,
44
],
[
5,
null
],
[
6,
66
],
[
81.75,
null
],
[
7065.25,
169.75
],
[
30.75,
null
],
[
14.625,
77.125
],
[
21.5625,
null
],
[
14.25,
215.8125
]
]
}
}
]
}
@@ -0,0 +1,20 @@
-- SELECT * FROM tbl
-- test all numeric postgres data types
CREATE TEMPORARY TABLE tbl (
i16 smallint,
i16nn smallint NOT NULL,
i32 integer,
i32nn integer NOT NULL,
i64 bigint,
i64nn bigint NOT NULL,
n numeric(7, 2),
nnn numeric(7,2) NOT NULL,
f32 real,
f32nn real NOT NULL,
f64 double precision,
f64nn double precision NOT NULL
);
INSERT INTO tbl (i16, i16nn, i32, i32nn, i64, i64nn, n, nnn, f32, f32nn, f64, f64nn) VALUES
(1, 2, 3, 4, 5, 6, 81.75, 7065.25, 30.75, 14.625, 21.5625, 14.25),
(NULL, 22, NULL, 44, NULL, 66, NULL, 169.75, NULL, 77.125, NULL, 215.8125);
@@ -0,0 +1,123 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "typeVersion": [
// 0,
// 0
// ],
// "executedQueryString": "SELECT * FROM tbl"
// }
// Name:
// Dimensions: 6 Fields by 3 Rows
// +-----------------+-----------------+-----------------+-----------------+---------------+---------------+
// | Name: m | Name: mnn | Name: bt | Name: btnn | Name: bl | Name: blnn |
// | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: |
// | Type: []*string | Type: []*string | Type: []*string | Type: []*string | Type: []*bool | Type: []*bool |
// +-----------------+-----------------+-----------------+-----------------+---------------+---------------+
// | $12.34 | $23.45 | ABCD | EFGH | true | true |
// | $12.34 | $23.45 | QRST | UVWX | false | false |
// | null | $34.56 | null | abcd | null | true |
// +-----------------+-----------------+-----------------+-----------------+---------------+---------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"meta": {
"typeVersion": [
0,
0
],
"executedQueryString": "SELECT * FROM tbl"
},
"fields": [
{
"name": "m",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
},
{
"name": "mnn",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
},
{
"name": "bt",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
},
{
"name": "btnn",
"type": "string",
"typeInfo": {
"frame": "string",
"nullable": true
}
},
{
"name": "bl",
"type": "boolean",
"typeInfo": {
"frame": "bool",
"nullable": true
}
},
{
"name": "blnn",
"type": "boolean",
"typeInfo": {
"frame": "bool",
"nullable": true
}
}
]
},
"data": {
"values": [
[
"$12.34",
"$12.34",
null
],
[
"$23.45",
"$23.45",
"$34.56"
],
[
"ABCD",
"QRST",
null
],
[
"EFGH",
"UVWX",
"abcd"
],
[
true,
false,
null
],
[
true,
false,
true
]
]
}
}
]
}
@@ -0,0 +1,15 @@
-- SELECT * FROM tbl
-- test the less used postgres data types
CREATE TEMPORARY TABLE tbl (
m money,
mnn money NOT NULL,
bt bytea,
btnn bytea NOT NULL,
bl boolean,
blnn boolean NOT NULL
);
INSERT INTO tbl (m, mnn, bt, btnn, bl, blnn) VALUES
(12.34, 23.45, '\x41424344'::bytea, '\x45464748'::bytea, TRUE, TRUE),
(12.34, 23.45, '\x51525354'::bytea, '\x55565758'::bytea, FALSE, FALSE),
(NULL, 34.56, NULL, '\x61626364'::bytea, NULL, TRUE);
@@ -0,0 +1,93 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "type": "timeseries-wide",
// "typeVersion": [
// 0,
// 0
// ],
// "executedQueryString": "SELECT * FROM tbl"
// }
// Name:
// Dimensions: 3 Fields by 5 Rows
// +-------------------------------+------------------+------------------+
// | Name: Time | Name: a | Name: b |
// | Labels: | Labels: | Labels: |
// | Type: []time.Time | Type: []*float64 | Type: []*float64 |
// +-------------------------------+------------------+------------------+
// | 2023-12-24 14:30:03 +0000 UTC | 10 | 110 |
// | 2023-12-24 14:31:03 +0000 UTC | 20 | 120 |
// | 2023-12-24 14:32:03 +0000 UTC | 30 | 130 |
// | 2023-12-24 14:33:03 +0000 UTC | 40 | 140 |
// | 2023-12-24 14:34:03 +0000 UTC | 50 | 150 |
// +-------------------------------+------------------+------------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"meta": {
"type": "timeseries-wide",
"typeVersion": [
0,
0
],
"executedQueryString": "SELECT * FROM tbl"
},
"fields": [
{
"name": "Time",
"type": "time",
"typeInfo": {
"frame": "time.Time"
}
},
{
"name": "a",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
}
},
{
"name": "b",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
}
}
]
},
"data": {
"values": [
[
1703428203000,
1703428263000,
1703428323000,
1703428383000,
1703428443000
],
[
10,
20,
30,
40,
50
],
[
110,
120,
130,
140,
150
]
]
}
}
]
}
@@ -0,0 +1,19 @@
-- SELECT * FROM tbl
-- there's special backward-compat code which handles a field named 'metric'
CREATE TEMPORARY TABLE tbl (
"time" timestamp with time zone,
v double precision,
metric text
);
INSERT INTO tbl ("time", v, metric) VALUES
('2023-12-24 14:30:03 UTC', 10, 'a'),
('2023-12-24 14:30:03 UTC', 110, 'b'),
('2023-12-24 14:31:03 UTC', 20, 'a'),
('2023-12-24 14:31:03 UTC', 120, 'b'),
('2023-12-24 14:32:03 UTC', 30, 'a'),
('2023-12-24 14:32:03 UTC', 130, 'b'),
('2023-12-24 14:33:03 UTC', 40, 'a'),
('2023-12-24 14:33:03 UTC', 140, 'b'),
('2023-12-24 14:34:03 UTC', 50, 'a'),
('2023-12-24 14:34:03 UTC', 150, 'b');
@@ -0,0 +1,245 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "type": "timeseries-wide",
// "typeVersion": [
// 0,
// 0
// ],
// "executedQueryString": "SELECT * FROM tbl"
// }
// Name:
// Dimensions: 11 Fields by 1 Rows
// +-------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+
// | Name: Time | Name: v1 | Name: v1nn | Name: v2 | Name: v2nn | Name: x1 | Name: x1nn | Name: x2 | Name: x2nn | Name: x3 | Name: x3nn |
// | Labels: | Labels: c1=one, c1nn=two, c2=three, c2nn=four, c3=five , c3nn=six | Labels: c1=one, c1nn=two, c2=three, c2nn=four, c3=five , c3nn=six | Labels: c1=one, c1nn=two, c2=three, c2nn=four, c3=five , c3nn=six | Labels: c1=one, c1nn=two, c2=three, c2nn=four, c3=five , c3nn=six | Labels: c1=one, c1nn=two, c2=three, c2nn=four, c3=five , c3nn=six | Labels: c1=one, c1nn=two, c2=three, c2nn=four, c3=five , c3nn=six | Labels: c1=one, c1nn=two, c2=three, c2nn=four, c3=five , c3nn=six | Labels: c1=one, c1nn=two, c2=three, c2nn=four, c3=five , c3nn=six | Labels: c1=one, c1nn=two, c2=three, c2nn=four, c3=five , c3nn=six | Labels: c1=one, c1nn=two, c2=three, c2nn=four, c3=five , c3nn=six |
// | Type: []time.Time | Type: []*float64 | Type: []*float64 | Type: []*float64 | Type: []*float64 | Type: []*float64 | Type: []*float64 | Type: []*float64 | Type: []*float64 | Type: []*float64 | Type: []*float64 |
// +-------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+
// | 2023-12-21 11:30:03 +0000 UTC | 10.1 | 11.1 | 12.1 | 13.1 | 101 | 102 | 103 | 104 | 105 | 106 |
// +-------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"meta": {
"type": "timeseries-wide",
"typeVersion": [
0,
0
],
"executedQueryString": "SELECT * FROM tbl"
},
"fields": [
{
"name": "Time",
"type": "time",
"typeInfo": {
"frame": "time.Time"
}
},
{
"name": "v1",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c1": "one",
"c1nn": "two",
"c2": "three",
"c2nn": "four",
"c3": "five ",
"c3nn": "six "
}
},
{
"name": "v1nn",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c1": "one",
"c1nn": "two",
"c2": "three",
"c2nn": "four",
"c3": "five ",
"c3nn": "six "
}
},
{
"name": "v2",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c1": "one",
"c1nn": "two",
"c2": "three",
"c2nn": "four",
"c3": "five ",
"c3nn": "six "
}
},
{
"name": "v2nn",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c1": "one",
"c1nn": "two",
"c2": "three",
"c2nn": "four",
"c3": "five ",
"c3nn": "six "
}
},
{
"name": "x1",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c1": "one",
"c1nn": "two",
"c2": "three",
"c2nn": "four",
"c3": "five ",
"c3nn": "six "
}
},
{
"name": "x1nn",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c1": "one",
"c1nn": "two",
"c2": "three",
"c2nn": "four",
"c3": "five ",
"c3nn": "six "
}
},
{
"name": "x2",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c1": "one",
"c1nn": "two",
"c2": "three",
"c2nn": "four",
"c3": "five ",
"c3nn": "six "
}
},
{
"name": "x2nn",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c1": "one",
"c1nn": "two",
"c2": "three",
"c2nn": "four",
"c3": "five ",
"c3nn": "six "
}
},
{
"name": "x3",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c1": "one",
"c1nn": "two",
"c2": "three",
"c2nn": "four",
"c3": "five ",
"c3nn": "six "
}
},
{
"name": "x3nn",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c1": "one",
"c1nn": "two",
"c2": "three",
"c2nn": "four",
"c3": "five ",
"c3nn": "six "
}
}
]
},
"data": {
"values": [
[
1703158203000
],
[
10.1
],
[
11.1
],
[
12.1
],
[
13.1
],
[
101
],
[
102
],
[
103
],
[
104
],
[
105
],
[
106
]
]
}
}
]
}
@@ -0,0 +1,30 @@
-- SELECT * FROM tbl
-- in timeseries mode, most fields gets converted to float6
CREATE TEMPORARY TABLE tbl (
"time" timestamp with time zone,
v1 double precision,
v1nn double precision NOT NULL,
v2 real,
v2nn real NOT NULL,
c1 text,
c1nn text NOT NULL,
c2 varchar(10),
c2nn varchar(10) NOT NULL,
c3 char(10),
c3nn char(10) NOT NULL,
x1 smallint,
x1nn smallint NOT NULL,
x2 integer,
x2nn integer NOT NULL,
x3 bigint,
x3nn bigint NOT NULL
);
INSERT INTO tbl ("time",
v1, v1nn, v2, v2nn,
c1, c1nn, c2, c2nn, c3, c3nn,
x1, x1nn, x2, x2nn, x3, x3nn) VALUES
('2023-12-21 11:30:03 UTC',
10.1, 11.1, 12.1, 13.1,
'one', 'two', 'three', 'four', 'five', 'six',
101, 102, 103, 104, 105, 106);
@@ -0,0 +1,107 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "type": "timeseries-wide",
// "typeVersion": [
// 0,
// 0
// ],
// "executedQueryString": "SELECT floor(extract(epoch from \"time\")/300)*300 AS \"time\",c,avg(v) AS \"v\" FROM tbl GROUP BY 1,2 ORDER BY 1,2"
// }
// Name:
// Dimensions: 3 Fields by 7 Rows
// +-------------------------------+------------------+------------------+
// | Name: Time | Name: v | Name: v |
// | Labels: | Labels: c=a | Labels: c=b |
// | Type: []time.Time | Type: []*float64 | Type: []*float64 |
// +-------------------------------+------------------+------------------+
// | 2023-12-24 15:15:00 +0100 CET | null | null |
// | 2023-12-24 15:20:00 +0100 CET | 15 | 115 |
// | 2023-12-24 15:25:00 +0100 CET | null | null |
// | 2023-12-24 15:30:00 +0100 CET | null | null |
// | 2023-12-24 15:35:00 +0100 CET | 50 | 150 |
// | 2023-12-24 15:40:00 +0100 CET | null | null |
// | 2023-12-24 15:45:00 +0100 CET | null | null |
// +-------------------------------+------------------+------------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"meta": {
"type": "timeseries-wide",
"typeVersion": [
0,
0
],
"executedQueryString": "SELECT floor(extract(epoch from \"time\")/300)*300 AS \"time\",c,avg(v) AS \"v\" FROM tbl GROUP BY 1,2 ORDER BY 1,2"
},
"fields": [
{
"name": "Time",
"type": "time",
"typeInfo": {
"frame": "time.Time"
}
},
{
"name": "v",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c": "a"
}
},
{
"name": "v",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c": "b"
}
}
]
},
"data": {
"values": [
[
1703427300000,
1703427600000,
1703427900000,
1703428200000,
1703428500000,
1703428800000,
1703429100000
],
[
null,
15,
null,
null,
50,
null,
null
],
[
null,
115,
null,
null,
150,
null,
null
]
]
}
}
]
}
@@ -0,0 +1,15 @@
-- SELECT $__timeGroup("time",5m,NULL),c,avg(v) AS "v" FROM tbl GROUP BY 1,2 ORDER BY 1,2
-- tests fill-mode=null
CREATE TEMPORARY TABLE tbl (
"time" timestamp with time zone,
v double precision,
c text
);
INSERT INTO tbl ("time", v, c) VALUES
('2023-12-24 14:21:03 UTC', 10, 'a'),
('2023-12-24 14:21:03 UTC', 110, 'b'),
('2023-12-24 14:23:03 UTC', 20, 'a'),
('2023-12-24 14:23:03 UTC', 120, 'b'),
('2023-12-24 14:39:03 UTC', 50, 'a'),
('2023-12-24 14:39:03 UTC', 150, 'b');
@@ -0,0 +1,107 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "type": "timeseries-wide",
// "typeVersion": [
// 0,
// 0
// ],
// "executedQueryString": "SELECT floor(extract(epoch from \"time\")/300)*300 AS \"time\",c,avg(v) AS \"v\" FROM tbl GROUP BY 1,2 ORDER BY 1,2"
// }
// Name:
// Dimensions: 3 Fields by 7 Rows
// +-------------------------------+------------------+------------------+
// | Name: Time | Name: v | Name: v |
// | Labels: | Labels: c=a | Labels: c=b |
// | Type: []time.Time | Type: []*float64 | Type: []*float64 |
// +-------------------------------+------------------+------------------+
// | 2023-12-24 15:15:00 +0100 CET | null | null |
// | 2023-12-24 15:20:00 +0100 CET | 15 | 115 |
// | 2023-12-24 15:25:00 +0100 CET | 15 | 115 |
// | 2023-12-24 15:30:00 +0100 CET | 15 | 115 |
// | 2023-12-24 15:35:00 +0100 CET | 50 | 150 |
// | 2023-12-24 15:40:00 +0100 CET | 50 | 150 |
// | 2023-12-24 15:45:00 +0100 CET | 50 | 150 |
// +-------------------------------+------------------+------------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"meta": {
"type": "timeseries-wide",
"typeVersion": [
0,
0
],
"executedQueryString": "SELECT floor(extract(epoch from \"time\")/300)*300 AS \"time\",c,avg(v) AS \"v\" FROM tbl GROUP BY 1,2 ORDER BY 1,2"
},
"fields": [
{
"name": "Time",
"type": "time",
"typeInfo": {
"frame": "time.Time"
}
},
{
"name": "v",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c": "a"
}
},
{
"name": "v",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c": "b"
}
}
]
},
"data": {
"values": [
[
1703427300000,
1703427600000,
1703427900000,
1703428200000,
1703428500000,
1703428800000,
1703429100000
],
[
null,
15,
15,
15,
50,
50,
50
],
[
null,
115,
115,
115,
150,
150,
150
]
]
}
}
]
}
@@ -0,0 +1,15 @@
-- SELECT $__timeGroup("time",5m,previous),c,avg(v) AS "v" FROM tbl GROUP BY 1,2 ORDER BY 1,2
-- tests fill-mode=previous
CREATE TEMPORARY TABLE tbl (
"time" timestamp with time zone,
v double precision,
c text
);
INSERT INTO tbl ("time", v, c) VALUES
('2023-12-24 14:21:03 UTC', 10, 'a'),
('2023-12-24 14:21:03 UTC', 110, 'b'),
('2023-12-24 14:23:03 UTC', 20, 'a'),
('2023-12-24 14:23:03 UTC', 120, 'b'),
('2023-12-24 14:39:03 UTC', 50, 'a'),
('2023-12-24 14:39:03 UTC', 150, 'b');
@@ -0,0 +1,107 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "type": "timeseries-wide",
// "typeVersion": [
// 0,
// 0
// ],
// "executedQueryString": "SELECT floor(extract(epoch from \"time\")/300)*300 AS \"time\",c,avg(v) AS \"v\" FROM tbl GROUP BY 1,2 ORDER BY 1,2"
// }
// Name:
// Dimensions: 3 Fields by 7 Rows
// +-------------------------------+------------------+------------------+
// | Name: Time | Name: v | Name: v |
// | Labels: | Labels: c=a | Labels: c=b |
// | Type: []time.Time | Type: []*float64 | Type: []*float64 |
// +-------------------------------+------------------+------------------+
// | 2023-12-24 15:15:00 +0100 CET | 27 | 27 |
// | 2023-12-24 15:20:00 +0100 CET | 15 | 115 |
// | 2023-12-24 15:25:00 +0100 CET | 27 | 27 |
// | 2023-12-24 15:30:00 +0100 CET | 27 | 27 |
// | 2023-12-24 15:35:00 +0100 CET | 50 | 150 |
// | 2023-12-24 15:40:00 +0100 CET | 27 | 27 |
// | 2023-12-24 15:45:00 +0100 CET | 27 | 27 |
// +-------------------------------+------------------+------------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"meta": {
"type": "timeseries-wide",
"typeVersion": [
0,
0
],
"executedQueryString": "SELECT floor(extract(epoch from \"time\")/300)*300 AS \"time\",c,avg(v) AS \"v\" FROM tbl GROUP BY 1,2 ORDER BY 1,2"
},
"fields": [
{
"name": "Time",
"type": "time",
"typeInfo": {
"frame": "time.Time"
}
},
{
"name": "v",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c": "a"
}
},
{
"name": "v",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c": "b"
}
}
]
},
"data": {
"values": [
[
1703427300000,
1703427600000,
1703427900000,
1703428200000,
1703428500000,
1703428800000,
1703429100000
],
[
27,
15,
27,
27,
50,
27,
27
],
[
27,
115,
27,
27,
150,
27,
27
]
]
}
}
]
}
@@ -0,0 +1,15 @@
-- SELECT $__timeGroup("time",5m,27),c,avg(v) AS "v" FROM tbl GROUP BY 1,2 ORDER BY 1,2
-- tests fill-mode=value
CREATE TEMPORARY TABLE tbl (
"time" timestamp with time zone,
v double precision,
c text
);
INSERT INTO tbl ("time", v, c) VALUES
('2023-12-24 14:21:03 UTC', 10, 'a'),
('2023-12-24 14:21:03 UTC', 110, 'b'),
('2023-12-24 14:23:03 UTC', 20, 'a'),
('2023-12-24 14:23:03 UTC', 120, 'b'),
('2023-12-24 14:39:03 UTC', 50, 'a'),
('2023-12-24 14:39:03 UTC', 150, 'b');
@@ -0,0 +1,99 @@
// 🌟 This was machine generated. Do not edit. 🌟
//
// Frame[0] {
// "type": "timeseries-wide",
// "typeVersion": [
// 0,
// 0
// ],
// "executedQueryString": "SELECT * FROM tbl"
// }
// Name:
// Dimensions: 3 Fields by 5 Rows
// +-------------------------------+------------------+------------------+
// | Name: Time | Name: v | Name: v |
// | Labels: | Labels: c=a | Labels: c=b |
// | Type: []time.Time | Type: []*float64 | Type: []*float64 |
// +-------------------------------+------------------+------------------+
// | 2023-12-24 14:30:03 +0000 UTC | 10 | 110 |
// | 2023-12-24 14:31:03 +0000 UTC | 20 | 120 |
// | 2023-12-24 14:32:03 +0000 UTC | 30 | 130 |
// | 2023-12-24 14:33:03 +0000 UTC | 40 | 140 |
// | 2023-12-24 14:34:03 +0000 UTC | 50 | 150 |
// +-------------------------------+------------------+------------------+
//
//
// 🌟 This was machine generated. Do not edit. 🌟
{
"status": 200,
"frames": [
{
"schema": {
"meta": {
"type": "timeseries-wide",
"typeVersion": [
0,
0
],
"executedQueryString": "SELECT * FROM tbl"
},
"fields": [
{
"name": "Time",
"type": "time",
"typeInfo": {
"frame": "time.Time"
}
},
{
"name": "v",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c": "a"
}
},
{
"name": "v",
"type": "number",
"typeInfo": {
"frame": "float64",
"nullable": true
},
"labels": {
"c": "b"
}
}
]
},
"data": {
"values": [
[
1703428203000,
1703428263000,
1703428323000,
1703428383000,
1703428443000
],
[
10,
20,
30,
40,
50
],
[
110,
120,
130,
140,
150
]
]
}
}
]
}
@@ -0,0 +1,18 @@
-- SELECT * FROM tbl
CREATE TEMPORARY TABLE tbl (
"time" timestamp with time zone,
v double precision,
c text
);
INSERT INTO tbl ("time", v, c) VALUES
('2023-12-24 14:30:03 UTC', 10, 'a'),
('2023-12-24 14:30:03 UTC', 110, 'b'),
('2023-12-24 14:31:03 UTC', 20, 'a'),
('2023-12-24 14:31:03 UTC', 120, 'b'),
('2023-12-24 14:32:03 UTC', 30, 'a'),
('2023-12-24 14:32:03 UTC', 130, 'b'),
('2023-12-24 14:33:03 UTC', 40, 'a'),
('2023-12-24 14:33:03 UTC', 140, 'b'),
('2023-12-24 14:34:03 UTC', 50, 'a'),
('2023-12-24 14:34:03 UTC', 150, 'b');
@@ -10,9 +10,11 @@ import { logInfo, LogMessages } from './Analytics';
import { GrafanaRulesExporter } from './components/export/GrafanaRulesExporter';
import { AlertingAction, useAlertingAbility } from './hooks/useAbilities';
interface Props {}
interface Props {
enableExport?: boolean;
}
export function MoreActionsRuleButtons({}: Props) {
export function MoreActionsRuleButtons({ enableExport }: Props) {
const [createRuleSupported, createRuleAllowed] = useAlertingAbility(AlertingAction.CreateAlertRule);
const [createCloudRuleSupported, createCloudRuleAllowed] = useAlertingAbility(AlertingAction.CreateExternalAlertRule);
const [exportRulesSupported, exportRulesAllowed] = useAlertingAbility(AlertingAction.ExportGrafanaManagedRules);
@@ -40,7 +42,13 @@ export function MoreActionsRuleButtons({}: Props) {
if (canExportRules) {
menuItems.push(
<MenuItem label="Export all Grafana-managed rules" key="export-all-rules" onClick={toggleShowExportDrawer} />
<MenuItem
label="Export all Grafana-managed rules"
key="export-all-rules"
onClick={toggleShowExportDrawer}
disabled={!enableExport}
description={enableExport ? '' : 'No Grafana-managed rules found'}
/>
);
}
@@ -24,7 +24,7 @@ import { useFilteredRules, useRulesFilter } from './hooks/useFilteredRules';
import { useUnifiedAlertingSelector } from './hooks/useUnifiedAlertingSelector';
import { fetchAllPromAndRulerRulesAction } from './state/actions';
import { RULE_LIST_POLL_INTERVAL_MS } from './utils/constants';
import { getAllRulesSourceNames } from './utils/datasource';
import { getAllRulesSourceNames, GRAFANA_RULES_SOURCE_NAME } from './utils/datasource';
const VIEWS = {
groups: RuleListGroupView,
@@ -77,6 +77,9 @@ const RuleList = withErrorBoundary(
return noRules && state.dispatched;
});
const grafanaRules = useUnifiedAlertingSelector((state) => state.rulerRules[GRAFANA_RULES_SOURCE_NAME]?.result);
const hasGrafanaRules = Object.keys(grafanaRules ?? {}).length > 0;
const limitAlerts = hasActiveFilters ? undefined : LIMIT_ALERTS;
// Trigger data refresh only when the RULE_LIST_POLL_INTERVAL_MS elapsed since the previous load FINISHED
const [_, fetchRules] = useAsyncFn(async () => {
@@ -126,7 +129,7 @@ const RuleList = withErrorBoundary(
<RuleStats namespaces={filteredNamespaces} />
</div>
<Stack direction="row" gap={0.5}>
<MoreActionsRuleButtons />
<MoreActionsRuleButtons enableExport={hasGrafanaRules} />
</Stack>
</div>
</>
@@ -0,0 +1,36 @@
import React from 'react';
import { PageLayoutType } from '@grafana/data';
import { SceneComponentProps, SceneObjectBase } from '@grafana/scenes';
import { Page } from 'app/core/components/Page/Page';
import { DashboardScene } from '../scene/DashboardScene';
import { getDashboardSceneFor } from '../utils/utils';
import { DashboardEditView, DashboardEditViewState, useDashboardEditPageNav } from './utils';
export interface VersionsEditViewState extends DashboardEditViewState {}
export class VersionsEditView extends SceneObjectBase<VersionsEditViewState> implements DashboardEditView {
public static Component = VersionsEditorSettingsListView;
public getUrlKey(): string {
return 'versions';
}
public getDashboard(): DashboardScene {
return getDashboardSceneFor(this);
}
}
function VersionsEditorSettingsListView({ model }: SceneComponentProps<VersionsEditView>) {
const dashboard = model.getDashboard();
const { navModel, pageNav } = useDashboardEditPageNav(dashboard, model.getUrlKey());
return (
<Page navModel={navModel} pageNav={pageNav} layout={PageLayoutType.Standard}>
<div>TODO</div>
</Page>
);
}
@@ -12,6 +12,7 @@ import { AnnotationsEditView } from './AnnotationsEditView';
import { DashboardLinksEditView } from './DashboardLinksEditView';
import { GeneralSettingsEditView } from './GeneralSettingsEditView';
import { VariablesEditView } from './VariablesEditView';
import { VersionsEditView } from './VersionsEditView';
export interface DashboardEditViewState extends SceneObjectState {}
@@ -54,6 +55,11 @@ export function useDashboardEditPageNav(dashboard: DashboardScene, currentEditVi
url: locationUtil.getUrlForPartial(location, { editview: 'links', editIndex: null }),
active: currentEditView === 'links',
},
{
text: t('dashboard-settings.versions.title', 'Versions'),
url: locationUtil.getUrlForPartial(location, { editview: 'versions', editIndex: null }),
active: currentEditView === 'versions',
},
],
parentItem: dashboardPageNav,
};
@@ -69,6 +75,8 @@ export function createDashboardEditViewFor(editview: string): DashboardEditView
return new VariablesEditView({});
case 'links':
return new DashboardLinksEditView({});
case 'versions':
return new VersionsEditView({});
case 'settings':
default:
return new GeneralSettingsEditView({});
@@ -251,7 +251,7 @@ export const DashNav = React.memo<Props>((props) => {
};
const renderRightActions = () => {
const { dashboard, onAddPanel, isFullscreen, kioskMode } = props;
const { dashboard, onAddPanel, isFullscreen, kioskMode, hideTimePicker } = props;
const { canSave, canEdit, showSettings, canShare } = dashboard.meta;
const { snapshot } = dashboard;
const snapshotUrl = snapshot && snapshot.originalUrl;
@@ -335,7 +335,10 @@ export const DashNav = React.memo<Props>((props) => {
buttons.push(<ShareButton key="button-share" dashboard={dashboard} />);
}
buttons.push(<NavToolbarSeparator key="toolbar-separator" />);
// if the timepicker is hidden, we don't need to add this separator
if (!hideTimePicker) {
buttons.push(<NavToolbarSeparator key="toolbar-separator" />);
}
buttons.push(renderTimeControls());
@@ -1,4 +1,4 @@
import { css, CSSObject } from '@emotion/css';
import { css } from '@emotion/css';
import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
@@ -25,12 +25,11 @@ export function OptionsPaneItemOverrides({ overrides }: Props) {
}
const getStyles = (theme: GrafanaTheme2) => {
const common: CSSObject = {
const common = {
width: 8,
height: 8,
borderRadius: theme.shape.radius.circle,
marginLeft: theme.spacing(1),
position: 'relative',
top: '-1px',
};
@@ -40,10 +39,12 @@ const getStyles = (theme: GrafanaTheme2) => {
}),
rule: css({
...common,
position: 'relative',
backgroundColor: theme.colors.primary.main,
}),
data: css({
...common,
position: 'relative',
backgroundColor: theme.colors.warning.main,
}),
};
@@ -3,6 +3,7 @@ import userEvent from '@testing-library/user-event';
import React, { ComponentProps } from 'react';
import { LogsSortOrder } from '@grafana/data';
import { DataQuery } from '@grafana/schema';
import LogsNavigation from './LogsNavigation';
@@ -93,6 +94,42 @@ describe('LogsNavigation', () => {
expect(onChangeTimeMock).toHaveBeenCalledWith({ from: 1637319338000, to: 1637322938000 });
});
it('should correctly display the active page', async () => {
const queries: DataQuery[] = [];
const { rerender } = setup({
absoluteRange: { from: 1704737384139, to: 1704737684139 },
visibleRange: { from: 1704737384207, to: 1704737683316 },
queries,
logsSortOrder: LogsSortOrder.Descending,
});
expect(await screen.findByTestId('page1')).toBeInTheDocument();
expect(screen.getByTestId('page1').firstChild).toHaveClass('selectedBg');
expect(screen.queryByTestId('page2')).not.toBeInTheDocument();
await userEvent.click(screen.getByTestId('olderLogsButton'));
rerender(
<LogsNavigation
{...defaultProps}
absoluteRange={{ from: 1704737084207, to: 1704737384207 }}
visibleRange={{ from: 1704737084627, to: 1704737383765 }}
onChangeTime={jest.fn()}
logsSortOrder={LogsSortOrder.Descending}
queries={queries}
/>
);
expect(await screen.findByTestId('page1')).toBeInTheDocument();
expect(screen.getByTestId('page1').firstChild).not.toHaveClass('selectedBg');
expect(await screen.findByTestId('page2')).toBeInTheDocument();
expect(screen.getByTestId('page2').firstChild).toHaveClass('selectedBg');
expect(screen.queryByTestId('page3')).not.toBeInTheDocument();
});
it('should reset the scroll when pagination is clicked', async () => {
const scrollToTopLogsMock = jest.fn();
setup({ scrollToTopLogs: scrollToTopLogsMock });
@@ -1,6 +1,6 @@
import { css } from '@emotion/css';
import { isEqual } from 'lodash';
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { AbsoluteTimeRange, GrafanaTheme2, LogsSortOrder } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
@@ -41,7 +41,6 @@ function LogsNavigation({
addResultsToCache,
}: Props) {
const [pages, setPages] = useState<LogsPage[]>([]);
const [currentPageIndex, setCurrentPageIndex] = useState(0);
// These refs are to determine, if we want to clear up logs navigation when totally new query is run
const expectedQueriesRef = useRef<DataQuery[]>();
@@ -50,6 +49,14 @@ function LogsNavigation({
// e.g. if last 5 min selected, always run 5 min range
const rangeSpanRef = useRef(0);
const currentPageIndex = useMemo(
() =>
pages.findIndex((page) => {
return page.queryRange.to === absoluteRange.to;
}),
[absoluteRange.to, pages]
);
const oldestLogsFirst = logsSortOrder === LogsSortOrder.Ascending;
const onFirstPage = oldestLogsFirst ? currentPageIndex === pages.length - 1 : currentPageIndex === 0;
const onLastPage = oldestLogsFirst ? currentPageIndex === 0 : currentPageIndex === pages.length - 1;
@@ -64,7 +71,6 @@ function LogsNavigation({
if (!isEqual(expectedRangeRef.current, absoluteRange) || !isEqual(expectedQueriesRef.current, queries)) {
clearCache();
setPages([newPage]);
setCurrentPageIndex(0);
expectedQueriesRef.current = queries;
rangeSpanRef.current = absoluteRange.to - absoluteRange.from;
} else {
@@ -73,14 +79,8 @@ function LogsNavigation({
newPages = pages.filter((page) => !isEqual(newPage.queryRange, page.queryRange));
// Sort pages based on logsOrder so they visually align with displayed logs
newPages = [...newPages, newPage].sort((a, b) => sortPages(a, b, logsSortOrder));
// Set new pages
return newPages;
});
// Set current page index
const index = newPages.findIndex((page) => page.queryRange.to === absoluteRange.to);
setCurrentPageIndex(index);
}
addResultsToCache();
}, [visibleRange, absoluteRange, logsSortOrder, queries, clearCache, addResultsToCache]);
@@ -292,6 +292,117 @@ describe('LogRowContextModal', () => {
});
});
it('should highlight the same `foo123` searchwords', async () => {
const dfBeforeNs = createDataFrame({
fields: [
{
name: 'time',
type: FieldType.time,
values: [1, 1],
},
{
name: 'message',
type: FieldType.string,
values: ['this contains foo123', 'this contains foo123'],
},
{
name: 'tsNs',
type: FieldType.string,
values: ['1', '2'],
},
],
});
const dfNowNs = createDataFrame({
fields: [
{
name: 'time',
type: FieldType.time,
values: [1],
},
{
name: 'message',
type: FieldType.string,
values: ['this contains foo123'],
},
{
name: 'tsNs',
type: FieldType.string,
values: ['2'],
},
],
});
const dfAfterNs = createDataFrame({
fields: [
{
name: 'time',
type: FieldType.time,
values: [1, 1],
},
{
name: 'message',
type: FieldType.string,
values: ['this contains foo123', 'this contains foo123'],
},
{
name: 'tsNs',
type: FieldType.string,
values: ['2', '3'],
},
],
});
let uniqueRefIdCounter = 1;
const logs = dataFrameToLogsModel([dfNowNs]);
const row = logs.rows[0];
row.searchWords = ['foo123'];
const getRowContext = jest.fn().mockImplementation(async (_, options) => {
uniqueRefIdCounter += 1;
const refId = `refid_${uniqueRefIdCounter}`;
if (uniqueRefIdCounter === 2) {
return {
data: [
{
refId,
...dfBeforeNs,
},
],
};
} else if (uniqueRefIdCounter === 3) {
return {
data: [
{
refId,
...dfAfterNs,
},
],
};
}
return { data: [] };
});
render(
<LogRowContextModal
row={row}
open={true}
onClose={() => {}}
getRowContext={getRowContext}
timeZone={timeZone}
logsSortOrder={LogsSortOrder.Descending}
/>
);
// there need to be 3 lines with that message, all `foo123` should be highlighted
await waitFor(() => {
expect(screen.getAllByText('foo123').length).toBe(3);
for (const el of screen.getAllByText('foo123')) {
// highlights are done in `<mark>` tags
expect(el.tagName).toBe('MARK');
}
// test that the rest is not in a MARK
expect(screen.getAllByText('this contains')[0].tagName).not.toBe('MARK');
});
});
it('should show a split view button', async () => {
const getRowContextQuery = jest.fn().mockResolvedValue({ datasource: { uid: 'test-uid' } });
@@ -363,7 +363,10 @@ export const LogRowContextModal: React.FunctionComponent<LogRowContextModalProps
// this way this array of rows will never be empty
const allRows = [...above.rows, row, ...below.rows];
const newRows = await loadMore(place, allRows);
const newRows = (await loadMore(place, allRows)).map((r) =>
// apply the original row's searchWords to all the rows for highlighting
!r.searchWords || !r.searchWords?.length ? { ...r, searchWords: row.searchWords } : r
);
const [older, newer] = partition(newRows, (newRow) => newRow.timeEpochNs > row.timeEpochNs);
const newAbove = logsSortOrder === LogsSortOrder.Ascending ? newer : older;
const newBelow = logsSortOrder === LogsSortOrder.Ascending ? older : newer;
@@ -69,7 +69,7 @@ export class TemplateSrv implements BaseTemplateSrv {
*
* Use getVariables function instead
*/
get variables(): any[] {
get variables(): TypedVariableModel[] {
deprecationWarning('template_srv.ts', 'variables', 'getVariables');
return this.getVariables();
}
@@ -84,7 +84,7 @@ export class TemplateSrv implements BaseTemplateSrv {
}
updateIndex() {
const existsOrEmpty = (value: any) => value || value === '';
const existsOrEmpty = (value: unknown) => value || value === '';
this.index = this._variables.reduce((acc, currentValue) => {
if (currentValue.current && (currentValue.current.isNone || existsOrEmpty(currentValue.current.value))) {
@@ -360,7 +360,7 @@ export class TemplateSrv implements BaseTemplateSrv {
});
}
isAllValue(value: any) {
isAllValue(value: unknown) {
return value === ALL_VARIABLE_VALUE || (Array.isArray(value) && value[0] === ALL_VARIABLE_VALUE);
}
@@ -0,0 +1,82 @@
import { css } from '@emotion/css';
import React from 'react';
import { useToggle } from 'react-use';
import { GrafanaTheme2 } from '@grafana/data';
import { Collapse, useStyles2, Stack } from '@grafana/ui';
export interface Props {
title: string;
collapsedInfo: string[];
children: React.ReactNode;
}
export function QueryOptionGroup({ title, children, collapsedInfo }: Props) {
const [isOpen, toggleOpen] = useToggle(false);
const styles = useStyles2(getStyles);
return (
<div className={styles.wrapper}>
<Collapse
className={styles.collapse}
collapsible
isOpen={isOpen}
onToggle={toggleOpen}
label={
<Stack gap={0}>
<h6 className={styles.title}>{title}</h6>
{!isOpen && (
<div className={styles.description}>
{collapsedInfo.map((x, i) => (
<span key={i}>{x}</span>
))}
</div>
)}
</Stack>
}
>
<div className={styles.body}>{children}</div>
</Collapse>
</div>
);
}
const getStyles = (theme: GrafanaTheme2) => {
return {
collapse: css({
backgroundColor: 'unset',
border: 'unset',
marginBottom: 0,
['> button']: {
padding: theme.spacing(0, 1),
},
}),
wrapper: css({
width: '100%',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'baseline',
}),
title: css({
flexGrow: 1,
overflow: 'hidden',
fontSize: theme.typography.bodySmall.fontSize,
fontWeight: theme.typography.fontWeightMedium,
margin: 0,
}),
description: css({
color: theme.colors.text.secondary,
fontSize: theme.typography.bodySmall.fontSize,
fontWeight: theme.typography.bodySmall.fontWeight,
paddingLeft: theme.spacing(2),
gap: theme.spacing(2),
display: 'flex',
}),
body: css({
display: 'flex',
gap: theme.spacing(2),
flexWrap: 'wrap',
}),
};
};
@@ -5,10 +5,10 @@ import { CoreApp, GrafanaTheme2, SelectableValue } from '@grafana/data';
import { config } from '@grafana/runtime';
import { useStyles2, RadioButtonGroup, MultiSelect, Input } from '@grafana/ui';
import { QueryOptionGroup } from '../../prometheus/querybuilder/shared/QueryOptionGroup';
import { Query } from '../types';
import { EditorField } from './EditorField';
import { QueryOptionGroup } from './QueryOptionGroup';
import { Stack } from './Stack';
export interface Props {
@@ -2,8 +2,6 @@ import { from, map, Observable, of } from 'rxjs';
import { CustomVariableSupport, DataQueryRequest, DataQueryResponse, MetricFindValue } from '@grafana/data';
import { getTimeSrv, TimeSrv } from '../../../features/dashboard/services/TimeSrv';
import { VariableQueryEditor } from './VariableQueryEditor';
import { PyroscopeDataSource } from './datasource';
import { ProfileTypeMessage, VariableQuery } from './types';
@@ -15,10 +13,7 @@ export interface DataAPI {
}
export class VariableSupport extends CustomVariableSupport<PyroscopeDataSource> {
constructor(
private readonly dataAPI: DataAPI,
private readonly timeSrv: TimeSrv = getTimeSrv()
) {
constructor(private readonly dataAPI: DataAPI) {
super();
}
@@ -26,9 +21,7 @@ export class VariableSupport extends CustomVariableSupport<PyroscopeDataSource>
query(request: DataQueryRequest<VariableQuery>): Observable<DataQueryResponse> {
if (request.targets[0].type === 'profileType') {
return from(
this.dataAPI.getProfileTypes(this.timeSrv.timeRange().from.valueOf(), this.timeSrv.timeRange().to.valueOf())
).pipe(
return from(this.dataAPI.getProfileTypes(request.range.from.valueOf(), request.range.to.valueOf())).pipe(
map((values) => {
return { data: values.map<MetricFindValue>((v) => ({ text: v.label, value: v.id })) };
})
@@ -42,8 +35,8 @@ export class VariableSupport extends CustomVariableSupport<PyroscopeDataSource>
return from(
this.dataAPI.getLabelNames(
request.targets[0].profileTypeId + '{}',
this.timeSrv.timeRange().from.valueOf(),
this.timeSrv.timeRange().to.valueOf()
request.range.from.valueOf(),
request.range.to.valueOf()
)
).pipe(
map((values) => {
@@ -60,8 +53,8 @@ export class VariableSupport extends CustomVariableSupport<PyroscopeDataSource>
this.dataAPI.getLabelValues(
request.targets[0].profileTypeId + '{}',
request.targets[0].labelName,
this.timeSrv.timeRange().from.valueOf(),
this.timeSrv.timeRange().to.valueOf()
request.range.from.valueOf(),
request.range.to.valueOf()
)
).pipe(
map((values) => {
@@ -409,12 +409,12 @@ function setup(value: string, offset: number, tagsV1?: string[], tagsV2?: Scope[
EnumMember: 2,
},
},
} as any;
} as unknown as typeof monacoTypes;
provider.editor = {
getModel() {
return model;
},
} as any;
} as unknown as monacoTypes.editor.IStandaloneCodeEditor;
return { provider, model } as unknown as { provider: CompletionProvider; model: monacoTypes.editor.ITextModel };
}
@@ -183,7 +183,7 @@ type OptionsState = {
// Exported for tests
export function useLoadOptions(datasource: ZipkinDatasource) {
const isMounted = useMountedState();
const [allOptions, setAllOptions] = useState({} as OptionsState);
const [allOptions, setAllOptions] = useState<OptionsState>({});
const [, fetchSpans] = useAsyncFn(
async function findSpans(service: string): Promise<void> {
@@ -61,7 +61,7 @@ export class ZipkinDatasource extends DataSourceApi<ZipkinQuery, ZipkinJsonData>
return of(emptyDataQueryResponse);
}
async metadataRequest(url: string, params?: Record<string, any>): Promise<any> {
async metadataRequest(url: string, params?: Record<string, unknown>) {
const res = await lastValueFrom(this.request(url, params, { hideFromInspector: true }));
return res.data;
}
@@ -100,7 +100,7 @@ export class ZipkinDatasource extends DataSourceApi<ZipkinQuery, ZipkinJsonData>
private request<T = any>(
apiUrl: string,
data?: any,
data?: unknown,
options?: Partial<BackendSrvRequest>
): Observable<FetchResponse<T>> {
const params = data ? urlUtil.serializeParams(data) : '';
@@ -16,7 +16,7 @@ export const alertListPanelMigrationHandler = (
tags: panel.options.tags ?? panel.dashboardTags ?? [],
stateFilter:
panel.options.stateFilter ??
panel.stateFilter?.reduce((filterObj: any, curFilter: any) => ({ ...filterObj, [curFilter]: true }), {}) ??
panel.stateFilter?.reduce((filterObj: object, curFilter: any) => ({ ...filterObj, [curFilter]: true }), {}) ??
{},
};
@@ -30,7 +30,7 @@ const defaultOptions: Options = {
tags: ['tag A', 'tag B'],
};
const defaultResult: any = {
const defaultResult = {
text: 'Result text',
userId: 1,
login: 'Result login',
@@ -40,7 +40,7 @@ const defaultResult: any = {
time: Date.UTC(2021, 0, 1, 0, 0, 0, 0),
panelId: 13,
dashboardId: 14, // deliberately different from panelId
id: 14,
id: '14',
uid: '7MeksYbmk',
dashboardUID: '7MeksYbmk',
url: '/d/asdkjhajksd/some-dash',
@@ -55,7 +55,7 @@ async function setupTestContext({
const getMock = jest.spyOn(backendSrv, 'get');
getMock.mockResolvedValue(results);
const dash: any = { uid: 'srx16xR4z', formatDate: (time: number) => new Date(time).toISOString() };
const dash = { uid: 'srx16xR4z', formatDate: (time: number) => new Date(time).toISOString() };
const dashSrv: any = { getCurrent: () => dash };
setDashboardSrv(dashSrv);
const pushSpy = jest.spyOn(locationService, 'push');
@@ -90,7 +90,11 @@ export class AnnoListPanel extends PureComponent<Props, State> {
const { options } = this.props;
const { queryUser, queryTags } = this.state;
const params: any = {
const params: {
tags: typeof options.tags;
limit: typeof options.limit;
type: string;
} & Record<string, unknown> = {
tags: options.tags,
limit: options.limit,
type: 'annotation', // Skip the Annotations that are really alerts. (Use the alerts panel!)
@@ -139,15 +143,12 @@ export class AnnoListPanel extends PureComponent<Props, State> {
const dashboardSrv = getDashboardSrv();
const current = dashboardSrv.getCurrent();
const params: any = {
const params = {
from: this._timeOffset(anno.time, options.navigateBefore, true),
to: this._timeOffset(anno.timeEnd ?? anno.time, options.navigateAfter, false),
viewPanel: options.navigateToPanel ? anno.panelId : undefined,
};
if (options.navigateToPanel) {
params.viewPanel = anno.panelId;
}
if (current?.uid === anno.dashboardUID) {
locationService.partial(params);
return;
@@ -157,8 +158,8 @@ export class AnnoListPanel extends PureComponent<Props, State> {
if (result && result.length && result[0].uid === anno.dashboardUID) {
const dash = result[0];
const url = new URL(dash.url, window.location.origin);
url.searchParams.set('from', params.from);
url.searchParams.set('to', params.to);
url.searchParams.set('from', String(params.from));
url.searchParams.set('to', String(params.to));
locationService.push(locationUtil.stripBaseFromUrl(url.toString()));
return;
}
+2 -2
View File
@@ -95,9 +95,9 @@ export const plugin = new PanelPlugin<Options>(AnnoListPanel)
});
})
// TODO, we should support this directly in the plugin infrastructure
.setPanelChangeHandler((panel: PanelModel<Options>, prevPluginId: string, prevOptions: unknown) => {
.setPanelChangeHandler((panel: PanelModel<Options>, prevPluginId, prevOptions) => {
if (prevPluginId === 'ryantxu-annolist-panel') {
return prevOptions as Options;
return prevOptions;
}
return panel.options;
@@ -19,7 +19,7 @@ describe('dashlist migrations', () => {
const basePanelModel = wellFormedPanelModel({});
basePanelModel.pluginVersion = '5.1';
const angularPanel: PanelModel<any> & AngularModel = {
const angularPanel: PanelModel & AngularModel = {
...basePanelModel,
// pluginVersion: '5.1',
starred: true,
@@ -52,7 +52,7 @@ export class EventBusLoggerPanel extends PureComponent<Props, State> {
eventObserver: PartialObserver<BusEvent> = {
next: (event: BusEvent) => {
const origin = event.origin as any;
const origin: any = event.origin;
this.history.add({
key: counter++,
type: event.type,
@@ -154,6 +154,6 @@ export async function initLayer(
return state;
}
export const getMapLayerState = (l: any) => {
return l?.__state as MapLayerState;
export const getMapLayerState = (l: any): MapLayerState => {
return l?.__state;
};
@@ -62,7 +62,7 @@ export const pointerMoveListener = (evt: MapBrowserEvent<MouseEvent>, panel: Geo
panel.map.forEachFeatureAtPixel(
pixel,
(feature, layer, geo) => {
const s: MapLayerState = getMapLayerState(layer);
const s = getMapLayerState(layer);
//match hover layer to layer in layers
//check if the layer show tooltip is enabled
//then also pass the list of tooltip fields if exists
@@ -287,7 +287,9 @@ export class Histogram extends React.Component<HistogramProps, State> {
}
prepState(props: HistogramProps, withConfig = true) {
let state: State = null as any;
let state: State = {
alignedData: [],
};
const { alignedFrame } = props;
if (alignedFrame) {
@@ -184,10 +184,10 @@ function inBounds(value: number, min: number | undefined, max: number | undefine
}
// The issue here is that TouchEvent is undefined while using instanceof in Firefox and Safari
// which will throw an exception but if it's (event as TouchEvent).changedTouches it will be undefined
// which will throw an exception but if it's event.changedTouches it will be undefined
// and the if check will fail so it will go to the else but will not throw an exception
function getEventXY(event: Event): { x: number; y: number } {
if ((event as TouchEvent).changedTouches && event instanceof TouchEvent) {
if ('changedTouches' in event && event instanceof TouchEvent) {
return { x: event.changedTouches[0].clientX, y: event.changedTouches[0].clientY };
} else if (event instanceof MouseEvent) {
return { x: event.clientX, y: event.clientY };
@@ -275,6 +275,7 @@ function makeNodeDatum(options: Partial<NodeDatum> = {}) {
config: {
color: {
fixedColor: 'green',
mode: 'fixed',
},
},
name: 'arc__success',
@@ -285,6 +286,7 @@ function makeNodeDatum(options: Partial<NodeDatum> = {}) {
config: {
color: {
fixedColor: 'red',
mode: 'fixed',
},
},
name: 'arc__errors',
+9 -9
View File
@@ -370,7 +370,7 @@ function makeNode(index: number) {
}
function nodesFrame() {
const fields: any = {
const fields = {
[NodeGraphDataFrameFieldNames.id]: {
values: [],
type: FieldType.string,
@@ -394,17 +394,17 @@ function nodesFrame() {
[NodeGraphDataFrameFieldNames.arc + 'success']: {
values: [],
type: FieldType.number,
config: { color: { fixedColor: 'green' } },
config: { color: { mode: FieldColorModeId.Fixed, fixedColor: 'green' } },
},
[NodeGraphDataFrameFieldNames.arc + 'errors']: {
values: [],
type: FieldType.number,
config: { color: { fixedColor: 'red' } },
config: { color: { mode: FieldColorModeId.Fixed, fixedColor: 'red' } },
},
[NodeGraphDataFrameFieldNames.color]: {
values: [],
type: FieldType.number,
config: { color: { mode: 'continuous-GrYlRd' } },
config: { color: { mode: FieldColorModeId.ContinuousGrYlRd } },
},
[NodeGraphDataFrameFieldNames.icon]: {
values: [],
@@ -418,8 +418,8 @@ function nodesFrame() {
return new MutableDataFrame({
name: 'nodes',
fields: Object.keys(fields).map((key) => ({
...fields[key],
fields: Object.entries(fields).map(([key, value]) => ({
...value,
name: key,
})),
});
@@ -440,7 +440,7 @@ export function makeEdgesDataFrame(
}
function edgesFrame() {
const fields: any = {
const fields = {
[NodeGraphDataFrameFieldNames.id]: {
values: [],
type: FieldType.string,
@@ -465,8 +465,8 @@ function edgesFrame() {
return new MutableDataFrame({
name: 'edges',
fields: Object.keys(fields).map((key) => ({
...fields[key],
fields: Object.entries(fields).map(([key, value]) => ({
...value,
name: key,
})),
});
@@ -6,7 +6,7 @@ import { Options } from './panelcfg.gen';
// This is called when the panel changes from another panel
export const statPanelChangedHandler = (
panel: PanelModel<Partial<Options>> | any,
panel: PanelModel<Partial<Options>>,
prevPluginId: string,
prevOptions: any
) => {
+9 -8
View File
@@ -1,7 +1,9 @@
import { IScope, IAngularStatic } from 'angular';
import $ from 'jquery';
import { defaults } from 'lodash';
import { isTableData, PanelEvents, PanelPlugin } from '@grafana/data';
import { AnnotationsSrv } from 'app/angular/services/annotations_srv';
import config from 'app/core/config';
import { applyFilterFromTable } from 'app/features/variables/adhoc/actions';
import { MetricsPanelCtrl } from 'app/plugins/sdk';
@@ -56,9 +58,9 @@ export class TablePanelCtrl extends MetricsPanelCtrl {
static $inject = ['$scope', '$injector', 'annotationsSrv', '$sanitize'];
constructor(
$scope: any,
$injector: any,
private annotationsSrv: any,
$scope: IScope,
$injector: IAngularStatic['injector'],
private annotationsSrv: AnnotationsSrv,
private $sanitize: any
) {
super($scope, $injector);
@@ -101,12 +103,11 @@ export class TablePanelCtrl extends MetricsPanelCtrl {
panel: this.panel,
range: this.range,
})
.then((anno: any) => {
.then((anno) => {
this.loading = false;
this.dataRaw = anno;
this.pageIndex = 0;
this.render();
return { data: this.dataRaw }; // Not used
});
}
@@ -170,7 +171,7 @@ export class TablePanelCtrl extends MetricsPanelCtrl {
this.render();
}
link(scope: any, elem: JQuery, attrs: any, ctrl: TablePanelCtrl) {
link(scope: IScope, elem: JQuery, attrs: any, ctrl: TablePanelCtrl) {
let data: any;
const panel = ctrl.panel;
let pageCount = 0;
@@ -191,7 +192,7 @@ export class TablePanelCtrl extends MetricsPanelCtrl {
tbodyElem.html(ctrl.renderer.render(ctrl.pageIndex));
}
function switchPage(e: any) {
function switchPage(e: JQueryEventObject) {
const el = $(e.currentTarget);
ctrl.pageIndex = parseInt(el.text(), 10) - 1;
renderPanel();
@@ -263,7 +264,7 @@ export class TablePanelCtrl extends MetricsPanelCtrl {
unbindDestroy();
});
ctrl.events.on(PanelEvents.render, (renderData: any) => {
ctrl.events.on(PanelEvents.render, (renderData: unknown) => {
data = renderData || data;
if (data) {
renderPanel();
@@ -399,7 +399,7 @@ export function graphToTimeseriesOptions(angular: any): {
// timeRegions migration
if (angular.timeRegions?.length) {
let regions: any[] = angular.timeRegions.map((old: GraphTimeRegionConfig, idx: number) => ({
let regions = angular.timeRegions.map((old: GraphTimeRegionConfig, idx: number) => ({
name: `T${idx + 1}`,
color: old.colorMode !== 'custom' ? old.colorMode : old.fillColor,
line: old.line,
@@ -684,7 +684,7 @@ function validNumber(val: unknown): number | undefined {
return undefined;
}
function getReducersFromLegend(obj: Record<string, any>): string[] {
function getReducersFromLegend(obj: Record<string, unknown>): string[] {
const ids: string[] = [];
for (const key of Object.keys(obj)) {
const r = fieldReducers.getIfExists(key);
@@ -45,7 +45,7 @@ export const AutoEditor = ({ value, onChange, context }: StandardEditorProps<XYD
};
const frame = context.data ? context.data[value?.frame ?? 0] : undefined;
if (frame) {
const xName = dims.x ? getFieldDisplayName(dims.x, dims.frame, context.data) : undefined;
const xName = 'x' in dims ? getFieldDisplayName(dims.x, dims.frame, context.data) : undefined;
for (let field of frame.fields) {
if (isGraphable(field)) {
const name = getFieldDisplayName(field, frame, context.data);
+8 -5
View File
@@ -15,18 +15,21 @@ export interface XYDimensions {
frame: DataFrame; // matches order from configs, excluds non-graphable values
x: Field;
fields: XYFieldMatchers;
error?: DimensionError;
hasData?: boolean;
hasTime?: boolean;
}
export interface XYDimensionsError {
error: DimensionError;
}
export function isGraphable(field: Field) {
return field.type === FieldType.number;
}
export function getXYDimensions(cfg?: XYDimensionConfig, data?: DataFrame[]): XYDimensions {
export function getXYDimensions(cfg?: XYDimensionConfig, data?: DataFrame[]): XYDimensions | XYDimensionsError {
if (!data || !data.length) {
return { error: DimensionError.NoData } as XYDimensions;
return { error: DimensionError.NoData };
}
if (!cfg) {
cfg = {
@@ -36,7 +39,7 @@ export function getXYDimensions(cfg?: XYDimensionConfig, data?: DataFrame[]): XY
let frame = data[cfg.frame ?? 0];
if (!frame) {
return { error: DimensionError.BadFrameSelection } as XYDimensions;
return { error: DimensionError.BadFrameSelection };
}
let xIndex = -1;
@@ -99,5 +102,5 @@ function getSimpleFieldNotMatcher(f: Field): FieldMatcher {
return () => false;
}
const m = getSimpleFieldMatcher(f);
return (field) => !m(field, undefined as any, undefined as any);
return (field) => !m(field, { fields: [], length: 0 }, []);
}
+1 -1
View File
@@ -101,7 +101,7 @@ export function ControllerTestContext(this: any) {
});
};
this.setIsUtc = (isUtc: any = false) => {
this.setIsUtc = (isUtc = false) => {
self.isUtc = isUtc;
};
}
+31 -879
View File
File diff suppressed because it is too large Load Diff