diff --git a/.betterer.results b/.betterer.results index a07a231954f..b593c39ca68 100644 --- a/.betterer.results +++ b/.betterer.results @@ -491,6 +491,9 @@ exports[`better eslint`] = { "packages/grafana-runtime/src/services/pluginExtensions/usePluginExtensions.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "packages/grafana-runtime/src/services/pluginExtensions/usePluginFunctions.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] + ], "packages/grafana-runtime/src/utils/DataSourceWithBackend.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -515,46 +518,15 @@ exports[`better eslint`] = { "packages/grafana-sql/src/components/visual-query-builder/AwesomeQueryBuilder.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "packages/grafana-ui/src/components/Alert/Alert.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] - ], - "packages/grafana-ui/src/components/Card/Card.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], - "packages/grafana-ui/src/components/Cascader/Cascader.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], "packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], - "packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] - ], - "packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] - ], - "packages/grafana-ui/src/components/Combobox/Combobox.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"] - ], "packages/grafana-ui/src/components/Combobox/MultiCombobox.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "packages/grafana-ui/src/components/Combobox/ValuePill.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], - "packages/grafana-ui/src/components/ConfirmModal/ConfirmContent.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], - "packages/grafana-ui/src/components/DataLinks/DataLinkEditor.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"] + "packages/grafana-ui/src/components/Combobox/useOptions.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] ], "packages/grafana-ui/src/components/DataLinks/DataLinkInput.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] @@ -562,81 +534,14 @@ exports[`better eslint`] = { "packages/grafana-ui/src/components/DataLinks/DataLinkSuggestions.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], - "packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], - "packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksListItem.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] - ], - "packages/grafana-ui/src/components/DataSourceSettings/AlertingSettings.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] - ], - "packages/grafana-ui/src/components/DataSourceSettings/BasicAuthSettings.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] - ], "packages/grafana-ui/src/components/DataSourceSettings/CustomHeadersSettings.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"], - [0, 0, 0, "Unexpected any. Specify a different type.", "6"], - [0, 0, 0, "Unexpected any. Specify a different type.", "7"] - ], - "packages/grafana-ui/src/components/DataSourceSettings/DataSourceHttpSettings.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "10"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "11"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "12"] - ], - "packages/grafana-ui/src/components/DataSourceSettings/HttpProxySettings.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"] - ], - "packages/grafana-ui/src/components/DataSourceSettings/SecureSocksProxySettings.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] - ], - "packages/grafana-ui/src/components/DataSourceSettings/TLSAuthSettings.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"] + [0, 0, 0, "Unexpected any. Specify a different type.", "0"], + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "packages/grafana-ui/src/components/DataSourceSettings/types.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], - "packages/grafana-ui/src/components/DateTimePickers/DateTimePicker/DateTimePicker.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] - ], - "packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"] - ], "packages/grafana-ui/src/components/FileDropzone/FileDropzone.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], @@ -1003,9 +908,8 @@ exports[`better eslint`] = { "public/app/core/components/AppChrome/MegaMenu/MegaMenuItem.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], - "public/app/core/components/AppChrome/News/NewsContainer.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] + "public/app/core/components/AppChrome/News/NewsDrawer.tsx:5381": [ + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], "public/app/core/components/AppChrome/News/NewsWrapper.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] @@ -1016,10 +920,12 @@ exports[`better eslint`] = { "public/app/core/components/AppChrome/QuickAdd/QuickAdd.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], + "public/app/core/components/AppChrome/TopBar/ProfileButton.tsx:5381": [ + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] + ], "public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] ], "public/app/core/components/AppChrome/TopBar/TopSearchBarCommandPaletteTrigger.tsx:5381": [ [0, 0, 0, "\'@grafana/ui/src/themes/mixins\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] @@ -1194,9 +1100,6 @@ exports[`better eslint`] = { "public/app/core/components/TagFilter/TagOption.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], - "public/app/core/components/TimePicker/TimePickerWithHistory.tsx:5381": [ - [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] - ], "public/app/core/components/TimeSeries/TimeSeries.tsx:5381": [ [0, 0, 0, "\'@grafana/ui/src/components/uPlot/PlotLegend\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], [0, 0, 0, "\'@grafana/ui/src/components/uPlot/config/UPlotConfigBuilder\' import is restricted from being used by a pattern. Import from the public export instead.", "1"], @@ -1325,8 +1228,10 @@ exports[`better eslint`] = { [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, "Unexpected any. Specify a different type.", "4"] + [0, 0, 0, "Do not use any type assertions.", "3"], + [0, 0, 0, "Unexpected any. Specify a different type.", "4"], + [0, 0, 0, "Unexpected any. Specify a different type.", "5"], + [0, 0, 0, "Unexpected any. Specify a different type.", "6"] ], "public/app/core/utils/richHistory.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -1348,32 +1253,15 @@ exports[`better eslint`] = { [0, 0, 0, "\'@grafana/ui/src/components/Forms/RadioButtonGroup/RadioButtonGroup\' import is restricted from being used by a pattern. Import from the public export instead.", "3"], [0, 0, 0, "\'@grafana/ui/src/components/JSONFormatter/JSONFormatter\' import is restricted from being used by a pattern. Import from the public export instead.", "4"], [0, 0, 0, "\'@grafana/ui/src/themes\' import is restricted from being used by a pattern. Import from the public export instead.", "5"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"], + [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "6"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "10"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "11"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "12"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "13"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"] ], "public/app/features/actions/ActionEditorModalContent.tsx:5381": [ [0, 0, 0, "\'@grafana/ui/src/components/Button\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], [0, 0, 0, "\'@grafana/ui/src/components/Modal/Modal\' import is restricted from being used by a pattern. Import from the public export instead.", "1"] ], - "public/app/features/actions/ActionsInlineEditor.tsx:5381": [ - [0, 0, 0, "\'@grafana/ui/src/components/Button\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], - [0, 0, 0, "\'@grafana/ui/src/components/Modal/Modal\' import is restricted from being used by a pattern. Import from the public export instead.", "1"], - [0, 0, 0, "\'@grafana/ui/src/themes\' import is restricted from being used by a pattern. Import from the public export instead.", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"] - ], - "public/app/features/actions/ActionsListItem.tsx:5381": [ - [0, 0, 0, "\'@grafana/ui/src/components/Icon/Icon\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], - [0, 0, 0, "\'@grafana/ui/src/components/IconButton/IconButton\' import is restricted from being used by a pattern. Import from the public export instead.", "1"], - [0, 0, 0, "\'@grafana/ui/src/themes\' import is restricted from being used by a pattern. Import from the public export instead.", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"] - ], "public/app/features/actions/ParamsEditor.tsx:5381": [ [0, 0, 0, "\'@grafana/ui/src/components/IconButton/IconButton\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], [0, 0, 0, "\'@grafana/ui/src/components/Input/Input\' import is restricted from being used by a pattern. Import from the public export instead.", "1"], @@ -1558,23 +1446,10 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] ], - "public/app/features/alerting/unified/CloneRuleEditor.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"] - ], - "public/app/features/alerting/unified/ExistingRuleEditor.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] - ], "public/app/features/alerting/unified/GrafanaRuleQueryViewer.tsx:5381": [ [0, 0, 0, "\'@grafana/data/src/datetime/rangeutil\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], @@ -1587,7 +1462,8 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "12"], [0, 0, 0, "No untranslated strings. Wrap text with ", "13"], [0, 0, 0, "No untranslated strings. Wrap text with ", "14"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "15"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "15"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "16"] ], "public/app/features/alerting/unified/NotificationPoliciesPage.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], @@ -1597,9 +1473,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] ], "public/app/features/alerting/unified/RedirectToRuleViewer.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], @@ -1611,15 +1485,6 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], [0, 0, 0, "No untranslated strings. Wrap text with ", "7"] ], - "public/app/features/alerting/unified/RuleEditor.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] - ], - "public/app/features/alerting/unified/RuleViewer.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], "public/app/features/alerting/unified/Settings.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], @@ -1658,10 +1523,6 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] ], - "public/app/features/alerting/unified/components/InfoPausedRule.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], "public/app/features/alerting/unified/components/InvalidIntervalWarning.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], @@ -2258,10 +2119,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "10"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "11"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "12"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "13"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "10"] ], "public/app/features/alerting/unified/components/rule-editor/GrafanaFolderAndLabelsStep.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] @@ -2383,6 +2241,9 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "10"], [0, 0, 0, "No untranslated strings. Wrap text with ", "11"] ], + "public/app/features/alerting/unified/components/rule-editor/dag.test.ts:5381": [ + [0, 0, 0, "\'@grafana/runtime/src/utils/DataSourceWithBackend\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] + ], "public/app/features/alerting/unified/components/rule-editor/labels/LabelsButtons.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] @@ -2501,29 +2362,11 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/alerting/unified/components/rule-viewer/RuleViewer.tsx:5381": [ - [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] - ], - "public/app/features/alerting/unified/components/rule-viewer/tabs/Details.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "7"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "8"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "9"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "10"] - ], - "public/app/features/alerting/unified/components/rule-viewer/tabs/Query.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] ], "public/app/features/alerting/unified/components/rule-viewer/tabs/Routing.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] @@ -2923,14 +2766,30 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "2"], [0, 0, 0, "Do not use any type assertions.", "3"], [0, 0, 0, "Do not use any type assertions.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"], - [0, 0, 0, "Unexpected any. Specify a different type.", "6"], - [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.", "5"] ], "public/app/features/alerting/unified/plugins/PluginOriginBadge.tsx:5381": [ [0, 0, 0, "\'@grafana/ui/src/components/Icon/utils\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] ], + "public/app/features/alerting/unified/rule-editor/CloneRuleEditor.tsx:5381": [ + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"] + ], + "public/app/features/alerting/unified/rule-editor/ExistingRuleEditor.tsx:5381": [ + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] + ], + "public/app/features/alerting/unified/rule-editor/RuleEditor.tsx:5381": [ + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] + ], "public/app/features/alerting/unified/rule-list/FilterView.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], @@ -2958,6 +2817,9 @@ exports[`better eslint`] = { "public/app/features/alerting/unified/rule-list/components/RuleListIcon.tsx:5381": [ [0, 0, 0, "\'@grafana/ui/src/components/Text/Text\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] ], + "public/app/features/alerting/unified/state/AlertingQueryRunner.test.ts:5381": [ + [0, 0, 0, "\'@grafana/runtime/src/utils/DataSourceWithBackend\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] + ], "public/app/features/alerting/unified/state/actions.ts:5381": [ [0, 0, 0, "\'@grafana/runtime/src/utils/logging\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] ], @@ -3183,8 +3045,7 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] ], "public/app/features/connections/tabs/ConnectData/ConnectData.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] + [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], @@ -3234,13 +3095,6 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], [0, 0, 0, "No untranslated strings. Wrap text with ", "7"] ], - "public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] - ], - "public/app/features/dashboard-scene/embedding/EmbeddedDashboard.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], "public/app/features/dashboard-scene/embedding/EmbeddedDashboardTestPage.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], @@ -3283,8 +3137,7 @@ exports[`better eslint`] = { "public/app/features/dashboard-scene/pages/DashboardScenePage.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, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"] + [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], "public/app/features/dashboard-scene/panel-edit/LibraryVizPanelInfo.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] @@ -3301,7 +3154,7 @@ exports[`better eslint`] = { "public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] ], "public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx:5381": [ @@ -3415,40 +3268,6 @@ exports[`better eslint`] = { "public/app/features/dashboard-scene/scene/DashboardLinksControls.tsx:5381": [ [0, 0, 0, "\'@grafana/data/src/text/sanitize\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] ], - "public/app/features/dashboard-scene/scene/NavToolbarActions.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "10"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "11"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "12"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "13"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "14"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "15"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "16"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "17"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "18"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "19"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "20"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "21"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "22"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "23"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "24"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "25"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "26"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "27"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "28"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "29"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "30"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "31"] - ], "public/app/features/dashboard-scene/scene/PanelLinks.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], @@ -3461,28 +3280,6 @@ exports[`better eslint`] = { "public/app/features/dashboard-scene/scene/UnlinkModal.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], - "public/app/features/dashboard-scene/scene/row-actions/RowActions.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] - ], - "public/app/features/dashboard-scene/scene/row-actions/RowOptionsButton.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], - "public/app/features/dashboard-scene/scene/row-actions/RowOptionsForm.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] - ], - "public/app/features/dashboard-scene/scene/row-actions/RowOptionsModal.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], - "public/app/features/dashboard-scene/scene/types.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"] - ], "public/app/features/dashboard-scene/serialization/angularMigration.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -3503,9 +3300,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "7"], [0, 0, 0, "Unexpected any. Specify a different type.", "8"] ], - "public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], "public/app/features/dashboard-scene/serialization/transformToV1TypesUtils.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -3737,8 +3531,7 @@ exports[`better eslint`] = { ], "public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/UpsertSnapshot.tsx:5381": [ [0, 0, 0, "\'@grafana/ui/src/components/Input/Input\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], - [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] ], "public/app/features/dashboard-scene/sharing/ShareDrawer/ShareDrawerConfirmAction.tsx:5381": [ [0, 0, 0, "\'@grafana/ui/src/components/ConfirmModal/ConfirmContent\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] @@ -3773,7 +3566,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "5"], [0, 0, 0, "Unexpected any. Specify a different type.", "6"] ], - "public/app/features/dashboard/api/v0.ts:5381": [ + "public/app/features/dashboard/api/v1.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/features/dashboard/components/AddLibraryPanelWidget/AddLibraryPanelWidget.tsx:5381": [ @@ -4218,6 +4011,9 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], + "public/app/features/dashboard/containers/PublicDashboardPage.tsx:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] + ], "public/app/features/dashboard/containers/SoloPanelPage.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] @@ -4236,9 +4032,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], - "public/app/features/dashboard/services/TimeSrv.ts:5381": [ - [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] - ], "public/app/features/dashboard/state/DashboardMigrator.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -4656,13 +4449,9 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"] ], - "public/app/features/explore/ExplorePage.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], "public/app/features/explore/ExploreRunQueryButton.tsx:5381": [ - [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] ], "public/app/features/explore/ExploreToolbar.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], @@ -4720,10 +4509,6 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] ], - "public/app/features/explore/Logs/LogsNavigation.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], "public/app/features/explore/Logs/LogsSamplePanel.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], @@ -4799,27 +4584,6 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] ], - "public/app/features/explore/QueryLibrary/QueryLibraryExpmInfo.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"] - ], - "public/app/features/explore/QueryLibrary/QueryTemplateForm.tsx:5381": [ - [0, 0, 0, "\'@grafana/ui/src/components/Input/Input\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] - ], - "public/app/features/explore/QueryLibrary/QueryTemplatesList.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"] - ], - "public/app/features/explore/QueryLibrary/QueryTemplatesTable/QueryDescriptionCell.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], "public/app/features/explore/RichHistory/RichHistoryStarredTab.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], @@ -5038,9 +4802,7 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"] ], "public/app/features/explore/extensions/ConfirmNavigationModal.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -5069,9 +4831,6 @@ exports[`better eslint`] = { "public/app/features/explore/state/time.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/features/explore/state/time.ts:5381": [ - [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] - ], "public/app/features/explore/state/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -5132,12 +4891,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"] ], "public/app/features/expressions/components/Threshold.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], "public/app/features/expressions/guards.ts:5381": [ [0, 0, 0, "\'@grafana/runtime/src/utils/DataSourceWithBackend\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], @@ -5383,9 +5137,6 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] ], - "public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] - ], "public/app/features/manage-dashboards/components/SnapshotListTable.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], @@ -5577,12 +5328,6 @@ exports[`better eslint`] = { "public/app/features/plugins/admin/components/GetStartedWithPlugin/index.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./GetStartedWithPlugin\`)", "0"] ], - "public/app/features/plugins/admin/components/InstallControls/ExternallyManagedButton.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] - ], "public/app/features/plugins/admin/components/InstallControls/InstallControlsButton.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], @@ -5646,7 +5391,8 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] ], "public/app/features/plugins/admin/components/PluginDetailsPanel.tsx:5381": [ - [0, 0, 0, "\'@grafana/runtime/src/components/PluginPage\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] + [0, 0, 0, "\'@grafana/runtime/src/components/PluginPage\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/plugins/admin/components/PluginDetailsSignature.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], @@ -5747,11 +5493,10 @@ exports[`better eslint`] = { [0, 0, 0, "\'@grafana/data/src/types/pluginExtensions\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] ], "public/app/features/plugins/extensions/usePluginComponents.tsx:5381": [ - [0, 0, 0, "\'@grafana/runtime/src/services/pluginExtensions/getPluginExtensions\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"] + [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/features/plugins/extensions/usePluginLinks.tsx:5381": [ - [0, 0, 0, "\'@grafana/runtime/src/services/pluginExtensions/getPluginExtensions\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] + "public/app/features/plugins/extensions/usePluginFunctions.tsx:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/features/plugins/extensions/validators.ts:5381": [ [0, 0, 0, "\'@grafana/data/src/types/pluginExtensions\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] @@ -5802,9 +5547,6 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] ], - "public/app/features/query-library/api/query.ts:5381": [ - [0, 0, 0, "\'@grafana/runtime/src/services/backendSrv\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] - ], "public/app/features/query/components/QueryEditorRow.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -5979,9 +5721,6 @@ exports[`better eslint`] = { "public/app/features/search/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/features/serviceaccounts/ServiceAccountCreatePage.tsx:5381": [ - [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] - ], "public/app/features/serviceaccounts/ServiceAccountPage.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], @@ -6182,8 +5921,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/trails/DataTrailSettings.tsx:5381": [ - [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/trails/DataTrailsHistory.tsx:5381": [ [0, 0, 0, "\'@grafana/data/src/datetime/rangeutil\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], @@ -6210,9 +5948,6 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "7"], [0, 0, 0, "No untranslated strings. Wrap text with ", "8"] ], - "public/app/features/trails/MetricSelect/NativeHistogramBadge.tsx:5381": [ - [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] - ], "public/app/features/trails/MetricsHeader.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] @@ -6226,8 +5961,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/trails/banners/NativeHistogramBanner.tsx:5381": [ - [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], "public/app/features/transformers/FilterByValueTransformer/FilterByValueFilterEditor.tsx:5381": [ [0, 0, 0, "\'@grafana/data/src/transformations/transformers/filterByValue\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], @@ -6248,8 +5982,7 @@ exports[`better eslint`] = { ], "public/app/features/transformers/FilterByValueTransformer/ValueMatchers/BasicMatcherEditor.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"] + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "public/app/features/transformers/FilterByValueTransformer/ValueMatchers/NoopMatcherEditor.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -6257,15 +5990,11 @@ exports[`better eslint`] = { "public/app/features/transformers/FilterByValueTransformer/ValueMatchers/RangeMatcherEditor.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], - [0, 0, 0, "Unexpected any. Specify a different type.", "6"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], + [0, 0, 0, "Unexpected any. Specify a different type.", "3"] ], "public/app/features/transformers/FilterByValueTransformer/ValueMatchers/RegexMatcherEditor.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], "public/app/features/transformers/FilterByValueTransformer/ValueMatchers/types.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -6448,10 +6177,7 @@ exports[`better eslint`] = { [0, 0, 0, "\'@grafana/data/src/transformations/transformers/histogram\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"] ], "public/app/features/transformers/editors/JoinByFieldTransformerEditor.tsx:5381": [ [0, 0, 0, "\'@grafana/data/src/transformations/transformers/joinByField\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], @@ -6470,9 +6196,7 @@ exports[`better eslint`] = { ], "public/app/features/transformers/editors/LimitTransformerEditor.tsx:5381": [ [0, 0, 0, "\'@grafana/data/src/transformations/transformers/limit\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] ], "public/app/features/transformers/editors/MergeTransformerEditor.tsx:5381": [ [0, 0, 0, "\'@grafana/data/src/transformations/transformers/merge\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], @@ -7578,8 +7302,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], "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, "Do not use any type assertions.", "0"] ], "public/app/plugins/datasource/zipkin/utils/transforms.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index 56a916a0d25..80b5946b497 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -5,6 +5,10 @@ GOPATH ?= $(shell go env GOPATH) GOBIN ?= $(firstword $(subst :, ,${GOPATH}))/bin GO ?= $(shell which go) +# Add this near the top of the file, after the initial variable definitions +ifndef VARIABLES_MK +VARIABLES_MK := 1 + # Below generated variables ensure that every time a tool under each variable is invoked, the correct version # will be used; reinstalling only if needed. # For example for bra variable: @@ -65,3 +69,4 @@ $(SWAGGER): $(BINGO_DIR)/swagger.mod @echo "(re)installing $(GOBIN)/swagger-v0.30.6-0.20240310114303-db51e79a0e37" @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=swagger.mod -o=$(GOBIN)/swagger-v0.30.6-0.20240310114303-db51e79a0e37 "github.com/go-swagger/go-swagger/cmd/swagger" +endif diff --git a/.drone.yml b/.drone.yml index f81d8bbbc69..491c607a83b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -18,7 +18,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd @@ -69,7 +69,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - go install github.com/bazelbuild/buildtools/buildifier@latest @@ -112,7 +112,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -170,7 +170,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -179,7 +179,7 @@ steps: name: yarn-install - commands: - apk add --update git bash - - yarn betterer ci + - yarn betterer:ci depends_on: - yarn-install image: node:22.11.0-alpine @@ -309,7 +309,7 @@ steps: path: /github-app - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -339,6 +339,16 @@ steps: - yarn-install image: node:22-bookworm name: verify-i18n +- commands: + - yarn generate-apis + - "\n file_diff=$(git diff ':!conf')\n if [ -n \"$file_diff\" + ]; then\n echo $file_diff\n echo \"\nAPI client + generation has not been committed. Please run 'yarn generate-apis', commit the + changes and push again.\"\n exit 1\n fi\n " + depends_on: + - yarn-install + image: node:22-bookworm + name: verify-api-clients trigger: event: - pull_request @@ -417,7 +427,7 @@ steps: path: /github-app - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -479,6 +489,7 @@ trigger: - public/app/plugins/**/plugin.json - docs/sources/setup-grafana/configure-grafana/feature-toggles/** - devenv/** + - apps/** type: docker volumes: - host: @@ -506,7 +517,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd @@ -588,6 +599,7 @@ trigger: - public/app/plugins/**/plugin.json - devenv/** - .bingo/** + - apps/** type: docker volumes: - host: @@ -630,11 +642,11 @@ steps: path: /github-app - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -694,12 +706,14 @@ steps: token: from_secret: drone_token - commands: - - docker run --privileged --rm tonistiigi/binfmt --install all + - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version + - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' + - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all - /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 -a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu -a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7 -a docker:grafana:linux/arm/v7:ubuntu --go-version=1.23.5 --yarn-cache=$$YARN_CACHE_FOLDER - --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.3 + --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD > packages.txt @@ -756,7 +770,7 @@ steps: GF_APP_MODE: development GF_SERVER_HTTP_PORT: "3001" GF_SERVER_ROUTER_LOGGING: "1" - image: alpine:3.20.3 + image: alpine:3.21.3 name: grafana-server - commands: - ./bin/build e2e-tests --port 3001 --suite dashboards-suite @@ -1087,7 +1101,7 @@ steps: path: /github-app - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -1100,7 +1114,7 @@ steps: name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -1266,7 +1280,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -1623,7 +1637,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -1694,7 +1708,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -1703,7 +1717,7 @@ steps: name: yarn-install - commands: - apk add --update git bash - - yarn betterer ci + - yarn betterer:ci depends_on: - yarn-install image: node:22.11.0-alpine @@ -1752,7 +1766,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -1782,6 +1796,16 @@ steps: - yarn-install image: node:22-bookworm name: verify-i18n +- commands: + - yarn generate-apis + - "\n file_diff=$(git diff ':!conf')\n if [ -n \"$file_diff\" + ]; then\n echo $file_diff\n echo \"\nAPI client + generation has not been committed. Please run 'yarn generate-apis', commit the + changes and push again.\"\n exit 1\n fi\n " + depends_on: + - yarn-install + image: node:22-bookworm + name: verify-api-clients trigger: branch: main event: @@ -1818,7 +1842,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -1898,7 +1922,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd @@ -1964,7 +1988,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -2039,11 +2063,11 @@ steps: path: /github-app - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2102,12 +2126,14 @@ steps: image: node:22.11.0-alpine name: build-frontend-packages - commands: - - docker run --privileged --rm tonistiigi/binfmt --install all + - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version + - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' + - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all - /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 -a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu -a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7 -a docker:grafana:linux/arm/v7:ubuntu --go-version=1.23.5 --yarn-cache=$$YARN_CACHE_FOLDER - --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.3 + --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD > packages.txt @@ -2168,7 +2194,7 @@ steps: GF_APP_MODE: development GF_SERVER_HTTP_PORT: "3001" GF_SERVER_ROUTER_LOGGING: "1" - image: alpine:3.20.3 + image: alpine:3.21.3 name: grafana-server - commands: - ./bin/build e2e-tests --port 3001 --suite dashboards-suite @@ -2572,7 +2598,7 @@ services: steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2585,7 +2611,7 @@ steps: name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -2830,7 +2856,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -2839,7 +2865,7 @@ steps: name: yarn-install - commands: - apk add --update git bash - - yarn betterer ci + - yarn betterer:ci depends_on: - yarn-install image: node:22.11.0-alpine @@ -2886,7 +2912,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -2916,6 +2942,16 @@ steps: - yarn-install image: node:22-bookworm name: verify-i18n +- commands: + - yarn generate-apis + - "\n file_diff=$(git diff ':!conf')\n if [ -n \"$file_diff\" + ]; then\n echo $file_diff\n echo \"\nAPI client + generation has not been committed. Please run 'yarn generate-apis', commit the + changes and push again.\"\n exit 1\n fi\n " + depends_on: + - yarn-install + image: node:22-bookworm + name: verify-api-clients trigger: branch: - instant @@ -2950,7 +2986,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -3028,7 +3064,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd @@ -3120,7 +3156,7 @@ services: steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -3133,7 +3169,7 @@ steps: name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -3336,11 +3372,11 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -3468,11 +3504,11 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -3958,7 +3994,7 @@ steps: environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - ALPINE_BASE: alpine:3.20.3 + ALPINE_BASE: alpine:3.21.3 CDN_DESTINATION: from_secret: rgm_cdn_destination DESTINATION: @@ -4033,7 +4069,7 @@ steps: environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - ALPINE_BASE: alpine:3.20.3 + ALPINE_BASE: alpine:3.21.3 CDN_DESTINATION: from_secret: rgm_cdn_destination DESTINATION: @@ -4083,51 +4119,6 @@ volumes: path: /var/run/docker.sock name: docker --- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: release-whatsnew-checker -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd - depends_on: [] - environment: - CGO_ENABLED: 0 - image: golang:1.23.5-alpine - name: compile-build-cmd -- commands: - - ./bin/build whatsnew-checker - depends_on: - - compile-build-cmd - image: golang:1.23.5-alpine - name: whats-new-checker -trigger: - event: - exclude: - - promote - ref: - exclude: - - refs/tags/*-cloud* - include: - - refs/tags/v* -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- clone: retries: 3 depends_on: @@ -4195,7 +4186,7 @@ steps: environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - ALPINE_BASE: alpine:3.20.3 + ALPINE_BASE: alpine:3.21.3 CDN_DESTINATION: from_secret: rgm_cdn_destination DESTINATION: @@ -4297,7 +4288,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -4306,7 +4297,7 @@ steps: name: yarn-install - commands: - apk add --update git bash - - yarn betterer ci + - yarn betterer:ci depends_on: - yarn-install image: node:22.11.0-alpine @@ -4351,7 +4342,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -4432,7 +4423,7 @@ steps: environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - ALPINE_BASE: alpine:3.20.3 + ALPINE_BASE: alpine:3.21.3 CDN_DESTINATION: from_secret: rgm_cdn_destination DESTINATION: @@ -4576,7 +4567,7 @@ steps: environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - ALPINE_BASE: alpine:3.20.3 + ALPINE_BASE: alpine:3.21.3 CDN_DESTINATION: from_secret: rgm_cdn_destination DESTINATION: @@ -4696,15 +4687,15 @@ steps: path: /github-app - commands: - export GITHUB_TOKEN=$(cat /github-app/token) - - 'dagger run --silent /src/grafana-build artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF} - --enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --version=$${VERSION} ' - - --go-version=1.23.5 + - dagger run --silent /src/grafana-build artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF} + --enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --version=$${VERSION} + --go-version=1.23.5 depends_on: - github-app-generate-token environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - ALPINE_BASE: alpine:3.20.3 + ALPINE_BASE: alpine:3.21.3 CDN_DESTINATION: from_secret: rgm_cdn_destination DESTINATION: @@ -4743,6 +4734,8 @@ steps: - printenv GCP_KEY_BASE64 | base64 -d > /tmp/key.json - gcloud auth activate-service-account --key-file=/tmp/key.json - gcloud storage cp -r dist/* $${UPLOAD_TO} + depends_on: + - rgm-build environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token @@ -4783,6 +4776,8 @@ volumes: name: docker - name: github-app path: /github-app +- name: github-app + temp: {} --- clone: retries: 3 @@ -4837,13 +4832,13 @@ services: steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.21.3 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -5267,7 +5262,7 @@ steps: - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22-bookworm - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM google/cloud-sdk:431.0.0 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/grafana-ci-deploy:1.3.3 - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine:3.20.3 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine:3.21.3 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM ubuntu:22.04 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM byrnedo/alpine-curl:0.1.8 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM plugins/slack @@ -5305,7 +5300,7 @@ steps: - trivy --exit-code 1 --severity HIGH,CRITICAL node:22-bookworm - trivy --exit-code 1 --severity HIGH,CRITICAL google/cloud-sdk:431.0.0 - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/grafana-ci-deploy:1.3.3 - - trivy --exit-code 1 --severity HIGH,CRITICAL alpine:3.20.3 + - trivy --exit-code 1 --severity HIGH,CRITICAL alpine:3.21.3 - trivy --exit-code 1 --severity HIGH,CRITICAL ubuntu:22.04 - trivy --exit-code 1 --severity HIGH,CRITICAL byrnedo/alpine-curl:0.1.8 - trivy --exit-code 1 --severity HIGH,CRITICAL plugins/slack @@ -5569,6 +5564,6 @@ kind: secret name: gcr_credentials --- kind: signature -hmac: 154c1f072294cef09ba4f579eed30b1e29bc5c263897c862f4574432d321502c +hmac: f16a4715c7a4e6a4ffb1fe041b42fb966310fd5da455239614e9a239493aff82 ... diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bb3c9871f91..92581c315aa 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -38,7 +38,7 @@ /docs/sources/ @irenerl24 -/docs/sources/alerting/ @brendamuir +/docs/sources/alerting/ @JohnnyK-Grafana /docs/sources/dashboards/ @imatwawana /docs/sources/datasources/ @lwandz13 /docs/sources/panels-visualizations/ @imatwawana @@ -66,9 +66,12 @@ /scripts/go-workspace @grafana/grafana-app-platform-squad /hack/ @grafana/grafana-app-platform-squad +/pkg/apis/provisioning @grafana/grafana-git-ui-sync-team +/pkg/registry/apis/provisioning @grafana/grafana-git-ui-sync-team + /apps/alerting/ @grafana/alerting-backend /apps/playlist/ @grafana/grafana-app-platform-squad -/apps/investigation/ @fcjack @matryer +/apps/investigations/ @fcjack @matryer @svennergr /apps/advisor/ @grafana/plugins-platform-backend /pkg/api/ @grafana/grafana-backend-group /pkg/apis/ @grafana/grafana-app-platform-squad @@ -238,6 +241,7 @@ /devenv/dev-dashboards/extensions/ @grafana/plugins-platform-frontend /devenv/docker/blocks/alert_webhook_listener/ @grafana/alerting-backend +/devenv/docker/blocks/stateful_webhook/ @grafana/alerting-backend /devenv/docker/blocks/caddy_tls/ @grafana/alerting-backend /devenv/docker/blocks/clickhouse/ @grafana/partner-datasources /devenv/docker/blocks/collectd/ @grafana/observability-metrics @@ -377,6 +381,11 @@ /crowdin.yml @grafana/grafana-frontend-platform /public/locales/ @grafana/grafana-frontend-platform +/public/locales/de-DE @grafanabot +/public/locales/es-ES @grafanabot +/public/locales/fr-FR @grafanabot +/public/locales/pt-BR @grafanabot +/public/locales/zh-Hans @grafanabot /public/app/core/internationalization/ @grafana/grafana-frontend-platform /e2e/ @grafana/grafana-frontend-platform /e2e/cloud-plugins-suite/ @grafana/partner-datasources @@ -402,7 +411,7 @@ /packages/grafana-schema/src/**/*canvas* @grafana/dataviz-squad /packages/grafana-schema/src/**/*tempo* @grafana/observability-traces-and-profiling /packages/grafana-sql/ @grafana/partner-datasources @grafana/oss-big-tent -/packages/grafana-ui/.storybook/ @grafana/plugins-platform-frontend +/packages/grafana-ui/.storybook/ @grafana/grafana-frontend-platform /packages/grafana-ui/src/components/ @grafana/grafana-frontend-platform /packages/grafana-ui/src/components/BarGauge/ @grafana/dataviz-squad /packages/grafana-ui/src/components/DataLinks/ @grafana/dataviz-squad @@ -421,7 +430,7 @@ /packages/grafana-ui/src/graveyard/Graph/ @grafana/dataviz-squad /packages/grafana-ui/src/graveyard/GraphNG/ @grafana/dataviz-squad /packages/grafana-ui/src/graveyard/TimeSeries/ @grafana/dataviz-squad -/packages/grafana-ui/src/utils/storybook/ @grafana/plugins-platform-frontend +/packages/grafana-ui/src/utils/storybook/ @grafana/grafana-frontend-platform # root files, mostly frontend /.browserslistrc @grafana/frontend-ops @@ -435,6 +444,7 @@ /.betterer.eslint.config.js @grafana/frontend-ops /.gitattributes @grafana/frontend-ops /.gitignore @grafana/frontend-ops +/.ignore @grafana/frontend-ops /.nvmrc @grafana/frontend-ops /.prettierignore @grafana/frontend-ops /.yarn @grafana/frontend-ops @@ -455,6 +465,7 @@ playwright.config.ts @grafana/plugins-platform-frontend # public folder +/public/app/api/ @grafana/grafana-frontend-platform /public/app/core/ @grafana/grafana-frontend-platform /public/app/core/components/TimePicker/ @grafana/grafana-frontend-platform /public/app/core/components/Layers/ @grafana/dataviz-squad @@ -491,6 +502,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/features/explore/ @grafana/observability-traces-and-profiling /public/app/features/expressions/ @grafana/observability-metrics /public/app/features/folders/ @grafana/grafana-frontend-platform +/public/app/features/iam/ @grafana/grafana-frontend-platform /public/app/features/inspector/ @grafana/dashboards-squad /public/app/features/invites/ @grafana/grafana-frontend-platform /public/app/features/library-panels/ @grafana/dashboards-squad @@ -504,7 +516,6 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/features/playlist/ @grafana/dashboards-squad /public/app/features/plugins/ @grafana/plugins-platform-frontend /public/app/features/profile/ @grafana/grafana-frontend-platform -/public/app/features/query-library/ @grafana/grafana-frontend-platform /public/app/features/runtime/ @ryantxu /public/app/features/query/ @grafana/dashboards-squad /public/app/features/sandbox/ @grafana/grafana-frontend-platform @@ -533,6 +544,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/plugins/panel/heatmap/ @grafana/dataviz-squad /public/app/plugins/panel/histogram/ @grafana/dataviz-squad /public/app/plugins/panel/logs/ @grafana/observability-logs +/public/app/plugins/panel/logs-new/ @grafana/observability-logs /public/app/plugins/panel/nodeGraph/ @grafana/observability-traces-and-profiling @grafana/app-o11y-visualizations /public/app/plugins/panel/traces/ @grafana/observability-traces-and-profiling /public/app/plugins/panel/flamegraph/ @grafana/observability-traces-and-profiling @@ -581,6 +593,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/features/explore/NodeGraph/ @grafana/observability-traces-and-profiling /public/app/features/explore/FlameGraph/ @grafana/observability-traces-and-profiling /public/app/features/explore/TraceView/ @grafana/observability-traces-and-profiling +/public/app/features/explore/QueryLibrary/ @grafana/grafana-frontend-platform /public/api-merged.json @grafana/grafana-backend-group /public/api-enterprise-spec.json @grafana/grafana-backend-group @@ -593,9 +606,6 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/AppWrapper.tsx @grafana/frontend-ops /public/app/partials/ @grafana/grafana-frontend-platform - - - /scripts/benchmark-access-control.sh @grafana/access-squad /scripts/check-breaking-changes.sh @grafana/plugins-platform-frontend /scripts/ci-* @grafana/grafana-developer-enablement-squad @@ -620,6 +630,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /scripts/cleanup-husky.sh @grafana/frontend-ops /scripts/verify-repo-update/ @grafana/grafana-developer-enablement-squad /scripts/generate-rtk-apis.ts @grafana/grafana-frontend-platform +/scripts/process-specs.ts @grafana/grafana-frontend-platform /scripts/generate-alerting-rtk-apis.ts @grafana/alerting-frontend /scripts/levitate-parse-json-report.js @grafana/plugins-platform-frontend /scripts/levitate-show-affected-plugins.js @grafana/plugins-platform-frontend @@ -633,9 +644,6 @@ playwright.config.ts @grafana/plugins-platform-frontend .betterer.results @grafanabot .betterer.ts @grafana/grafana-frontend-platform -# @grafana/ui component documentation -*.mdx @grafana/plugins-platform-frontend - # Design system /public/img/icons/unicons/ @grafana/design-system @@ -746,14 +754,19 @@ embed.go @grafana/grafana-as-code /.github/pr-checks.json @tolzhabayev /.github/pr-commands.json @tolzhabayev /.github/renovate.json5 @grafana/frontend-ops +/.github/workflows/add-to-whats-new.yml @grafana/docs-tooling /.github/workflows/auto-triager/ @grafana/plugins-platform-frontend /.github/workflows/alerting-swagger-gen.yml @grafana/alerting-backend +/.github/workflows/alerting-update-module.yml @grafana/alerting-backend /.github/workflows/auto-milestone.yml @grafana/grafana-developer-enablement-squad /.github/workflows/backport.yml @grafana/grafana-developer-enablement-squad /.github/workflows/bump-version.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/backend-coverage.yml @Proximyst /.github/workflows/close-milestone.yml @grafana/grafana-developer-enablement-squad /.github/workflows/release-pr.yml @grafana/grafana-developer-enablement-squad /.github/workflows/release-comms.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/migrate-prs.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/create-next-release-branch.yml @grafana/grafana-developer-enablement-squad /.github/workflows/codeowners-validator.yml @tolzhabayev /.github/workflows/codeql-analysis.yml @DanCech /.github/workflows/commands.yml @torkelo @@ -777,7 +790,6 @@ embed.go @grafana/grafana-as-code /.github/workflows/publish-technical-documentation-next.yml @grafana/docs-tooling /.github/workflows/publish-technical-documentation-release.yml @grafana/docs-tooling /.github/workflows/remove-milestone.yml @grafana/grafana-developer-enablement-squad -/.github/workflows/sbom-report.yml @grafana/security-team /.github/workflows/scripts/json-file-to-job-output.js @grafana/plugins-platform-frontend /.github/workflows/stale.yml @grafana/grafana-developer-enablement-squad /.github/workflows/update-changelog.yml @grafana/grafana-developer-enablement-squad @@ -787,6 +799,7 @@ embed.go @grafana/grafana-as-code /.github/workflows/publish-kinds-release.yml @grafana/platform-monitoring /.github/workflows/verify-kinds.yml @grafana/platform-monitoring /.github/workflows/dashboards-issue-add-label.yml @grafana/dashboards-squad +/.github/workflows/run-schema-v2-e2e.yml @grafana/dashboards-squad /.github/workflows/ephemeral-instances-pr-comment.yml @grafana/grafana-backend-services-squad /.github/workflows/create-security-patch-from-security-mirror.yml @grafana/grafana-developer-enablement-squad /.github/workflows/core-plugins-build-and-release.yml @grafana/plugins-platform-frontend @grafana/plugins-platform-backend diff --git a/.github/commands.json b/.github/commands.json index fb10220fb21..f2896d61551 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -640,7 +640,7 @@ "name": "area/configuration", "action": "addToProject", "addToProject": { - "url": "https://github.com/orgs/grafana/projects/96" + "url": "https://github.com/orgs/grafana/projects/665" } }, { @@ -744,7 +744,7 @@ "name": "area/backend/db/postgres", "action": "addToProject", "addToProject": { - "url": "https://github.com/orgs/grafana/projects/96" + "url": "https://github.com/orgs/grafana/projects/835" } }, { diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f37f7a732f6..0de94ce207d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,7 +8,7 @@ updates: directories: - "/" - "/apps/playlist" - - "/apps/investigation" + - "/apps/investigations" - "/pkg/aggregator" - "/pkg/apimachinery" - "/pkg/apiserver" diff --git a/.github/pr-commands.json b/.github/pr-commands.json index 1803aa29a8a..d211b5cce3a 100644 --- a/.github/pr-commands.json +++ b/.github/pr-commands.json @@ -247,7 +247,8 @@ "/pkg/services/sqlstore/migrations/ualert/**/*", "/pkg/services/alerting/**/*", "/public/app/features/alerting/**/*", - "/pkg/tests/api/alerting/**/*" + "/pkg/tests/api/alerting/**/*", + "/pkg/tests/alertmanager/**/*" ], "action": "updateLabel", "addLabel": "area/alerting" diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 6cc2ac80423..a782ac2c0a0 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -13,7 +13,6 @@ "slate-react", // we don't want to continue using this on the long run, use Monaco editor instead of Slate "@types/slate-react", // we don't want to continue using this on the long run, use Monaco editor instead of Slate "@types/slate", // we don't want to continue using this on the long run, use Monaco editor instead of Slate - "storybook-dark-mode", // 4.0.2 causes storybook 8.4 to break with react hooks errors // Temporarily pause updating lerna and nx until we resolve build issues "lerna", "nx" diff --git a/.github/workflows/actions/changelog/index.js b/.github/workflows/actions/changelog/index.js index 48da9dcf33a..b658caf349c 100644 --- a/.github/workflows/actions/changelog/index.js +++ b/.github/workflows/actions/changelog/index.js @@ -69,8 +69,17 @@ const graphql = async (ghtoken, query, variables) => { }, body: JSON.stringify({ query, variables }), }); - const { data } = await results.json(); - return data; + + const res = await results.json(); + + LOG( + JSON.stringify({ + status: results.status, + text: results.statusText, + }) + ); + + return res.data; }; // Using Github GraphQL API find the timestamp for the given tag/commit hash. @@ -99,20 +108,20 @@ const getCommitishDate = async (name, owner, target) => { // Using Github GraphQL API get a list of PRs between the two "commitish" items. // This resoves the "since" item's timestamp first and iterates over all PRs // till "target" using naïve pagination. -const getHistory = async (name, owner, target, sinceDate) => { - LOG(`Fetching ${owner}/${name} PRs since ${sinceDate} till ${target}`); +const getHistory = async (name, owner, from, to) => { + LOG(`Fetching ${owner}/${name} PRs between ${from} and ${to}`); const query = ` query findCommitsWithAssociatedPullRequests( $name: String! $owner: String! - $target: String! - $sinceDate: GitTimestamp + $from: String! + $to: String! $cursor: String ) { repository(name: $name, owner: $owner) { - object(expression: $target) { - ... on Commit { - history(first: 50, since: $sinceDate, after: $cursor) { + ref(qualifiedName: $from) { + compare(headRef: $to) { + commits(first: 25, after: $cursor) { totalCount pageInfo { hasNextPage @@ -155,13 +164,13 @@ const getHistory = async (name, owner, target, sinceDate) => { const result = await graphql(ghtoken, query, { name, owner, - target, - sinceDate, + from, + to, cursor, }); LOG(`GraphQL: ${JSON.stringify(result)}`); - nodes = [...nodes, ...result.repository.object.history.nodes]; - const { hasNextPage, endCursor } = result.repository.object.history.pageInfo; + nodes = [...nodes, ...result.repository.ref.compare.commits.nodes]; + const { hasNextPage, endCursor } = result.repository.ref.compare.commits.pageInfo; if (!hasNextPage) { break; } @@ -175,11 +184,11 @@ const getHistory = async (name, owner, target, sinceDate) => { // feature, deprecation, breaking change and plugin fixes/enhancements). // // PR grouping relies on Github labels only, not on the PR contents. -const getChangeLogItems = async (name, owner, sinceDate, to) => { +const getChangeLogItems = async (name, owner, from, to) => { // check if a node contains a certain label const hasLabel = ({ labels }, label) => labels.nodes.some(({ name }) => name === label); // get all the PRs between the two "commitish" items - const history = await getHistory(name, owner, to, sinceDate); + const history = await getHistory(name, owner, from, to); const items = history.flatMap((node) => { // discard PRs without a "changelog" label @@ -231,13 +240,10 @@ const previous = process.argv[3] || process.env.INPUT_PREVIOUS || (await getPrev LOG(`Previous tag/commit: ${previous}`); -const sinceDate = await getCommitishDate('grafana', 'grafana', previous); -LOG(`Previous tag/commit timestamp: ${sinceDate}`); - // Get all changelog items from Grafana OSS -const oss = await getChangeLogItems('grafana', 'grafana', sinceDate, target); +const oss = await getChangeLogItems('grafana', 'grafana', previous, target); // Get all changelog items from Grafana Enterprise -const entr = await getChangeLogItems('grafana-enterprise', 'grafana', sinceDate, target); +const entr = await getChangeLogItems('grafana-enterprise', 'grafana', previous, target); LOG(`Found OSS PRs: ${oss.length}`); LOG(`Found Enterprise PRs: ${entr.length}`); diff --git a/.github/workflows/add-to-whats-new.yml b/.github/workflows/add-to-whats-new.yml new file mode 100644 index 00000000000..900ab9615bd --- /dev/null +++ b/.github/workflows/add-to-whats-new.yml @@ -0,0 +1,16 @@ +name: Add comment about adding a What's new note +on: + pull_request: + types: [labeled] + +jobs: + add-comment: + if: ${{ ! github.event.pull_request.head.repo.fork && contains(github.event.pull_request.labels.*.name, 'add to what''s new') }} + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 + with: + message: | + Since you've added the `Add to what's new` label, consider drafting a [What's new note](https://admin.grafana.com/content-admin/#/collections/whats-new/new) for this feature. diff --git a/.github/workflows/alerting-update-module.yml b/.github/workflows/alerting-update-module.yml new file mode 100644 index 00000000000..1d919f3dc4b --- /dev/null +++ b/.github/workflows/alerting-update-module.yml @@ -0,0 +1,131 @@ +name: Update Alerting Module + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + update-grafana: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + + - name: Check if update branch exists + run: | + if git ls-remote --heads origin update-alerting-module | grep -q 'update-alerting-module'; then + echo "Branch 'update-alerting-module' already exists. There might be an open PR with Grafana updates." + echo "Please review and merge/close the existing PR before running this workflow again." + exit 1 + fi + + - name: Setup Go + uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # 5.3.0 + with: + "go-version-file": "go.mod" + + - name: Extract current commit hash of alerting module + id: current-commit + run: | + FROM_COMMIT=$(go list -m -json github.com/grafana/alerting | jq -r '.Version' | grep -oP '(?<=-)[a-f0-9]+$') + echo "from_commit=$FROM_COMMIT" >> $GITHUB_OUTPUT + + - name: Get latest commit + id: latest-commit + env: + GH_TOKEN: ${{ github.token }} + run: | + TO_COMMIT=$(gh api repos/grafana/alerting/commits/main --jq '.sha') + if [ -z "$TO_COMMIT" ]; then + echo "Failed to fetch latest commit" + exit 1 + fi + echo "to_commit=$TO_COMMIT" >> $GITHUB_OUTPUT + + - name: Compare commit hashes + run: | + FROM_COMMIT="${{ steps.current-commit.outputs.from_commit }}" + TO_COMMIT="${{ steps.latest-commit.outputs.to_commit }}" + + # Compare just the length of the shorter hash + SHORT_TO_COMMIT="${TO_COMMIT:0:${#FROM_COMMIT}}" + + if [ "$FROM_COMMIT" = "$SHORT_TO_COMMIT" ]; then + echo "Current version ($FROM_COMMIT) is already at latest ($SHORT_TO_COMMIT). No update needed." + exit 0 + fi + echo "Updates available: $FROM_COMMIT -> $TO_COMMIT" + + - name: Check for commit history + id: check-commits + env: + GH_TOKEN: ${{ github.token }} + run: | + # get all commits that contains 'Alerting:' in the message + ALERTING_COMMITS=$(gh api repos/grafana/alerting/compare/${{ steps.current-commit.outputs.from_commit }}...${{ steps.latest-commit.outputs.to_commit }} \ + --jq '.commits[].commit.message | split("\n")[0]') || true + + # Use printf instead of echo -e for better multiline handling + printf "%s\n" "$ALERTING_COMMITS" + + # make the list for markdown and replace PR numbers with links + ALERTING_COMMITS_FORMATTED=$(echo "$ALERTING_COMMITS" | while read -r line; do echo "- $line" | sed -E 's/\(#([0-9]+)\)/[#\1](https:\/\/github.com\/grafana\/grafana\/pull\/\1)/g'; done) + + echo "alerting_commits<> $GITHUB_OUTPUT + echo "$ALERTING_COMMITS_FORMATTED" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Update alerting module + env: + GOSUMDB: off + run: | + go get github.com/grafana/alerting@${{ steps.latest-commit.outputs.to_commit }} + make update-workspace + + - id: get-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@28361cdb22223e5f1e34358c86c20908e7248760 # 1.1.0 + with: + repo_secrets: | + GITHUB_APP_ID=alerting-team:app-id + GITHUB_APP_PRIVATE_KEY=alerting-team:private-key + + - name: "Generate token" + id: generate_token + uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # 1.11.5 + with: + app-id: ${{ env.GITHUB_APP_ID }} + private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # 7.0.6 + id: create-pr + with: + token: '${{ steps.generate_token.outputs.token }}' + title: 'Alerting: Update alerting module to ${{ steps.latest-commit.outputs.to_commit }}' + branch: alerting/update-alerting-module + delete-branch: true + body: | + Updates Grafana Alerting module to latest version. + + Compare changes: https://github.com/grafana/alerting/compare/${{ steps.current-commit.outputs.from_commit }}...${{ steps.latest-commit.outputs.to_commit }} +
+ Commits + + ${{ steps.check-commits.outputs.alerting_commits }} + +
+ + Created by: [GitHub Action Job](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) + - name: Add PR URL to Summary + if: steps.create-pr.outputs.pull-request-url != '' + run: | + echo "## Pull Request Created" >> $GITHUB_STEP_SUMMARY + echo "🔗 [View Pull Request](${{ steps.create-pr.outputs.pull-request-url }})" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/backend-coverage.yml b/.github/workflows/backend-coverage.yml new file mode 100644 index 00000000000..f103e5b1eed --- /dev/null +++ b/.github/workflows/backend-coverage.yml @@ -0,0 +1,109 @@ +name: Backend Coverage (OSS) +on: + push: + paths: + - pkg/** + - .github/workflows/backend-coverage.yml + - go.* + branches: + - main + pull_request: + +jobs: + unit-tests: + name: Run unit tests (OSS) + runs-on: ubuntu-latest + if: github.repository == 'grafana/grafana' + steps: + - name: Check out repository + uses: actions/checkout@v4.2.2 + - name: Setup Go environment + uses: actions/setup-go@v5.3.0 + with: + go-version-file: go.work + - name: Run tests + run: make gen-go test-go-unit + - name: Upload coverage file + uses: actions/upload-artifact@v4 + if: success() || failure() + with: + name: unit-cov + path: unit.cov + retention-days: 1 + compression-level: 9 # this is raw text, and includes a _lot_ of repetition. compressing it should yield pretty good results. + integration-tests: + name: Run integration tests (OSS) + runs-on: ubuntu-latest + if: github.repository == 'grafana/grafana' + steps: + - name: Check out repository + uses: actions/checkout@v4.2.2 + - name: Setup Go environment + uses: actions/setup-go@v5.3.0 + with: + go-version-file: go.work + - name: Run tests + run: make gen-go test-go-integration + - name: Upload coverage file + uses: actions/upload-artifact@v4 + if: success() || failure() + with: + name: integration-cov + path: integration.cov + retention-days: 1 + compression-level: 9 # this is raw text, and includes a _lot_ of repetition. compressing it should yield pretty good results. + report-coverage: + name: Report coverage from unit and integration tests (OSS) + needs: [unit-tests, integration-tests] + runs-on: ubuntu-latest + # we don't do always() so as to not run even if the workflow is cancelled + if: github.repository == 'grafana/grafana' && (success() || failure()) + steps: + - name: Check out repository + uses: actions/checkout@v4.2.2 + - name: Setup Go environment + uses: actions/setup-go@v5.3.0 + with: + go-version-file: go.work + - name: Generate Go + run: make gen-go + - uses: actions/download-artifact@v4 + with: + pattern: '*-cov' + path: . + merge-multiple: true + - name: Join coverage outputs + run: | + cp unit.cov backend.cov + tail -n+2 integration.cov >> backend.cov + - name: Convert coverage info to per-func stats + run: go tool cover -func backend.cov > backend-funcs.log + # The HTML can be useful in some UI to browse the artifacts easily. + - name: Convert coverage info to HTML + run: go tool cover -html backend.cov -o backend.html + - name: Upload coverage file + uses: actions/upload-artifact@v4 + with: + name: backend-cov + path: | + backend.cov + backend-funcs.log + backend.html + retention-days: 30 + compression-level: 9 # this is raw text, and includes a _lot_ of repetition. compressing it should yield pretty good results. + - name: Delete old coverage files + # This is a community tool, not one provided by GitHub. Hence it shall be pinned to a hash. + # https://github.com/GeekyEggo/delete-artifact/tree/v5 + uses: GeekyEggo/delete-artifact@f275313e70c08f6120db482d7a6b98377786765b + with: + name: | + unit-cov + integration-cov + failOnError: false # oh well, we'll just have the extra artifacts... + - name: Set summary to total coverage + # We use single quotes here to disable the bash backtick behaviour of executing commands. + run: | + echo '# Coverage' >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + grep 'total:' backend-funcs.log | tr '\t' ' ' >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 18407b67f76..5f21ab276ee 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -10,24 +10,18 @@ jobs: if: github.repository == 'grafana/grafana' runs-on: ubuntu-latest steps: - - name: Checkout Actions + - name: Checkout uses: actions/checkout@v4 - with: - repository: "grafana/grafana-github-actions" - path: ./actions - ref: main - - name: Install Actions - run: npm install --production --prefix ./actions - name: "Generate token" id: generate_token uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 with: app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + - run: git config --global user.email '132647405+grafana-delivery-bot[bot]@users.noreply.github.com' + - run: git config --global user.name 'grafana-delivery-bot[bot]' + - run: git remote set-url origin "https://grafana-delivery-bot:${{ steps.generate_token.outputs.token }}@github.com/grafana/grafana.git" - name: Run backport - uses: ./actions/backport + uses: grafana/grafana-github-actions-go/backport@main with: - metricsWriteAPIKey: ${{secrets.GRAFANA_MISC_STATS_API_KEY}} token: ${{ steps.generate_token.outputs.token }} - labelsToAdd: "backport" - title: "[{{base}}] {{originalTitle}}" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 14981389deb..c1f90ceb831 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -52,7 +52,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -67,4 +67,4 @@ jobs: make build-go - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/create-next-release-branch.yml b/.github/workflows/create-next-release-branch.yml new file mode 100644 index 00000000000..8fc01cd442d --- /dev/null +++ b/.github/workflows/create-next-release-branch.yml @@ -0,0 +1,53 @@ +name: Create next release branch +on: + workflow_call: + inputs: + ownerRepo: + type: string + description: Owner/repo of the repository where the branch is created (e.g. 'grafana/grafana') + required: true + source: + description: The release branch to increment (eg providing `release-11.2.3` will result in `release-11.2.4` being created) + type: string + required: true + secrets: + GRAFANA_DELIVERY_BOT_APP_ID: + required: true + GRAFANA_DELIVERY_BOT_APP_PEM: + required: true + outputs: + branch: + description: The new branch that was created + value: ${{ jobs.main.outputs.branch }} + workflow_dispatch: + inputs: + ownerRepo: + description: Owner/repo of the repository where the branch is created (e.g. 'grafana/grafana') + source: + description: The release branch to increment (eg providing `release-11.2.3` will result in `release-11.2.4` being created) + type: string + required: true + secrets: + GRAFANA_DELIVERY_BOT_APP_ID: + required: true + GRAFANA_DELIVERY_BOT_APP_PEM: + required: true +jobs: + main: + runs-on: ubuntu-latest + outputs: + branch: ${{ steps.branch.outputs.branch }} + steps: + - name: "Generate token" + id: generate_token + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + with: + app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} + private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + - name: Create release branch + id: branch + uses: grafana/grafana-github-actions-go/bump-release@main + with: + ownerRepo: ${{ inputs.ownerRepo }} + source: ${{ inputs.source }} + token: ${{ steps.generate_token.outputs.token }} diff --git a/.github/workflows/create-security-patch-from-security-mirror.yml b/.github/workflows/create-security-patch-from-security-mirror.yml index e187149b8b4..f85239241ad 100644 --- a/.github/workflows/create-security-patch-from-security-mirror.yml +++ b/.github/workflows/create-security-patch-from-security-mirror.yml @@ -11,6 +11,7 @@ on: branches: - "main" - "v*.*.*" + - "release-*.*.*" # This is run before the pull request has been merged, so we'll run against the src branch jobs: diff --git a/.github/workflows/deploy-pr-preview.yml b/.github/workflows/deploy-pr-preview.yml index dcb8fcd1c2a..bfad92ebf73 100644 --- a/.github/workflows/deploy-pr-preview.yml +++ b/.github/workflows/deploy-pr-preview.yml @@ -11,14 +11,21 @@ on: jobs: deploy-pr-preview: - if: ${{ ! github.event.pull_request.head.repo.fork }} + if: "!github.event.pull_request.head.repo.fork" uses: grafana/writers-toolkit/.github/workflows/deploy-preview.yml@main with: - sha: ${{ github.event.pull_request.head.sha }} branch: ${{ github.head_ref }} event_number: ${{ github.event.number }} - title: ${{ github.event.pull_request.title }} repo: grafana - website_directory: content/docs/grafana/latest - relative_prefix: /docs/grafana/latest/ - index_file: true + sha: ${{ github.event.pull_request.head.sha }} + sources: | + [ + { + "index_file": "content/docs/grafana/_index.md", + "relative_prefix": "/docs/grafana/latest/", + "repo": "grafana", + "source_directory": "docs/sources", + "website_directory": "content/docs/grafana/latest" + } + ] + title: ${{ github.event.pull_request.title }} diff --git a/.github/workflows/documentation-ci.yml b/.github/workflows/documentation-ci.yml index 0f041220ec9..4c688968a09 100644 --- a/.github/workflows/documentation-ci.yml +++ b/.github/workflows/documentation-ci.yml @@ -10,10 +10,10 @@ jobs: container: image: grafana/vale:latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@v4 with: persist-credentials: false - - uses: grafana/writers-toolkit/vale-action@13205961f20ad13843505a9b84fdf032f911a3f4 # vale-action/v1.1.0 + - uses: grafana/writers-toolkit/vale-action@vale-action/v1 with: filter: '.Name in ["Grafana.WordList", "Grafana.Spelling", "Grafana.ProductPossessives"]' token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/i18n-crowdin-download.yml b/.github/workflows/i18n-crowdin-download.yml index cba14abe43b..e0c3c50b9bb 100644 --- a/.github/workflows/i18n-crowdin-download.yml +++ b/.github/workflows/i18n-crowdin-download.yml @@ -3,7 +3,7 @@ name: Crowdin Download Action on: workflow_dispatch: schedule: - - cron: "0 * * * *" + - cron: "0 0 * * *" jobs: download-sources-from-crowdin: @@ -12,6 +12,7 @@ jobs: permissions: contents: write # needed to commit changes into the PR pull-requests: write # needed to update PR description, labels, etc + id-token: write # needed to get vault secrets steps: - name: Generate token @@ -41,17 +42,11 @@ jobs: pull_request_body: | :robot: Automatic download of translations from Crowdin. - Steps for merging: - 1. A quick sanity check of the changes and approve. Things to look out for: - - No changes in the English file. The source of truth is in the main branch, NOT in Crowdin. - - Translations maybe be removed if the English phrase was removed, but there should not be many of these - - Anything else that looks 'funky'. Ask if you're not sure. - 2. Approve & (Auto-)merge. :tada: + This runs once per day and will merge automatically if all the required checks pass. - If there's a conflict, close the pull request and **delete the branch**. A GH action will recreate the pull request. - Remember, the longer this pull request is open, the more likely it is that it'll get conflicts. + If there's a conflict, close the pull request and **delete the branch**. + You can then either wait for the schedule to trigger a new PR, or rerun the action manually. pull_request_labels: 'area/frontend, area/internationalization, no-changelog, no-backport' - pull_request_reviewers: 'grafana-frontend-platform' pull_request_base_branch_name: 'main' base_url: 'https://grafana.api.crowdin.com' config: 'crowdin.yml' @@ -119,3 +114,45 @@ jobs: with: pr: ${{ steps.crowdin-download.outputs.pull_request_number }} token: ${{ steps.generate_token.outputs.token }} + + - name: Get vault secrets + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in ci/repo/grafana/grafana/grafana-pr-approver + repo_secrets: | + GRAFANA_PR_APPROVER_APP_ID=grafana-pr-approver:app-id + GRAFANA_PR_APPROVER_APP_PEM=grafana-pr-approver:private-key + + - name: Generate approver token + if: steps.crowdin-download.outputs.pull_request_url + id: generate_approver_token + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + with: + app_id: ${{ env.GRAFANA_PR_APPROVER_APP_ID }} + private_key: ${{ env.GRAFANA_PR_APPROVER_APP_PEM }} + + - name: Approve and automerge PR + if: steps.crowdin-download.outputs.pull_request_url + shell: bash + # Only approve if: + # - the PR does not modify files other than json files under the public/locales/ directory + # - the PR does not modify the en-US locale + run: | + filesChanged=$(gh pr diff --name-only ${{ steps.crowdin-download.outputs.pull_request_url }}) + + if [[ $(echo $filesChanged | grep -v 'public/locales/[a-zA-Z\-]*/grafana.json' | wc -l) -ne 0 ]]; then + echo "Non-i18n changes detected, not approving" + exit 1 + fi + + if [[ $(echo $filesChanged | grep "public/locales/en-US" | wc -l) -ne 0 ]]; then + echo "public/locales/en-US changes detected, not approving" + exit 1 + fi + + echo "Approving and enabling automerge" + gh pr review ${{ steps.crowdin-download.outputs.pull_request_url }} --approve + gh pr merge --auto --squash ${{ steps.crowdin-download.outputs.pull_request_url }} + env: + GITHUB_TOKEN: ${{ steps.generate_approver_token.outputs.token }} diff --git a/.github/workflows/migrate-prs.yml b/.github/workflows/migrate-prs.yml new file mode 100644 index 00000000000..31bb8f9f9da --- /dev/null +++ b/.github/workflows/migrate-prs.yml @@ -0,0 +1,60 @@ +name: Migrate open PRs +# Migrate open PRs from a superseded release branch to the current release branch and notify authors +on: + workflow_call: + inputs: + from: + description: 'The base branch to check for open PRs' + required: true + type: string + to: + description: 'The base branch to migrate open PRs to' + required: true + type: string + ownerRepo: + description: Owner/repo of the repository where the branch is created (e.g. 'grafana/grafana') + required: true + type: string + secrets: + GRAFANA_DELIVERY_BOT_APP_ID: + required: true + GRAFANA_DELIVERY_BOT_APP_PEM: + required: true + workflow_dispatch: + inputs: + from: + description: 'The base branch to check for open PRs' + required: true + type: string + to: + description: 'The base branch to migrate open PRs to' + required: true + type: string + ownerRepo: + description: Owner/repo of the repository where the branch is created (e.g. 'grafana/grafana') + required: true + type: string + secrets: + GRAFANA_DELIVERY_BOT_APP_ID: + required: true + GRAFANA_DELIVERY_BOT_APP_PEM: + required: true + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: "Generate token" + id: generate_token + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + with: + app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} + private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + - name: Migrate PRs + uses: grafana/grafana-github-actions-go/migrate-open-prs@main + with: + token: ${{ steps.generate_token.outputs.token }} + ownerRepo: ${{ inputs.ownerRepo }} + from: ${{ inputs.from }} + to: ${{ inputs.to }} + binary_release_tag: 'dev' diff --git a/.github/workflows/publish-technical-documentation-release.yml b/.github/workflows/publish-technical-documentation-release.yml index b36eb48885a..57d779660c5 100644 --- a/.github/workflows/publish-technical-documentation-release.yml +++ b/.github/workflows/publish-technical-documentation-release.yml @@ -3,7 +3,7 @@ name: publish-technical-documentation-release on: push: branches: - - v[0-9]+.[0-9]+.x + - release-[0-9]+.[0-9]+.[0-9]+ tags: - v[0-9]+.[0-9]+.[0-9]+ paths: @@ -23,7 +23,7 @@ jobs: - uses: grafana/writers-toolkit/publish-technical-documentation-release@publish-technical-documentation-release/v2 with: release_tag_regexp: "^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" - release_branch_regexp: "^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.x$" - release_branch_with_patch_regexp: "^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" + release_branch_regexp: "^release-(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" + release_branch_with_patch_regexp: "^release-(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" website_directory: content/docs/grafana version_suffix: "" diff --git a/.github/workflows/release-comms.yml b/.github/workflows/release-comms.yml index 02d53057184..720948e659b 100644 --- a/.github/workflows/release-comms.yml +++ b/.github/workflows/release-comms.yml @@ -19,7 +19,7 @@ on: - closed branches: - 'main' - - 'v*.*.*' + - 'release-*.*.*' jobs: setup: @@ -27,6 +27,7 @@ jobs: name: Setup and establish latest outputs: version: ${{ steps.output.outputs.version }} + release_branch: ${{ steps.output.outputs.release_branch }} dry_run: ${{ steps.output.outputs.dry_run }} latest: ${{ steps.output.outputs.latest }} runs-on: ubuntu-latest @@ -49,9 +50,54 @@ jobs: echo "latest: $LATEST" echo "version: $VERSION" + echo "release_branch=$(echo $VERSION | sed -s 's/^v/release-/g')" >> "$GITHUB_OUTPUT" echo "dry_run=$DRY_RUN" >> "$GITHUB_OUTPUT" echo "latest=$LATEST" >> "$GITHUB_OUTPUT" echo "version=$VERSION" >> "$GITHUB_OUTPUT" + create_next_release_branch_grafana: + name: Create next release branch (Grafana) + needs: setup + uses: ./.github/workflows/create-next-release-branch.yml + secrets: + GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} + GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + with: + ownerRepo: 'grafana/grafana' + source: ${{ needs.setup.outputs.release_branch }} + create_next_release_branch_enterprise: + name: Create next release branch (Grafana Enterprise) + needs: setup + uses: ./.github/workflows/create-next-release-branch.yml + secrets: + GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} + GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + with: + ownerRepo: 'grafana/grafana-enterprise' + source: ${{ needs.setup.outputs.release_branch }} + migrate_prs_grafana: + needs: + - setup + - create_next_release_branch_grafana + uses: ./.github/workflows/migrate-prs.yml + secrets: + GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} + GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + with: + ownerRepo: 'grafana/grafana' + from: ${{ needs.setup.outputs.release_branch }} + to: ${{ needs.create_next_release_branch_grafana.outputs.branch }} + migrate_prs_enterprise: + needs: + - setup + - create_next_release_branch_enterprise + uses: ./.github/workflows/migrate-prs.yml + secrets: + GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} + GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + with: + ownerRepo: 'grafana/grafana-enterprise' + from: ${{ needs.setup.outputs.release_branch }} + to: ${{ needs.create_next_release_branch_enterprise.outputs.branch }} post_changelog_on_forum: needs: setup uses: ./.github/workflows/community-release.yml diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index d6bcd7d5446..1234cdc75ef 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -4,7 +4,7 @@ # Please refrain from including any processes that do not result in code changes in this workflow. Instead, they should # either be triggered in the release promotion process or in the release comms process (that is triggered by merging # this PR). -name: Complete a Grafana release +name: Grafana Release PR on: workflow_dispatch: inputs: @@ -19,7 +19,7 @@ on: target: required: true type: string - description: The base branch that these changes are being merged into + description: The release branch pattern (eg v9.5.x) that these changes are being merged into backport: required: false type: string @@ -61,12 +61,20 @@ jobs: with: app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + - name: Get release branch + id: branch + uses: grafana/grafana-github-actions-go/latest-release-branch@main + with: + token: ${{ steps.generate_token.outputs.token }} + ownerRepo: 'grafana/grafana' + pattern: ${{ inputs.target }} - name: Checkout Grafana uses: actions/checkout@v4 with: - ref: ${{ inputs.target }} + ref: ${{ steps.branch.outputs.branch }} fetch-depth: 0 fetch-tags: true + token: ${{ steps.generate_token.outputs.token }} - name: Checkout Grafana (main) uses: actions/checkout@v4 with: @@ -74,6 +82,7 @@ jobs: fetch-depth: '0' fetch-tags: 'false' path: .grafana-main + token: ${{ steps.generate_token.outputs.token }} - name: Setup nodejs environment uses: actions/setup-node@v4 with: @@ -124,7 +133,6 @@ jobs: rm -f CHANGELOG.part changelog_items.md git diff CHANGELOG.md - - name: "Prettify CHANGELOG.md" run: npx prettier --write CHANGELOG.md - name: Commit CHANGELOG.md changes @@ -138,6 +146,7 @@ jobs: - name: Add package.json changes run: | git add package.json lerna.json yarn.lock packages public + test -e e2e/test-plugins && git add e2e/test-plugins git commit -m "Update version to ${{ inputs.version }}" - name: Git push @@ -151,7 +160,7 @@ jobs: $( [ "x${{ inputs.latest }}" == "xtrue" ] && printf %s '-l "release/latest"') \ -l "no-changelog" \ --dry-run=${{ inputs.dry_run }} \ - -B "${{ inputs.target }}" \ + -B "${{ steps.branch.outputs.branch }}" \ --title "Release: ${{ inputs.version }}" \ --body "These code changes must be merged after a release is complete" env: @@ -165,7 +174,7 @@ jobs: -l "product-approved" \ -l "no-changelog" \ --dry-run=${{ inputs.dry_run }} \ - -B "${{ inputs.target }}" \ + -B "${{ steps.branch.outputs.branch }}" \ --title "Release: ${{ inputs.version }}" \ --body "These code changes must be merged after a release is complete" env: diff --git a/.github/workflows/run-schema-v2-e2e.yml b/.github/workflows/run-schema-v2-e2e.yml new file mode 100644 index 00000000000..8b55aa4c430 --- /dev/null +++ b/.github/workflows/run-schema-v2-e2e.yml @@ -0,0 +1,44 @@ +name: Run dashboard schema v2 e2e + +on: + push: + branches: + - main + pull_request: + branches: + - '**' + +env: + ARCH: linux-amd64 + +jobs: + dashboard-schema-v2-e2e: + runs-on: ubuntu-latest + continue-on-error: true + if: github.event.pull_request.draft == false + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Pin Go version to mod file + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + - run: go version + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'yarn' + - name: Install dependencies + run: yarn install --immutable + - name: Build grafana + run: make build + - name: Install Cypress dependencies + uses: cypress-io/github-action@v6 + with: + runTests: false + - name: Run dashboard scenes e2e + run: yarn e2e:schema-v2 || echo "Test failed but marking as success since schema V2 is behind a feature flag and should not block PRs" + + - name: Always succeed # This is a workaround to make the job pass even if the previous step fails + if: failure() + run: exit 0 \ No newline at end of file diff --git a/.github/workflows/sbom-report.yml b/.github/workflows/sbom-report.yml deleted file mode 100644 index efd188da331..00000000000 --- a/.github/workflows/sbom-report.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: syft-sbom-ci -on: - release: - types: [ published ] - workflow_dispatch: -jobs: - syft-sbom: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Anchore SBOM Action - uses: grafana/shared-workflows/actions@syft-sbom-v0.0.1 - with: - artifact-name: ${{ github.event.repository.name }}-spdx.json diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index b1346236770..25104af8f29 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -27,11 +27,14 @@ jobs: trivy fs --no-progress --download-db-only --db-repository public.ecr.aws/aquasecurity/trivy-db - name: Run Trivy vulnerability scanner (table output) # Use the trivy binary rather than the aquasecurity/trivy-action action - # to avoid a few bugs - # scan the filesystem, rather than building a Docker image prior - the - # downside is we won't catch dependencies that are only installed in the - # image, but the upside is we'll only catch vulnerabilities that are - # explicitly in the our dependencies + # to avoid a few bugs. + # + # We scan the file system rather than building the Docker image to only scan + # our direct dependencies. The Docker images are still scanned by + # Vulnerability Observability: + # - OSS: https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/projects/sources/1 + # - Enterprise: https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/projects/sources/12 + # (If these links are outdated, just go to the list and find the images manually.) run: | trivy fs \ --scanners vuln \ diff --git a/.gitignore b/.gitignore index ac5ad6f5f7d..c043094a016 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ vendor/ /requests tsconfig.tsbuildinfo __debug_bin* +*.cov # yarn .yarn/cache @@ -40,6 +41,7 @@ __debug_bin* # This is the new place of the block, but I leave the previous here for a while /devenv/docker/blocks/auth/saml-enterprise /devenv/docker/blocks/auth/signer +/devenv/docker/blocks/spanner_tests /tmp tools/phantomjs/phantomjs @@ -103,14 +105,19 @@ profile.cov /pkg/cmd/grafana-cli/grafana-cli /pkg/cmd/grafana-server/grafana-server /pkg/cmd/grafana-server/debug -/pkg/extensions/* -/pkg/build/cmd/artifactspage.go -/pkg/build/cmd/artifactspage.tmpl.html -/pkg/build/cmd/exportversion.go -/pkg/server/wireexts_enterprise.go + +# Extensions /pkg/cmd/grafana-cli/runner/wireexts_enterprise.go +/pkg/server/wireexts_enterprise.go +/pkg/build/cmd/enterprise.go +/pkg/extensions/* +!/pkg/extensions/.keep !/pkg/extensions/main.go +!/pkg/extensions/enterprise_imports.go /public/app/extensions +!/public/app/extensions/.keep + + debug.test /examples/*/dist /packaging/**/*.rpm @@ -170,13 +177,13 @@ compilation-stats.json /e2e/benchmarks/**/results /e2e/build_results.zip /e2e/extensions +!/e2e/extensions/.keep /e2e/extensions-suite /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ /playwright/.auth/ - # grafana server /scripts/grafana-server/server.log diff --git a/.golangci.yml b/.golangci.yml index 9a49288fb2b..35b9bd1d4ec 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,5 +1,5 @@ run: - timeout: 10m + timeout: 15m concurrency: 10 allow-parallel-runners: true linters-settings: @@ -191,10 +191,10 @@ linters-settings: allow: [] deny: - pkg: github.com/grafana/grafana/pkg - desc: apps/investigation is not allowed to import grafana core + desc: apps/investigations is not allowed to import grafana core files: - - ./apps/investigation/* - - ./apps/investigation/**/* + - ./apps/investigations/* + - ./apps/investigations/**/* gocritic: enabled-checks: - ruleguard diff --git a/.ignore b/.ignore new file mode 100644 index 00000000000..01e1bb8d6e2 --- /dev/null +++ b/.ignore @@ -0,0 +1,22 @@ +# This is a trick to ignore files only by git but not by other tools +!/public/app/extensions +!/pkg/extensions/* +!/pkg/cmd/grafana-cli/runner/wireexts_enterprise.go +!/pkg/server/wireexts_enterprise.go +!/pkg/build/cmd/enterprise.go +!/pkg/extensions/* + +# Enterprise emails +!/emails/templates/enterprise_* +!/public/emails/enterprise_* + +# Enterprise reporting fonts +!/public/fonts/dejavu + +# Enterprise devenv +!/devenv/docker/blocks/grafana-enterprise +!/devenv/docker/blocks/saml-enterprise +# This is the new place of the block, but I leave the previous here for a while +!/devenv/docker/blocks/auth/saml-enterprise +!/devenv/docker/blocks/auth/signer +!/devenv/docker/blocks/spanner_tests \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 80bafb80307..67c4f82699b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -96,6 +96,15 @@ "NODE_ENV": "test" } }, + { + "name": "Debug ESLint", + "type": "node", + "request": "launch", + "runtimeExecutable": "yarn", + "runtimeArgs": ["run", "eslint", "${file}"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + }, { "name": "Debug Go test", "type": "go", diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bb3ea46a8e..5b7107fc70a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,407 @@ + + +# 11.5.2 (2025-02-18) + +### Features and enhancements + +- **Docker:** Use our own glibc 2.40 binaries [#99918](https://github.com/grafana/grafana/pull/99918), [@DanCech](https://github.com/DanCech) +- **TransformationFilter:** Include transformation outputs in transformation filtering options [#99878](https://github.com/grafana/grafana/pull/99878), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov) +- **grafana-ui:** Update InlineField error prop type to React.ReactNode [#100373](https://github.com/grafana/grafana/pull/100373), [@Clarity-89](https://github.com/Clarity-89) + +### Bug fixes + +- **Alerting:** Allow specifying uid for new rules added to groups [#100450](https://github.com/grafana/grafana/pull/100450), [@yuri-tceretian](https://github.com/yuri-tceretian) +- **Alerting:** Allow specifying uid for new rules added to groups [#100450](https://github.com/grafana/grafana/pull/100450), [@yuri-tceretian](https://github.com/yuri-tceretian) +- **Alerting:** Call RLock() before reading sendAlertsTo map [#99880](https://github.com/grafana/grafana/pull/99880), [@santihernandezc](https://github.com/santihernandezc) +- **Auth:** Fix redirect with JWT auth URL login [#100355](https://github.com/grafana/grafana/pull/100355), [@mgyongyosi](https://github.com/mgyongyosi) +- **AuthN:** Refetch user on "ErrUserAlreadyExists" [#100582](https://github.com/grafana/grafana/pull/100582), [@kalleep](https://github.com/kalleep) +- **Azure:** Correctly set application insights resource values [#99599](https://github.com/grafana/grafana/pull/99599), [@aangelisc](https://github.com/aangelisc) +- **CodeEditor:** Fix cursor alignment [#99863](https://github.com/grafana/grafana/pull/99863), [@ashharrison90](https://github.com/ashharrison90) +- **DashboardList:** Throttle the re-renders [#100046](https://github.com/grafana/grafana/pull/100046), [@bfmatei](https://github.com/bfmatei) +- **Dashboards:** Bring back scripted dashboards [#100633](https://github.com/grafana/grafana/pull/100633), [@dprokop](https://github.com/dprokop) +- **Plugin Metrics:** Eliminate data race in plugin metrics middleware [#100078](https://github.com/grafana/grafana/pull/100078), [@clord](https://github.com/clord) +- **RBAC:** Don't check folder access if `annotationPermissionUpdate` FT is enabled [#100117](https://github.com/grafana/grafana/pull/100117), [@IevaVasiljeva](https://github.com/IevaVasiljeva) + + + + +# 11.4.2 (2025-02-18) + +### Features and enhancements + +- **Docker:** Use our own glibc 2.40 binaries [#99924](https://github.com/grafana/grafana/pull/99924), [@DanCech](https://github.com/DanCech) + +### Bug fixes + +- **Auth:** Fix redirect with JWT auth URL login [#100494](https://github.com/grafana/grafana/pull/100494), [@mgyongyosi](https://github.com/mgyongyosi) +- **AuthN:** Refetch user on "ErrUserAlreadyExists" [#100585](https://github.com/grafana/grafana/pull/100585), [@kalleep](https://github.com/kalleep) +- **Azure:** Correctly set application insights resource values [#99598](https://github.com/grafana/grafana/pull/99598), [@aangelisc](https://github.com/aangelisc) +- **Dashboards:** Bring back scripted dashboards [#100629](https://github.com/grafana/grafana/pull/100629), [@dprokop](https://github.com/dprokop) +- **Plugin Metrics:** Eliminate data race in plugin metrics middleware [#100077](https://github.com/grafana/grafana/pull/100077), [@clord](https://github.com/clord) +- **RBAC:** Don't check folder access if `annotationPermissionUpdate` FT is enabled [#100116](https://github.com/grafana/grafana/pull/100116), [@IevaVasiljeva](https://github.com/IevaVasiljeva) + + + + +# 11.3.4 (2025-02-18) + +### Features and enhancements + +- **Docker:** Use our own glibc 2.40 binaries [#99923](https://github.com/grafana/grafana/pull/99923), [@DanCech](https://github.com/DanCech) + +### Bug fixes + +- **Auth:** Fix redirect with JWT auth URL login [#100495](https://github.com/grafana/grafana/pull/100495), [@mgyongyosi](https://github.com/mgyongyosi) +- **Azure:** Correctly set application insights resource values [#99597](https://github.com/grafana/grafana/pull/99597), [@aangelisc](https://github.com/aangelisc) +- **Dashboards:** Bring back scripted dashboards [#100627](https://github.com/grafana/grafana/pull/100627), [@dprokop](https://github.com/dprokop) +- **Plugin Metrics:** Eliminate data race in plugin metrics middleware [#100076](https://github.com/grafana/grafana/pull/100076), [@clord](https://github.com/clord) + + + + +# 11.2.7 (2025-02-18) + +### Features and enhancements + +- **Docker:** Use our own glibc 2.40 binaries [#99922](https://github.com/grafana/grafana/pull/99922), [@DanCech](https://github.com/DanCech) + +### Bug fixes + +- **Azure:** Correctly set application insights resource values [#99596](https://github.com/grafana/grafana/pull/99596), [@aangelisc](https://github.com/aangelisc) + + + + +# 11.1.12 (2025-02-18) + +### Features and enhancements + +- **Docker:** Use our own glibc 2.40 binaries [#99917](https://github.com/grafana/grafana/pull/99917), [@DanCech](https://github.com/DanCech) + +### Bug fixes + +- **Azure:** Correctly set application insights resource values [#99595](https://github.com/grafana/grafana/pull/99595), [@aangelisc](https://github.com/aangelisc) + + + + +# 11.0.11 (2025-02-18) + +### Features and enhancements + +- **Docker:** Use our own glibc 2.40 binaries [#100730](https://github.com/grafana/grafana/pull/100730), [@DanCech](https://github.com/DanCech) + +### Bug fixes + +- **Azure:** Correctly set application insights resource values [#99594](https://github.com/grafana/grafana/pull/99594), [@aangelisc](https://github.com/aangelisc) + + + + +# 10.4.16 (2025-02-18) + +### Features and enhancements + +- **Docker:** Use our own glibc 2.40 binaries [#99920](https://github.com/grafana/grafana/pull/99920), [@DanCech](https://github.com/DanCech) + +### Bug fixes + +- **Dashboard:** Fix for overwriting an edited dashboard in the old architecture [#100288](https://github.com/grafana/grafana/pull/100288), [@bfmatei](https://github.com/bfmatei) + + + + +# 11.5.1 (2025-02-03) + +### Bug fixes + +- **CodeEditor:** Fix cursor alignment [#99090](https://github.com/grafana/grafana/pull/99090), [@ashharrison90](https://github.com/ashharrison90) +- **TransformationFilter**: Include transformation outputs in transformation filtering options: Include transformation outputs in transformation filtering options [#98323](https://github.com/grafana/grafana/pull/98323), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov) + + + + +# 11.5.0 (2025-01-28) + +### Features and enhancements + +- ** CloudMigration:** Create authapi service [#96581](https://github.com/grafana/grafana/pull/96581), [@leandro-deveikis](https://github.com/leandro-deveikis) +- **Alerting:** Add new button for exporting new alert rule in HCL format [#96785](https://github.com/grafana/grafana/pull/96785), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Add option to show inactive alerts in alert list panel [#96888](https://github.com/grafana/grafana/pull/96888), [@bradleypettit](https://github.com/bradleypettit) +- **Alerting:** Add state_periodic_save_batch_size config option [#98019](https://github.com/grafana/grafana/pull/98019), [@alexander-akhmetov](https://github.com/alexander-akhmetov) +- **Alerting:** Change default for max_attempts to 3. [#97461](https://github.com/grafana/grafana/pull/97461), [@stevesg](https://github.com/stevesg) +- **Alerting:** Consume k8s API for notification policies tree [#96147](https://github.com/grafana/grafana/pull/96147), [@konrad147](https://github.com/konrad147) +- **Alerting:** Enable flag alertingApiServer by default [#98282](https://github.com/grafana/grafana/pull/98282), [@yuri-tceretian](https://github.com/yuri-tceretian) +- **Alerting:** Explore button in Insights view [#96496](https://github.com/grafana/grafana/pull/96496), [@ppcano](https://github.com/ppcano) +- **Alerting:** Improve performance ash page [#97619](https://github.com/grafana/grafana/pull/97619), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Make alert rule policies preview use k8s API [#97070](https://github.com/grafana/grafana/pull/97070), [@tomratcliffe](https://github.com/tomratcliffe) +- **Alerting:** Return default builtin templates in k8s templategroup API and UI [#96330](https://github.com/grafana/grafana/pull/96330), [@JacobsonMT](https://github.com/JacobsonMT) +- **Alerting:** Simplify notification step [#96430](https://github.com/grafana/grafana/pull/96430), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Update state manager to take image only once per rule evaluation [#98289](https://github.com/grafana/grafana/pull/98289), [@yuri-tceretian](https://github.com/yuri-tceretian) +- **Analytics Views:** Deprecate :dashboardID endpoints in favor of uid/:dashboardUID (Enterprise) +- **Analytics:** Summaries: Deprecate dashboard_id endpoints in favor of dashboard_uid (Enterprise) +- **Announcement Banners:** Enable feature for all cloud tiers (Enterprise) +- **Announcement banner:** Remove feature toggle [#98782](https://github.com/grafana/grafana/pull/98782), [@Clarity-89](https://github.com/Clarity-89) +- **Announcement banner:** Remove feature toggle (Enterprise) +- **Announcement banner:** Sort by last updated (Enterprise) +- **Auth:** Return error when retries have been exhausted for OAuth token refresh [#98034](https://github.com/grafana/grafana/pull/98034), [@mgyongyosi](https://github.com/mgyongyosi) +- **Azure Monitor:** Add a feature flag to toggle user auth for Azure Monitor only [#96858](https://github.com/grafana/grafana/pull/96858), [@adamyeats](https://github.com/adamyeats) +- **Azure:** Improve Azure Prometheus exemplars UI/UX [#97198](https://github.com/grafana/grafana/pull/97198), [@aangelisc](https://github.com/aangelisc) +- **Azure:** Unify credentials in frontend for Prometheus [#96568](https://github.com/grafana/grafana/pull/96568), [@yjsong11](https://github.com/yjsong11) +- **Chore:** Bump Go to 1.23.4 [#98853](https://github.com/grafana/grafana/pull/98853), [@Proximyst](https://github.com/Proximyst) +- **Chore:** Bump Go to 1.23.4 (Enterprise) +- **Chore:** Remove experimental Storage UI [#96887](https://github.com/grafana/grafana/pull/96887), [@ryantxu](https://github.com/ryantxu) +- **Chore:** Update to node 22 [#97779](https://github.com/grafana/grafana/pull/97779), [@ashharrison90](https://github.com/ashharrison90) +- **CloudMigrations:** Enable feature toggle by default in 11.5 [#98686](https://github.com/grafana/grafana/pull/98686), [@mmandrus](https://github.com/mmandrus) +- **CloudMigrations:** Introduce RBAC role for migration assistant [#98588](https://github.com/grafana/grafana/pull/98588), [@macabu](https://github.com/macabu) +- **CloudWatch:** Add OpenSearch PPL and SQL support in Logs Insights [#97508](https://github.com/grafana/grafana/pull/97508), [@idastambuk](https://github.com/idastambuk) +- **CloudWatch:** Batch different time ranges separately [#98230](https://github.com/grafana/grafana/pull/98230), [@iwysiu](https://github.com/iwysiu) +- **Cloudwatch:** Accept empty string for logstimeout and mark errors downstream [#96947](https://github.com/grafana/grafana/pull/96947), [@iwysiu](https://github.com/iwysiu) +- **Cloudwatch:** Update grafana-aws-sdk for AWS/AmplifyHosting metrics [#97799](https://github.com/grafana/grafana/pull/97799), [@iwysiu](https://github.com/iwysiu) +- **Dashboard Scene:** Shows usages in variables list [#96000](https://github.com/grafana/grafana/pull/96000), [@harisrozajac](https://github.com/harisrozajac) +- **Dashboards:** Add option to specify explicit percent change text size for stat panels [#96952](https://github.com/grafana/grafana/pull/96952), [@XZCendence](https://github.com/XZCendence) +- **Dashboards:** Allow DashboardDS subqueries in MixedDS [#97116](https://github.com/grafana/grafana/pull/97116), [@mdvictor](https://github.com/mdvictor) +- **Dashboards:** Update docs of the `overwrite` param in Save Dashboard API Call [#97011](https://github.com/grafana/grafana/pull/97011), [@ArturWierzbicki](https://github.com/ArturWierzbicki) +- **Datasources:** Add toggle to control default behaviour of 'Manage alerts via Alerts UI' toggle [#98441](https://github.com/grafana/grafana/pull/98441), [@macabu](https://github.com/macabu) +- **Datasources:** Allow clearing trace to logs, metrics and profiles datasource pickers [#96554](https://github.com/grafana/grafana/pull/96554), [@adrapereira](https://github.com/adrapereira) +- **Docker:** Don't use legacy ENV syntax [#93218](https://github.com/grafana/grafana/pull/93218), [@simPod](https://github.com/simPod) +- **Elasticsearch:** Health endpoint should handle http errors [#96803](https://github.com/grafana/grafana/pull/96803), [@iwysiu](https://github.com/iwysiu) +- **Elasticsearch:** Use \_field_caps instead of \_mapping to get fields [#97607](https://github.com/grafana/grafana/pull/97607), [@iwysiu](https://github.com/iwysiu) +- **Explore Profiles:** Preinstall for onprem Grafana instances [#97775](https://github.com/grafana/grafana/pull/97775), [@ifrost](https://github.com/ifrost) +- **Explore metrics:** Consolidate filters with the OTel experience [#98371](https://github.com/grafana/grafana/pull/98371), [@bohandley](https://github.com/bohandley) +- **Explore:** Show links to queryless apps [#96625](https://github.com/grafana/grafana/pull/96625), [@ifrost](https://github.com/ifrost) +- **Expressions:** Add notification for Strict Mode behavior in Reduce component [#97224](https://github.com/grafana/grafana/pull/97224), [@shubhankarunhale](https://github.com/shubhankarunhale) +- **Faro:** Improve performance of TRACKING_URLS regex [#98022](https://github.com/grafana/grafana/pull/98022), [@kpelelis](https://github.com/kpelelis) +- **FeatureToggles:** Make newFiltersUI feature toggle generally available [#97460](https://github.com/grafana/grafana/pull/97460), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov) +- **Features:** Remove cloudwatchMetricInsightsCrossAccount feature toggle [#98826](https://github.com/grafana/grafana/pull/98826), [@idastambuk](https://github.com/idastambuk) +- **Frontend Sandbox:** Add switch to toggle plugins frontend sandbox via catalog UI (Enterprise) +- **Graphite:** Set `maxDataPoints` based on user value in alerting [#97178](https://github.com/grafana/grafana/pull/97178), [@aangelisc](https://github.com/aangelisc) +- **Licensing:** Tidy up license token database code (Enterprise) +- **LoginAttempt:** Add setting to control max number of attempts before user login gets locked [#97091](https://github.com/grafana/grafana/pull/97091), [@kalleep](https://github.com/kalleep) +- **Logs Panel:** Add infinite scrolling support for Dashboards and Apps [#97095](https://github.com/grafana/grafana/pull/97095), [@matyax](https://github.com/matyax) +- **Logs Panel:** Allow text selection without changing Log Details state [#96995](https://github.com/grafana/grafana/pull/96995), [@matyax](https://github.com/matyax) +- **Logs Panel:** Limit displayed characters to MAX_CHARACTERS [#96997](https://github.com/grafana/grafana/pull/96997), [@matyax](https://github.com/matyax) +- **Logs:** Added option to show the log line body when displayed fields are used [#97209](https://github.com/grafana/grafana/pull/97209), [@matyax](https://github.com/matyax) +- **Logs:** Added support to disable and re-enable the popover menu [#98254](https://github.com/grafana/grafana/pull/98254), [@matyax](https://github.com/matyax) +- **Logs:** Allow scroll to reach the bottom of the log list before loading more [#96668](https://github.com/grafana/grafana/pull/96668), [@matyax](https://github.com/matyax) +- **Loki:** Added support for disabled operations in Query Builder [#96751](https://github.com/grafana/grafana/pull/96751), [@matyax](https://github.com/matyax) +- **Loki:** Added support to show label types in Log Details [#97284](https://github.com/grafana/grafana/pull/97284), [@matyax](https://github.com/matyax) +- **Loki:** Allow regex in `label` derived field [#96609](https://github.com/grafana/grafana/pull/96609), [@svennergr](https://github.com/svennergr) +- **Loki:** Hide internal labels [#97323](https://github.com/grafana/grafana/pull/97323), [@svennergr](https://github.com/svennergr) +- **Loki:** Sync query direction with sort order in Explore and Dashboards [#98722](https://github.com/grafana/grafana/pull/98722), [@matyax](https://github.com/matyax) +- **OAuth:** Support client_secret_jwt for oauth providers when doing token exchange [#95455](https://github.com/grafana/grafana/pull/95455), [@naizerjohn-ms](https://github.com/naizerjohn-ms) +- **OAuth:** Use the attached external session data in OAuthToken and OAuthTokenSync [#96655](https://github.com/grafana/grafana/pull/96655), [@mgyongyosi](https://github.com/mgyongyosi) +- **Org Selection:** Show correct selected org when select is open [#96601](https://github.com/grafana/grafana/pull/96601), [@yincongcyincong](https://github.com/yincongcyincong) +- **PDF:** Add new zoom options (Enterprise) +- **Plugin Extensions:** Only load app plugins when necessary [#86624](https://github.com/grafana/grafana/pull/86624), [@leventebalogh](https://github.com/leventebalogh) +- **Plugins:** Add token to gcom requests [#96261](https://github.com/grafana/grafana/pull/96261), [@oshirohugo](https://github.com/oshirohugo) +- **Plugins:** Add token to gcom requests (Enterprise) +- **Plugins:** Disable version install when angular version is not supported [#97189](https://github.com/grafana/grafana/pull/97189), [@oshirohugo](https://github.com/oshirohugo) +- **Plugins:** Disable version installation for specific plugin types [#98597](https://github.com/grafana/grafana/pull/98597), [@oshirohugo](https://github.com/oshirohugo) +- **Plugins:** Update to latest go plugin SDK (v0.260.3) w/ arrow v18 [#97561](https://github.com/grafana/grafana/pull/97561), [@ryantxu](https://github.com/ryantxu) +- **Plugins:** Use grafana-com sso_api_token [#97096](https://github.com/grafana/grafana/pull/97096), [@oshirohugo](https://github.com/oshirohugo) +- **Plugins:** Use grafana-com sso_api_token (Enterprise) +- **Prometheus datasource:** Show info annotations in the UI [#97978](https://github.com/grafana/grafana/pull/97978), [@zenador](https://github.com/zenador) +- **Prometheus:** Improve handling of special chars in label values [#96067](https://github.com/grafana/grafana/pull/96067), [@NWRichmond](https://github.com/NWRichmond) +- **PublicDashboards:** Remove publicDashboards FF [#96578](https://github.com/grafana/grafana/pull/96578), [@juanicabanas](https://github.com/juanicabanas) +- **Reporting:** Add allow list email domain configuration (Enterprise) +- **Reporting:** Include the apiserver by default and deprecated internal ids (Enterprise) +- **RuntimeDataSource:** Support in core for runtime registered data sources [#93956](https://github.com/grafana/grafana/pull/93956), [@torkelo](https://github.com/torkelo) +- **SAML:** Add the ability to specify EntityID (Enterprise) +- **SAML:** Implement correct SLO with NameID and SessionIndex handling (Enterprise) +- **Security:** Update to Go 1.23.5 - Backport to v11.5.x [#99122](https://github.com/grafana/grafana/pull/99122), [@Proximyst](https://github.com/Proximyst) +- **Security:** Update to Go 1.23.5 - Backport to v11.5.x (Enterprise) +- **Snapshots:** Add RBAC roles for creating and deleting [#96126](https://github.com/grafana/grafana/pull/96126), [@evictorero](https://github.com/evictorero) +- **Storage:** Removes integration tests for MySQL 5.7 since it is EOL [#98013](https://github.com/grafana/grafana/pull/98013), [@inf0rmer](https://github.com/inf0rmer) +- **Tempo:** Add support for TraceQL Metrics exemplars [#96859](https://github.com/grafana/grafana/pull/96859), [@adrapereira](https://github.com/adrapereira) +- **Tempo:** Honor datasource TLS settings for gRPC requests [#97484](https://github.com/grafana/grafana/pull/97484), [@mdisibio](https://github.com/mdisibio) +- **Tempo:** Improve handling of multiple values in the Search tab query generation [#98427](https://github.com/grafana/grafana/pull/98427), [@adrapereira](https://github.com/adrapereira) +- **ToolbarButton:** Auto width on smaller screen sizes [#96023](https://github.com/grafana/grafana/pull/96023), [@yincongcyincong](https://github.com/yincongcyincong) +- **Trace View:** Set span filters as panel options [#98328](https://github.com/grafana/grafana/pull/98328), [@adrapereira](https://github.com/adrapereira) +- **TransformationFilter:** Implement RefID multi picker [#96841](https://github.com/grafana/grafana/pull/96841), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov) +- **Transformations:** Add Delimiter format option to Extract fields [#97340](https://github.com/grafana/grafana/pull/97340), [@tskarhed](https://github.com/tskarhed) +- **Transformations:** Add RegExp option to Extract fields transformer [#96593](https://github.com/grafana/grafana/pull/96593), [@leeoniya](https://github.com/leeoniya) +- **Transformations:** GroupToMatrix add 0 as special value [#97642](https://github.com/grafana/grafana/pull/97642), [@tskarhed](https://github.com/tskarhed) +- **Zipkin:** Run queries through backend [#97754](https://github.com/grafana/grafana/pull/97754), [@ivanahuckova](https://github.com/ivanahuckova) + +### Bug fixes + +- **Alerting:** AlertingQueryRunner should skip descendant nodes of invalid queries [#97528](https://github.com/grafana/grafana/pull/97528), [@gillesdemey](https://github.com/gillesdemey) +- **Alerting:** Allow notification policy filters to match quoted matchers [#98525](https://github.com/grafana/grafana/pull/98525), [@gillesdemey](https://github.com/gillesdemey) +- **Alerting:** Fix alert rule list view summaries [#98433](https://github.com/grafana/grafana/pull/98433), [@yincongcyincong](https://github.com/yincongcyincong) +- **Alerting:** Fix alert rules unpausing after moving rule to different folder [#97580](https://github.com/grafana/grafana/pull/97580), [@santihernandezc](https://github.com/santihernandezc) +- **Alerting:** Fix ash not showing history graph in firefox [#98128](https://github.com/grafana/grafana/pull/98128), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Fix bug when saving a rule more than once [#96658](https://github.com/grafana/grafana/pull/96658), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Fix data-testid in RuleEditorSection [#97473](https://github.com/grafana/grafana/pull/97473), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Fix filtering rule group permissions based on their full path (Enterprise) +- **Alerting:** Fix go template parsing [#97145](https://github.com/grafana/grafana/pull/97145), [@konrad147](https://github.com/konrad147) +- **Alerting:** Fix label escaping in rule export [#97985](https://github.com/grafana/grafana/pull/97985), [@moustafab](https://github.com/moustafab) +- **Alerting:** Fix missing instances and history when Grafana rule is stored in folder with / [#97956](https://github.com/grafana/grafana/pull/97956), [@gillesdemey](https://github.com/gillesdemey) +- **Alerting:** Fix navigating to URLs with "%25" [#96992](https://github.com/grafana/grafana/pull/96992), [@gillesdemey](https://github.com/gillesdemey) +- **Alerting:** Fix no-change scenario in provisioning rule update API [#98389](https://github.com/grafana/grafana/pull/98389), [@alexander-akhmetov](https://github.com/alexander-akhmetov) +- **Alerting:** Fix not being able to remove a reducer when using range query [#97757](https://github.com/grafana/grafana/pull/97757), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Fix recording rules rendering simplified condition [#97497](https://github.com/grafana/grafana/pull/97497), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Fix removing reducer when inital value is instant [#97054](https://github.com/grafana/grafana/pull/97054), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Fix simplified query step [#97046](https://github.com/grafana/grafana/pull/97046), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Fix slack image uploading to use new api [#97817](https://github.com/grafana/grafana/pull/97817), [@moustafab](https://github.com/moustafab) +- **Alerting:** Fix terraform export of notification policy [#98429](https://github.com/grafana/grafana/pull/98429), [@moustafab](https://github.com/moustafab) +- **Alerting:** Fix updating condition when refId changes [#97753](https://github.com/grafana/grafana/pull/97753), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Fix using stacks- prefix instead of stack- for checking the namespace in boot data [#97492](https://github.com/grafana/grafana/pull/97492), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Anonymous User:** Adds validator service for anonymous users (Enterprise) +- **Auth:** Fix SAML user IsExternallySynced not being set correctly [#98487](https://github.com/grafana/grafana/pull/98487), [@volcanonoodle](https://github.com/volcanonoodle) +- **Azure Monitor:** Add safety around usage of frame.Meta.Custom struct [#97766](https://github.com/grafana/grafana/pull/97766), [@adamyeats](https://github.com/adamyeats) +- **Azure/GCM:** Improve error display [#96921](https://github.com/grafana/grafana/pull/96921), [@aangelisc](https://github.com/aangelisc) +- **CloudWatch:** Fix conditions for fetching wildcards [#98648](https://github.com/grafana/grafana/pull/98648), [@iwysiu](https://github.com/iwysiu) +- **CloudWatch:** Fix interpolation of log groups when fetching fields [#98054](https://github.com/grafana/grafana/pull/98054), [@idastambuk](https://github.com/idastambuk) +- **Dashboard:** Fixes issue with compatability of old DashboardModel.annotations [#97328](https://github.com/grafana/grafana/pull/97328), [@torkelo](https://github.com/torkelo) +- **Dashboards:** Fix issue where filtered panels would not react to variable changes [#98718](https://github.com/grafana/grafana/pull/98718), [@oscarkilhed](https://github.com/oscarkilhed) +- **Dashboards:** Fixes week relative time ranges when weekStart was changed [#98167](https://github.com/grafana/grafana/pull/98167), [@torkelo](https://github.com/torkelo) +- **Dashboards:** Panel react for `timeFrom` and `timeShift` changes using variables [#98510](https://github.com/grafana/grafana/pull/98510), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov) +- **DateTimePicker:** Fixes issue with date picker showing invalid date [#97888](https://github.com/grafana/grafana/pull/97888), [@torkelo](https://github.com/torkelo) +- **Fix:** Add support for datasource variable queries [#98098](https://github.com/grafana/grafana/pull/98098), [@sunker](https://github.com/sunker) +- **Fix:** Do not fetch Orgs if the user is authenticated by apikey/sa or render key [#97162](https://github.com/grafana/grafana/pull/97162), [@mgyongyosi](https://github.com/mgyongyosi) +- **Fix:** Double encoding of URLs when using data proxy [#98494](https://github.com/grafana/grafana/pull/98494), [@s4kh](https://github.com/s4kh) +- **Font:** Disable contextual font ligatures [#98521](https://github.com/grafana/grafana/pull/98521), [@ashharrison90](https://github.com/ashharrison90) +- **GrafanaUI:** Fix inconsistent controlled/uncontrolled state in AutoSizeInput [#96696](https://github.com/grafana/grafana/pull/96696), [@joshhunt](https://github.com/joshhunt) +- **GrafanaUI:** Revert: Fix inconsistent controlled/uncontrolled state in AutoSizeInput [#97551](https://github.com/grafana/grafana/pull/97551), [@itsmylife](https://github.com/itsmylife) +- **InfluxDB:** Adhoc filters can use template vars as values [#98567](https://github.com/grafana/grafana/pull/98567), [@bossinc](https://github.com/bossinc) +- **Library Panel:** Fix issue where library panels did not display panel links. [#98655](https://github.com/grafana/grafana/pull/98655), [@yincongcyincong](https://github.com/yincongcyincong) +- **LibraryPanel:** Fallback to panel title if library panel title is not set [#99411](https://github.com/grafana/grafana/pull/99411), [@ivanortegaalba](https://github.com/ivanortegaalba) +- **Loki:** Fix a bug when reading frames without values but warnings [#97197](https://github.com/grafana/grafana/pull/97197), [@svennergr](https://github.com/svennergr) +- **Loki:** Only hide a set of labels instead of every label starting with `__` [#98730](https://github.com/grafana/grafana/pull/98730), [@svennergr](https://github.com/svennergr) +- **Org:** Fix redirection logic to work consistently [#96521](https://github.com/grafana/grafana/pull/96521), [@yincongcyincong](https://github.com/yincongcyincong) +- **Panel inspect:** Fix file names of data download included uninterpolated variable names. [#98832](https://github.com/grafana/grafana/pull/98832), [@alexrosenfeld10](https://github.com/alexrosenfeld10) +- **Scenes:** Upgrade to 5.36.3 [#98661](https://github.com/grafana/grafana/pull/98661), [@ivanortegaalba](https://github.com/ivanortegaalba) +- **Snapshot:** Show proper breadcrumb path [#98806](https://github.com/grafana/grafana/pull/98806), [@ashharrison90](https://github.com/ashharrison90) +- **Time Picker:** Fix "Fiscal year start month" selection behaviour [#98576](https://github.com/grafana/grafana/pull/98576), [@ashharrison90](https://github.com/ashharrison90) +- **Unified Storage:** Add support for verify-full in postgres [#96825](https://github.com/grafana/grafana/pull/96825), [@chaudyg](https://github.com/chaudyg) +- **Unified Storage:** Use tls preferred when grafana db using ssl [#97378](https://github.com/grafana/grafana/pull/97378), [@owensmallwood](https://github.com/owensmallwood) +- **Usage Insights:** Fix usage insight errors being logged as [object Object] [#93502](https://github.com/grafana/grafana/pull/93502), [@mmandrus](https://github.com/mmandrus) + +### Breaking changes + +- **Loki:** Default to `/labels` API with `query` param instead of `/series` API [#97935](https://github.com/grafana/grafana/pull/97935), [@svennergr](https://github.com/svennergr) + +### Plugin development fixes & changes + +- **Grafana UI:** Re-add react-router-dom as a dependency [#97540](https://github.com/grafana/grafana/pull/97540), [@leventebalogh](https://github.com/leventebalogh) + + + + +# 11.4.1 (2025-01-28) + +### Features and enhancements + +- **Security:** Update to Go 1.23.5 - Backport to v11.4.x [#99123](https://github.com/grafana/grafana/pull/99123), [@Proximyst](https://github.com/Proximyst) +- **Security:** Update to Go 1.23.5 - Backport to v11.4.x (Enterprise) + +### Bug fixes + +- **Alerting:** AlertingQueryRunner should skip descendant nodes of invalid queries [#97830](https://github.com/grafana/grafana/pull/97830), [@gillesdemey](https://github.com/gillesdemey) +- **Alerting:** Fix alert rules unpausing after moving rule to different folder [#97583](https://github.com/grafana/grafana/pull/97583), [@santihernandezc](https://github.com/santihernandezc) +- **Alerting:** Fix label escaping in rule export [#98649](https://github.com/grafana/grafana/pull/98649), [@moustafab](https://github.com/moustafab) +- **Alerting:** Fix slack image uploading to use new api [#98066](https://github.com/grafana/grafana/pull/98066), [@moustafab](https://github.com/moustafab) +- **Azure/GCM:** Improve error display [#97594](https://github.com/grafana/grafana/pull/97594), [@aangelisc](https://github.com/aangelisc) +- **Dashboards:** Fix issue where filtered panels would not react to variable changes [#98734](https://github.com/grafana/grafana/pull/98734), [@oscarkilhed](https://github.com/oscarkilhed) +- **Dashboards:** Fixes issue with panel header showing even when hide time override was enabled [#98747](https://github.com/grafana/grafana/pull/98747), [@torkelo](https://github.com/torkelo) +- **Dashboards:** Fixes week relative time ranges when weekStart was changed [#98269](https://github.com/grafana/grafana/pull/98269), [@torkelo](https://github.com/torkelo) +- **Dashboards:** Panel react for `timeFrom` and `timeShift` changes using variables [#98659](https://github.com/grafana/grafana/pull/98659), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov) +- **DateTimePicker:** Fixes issue with date picker showing invalid date [#97971](https://github.com/grafana/grafana/pull/97971), [@torkelo](https://github.com/torkelo) +- **Fix:** Add support for datasource variable queries [#98119](https://github.com/grafana/grafana/pull/98119), [@sunker](https://github.com/sunker) +- **InfluxDB:** Adhoc filters can use template vars as values [#98786](https://github.com/grafana/grafana/pull/98786), [@bossinc](https://github.com/bossinc) +- **LibraryPanel:** Fallback to panel title if library panel title is not set [#99410](https://github.com/grafana/grafana/pull/99410), [@ivanortegaalba](https://github.com/ivanortegaalba) + +### Plugin development fixes & changes + +- **Grafana UI:** Re-add react-router-dom as a dependency [#98422](https://github.com/grafana/grafana/pull/98422), [@leventebalogh](https://github.com/leventebalogh) + + + + +# 11.3.3 (2025-01-28) + +### Features and enhancements + +- **Azure Monitor:** Add a feature flag to toggle user auth for Azure Monitor only [#97576](https://github.com/grafana/grafana/pull/97576), [@adamyeats](https://github.com/adamyeats) +- **Security:** Update to Go 1.23.5 - Backport to v11.3.x [#99124](https://github.com/grafana/grafana/pull/99124), [@Proximyst](https://github.com/Proximyst) +- **Security:** Update to Go 1.23.5 - Backport to v11.3.x (Enterprise) + +### Bug fixes + +- **Alerting:** AlertingQueryRunner should skip descendant nodes of invalid queries [#97829](https://github.com/grafana/grafana/pull/97829), [@gillesdemey](https://github.com/gillesdemey) +- **Azure/GCM:** Improve error display [#97593](https://github.com/grafana/grafana/pull/97593), [@aangelisc](https://github.com/aangelisc) +- **Dashboard:** Fixes issue with compatability of old DashboardModel.annotations [#97467](https://github.com/grafana/grafana/pull/97467), [@torkelo](https://github.com/torkelo) +- **Dashboards:** Fix issue where filtered panels would not react to variable changes [#98733](https://github.com/grafana/grafana/pull/98733), [@oscarkilhed](https://github.com/oscarkilhed) +- **Dashboards:** Fixes issue with panel header showing even when hide time override was enabled [#97389](https://github.com/grafana/grafana/pull/97389), [@torkelo](https://github.com/torkelo) +- **Dashboards:** Fixes week relative time ranges when weekStart was changed [#98268](https://github.com/grafana/grafana/pull/98268), [@torkelo](https://github.com/torkelo) +- **DateTimePicker:** Fixes issue with date picker showing invalid date [#97970](https://github.com/grafana/grafana/pull/97970), [@torkelo](https://github.com/torkelo) +- **Fix:** Add support for datasource variable queries [#98118](https://github.com/grafana/grafana/pull/98118), [@sunker](https://github.com/sunker) +- **InfluxDB:** Adhoc filters can use template vars as values [#98785](https://github.com/grafana/grafana/pull/98785), [@bossinc](https://github.com/bossinc) +- **Unified Storage:** Use tls preferred when grafana db using ssl [#97379](https://github.com/grafana/grafana/pull/97379), [@owensmallwood](https://github.com/owensmallwood) + +### Plugin development fixes & changes + +- **Grafana UI:** Re-add react-router-dom as a dependency [#98421](https://github.com/grafana/grafana/pull/98421), [@leventebalogh](https://github.com/leventebalogh) + + + + +# 11.2.6 (2025-01-28) + +### Features and enhancements + +- **Azure Monitor:** Add a feature flag to toggle user auth for Azure Monitor only [#97565](https://github.com/grafana/grafana/pull/97565), [@adamyeats](https://github.com/adamyeats) +- **Security:** Update to Go 1.22.11 - Backport to v11.2.x [#99125](https://github.com/grafana/grafana/pull/99125), [@Proximyst](https://github.com/Proximyst) +- **Security:** Update to Go 1.22.11 - Backport to v11.2.x (Enterprise) + +### Bug fixes + +- **Azure/GCM:** Improve error display [#97591](https://github.com/grafana/grafana/pull/97591), [@aangelisc](https://github.com/aangelisc) + + + + +# 11.1.11 (2025-01-28) + +### Features and enhancements + +- **Security:** Update to Go 1.22.11 - Backport to v11.1.x [#99126](https://github.com/grafana/grafana/pull/99126), [@Proximyst](https://github.com/Proximyst) +- **Security:** Update to Go 1.22.11 - Backport to v11.1.x (Enterprise) + +### Bug fixes + +- **Azure/GCM:** Improve error display [#97595](https://github.com/grafana/grafana/pull/97595), [@aangelisc](https://github.com/aangelisc) + + + + +# 11.0.10 (2025-01-28) + +### Features and enhancements + +- **Security:** Update to Go 1.22.11 - Backport to v11.0.x [#99127](https://github.com/grafana/grafana/pull/99127), [@Proximyst](https://github.com/Proximyst) +- **Security:** Update to Go 1.22.11 - Backport to v11.0.x (Enterprise) + +### Bug fixes + +- **Azure/GCM:** Improve error display [#97592](https://github.com/grafana/grafana/pull/97592), [@aangelisc](https://github.com/aangelisc) + + + + +# 10.4.15 (2025-01-28) + +### Features and enhancements + +- **Security:** Update to Go 1.22.11 - Backport to v10.4.x [#99128](https://github.com/grafana/grafana/pull/99128), [@Proximyst](https://github.com/Proximyst) +- **Security:** Update to Go 1.22.11 - Backport to v10.4.x (Enterprise) + +### Bug fixes + +- **Azure/GCM:** Improve error display [#97590](https://github.com/grafana/grafana/pull/97590), [@aangelisc](https://github.com/aangelisc) + + # 11.4.0 (2024-12-05) diff --git a/Dockerfile b/Dockerfile index 7f05689eef2..09cba46da26 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,19 @@ +# syntax=docker/dockerfile:1 -ARG BASE_IMAGE=alpine:3.20 +# to maintain formatting of multiline commands in vscode, add the following to settings.json: +# "docker.languageserver.formatter.ignoreMultilineInstructions": true + +ARG BASE_IMAGE=alpine:3.21 ARG JS_IMAGE=node:22-alpine ARG JS_PLATFORM=linux/amd64 ARG GO_IMAGE=golang:1.23.5-alpine +# Default to building locally ARG GO_SRC=go-builder ARG JS_SRC=js-builder -FROM --platform=${JS_PLATFORM} ${JS_IMAGE} as js-builder +# Javascript build stage +FROM --platform=${JS_PLATFORM} ${JS_IMAGE} AS js-builder ENV NODE_OPTIONS=--max_old_space_size=8000 @@ -32,7 +38,8 @@ COPY emails emails ENV NODE_ENV=production RUN yarn build -FROM ${GO_IMAGE} as go-builder +# Golang build stage +FROM ${GO_IMAGE} AS go-builder ARG COMMIT_SHA="" ARG BUILD_BRANCH="" @@ -55,24 +62,24 @@ COPY go.* ./ COPY .bingo .bingo # Include vendored dependencies -COPY pkg/util/xorm/go.* pkg/util/xorm/ -COPY pkg/apiserver/go.* pkg/apiserver/ -COPY pkg/apimachinery/go.* pkg/apimachinery/ -COPY pkg/build/go.* pkg/build/ -COPY pkg/build/wire/go.* pkg/build/wire/ -COPY pkg/promlib/go.* pkg/promlib/ -COPY pkg/storage/unified/resource/go.* pkg/storage/unified/resource/ -COPY pkg/storage/unified/apistore/go.* pkg/storage/unified/apistore/ -COPY pkg/semconv/go.* pkg/semconv/ -COPY pkg/aggregator/go.* pkg/aggregator/ -COPY apps/playlist/go.* apps/playlist/ -COPY apps/investigation/go.* apps/investigation/ -COPY apps/advisor/go.* apps/advisor/ +COPY pkg/util/xorm pkg/util/xorm +COPY pkg/apiserver pkg/apiserver +COPY pkg/apimachinery pkg/apimachinery +COPY pkg/build pkg/build +COPY pkg/build/wire pkg/build/wire +COPY pkg/promlib pkg/promlib +COPY pkg/storage/unified/resource pkg/storage/unified/resource +COPY pkg/storage/unified/apistore pkg/storage/unified/apistore +COPY pkg/semconv pkg/semconv +COPY pkg/aggregator pkg/aggregator +COPY apps/playlist apps/playlist +COPY apps/investigations apps/investigations +COPY apps/advisor apps/advisor COPY apps apps COPY kindsv2 kindsv2 -COPY apps/alerting/notifications/go.* apps/alerting/notifications/ -COPY pkg/codegen/go.* pkg/codegen/ -COPY pkg/plugins/codegen/go.* pkg/plugins/codegen/ +COPY apps/alerting/notifications apps/alerting/notifications +COPY pkg/codegen pkg/codegen +COPY pkg/plugins/codegen pkg/plugins/codegen RUN go mod download RUN if [[ "$BINGO" = "true" ]]; then \ @@ -97,7 +104,8 @@ ENV BUILD_BRANCH=${BUILD_BRANCH} RUN make build-go GO_BUILD_TAGS=${GO_BUILD_TAGS} WIRE_TAGS=${WIRE_TAGS} -FROM ${BASE_IMAGE} as tgz-builder +# From-tarball build stage +FROM ${BASE_IMAGE} AS tgz-builder WORKDIR /tmp/grafana @@ -109,13 +117,14 @@ COPY ${GRAFANA_TGZ} /tmp/grafana.tar.gz RUN tar x -z -f /tmp/grafana.tar.gz --strip-components=1 # helpers for COPY --from -FROM ${GO_SRC} as go-src -FROM ${JS_SRC} as js-src +FROM ${GO_SRC} AS go-src +FROM ${JS_SRC} AS js-src # Final stage FROM ${BASE_IMAGE} LABEL maintainer="Grafana Labs " +LABEL org.opencontainers.image.source="https://github.com/grafana/grafana" ARG GF_UID="472" ARG GF_GID="0" @@ -145,19 +154,20 @@ RUN if grep -i -q alpine /etc/issue; then \ fi # glibc support for alpine x86_64 only +# docker run --rm --env STDOUT=1 sgerrand/glibc-builder 2.40 /usr/glibc-compat > glibc-bin-2.40.tar.gz +ARG GLIBC_VERSION=2.40 + RUN if grep -i -q alpine /etc/issue && [ `arch` = "x86_64" ]; then \ - wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub && \ - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-2.35-r0.apk \ - -O /tmp/glibc-2.35-r0.apk && \ - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-bin-2.35-r0.apk \ - -O /tmp/glibc-bin-2.35-r0.apk && \ - apk add --force-overwrite --no-cache /tmp/glibc-2.35-r0.apk /tmp/glibc-bin-2.35-r0.apk && \ - rm -f /lib64/ld-linux-x86-64.so.2 && \ - ln -s /usr/glibc-compat/lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2 && \ - rm -f /tmp/glibc-2.35-r0.apk && \ - rm -f /tmp/glibc-bin-2.35-r0.apk && \ - rm -f /lib/ld-linux-x86-64.so.2 && \ - rm -f /etc/ld.so.cache; \ + wget -qO- "https://dl.grafana.com/glibc/glibc-bin-$GLIBC_VERSION.tar.gz" | tar zxf - -C / \ + usr/glibc-compat/lib/ld-linux-x86-64.so.2 \ + usr/glibc-compat/lib/libc.so.6 \ + usr/glibc-compat/lib/libdl.so.2 \ + usr/glibc-compat/lib/libm.so.6 \ + usr/glibc-compat/lib/libpthread.so.0 \ + usr/glibc-compat/lib/librt.so.1 \ + usr/glibc-compat/lib/libresolv.so.2 && \ + mkdir /lib64 && \ + ln -s /usr/glibc-compat/lib/ld-linux-x86-64.so.2 /lib64; \ fi COPY --from=go-src /tmp/grafana/conf ./conf diff --git a/MAINTAINERS.md b/MAINTAINERS.md index cab21a089cc..b1443b33722 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -8,4 +8,4 @@ - @davkal - Docs: - @chri2547 - - @brendamuir + - @JohnnyK-Grafana diff --git a/Makefile b/Makefile index 3df11c69545..bfc5491e782 100644 --- a/Makefile +++ b/Makefile @@ -248,8 +248,7 @@ test-go: test-go-unit test-go-integration .PHONY: test-go-unit test-go-unit: ## Run unit tests for backend with flags. @echo "test backend unit tests" - printf '$(GO_TEST_FILES)' | xargs \ - $(GO) test $(GO_RACE_FLAG) -short -covermode=atomic -timeout=30m + $(GO) test $(GO_RACE_FLAG) -short -covermode=atomic -coverprofile=unit.cov -timeout=30m $(GO_TEST_FILES) .PHONY: test-go-unit-pretty test-go-unit-pretty: check-tparse @@ -262,7 +261,7 @@ test-go-unit-pretty: check-tparse .PHONY: test-go-integration test-go-integration: ## Run integration tests for backend with flags. @echo "test backend integration tests" - $(GO) test $(GO_RACE_FLAG) -count=1 -run "^TestIntegration" -covermode=atomic -timeout=5m $(GO_INTEGRATION_TESTS) + $(GO) test $(GO_RACE_FLAG) -count=1 -run "^TestIntegration" -covermode=atomic -coverprofile=integration.cov -timeout=5m $(GO_INTEGRATION_TESTS) .PHONY: test-go-integration-alertmanager test-go-integration-alertmanager: ## Run integration tests for the remote alertmanager (config taken from the mimir_backend block). @@ -271,6 +270,14 @@ test-go-integration-alertmanager: ## Run integration tests for the remote alertm AM_URL=http://localhost:8080 AM_TENANT_ID=test \ $(GO) test $(GO_RACE_FLAG) -count=1 -run "^TestIntegrationRemoteAlertmanager" -covermode=atomic -timeout=5m ./pkg/services/ngalert/... +.PHONY: test-go-integration-grafana-alertmanager +test-go-integration-grafana-alertmanager: ## Run integration tests for the grafana alertmanager + @echo "test grafana alertmanager integration tests" + @export GRAFANA_VERSION=11.5.0-81938; \ + $(GO) run tools/setup_grafana_alertmanager_integration_test_images.go; \ + $(GO) clean -testcache; \ + $(GO) test $(GO_RACE_FLAG) -count=1 -run "^TestAlertmanagerIntegration" -covermode=atomic -timeout=10m ./pkg/tests/alertmanager/... + .PHONY: test-go-integration-postgres test-go-integration-postgres: devenv-postgres ## Run integration tests for postgres backend with flags. @echo "test backend integration postgres tests" @@ -421,6 +428,7 @@ protobuf: ## Compile protobuf definitions buf generate pkg/plugins/backendplugin/secretsmanagerplugin --template pkg/plugins/backendplugin/secretsmanagerplugin/buf.gen.yaml buf generate pkg/storage/unified/resource --template pkg/storage/unified/resource/buf.gen.yaml buf generate pkg/services/authz/proto/v1 --template pkg/services/authz/proto/v1/buf.gen.yaml + buf generate pkg/services/ngalert/store/proto/v1 --template pkg/services/ngalert/store/proto/v1/buf.gen.yaml .PHONY: clean clean: ## Clean up intermediate build artifacts. diff --git a/README.md b/README.md index 7322e6a1c7d..033ebdc1ea6 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ If you're interested in contributing to the Grafana project: - If you have a specific question, check out our [discussion forums](https://community.grafana.com/). - For general discussions, join us on the [official Slack](https://slack.grafana.com) team. -This project is tested with [BrowserStack](https://www.browserstack.com/) +This project is tested with [BrowserStack](https://www.browserstack.com/). ## License diff --git a/apps/advisor/.gitignore b/apps/advisor/.gitignore new file mode 100644 index 00000000000..9e73bafbae8 --- /dev/null +++ b/apps/advisor/.gitignore @@ -0,0 +1 @@ +plugin/src \ No newline at end of file diff --git a/apps/advisor/Makefile b/apps/advisor/Makefile index 926f5a2840f..7a5f1c99365 100644 --- a/apps/advisor/Makefile +++ b/apps/advisor/Makefile @@ -1,8 +1,3 @@ .PHONY: generate generate: - @grafana-app-sdk generate -g ./pkg/apis --grouping=group --postprocess - # HACK: Clean up generated CRD files. - # TODO: The SDK currently omits generating the manifest Go file with `--defencoding=none`, - # which we would normally use here to skip generating the CRD files. - # This needs to be addressed. - @rm -rf definitions + @grafana-app-sdk generate -g ./pkg/apis --grouping=group --postprocess --defencoding=none diff --git a/apps/advisor/go.mod b/apps/advisor/go.mod index 8fcc336a52b..df422dc8f68 100644 --- a/apps/advisor/go.mod +++ b/apps/advisor/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/advisor go 1.23.4 require ( - github.com/grafana/grafana-app-sdk v0.30.0 + github.com/grafana/grafana-app-sdk v0.31.0 k8s.io/apimachinery v0.32.0 k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f diff --git a/apps/advisor/kinds/check.cue b/apps/advisor/kinds/check.cue index 4ab33fb05df..4aea38d6759 100644 --- a/apps/advisor/kinds/check.cue +++ b/apps/advisor/kinds/check.cue @@ -7,28 +7,45 @@ check: { versions: { "v0alpha1": { codegen: { - frontend: false + frontend: true backend: true } + validation: { + operations: [ + "CREATE", + "UPDATE", + ] + } schema: { - spec: { + #Data: { // Generic data input that a check can receive data?: [string]: string } - status: { - report: { + #ErrorLink: { + // URL to a page with more information about the error + url: string + // Human readable error message + message: string + } + #ReportFailure: { + // Severity of the failure + severity: "high" | "low" + // Step ID that the failure is associated with + stepID: string + // Human readable identifier of the item that failed + item: string + // Links to actions that can be taken to resolve the failure + links: [...#ErrorLink] + } + #Report: { // Number of elements analyzed count: int - // List of errors - errors: [...{ - // Severity of the error - severity: "high" | "low" - // Human readable reason for the error - reason: string - // Action to take to resolve the error - action: string - }] - } + // List of failures + failures: [...#ReportFailure] + } + spec: #Data + status: { + report: #Report } } } diff --git a/apps/advisor/kinds/checktype.cue b/apps/advisor/kinds/checktype.cue new file mode 100644 index 00000000000..e2d653e629e --- /dev/null +++ b/apps/advisor/kinds/checktype.cue @@ -0,0 +1,27 @@ +package advisor + +checktype: { + kind: "CheckType" + pluralName: "CheckTypes" + current: "v0alpha1" + versions: { + "v0alpha1": { + codegen: { + frontend: true + backend: true + } + schema: { + #Step: { + title: string + description: string + stepID: string + resolution: string + } + spec: { + name: string + steps: [...#Step] + } + } + } + } +} diff --git a/apps/advisor/kinds/manifest.cue b/apps/advisor/kinds/manifest.cue index 6f96066967d..75a0eee05ef 100644 --- a/apps/advisor/kinds/manifest.cue +++ b/apps/advisor/kinds/manifest.cue @@ -5,5 +5,6 @@ manifest: { groupOverride: "advisor.grafana.app" kinds: [ check, + checktype, ] } diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/check_status_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/check_status_gen.go index 057ed6c4b7e..c9acc5ce31c 100644 --- a/apps/advisor/pkg/apis/advisor/v0alpha1/check_status_gen.go +++ b/apps/advisor/pkg/apis/advisor/v0alpha1/check_status_gen.go @@ -2,6 +2,36 @@ package v0alpha1 +// +k8s:openapi-gen=true +type CheckErrorLink struct { + // URL to a page with more information about the error + Url string `json:"url"` + // Human readable error message + Message string `json:"message"` +} + +// NewCheckErrorLink creates a new CheckErrorLink object. +func NewCheckErrorLink() *CheckErrorLink { + return &CheckErrorLink{} +} + +// +k8s:openapi-gen=true +type CheckReportFailure struct { + // Severity of the failure + Severity CheckReportFailureSeverity `json:"severity"` + // Step ID that the failure is associated with + StepID string `json:"stepID"` + // Human readable identifier of the item that failed + Item string `json:"item"` + // Links to actions that can be taken to resolve the failure + Links []CheckErrorLink `json:"links"` +} + +// NewCheckReportFailure creates a new CheckReportFailure object. +func NewCheckReportFailure() *CheckReportFailure { + return &CheckReportFailure{} +} + // +k8s:openapi-gen=true type CheckstatusOperatorState struct { // lastEvaluation is the ResourceVersion last evaluated @@ -37,6 +67,14 @@ func NewCheckStatus() *CheckStatus { } } +// +k8s:openapi-gen=true +type CheckReportFailureSeverity string + +const ( + CheckReportFailureSeverityHigh CheckReportFailureSeverity = "high" + CheckReportFailureSeverityLow CheckReportFailureSeverity = "low" +) + // +k8s:openapi-gen=true type CheckStatusOperatorStateState string @@ -46,35 +84,12 @@ const ( CheckStatusOperatorStateStateFailed CheckStatusOperatorStateState = "failed" ) -// +k8s:openapi-gen=true -type CheckStatusSeverity string - -const ( - CheckStatusSeverityHigh CheckStatusSeverity = "high" - CheckStatusSeverityLow CheckStatusSeverity = "low" -) - -// +k8s:openapi-gen=true -type CheckV0alpha1StatusReportErrors struct { - // Severity of the error - Severity CheckStatusSeverity `json:"severity"` - // Human readable reason for the error - Reason string `json:"reason"` - // Action to take to resolve the error - Action string `json:"action"` -} - -// NewCheckV0alpha1StatusReportErrors creates a new CheckV0alpha1StatusReportErrors object. -func NewCheckV0alpha1StatusReportErrors() *CheckV0alpha1StatusReportErrors { - return &CheckV0alpha1StatusReportErrors{} -} - // +k8s:openapi-gen=true type CheckV0alpha1StatusReport struct { // Number of elements analyzed Count int64 `json:"count"` - // List of errors - Errors []CheckV0alpha1StatusReportErrors `json:"errors"` + // List of failures + Failures []CheckReportFailure `json:"failures"` } // NewCheckV0alpha1StatusReport creates a new CheckV0alpha1StatusReport object. diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_codec_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_codec_gen.go new file mode 100644 index 00000000000..9881ce9d96f --- /dev/null +++ b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// CheckTypeJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type CheckTypeJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*CheckTypeJSONCodec) Read(reader io.Reader, into resource.Object) error { + return json.NewDecoder(reader).Decode(into) +} + +// Write writes JSON-encoded bytes into `writer` marshaled from `from` +func (*CheckTypeJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &CheckTypeJSONCodec{} diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_metadata_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_metadata_gen.go new file mode 100644 index 00000000000..9998cffb37d --- /dev/null +++ b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_metadata_gen.go @@ -0,0 +1,28 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type CheckTypeMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewCheckTypeMetadata creates a new CheckTypeMetadata object. +func NewCheckTypeMetadata() *CheckTypeMetadata { + return &CheckTypeMetadata{} +} diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_object_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_object_gen.go new file mode 100644 index 00000000000..80bd02b1cc8 --- /dev/null +++ b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_object_gen.go @@ -0,0 +1,266 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type CheckType struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + Spec CheckTypeSpec `json:"spec" yaml:"spec"` + CheckTypeStatus CheckTypeStatus `json:"status" yaml:"status"` +} + +func (o *CheckType) GetSpec() any { + return o.Spec +} + +func (o *CheckType) SetSpec(spec any) error { + cast, ok := spec.(CheckTypeSpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *CheckType) GetSubresources() map[string]any { + return map[string]any{ + "status": o.CheckTypeStatus, + } +} + +func (o *CheckType) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.CheckTypeStatus, true + default: + return nil, false + } +} + +func (o *CheckType) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(CheckTypeStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type CheckTypeStatus", value) + } + o.CheckTypeStatus = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *CheckType) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *CheckType) SetStaticMetadata(metadata resource.StaticMetadata) { + o.Name = metadata.Name + o.Namespace = metadata.Namespace + o.SetGroupVersionKind(schema.GroupVersionKind{ + Group: metadata.Group, + Version: metadata.Version, + Kind: metadata.Kind, + }) +} + +func (o *CheckType) GetCommonMetadata() resource.CommonMetadata { + dt := o.DeletionTimestamp + var deletionTimestamp *time.Time + if dt != nil { + deletionTimestamp = &dt.Time + } + // Legacy ExtraFields support + extraFields := make(map[string]any) + if o.Annotations != nil { + extraFields["annotations"] = o.Annotations + } + if o.ManagedFields != nil { + extraFields["managedFields"] = o.ManagedFields + } + if o.OwnerReferences != nil { + extraFields["ownerReferences"] = o.OwnerReferences + } + return resource.CommonMetadata{ + UID: string(o.UID), + ResourceVersion: o.ResourceVersion, + Generation: o.Generation, + Labels: o.Labels, + CreationTimestamp: o.CreationTimestamp.Time, + DeletionTimestamp: deletionTimestamp, + Finalizers: o.Finalizers, + UpdateTimestamp: o.GetUpdateTimestamp(), + CreatedBy: o.GetCreatedBy(), + UpdatedBy: o.GetUpdatedBy(), + ExtraFields: extraFields, + } +} + +func (o *CheckType) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *CheckType) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *CheckType) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *CheckType) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *CheckType) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *CheckType) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *CheckType) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *CheckType) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *CheckType) DeepCopyObject() runtime.Object { + return o.Copy() +} + +// Interface compliance compile-time check +var _ resource.Object = &CheckType{} + +// +k8s:openapi-gen=true +type CheckTypeList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []CheckType `json:"items" yaml:"items"` +} + +func (o *CheckTypeList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *CheckTypeList) Copy() resource.ListObject { + cpy := &CheckTypeList{ + TypeMeta: o.TypeMeta, + Items: make([]CheckType, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*CheckType); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *CheckTypeList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *CheckTypeList) SetItems(items []resource.Object) { + o.Items = make([]CheckType, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*CheckType) + } +} + +// Interface compliance compile-time check +var _ resource.ListObject = &CheckTypeList{} diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_schema_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_schema_gen.go new file mode 100644 index 00000000000..15b4a6fbb11 --- /dev/null +++ b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaCheckType = resource.NewSimpleSchema("advisor.grafana.app", "v0alpha1", &CheckType{}, &CheckTypeList{}, resource.WithKind("CheckType"), + resource.WithPlural("checktypes"), resource.WithScope(resource.NamespacedScope)) + kindCheckType = resource.Kind{ + Schema: schemaCheckType, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &CheckTypeJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func CheckTypeKind() resource.Kind { + return kindCheckType +} + +// Schema returns a resource.SimpleSchema representation of CheckType +func CheckTypeSchema() *resource.SimpleSchema { + return schemaCheckType +} + +// Interface compliance checks +var _ resource.Schema = kindCheckType diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_spec_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_spec_gen.go new file mode 100644 index 00000000000..528643b69dd --- /dev/null +++ b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_spec_gen.go @@ -0,0 +1,27 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type CheckTypeStep struct { + Title string `json:"title"` + Description string `json:"description"` + StepID string `json:"stepID"` + Resolution string `json:"resolution"` +} + +// NewCheckTypeStep creates a new CheckTypeStep object. +func NewCheckTypeStep() *CheckTypeStep { + return &CheckTypeStep{} +} + +// +k8s:openapi-gen=true +type CheckTypeSpec struct { + Name string `json:"name"` + Steps []CheckTypeStep `json:"steps"` +} + +// NewCheckTypeSpec creates a new CheckTypeSpec object. +func NewCheckTypeSpec() *CheckTypeSpec { + return &CheckTypeSpec{} +} diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_status_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_status_gen.go new file mode 100644 index 00000000000..87db65316d0 --- /dev/null +++ b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_status_gen.go @@ -0,0 +1,44 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type CheckTypestatusOperatorState struct { + // lastEvaluation is the ResourceVersion last evaluated + LastEvaluation string `json:"lastEvaluation"` + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + State CheckTypeStatusOperatorStateState `json:"state"` + // descriptiveState is an optional more descriptive state field which has no requirements on format + DescriptiveState *string `json:"descriptiveState,omitempty"` + // details contains any extra information that is operator-specific + Details map[string]interface{} `json:"details,omitempty"` +} + +// NewCheckTypestatusOperatorState creates a new CheckTypestatusOperatorState object. +func NewCheckTypestatusOperatorState() *CheckTypestatusOperatorState { + return &CheckTypestatusOperatorState{} +} + +// +k8s:openapi-gen=true +type CheckTypeStatus struct { + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + OperatorStates map[string]CheckTypestatusOperatorState `json:"operatorStates,omitempty"` + // additionalFields is reserved for future use + AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` +} + +// NewCheckTypeStatus creates a new CheckTypeStatus object. +func NewCheckTypeStatus() *CheckTypeStatus { + return &CheckTypeStatus{} +} + +// +k8s:openapi-gen=true +type CheckTypeStatusOperatorStateState string + +const ( + CheckTypeStatusOperatorStateStateSuccess CheckTypeStatusOperatorStateState = "success" + CheckTypeStatusOperatorStateStateInProgress CheckTypeStatusOperatorStateState = "in_progress" + CheckTypeStatusOperatorStateStateFailed CheckTypeStatusOperatorStateState = "failed" +) diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/zz_openapi_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/zz_openapi_gen.go index a78d033bb26..79d78cd2766 100644 --- a/apps/advisor/pkg/apis/advisor/v0alpha1/zz_openapi_gen.go +++ b/apps/advisor/pkg/apis/advisor/v0alpha1/zz_openapi_gen.go @@ -12,13 +12,20 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.Check": schema_pkg_apis_advisor_v0alpha1_Check(ref), - "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckList": schema_pkg_apis_advisor_v0alpha1_CheckList(ref), - "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckSpec": schema_pkg_apis_advisor_v0alpha1_CheckSpec(ref), - "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckStatus": schema_pkg_apis_advisor_v0alpha1_CheckStatus(ref), - "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckV0alpha1StatusReport": schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReport(ref), - "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckV0alpha1StatusReportErrors": schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReportErrors(ref), - "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckstatusOperatorState": schema_pkg_apis_advisor_v0alpha1_CheckstatusOperatorState(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.Check": schema_pkg_apis_advisor_v0alpha1_Check(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckErrorLink": schema_pkg_apis_advisor_v0alpha1_CheckErrorLink(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckList": schema_pkg_apis_advisor_v0alpha1_CheckList(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReportFailure": schema_pkg_apis_advisor_v0alpha1_CheckReportFailure(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckSpec": schema_pkg_apis_advisor_v0alpha1_CheckSpec(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckStatus": schema_pkg_apis_advisor_v0alpha1_CheckStatus(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckType": schema_pkg_apis_advisor_v0alpha1_CheckType(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeList": schema_pkg_apis_advisor_v0alpha1_CheckTypeList(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeSpec": schema_pkg_apis_advisor_v0alpha1_CheckTypeSpec(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStatus": schema_pkg_apis_advisor_v0alpha1_CheckTypeStatus(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStep": schema_pkg_apis_advisor_v0alpha1_CheckTypeStep(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypestatusOperatorState": schema_pkg_apis_advisor_v0alpha1_CheckTypestatusOperatorState(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckV0alpha1StatusReport": schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReport(ref), + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckstatusOperatorState": schema_pkg_apis_advisor_v0alpha1_CheckstatusOperatorState(ref), } } @@ -69,6 +76,35 @@ func schema_pkg_apis_advisor_v0alpha1_Check(ref common.ReferenceCallback) common } } +func schema_pkg_apis_advisor_v0alpha1_CheckErrorLink(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "url": { + SchemaProps: spec.SchemaProps{ + Description: "URL to a page with more information about the error", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Human readable error message", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"url", "message"}, + }, + }, + } +} + func schema_pkg_apis_advisor_v0alpha1_CheckList(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -117,6 +153,59 @@ func schema_pkg_apis_advisor_v0alpha1_CheckList(ref common.ReferenceCallback) co } } +func schema_pkg_apis_advisor_v0alpha1_CheckReportFailure(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "severity": { + SchemaProps: spec.SchemaProps{ + Description: "Severity of the failure", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "stepID": { + SchemaProps: spec.SchemaProps{ + Description: "Step ID that the failure is associated with", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "item": { + SchemaProps: spec.SchemaProps{ + Description: "Human readable identifier of the item that failed", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "links": { + SchemaProps: spec.SchemaProps{ + Description: "Links to actions that can be taken to resolve the failure", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckErrorLink"), + }, + }, + }, + }, + }, + }, + Required: []string{"severity", "stepID", "item", "links"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckErrorLink"}, + } +} + func schema_pkg_apis_advisor_v0alpha1_CheckSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -196,6 +285,272 @@ func schema_pkg_apis_advisor_v0alpha1_CheckStatus(ref common.ReferenceCallback) } } +func schema_pkg_apis_advisor_v0alpha1_CheckType(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeSpec", "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_advisor_v0alpha1_CheckTypeList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckType"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckType", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_advisor_v0alpha1_CheckTypeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "steps": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStep"), + }, + }, + }, + }, + }, + }, + Required: []string{"name", "steps"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStep"}, + } +} + +func schema_pkg_apis_advisor_v0alpha1_CheckTypeStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypestatusOperatorState"), + }, + }, + }, + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypestatusOperatorState"}, + } +} + +func schema_pkg_apis_advisor_v0alpha1_CheckTypeStep(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "stepID": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "resolution": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"title", "description", "stepID", "resolution"}, + }, + }, + } +} + +func schema_pkg_apis_advisor_v0alpha1_CheckTypestatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} + func schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReport(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -210,63 +565,26 @@ func schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReport(ref common.Refer Format: "int64", }, }, - "errors": { + "failures": { SchemaProps: spec.SchemaProps{ - Description: "List of errors", + Description: "List of failures", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckV0alpha1StatusReportErrors"), + Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReportFailure"), }, }, }, }, }, }, - Required: []string{"count", "errors"}, + Required: []string{"count", "failures"}, }, }, Dependencies: []string{ - "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckV0alpha1StatusReportErrors"}, - } -} - -func schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReportErrors(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "severity": { - SchemaProps: spec.SchemaProps{ - Description: "Severity of the error", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "Human readable reason for the error", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "action": { - SchemaProps: spec.SchemaProps{ - Description: "Action to take to resolve the error", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"severity", "reason", "action"}, - }, - }, + "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReportFailure"}, } } diff --git a/apps/advisor/pkg/apis/advisor_manifest.go b/apps/advisor/pkg/apis/advisor_manifest.go index 2e8bb06fb65..e333774303f 100644 --- a/apps/advisor/pkg/apis/advisor_manifest.go +++ b/apps/advisor/pkg/apis/advisor_manifest.go @@ -12,9 +12,12 @@ import ( ) var ( - rawSchemaCheckv0alpha1 = []byte(`{"spec":{"properties":{"data":{"additionalProperties":{"type":"string"},"description":"Generic data input that a check can receive","type":"object"}},"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"},"report":{"properties":{"count":{"description":"Number of elements analyzed","type":"integer"},"errors":{"description":"List of errors","items":{"properties":{"action":{"description":"Action to take to resolve the error","type":"string"},"reason":{"description":"Human readable reason for the error","type":"string"},"severity":{"description":"Severity of the error","enum":["high","low"],"type":"string"}},"required":["severity","reason","action"],"type":"object"},"type":"array"}},"required":["count","errors"],"type":"object"}},"required":["report"],"type":"object","x-kubernetes-preserve-unknown-fields":true}}`) - versionSchemaCheckv0alpha1 app.VersionSchema - _ = json.Unmarshal(rawSchemaCheckv0alpha1, &versionSchemaCheckv0alpha1) + rawSchemaCheckv0alpha1 = []byte(`{"spec":{"properties":{"data":{"additionalProperties":{"type":"string"},"description":"Generic data input that a check can receive","type":"object"}},"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"},"report":{"properties":{"count":{"description":"Number of elements analyzed","type":"integer"},"failures":{"description":"List of failures","items":{"properties":{"item":{"description":"Human readable identifier of the item that failed","type":"string"},"links":{"description":"Links to actions that can be taken to resolve the failure","items":{"properties":{"message":{"description":"Human readable error message","type":"string"},"url":{"description":"URL to a page with more information about the error","type":"string"}},"required":["url","message"],"type":"object"},"type":"array"},"severity":{"description":"Severity of the failure","enum":["high","low"],"type":"string"},"stepID":{"description":"Step ID that the failure is associated with","type":"string"}},"required":["severity","stepID","item","links"],"type":"object"},"type":"array"}},"required":["count","failures"],"type":"object"}},"required":["report"],"type":"object","x-kubernetes-preserve-unknown-fields":true}}`) + versionSchemaCheckv0alpha1 app.VersionSchema + _ = json.Unmarshal(rawSchemaCheckv0alpha1, &versionSchemaCheckv0alpha1) + rawSchemaCheckTypev0alpha1 = []byte(`{"spec":{"properties":{"name":{"type":"string"},"steps":{"items":{"properties":{"description":{"type":"string"},"resolution":{"type":"string"},"stepID":{"type":"string"},"title":{"type":"string"}},"required":["title","description","stepID","resolution"],"type":"object"},"type":"array"}},"required":["name","steps"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`) + versionSchemaCheckTypev0alpha1 app.VersionSchema + _ = json.Unmarshal(rawSchemaCheckTypev0alpha1, &versionSchemaCheckTypev0alpha1) ) var appManifestData = app.ManifestData{ @@ -27,11 +30,31 @@ var appManifestData = app.ManifestData{ Conversion: false, Versions: []app.ManifestKindVersion{ { - Name: "v0alpha1", + Name: "v0alpha1", + Admission: &app.AdmissionCapabilities{ + Validation: &app.ValidationCapability{ + Operations: []app.AdmissionOperation{ + app.AdmissionOperationCreate, + app.AdmissionOperationUpdate, + }, + }, + }, Schema: &versionSchemaCheckv0alpha1, }, }, }, + + { + Kind: "CheckType", + Scope: "Namespaced", + Conversion: false, + Versions: []app.ManifestKindVersion{ + { + Name: "v0alpha1", + Schema: &versionSchemaCheckTypev0alpha1, + }, + }, + }, }, } diff --git a/apps/advisor/pkg/app/app.go b/apps/advisor/pkg/app/app.go index e4ccafbcaaf..a640682b0fc 100644 --- a/apps/advisor/pkg/app/app.go +++ b/apps/advisor/pkg/app/app.go @@ -2,16 +2,42 @@ package app import ( "context" + "fmt" "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/k8s" "github.com/grafana/grafana-app-sdk/resource" "github.com/grafana/grafana-app-sdk/simple" advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checkregistry" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/apps/advisor/pkg/app/checkscheduler" + "github.com/grafana/grafana/apps/advisor/pkg/app/checktyperegisterer" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/klog/v2" ) func New(cfg app.Config) (app.App, error) { + // Read config + specificConfig, ok := cfg.SpecificConfig.(checkregistry.AdvisorAppConfig) + if !ok { + return nil, fmt.Errorf("invalid config type") + } + checkRegistry := specificConfig.CheckRegistry + + // Prepare storage client + clientGenerator := k8s.NewClientRegistry(cfg.KubeConfig, k8s.ClientConfig{}) + client, err := clientGenerator.ClientFor(advisorv0alpha1.CheckKind()) + if err != nil { + return nil, err + } + + // Initialize checks + checkMap := map[string]checks.Check{} + for _, c := range checkRegistry.Checks() { + checkMap[c.ID()] = c + } + simpleConfig := simple.AppConfig{ Name: "advisor", KubeConfig: cfg.KubeConfig, @@ -23,6 +49,27 @@ func New(cfg app.Config) (app.App, error) { ManagedKinds: []simple.AppManagedKind{ { Kind: advisorv0alpha1.CheckKind(), + Validator: &simple.Validator{ + ValidateFunc: func(ctx context.Context, req *app.AdmissionRequest) error { + if req.Object != nil { + _, err := getCheck(req.Object, checkMap) + return err + } + return nil + }, + }, + Watcher: &simple.Watcher{ + AddFunc: func(ctx context.Context, obj resource.Object) error { + check, err := getCheck(obj, checkMap) + if err != nil { + return err + } + return processCheck(ctx, client, obj, check) + }, + }, + }, + { + Kind: advisorv0alpha1.CheckTypeKind(), }, }, } @@ -37,6 +84,20 @@ func New(cfg app.Config) (app.App, error) { return nil, err } + // Save check types as resources + ctr, err := checktyperegisterer.New(cfg) + if err != nil { + return nil, err + } + a.AddRunnable(ctr) + + // Start scheduler + csch, err := checkscheduler.New(cfg) + if err != nil { + return nil, err + } + a.AddRunnable(csch) + return a, nil } @@ -49,6 +110,7 @@ func GetKinds() map[schema.GroupVersion][]resource.Kind { return map[schema.GroupVersion][]resource.Kind{ gv: { advisorv0alpha1.CheckKind(), + advisorv0alpha1.CheckTypeKind(), }, } } diff --git a/apps/advisor/pkg/app/authorizer.go b/apps/advisor/pkg/app/authorizer.go new file mode 100644 index 00000000000..4d216ee9edd --- /dev/null +++ b/apps/advisor/pkg/app/authorizer.go @@ -0,0 +1,31 @@ +package app + +import ( + "context" + + "github.com/grafana/grafana/pkg/apimachinery/identity" + "k8s.io/apiserver/pkg/authorization/authorizer" +) + +func GetAuthorizer() authorizer.Authorizer { + return authorizer.AuthorizerFunc(func( + ctx context.Context, attr authorizer.Attributes, + ) (authorized authorizer.Decision, reason string, err error) { + if !attr.IsResourceRequest() { + return authorizer.DecisionNoOpinion, "", nil + } + + // require a user + u, err := identity.GetRequester(ctx) + if err != nil { + return authorizer.DecisionDeny, "valid user is required", err + } + + // check if is admin + if u.HasRole(identity.RoleAdmin) { + return authorizer.DecisionAllow, "", nil + } + + return authorizer.DecisionDeny, "forbidden", nil + }) +} diff --git a/apps/advisor/pkg/app/authorizer_test.go b/apps/advisor/pkg/app/authorizer_test.go new file mode 100644 index 00000000000..1384176969e --- /dev/null +++ b/apps/advisor/pkg/app/authorizer_test.go @@ -0,0 +1,82 @@ +package app + +import ( + "context" + "testing" + + "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/stretchr/testify/assert" + "k8s.io/apiserver/pkg/authorization/authorizer" +) + +func TestGetAuthorizer(t *testing.T) { + tests := []struct { + name string + ctx context.Context + attr authorizer.Attributes + expectedDecision authorizer.Decision + expectedReason string + expectedErr error + }{ + { + name: "non-resource request", + ctx: context.TODO(), + attr: &mockAttributes{resourceRequest: false}, + expectedDecision: authorizer.DecisionNoOpinion, + expectedReason: "", + expectedErr: nil, + }, + { + name: "user is admin", + ctx: identity.WithRequester(context.TODO(), &mockUser{isGrafanaAdmin: true}), + attr: &mockAttributes{resourceRequest: true}, + expectedDecision: authorizer.DecisionAllow, + expectedReason: "", + expectedErr: nil, + }, + { + name: "user is not admin", + ctx: identity.WithRequester(context.TODO(), &mockUser{isGrafanaAdmin: false}), + attr: &mockAttributes{resourceRequest: true}, + expectedDecision: authorizer.DecisionDeny, + expectedReason: "forbidden", + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + auth := GetAuthorizer() + decision, reason, err := auth.Authorize(tt.ctx, tt.attr) + assert.Equal(t, tt.expectedDecision, decision) + assert.Equal(t, tt.expectedReason, reason) + assert.Equal(t, tt.expectedErr, err) + }) + } +} + +type mockAttributes struct { + authorizer.Attributes + resourceRequest bool +} + +func (m *mockAttributes) IsResourceRequest() bool { + return m.resourceRequest +} + +// Implement other methods of authorizer.Attributes as needed + +type mockUser struct { + identity.Requester + isGrafanaAdmin bool +} + +func (m *mockUser) GetIsGrafanaAdmin() bool { + return m.isGrafanaAdmin +} + +func (m *mockUser) HasRole(role identity.RoleType) bool { + return role == identity.RoleAdmin && m.isGrafanaAdmin +} + +// Implement other methods of identity.Requester as needed diff --git a/apps/advisor/pkg/app/checkregistry/checkregistry.go b/apps/advisor/pkg/app/checkregistry/checkregistry.go new file mode 100644 index 00000000000..335b9cc7567 --- /dev/null +++ b/apps/advisor/pkg/app/checkregistry/checkregistry.go @@ -0,0 +1,66 @@ +package checkregistry + +import ( + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks/datasourcecheck" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks/plugincheck" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/plugins/repo" + "github.com/grafana/grafana/pkg/services/datasources" + "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" + "github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext" + "github.com/grafana/grafana/pkg/services/pluginsintegration/plugininstaller" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" +) + +type CheckService interface { + Checks() []checks.Check +} + +type Service struct { + datasourceSvc datasources.DataSourceService + pluginStore pluginstore.Store + pluginContextProvider *plugincontext.Provider + pluginClient plugins.Client + pluginRepo repo.Service + pluginPreinstall plugininstaller.Preinstall + managedPlugins managedplugins.Manager +} + +func ProvideService(datasourceSvc datasources.DataSourceService, pluginStore pluginstore.Store, + pluginContextProvider *plugincontext.Provider, pluginClient plugins.Client, + pluginRepo repo.Service, pluginPreinstall plugininstaller.Preinstall, managedPlugins managedplugins.Manager) *Service { + return &Service{ + datasourceSvc: datasourceSvc, + pluginStore: pluginStore, + pluginContextProvider: pluginContextProvider, + pluginClient: pluginClient, + pluginRepo: pluginRepo, + pluginPreinstall: pluginPreinstall, + managedPlugins: managedPlugins, + } +} + +func (s *Service) Checks() []checks.Check { + return []checks.Check{ + datasourcecheck.New( + s.datasourceSvc, + s.pluginStore, + s.pluginContextProvider, + s.pluginClient, + ), + plugincheck.New( + s.pluginStore, + s.pluginRepo, + s.pluginPreinstall, + s.managedPlugins, + ), + } +} + +// AdvisorAppConfig is the configuration received from Grafana to run the app +type AdvisorAppConfig struct { + CheckRegistry CheckService + PluginConfig map[string]string + StackID string +} diff --git a/apps/advisor/pkg/app/checks/datasourcecheck/check.go b/apps/advisor/pkg/app/checks/datasourcecheck/check.go new file mode 100644 index 00000000000..0cdac81e4b8 --- /dev/null +++ b/apps/advisor/pkg/app/checks/datasourcecheck/check.go @@ -0,0 +1,177 @@ +package datasourcecheck + +import ( + "context" + "errors" + "fmt" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/services/datasources" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" + "github.com/grafana/grafana/pkg/util" +) + +type check struct { + DatasourceSvc datasources.DataSourceService + PluginStore pluginstore.Store + PluginContextProvider pluginContextProvider + PluginClient plugins.Client + log log.Logger +} + +func New( + datasourceSvc datasources.DataSourceService, + pluginStore pluginstore.Store, + pluginContextProvider pluginContextProvider, + pluginClient plugins.Client, +) checks.Check { + return &check{ + DatasourceSvc: datasourceSvc, + PluginStore: pluginStore, + PluginContextProvider: pluginContextProvider, + PluginClient: pluginClient, + log: log.New("advisor.datasourcecheck"), + } +} + +func (c *check) Items(ctx context.Context) ([]any, error) { + dss, err := c.DatasourceSvc.GetAllDataSources(ctx, &datasources.GetAllDataSourcesQuery{}) + if err != nil { + return nil, err + } + res := make([]any, len(dss)) + for i, ds := range dss { + res[i] = ds + } + return res, nil +} + +func (c *check) ID() string { + return "datasource" +} + +func (c *check) Steps() []checks.Step { + return []checks.Step{ + &uidValidationStep{}, + &healthCheckStep{ + PluginContextProvider: c.PluginContextProvider, + PluginClient: c.PluginClient, + log: c.log, + }, + } +} + +type uidValidationStep struct{} + +func (s *uidValidationStep) ID() string { + return "uid-validation" +} + +func (s *uidValidationStep) Title() string { + return "UID validation" +} + +func (s *uidValidationStep) Description() string { + return "Checks if the UID of a data source is valid." +} + +func (s *uidValidationStep) Resolution() string { + return "Check the documentation for more information or delete the data source and create a new one." +} + +func (s *uidValidationStep) Run(ctx context.Context, obj *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) { + ds, ok := i.(*datasources.DataSource) + if !ok { + return nil, fmt.Errorf("invalid item type %T", i) + } + // Data source UID validation + err := util.ValidateUID(ds.UID) + if err != nil { + return checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityLow, + s.ID(), + fmt.Sprintf("%s (%s)", ds.Name, ds.UID), + []advisor.CheckErrorLink{}, + ), nil + } + return nil, nil +} + +type healthCheckStep struct { + PluginContextProvider pluginContextProvider + PluginClient plugins.Client + log log.Logger +} + +func (s *healthCheckStep) Title() string { + return "Health check" +} + +func (s *healthCheckStep) Description() string { + return "Checks if a data sources is healthy." +} + +func (s *healthCheckStep) Resolution() string { + return "Go to the data source configuration page and address the issues reported." +} + +func (s *healthCheckStep) ID() string { + return "health-check" +} + +func (s *healthCheckStep) Run(ctx context.Context, obj *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) { + ds, ok := i.(*datasources.DataSource) + if !ok { + return nil, fmt.Errorf("invalid item type %T", i) + } + + // Health check execution + requester, err := identity.GetRequester(ctx) + if err != nil { + return nil, err + } + pCtx, err := s.PluginContextProvider.GetWithDataSource(ctx, ds.Type, requester, ds) + if err != nil { + // Unable to check health check + s.log.Error("Failed to get plugin context", "datasource_uid", ds.UID, "error", err) + return nil, nil + } + req := &backend.CheckHealthRequest{ + PluginContext: pCtx, + Headers: map[string]string{}, + } + resp, err := s.PluginClient.CheckHealth(ctx, req) + if err != nil || resp.Status != backend.HealthStatusOk { + if err != nil { + s.log.Debug("Failed to check health", "datasource_uid", ds.UID, "error", err) + if errors.Is(err, plugins.ErrMethodNotImplemented) || errors.Is(err, plugins.ErrPluginUnavailable) { + // The plugin does not support backend health checks + return nil, nil + } + } else { + s.log.Debug("Failed to check health", "datasource_uid", ds.UID, "status", resp.Status, "message", resp.Message) + } + return checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + s.ID(), + ds.Name, + []advisor.CheckErrorLink{ + { + Message: "Fix me", + Url: fmt.Sprintf("/connections/datasources/edit/%s", ds.UID), + }, + }, + ), nil + } + return nil, nil +} + +type pluginContextProvider interface { + GetWithDataSource(ctx context.Context, pluginID string, user identity.Requester, ds *datasources.DataSource) (backend.PluginContext, error) +} diff --git a/apps/advisor/pkg/app/checks/datasourcecheck/check_test.go b/apps/advisor/pkg/app/checks/datasourcecheck/check_test.go new file mode 100644 index 00000000000..ee8a991555d --- /dev/null +++ b/apps/advisor/pkg/app/checks/datasourcecheck/check_test.go @@ -0,0 +1,188 @@ +package datasourcecheck + +import ( + "context" + "testing" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/services/datasources" + "github.com/grafana/grafana/pkg/services/user" + "github.com/stretchr/testify/assert" +) + +func TestCheck_Run(t *testing.T) { + t.Run("should return no failures when all datasources are healthy", func(t *testing.T) { + datasources := []*datasources.DataSource{ + {UID: "valid-uid-1", Type: "prometheus", Name: "Prometheus"}, + {UID: "valid-uid-2", Type: "mysql", Name: "MySQL"}, + } + + mockDatasourceSvc := &MockDatasourceSvc{dss: datasources} + mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}} + mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusOk}} + + check := &check{ + DatasourceSvc: mockDatasourceSvc, + PluginContextProvider: mockPluginContextProvider, + PluginClient: mockPluginClient, + log: log.New("advisor.datasourcecheck"), + } + + ctx := identity.WithRequester(context.Background(), &user.SignedInUser{}) + items, err := check.Items(ctx) + assert.NoError(t, err) + failures := []advisor.CheckReportFailure{} + for _, step := range check.Steps() { + for _, item := range items { + stepFailures, err := step.Run(ctx, &advisor.CheckSpec{}, item) + assert.NoError(t, err) + if stepFailures != nil { + failures = append(failures, *stepFailures) + } + } + } + + assert.NoError(t, err) + assert.Equal(t, 2, len(items)) + assert.Empty(t, failures) + }) + + t.Run("should return failures when datasource UID is invalid", func(t *testing.T) { + datasources := []*datasources.DataSource{ + {UID: "invalid uid", Type: "prometheus", Name: "Prometheus"}, + } + + mockDatasourceSvc := &MockDatasourceSvc{dss: datasources} + mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}} + mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusOk}} + + check := &check{ + DatasourceSvc: mockDatasourceSvc, + PluginContextProvider: mockPluginContextProvider, + PluginClient: mockPluginClient, + log: log.New("advisor.datasourcecheck"), + } + + ctx := identity.WithRequester(context.Background(), &user.SignedInUser{}) + items, err := check.Items(ctx) + assert.NoError(t, err) + failures := []advisor.CheckReportFailure{} + for _, step := range check.Steps() { + for _, item := range items { + stepFailures, err := step.Run(ctx, &advisor.CheckSpec{}, item) + assert.NoError(t, err) + if stepFailures != nil { + failures = append(failures, *stepFailures) + } + } + } + + assert.NoError(t, err) + assert.Equal(t, 1, len(items)) + assert.Len(t, failures, 1) + assert.Equal(t, "uid-validation", failures[0].StepID) + }) + + t.Run("should return failures when datasource health check fails", func(t *testing.T) { + datasources := []*datasources.DataSource{ + {UID: "valid-uid-1", Type: "prometheus", Name: "Prometheus"}, + } + + mockDatasourceSvc := &MockDatasourceSvc{dss: datasources} + mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}} + mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusError}} + + check := &check{ + DatasourceSvc: mockDatasourceSvc, + PluginContextProvider: mockPluginContextProvider, + PluginClient: mockPluginClient, + log: log.New("advisor.datasourcecheck"), + } + + ctx := identity.WithRequester(context.Background(), &user.SignedInUser{}) + items, err := check.Items(ctx) + assert.NoError(t, err) + failures := []advisor.CheckReportFailure{} + for _, step := range check.Steps() { + for _, item := range items { + stepFailures, err := step.Run(ctx, &advisor.CheckSpec{}, item) + assert.NoError(t, err) + if stepFailures != nil { + failures = append(failures, *stepFailures) + } + } + } + + assert.NoError(t, err) + assert.Equal(t, 1, len(items)) + assert.Len(t, failures, 1) + assert.Equal(t, "health-check", failures[0].StepID) + }) + + t.Run("should skip health check when plugin does not support backend health checks", func(t *testing.T) { + datasources := []*datasources.DataSource{ + {UID: "valid-uid-1", Type: "prometheus", Name: "Prometheus"}, + } + mockDatasourceSvc := &MockDatasourceSvc{dss: datasources} + mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}} + mockPluginClient := &MockPluginClient{err: plugins.ErrMethodNotImplemented} + + check := &check{ + DatasourceSvc: mockDatasourceSvc, + PluginContextProvider: mockPluginContextProvider, + PluginClient: mockPluginClient, + log: log.New("advisor.datasourcecheck"), + } + + ctx := identity.WithRequester(context.Background(), &user.SignedInUser{}) + items, err := check.Items(ctx) + assert.NoError(t, err) + failures := []advisor.CheckReportFailure{} + for _, step := range check.Steps() { + for _, item := range items { + stepFailures, err := step.Run(ctx, &advisor.CheckSpec{}, item) + assert.NoError(t, err) + if stepFailures != nil { + failures = append(failures, *stepFailures) + } + } + } + + assert.NoError(t, err) + assert.Equal(t, 1, len(items)) + assert.Len(t, failures, 0) + }) +} + +type MockDatasourceSvc struct { + datasources.DataSourceService + + dss []*datasources.DataSource +} + +func (m *MockDatasourceSvc) GetAllDataSources(ctx context.Context, query *datasources.GetAllDataSourcesQuery) ([]*datasources.DataSource, error) { + return m.dss, nil +} + +type MockPluginContextProvider struct { + pCtx backend.PluginContext +} + +func (m *MockPluginContextProvider) GetWithDataSource(ctx context.Context, pluginID string, user identity.Requester, ds *datasources.DataSource) (backend.PluginContext, error) { + return m.pCtx, nil +} + +type MockPluginClient struct { + plugins.Client + + res *backend.CheckHealthResult + err error +} + +func (m *MockPluginClient) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) { + return m.res, m.err +} diff --git a/apps/advisor/pkg/app/checks/ifaces.go b/apps/advisor/pkg/app/checks/ifaces.go new file mode 100644 index 00000000000..31456599381 --- /dev/null +++ b/apps/advisor/pkg/app/checks/ifaces.go @@ -0,0 +1,31 @@ +package checks + +import ( + "context" + + advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" +) + +// Check returns metadata about the check being executed and the list of Steps +type Check interface { + // ID returns the unique identifier of the check + ID() string + // Items returns the list of items that will be checked + Items(ctx context.Context) ([]any, error) + // Steps returns the list of steps that will be executed + Steps() []Step +} + +// Step is a single step in a check, including its metadata +type Step interface { + // ID returns the unique identifier of the step + ID() string + // Title returns the title of the step + Title() string + // Description returns the description of the step + Description() string + // Explains the action that needs to be taken to resolve the issue + Resolution() string + // Run executes the step for an item and returns a report + Run(ctx context.Context, obj *advisorv0alpha1.CheckSpec, item any) (*advisorv0alpha1.CheckReportFailure, error) +} diff --git a/apps/advisor/pkg/app/checks/plugincheck/check.go b/apps/advisor/pkg/app/checks/plugincheck/check.go new file mode 100644 index 00000000000..45c54d4fdc0 --- /dev/null +++ b/apps/advisor/pkg/app/checks/plugincheck/check.go @@ -0,0 +1,199 @@ +package plugincheck + +import ( + "context" + "fmt" + sysruntime "runtime" + + "github.com/Masterminds/semver/v3" + advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/pkg/cmd/grafana-cli/services" + "github.com/grafana/grafana/pkg/plugins/repo" + "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" + "github.com/grafana/grafana/pkg/services/pluginsintegration/plugininstaller" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" +) + +func New( + pluginStore pluginstore.Store, + pluginRepo repo.Service, + pluginPreinstall plugininstaller.Preinstall, + managedPlugins managedplugins.Manager, +) checks.Check { + return &check{ + PluginStore: pluginStore, + PluginRepo: pluginRepo, + PluginPreinstall: pluginPreinstall, + ManagedPlugins: managedPlugins, + } +} + +type check struct { + PluginStore pluginstore.Store + PluginRepo repo.Service + PluginPreinstall plugininstaller.Preinstall + ManagedPlugins managedplugins.Manager +} + +func (c *check) ID() string { + return "plugin" +} + +func (c *check) Items(ctx context.Context) ([]any, error) { + ps := c.PluginStore.Plugins(ctx) + res := make([]any, len(ps)) + for i, p := range ps { + res[i] = p + } + return res, nil +} + +func (c *check) Steps() []checks.Step { + return []checks.Step{ + &deprecationStep{ + PluginRepo: c.PluginRepo, + }, + &updateStep{ + PluginRepo: c.PluginRepo, + PluginPreinstall: c.PluginPreinstall, + ManagedPlugins: c.ManagedPlugins, + }, + } +} + +type deprecationStep struct { + PluginRepo repo.Service +} + +func (s *deprecationStep) Title() string { + return "Deprecation check" +} + +func (s *deprecationStep) Description() string { + return "Check if any installed plugins are deprecated." +} + +func (s *deprecationStep) Resolution() string { + return "Check the documentation for recommended steps or delete the plugin." +} + +func (s *deprecationStep) ID() string { + return "deprecation" +} + +func (s *deprecationStep) Run(ctx context.Context, _ *advisor.CheckSpec, it any) (*advisor.CheckReportFailure, error) { + p, ok := it.(pluginstore.Plugin) + if !ok { + return nil, fmt.Errorf("invalid item type %T", it) + } + + // Skip if it's a core plugin + if p.IsCorePlugin() { + return nil, nil + } + + // Check if plugin is deprecated + i, err := s.PluginRepo.PluginInfo(ctx, p.ID) + if err != nil { + // Unable to check deprecation status + return nil, nil + } + if i.Status == "deprecated" { + return checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + s.ID(), + p.ID, + []advisor.CheckErrorLink{ + { + Message: "Admin", + Url: fmt.Sprintf("/plugins/%s", p.ID), + }, + }, + ), nil + } + return nil, nil +} + +type updateStep struct { + PluginRepo repo.Service + PluginPreinstall plugininstaller.Preinstall + ManagedPlugins managedplugins.Manager +} + +func (s *updateStep) Title() string { + return "Update check" +} + +func (s *updateStep) Description() string { + return "Checks if an installed plugins has a newer version available." +} + +func (s *updateStep) Resolution() string { + return "Go to the plugin admin page and upgrade to the latest version." +} + +func (s *updateStep) ID() string { + return "update" +} + +func (s *updateStep) Run(ctx context.Context, _ *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) { + p, ok := i.(pluginstore.Plugin) + if !ok { + return nil, fmt.Errorf("invalid item type %T", i) + } + + // Skip if it's a core plugin + if p.IsCorePlugin() { + return nil, nil + } + + // Skip if it's managed or pinned + if s.isManaged(ctx, p.ID) || s.PluginPreinstall.IsPinned(p.ID) { + return nil, nil + } + + // Check if plugin has a newer version available + compatOpts := repo.NewCompatOpts(services.GrafanaVersion, sysruntime.GOOS, sysruntime.GOARCH) + info, err := s.PluginRepo.GetPluginArchiveInfo(ctx, p.ID, "", compatOpts) + if err != nil { + // Unable to check updates + return nil, nil + } + if hasUpdate(p, info) { + return checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityLow, + s.ID(), + p.ID, + []advisor.CheckErrorLink{ + { + Message: "Upgrade", + Url: fmt.Sprintf("/plugins/%s?page=version-history", p.ID), + }, + }, + ), nil + } + + return nil, nil +} + +func hasUpdate(current pluginstore.Plugin, latest *repo.PluginArchiveInfo) bool { + // If both versions are semver-valid, compare them + v1, err1 := semver.NewVersion(current.Info.Version) + v2, err2 := semver.NewVersion(latest.Version) + if err1 == nil && err2 == nil { + return v1.LessThan(v2) + } + // In other case, assume that a different latest version will always be newer + return current.Info.Version != latest.Version +} + +func (s *updateStep) isManaged(ctx context.Context, pluginID string) bool { + for _, managedPlugin := range s.ManagedPlugins.ManagedPlugins(ctx) { + if managedPlugin == pluginID { + return true + } + } + return false +} diff --git a/apps/advisor/pkg/app/checks/plugincheck/check_test.go b/apps/advisor/pkg/app/checks/plugincheck/check_test.go new file mode 100644 index 00000000000..13b2a17ea1b --- /dev/null +++ b/apps/advisor/pkg/app/checks/plugincheck/check_test.go @@ -0,0 +1,210 @@ +package plugincheck + +import ( + "context" + "testing" + + advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/plugins/repo" + "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" + "github.com/grafana/grafana/pkg/services/pluginsintegration/plugininstaller" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" + "github.com/stretchr/testify/assert" +) + +func TestRun(t *testing.T) { + tests := []struct { + name string + plugins []pluginstore.Plugin + pluginInfo map[string]*repo.PluginInfo + pluginArchives map[string]*repo.PluginArchiveInfo + pluginPreinstalled []string + pluginManaged []string + expectedFailures []advisor.CheckReportFailure + }{ + { + name: "No plugins", + plugins: []pluginstore.Plugin{}, + expectedFailures: []advisor.CheckReportFailure{}, + }, + { + name: "Deprecated plugin", + plugins: []pluginstore.Plugin{ + {JSONData: plugins.JSONData{ID: "plugin1", Info: plugins.Info{Version: "1.0.0"}}}, + }, + pluginInfo: map[string]*repo.PluginInfo{ + "plugin1": {Status: "deprecated"}, + }, + pluginArchives: map[string]*repo.PluginArchiveInfo{ + "plugin1": {Version: "1.0.0"}, + }, + expectedFailures: []advisor.CheckReportFailure{ + { + Severity: advisor.CheckReportFailureSeverityHigh, + StepID: "deprecation", + Item: "plugin1", + Links: []advisor.CheckErrorLink{ + { + Url: "/plugins/plugin1", + Message: "Admin", + }, + }, + }, + }, + }, + { + name: "Plugin with update", + plugins: []pluginstore.Plugin{ + {JSONData: plugins.JSONData{ID: "plugin2", Info: plugins.Info{Version: "1.0.0"}}}, + }, + pluginInfo: map[string]*repo.PluginInfo{ + "plugin2": {Status: "active"}, + }, + pluginArchives: map[string]*repo.PluginArchiveInfo{ + "plugin2": {Version: "1.1.0"}, + }, + expectedFailures: []advisor.CheckReportFailure{ + { + Severity: advisor.CheckReportFailureSeverityLow, + StepID: "update", + Item: "plugin2", + Links: []advisor.CheckErrorLink{ + { + Url: "/plugins/plugin2?page=version-history", + Message: "Upgrade", + }, + }, + }, + }, + }, + { + name: "Plugin with update (non semver)", + plugins: []pluginstore.Plugin{ + {JSONData: plugins.JSONData{ID: "plugin2", Info: plugins.Info{Version: "alpha"}}}, + }, + pluginInfo: map[string]*repo.PluginInfo{ + "plugin2": {Status: "active"}, + }, + pluginArchives: map[string]*repo.PluginArchiveInfo{ + "plugin2": {Version: "beta"}, + }, + expectedFailures: []advisor.CheckReportFailure{ + { + Severity: advisor.CheckReportFailureSeverityLow, + StepID: "update", + Item: "plugin2", + Links: []advisor.CheckErrorLink{ + { + Url: "/plugins/plugin2?page=version-history", + Message: "Upgrade", + }, + }, + }, + }, + }, + { + name: "Plugin pinned", + plugins: []pluginstore.Plugin{ + {JSONData: plugins.JSONData{ID: "plugin3", Info: plugins.Info{Version: "1.0.0"}}}, + }, + pluginInfo: map[string]*repo.PluginInfo{ + "plugin3": {Status: "active"}, + }, + pluginArchives: map[string]*repo.PluginArchiveInfo{ + "plugin3": {Version: "1.1.0"}, + }, + pluginPreinstalled: []string{"plugin3"}, + expectedFailures: []advisor.CheckReportFailure{}, + }, + { + name: "Managed plugin", + plugins: []pluginstore.Plugin{ + {JSONData: plugins.JSONData{ID: "plugin4", Info: plugins.Info{Version: "1.0.0"}}}, + }, + pluginInfo: map[string]*repo.PluginInfo{ + "plugin4": {Status: "active"}, + }, + pluginArchives: map[string]*repo.PluginArchiveInfo{ + "plugin4": {Version: "1.1.0"}, + }, + pluginManaged: []string{"plugin4"}, + expectedFailures: []advisor.CheckReportFailure{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pluginStore := &mockPluginStore{plugins: tt.plugins} + pluginRepo := &mockPluginRepo{ + pluginInfo: tt.pluginInfo, + pluginArchiveInfo: tt.pluginArchives, + } + pluginPreinstall := &mockPluginPreinstall{pinned: tt.pluginPreinstalled} + managedPlugins := &mockManagedPlugins{managed: tt.pluginManaged} + check := New(pluginStore, pluginRepo, pluginPreinstall, managedPlugins) + + items, err := check.Items(context.Background()) + assert.NoError(t, err) + failures := []advisor.CheckReportFailure{} + for _, step := range check.Steps() { + for _, item := range items { + stepFailures, err := step.Run(context.Background(), &advisor.CheckSpec{}, item) + assert.NoError(t, err) + if stepFailures != nil { + failures = append(failures, *stepFailures) + } + } + } + assert.NoError(t, err) + assert.Equal(t, len(tt.plugins), len(items)) + assert.Equal(t, tt.expectedFailures, failures) + }) + } +} + +type mockPluginStore struct { + pluginstore.Store + plugins []pluginstore.Plugin +} + +func (m *mockPluginStore) Plugins(ctx context.Context, t ...plugins.Type) []pluginstore.Plugin { + return m.plugins +} + +type mockPluginRepo struct { + repo.Service + pluginInfo map[string]*repo.PluginInfo + pluginArchiveInfo map[string]*repo.PluginArchiveInfo +} + +func (m *mockPluginRepo) PluginInfo(ctx context.Context, id string) (*repo.PluginInfo, error) { + return m.pluginInfo[id], nil +} + +func (m *mockPluginRepo) GetPluginArchiveInfo(ctx context.Context, id, version string, opts repo.CompatOpts) (*repo.PluginArchiveInfo, error) { + return m.pluginArchiveInfo[id], nil +} + +type mockPluginPreinstall struct { + plugininstaller.Preinstall + pinned []string +} + +func (m *mockPluginPreinstall) IsPinned(pluginID string) bool { + for _, p := range m.pinned { + if p == pluginID { + return true + } + } + return false +} + +type mockManagedPlugins struct { + managedplugins.Manager + managed []string +} + +func (m *mockManagedPlugins) ManagedPlugins(ctx context.Context) []string { + return m.managed +} diff --git a/apps/advisor/pkg/app/checks/utils.go b/apps/advisor/pkg/app/checks/utils.go new file mode 100644 index 00000000000..145c98d0961 --- /dev/null +++ b/apps/advisor/pkg/app/checks/utils.go @@ -0,0 +1,40 @@ +package checks + +import ( + "fmt" + "strconv" + + "github.com/grafana/authlib/types" + advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + TypeLabel = "advisor.grafana.app/type" + StatusAnnotation = "advisor.grafana.app/status" +) + +func NewCheckReportFailure( + severity advisor.CheckReportFailureSeverity, + stepID string, + item string, + links []advisor.CheckErrorLink, +) *advisor.CheckReportFailure { + return &advisor.CheckReportFailure{ + Severity: severity, + StepID: stepID, + Item: item, + Links: links, + } +} + +func GetNamespace(stackID string) (string, error) { + if stackID == "" { + return metav1.NamespaceDefault, nil + } + stackId, err := strconv.ParseInt(stackID, 10, 64) + if err != nil { + return "", fmt.Errorf("invalid stack id: %s", stackID) + } + return types.CloudNamespaceFormatter(stackId), nil +} diff --git a/apps/advisor/pkg/app/checks/utils_test.go b/apps/advisor/pkg/app/checks/utils_test.go new file mode 100644 index 00000000000..ad0fda89de8 --- /dev/null +++ b/apps/advisor/pkg/app/checks/utils_test.go @@ -0,0 +1,46 @@ +package checks + +import ( + "testing" + + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestGetNamespace(t *testing.T) { + tests := []struct { + name string + input string + expected string + expectedErr string + }{ + { + name: "empty stack ID", + input: "", + expected: metav1.NamespaceDefault, + }, + { + name: "valid stack ID", + input: "1234567890", + expected: "stacks-1234567890", + }, + { + name: "invalid stack ID", + input: "invalid", + expected: "", + expectedErr: "invalid stack id: invalid", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := GetNamespace(tt.input) + if tt.expectedErr != "" { + assert.EqualError(t, err, tt.expectedErr) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.expected, result) + }) + } +} diff --git a/apps/advisor/pkg/app/checkscheduler/checkscheduler.go b/apps/advisor/pkg/app/checkscheduler/checkscheduler.go new file mode 100644 index 00000000000..2bcb8d43f19 --- /dev/null +++ b/apps/advisor/pkg/app/checkscheduler/checkscheduler.go @@ -0,0 +1,225 @@ +package checkscheduler + +import ( + "context" + "fmt" + "sort" + "strconv" + "time" + + "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/k8s" + "github.com/grafana/grafana-app-sdk/resource" + "github.com/grafana/grafana-plugin-sdk-go/backend/gtime" + advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checkregistry" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/klog/v2" +) + +const defaultEvaluationInterval = 24 * time.Hour +const defaultMaxHistory = 10 + +// Runner is a "runnable" app used to be able to expose and API endpoint +// with the existing checks types. This does not need to be a CRUD resource, but it is +// the only way existing at the moment to expose the check types. +type Runner struct { + checkRegistry checkregistry.CheckService + client resource.Client + evaluationInterval time.Duration + maxHistory int + namespace string +} + +// NewRunner creates a new Runner. +func New(cfg app.Config) (app.Runnable, error) { + // Read config + specificConfig, ok := cfg.SpecificConfig.(checkregistry.AdvisorAppConfig) + if !ok { + return nil, fmt.Errorf("invalid config type") + } + checkRegistry := specificConfig.CheckRegistry + evalInterval, err := getEvaluationInterval(specificConfig.PluginConfig) + if err != nil { + return nil, err + } + maxHistory, err := getMaxHistory(specificConfig.PluginConfig) + if err != nil { + return nil, err + } + namespace, err := checks.GetNamespace(specificConfig.StackID) + if err != nil { + return nil, err + } + + // Prepare storage client + clientGenerator := k8s.NewClientRegistry(cfg.KubeConfig, k8s.ClientConfig{}) + client, err := clientGenerator.ClientFor(advisorv0alpha1.CheckKind()) + if err != nil { + return nil, err + } + + return &Runner{ + checkRegistry: checkRegistry, + client: client, + evaluationInterval: evalInterval, + maxHistory: maxHistory, + namespace: namespace, + }, nil +} + +func (r *Runner) Run(ctx context.Context) error { + lastCreated, err := r.checkLastCreated(ctx) + if err != nil { + return err + } + + // do an initial creation if necessary + if lastCreated.IsZero() { + err = r.createChecks(ctx) + if err != nil { + klog.Error("Error creating new check reports", "error", err) + } else { + lastCreated = time.Now() + } + } + + nextSendInterval := time.Until(lastCreated.Add(r.evaluationInterval)) + if nextSendInterval < time.Minute { + nextSendInterval = 1 * time.Minute + } + + ticker := time.NewTicker(nextSendInterval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + err = r.createChecks(ctx) + if err != nil { + klog.Error("Error creating new check reports", "error", err) + } + + err = r.cleanupChecks(ctx) + if err != nil { + klog.Error("Error cleaning up old check reports", "error", err) + } + + if nextSendInterval != r.evaluationInterval { + nextSendInterval = r.evaluationInterval + } + ticker.Reset(nextSendInterval) + case <-ctx.Done(): + return ctx.Err() + } + } +} + +// checkLastCreated returns the creation time of the last check created +// regardless of its ID. This assumes that the checks are created in batches +// so a batch will have a similar creation time. +func (r *Runner) checkLastCreated(ctx context.Context) (time.Time, error) { + list, err := r.client.List(ctx, r.namespace, resource.ListOptions{}) + if err != nil { + return time.Time{}, err + } + lastCreated := time.Time{} + for _, item := range list.GetItems() { + itemCreated := item.GetCreationTimestamp().Time + if itemCreated.After(lastCreated) { + lastCreated = itemCreated + } + } + return lastCreated, nil +} + +// createChecks creates a new check for each check type in the registry. +func (r *Runner) createChecks(ctx context.Context) error { + for _, check := range r.checkRegistry.Checks() { + obj := &advisorv0alpha1.Check{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "check-", + Namespace: r.namespace, + Labels: map[string]string{ + checks.TypeLabel: check.ID(), + }, + }, + Spec: advisorv0alpha1.CheckSpec{}, + } + id := obj.GetStaticMetadata().Identifier() + _, err := r.client.Create(ctx, id, obj, resource.CreateOptions{}) + if err != nil { + return fmt.Errorf("error creating check: %w", err) + } + } + return nil +} + +// cleanupChecks deletes the olders checks if the number of checks exceeds the limit. +func (r *Runner) cleanupChecks(ctx context.Context) error { + list, err := r.client.List(ctx, r.namespace, resource.ListOptions{Limit: -1}) + if err != nil { + return err + } + + // organize checks by type + checksByType := map[string][]resource.Object{} + for _, check := range list.GetItems() { + labels := check.GetLabels() + checkType, ok := labels[checks.TypeLabel] + if !ok { + klog.Error("Check type not found in labels", "check", check) + continue + } + checksByType[checkType] = append(checksByType[checkType], check) + } + + for _, checks := range checksByType { + if len(checks) > r.maxHistory { + // Sort checks by creation time + sort.Slice(checks, func(i, j int) bool { + ti := checks[i].GetCreationTimestamp().Time + tj := checks[j].GetCreationTimestamp().Time + return ti.Before(tj) + }) + // Delete the oldest checks + for i := 0; i < len(checks)-r.maxHistory; i++ { + check := checks[i] + id := check.GetStaticMetadata().Identifier() + err := r.client.Delete(ctx, id, resource.DeleteOptions{}) + if err != nil { + return fmt.Errorf("error deleting check: %w", err) + } + } + } + } + + return nil +} + +func getEvaluationInterval(pluginConfig map[string]string) (time.Duration, error) { + evaluationInterval := defaultEvaluationInterval + configEvaluationInterval, ok := pluginConfig["evaluation_interval"] + if ok { + var err error + evaluationInterval, err = gtime.ParseDuration(configEvaluationInterval) + if err != nil { + return 0, fmt.Errorf("invalid evaluation interval: %w", err) + } + } + return evaluationInterval, nil +} + +func getMaxHistory(pluginConfig map[string]string) (int, error) { + maxHistory := defaultMaxHistory + configMaxHistory, ok := pluginConfig["max_history"] + if ok { + var err error + maxHistory, err = strconv.Atoi(configMaxHistory) + if err != nil { + return 0, fmt.Errorf("invalid max history: %w", err) + } + } + return maxHistory, nil +} diff --git a/apps/advisor/pkg/app/checkscheduler/checkscheduler_test.go b/apps/advisor/pkg/app/checkscheduler/checkscheduler_test.go new file mode 100644 index 00000000000..4fd5876b9ff --- /dev/null +++ b/apps/advisor/pkg/app/checkscheduler/checkscheduler_test.go @@ -0,0 +1,279 @@ +package checkscheduler + +import ( + "context" + "errors" + "fmt" + "math/rand/v2" + "testing" + "time" + + "github.com/grafana/grafana-app-sdk/resource" + advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestRunner_Run_ErrorOnList(t *testing.T) { + mockCheckService := &MockCheckService{} + mockClient := &MockClient{ + listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) { + return nil, errors.New("list error") + }, + createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) { + return &advisorv0alpha1.Check{}, nil + }, + } + + runner := &Runner{ + checkRegistry: mockCheckService, + client: mockClient, + } + + err := runner.Run(context.Background()) + assert.Error(t, err) +} + +func TestRunner_checkLastCreated_ErrorOnList(t *testing.T) { + mockClient := &MockClient{ + listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) { + return nil, errors.New("list error") + }, + } + + runner := &Runner{ + client: mockClient, + } + + lastCreated, err := runner.checkLastCreated(context.Background()) + assert.Error(t, err) + assert.True(t, lastCreated.IsZero()) +} + +func TestRunner_createChecks_ErrorOnCreate(t *testing.T) { + mockCheckService := &MockCheckService{ + checks: []checks.Check{ + &mockCheck{ + id: "check-1", + }, + }, + } + mockClient := &MockClient{ + createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) { + return nil, errors.New("create error") + }, + } + + runner := &Runner{ + checkRegistry: mockCheckService, + client: mockClient, + } + + err := runner.createChecks(context.Background()) + assert.Error(t, err) +} + +func TestRunner_createChecks_Success(t *testing.T) { + mockCheckService := &MockCheckService{ + checks: []checks.Check{ + &mockCheck{ + id: "check-1", + }, + }, + } + mockClient := &MockClient{ + createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) { + return &advisorv0alpha1.Check{}, nil + }, + } + + runner := &Runner{ + checkRegistry: mockCheckService, + client: mockClient, + } + + err := runner.createChecks(context.Background()) + assert.NoError(t, err) +} + +func TestRunner_cleanupChecks_ErrorOnList(t *testing.T) { + mockClient := &MockClient{ + listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) { + return nil, errors.New("list error") + }, + } + + runner := &Runner{ + client: mockClient, + } + + err := runner.cleanupChecks(context.Background()) + assert.Error(t, err) +} + +func TestRunner_cleanupChecks_WithinMax(t *testing.T) { + mockClient := &MockClient{ + listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) { + return &advisorv0alpha1.CheckList{ + Items: []advisorv0alpha1.Check{{}, {}}, + }, nil + }, + deleteFunc: func(ctx context.Context, identifier resource.Identifier, options resource.DeleteOptions) error { + return fmt.Errorf("shouldn't be called") + }, + } + + runner := &Runner{ + client: mockClient, + } + + err := runner.cleanupChecks(context.Background()) + assert.NoError(t, err) +} + +func TestRunner_cleanupChecks_ErrorOnDelete(t *testing.T) { + mockClient := &MockClient{ + listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) { + items := make([]advisorv0alpha1.Check, 0, defaultMaxHistory+1) + for i := 0; i < defaultMaxHistory+1; i++ { + item := advisorv0alpha1.Check{} + item.ObjectMeta.SetLabels(map[string]string{ + checks.TypeLabel: "mock", + }) + items = append(items, item) + } + return &advisorv0alpha1.CheckList{ + Items: items, + }, nil + }, + deleteFunc: func(ctx context.Context, identifier resource.Identifier, options resource.DeleteOptions) error { + return errors.New("delete error") + }, + } + + runner := &Runner{ + client: mockClient, + maxHistory: defaultMaxHistory, + } + err := runner.cleanupChecks(context.Background()) + assert.ErrorContains(t, err, "delete error") +} + +func TestRunner_cleanupChecks_Success(t *testing.T) { + itemsDeleted := []string{} + items := make([]advisorv0alpha1.Check, 0, defaultMaxHistory+1) + for i := 0; i < defaultMaxHistory+1; i++ { + item := advisorv0alpha1.Check{} + item.ObjectMeta.SetName(fmt.Sprintf("check-%d", i)) + item.ObjectMeta.SetLabels(map[string]string{ + checks.TypeLabel: "mock", + }) + item.ObjectMeta.SetCreationTimestamp(metav1.NewTime(time.Time{}.Add(time.Duration(i) * time.Hour))) + items = append(items, item) + } + // shuffle the items to ensure the oldest are deleted + rand.Shuffle(len(items), func(i, j int) { items[i], items[j] = items[j], items[i] }) + + mockClient := &MockClient{ + listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) { + return &advisorv0alpha1.CheckList{ + Items: items, + }, nil + }, + deleteFunc: func(ctx context.Context, identifier resource.Identifier, options resource.DeleteOptions) error { + itemsDeleted = append(itemsDeleted, identifier.Name) + return nil + }, + } + + runner := &Runner{ + client: mockClient, + maxHistory: defaultMaxHistory, + } + err := runner.cleanupChecks(context.Background()) + assert.NoError(t, err) + assert.Equal(t, []string{"check-0"}, itemsDeleted) +} + +func Test_getEvaluationInterval(t *testing.T) { + t.Run("default", func(t *testing.T) { + interval, err := getEvaluationInterval(map[string]string{}) + assert.NoError(t, err) + assert.Equal(t, 24*time.Hour, interval) + }) + + t.Run("invalid", func(t *testing.T) { + interval, err := getEvaluationInterval(map[string]string{"evaluation_interval": "invalid"}) + assert.Error(t, err) + assert.Zero(t, interval) + }) + + t.Run("custom", func(t *testing.T) { + interval, err := getEvaluationInterval(map[string]string{"evaluation_interval": "1h"}) + assert.NoError(t, err) + assert.Equal(t, time.Hour, interval) + }) +} + +func Test_getMaxHistory(t *testing.T) { + t.Run("default", func(t *testing.T) { + history, err := getMaxHistory(map[string]string{}) + assert.NoError(t, err) + assert.Equal(t, 10, history) + }) + + t.Run("invalid", func(t *testing.T) { + history, err := getMaxHistory(map[string]string{"max_history": "invalid"}) + assert.Error(t, err) + assert.Zero(t, history) + }) + + t.Run("custom", func(t *testing.T) { + history, err := getMaxHistory(map[string]string{"max_history": "5"}) + assert.NoError(t, err) + assert.Equal(t, 5, history) + }) +} + +type MockCheckService struct { + checks []checks.Check +} + +func (m *MockCheckService) Checks() []checks.Check { + return m.checks +} + +type MockClient struct { + resource.Client + listFunc func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) + createFunc func(ctx context.Context, identifier resource.Identifier, obj resource.Object, options resource.CreateOptions) (resource.Object, error) + deleteFunc func(ctx context.Context, identifier resource.Identifier, options resource.DeleteOptions) error +} + +func (m *MockClient) List(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) { + return m.listFunc(ctx, namespace, options) +} + +func (m *MockClient) Create(ctx context.Context, identifier resource.Identifier, obj resource.Object, options resource.CreateOptions) (resource.Object, error) { + return m.createFunc(ctx, identifier, obj, options) +} + +func (m *MockClient) Delete(ctx context.Context, identifier resource.Identifier, options resource.DeleteOptions) error { + return m.deleteFunc(ctx, identifier, options) +} + +type mockCheck struct { + checks.Check + + id string + steps []checks.Step +} + +func (m *mockCheck) ID() string { + return m.id +} + +func (m *mockCheck) Steps() []checks.Step { + return m.steps +} diff --git a/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer.go b/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer.go new file mode 100644 index 00000000000..2ed38e51ecb --- /dev/null +++ b/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer.go @@ -0,0 +1,91 @@ +package checktyperegisterer + +import ( + "context" + "fmt" + + "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/k8s" + "github.com/grafana/grafana-app-sdk/resource" + advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checkregistry" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Runner is a "runnable" app used to be able to expose and API endpoint +// with the existing checks types. This does not need to be a CRUD resource, but it is +// the only way existing at the moment to expose the check types. +type Runner struct { + checkRegistry checkregistry.CheckService + client resource.Client + namespace string +} + +// NewRunner creates a new Runner. +func New(cfg app.Config) (app.Runnable, error) { + // Read config + specificConfig, ok := cfg.SpecificConfig.(checkregistry.AdvisorAppConfig) + if !ok { + return nil, fmt.Errorf("invalid config type") + } + checkRegistry := specificConfig.CheckRegistry + namespace, err := checks.GetNamespace(specificConfig.StackID) + if err != nil { + return nil, err + } + + // Prepare storage client + clientGenerator := k8s.NewClientRegistry(cfg.KubeConfig, k8s.ClientConfig{}) + client, err := clientGenerator.ClientFor(advisorv0alpha1.CheckTypeKind()) + if err != nil { + return nil, err + } + + return &Runner{ + checkRegistry: checkRegistry, + client: client, + namespace: namespace, + }, nil +} + +func (r *Runner) Run(ctx context.Context) error { + for _, t := range r.checkRegistry.Checks() { + steps := t.Steps() + stepTypes := make([]advisorv0alpha1.CheckTypeStep, len(steps)) + for i, s := range steps { + stepTypes[i] = advisorv0alpha1.CheckTypeStep{ + Title: s.Title(), + Description: s.Description(), + StepID: s.ID(), + Resolution: s.Resolution(), + } + } + obj := &advisorv0alpha1.CheckType{ + ObjectMeta: metav1.ObjectMeta{ + Name: t.ID(), + Namespace: r.namespace, + }, + Spec: advisorv0alpha1.CheckTypeSpec{ + Name: t.ID(), + Steps: stepTypes, + }, + } + id := obj.GetStaticMetadata().Identifier() + _, err := r.client.Create(ctx, id, obj, resource.CreateOptions{}) + if err != nil { + if errors.IsAlreadyExists(err) { + // Already exists, update + _, err = r.client.Update(ctx, id, obj, resource.UpdateOptions{}) + if err != nil { + return err + } else { + continue + } + } + return err + } + } + return nil +} diff --git a/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer_test.go b/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer_test.go new file mode 100644 index 00000000000..97fc7742504 --- /dev/null +++ b/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer_test.go @@ -0,0 +1,196 @@ +package checktyperegisterer + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/grafana/grafana-app-sdk/resource" + advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + k8sErrs "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func TestCheckTypesRegisterer_Run(t *testing.T) { + tests := []struct { + name string + checks []checks.Check + createFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) + updateFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) + expectedErr error + }{ + { + name: "successful create", + checks: []checks.Check{ + &mockCheck{ + id: "check1", + steps: []checks.Step{ + &mockStep{id: "step1", title: "Step 1", description: "Description 1"}, + }, + }, + }, + createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) { + return obj, nil + }, + updateFunc: nil, + expectedErr: nil, + }, + { + name: "create already exists, successful update", + checks: []checks.Check{ + &mockCheck{ + id: "check1", + steps: []checks.Step{ + &mockStep{id: "step1", title: "Step 1", description: "Description 1"}, + }, + }, + }, + createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) { + return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName()) + }, + updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) { + return obj, nil + }, + expectedErr: nil, + }, + { + name: "create error", + checks: []checks.Check{ + &mockCheck{ + id: "check1", + steps: []checks.Step{ + &mockStep{id: "step1", title: "Step 1", description: "Description 1"}, + }, + }, + }, + createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) { + return nil, errors.New("create error") + }, + updateFunc: nil, + expectedErr: errors.New("create error"), + }, + { + name: "update error", + checks: []checks.Check{ + &mockCheck{ + id: "check1", + steps: []checks.Step{ + &mockStep{id: "step1", title: "Step 1", description: "Description 1"}, + }, + }, + }, + createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) { + return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName()) + }, + updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) { + return nil, errors.New("update error") + }, + expectedErr: errors.New("update error"), + }, + { + name: "custom namespace", + checks: []checks.Check{ + &mockCheck{ + id: "check1", + steps: []checks.Step{ + &mockStep{id: "step1", title: "Step 1", description: "Description 1"}, + }, + }, + }, + createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) { + if obj.GetNamespace() != "custom-namespace" { + return nil, fmt.Errorf("expected namespace %s, got %s", "custom-namespace", obj.GetNamespace()) + } + return obj, nil + }, + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &Runner{ + checkRegistry: &mockCheckRegistry{checks: tt.checks}, + client: &mockClient{ + createFunc: tt.createFunc, + updateFunc: tt.updateFunc, + }, + namespace: "custom-namespace", + } + err := r.Run(context.Background()) + if err != nil { + if tt.expectedErr == nil { + t.Errorf("unexpected error: %v", err) + } else if err.Error() != tt.expectedErr.Error() { + t.Errorf("expected error: %v, got: %v", tt.expectedErr, err) + } + } + }) + } +} + +type mockCheckRegistry struct { + checks []checks.Check +} + +func (m *mockCheckRegistry) Checks() []checks.Check { + return m.checks +} + +type mockCheck struct { + checks.Check + + id string + steps []checks.Step +} + +func (m *mockCheck) ID() string { + return m.id +} + +func (m *mockCheck) Steps() []checks.Step { + return m.steps +} + +type mockStep struct { + id string + title string + description string +} + +func (m *mockStep) ID() string { + return m.id +} + +func (m *mockStep) Title() string { + return m.title +} + +func (m *mockStep) Description() string { + return m.description +} + +func (m *mockStep) Resolution() string { + return "" +} + +func (m *mockStep) Run(ctx context.Context, obj *advisorv0alpha1.CheckSpec, item any) (*advisorv0alpha1.CheckReportFailure, error) { + return nil, nil +} + +type mockClient struct { + resource.Client + + createFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) + updateFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) +} + +func (m *mockClient) Create(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) { + return m.createFunc(ctx, id, obj, opts) +} + +func (m *mockClient) Update(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) { + return m.updateFunc(ctx, id, obj, opts) +} diff --git a/apps/advisor/pkg/app/utils.go b/apps/advisor/pkg/app/utils.go new file mode 100644 index 00000000000..8c1b6c00eb8 --- /dev/null +++ b/apps/advisor/pkg/app/utils.go @@ -0,0 +1,143 @@ +package app + +import ( + "context" + "errors" + "fmt" + "sync" + + claims "github.com/grafana/authlib/types" + "github.com/grafana/grafana-app-sdk/resource" + advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/grafana/pkg/apimachinery/utils" + "github.com/grafana/grafana/pkg/services/user" +) + +func getCheck(obj resource.Object, checkMap map[string]checks.Check) (checks.Check, error) { + labels := obj.GetLabels() + objTypeLabel, ok := labels[checks.TypeLabel] + if !ok { + return nil, errors.New("missing check type as label") + } + c, ok := checkMap[objTypeLabel] + if !ok { + supportedTypes := "" + for k := range checkMap { + supportedTypes += k + ", " + } + return nil, fmt.Errorf("unknown check type %s. Supported types are: %s", objTypeLabel, supportedTypes) + } + + return c, nil +} + +func getStatusAnnotation(obj resource.Object) string { + return obj.GetAnnotations()[checks.StatusAnnotation] +} + +func setStatusAnnotation(ctx context.Context, client resource.Client, obj resource.Object, status string) error { + annotations := obj.GetAnnotations() + annotations[checks.StatusAnnotation] = status + return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{ + Operations: []resource.PatchOperation{{ + Operation: resource.PatchOpAdd, + Path: "/metadata/annotations", + Value: annotations, + }}, + }, resource.PatchOptions{}, obj) +} + +func processCheck(ctx context.Context, client resource.Client, obj resource.Object, check checks.Check) error { + status := getStatusAnnotation(obj) + if status != "" { + // Check already processed + return nil + } + c, ok := obj.(*advisorv0alpha1.Check) + if !ok { + return fmt.Errorf("invalid object type") + } + // Populate ctx with the user that created the check + meta, err := utils.MetaAccessor(obj) + if err != nil { + return err + } + createdBy := meta.GetCreatedBy() + typ, uid, err := claims.ParseTypeID(createdBy) + if err != nil { + return err + } + ctx = identity.WithRequester(ctx, &user.SignedInUser{ + UserUID: uid, + FallbackType: typ, + }) + // Get the items to check + items, err := check.Items(ctx) + if err != nil { + setErr := setStatusAnnotation(ctx, client, obj, "error") + if setErr != nil { + return setErr + } + return fmt.Errorf("error initializing check: %w", err) + } + // Run the steps + steps := check.Steps() + failures, err := runStepsInParallel(ctx, &c.Spec, steps, items) + if err != nil { + setErr := setStatusAnnotation(ctx, client, obj, "error") + if setErr != nil { + return setErr + } + return fmt.Errorf("error running steps: %w", err) + } + + report := &advisorv0alpha1.CheckV0alpha1StatusReport{ + Failures: failures, + Count: int64(len(items)), + } + err = setStatusAnnotation(ctx, client, obj, "processed") + if err != nil { + return err + } + return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{ + Operations: []resource.PatchOperation{{ + Operation: resource.PatchOpAdd, + Path: "/status/report", + Value: *report, + }}, + }, resource.PatchOptions{}, obj) +} + +func runStepsInParallel(ctx context.Context, spec *advisorv0alpha1.CheckSpec, steps []checks.Step, items []any) ([]advisorv0alpha1.CheckReportFailure, error) { + reportFailures := []advisorv0alpha1.CheckReportFailure{} + var internalErr error + var wg sync.WaitGroup + var mu sync.Mutex + // Avoid too many concurrent requests + limit := make(chan struct{}, 10) + + for _, step := range steps { + for _, item := range items { + wg.Add(1) + limit <- struct{}{} + go func(step checks.Step, item any) { + defer wg.Done() + defer func() { <-limit }() + stepErr, err := step.Run(ctx, spec, item) + mu.Lock() + defer mu.Unlock() + if err != nil { + internalErr = fmt.Errorf("error running step %s: %w", step.ID(), err) + return + } + if stepErr != nil { + reportFailures = append(reportFailures, *stepErr) + } + }(step, item) + } + } + wg.Wait() + return reportFailures, internalErr +} diff --git a/apps/advisor/pkg/app/utils_test.go b/apps/advisor/pkg/app/utils_test.go new file mode 100644 index 00000000000..b3b8d36f60b --- /dev/null +++ b/apps/advisor/pkg/app/utils_test.go @@ -0,0 +1,200 @@ +package app + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/grafana/grafana-app-sdk/resource" + advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/pkg/apimachinery/utils" + "github.com/stretchr/testify/assert" +) + +func TestGetCheck(t *testing.T) { + obj := &advisorv0alpha1.Check{} + obj.SetLabels(map[string]string{checks.TypeLabel: "testType"}) + + checkMap := map[string]checks.Check{ + "testType": &mockCheck{}, + } + + check, err := getCheck(obj, checkMap) + assert.NoError(t, err) + assert.NotNil(t, check) +} + +func TestGetCheck_MissingLabel(t *testing.T) { + obj := &advisorv0alpha1.Check{} + checkMap := map[string]checks.Check{} + + _, err := getCheck(obj, checkMap) + assert.Error(t, err) + assert.Equal(t, "missing check type as label", err.Error()) +} + +func TestGetCheck_UnknownType(t *testing.T) { + obj := &advisorv0alpha1.Check{} + obj.SetLabels(map[string]string{checks.TypeLabel: "unknownType"}) + + checkMap := map[string]checks.Check{ + "testType": &mockCheck{}, + } + + _, err := getCheck(obj, checkMap) + assert.Error(t, err) + assert.Contains(t, err.Error(), "unknown check type unknownType") +} + +func TestSetStatusAnnotation(t *testing.T) { + obj := &advisorv0alpha1.Check{} + obj.SetAnnotations(map[string]string{}) + client := &mockClient{} + ctx := context.TODO() + + err := setStatusAnnotation(ctx, client, obj, "processed") + assert.NoError(t, err) + assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation]) +} + +func TestProcessCheck(t *testing.T) { + obj := &advisorv0alpha1.Check{} + obj.SetAnnotations(map[string]string{}) + meta, err := utils.MetaAccessor(obj) + if err != nil { + t.Fatal(err) + } + meta.SetCreatedBy("user:1") + client := &mockClient{} + ctx := context.TODO() + check := &mockCheck{ + items: []any{"item"}, + } + + err = processCheck(ctx, client, obj, check) + assert.NoError(t, err) + assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation]) +} + +func TestProcessMultipleCheckItems(t *testing.T) { + obj := &advisorv0alpha1.Check{} + obj.SetAnnotations(map[string]string{}) + meta, err := utils.MetaAccessor(obj) + if err != nil { + t.Fatal(err) + } + meta.SetCreatedBy("user:1") + client := &mockClient{} + ctx := context.TODO() + items := make([]any, 100) + for i := range items { + if i%2 == 0 { + items[i] = fmt.Sprintf("item-%d", i) + } else { + items[i] = errors.New("error") + } + } + check := &mockCheck{ + items: items, + } + + err = processCheck(ctx, client, obj, check) + assert.NoError(t, err) + assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation]) + r := client.lastValue.(advisorv0alpha1.CheckV0alpha1StatusReport) + assert.Equal(t, r.Count, int64(100)) + assert.Len(t, r.Failures, 50) +} + +func TestProcessCheck_AlreadyProcessed(t *testing.T) { + obj := &advisorv0alpha1.Check{} + obj.SetAnnotations(map[string]string{checks.StatusAnnotation: "processed"}) + client := &mockClient{} + ctx := context.TODO() + check := &mockCheck{} + + err := processCheck(ctx, client, obj, check) + assert.NoError(t, err) +} + +func TestProcessCheck_RunError(t *testing.T) { + obj := &advisorv0alpha1.Check{} + obj.SetAnnotations(map[string]string{}) + meta, err := utils.MetaAccessor(obj) + if err != nil { + t.Fatal(err) + } + meta.SetCreatedBy("user:1") + client := &mockClient{} + ctx := context.TODO() + + check := &mockCheck{ + items: []any{"item"}, + err: errors.New("run error"), + } + + err = processCheck(ctx, client, obj, check) + assert.Error(t, err) + assert.Equal(t, "error", obj.GetAnnotations()[checks.StatusAnnotation]) +} + +type mockClient struct { + resource.Client + lastValue any +} + +func (m *mockClient) PatchInto(ctx context.Context, id resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions, obj resource.Object) error { + m.lastValue = req.Operations[0].Value + return nil +} + +type mockCheck struct { + err error + items []any +} + +func (m *mockCheck) ID() string { + return "mock" +} + +func (m *mockCheck) Items(ctx context.Context) ([]any, error) { + return m.items, nil +} + +func (m *mockCheck) Steps() []checks.Step { + return []checks.Step{ + &mockStep{err: m.err}, + } +} + +type mockStep struct { + err error +} + +func (m *mockStep) Run(ctx context.Context, obj *advisorv0alpha1.CheckSpec, items any) (*advisorv0alpha1.CheckReportFailure, error) { + if m.err != nil { + return nil, m.err + } + if _, ok := items.(error); ok { + return &advisorv0alpha1.CheckReportFailure{}, nil + } + return nil, nil +} + +func (m *mockStep) Title() string { + return "mock" +} + +func (m *mockStep) Description() string { + return "mock" +} + +func (m *mockStep) Resolution() string { + return "mock" +} + +func (m *mockStep) ID() string { + return "mock" +} diff --git a/apps/advisor/plugin/README.md b/apps/advisor/plugin/README.md new file mode 100644 index 00000000000..5b9e2bc8744 --- /dev/null +++ b/apps/advisor/plugin/README.md @@ -0,0 +1,7 @@ +This folder contains the automatically generated types for the frontend that are used in the app plugin. + +To update the types: + +1. Make any necessary changes in the `kinds` directory +2. Run `make generate` +3. Copy the `plugin` directory to the frontend app: https://github.com/grafana/grafana-advisor-app/tree/main/src/plugin diff --git a/apps/alerting/notifications/go.mod b/apps/alerting/notifications/go.mod index 61093d88f4b..74dc1dfe028 100644 --- a/apps/alerting/notifications/go.mod +++ b/apps/alerting/notifications/go.mod @@ -6,9 +6,9 @@ replace github.com/grafana/grafana => ../../.. require ( github.com/grafana/grafana v11.4.0-00010101000000-000000000000+incompatible - github.com/grafana/grafana-app-sdk v0.30.0 - k8s.io/apimachinery v0.32.0 - k8s.io/apiserver v0.32.0 + github.com/grafana/grafana-app-sdk v0.31.0 + k8s.io/apimachinery v0.32.1 + k8s.io/apiserver v0.32.1 k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f ) @@ -36,7 +36,7 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250121113133-e747350fee2d // indirect + github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250220154326-6e5de80ef295 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -52,7 +52,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.61.0 // indirect + github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -61,35 +61,35 @@ require ( go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect go.etcd.io/etcd/client/v3 v3.5.16 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect - go.opentelemetry.io/otel v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.33.0 // indirect - go.opentelemetry.io/otel/sdk v1.33.0 // indirect - go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/sdk v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.32.0 // indirect + golang.org/x/crypto v0.35.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/net v0.34.0 // indirect - golang.org/x/oauth2 v0.25.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/term v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/oauth2 v0.27.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/term v0.29.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect - google.golang.org/grpc v1.69.4 // indirect - google.golang.org/protobuf v1.36.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/grpc v1.70.0 // indirect + google.golang.org/protobuf v1.36.4 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.32.0 // indirect - k8s.io/client-go v0.32.0 // indirect - k8s.io/component-base v0.32.0 // indirect + k8s.io/api v0.32.1 // indirect + k8s.io/client-go v0.32.1 // indirect + k8s.io/component-base v0.32.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect diff --git a/apps/alerting/notifications/go.sum b/apps/alerting/notifications/go.sum index a140089cc39..cae5be00879 100644 --- a/apps/alerting/notifications/go.sum +++ b/apps/alerting/notifications/go.sum @@ -71,10 +71,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/grafana-app-sdk v0.30.0 h1:Hqn2pETu2mQ4RpWkZYEQfu01P7xd1Z1Gj+HX/8aB0tw= -github.com/grafana/grafana-app-sdk v0.30.0/go.mod h1:jhfqNIovb+Mes2vdMf9iMCWQkp1GTNtyNuExONtiNuk= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250121113133-e747350fee2d h1:0WJ4uZ3gtbX7S5RGMarA+0TccmB7qEPhhNm34NcfBzs= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250121113133-e747350fee2d/go.mod h1:PNmbi49lVrv2b2I8pdu46dTs2728lKEbnVuQD8I5MnM= +github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDRgZwHkksFk= +github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250220154326-6e5de80ef295 h1:bOKkAzmkCnK2EN0LGsHoq5HyfXMLKXrr8+q35Fh+XQI= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250220154326-6e5de80ef295/go.mod h1:1iWqntBW91TsiyqneHrqTFV12vd1Tei+f5VY2ovkNAo= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 h1:uGoIog/wiQHI9GAxXO5TJbT0wWKH3O9HhOJW1F9c3fY= @@ -130,8 +130,8 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= -github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= @@ -178,24 +178,24 @@ go.etcd.io/etcd/server/v3 v3.5.16 h1:d0/SAdJ3vVsZvF8IFVb1k8zqMZ+heGcNfft71ul9GWE go.etcd.io/etcd/server/v3 v3.5.16/go.mod h1:ynhyZZpdDp1Gq49jkUg5mfkDWZwXnn3eIqCqtJnrD/s= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= -go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= -go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= -go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= -go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= -go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= -go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= -go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= -go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= -go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -207,8 +207,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -223,11 +223,11 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -237,14 +237,14 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -262,15 +262,15 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU= google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4= -google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA= -google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= -google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= -google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -283,16 +283,16 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= -k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= -k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= -k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/apiserver v0.32.0 h1:VJ89ZvQZ8p1sLeiWdRJpRD6oLozNZD2+qVSLi+ft5Qs= -k8s.io/apiserver v0.32.0/go.mod h1:HFh+dM1/BE/Hm4bS4nTXHVfN6Z6tFIZPi649n83b4Ag= -k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= -k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= -k8s.io/component-base v0.32.0 h1:d6cWHZkCiiep41ObYQS6IcgzOUQUNpywm39KVYaUqzU= -k8s.io/component-base v0.32.0/go.mod h1:JLG2W5TUxUu5uDyKiH2R/7NnxJo1HlPoRIIbVLkK5eM= +k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= +k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= +k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= +k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/apiserver v0.32.1 h1:oo0OozRos66WFq87Zc5tclUX2r0mymoVHRq8JmR7Aak= +k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw= +k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= +k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= +k8s.io/component-base v0.32.1 h1:/5IfJ0dHIKBWysGV0yKTFfacZ5yNV1sulPh3ilJjRZk= +k8s.io/component-base v0.32.1/go.mod h1:j1iMMHi/sqAHeG5z+O9BFNCF698a1u0186zkjMZQ28w= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= diff --git a/apps/investigation/Makefile b/apps/investigation/Makefile deleted file mode 100644 index b4ea1f97c89..00000000000 --- a/apps/investigation/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -.PHONY: generate -generate: - @grafana-app-sdk generate -g ./pkg/apis --kindgrouping=group --postprocess --crdencoding none diff --git a/apps/investigation/kinds/cue.mod/module.cue b/apps/investigation/kinds/cue.mod/module.cue deleted file mode 100644 index 80ce68f4833..00000000000 --- a/apps/investigation/kinds/cue.mod/module.cue +++ /dev/null @@ -1,2 +0,0 @@ -module: "github.com/grafana/grafana/apps/investigation/kinds" -language: version: "v0.8.2" diff --git a/apps/investigation/kinds/investigation.cue b/apps/investigation/kinds/investigation.cue deleted file mode 100644 index 64d4b9b3105..00000000000 --- a/apps/investigation/kinds/investigation.cue +++ /dev/null @@ -1,96 +0,0 @@ -package kinds - -// This is our Investigation definition, which contains metadata about the kind, and the kind's schema -investigation: { - group: "investigation" - kind: "Investigation" - pluralName: "Investigations" - scope: "Namespaced" - - apiResource: { - groupOverride: "investigation.grafana.app" - } - - codegen: { - frontend: false - backend: true - } - - current: "v1alpha1" - versions: { - "v1alpha1": { - version: "v1alpha1" - schema: { - // spec is the schema of our resource. The spec should include all the user-ediable information for the kind. - spec: #InvestigationSpec - } - } - } -} - -#InvestigationSpec: { - title: string - status: "open" | "closed" - items: [...#InvestigationItem] -} - -// InvestigationItem is an item in an investigation. -#InvestigationItem: { - id: string - title: string - // type is the type of the item "timeseries", "heatmap", "log-table" (not an enum to allow for future extensions). - type: string - // url is the URL to the item. - url: string - // origin is where the item was created from. - origin: string // "explore-metrics", "explore-logs", "explore-traces" (not an enum to allow for future extensions) - // iconPath (optional) is the path to the icon for the item. - iconPath?: string - // timeRange (optional) is the time range of the item. - timeRange: #AbsoluteTimeRange - // note (optional) is a comment on the item. - note?: [...#Comment] - // queryType is the type of the query used to generate this item. - queryType: "logs" | "metrics" - // dataQuery contains the query used to generate this item. - dataQuery: #DataQueryLogs | #DataQueryMetrics -} - -// DataQueryLogs is a data query for logs. -#DataQueryLogs: { - // refId is the reference ID of the query. - refId: string - // datasource is the datasource of the query. - datasource: #DatasourceRef - // expr is the expression of the query. - expr: string - // maxLines (optional) is used to limit the number of log rows returned. - maxLines?: int64 -} - -// DataQueryMetrics is a data query for metrics. -#DataQueryMetrics: { - refId: string - datasource: #DatasourceRef - expr: string -} - -// Comment is a comment on an investigation item. -#Comment: { - authorUserID: string - bodyMarkdown: string -} - -// DatasourceRef is a reference to a datasource. -#DatasourceRef: { - uid: string - type: string - apiVersion: string - name: string -} - -// AbsoluteTimeRange is a time range specified by absolute timestamps. -#AbsoluteTimeRange: { - from: number - to: number -} diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_metadata_gen.go b/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_metadata_gen.go deleted file mode 100644 index 8a8e44d35ac..00000000000 --- a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_metadata_gen.go +++ /dev/null @@ -1,32 +0,0 @@ -package v1alpha1 - -import ( - "time" -) - -// InvestigationMetadata defines model for InvestigationMetadata. -type InvestigationMetadata struct { - CreatedBy string `json:"createdBy"` - CreationTimestamp time.Time `json:"creationTimestamp"` - DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` - Finalizers []string `json:"finalizers"` - Generation int64 `json:"generation"` - Labels map[string]string `json:"labels"` - ResourceVersion string `json:"resourceVersion"` - Uid string `json:"uid"` - UpdateTimestamp time.Time `json:"updateTimestamp"` - UpdatedBy string `json:"updatedBy"` -} - -// _kubeObjectMetadata is metadata found in a kubernetes object's metadata field. -// It is not exhaustive and only includes fields which may be relevant to a kind's implementation, -// As it is also intended to be generic enough to function with any API Server. -type InvestigationKubeObjectMetadata struct { - CreationTimestamp time.Time `json:"creationTimestamp"` - DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` - Finalizers []string `json:"finalizers"` - Generation int64 `json:"generation"` - Labels map[string]string `json:"labels"` - ResourceVersion string `json:"resourceVersion"` - Uid string `json:"uid"` -} diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_spec_gen.go b/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_spec_gen.go deleted file mode 100644 index 1ed1173238b..00000000000 --- a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_spec_gen.go +++ /dev/null @@ -1,107 +0,0 @@ -package v1alpha1 - -// Defines values for InvestigationInvestigationItemQueryType. -const ( - InvestigationInvestigationItemQueryTypeLogs InvestigationInvestigationItemQueryType = "logs" - InvestigationInvestigationItemQueryTypeMetrics InvestigationInvestigationItemQueryType = "metrics" -) - -// Defines values for InvestigationSpecStatus. -const ( - InvestigationSpecStatusClosed InvestigationSpecStatus = "closed" - InvestigationSpecStatusOpen InvestigationSpecStatus = "open" -) - -// AbsoluteTimeRange is a time range specified by absolute timestamps. -// +k8s:openapi-gen=true -type InvestigationAbsoluteTimeRange struct { - From float32 `json:"from"` - To float32 `json:"to"` -} - -// Comment is a comment on an investigation item. -// +k8s:openapi-gen=true -type InvestigationComment struct { - AuthorUserID string `json:"authorUserID"` - BodyMarkdown string `json:"bodyMarkdown"` -} - -// DataQueryLogs is a data query for logs. -// +k8s:openapi-gen=true -type InvestigationDataQueryLogs struct { - // DatasourceRef is a reference to a datasource. - Datasource InvestigationDatasourceRef `json:"datasource"` - - // expr is the expression of the query. - Expr string `json:"expr"` - - // maxLines (optional) is used to limit the number of log rows returned. - MaxLines *int64 `json:"maxLines,omitempty"` - - // refId is the reference ID of the query. - RefId string `json:"refId"` -} - -// DataQueryMetrics is a data query for metrics. -// +k8s:openapi-gen=true -type InvestigationDataQueryMetrics struct { - // DatasourceRef is a reference to a datasource. - Datasource InvestigationDatasourceRef `json:"datasource"` - Expr string `json:"expr"` - RefId string `json:"refId"` -} - -// DatasourceRef is a reference to a datasource. -// +k8s:openapi-gen=true -type InvestigationDatasourceRef struct { - ApiVersion string `json:"apiVersion"` - Name string `json:"name"` - Type string `json:"type"` - Uid string `json:"uid"` -} - -// InvestigationItem is an item in an investigation. -// +k8s:openapi-gen=true -type InvestigationInvestigationItem struct { - // dataQuery contains the query used to generate this item. - DataQuery interface{} `json:"dataQuery"` - - // iconPath (optional) is the path to the icon for the item. - IconPath *string `json:"iconPath,omitempty"` - Id string `json:"id"` - - // note (optional) is a comment on the item. - Note []InvestigationComment `json:"note,omitempty"` - - // origin is where the item was created from. - Origin string `json:"origin"` - - // queryType is the type of the query used to generate this item. - QueryType InvestigationInvestigationItemQueryType `json:"queryType"` - - // AbsoluteTimeRange is a time range specified by absolute timestamps. - TimeRange InvestigationAbsoluteTimeRange `json:"timeRange"` - Title string `json:"title"` - - // type is the type of the item "timeseries", "heatmap", "log-table" (not an enum to allow for future extensions). - Type string `json:"type"` - - // url is the URL to the item. - Url string `json:"url"` -} - -// InvestigationInvestigationItemQueryType queryType is the type of the query used to generate this item. -// +k8s:openapi-gen=true -type InvestigationInvestigationItemQueryType string - -// InvestigationSpec defines model for InvestigationSpec. -// +k8s:openapi-gen=true -type InvestigationSpec struct { - Items []InvestigationInvestigationItem `json:"items"` - Status InvestigationSpecStatus `json:"status"` - Title string `json:"title"` -} - -// InvestigationSpecStatus defines model for InvestigationSpec.Status. -// +k8s:openapi-gen=true -type InvestigationSpecStatus string diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_status_gen.go b/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_status_gen.go deleted file mode 100644 index 3f0870f9c04..00000000000 --- a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_status_gen.go +++ /dev/null @@ -1,70 +0,0 @@ -package v1alpha1 - -// Defines values for InvestigationOperatorStateState. -const ( - InvestigationOperatorStateStateFailed InvestigationOperatorStateState = "failed" - InvestigationOperatorStateStateInProgress InvestigationOperatorStateState = "in_progress" - InvestigationOperatorStateStateSuccess InvestigationOperatorStateState = "success" -) - -// Defines values for InvestigationstatusOperatorStateState. -const ( - InvestigationstatusOperatorStateStateFailed InvestigationstatusOperatorStateState = "failed" - InvestigationstatusOperatorStateStateInProgress InvestigationstatusOperatorStateState = "in_progress" - InvestigationstatusOperatorStateStateSuccess InvestigationstatusOperatorStateState = "success" -) - -// InvestigationOperatorState defines model for InvestigationOperatorState. -// +k8s:openapi-gen=true -type InvestigationOperatorState struct { - // descriptiveState is an optional more descriptive state field which has no requirements on format - DescriptiveState *string `json:"descriptiveState,omitempty"` - - // details contains any extra information that is operator-specific - Details map[string]interface{} `json:"details,omitempty"` - - // lastEvaluation is the ResourceVersion last evaluated - LastEvaluation string `json:"lastEvaluation"` - - // state describes the state of the lastEvaluation. - // It is limited to three possible states for machine evaluation. - State InvestigationOperatorStateState `json:"state"` -} - -// InvestigationOperatorStateState state describes the state of the lastEvaluation. -// It is limited to three possible states for machine evaluation. -// +k8s:openapi-gen=true -type InvestigationOperatorStateState string - -// InvestigationStatus defines model for InvestigationStatus. -// +k8s:openapi-gen=true -type InvestigationStatus struct { - // additionalFields is reserved for future use - AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` - - // operatorStates is a map of operator ID to operator state evaluations. - // Any operator which consumes this kind SHOULD add its state evaluation information to this field. - OperatorStates map[string]InvestigationstatusOperatorState `json:"operatorStates,omitempty"` -} - -// InvestigationstatusOperatorState defines model for Investigationstatus.#OperatorState. -// +k8s:openapi-gen=true -type InvestigationstatusOperatorState struct { - // descriptiveState is an optional more descriptive state field which has no requirements on format - DescriptiveState *string `json:"descriptiveState,omitempty"` - - // details contains any extra information that is operator-specific - Details map[string]interface{} `json:"details,omitempty"` - - // lastEvaluation is the ResourceVersion last evaluated - LastEvaluation string `json:"lastEvaluation"` - - // state describes the state of the lastEvaluation. - // It is limited to three possible states for machine evaluation. - State InvestigationstatusOperatorStateState `json:"state"` -} - -// InvestigationstatusOperatorStateState state describes the state of the lastEvaluation. -// It is limited to three possible states for machine evaluation. -// +k8s:openapi-gen=true -type InvestigationstatusOperatorStateState string diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/zz_openapi_gen.go b/apps/investigation/pkg/apis/investigation/v1alpha1/zz_openapi_gen.go deleted file mode 100644 index 356c42c01fc..00000000000 --- a/apps/investigation/pkg/apis/investigation/v1alpha1/zz_openapi_gen.go +++ /dev/null @@ -1,593 +0,0 @@ -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -// Code generated by grafana-app-sdk. DO NOT EDIT. - -package v1alpha1 - -import ( - common "k8s.io/kube-openapi/pkg/common" - spec "k8s.io/kube-openapi/pkg/validation/spec" -) - -func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { - return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.Investigation": schema_pkg_apis_investigation_v1alpha1_Investigation(ref), - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationAbsoluteTimeRange": schema_pkg_apis_investigation_v1alpha1_InvestigationAbsoluteTimeRange(ref), - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationComment": schema_pkg_apis_investigation_v1alpha1_InvestigationComment(ref), - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDataQueryLogs": schema_pkg_apis_investigation_v1alpha1_InvestigationDataQueryLogs(ref), - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDataQueryMetrics": schema_pkg_apis_investigation_v1alpha1_InvestigationDataQueryMetrics(ref), - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef": schema_pkg_apis_investigation_v1alpha1_InvestigationDatasourceRef(ref), - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationInvestigationItem": schema_pkg_apis_investigation_v1alpha1_InvestigationInvestigationItem(ref), - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationList": schema_pkg_apis_investigation_v1alpha1_InvestigationList(ref), - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationOperatorState": schema_pkg_apis_investigation_v1alpha1_InvestigationOperatorState(ref), - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationSpec": schema_pkg_apis_investigation_v1alpha1_InvestigationSpec(ref), - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationStatus": schema_pkg_apis_investigation_v1alpha1_InvestigationStatus(ref), - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationstatusOperatorState": schema_pkg_apis_investigation_v1alpha1_InvestigationstatusOperatorState(ref), - } -} - -func schema_pkg_apis_investigation_v1alpha1_Investigation(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationStatus"), - }, - }, - }, - Required: []string{"metadata", "spec", "status"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationSpec", "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_pkg_apis_investigation_v1alpha1_InvestigationAbsoluteTimeRange(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "AbsoluteTimeRange is a time range specified by absolute timestamps.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "from": { - SchemaProps: spec.SchemaProps{ - Default: 0, - Type: []string{"number"}, - Format: "float", - }, - }, - "to": { - SchemaProps: spec.SchemaProps{ - Default: 0, - Type: []string{"number"}, - Format: "float", - }, - }, - }, - Required: []string{"from", "to"}, - }, - }, - } -} - -func schema_pkg_apis_investigation_v1alpha1_InvestigationComment(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Comment is a comment on an investigation item.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "authorUserID": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "bodyMarkdown": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"authorUserID", "bodyMarkdown"}, - }, - }, - } -} - -func schema_pkg_apis_investigation_v1alpha1_InvestigationDataQueryLogs(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "DataQueryLogs is a data query for logs.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "datasource": { - SchemaProps: spec.SchemaProps{ - Description: "DatasourceRef is a reference to a datasource.", - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef"), - }, - }, - "expr": { - SchemaProps: spec.SchemaProps{ - Description: "expr is the expression of the query.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "maxLines": { - SchemaProps: spec.SchemaProps{ - Description: "maxLines (optional) is used to limit the number of log rows returned.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "refId": { - SchemaProps: spec.SchemaProps{ - Description: "refId is the reference ID of the query.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"datasource", "expr", "refId"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef"}, - } -} - -func schema_pkg_apis_investigation_v1alpha1_InvestigationDataQueryMetrics(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "DataQueryMetrics is a data query for metrics.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "datasource": { - SchemaProps: spec.SchemaProps{ - Description: "DatasourceRef is a reference to a datasource.", - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef"), - }, - }, - "expr": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "refId": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"datasource", "expr", "refId"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef"}, - } -} - -func schema_pkg_apis_investigation_v1alpha1_InvestigationDatasourceRef(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "DatasourceRef is a reference to a datasource.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "name": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "type": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "uid": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"apiVersion", "name", "type", "uid"}, - }, - }, - } -} - -func schema_pkg_apis_investigation_v1alpha1_InvestigationInvestigationItem(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "InvestigationItem is an item in an investigation.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "dataQuery": { - SchemaProps: spec.SchemaProps{ - Description: "dataQuery contains the query used to generate this item.", - Type: []string{"object"}, - Format: "", - }, - }, - "iconPath": { - SchemaProps: spec.SchemaProps{ - Description: "iconPath (optional) is the path to the icon for the item.", - Type: []string{"string"}, - Format: "", - }, - }, - "id": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "note": { - SchemaProps: spec.SchemaProps{ - Description: "note (optional) is a comment on the item.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationComment"), - }, - }, - }, - }, - }, - "origin": { - SchemaProps: spec.SchemaProps{ - Description: "origin is where the item was created from.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "queryType": { - SchemaProps: spec.SchemaProps{ - Description: "queryType is the type of the query used to generate this item.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "timeRange": { - SchemaProps: spec.SchemaProps{ - Description: "AbsoluteTimeRange is a time range specified by absolute timestamps.", - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationAbsoluteTimeRange"), - }, - }, - "title": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "type": { - SchemaProps: spec.SchemaProps{ - Description: "type is the type of the item \"timeseries\", \"heatmap\", \"log-table\" (not an enum to allow for future extensions).", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "url": { - SchemaProps: spec.SchemaProps{ - Description: "url is the URL to the item.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"dataQuery", "id", "origin", "queryType", "timeRange", "title", "type", "url"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationAbsoluteTimeRange", "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationComment"}, - } -} - -func schema_pkg_apis_investigation_v1alpha1_InvestigationList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.Investigation"), - }, - }, - }, - }, - }, - }, - Required: []string{"metadata", "items"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.Investigation", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_pkg_apis_investigation_v1alpha1_InvestigationOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "InvestigationOperatorState defines model for InvestigationOperatorState.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "descriptiveState": { - SchemaProps: spec.SchemaProps{ - Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", - Type: []string{"string"}, - Format: "", - }, - }, - "details": { - SchemaProps: spec.SchemaProps{ - Description: "details contains any extra information that is operator-specific", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Format: "", - }, - }, - }, - }, - }, - "lastEvaluation": { - SchemaProps: spec.SchemaProps{ - Description: "lastEvaluation is the ResourceVersion last evaluated", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "state": { - SchemaProps: spec.SchemaProps{ - Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"lastEvaluation", "state"}, - }, - }, - } -} - -func schema_pkg_apis_investigation_v1alpha1_InvestigationSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "InvestigationSpec defines model for InvestigationSpec.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "items": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationInvestigationItem"), - }, - }, - }, - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "title": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"items", "status", "title"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationInvestigationItem"}, - } -} - -func schema_pkg_apis_investigation_v1alpha1_InvestigationStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "InvestigationStatus defines model for InvestigationStatus.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "additionalFields": { - SchemaProps: spec.SchemaProps{ - Description: "additionalFields is reserved for future use", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Format: "", - }, - }, - }, - }, - }, - "operatorStates": { - SchemaProps: spec.SchemaProps{ - Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationstatusOperatorState"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationstatusOperatorState"}, - } -} - -func schema_pkg_apis_investigation_v1alpha1_InvestigationstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "InvestigationstatusOperatorState defines model for Investigationstatus.#OperatorState.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "descriptiveState": { - SchemaProps: spec.SchemaProps{ - Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", - Type: []string{"string"}, - Format: "", - }, - }, - "details": { - SchemaProps: spec.SchemaProps{ - Description: "details contains any extra information that is operator-specific", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Format: "", - }, - }, - }, - }, - }, - "lastEvaluation": { - SchemaProps: spec.SchemaProps{ - Description: "lastEvaluation is the ResourceVersion last evaluated", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "state": { - SchemaProps: spec.SchemaProps{ - Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"lastEvaluation", "state"}, - }, - }, - } -} diff --git a/apps/investigation/pkg/apis/manifest.go b/apps/investigation/pkg/apis/manifest.go deleted file mode 100644 index 2812059e14a..00000000000 --- a/apps/investigation/pkg/apis/manifest.go +++ /dev/null @@ -1,50 +0,0 @@ -// -// This file is generated by grafana-app-sdk -// DO NOT EDIT -// - -package apis - -import ( - "encoding/json" - - "github.com/grafana/grafana-app-sdk/app" -) - -var ( - rawSchemaInvestigationv1alpha1 = []byte(`{"spec":{"description":"spec is the schema of our resource. The spec should include all the user-ediable information for the kind.","properties":{"items":{"items":{"properties":{"dataQuery":{"description":"dataQuery contains the query used to generate this item.","oneOf":[{"allOf":[{"required":["refId","datasource","expr"]},{"not":{"anyOf":[{"required":["refId","datasource","expr"]}]}}]},{"allOf":[{"required":["refId","datasource","expr"]},{"not":{"anyOf":[{"required":["refId","datasource","expr"]}]}}]}],"properties":{"datasource":{"description":"datasource is the datasource of the query.","properties":{"apiVersion":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"uid":{"type":"string"}},"required":["uid","type","apiVersion","name"],"type":"object"},"expr":{"description":"expr is the expression of the query.","type":"string"},"maxLines":{"description":"maxLines (optional) is used to limit the number of log rows returned.","format":"int64","type":"integer"},"refId":{"description":"refId is the reference ID of the query.","type":"string"}},"type":"object"},"iconPath":{"description":"iconPath (optional) is the path to the icon for the item.","type":"string"},"id":{"type":"string"},"note":{"description":"note (optional) is a comment on the item.","items":{"properties":{"authorUserID":{"type":"string"},"bodyMarkdown":{"type":"string"}},"required":["authorUserID","bodyMarkdown"],"type":"object"},"type":"array"},"origin":{"description":"origin is where the item was created from.","type":"string"},"queryType":{"description":"queryType is the type of the query used to generate this item.","enum":["logs","metrics"],"type":"string"},"timeRange":{"description":"timeRange (optional) is the time range of the item.","properties":{"from":{"type":"number"},"to":{"type":"number"}},"required":["from","to"],"type":"object"},"title":{"type":"string"},"type":{"description":"type is the type of the item \"timeseries\", \"heatmap\", \"log-table\" (not an enum to allow for future extensions).","type":"string"},"url":{"description":"url is the URL to the item.","type":"string"}},"required":["id","title","type","url","origin","timeRange","queryType","dataQuery"],"type":"object"},"type":"array"},"status":{"enum":["open","closed"],"type":"string"},"title":{"type":"string"}},"required":["title","status","items"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`) - versionSchemaInvestigationv1alpha1 app.VersionSchema - _ = json.Unmarshal(rawSchemaInvestigationv1alpha1, &versionSchemaInvestigationv1alpha1) -) - -var appManifestData = app.ManifestData{ - AppName: "investigation", - Group: "investigation.grafana.app", - Kinds: []app.ManifestKind{ - { - Kind: "Investigation", - Scope: "Namespaced", - Conversion: false, - Versions: []app.ManifestKindVersion{ - { - Name: "v1alpha1", - Schema: &versionSchemaInvestigationv1alpha1, - }, - }, - }, - }, -} - -func jsonToMap(j string) map[string]any { - m := make(map[string]any) - json.Unmarshal([]byte(j), &j) - return m -} - -func LocalManifest() app.Manifest { - return app.NewEmbeddedManifest(appManifestData) -} - -func RemoteManifest() app.Manifest { - return app.NewAPIServerManifest("investigation") -} diff --git a/apps/investigations/Makefile b/apps/investigations/Makefile new file mode 100644 index 00000000000..7a5f1c99365 --- /dev/null +++ b/apps/investigations/Makefile @@ -0,0 +1,3 @@ +.PHONY: generate +generate: + @grafana-app-sdk generate -g ./pkg/apis --grouping=group --postprocess --defencoding=none diff --git a/apps/investigations/example.json b/apps/investigations/example.json new file mode 100644 index 00000000000..0a390fb074f --- /dev/null +++ b/apps/investigations/example.json @@ -0,0 +1,152 @@ +[ + { + "id": "896312ce-65b0-4b50-ade1-e7f04fa22c66", + "title": "Thursday morning investigation", + "hasCustomName": false, + "isFavorite": false, + "collectables": [ + { + "origin": "Explore Logs", + "type": "timeseries", + "queries": [ + { + "refId": "LABEL_BREAKDOWN_VALUES", + "queryType": "range", + "editorMode": "code", + "supportingQueryType": "grafana-lokiexplore-app", + "legendFormat": "{{detected_level}}", + "expr": "sum(count_over_time({service_name=\"web_app_1\"} | detected_level != \"\"[$__auto])) by (detected_level)" + } + ], + "timeRange": { + "to": "2025-02-13T11:31:20.536Z", + "from": "2025-02-13T11:16:20.536Z", + "raw": { + "from": "now-15m", + "to": "now" + } + }, + "datasource": { + "uid": "fe9k7u07b1a0wc" + }, + "url": "http://localhost:3000/a/grafana-lokiexplore-app/explore/service/web_app_1/labels?patterns=%5B%5D&from=now-15m&to=now&var-ds=fe9k7u07b1a0wc&var-filters=service_name%7C%3D%7Cweb_app_1&var-fields=&var-levels=&var-metadata=&var-patterns=&var-lineFilterV2=&var-lineFilters=&urlColumns=%5B%5D&visualizationType=%22logs%22&displayedFields=%5B%5D&timezone=browser&var-all-fields=&var-labelBy=$__all", + "id": "LABEL_BREAKDOWN_VALUES_detected_level", + "title": "detected_level", + "logoPath": "public/plugins/grafana-lokiexplore-app/img/img/logo.svg", + "createdAt": "2025-02-13T11:31:23.637Z" + } + ], + "createdAt": "2025-02-13T11:31:23.636Z", + "updatedAt": "2025-02-13T11:31:23.637Z", + "viewMode": { + "mode": "compact", + "showComments": true, + "showTooltips": false + } + }, + { + "id": "e9cf1958-d0ed-46b7-b597-9052c7648656", + "title": "Thursday morning investigation", + "hasCustomName": false, + "isFavorite": false, + "collectables": [ + { + "origin": "Explore Logs", + "type": "timeseries", + "queries": [ + { + "refId": "LABEL_BREAKDOWN_VALUES", + "queryType": "range", + "editorMode": "code", + "supportingQueryType": "grafana-lokiexplore-app", + "legendFormat": "{{detected_level}}", + "expr": "sum(count_over_time({service_name=\"web_app_1\"} | detected_level != \"\"[$__auto])) by (detected_level)" + } + ], + "timeRange": { + "to": "2025-02-13T11:31:20.536Z", + "from": "2025-02-13T11:16:20.536Z", + "raw": { + "from": "now-15m", + "to": "now" + } + }, + "datasource": { + "uid": "fe9k7u07b1a0wc" + }, + "url": "http://localhost:3000/a/grafana-lokiexplore-app/explore/service/web_app_1/labels?patterns=%5B%5D&from=now-15m&to=now&var-ds=fe9k7u07b1a0wc&var-filters=service_name%7C%3D%7Cweb_app_1&var-fields=&var-levels=&var-metadata=&var-patterns=&var-lineFilterV2=&var-lineFilters=&urlColumns=%5B%5D&visualizationType=%22logs%22&displayedFields=%5B%5D&timezone=browser&var-all-fields=&var-labelBy=$__all", + "id": "LABEL_BREAKDOWN_VALUES_detected_level", + "title": "detected_level", + "logoPath": "public/plugins/grafana-lokiexplore-app/img/img/logo.svg", + "createdAt": "2025-02-13T11:31:23.638Z" + }, + { + "origin": "Explore Logs", + "type": "timeseries", + "queries": [ + { + "refId": "LABEL_BREAKDOWN_VALUES", + "queryType": "range", + "editorMode": "code", + "supportingQueryType": "grafana-lokiexplore-app", + "legendFormat": "{{service_name}}", + "expr": "sum(count_over_time({service_name=\"web_app_1\",service_name != \"\"} [$__auto])) by (service_name)" + } + ], + "timeRange": { + "to": "2025-02-13T11:31:20.536Z", + "from": "2025-02-13T11:16:20.536Z", + "raw": { + "from": "now-15m", + "to": "now" + } + }, + "datasource": { + "uid": "fe9k7u07b1a0wc" + }, + "url": "http://localhost:3000/a/grafana-lokiexplore-app/explore/service/web_app_1/labels?patterns=%5B%5D&from=now-15m&to=now&var-ds=fe9k7u07b1a0wc&var-filters=service_name%7C%3D%7Cweb_app_1&var-fields=&var-levels=&var-metadata=&var-patterns=&var-lineFilterV2=&var-lineFilters=&urlColumns=%5B%5D&visualizationType=%22logs%22&displayedFields=%5B%5D&timezone=browser&var-all-fields=&var-labelBy=$__all", + "id": "LABEL_BREAKDOWN_VALUES_service_name", + "title": "service_name", + "logoPath": "public/plugins/grafana-lokiexplore-app/img/img/logo.svg", + "createdAt": "2025-02-13T11:31:41.507Z" + }, + { + "origin": "Explore Logs", + "type": "timeseries", + "queries": [ + { + "refId": "LABEL_BREAKDOWN_VALUES", + "queryType": "range", + "editorMode": "code", + "supportingQueryType": "grafana-lokiexplore-app", + "legendFormat": "{{service}}", + "expr": "sum(count_over_time({service_name=\"web_app_1\",service != \"\"} [$__auto])) by (service)" + } + ], + "timeRange": { + "to": "2025-02-13T11:31:20.536Z", + "from": "2025-02-13T11:16:20.536Z", + "raw": { + "from": "now-15m", + "to": "now" + } + }, + "datasource": { + "uid": "fe9k7u07b1a0wc" + }, + "url": "http://localhost:3000/a/grafana-lokiexplore-app/explore/service/web_app_1/labels?patterns=%5B%5D&from=now-15m&to=now&var-ds=fe9k7u07b1a0wc&var-filters=service_name%7C%3D%7Cweb_app_1&var-fields=&var-levels=&var-metadata=&var-patterns=&var-lineFilterV2=&var-lineFilters=&urlColumns=%5B%5D&visualizationType=%22logs%22&displayedFields=%5B%5D&timezone=browser&var-all-fields=&var-labelBy=$__all", + "id": "LABEL_BREAKDOWN_VALUES_service", + "title": "service", + "logoPath": "public/plugins/grafana-lokiexplore-app/img/img/logo.svg", + "createdAt": "2025-02-13T11:31:43.698Z" + } + ], + "createdAt": "2025-02-13T11:31:23.637Z", + "updatedAt": "2025-02-13T11:31:43.698Z", + "viewMode": { + "mode": "compact", + "showComments": true, + "showTooltips": false + } + } +] diff --git a/apps/investigation/go.mod b/apps/investigations/go.mod similarity index 67% rename from apps/investigation/go.mod rename to apps/investigations/go.mod index de0d3fa2621..12da166af27 100644 --- a/apps/investigation/go.mod +++ b/apps/investigations/go.mod @@ -1,10 +1,10 @@ -module github.com/grafana/grafana/apps/investigation +module github.com/grafana/grafana/apps/investigations go 1.23.4 require ( - github.com/grafana/grafana-app-sdk v0.30.0 - k8s.io/apimachinery v0.32.0 + github.com/grafana/grafana-app-sdk v0.31.0 + k8s.io/apimachinery v0.32.1 k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f ) @@ -18,7 +18,7 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/getkin/kin-openapi v0.128.0 // indirect + github.com/getkin/kin-openapi v0.129.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -31,11 +31,10 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grafana/grafana-app-sdk/logging v0.29.0 // indirect + github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/invopop/yaml v0.3.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.11 // indirect @@ -44,40 +43,42 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect + github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.61.0 // indirect + github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.33.0 // indirect - go.opentelemetry.io/otel/sdk v1.33.0 // indirect - go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/sdk v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect - golang.org/x/net v0.34.0 // indirect - golang.org/x/oauth2 v0.25.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/term v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/oauth2 v0.27.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/term v0.29.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect - google.golang.org/grpc v1.69.4 // indirect - google.golang.org/protobuf v1.36.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/grpc v1.70.0 // indirect + google.golang.org/protobuf v1.36.4 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.32.0 // indirect - k8s.io/apiextensions-apiserver v0.32.0 // indirect - k8s.io/client-go v0.32.0 // indirect + k8s.io/api v0.32.1 // indirect + k8s.io/apiextensions-apiserver v0.32.1 // indirect + k8s.io/client-go v0.32.1 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect diff --git a/apps/investigation/go.sum b/apps/investigations/go.sum similarity index 77% rename from apps/investigation/go.sum rename to apps/investigations/go.sum index 858c73dd4b4..d3caa3d08e3 100644 --- a/apps/investigation/go.sum +++ b/apps/investigations/go.sum @@ -16,8 +16,8 @@ github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCv github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4= -github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= +github.com/getkin/kin-openapi v0.129.0 h1:QGYTNcmyP5X0AtFQ2Dkou9DGBJsUETeLH9rFrJXZh30= +github.com/getkin/kin-openapi v0.129.0/go.mod h1:gmWI+b/J45xqpyK5wJmRRZse5wefA5H0RDMK46kLUtI= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -49,10 +49,10 @@ github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/Z github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grafana/grafana-app-sdk v0.30.0 h1:Hqn2pETu2mQ4RpWkZYEQfu01P7xd1Z1Gj+HX/8aB0tw= -github.com/grafana/grafana-app-sdk v0.30.0/go.mod h1:jhfqNIovb+Mes2vdMf9iMCWQkp1GTNtyNuExONtiNuk= -github.com/grafana/grafana-app-sdk/logging v0.29.0 h1:mgbXaAf33aFwqwGVeaX30l8rkeAJH0iACgX5Rn6YkN4= -github.com/grafana/grafana-app-sdk/logging v0.29.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= +github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDRgZwHkksFk= +github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= +github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= +github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -60,8 +60,6 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= -github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -87,6 +85,10 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 h1:nZspmSkneBbtxU9TopEAE0CY+SBJLxO8LPUlw2vG4pU= +github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80/go.mod h1:7tFDb+Y51LcDpn26GccuUgQXUk6t0CXZsivKjyimYX8= +github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 h1:t05Ww3DxZutOqbMN+7OIuqDwXbhl32HiZGpLy26BAPc= +github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= @@ -102,8 +104,8 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+ github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= -github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= @@ -124,22 +126,22 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= -go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= -go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= -go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= -go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= -go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= -go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= -go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= -go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= -go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -153,26 +155,26 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -187,14 +189,14 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA= -google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= -google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= -google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= -google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -204,14 +206,14 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= -k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= -k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0= -k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw= -k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= -k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= -k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= +k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= +k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= +k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw= +k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= +k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= +k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= +k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= diff --git a/apps/investigations/kinds/collectable.cue b/apps/investigations/kinds/collectable.cue new file mode 100644 index 00000000000..6844c0a8cae --- /dev/null +++ b/apps/investigations/kinds/collectable.cue @@ -0,0 +1,51 @@ +package investigations + +// Collectable represents an item collected during investigation +#Collectable: { + id: string + createdAt: string + + title: string + origin: string + type: string + queries: [...#Query] // +listType=atomic + timeRange: #TimeRange + datasource: #DatasourceRef + url: string + logoPath?: string + + note: string + noteUpdatedAt: string +} + +#CollectableSummary: { + id: string + title: string + logoPath: string + origin: string +} + +// Query represents a data query +#Query: { + refId: string + queryType: string + editorMode: string + supportingQueryType: string + legendFormat: string + expr: string +} + +// TimeRange represents a time range with both absolute and relative values +#TimeRange: { + from: string + to: string + raw: { + from: string + to: string + } +} + +// DatasourceRef is a reference to a datasource +#DatasourceRef: { + uid: string +} diff --git a/apps/investigations/kinds/cue.mod/module.cue b/apps/investigations/kinds/cue.mod/module.cue new file mode 100644 index 00000000000..55fe474ed5f --- /dev/null +++ b/apps/investigations/kinds/cue.mod/module.cue @@ -0,0 +1 @@ +module: "github.com/grafana/grafana/apps/investigations" diff --git a/apps/investigations/kinds/investigation.cue b/apps/investigations/kinds/investigation.cue new file mode 100644 index 00000000000..f1e9a69027f --- /dev/null +++ b/apps/investigations/kinds/investigation.cue @@ -0,0 +1,64 @@ +package investigations + +// This is our Investigation definition, which contains metadata about the kind, and the kind's schema +investigation: { + kind: "Investigation" + group: "investigations.grafana.app" + apiResource: { + groupOverride: "investigations.grafana.app" + } + pluralName: "Investigations" + current: "v0alpha1" + versions: { + "v0alpha1": { + codegen: { + frontend: true + backend: true + options: { + generateObjectMeta: true + generateClient: true + k8sLike: true + package: "github.com/grafana/grafana/apps/investigations" + } + } + schema: { + // spec is the schema of our resource + spec: { + title: string + createdByProfile: #Person + hasCustomName: bool + isFavorite: bool + overviewNote: string + overviewNoteUpdatedAt: string + collectables: [...#Collectable] // +listType=atomic + viewMode: #ViewMode + } + } + } + } +} + +// Type definition for investigation summaries +#InvestigationSummary: { + title: string + createdByProfile: #Person + hasCustomName: bool + isFavorite: bool + overviewNote: string + overviewNoteUpdatedAt: string + viewMode: #ViewMode + collectableSummaries: [...#CollectableSummary] // +listType=atomic +} + +// Person represents a user profile with basic information +#Person: { + uid: string // Unique identifier for the user + name: string // Display name of the user + gravatarUrl: string // URL to user's Gravatar image +} + +#ViewMode: { + mode: "compact" | "full" + showComments: bool + showTooltips: bool +} diff --git a/apps/investigations/kinds/investigationindex.cue b/apps/investigations/kinds/investigationindex.cue new file mode 100644 index 00000000000..447e156be4f --- /dev/null +++ b/apps/investigations/kinds/investigationindex.cue @@ -0,0 +1,37 @@ +package investigations + +investigationIndex: { + kind: "InvestigationIndex" + group: "investigations.grafana.app" + apiResource: { + groupOverride: "investigations.grafana.app" + } + pluralName: "InvestigationIndexes" + current: "v0alpha1" + versions: { + "v0alpha1": { + codegen: { + frontend: true + backend: true + options: { + generateObjectMeta: true + generateClient: true + k8sLike: true + package: "github.com/grafana/grafana/apps/investigations" + } + } + schema: { + spec: { + // Title of the index, e.g. 'Favorites' or 'My Investigations' + title: string + + // The Person who owns this investigation index + owner: #Person + + // Array of investigation summaries + investigationSummaries: [...#InvestigationSummary] // +listType=atomic + } + } + } + } +} diff --git a/apps/investigations/kinds/manifest.cue b/apps/investigations/kinds/manifest.cue new file mode 100644 index 00000000000..d9e93b00061 --- /dev/null +++ b/apps/investigations/kinds/manifest.cue @@ -0,0 +1,10 @@ +package investigations + +manifest: { + appName: "investigations" + groupOverride: "investigations.grafana.app" + kinds: [ + investigation, + investigationIndex, + ] +} diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/constants.go b/apps/investigations/pkg/apis/investigations/v0alpha1/constants.go new file mode 100644 index 00000000000..288c15885ce --- /dev/null +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/constants.go @@ -0,0 +1,18 @@ +package v0alpha1 + +import "k8s.io/apimachinery/pkg/runtime/schema" + +const ( + // Group is the API group used by all kinds in this package + Group = "investigations.grafana.app" + // Version is the API version used by all kinds in this package + Version = "v0alpha1" +) + +var ( + // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package + GroupVersion = schema.GroupVersion{ + Group: Group, + Version: Version, + } +) diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_codec_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_codec_gen.go similarity index 97% rename from apps/investigation/pkg/apis/investigation/v1alpha1/investigation_codec_gen.go rename to apps/investigations/pkg/apis/investigations/v0alpha1/investigation_codec_gen.go index 73e2232d045..6bde81b50d3 100644 --- a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_codec_gen.go +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_codec_gen.go @@ -2,7 +2,7 @@ // Code generated by grafana-app-sdk. DO NOT EDIT. // -package v1alpha1 +package v0alpha1 import ( "encoding/json" diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_metadata_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_metadata_gen.go new file mode 100644 index 00000000000..e0d2756f194 --- /dev/null +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_metadata_gen.go @@ -0,0 +1,28 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type InvestigationMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewInvestigationMetadata creates a new InvestigationMetadata object. +func NewInvestigationMetadata() *InvestigationMetadata { + return &InvestigationMetadata{} +} diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_object_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_object_gen.go similarity index 94% rename from apps/investigation/pkg/apis/investigation/v1alpha1/investigation_object_gen.go rename to apps/investigations/pkg/apis/investigations/v0alpha1/investigation_object_gen.go index c97fc75eec1..21c8d760fe6 100644 --- a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_object_gen.go +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_object_gen.go @@ -2,7 +2,7 @@ // Code generated by grafana-app-sdk. DO NOT EDIT. // -package v1alpha1 +package v0alpha1 import ( "fmt" @@ -16,10 +16,10 @@ import ( // +k8s:openapi-gen=true type Investigation struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata"` - Spec InvestigationSpec `json:"spec"` - InvestigationStatus InvestigationStatus `json:"status"` + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + Spec InvestigationSpec `json:"spec" yaml:"spec"` + InvestigationStatus InvestigationStatus `json:"status" yaml:"status"` } func (o *Investigation) GetSpec() any { @@ -224,9 +224,9 @@ var _ resource.Object = &Investigation{} // +k8s:openapi-gen=true type InvestigationList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - Items []Investigation `json:"items"` + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []Investigation `json:"items" yaml:"items"` } func (o *InvestigationList) DeepCopyObject() runtime.Object { diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_schema_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_schema_gen.go similarity index 81% rename from apps/investigation/pkg/apis/investigation/v1alpha1/investigation_schema_gen.go rename to apps/investigations/pkg/apis/investigations/v0alpha1/investigation_schema_gen.go index c20995f0981..2a1f03c884a 100644 --- a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_schema_gen.go +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_schema_gen.go @@ -2,7 +2,7 @@ // Code generated by grafana-app-sdk. DO NOT EDIT. // -package v1alpha1 +package v0alpha1 import ( "github.com/grafana/grafana-app-sdk/resource" @@ -10,7 +10,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( - schemaInvestigation = resource.NewSimpleSchema("investigation.grafana.app", "v1alpha1", &Investigation{}, &InvestigationList{}, resource.WithKind("Investigation"), + schemaInvestigation = resource.NewSimpleSchema("investigations.grafana.app", "v0alpha1", &Investigation{}, &InvestigationList{}, resource.WithKind("Investigation"), resource.WithPlural("investigations"), resource.WithScope(resource.NamespacedScope)) kindInvestigation = resource.Kind{ Schema: schemaInvestigation, diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_spec_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_spec_gen.go new file mode 100644 index 00000000000..3bca90e4579 --- /dev/null +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_spec_gen.go @@ -0,0 +1,140 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// Person represents a user profile with basic information +// +k8s:openapi-gen=true +type InvestigationPerson struct { + // Unique identifier for the user + Uid string `json:"uid"` + // Display name of the user + Name string `json:"name"` + // URL to user's Gravatar image + GravatarUrl string `json:"gravatarUrl"` +} + +// NewInvestigationPerson creates a new InvestigationPerson object. +func NewInvestigationPerson() *InvestigationPerson { + return &InvestigationPerson{} +} + +// Collectable represents an item collected during investigation +// +k8s:openapi-gen=true +type InvestigationCollectable struct { + Id string `json:"id"` + CreatedAt string `json:"createdAt"` + Title string `json:"title"` + Origin string `json:"origin"` + Type string `json:"type"` + // +listType=atomic + Queries []InvestigationQuery `json:"queries"` + TimeRange InvestigationTimeRange `json:"timeRange"` + Datasource InvestigationDatasourceRef `json:"datasource"` + Url string `json:"url"` + LogoPath *string `json:"logoPath,omitempty"` + Note string `json:"note"` + NoteUpdatedAt string `json:"noteUpdatedAt"` +} + +// NewInvestigationCollectable creates a new InvestigationCollectable object. +func NewInvestigationCollectable() *InvestigationCollectable { + return &InvestigationCollectable{ + TimeRange: *NewInvestigationTimeRange(), + Datasource: *NewInvestigationDatasourceRef(), + } +} + +// Query represents a data query +// +k8s:openapi-gen=true +type InvestigationQuery struct { + RefId string `json:"refId"` + QueryType string `json:"queryType"` + EditorMode string `json:"editorMode"` + SupportingQueryType string `json:"supportingQueryType"` + LegendFormat string `json:"legendFormat"` + Expr string `json:"expr"` +} + +// NewInvestigationQuery creates a new InvestigationQuery object. +func NewInvestigationQuery() *InvestigationQuery { + return &InvestigationQuery{} +} + +// TimeRange represents a time range with both absolute and relative values +// +k8s:openapi-gen=true +type InvestigationTimeRange struct { + From string `json:"from"` + To string `json:"to"` + Raw InvestigationV0alpha1TimeRangeRaw `json:"raw"` +} + +// NewInvestigationTimeRange creates a new InvestigationTimeRange object. +func NewInvestigationTimeRange() *InvestigationTimeRange { + return &InvestigationTimeRange{ + Raw: *NewInvestigationV0alpha1TimeRangeRaw(), + } +} + +// DatasourceRef is a reference to a datasource +// +k8s:openapi-gen=true +type InvestigationDatasourceRef struct { + Uid string `json:"uid"` +} + +// NewInvestigationDatasourceRef creates a new InvestigationDatasourceRef object. +func NewInvestigationDatasourceRef() *InvestigationDatasourceRef { + return &InvestigationDatasourceRef{} +} + +// +k8s:openapi-gen=true +type InvestigationViewMode struct { + Mode InvestigationViewModeMode `json:"mode"` + ShowComments bool `json:"showComments"` + ShowTooltips bool `json:"showTooltips"` +} + +// NewInvestigationViewMode creates a new InvestigationViewMode object. +func NewInvestigationViewMode() *InvestigationViewMode { + return &InvestigationViewMode{} +} + +// spec is the schema of our resource +// +k8s:openapi-gen=true +type InvestigationSpec struct { + Title string `json:"title"` + CreatedByProfile InvestigationPerson `json:"createdByProfile"` + HasCustomName bool `json:"hasCustomName"` + IsFavorite bool `json:"isFavorite"` + OverviewNote string `json:"overviewNote"` + OverviewNoteUpdatedAt string `json:"overviewNoteUpdatedAt"` + // +listType=atomic + Collectables []InvestigationCollectable `json:"collectables"` + ViewMode InvestigationViewMode `json:"viewMode"` +} + +// NewInvestigationSpec creates a new InvestigationSpec object. +func NewInvestigationSpec() *InvestigationSpec { + return &InvestigationSpec{ + CreatedByProfile: *NewInvestigationPerson(), + ViewMode: *NewInvestigationViewMode(), + } +} + +// +k8s:openapi-gen=true +type InvestigationV0alpha1TimeRangeRaw struct { + From string `json:"from"` + To string `json:"to"` +} + +// NewInvestigationV0alpha1TimeRangeRaw creates a new InvestigationV0alpha1TimeRangeRaw object. +func NewInvestigationV0alpha1TimeRangeRaw() *InvestigationV0alpha1TimeRangeRaw { + return &InvestigationV0alpha1TimeRangeRaw{} +} + +// +k8s:openapi-gen=true +type InvestigationViewModeMode string + +const ( + InvestigationViewModeModeCompact InvestigationViewModeMode = "compact" + InvestigationViewModeModeFull InvestigationViewModeMode = "full" +) diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_status_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_status_gen.go new file mode 100644 index 00000000000..c70224b96e4 --- /dev/null +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_status_gen.go @@ -0,0 +1,44 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type InvestigationstatusOperatorState struct { + // lastEvaluation is the ResourceVersion last evaluated + LastEvaluation string `json:"lastEvaluation"` + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + State InvestigationStatusOperatorStateState `json:"state"` + // descriptiveState is an optional more descriptive state field which has no requirements on format + DescriptiveState *string `json:"descriptiveState,omitempty"` + // details contains any extra information that is operator-specific + Details map[string]interface{} `json:"details,omitempty"` +} + +// NewInvestigationstatusOperatorState creates a new InvestigationstatusOperatorState object. +func NewInvestigationstatusOperatorState() *InvestigationstatusOperatorState { + return &InvestigationstatusOperatorState{} +} + +// +k8s:openapi-gen=true +type InvestigationStatus struct { + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + OperatorStates map[string]InvestigationstatusOperatorState `json:"operatorStates,omitempty"` + // additionalFields is reserved for future use + AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` +} + +// NewInvestigationStatus creates a new InvestigationStatus object. +func NewInvestigationStatus() *InvestigationStatus { + return &InvestigationStatus{} +} + +// +k8s:openapi-gen=true +type InvestigationStatusOperatorStateState string + +const ( + InvestigationStatusOperatorStateStateSuccess InvestigationStatusOperatorStateState = "success" + InvestigationStatusOperatorStateStateInProgress InvestigationStatusOperatorStateState = "in_progress" + InvestigationStatusOperatorStateStateFailed InvestigationStatusOperatorStateState = "failed" +) diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_codec_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_codec_gen.go new file mode 100644 index 00000000000..0496ce4af6f --- /dev/null +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// InvestigationIndexJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type InvestigationIndexJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*InvestigationIndexJSONCodec) Read(reader io.Reader, into resource.Object) error { + return json.NewDecoder(reader).Decode(into) +} + +// Write writes JSON-encoded bytes into `writer` marshaled from `from` +func (*InvestigationIndexJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &InvestigationIndexJSONCodec{} diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_metadata_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_metadata_gen.go new file mode 100644 index 00000000000..67e97598a93 --- /dev/null +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_metadata_gen.go @@ -0,0 +1,28 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type InvestigationIndexMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewInvestigationIndexMetadata creates a new InvestigationIndexMetadata object. +func NewInvestigationIndexMetadata() *InvestigationIndexMetadata { + return &InvestigationIndexMetadata{} +} diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_object_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_object_gen.go new file mode 100644 index 00000000000..cc3c33964ed --- /dev/null +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_object_gen.go @@ -0,0 +1,266 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type InvestigationIndex struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + Spec InvestigationIndexSpec `json:"spec" yaml:"spec"` + InvestigationIndexStatus InvestigationIndexStatus `json:"status" yaml:"status"` +} + +func (o *InvestigationIndex) GetSpec() any { + return o.Spec +} + +func (o *InvestigationIndex) SetSpec(spec any) error { + cast, ok := spec.(InvestigationIndexSpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *InvestigationIndex) GetSubresources() map[string]any { + return map[string]any{ + "status": o.InvestigationIndexStatus, + } +} + +func (o *InvestigationIndex) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.InvestigationIndexStatus, true + default: + return nil, false + } +} + +func (o *InvestigationIndex) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(InvestigationIndexStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type InvestigationIndexStatus", value) + } + o.InvestigationIndexStatus = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *InvestigationIndex) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *InvestigationIndex) SetStaticMetadata(metadata resource.StaticMetadata) { + o.Name = metadata.Name + o.Namespace = metadata.Namespace + o.SetGroupVersionKind(schema.GroupVersionKind{ + Group: metadata.Group, + Version: metadata.Version, + Kind: metadata.Kind, + }) +} + +func (o *InvestigationIndex) GetCommonMetadata() resource.CommonMetadata { + dt := o.DeletionTimestamp + var deletionTimestamp *time.Time + if dt != nil { + deletionTimestamp = &dt.Time + } + // Legacy ExtraFields support + extraFields := make(map[string]any) + if o.Annotations != nil { + extraFields["annotations"] = o.Annotations + } + if o.ManagedFields != nil { + extraFields["managedFields"] = o.ManagedFields + } + if o.OwnerReferences != nil { + extraFields["ownerReferences"] = o.OwnerReferences + } + return resource.CommonMetadata{ + UID: string(o.UID), + ResourceVersion: o.ResourceVersion, + Generation: o.Generation, + Labels: o.Labels, + CreationTimestamp: o.CreationTimestamp.Time, + DeletionTimestamp: deletionTimestamp, + Finalizers: o.Finalizers, + UpdateTimestamp: o.GetUpdateTimestamp(), + CreatedBy: o.GetCreatedBy(), + UpdatedBy: o.GetUpdatedBy(), + ExtraFields: extraFields, + } +} + +func (o *InvestigationIndex) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *InvestigationIndex) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *InvestigationIndex) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *InvestigationIndex) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *InvestigationIndex) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *InvestigationIndex) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *InvestigationIndex) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *InvestigationIndex) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *InvestigationIndex) DeepCopyObject() runtime.Object { + return o.Copy() +} + +// Interface compliance compile-time check +var _ resource.Object = &InvestigationIndex{} + +// +k8s:openapi-gen=true +type InvestigationIndexList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []InvestigationIndex `json:"items" yaml:"items"` +} + +func (o *InvestigationIndexList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *InvestigationIndexList) Copy() resource.ListObject { + cpy := &InvestigationIndexList{ + TypeMeta: o.TypeMeta, + Items: make([]InvestigationIndex, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*InvestigationIndex); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *InvestigationIndexList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *InvestigationIndexList) SetItems(items []resource.Object) { + o.Items = make([]InvestigationIndex, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*InvestigationIndex) + } +} + +// Interface compliance compile-time check +var _ resource.ListObject = &InvestigationIndexList{} diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_schema_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_schema_gen.go new file mode 100644 index 00000000000..3a0041dc6b8 --- /dev/null +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaInvestigationIndex = resource.NewSimpleSchema("investigations.grafana.app", "v0alpha1", &InvestigationIndex{}, &InvestigationIndexList{}, resource.WithKind("InvestigationIndex"), + resource.WithPlural("investigationindexes"), resource.WithScope(resource.NamespacedScope)) + kindInvestigationIndex = resource.Kind{ + Schema: schemaInvestigationIndex, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &InvestigationIndexJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func InvestigationIndexKind() resource.Kind { + return kindInvestigationIndex +} + +// Schema returns a resource.SimpleSchema representation of InvestigationIndex +func InvestigationIndexSchema() *resource.SimpleSchema { + return schemaInvestigationIndex +} + +// Interface compliance checks +var _ resource.Schema = kindInvestigationIndex diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_spec_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_spec_gen.go new file mode 100644 index 00000000000..4180a63a326 --- /dev/null +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_spec_gen.go @@ -0,0 +1,92 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// Person represents a user profile with basic information +// +k8s:openapi-gen=true +type InvestigationIndexPerson struct { + // Unique identifier for the user + Uid string `json:"uid"` + // Display name of the user + Name string `json:"name"` + // URL to user's Gravatar image + GravatarUrl string `json:"gravatarUrl"` +} + +// NewInvestigationIndexPerson creates a new InvestigationIndexPerson object. +func NewInvestigationIndexPerson() *InvestigationIndexPerson { + return &InvestigationIndexPerson{} +} + +// Type definition for investigation summaries +// +k8s:openapi-gen=true +type InvestigationIndexInvestigationSummary struct { + Title string `json:"title"` + CreatedByProfile InvestigationIndexPerson `json:"createdByProfile"` + HasCustomName bool `json:"hasCustomName"` + IsFavorite bool `json:"isFavorite"` + OverviewNote string `json:"overviewNote"` + OverviewNoteUpdatedAt string `json:"overviewNoteUpdatedAt"` + ViewMode InvestigationIndexViewMode `json:"viewMode"` + // +listType=atomic + CollectableSummaries []InvestigationIndexCollectableSummary `json:"collectableSummaries"` +} + +// NewInvestigationIndexInvestigationSummary creates a new InvestigationIndexInvestigationSummary object. +func NewInvestigationIndexInvestigationSummary() *InvestigationIndexInvestigationSummary { + return &InvestigationIndexInvestigationSummary{ + CreatedByProfile: *NewInvestigationIndexPerson(), + ViewMode: *NewInvestigationIndexViewMode(), + } +} + +// +k8s:openapi-gen=true +type InvestigationIndexViewMode struct { + Mode InvestigationIndexViewModeMode `json:"mode"` + ShowComments bool `json:"showComments"` + ShowTooltips bool `json:"showTooltips"` +} + +// NewInvestigationIndexViewMode creates a new InvestigationIndexViewMode object. +func NewInvestigationIndexViewMode() *InvestigationIndexViewMode { + return &InvestigationIndexViewMode{} +} + +// +k8s:openapi-gen=true +type InvestigationIndexCollectableSummary struct { + Id string `json:"id"` + Title string `json:"title"` + LogoPath string `json:"logoPath"` + Origin string `json:"origin"` +} + +// NewInvestigationIndexCollectableSummary creates a new InvestigationIndexCollectableSummary object. +func NewInvestigationIndexCollectableSummary() *InvestigationIndexCollectableSummary { + return &InvestigationIndexCollectableSummary{} +} + +// +k8s:openapi-gen=true +type InvestigationIndexSpec struct { + // Title of the index, e.g. 'Favorites' or 'My Investigations' + Title string `json:"title"` + // The Person who owns this investigation index + Owner InvestigationIndexPerson `json:"owner"` + // Array of investigation summaries + // +listType=atomic + InvestigationSummaries []InvestigationIndexInvestigationSummary `json:"investigationSummaries"` +} + +// NewInvestigationIndexSpec creates a new InvestigationIndexSpec object. +func NewInvestigationIndexSpec() *InvestigationIndexSpec { + return &InvestigationIndexSpec{ + Owner: *NewInvestigationIndexPerson(), + } +} + +// +k8s:openapi-gen=true +type InvestigationIndexViewModeMode string + +const ( + InvestigationIndexViewModeModeCompact InvestigationIndexViewModeMode = "compact" + InvestigationIndexViewModeModeFull InvestigationIndexViewModeMode = "full" +) diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_status_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_status_gen.go new file mode 100644 index 00000000000..455d39bdb48 --- /dev/null +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_status_gen.go @@ -0,0 +1,44 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type InvestigationIndexstatusOperatorState struct { + // lastEvaluation is the ResourceVersion last evaluated + LastEvaluation string `json:"lastEvaluation"` + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + State InvestigationIndexStatusOperatorStateState `json:"state"` + // descriptiveState is an optional more descriptive state field which has no requirements on format + DescriptiveState *string `json:"descriptiveState,omitempty"` + // details contains any extra information that is operator-specific + Details map[string]interface{} `json:"details,omitempty"` +} + +// NewInvestigationIndexstatusOperatorState creates a new InvestigationIndexstatusOperatorState object. +func NewInvestigationIndexstatusOperatorState() *InvestigationIndexstatusOperatorState { + return &InvestigationIndexstatusOperatorState{} +} + +// +k8s:openapi-gen=true +type InvestigationIndexStatus struct { + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + OperatorStates map[string]InvestigationIndexstatusOperatorState `json:"operatorStates,omitempty"` + // additionalFields is reserved for future use + AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` +} + +// NewInvestigationIndexStatus creates a new InvestigationIndexStatus object. +func NewInvestigationIndexStatus() *InvestigationIndexStatus { + return &InvestigationIndexStatus{} +} + +// +k8s:openapi-gen=true +type InvestigationIndexStatusOperatorStateState string + +const ( + InvestigationIndexStatusOperatorStateStateSuccess InvestigationIndexStatusOperatorStateState = "success" + InvestigationIndexStatusOperatorStateStateInProgress InvestigationIndexStatusOperatorStateState = "in_progress" + InvestigationIndexStatusOperatorStateStateFailed InvestigationIndexStatusOperatorStateState = "failed" +) diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/zz_openapi_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/zz_openapi_gen.go new file mode 100644 index 00000000000..36648cfcd11 --- /dev/null +++ b/apps/investigations/pkg/apis/investigations/v0alpha1/zz_openapi_gen.go @@ -0,0 +1,1061 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by grafana-app-sdk. DO NOT EDIT. + +package v0alpha1 + +import ( + common "k8s.io/kube-openapi/pkg/common" + spec "k8s.io/kube-openapi/pkg/validation/spec" +) + +func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { + return map[string]common.OpenAPIDefinition{ + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.Investigation": schema_pkg_apis_investigations_v0alpha1_Investigation(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationCollectable": schema_pkg_apis_investigations_v0alpha1_InvestigationCollectable(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationDatasourceRef": schema_pkg_apis_investigations_v0alpha1_InvestigationDatasourceRef(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndex": schema_pkg_apis_investigations_v0alpha1_InvestigationIndex(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexCollectableSummary": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexCollectableSummary(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexInvestigationSummary": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexInvestigationSummary(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexList": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexList(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexPerson": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexPerson(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexSpec": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexSpec(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexStatus": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexStatus(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexViewMode": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexViewMode(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexstatusOperatorState": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexstatusOperatorState(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationList": schema_pkg_apis_investigations_v0alpha1_InvestigationList(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationPerson": schema_pkg_apis_investigations_v0alpha1_InvestigationPerson(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationQuery": schema_pkg_apis_investigations_v0alpha1_InvestigationQuery(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationSpec": schema_pkg_apis_investigations_v0alpha1_InvestigationSpec(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationStatus": schema_pkg_apis_investigations_v0alpha1_InvestigationStatus(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationTimeRange": schema_pkg_apis_investigations_v0alpha1_InvestigationTimeRange(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationV0alpha1TimeRangeRaw": schema_pkg_apis_investigations_v0alpha1_InvestigationV0alpha1TimeRangeRaw(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationViewMode": schema_pkg_apis_investigations_v0alpha1_InvestigationViewMode(ref), + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationstatusOperatorState": schema_pkg_apis_investigations_v0alpha1_InvestigationstatusOperatorState(ref), + } +} + +func schema_pkg_apis_investigations_v0alpha1_Investigation(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationSpec", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationCollectable(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Collectable represents an item collected during investigation", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "id": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "createdAt": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "title": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "origin": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "queries": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationQuery"), + }, + }, + }, + }, + }, + "timeRange": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationTimeRange"), + }, + }, + "datasource": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationDatasourceRef"), + }, + }, + "url": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "logoPath": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "note": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "noteUpdatedAt": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"id", "createdAt", "title", "origin", "type", "queries", "timeRange", "datasource", "url", "note", "noteUpdatedAt"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationDatasourceRef", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationQuery", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationTimeRange"}, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationDatasourceRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DatasourceRef is a reference to a datasource", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "uid": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"uid"}, + }, + }, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationIndex(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexSpec", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexCollectableSummary(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "id": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "title": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "logoPath": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "origin": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"id", "title", "logoPath", "origin"}, + }, + }, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexInvestigationSummary(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Type definition for investigation summaries", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "createdByProfile": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexPerson"), + }, + }, + "hasCustomName": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "isFavorite": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "overviewNote": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "overviewNoteUpdatedAt": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "viewMode": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexViewMode"), + }, + }, + "collectableSummaries": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexCollectableSummary"), + }, + }, + }, + }, + }, + }, + Required: []string{"title", "createdByProfile", "hasCustomName", "isFavorite", "overviewNote", "overviewNoteUpdatedAt", "viewMode", "collectableSummaries"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexCollectableSummary", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexPerson", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexViewMode"}, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndex"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndex", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexPerson(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Person represents a user profile with basic information", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "Unique identifier for the user", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Display name of the user", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "gravatarUrl": { + SchemaProps: spec.SchemaProps{ + Description: "URL to user's Gravatar image", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"uid", "name", "gravatarUrl"}, + }, + }, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Description: "Title of the index, e.g. 'Favorites' or 'My Investigations'", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "owner": { + SchemaProps: spec.SchemaProps{ + Description: "The Person who owns this investigation index", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexPerson"), + }, + }, + "investigationSummaries": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Array of investigation summaries", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexInvestigationSummary"), + }, + }, + }, + }, + }, + }, + Required: []string{"title", "owner", "investigationSummaries"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexInvestigationSummary", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexPerson"}, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexstatusOperatorState"), + }, + }, + }, + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexstatusOperatorState"}, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexViewMode(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "mode": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "showComments": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "showTooltips": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"mode", "showComments", "showTooltips"}, + }, + }, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.Investigation"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.Investigation", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationPerson(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Person represents a user profile with basic information", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "Unique identifier for the user", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Display name of the user", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "gravatarUrl": { + SchemaProps: spec.SchemaProps{ + Description: "URL to user's Gravatar image", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"uid", "name", "gravatarUrl"}, + }, + }, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationQuery(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Query represents a data query", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "refId": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "queryType": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "editorMode": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "supportingQueryType": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "legendFormat": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "expr": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"refId", "queryType", "editorMode", "supportingQueryType", "legendFormat", "expr"}, + }, + }, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "spec is the schema of our resource", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "createdByProfile": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationPerson"), + }, + }, + "hasCustomName": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "isFavorite": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "overviewNote": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "overviewNoteUpdatedAt": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "collectables": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationCollectable"), + }, + }, + }, + }, + }, + "viewMode": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationViewMode"), + }, + }, + }, + Required: []string{"title", "createdByProfile", "hasCustomName", "isFavorite", "overviewNote", "overviewNoteUpdatedAt", "collectables", "viewMode"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationCollectable", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationPerson", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationViewMode"}, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationstatusOperatorState"), + }, + }, + }, + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationstatusOperatorState"}, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationTimeRange(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TimeRange represents a time range with both absolute and relative values", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "from": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "to": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "raw": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationV0alpha1TimeRangeRaw"), + }, + }, + }, + Required: []string{"from", "to", "raw"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationV0alpha1TimeRangeRaw"}, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationV0alpha1TimeRangeRaw(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "from": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "to": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"from", "to"}, + }, + }, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationViewMode(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "mode": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "showComments": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "showTooltips": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"mode", "showComments", "showTooltips"}, + }, + }, + } +} + +func schema_pkg_apis_investigations_v0alpha1_InvestigationstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} diff --git a/apps/investigations/pkg/apis/investigations_manifest.go b/apps/investigations/pkg/apis/investigations_manifest.go new file mode 100644 index 00000000000..eed596424bc --- /dev/null +++ b/apps/investigations/pkg/apis/investigations_manifest.go @@ -0,0 +1,65 @@ +// +// This file is generated by grafana-app-sdk +// DO NOT EDIT +// + +package apis + +import ( + "encoding/json" + + "github.com/grafana/grafana-app-sdk/app" +) + +var ( + rawSchemaInvestigationv0alpha1 = []byte(`{"spec":{"description":"spec is the schema of our resource","properties":{"collectables":{"items":{"properties":{"createdAt":{"type":"string"},"datasource":{"properties":{"uid":{"type":"string"}},"required":["uid"],"type":"object"},"id":{"type":"string"},"logoPath":{"type":"string"},"note":{"type":"string"},"noteUpdatedAt":{"type":"string"},"origin":{"type":"string"},"queries":{"items":{"properties":{"editorMode":{"type":"string"},"expr":{"type":"string"},"legendFormat":{"type":"string"},"queryType":{"type":"string"},"refId":{"type":"string"},"supportingQueryType":{"type":"string"}},"required":["refId","queryType","editorMode","supportingQueryType","legendFormat","expr"],"type":"object"},"type":"array"},"timeRange":{"properties":{"from":{"type":"string"},"raw":{"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"],"type":"object"},"to":{"type":"string"}},"required":["from","to","raw"],"type":"object"},"title":{"type":"string"},"type":{"type":"string"},"url":{"type":"string"}},"required":["id","createdAt","title","origin","type","queries","timeRange","datasource","url","note","noteUpdatedAt"],"type":"object"},"type":"array"},"createdByProfile":{"properties":{"gravatarUrl":{"type":"string"},"name":{"type":"string"},"uid":{"type":"string"}},"required":["uid","name","gravatarUrl"],"type":"object"},"hasCustomName":{"type":"boolean"},"isFavorite":{"type":"boolean"},"overviewNote":{"type":"string"},"overviewNoteUpdatedAt":{"type":"string"},"title":{"type":"string"},"viewMode":{"properties":{"mode":{"enum":["compact","full"],"type":"string"},"showComments":{"type":"boolean"},"showTooltips":{"type":"boolean"}},"required":["mode","showComments","showTooltips"],"type":"object"}},"required":["title","createdByProfile","hasCustomName","isFavorite","overviewNote","overviewNoteUpdatedAt","collectables","viewMode"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`) + versionSchemaInvestigationv0alpha1 app.VersionSchema + _ = json.Unmarshal(rawSchemaInvestigationv0alpha1, &versionSchemaInvestigationv0alpha1) + rawSchemaInvestigationIndexv0alpha1 = []byte(`{"spec":{"properties":{"investigationSummaries":{"description":"Array of investigation summaries","items":{"properties":{"collectableSummaries":{"items":{"properties":{"id":{"type":"string"},"logoPath":{"type":"string"},"origin":{"type":"string"},"title":{"type":"string"}},"required":["id","title","logoPath","origin"],"type":"object"},"type":"array"},"createdByProfile":{"properties":{"gravatarUrl":{"type":"string"},"name":{"type":"string"},"uid":{"type":"string"}},"required":["uid","name","gravatarUrl"],"type":"object"},"hasCustomName":{"type":"boolean"},"isFavorite":{"type":"boolean"},"overviewNote":{"type":"string"},"overviewNoteUpdatedAt":{"type":"string"},"title":{"type":"string"},"viewMode":{"properties":{"mode":{"enum":["compact","full"],"type":"string"},"showComments":{"type":"boolean"},"showTooltips":{"type":"boolean"}},"required":["mode","showComments","showTooltips"],"type":"object"}},"required":["title","createdByProfile","hasCustomName","isFavorite","overviewNote","overviewNoteUpdatedAt","viewMode","collectableSummaries"],"type":"object"},"type":"array"},"owner":{"description":"The Person who owns this investigation index","properties":{"gravatarUrl":{"type":"string"},"name":{"type":"string"},"uid":{"type":"string"}},"required":["uid","name","gravatarUrl"],"type":"object"},"title":{"description":"Title of the index, e.g. 'Favorites' or 'My Investigations'","type":"string"}},"required":["title","owner","investigationSummaries"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`) + versionSchemaInvestigationIndexv0alpha1 app.VersionSchema + _ = json.Unmarshal(rawSchemaInvestigationIndexv0alpha1, &versionSchemaInvestigationIndexv0alpha1) +) + +var appManifestData = app.ManifestData{ + AppName: "investigations", + Group: "investigations.grafana.app", + Kinds: []app.ManifestKind{ + { + Kind: "Investigation", + Scope: "Namespaced", + Conversion: false, + Versions: []app.ManifestKindVersion{ + { + Name: "v0alpha1", + Schema: &versionSchemaInvestigationv0alpha1, + }, + }, + }, + + { + Kind: "InvestigationIndex", + Scope: "Namespaced", + Conversion: false, + Versions: []app.ManifestKindVersion{ + { + Name: "v0alpha1", + Schema: &versionSchemaInvestigationIndexv0alpha1, + }, + }, + }, + }, +} + +func jsonToMap(j string) map[string]any { + m := make(map[string]any) + json.Unmarshal([]byte(j), &j) + return m +} + +func LocalManifest() app.Manifest { + return app.NewEmbeddedManifest(appManifestData) +} + +func RemoteManifest() app.Manifest { + return app.NewAPIServerManifest("investigations") +} diff --git a/apps/investigation/pkg/app/app.go b/apps/investigations/pkg/app/investigations_app.go similarity index 66% rename from apps/investigation/pkg/app/app.go rename to apps/investigations/pkg/app/investigations_app.go index 539b7241540..8ec35c1ed19 100644 --- a/apps/investigation/pkg/app/app.go +++ b/apps/investigations/pkg/app/investigations_app.go @@ -9,7 +9,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/klog/v2" - investigationv1alpha1 "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1" + investigationsv0alpha1 "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1" ) func New(cfg app.Config) (app.App, error) { @@ -24,7 +24,10 @@ func New(cfg app.Config) (app.App, error) { }, ManagedKinds: []simple.AppManagedKind{ { - Kind: investigationv1alpha1.InvestigationKind(), + Kind: investigationsv0alpha1.InvestigationKind(), + }, + { + Kind: investigationsv0alpha1.InvestigationIndexKind(), }, }, } @@ -44,10 +47,13 @@ func New(cfg app.Config) (app.App, error) { func GetKinds() map[schema.GroupVersion][]resource.Kind { gv := schema.GroupVersion{ - Group: investigationv1alpha1.InvestigationKind().Group(), - Version: investigationv1alpha1.InvestigationKind().Version(), + Group: investigationsv0alpha1.InvestigationKind().Group(), + Version: investigationsv0alpha1.InvestigationKind().Version(), } return map[schema.GroupVersion][]resource.Kind{ - gv: {investigationv1alpha1.InvestigationKind()}, + gv: { + investigationsv0alpha1.InvestigationKind(), + investigationsv0alpha1.InvestigationIndexKind(), + }, } } diff --git a/apps/investigations/plugin/src/generated/investigation/v0alpha1/investigation_object_gen.ts b/apps/investigations/plugin/src/generated/investigation/v0alpha1/investigation_object_gen.ts new file mode 100644 index 00000000000..b355ebaca05 --- /dev/null +++ b/apps/investigations/plugin/src/generated/investigation/v0alpha1/investigation_object_gen.ts @@ -0,0 +1,49 @@ +/* + * This file was generated by grafana-app-sdk. DO NOT EDIT. + */ +import { Spec } from './types.spec.gen'; +import { Status } from './types.status.gen'; + +export interface Metadata { + name: string; + namespace: string; + generateName?: string; + selfLink?: string; + uid?: string; + resourceVersion?: string; + generation?: number; + creationTimestamp?: string; + deletionTimestamp?: string; + deletionGracePeriodSeconds?: number; + labels?: Record; + annotations?: Record; + ownerReferences?: OwnerReference[]; + finalizers?: string[]; + managedFields?: ManagedFieldsEntry[]; +} + +export interface OwnerReference { + apiVersion: string; + kind: string; + name: string; + uid: string; + controller?: boolean; + blockOwnerDeletion?: boolean; +} + +export interface ManagedFieldsEntry { + manager?: string; + operation?: string; + apiVersion?: string; + time?: string; + fieldsType?: string; + subresource?: string; +} + +export interface Investigation { + kind: string; + apiVersion: string; + metadata: Metadata; + spec: Spec; + status: Status; +} diff --git a/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.metadata.gen.ts b/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.metadata.gen.ts new file mode 100644 index 00000000000..4377f3c1d08 --- /dev/null +++ b/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.metadata.gen.ts @@ -0,0 +1,30 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +export interface Metadata { + updateTimestamp: string; + createdBy: string; + uid: string; + creationTimestamp: string; + deletionTimestamp?: string; + finalizers: string[]; + resourceVersion: string; + generation: number; + updatedBy: string; + labels: Record; +} + +export const defaultMetadata = (): Metadata => ({ + updateTimestamp: "", + createdBy: "", + uid: "", + creationTimestamp: "", + finalizers: [], + resourceVersion: "", + generation: 0, + updatedBy: "", + labels: {}, +}); + diff --git a/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.spec.gen.ts b/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.spec.gen.ts new file mode 100644 index 00000000000..c2a128284bc --- /dev/null +++ b/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.spec.gen.ts @@ -0,0 +1,132 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +// Person represents a user profile with basic information +export interface Person { + // Unique identifier for the user + uid: string; + // Display name of the user + name: string; + // URL to user's Gravatar image + gravatarUrl: string; +} + +export const defaultPerson = (): Person => ({ + uid: "", + name: "", + gravatarUrl: "", +}); + +// Collectable represents an item collected during investigation +export interface Collectable { + id: string; + createdAt: string; + title: string; + origin: string; + type: string; + // +listType=atomic + queries: Query[]; + timeRange: TimeRange; + datasource: DatasourceRef; + url: string; + logoPath?: string; + note: string; + noteUpdatedAt: string; +} + +export const defaultCollectable = (): Collectable => ({ + id: "", + createdAt: "", + title: "", + origin: "", + type: "", + queries: [], + timeRange: defaultTimeRange(), + datasource: defaultDatasourceRef(), + url: "", + note: "", + noteUpdatedAt: "", +}); + +// Query represents a data query +export interface Query { + refId: string; + queryType: string; + editorMode: string; + supportingQueryType: string; + legendFormat: string; + expr: string; +} + +export const defaultQuery = (): Query => ({ + refId: "", + queryType: "", + editorMode: "", + supportingQueryType: "", + legendFormat: "", + expr: "", +}); + +// TimeRange represents a time range with both absolute and relative values +export interface TimeRange { + from: string; + to: string; + raw: { + from: string; + to: string; + }; +} + +export const defaultTimeRange = (): TimeRange => ({ + from: "", + to: "", + raw: { + from: "", + to: "", +}, +}); + +// DatasourceRef is a reference to a datasource +export interface DatasourceRef { + uid: string; +} + +export const defaultDatasourceRef = (): DatasourceRef => ({ + uid: "", +}); + +export interface ViewMode { + mode: "compact" | "full"; + showComments: boolean; + showTooltips: boolean; +} + +export const defaultViewMode = (): ViewMode => ({ + mode: "compact", + showComments: false, + showTooltips: false, +}); + +// spec is the schema of our resource +export interface Spec { + title: string; + createdByProfile: Person; + hasCustomName: boolean; + isFavorite: boolean; + overviewNote: string; + overviewNoteUpdatedAt: string; + // +listType=atomic + collectables: Collectable[]; + viewMode: ViewMode; +} + +export const defaultSpec = (): Spec => ({ + title: "", + createdByProfile: defaultPerson(), + hasCustomName: false, + isFavorite: false, + overviewNote: "", + overviewNoteUpdatedAt: "", + collectables: [], + viewMode: defaultViewMode(), +}); + diff --git a/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.status.gen.ts b/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.status.gen.ts new file mode 100644 index 00000000000..01be8df7961 --- /dev/null +++ b/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.status.gen.ts @@ -0,0 +1,30 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +export interface OperatorState { + // lastEvaluation is the ResourceVersion last evaluated + lastEvaluation: string; + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + state: "success" | "in_progress" | "failed"; + // descriptiveState is an optional more descriptive state field which has no requirements on format + descriptiveState?: string; + // details contains any extra information that is operator-specific + details?: Record; +} + +export const defaultOperatorState = (): OperatorState => ({ + lastEvaluation: "", + state: "success", +}); + +export interface Status { + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + operatorStates?: Record; + // additionalFields is reserved for future use + additionalFields?: Record; +} + +export const defaultStatus = (): Status => ({ +}); + diff --git a/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/investigationindex_object_gen.ts b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/investigationindex_object_gen.ts new file mode 100644 index 00000000000..399b25f3878 --- /dev/null +++ b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/investigationindex_object_gen.ts @@ -0,0 +1,49 @@ +/* + * This file was generated by grafana-app-sdk. DO NOT EDIT. + */ +import { Spec } from './types.spec.gen'; +import { Status } from './types.status.gen'; + +export interface Metadata { + name: string; + namespace: string; + generateName?: string; + selfLink?: string; + uid?: string; + resourceVersion?: string; + generation?: number; + creationTimestamp?: string; + deletionTimestamp?: string; + deletionGracePeriodSeconds?: number; + labels?: Record; + annotations?: Record; + ownerReferences?: OwnerReference[]; + finalizers?: string[]; + managedFields?: ManagedFieldsEntry[]; +} + +export interface OwnerReference { + apiVersion: string; + kind: string; + name: string; + uid: string; + controller?: boolean; + blockOwnerDeletion?: boolean; +} + +export interface ManagedFieldsEntry { + manager?: string; + operation?: string; + apiVersion?: string; + time?: string; + fieldsType?: string; + subresource?: string; +} + +export interface InvestigationIndex { + kind: string; + apiVersion: string; + metadata: Metadata; + spec: Spec; + status: Status; +} diff --git a/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.metadata.gen.ts b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.metadata.gen.ts new file mode 100644 index 00000000000..4377f3c1d08 --- /dev/null +++ b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.metadata.gen.ts @@ -0,0 +1,30 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +export interface Metadata { + updateTimestamp: string; + createdBy: string; + uid: string; + creationTimestamp: string; + deletionTimestamp?: string; + finalizers: string[]; + resourceVersion: string; + generation: number; + updatedBy: string; + labels: Record; +} + +export const defaultMetadata = (): Metadata => ({ + updateTimestamp: "", + createdBy: "", + uid: "", + creationTimestamp: "", + finalizers: [], + resourceVersion: "", + generation: 0, + updatedBy: "", + labels: {}, +}); + diff --git a/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.spec.gen.ts b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.spec.gen.ts new file mode 100644 index 00000000000..f0d2f25d96b --- /dev/null +++ b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.spec.gen.ts @@ -0,0 +1,84 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +// Person represents a user profile with basic information +export interface Person { + // Unique identifier for the user + uid: string; + // Display name of the user + name: string; + // URL to user's Gravatar image + gravatarUrl: string; +} + +export const defaultPerson = (): Person => ({ + uid: "", + name: "", + gravatarUrl: "", +}); + +// Type definition for investigation summaries +export interface InvestigationSummary { + title: string; + createdByProfile: Person; + hasCustomName: boolean; + isFavorite: boolean; + overviewNote: string; + overviewNoteUpdatedAt: string; + viewMode: ViewMode; + // +listType=atomic + collectableSummaries: CollectableSummary[]; +} + +export const defaultInvestigationSummary = (): InvestigationSummary => ({ + title: "", + createdByProfile: defaultPerson(), + hasCustomName: false, + isFavorite: false, + overviewNote: "", + overviewNoteUpdatedAt: "", + viewMode: defaultViewMode(), + collectableSummaries: [], +}); + +export interface ViewMode { + mode: "compact" | "full"; + showComments: boolean; + showTooltips: boolean; +} + +export const defaultViewMode = (): ViewMode => ({ + mode: "compact", + showComments: false, + showTooltips: false, +}); + +export interface CollectableSummary { + id: string; + title: string; + logoPath: string; + origin: string; +} + +export const defaultCollectableSummary = (): CollectableSummary => ({ + id: "", + title: "", + logoPath: "", + origin: "", +}); + +export interface Spec { + // Title of the index, e.g. 'Favorites' or 'My Investigations' + title: string; + // The Person who owns this investigation index + owner: Person; + // Array of investigation summaries + // +listType=atomic + investigationSummaries: InvestigationSummary[]; +} + +export const defaultSpec = (): Spec => ({ + title: "", + owner: defaultPerson(), + investigationSummaries: [], +}); + diff --git a/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.status.gen.ts b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.status.gen.ts new file mode 100644 index 00000000000..01be8df7961 --- /dev/null +++ b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.status.gen.ts @@ -0,0 +1,30 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +export interface OperatorState { + // lastEvaluation is the ResourceVersion last evaluated + lastEvaluation: string; + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + state: "success" | "in_progress" | "failed"; + // descriptiveState is an optional more descriptive state field which has no requirements on format + descriptiveState?: string; + // details contains any extra information that is operator-specific + details?: Record; +} + +export const defaultOperatorState = (): OperatorState => ({ + lastEvaluation: "", + state: "success", +}); + +export interface Status { + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + operatorStates?: Record; + // additionalFields is reserved for future use + additionalFields?: Record; +} + +export const defaultStatus = (): Status => ({ +}); + diff --git a/apps/playlist/Makefile b/apps/playlist/Makefile index 926f5a2840f..7a5f1c99365 100644 --- a/apps/playlist/Makefile +++ b/apps/playlist/Makefile @@ -1,8 +1,3 @@ .PHONY: generate generate: - @grafana-app-sdk generate -g ./pkg/apis --grouping=group --postprocess - # HACK: Clean up generated CRD files. - # TODO: The SDK currently omits generating the manifest Go file with `--defencoding=none`, - # which we would normally use here to skip generating the CRD files. - # This needs to be addressed. - @rm -rf definitions + @grafana-app-sdk generate -g ./pkg/apis --grouping=group --postprocess --defencoding=none diff --git a/apps/playlist/go.mod b/apps/playlist/go.mod index f7304ea395c..cd07f8c199e 100644 --- a/apps/playlist/go.mod +++ b/apps/playlist/go.mod @@ -3,9 +3,9 @@ module github.com/grafana/grafana/apps/playlist go 1.23.4 require ( - github.com/grafana/grafana-app-sdk v0.30.0 - k8s.io/apimachinery v0.32.0 - k8s.io/client-go v0.32.0 + github.com/grafana/grafana-app-sdk v0.31.0 + k8s.io/apimachinery v0.32.1 + k8s.io/client-go v0.32.1 k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f ) @@ -19,7 +19,7 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/getkin/kin-openapi v0.128.0 // indirect + github.com/getkin/kin-openapi v0.129.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -32,11 +32,10 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grafana/grafana-app-sdk/logging v0.29.0 // indirect + github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/invopop/yaml v0.3.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.11 // indirect @@ -45,39 +44,41 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect + github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.61.0 // indirect + github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.33.0 // indirect - go.opentelemetry.io/otel/sdk v1.33.0 // indirect - go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/sdk v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect - golang.org/x/net v0.34.0 // indirect - golang.org/x/oauth2 v0.25.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/term v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/oauth2 v0.27.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/term v0.29.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect - google.golang.org/grpc v1.69.4 // indirect - google.golang.org/protobuf v1.36.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/grpc v1.70.0 // indirect + google.golang.org/protobuf v1.36.4 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.32.0 // indirect - k8s.io/apiextensions-apiserver v0.32.0 // indirect + k8s.io/api v0.32.1 // indirect + k8s.io/apiextensions-apiserver v0.32.1 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect diff --git a/apps/playlist/go.sum b/apps/playlist/go.sum index 858c73dd4b4..d3caa3d08e3 100644 --- a/apps/playlist/go.sum +++ b/apps/playlist/go.sum @@ -16,8 +16,8 @@ github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCv github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4= -github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= +github.com/getkin/kin-openapi v0.129.0 h1:QGYTNcmyP5X0AtFQ2Dkou9DGBJsUETeLH9rFrJXZh30= +github.com/getkin/kin-openapi v0.129.0/go.mod h1:gmWI+b/J45xqpyK5wJmRRZse5wefA5H0RDMK46kLUtI= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -49,10 +49,10 @@ github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/Z github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grafana/grafana-app-sdk v0.30.0 h1:Hqn2pETu2mQ4RpWkZYEQfu01P7xd1Z1Gj+HX/8aB0tw= -github.com/grafana/grafana-app-sdk v0.30.0/go.mod h1:jhfqNIovb+Mes2vdMf9iMCWQkp1GTNtyNuExONtiNuk= -github.com/grafana/grafana-app-sdk/logging v0.29.0 h1:mgbXaAf33aFwqwGVeaX30l8rkeAJH0iACgX5Rn6YkN4= -github.com/grafana/grafana-app-sdk/logging v0.29.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= +github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDRgZwHkksFk= +github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= +github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= +github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -60,8 +60,6 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= -github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -87,6 +85,10 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 h1:nZspmSkneBbtxU9TopEAE0CY+SBJLxO8LPUlw2vG4pU= +github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80/go.mod h1:7tFDb+Y51LcDpn26GccuUgQXUk6t0CXZsivKjyimYX8= +github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 h1:t05Ww3DxZutOqbMN+7OIuqDwXbhl32HiZGpLy26BAPc= +github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= @@ -102,8 +104,8 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+ github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= -github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= @@ -124,22 +126,22 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= -go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= -go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= -go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= -go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= -go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= -go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= -go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= -go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= -go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -153,26 +155,26 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -187,14 +189,14 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA= -google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= -google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= -google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= -google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -204,14 +206,14 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= -k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= -k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0= -k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw= -k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= -k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= -k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= +k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= +k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= +k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw= +k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= +k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= +k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= +k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= diff --git a/conf/defaults.ini b/conf/defaults.ini index 91e7af25442..bf0bf994b56 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -130,6 +130,13 @@ password = # Example: mysql://user:secret@host:port/database url = +# Set to true or false to enable or disable high availability mode. +# When it's set to false some functions will be simplified and only run in-process +# instead of relying on the database. +# +# Only set it to false if you run only a single instance of Grafana. +high_availability = true + # Max idle conn setting default is 2 max_idle_conn = 2 @@ -354,6 +361,9 @@ disable_brute_force_login_protection = false # max number of failed login attempts before user gets locked brute_force_login_protection_max_attempts = 5 +# disable protection against brute force login attempts by IP address +disable_ip_address_login_protection = true + # set to true if you host Grafana behind HTTPS. default is false. cookie_secure = false @@ -452,6 +462,9 @@ min_refresh_interval = 5s # Path to the default home dashboard. If this value is empty, then Grafana uses StaticRootPath + "dashboards/home.json" default_home_dashboard_path = +# Dashboards UIDs to report performance metrics for. * can be used to report metrics for all dashboards +dashboard_performance_metrics = + ################################### Data sources ######################### [datasources] # Upper limit of data sources that Grafana will return. This limit is a temporary configuration and it will be deprecated when pagination will be introduced on the list data sources API. @@ -521,9 +534,11 @@ external_manage_link_url = external_manage_link_name = external_manage_info = +# Deprecated: Assign your viewers to editors. # Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard. viewers_can_edit = false +# Deprecated: Assign your editors to admins. # Editors can administrate dashboard, folders and teams they create editors_can_admin = false @@ -1013,6 +1028,10 @@ user_identity_fallback_credentials_enabled = true # By default is the same as token URL configured for AAD authentication settings user_identity_token_url = +# Override client authentication method for Azure Active Directory +# By default is the same as client authentication method configured for AAD authentication settings +user_identity_client_authentication = + # Override ADD application ID which would be used to exchange users token to an access token for the datasource # By default is the same as used in AAD authentication or can be set to another application (for OBO flow) user_identity_client_id = @@ -1021,6 +1040,14 @@ user_identity_client_id = # By default is the same as used in AAD authentication or can be set to another application (for OBO flow) user_identity_client_secret = +# Override the AAD managed identity client ID +# By default is the same as used in AAD authentication or can be set to another managed identity (for OBO flow) +user_identity_managed_identity_client_id = + +# Override the AAD federated credential audience +# By default is the same as used in AAD authentication or can be set to another audience (for OBO flow) +user_identity_federated_credential_audience = + # Allows the usage of a custom token request assertion when Grafana is behind an authentication proxy # In most cases this will not need to be used. To enable this set the value to "username" # The default is empty and any other value will not enable this functionality @@ -1559,6 +1586,9 @@ enabled = true # set the default offset for the time picker defaultTimeOffset = 1h +# hides the download logs button in Explore +hide_logs_download = false + #################################### Help ############################# [help] # Enable the Help section @@ -1723,6 +1753,7 @@ sas_token_expiration_days = # URL to a remote HTTP image renderer service, e.g. http://localhost:8081/render, will enable Grafana to render panels and dashboards to PNG-images using HTTP requests to an external service. server_url = # If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable, e.g. http://grafana.domain/. +# The `callback_url` can also be configured to support usage of the image renderer running as a plugin with support for SSL / HTTPS. For example https://localhost:3000/. callback_url = # An auth token that will be sent to and verified by the renderer. The renderer will deny any request without an auth token matching the one configured on the renderer side. renderer_token = - @@ -1785,6 +1816,10 @@ preinstall_disabled = false # tuning. 0 disables Live, -1 means unlimited connections. max_connections = 100 +# message_size_limit is the maximum size in bytes of Websocket messages from clients. Defaults to 64KB. +# The limit can be disabled by setting it to -1. +message_size_limit = 65536 + # allowed_origins is a comma-separated list of origins that can establish connection with Grafana Live. # If not set then origin will be matched over root_url. Supports wildcard symbol "*". allowed_origins = diff --git a/conf/sample.ini b/conf/sample.ini index d1a06d9c331..da688f03078 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -129,6 +129,13 @@ # Example: mysql://user:secret@host:port/database ;url = +# Set to true or false to enable or disable high availability mode. +# When it's set to false some functions will be simplified and only run in-process +# instead of relying on the database. +# +# Only set it to false if you run only a single instance of Grafana. +;high_availability = true + # Max idle conn setting default is 2 ;max_idle_conn = 2 @@ -353,6 +360,9 @@ # max number of failed login attempts before user gets locked ;brute_force_login_protection_max_attempts = 5 +# disable protection against brute force login attempts by IP address +; disable_ip_address_login_protection = true + # set to true if you host Grafana behind HTTPS. default is false. ;cookie_secure = false @@ -525,9 +535,12 @@ ;external_manage_link_name = ;external_manage_info = + +# Deprecated: Assign your viewers to editors. # Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard. ;viewers_can_edit = false +# Deprecated: Assign your editors to admins. # Editors can administrate dashboard, folders and teams they create ;editors_can_admin = false @@ -998,6 +1011,10 @@ # By default is the same as token URL configured for AAD authentication settings ;user_identity_token_url = +# Override client authentication method for Azure Active Directory +# By default is the same as client authentication method configured for AAD authentication settings +;user_identity_client_authentication = + # Override ADD application ID which would be used to exchange users token to an access token for the datasource # By default is the same as used in AAD authentication or can be set to another application (for OBO flow) ;user_identity_client_id = @@ -1006,6 +1023,14 @@ # By default is the same as used in AAD authentication or can be set to another application (for OBO flow) ;user_identity_client_secret = +# Override the AAD managed identity client ID +# By default is the same as used in AAD authentication or can be set to another managed identity (for OBO flow) +;user_identity_managed_identity_client_id = + +# Override the AAD federated credential audience +# By default is the same as used in AAD authentication or can be set to another audience (for OBO flow) +;user_identity_federated_credential_audience = + # Allows the usage of a custom token request assertion when Grafana is behind an authentication proxy # In most cases this will not need to be used. To enable this set the value to "username" # The default is empty and any other value will not enable this functionality @@ -1391,12 +1416,6 @@ # For example: `disabled_labels=grafana_folder` disabled_labels = - -[unified_alerting.reserved_labels] -# Comma-separated list of reserved labels added by the Grafana Alerting engine that should be disabled. -# For example: `disabled_labels=grafana_folder` -;disabled_labels = - [unified_alerting.state_history] # Enable the state history functionality in Unified Alerting. The previous states of alert rules will be visible in panels and in the UI. ; enabled = true diff --git a/contribute/developer-guide.md b/contribute/developer-guide.md index 9a4d98d8003..da3669615ab 100644 --- a/contribute/developer-guide.md +++ b/contribute/developer-guide.md @@ -318,7 +318,7 @@ Previously, Grafana used Yarn PnP to install frontend dependencies, which requir ### Too many open files when running `make run` -Depending on your environment, you may have to increase the maximum number of open files allowed. For the rest of this section, we will assume you are on a UNIX-like OS (for example, Linux or macOS), where you can control the maximum number of open files through the [ulimit](https://ss64.com/bash/ulimit.html) shell command. +Depending on your environment, you may need to increase the maximum number of open files allowed. For the rest of this section, we will assume you are on a UNIX-like OS (for example, Linux or macOS), where you can control the maximum number of open files through the [ulimit](https://ss64.com/bash/ulimit.html) shell command. To see how many open files are allowed, run: @@ -361,6 +361,58 @@ ulimit: open files: cannot modify limit: Operation not permitted If that happens to you, chances are you've already set a lower limit and your shell won't let you set a higher one. Try looking in your shell initialization files (`~/.bashrc`, typically), to see if there's already an `ulimit` command that you can tweak. +### System limit for number of file watchers reached while running `yarn start` + +Depending on your environment, you may need to increase the number of file watchers allowed by `inotify` package to monitor filesystem changes. You may encounter an error `Error: ENOSPC: System limit for number of file watchers reached` otherwise. + +Edit the system config file to insert the new value for file watchers limit: + +On Linux: + +```bash +echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p +``` + +On macOS: + +```bash +sudo sysctl -w kern.maxfiles=524288 +``` + +Check if the new value was applied. It must output `524288`: + +On Linux: + +```bash +cat /proc/sys/fs/inotify/max_user_watches +``` + +On macOS: + +```bash +sysctl kern.maxfiles +``` + +### JavaScript heap out of memory while running `yarn start` + +Running `yarn start` requires a substantial amount of memory space. You may check the currently allocated heap space to `node` by running the command: + +```bash +node -e 'console.log(v8.getHeapStatistics().heap_size_limit/(1024*1024))' +``` + +Increase the default heap memory to something greater than the currently allocated memory. Make sure the value is a multiple of `1024`. + +```bash +export NODE_OPTIONS="--max-old-space-size=8192" +``` + +Or on Windows: + +``` +Set NODE_OPTIONS="--max-old-space-size=8192" +``` + ### Getting `AggregateError` when building frontend tests If you encounter an `AggregateError` when building new tests, this is probably due to a call to our client [backend service](https://github.com/grafana/grafana/blob/main/public/app/core/services/backend_srv.ts) not being mocked. Our backend service anticipates multiple responses being returned and was built to return errors as an array. A test encountering errors from the service will group those errors as an `AggregateError` without breaking down the individual errors within. `backend_srv.processRequestError` is called once per error and is a great place to return information on what the individual errors might contain. diff --git a/contribute/style-guides/themes.md b/contribute/style-guides/themes.md index 61c01764429..e4608ec55f1 100644 --- a/contribute/style-guides/themes.md +++ b/contribute/style-guides/themes.md @@ -96,11 +96,12 @@ Example use cases: ### Background colors -| Property | When to use | -| --------------------------------- | ------------------------------------------------------------------------------------------------ | -| theme.colors.background.canvas | Dashboard background. A background surface for panels and panes that use primary background | -| theme.colors.background.primary | The default content background for content panes and panels | -| theme.colors.background.secondary | For cards and other surfaces that need to stand out when placed on top of the primary background | +| Property | When to use | +| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| theme.colors.background.canvas | Dashboard background. A background surface for panels and panes that use primary background | +| theme.colors.background.primary | The default content background for content panes and panels | +| theme.colors.background.secondary | For cards and other surfaces that need to stand out when placed on top of the primary background | +| theme.colors.background.elevated | For popovers and menu backgrounds. This is the same color as primary in most light themes but in dark themes it has a brighter shade to help give it contrast against the primary background | ### Borders diff --git a/devenv/bulk-dashboards/bulkdash.jsonnet b/devenv/bulk-dashboards/bulkdash.jsonnet index 1a77d8abd70..05e396df2d6 100644 --- a/devenv/bulk-dashboards/bulkdash.jsonnet +++ b/devenv/bulk-dashboards/bulkdash.jsonnet @@ -1118,18 +1118,6 @@ "1d" ], "status": "Stable", - "time_options": [ - "5m", - "15m", - "1h", - "2h", - " 6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ], "type": "timepicker" }, "timezone": "browser", diff --git a/devenv/datasources.yaml b/devenv/datasources.yaml index 452d2d3d1bc..00efa243c1c 100644 --- a/devenv/datasources.yaml +++ b/devenv/datasources.yaml @@ -136,6 +136,14 @@ datasources: tsdbResolution: 1 tsdbVersion: 3 + - name: gdev-opentsdb-v2.4 + type: opentsdb + access: proxy + url: http://localhost:4242 + jsonData: + tsdbResolution: 1 + tsdbVersion: 4 + - name: gdev-elasticsearch type: elasticsearch uid: gdev-elasticsearch diff --git a/devenv/dev-dashboards-without-uid/panel_tests_graph.json b/devenv/dev-dashboards-without-uid/panel_tests_graph.json index b5d50f4f7b7..6bba1d9ae02 100644 --- a/devenv/dev-dashboards-without-uid/panel_tests_graph.json +++ b/devenv/dev-dashboards-without-uid/panel_tests_graph.json @@ -1639,8 +1639,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "browser", "title": "Panel Tests - Graph", diff --git a/devenv/dev-dashboards-without-uid/panel_tests_graph_time_regions.json b/devenv/dev-dashboards-without-uid/panel_tests_graph_time_regions.json index 3ff76d12df2..98d49958aaf 100644 --- a/devenv/dev-dashboards-without-uid/panel_tests_graph_time_regions.json +++ b/devenv/dev-dashboards-without-uid/panel_tests_graph_time_regions.json @@ -490,17 +490,6 @@ "1h", "2h", "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" ] }, "timezone": "browser", diff --git a/devenv/dev-dashboards-without-uid/panel_tests_polystat.json b/devenv/dev-dashboards-without-uid/panel_tests_polystat.json index 951bb780017..25b1f7154e5 100644 --- a/devenv/dev-dashboards-without-uid/panel_tests_polystat.json +++ b/devenv/dev-dashboards-without-uid/panel_tests_polystat.json @@ -3408,8 +3408,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel Tests - Polystat", diff --git a/devenv/dev-dashboards/datasource-influxdb/influxdb-templated.json b/devenv/dev-dashboards/datasource-influxdb/influxdb-templated.json index 97719e82251..f46ccc2042f 100644 --- a/devenv/dev-dashboards/datasource-influxdb/influxdb-templated.json +++ b/devenv/dev-dashboards/datasource-influxdb/influxdb-templated.json @@ -312,7 +312,6 @@ "now": true, "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], "status": "Stable", - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"], "type": "timepicker" }, "timezone": "browser", diff --git a/devenv/dev-dashboards/datasource-mssql/mssql_fakedata.json b/devenv/dev-dashboards/datasource-mssql/mssql_fakedata.json index 19c5e1d0718..8fda3e8e714 100644 --- a/devenv/dev-dashboards/datasource-mssql/mssql_fakedata.json +++ b/devenv/dev-dashboards/datasource-mssql/mssql_fakedata.json @@ -537,8 +537,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Datasource tests - MSSQL", diff --git a/devenv/dev-dashboards/datasource-mssql/mssql_unittest.json b/devenv/dev-dashboards/datasource-mssql/mssql_unittest.json index 0137001067c..40f9d55e5ca 100644 --- a/devenv/dev-dashboards/datasource-mssql/mssql_unittest.json +++ b/devenv/dev-dashboards/datasource-mssql/mssql_unittest.json @@ -2831,8 +2831,7 @@ "to": "2018-03-15T13:55:01.000Z" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Datasource tests - MSSQL (unit test)", diff --git a/devenv/dev-dashboards/datasource-mysql/mysql_fakedata.json b/devenv/dev-dashboards/datasource-mysql/mysql_fakedata.json index 96e4688f7bd..cd1a24fb5bd 100644 --- a/devenv/dev-dashboards/datasource-mysql/mysql_fakedata.json +++ b/devenv/dev-dashboards/datasource-mysql/mysql_fakedata.json @@ -541,8 +541,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Datasource tests - MySQL", diff --git a/devenv/dev-dashboards/datasource-mysql/mysql_unittest.json b/devenv/dev-dashboards/datasource-mysql/mysql_unittest.json index b68c9db97ec..2f2a42c1175 100644 --- a/devenv/dev-dashboards/datasource-mysql/mysql_unittest.json +++ b/devenv/dev-dashboards/datasource-mysql/mysql_unittest.json @@ -2643,8 +2643,7 @@ "to": "2018-03-15T13:55:01.000Z" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Datasource tests - MySQL (unittest)", diff --git a/devenv/dev-dashboards/datasource-postgres/postgres_fakedata.json b/devenv/dev-dashboards/datasource-postgres/postgres_fakedata.json index d7d9514e639..750b3284517 100644 --- a/devenv/dev-dashboards/datasource-postgres/postgres_fakedata.json +++ b/devenv/dev-dashboards/datasource-postgres/postgres_fakedata.json @@ -577,8 +577,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Datasource tests - Postgres", diff --git a/devenv/dev-dashboards/datasource-postgres/postgres_unittest.json b/devenv/dev-dashboards/datasource-postgres/postgres_unittest.json index a114ed1b7ef..acec0d08a44 100644 --- a/devenv/dev-dashboards/datasource-postgres/postgres_unittest.json +++ b/devenv/dev-dashboards/datasource-postgres/postgres_unittest.json @@ -2621,8 +2621,7 @@ "to": "2018-03-15T13:55:01.000Z" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Datasource tests - Postgres (unittest)", diff --git a/devenv/dev-dashboards/datasource-testdata/demo1.json b/devenv/dev-dashboards/datasource-testdata/demo1.json index abe39ffeb55..6d8034f81e6 100644 --- a/devenv/dev-dashboards/datasource-testdata/demo1.json +++ b/devenv/dev-dashboards/datasource-testdata/demo1.json @@ -1092,7 +1092,6 @@ "now": true, "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], "status": "Stable", - "time_options": ["5m", "15m", "1h", "2h", " 6h", "12h", "24h", "2d", "7d", "30d"], "type": "timepicker" }, "timezone": "browser", diff --git a/devenv/dev-dashboards/datasource-testdata/new_features_in_v62.json b/devenv/dev-dashboards/datasource-testdata/new_features_in_v62.json index 4b9535ecbbd..a002a208f85 100644 --- a/devenv/dev-dashboards/datasource-testdata/new_features_in_v62.json +++ b/devenv/dev-dashboards/datasource-testdata/new_features_in_v62.json @@ -1326,8 +1326,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "New Features in v6.2", diff --git a/devenv/dev-dashboards/home.json b/devenv/dev-dashboards/home.json index 9c3d65d4add..840d32919ea 100644 --- a/devenv/dev-dashboards/home.json +++ b/devenv/dev-dashboards/home.json @@ -240,17 +240,6 @@ "1h", "2h", "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" ] }, "timezone": "", diff --git a/devenv/dev-dashboards/panel-bargauge/bar_gauge_demo.json b/devenv/dev-dashboards/panel-bargauge/bar_gauge_demo.json index a708467d7be..eda6ccfe996 100644 --- a/devenv/dev-dashboards/panel-bargauge/bar_gauge_demo.json +++ b/devenv/dev-dashboards/panel-bargauge/bar_gauge_demo.json @@ -654,8 +654,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["2s", "5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["2s", "5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Bar Gauge Demo", diff --git a/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge.json b/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge.json index 29a6929cf8f..3dfa360c740 100644 --- a/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge.json +++ b/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge.json @@ -1423,17 +1423,6 @@ "1h", "2h", "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" ] }, "timezone": "", diff --git a/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge2.json b/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge2.json index 0f36c203cd0..06fc26e382d 100644 --- a/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge2.json +++ b/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge2.json @@ -519,8 +519,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel Tests - Bar Gauge 2", diff --git a/devenv/dev-dashboards/panel-common/lazy_loading.json b/devenv/dev-dashboards/panel-common/lazy_loading.json index 859eede4b4f..960c466124c 100644 --- a/devenv/dev-dashboards/panel-common/lazy_loading.json +++ b/devenv/dev-dashboards/panel-common/lazy_loading.json @@ -2202,8 +2202,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Lazy Loading", diff --git a/devenv/dev-dashboards/panel-common/panels_without_title.json b/devenv/dev-dashboards/panel-common/panels_without_title.json index 44bd210e73b..8f62c9cca5e 100644 --- a/devenv/dev-dashboards/panel-common/panels_without_title.json +++ b/devenv/dev-dashboards/panel-common/panels_without_title.json @@ -893,8 +893,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel Tests - With & Without title", diff --git a/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json b/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json index 09b72e5c030..f7ff80edc12 100644 --- a/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json +++ b/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json @@ -254,8 +254,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel Tests - Gauge Multi Series", diff --git a/devenv/dev-dashboards/panel-gauge/gauge_tests.json b/devenv/dev-dashboards/panel-gauge/gauge_tests.json index 458f53dbc08..309a255fcc7 100644 --- a/devenv/dev-dashboards/panel-gauge/gauge_tests.json +++ b/devenv/dev-dashboards/panel-gauge/gauge_tests.json @@ -1319,8 +1319,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel Tests - Gauge", diff --git a/devenv/dev-dashboards/panel-graph/graph-gradient-area-fills.json b/devenv/dev-dashboards/panel-graph/graph-gradient-area-fills.json index 01e9e8c2f43..c2d27efd469 100644 --- a/devenv/dev-dashboards/panel-graph/graph-gradient-area-fills.json +++ b/devenv/dev-dashboards/panel-graph/graph-gradient-area-fills.json @@ -372,8 +372,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel Tests - Graph - Gradient Area Fills", diff --git a/devenv/dev-dashboards/panel-graph/graph-time-regions.json b/devenv/dev-dashboards/panel-graph/graph-time-regions.json index 2031788ae3f..a4e03148536 100644 --- a/devenv/dev-dashboards/panel-graph/graph-time-regions.json +++ b/devenv/dev-dashboards/panel-graph/graph-time-regions.json @@ -569,8 +569,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "browser", "title": "Panel Tests - Graph Time Regions", diff --git a/devenv/dev-dashboards/panel-graph/graph_tests.json b/devenv/dev-dashboards/panel-graph/graph_tests.json index 4d01c2cb534..bd1fc95d3d6 100644 --- a/devenv/dev-dashboards/panel-graph/graph_tests.json +++ b/devenv/dev-dashboards/panel-graph/graph_tests.json @@ -1639,8 +1639,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "browser", "title": "Panel Tests - Graph", diff --git a/devenv/dev-dashboards/panel-polystat/polystat_test.json b/devenv/dev-dashboards/panel-polystat/polystat_test.json index 6be355ebd99..faa84463019 100644 --- a/devenv/dev-dashboards/panel-polystat/polystat_test.json +++ b/devenv/dev-dashboards/panel-polystat/polystat_test.json @@ -3408,8 +3408,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel Tests - Polystat", diff --git a/devenv/dev-dashboards/panel-table/table_tests.json b/devenv/dev-dashboards/panel-table/table_tests.json index 8582ef068d7..b8ca436e110 100644 --- a/devenv/dev-dashboards/panel-table/table_tests.json +++ b/devenv/dev-dashboards/panel-table/table_tests.json @@ -440,8 +440,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "browser", "title": "Panel Tests - Table", diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-gradient-area.json b/devenv/dev-dashboards/panel-timeseries/timeseries-gradient-area.json index a7389cc6ed6..8f3267a62d7 100644 --- a/devenv/dev-dashboards/panel-timeseries/timeseries-gradient-area.json +++ b/devenv/dev-dashboards/panel-timeseries/timeseries-gradient-area.json @@ -562,8 +562,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel Tests - Graph NG - Gradient Area Fills", diff --git a/devenv/dev-dashboards/scenarios/slow_queries_and_annotations.json b/devenv/dev-dashboards/scenarios/slow_queries_and_annotations.json index a7cc41acd8f..8966137b55e 100644 --- a/devenv/dev-dashboards/scenarios/slow_queries_and_annotations.json +++ b/devenv/dev-dashboards/scenarios/slow_queries_and_annotations.json @@ -1132,8 +1132,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel tests - Slow Queries & Annotations", diff --git a/devenv/dev-dashboards/scenarios/time_zone_support.json b/devenv/dev-dashboards/scenarios/time_zone_support.json index feb317f917e..cc1f81ee221 100644 --- a/devenv/dev-dashboards/scenarios/time_zone_support.json +++ b/devenv/dev-dashboards/scenarios/time_zone_support.json @@ -684,8 +684,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "utc", "title": "Panel Tests - Time zone support", diff --git a/devenv/docker/blocks/elasticstack/docker-compose.yaml b/devenv/docker/blocks/elasticstack/docker-compose.yaml index ad8a0d5e7f0..35b4254ac63 100644 --- a/devenv/docker/blocks/elasticstack/docker-compose.yaml +++ b/devenv/docker/blocks/elasticstack/docker-compose.yaml @@ -5,6 +5,8 @@ - "discovery.type=single-node" - "xpack.license.self_generated.type=basic" - "xpack.security.enabled=false" + - 'ES_JAVA_OPTS=-XX:UseSVE=0' + - 'CLI_JAVA_OPTS=-XX:UseSVE=0' ports: - 9200:9200 diff --git a/devenv/docker/blocks/prometheus/recording.yml b/devenv/docker/blocks/prometheus/recording.yml index f45cdbeddf2..2b8ae3e477a 100644 --- a/devenv/docker/blocks/prometheus/recording.yml +++ b/devenv/docker/blocks/prometheus/recording.yml @@ -1,4 +1,18 @@ groups: + - name: TESTING_RULES + rules: + - record: test:rate5m + expr: rate(ismail_prometheus_tsdb_reloads_total{job="prometheus"}[5m]) + labels: + identifier: special_idx + - record: agent:test:rate5m + expr: rate(prometheus_tsdb_reloads_total{job="prometheus"}[5m]) + labels: + identifier: special_idx + - record: agent:test:rate5m_agg_ + expr: rate(prometheus_tsdb_reloads_total_agg_{job="aggregated"}[5m]) + labels: + identifier: special_idx - name: RECORDING_RULES rules: - record: instance_path:requests:rate5m @@ -9,6 +23,14 @@ groups: expr: rate(prometheus_tsdb_reloads_failures_total{job="prometheus"}[5m]) - record: instance_path:reloads:rate5m expr: rate(prometheus_tsdb_reloads_total{job="prometheus"}[5m]) + - record: test:rate5m + expr: rate(prometheus_tsdb_reloads_total{job="prometheus"}[5m]) + - record: test:rate5m_agg_ + expr: rate(prometheus_tsdb_reloads_total_agg_{job="aggregated"}[5m]) + - record: agent:test:rate5m + expr: rate(prometheus_tsdb_reloads_total{job="prometheus"}[5m]) + - record: agent:test:rate5m_agg_ + expr: rate(prometheus_tsdb_reloads_total_agg_{job="aggregated"}[5m]) - record: instance_path:request_failures_per_requests:ratio_rate5m expr: |2 instance_path:reloads_failures:rate5m{job="prometheus"} diff --git a/devenv/docker/blocks/prometheus_random_data/Dockerfile b/devenv/docker/blocks/prometheus_random_data/Dockerfile index c4ebc8cb0ec..41f90677409 100644 --- a/devenv/docker/blocks/prometheus_random_data/Dockerfile +++ b/devenv/docker/blocks/prometheus_random_data/Dockerfile @@ -7,7 +7,7 @@ RUN CGO_ENABLED=0 GOOS=linux go install -tags netgo -ldflags '-w' github.com/pro # Final image. FROM scratch -LABEL maintainer "The Prometheus Authors " +LABEL maintainer="The Prometheus Authors " COPY --from=builder /go/bin/random . EXPOSE 8080 ENTRYPOINT ["/random"] diff --git a/devenv/docker/blocks/stateful_webhook/Dockerfile b/devenv/docker/blocks/stateful_webhook/Dockerfile new file mode 100644 index 00000000000..03b50db2135 --- /dev/null +++ b/devenv/docker/blocks/stateful_webhook/Dockerfile @@ -0,0 +1,12 @@ +FROM golang:1.23.5 + +ADD main.go /go/src/webhook/main.go + +WORKDIR /go/src/webhook + +RUN mkdir /tmp/logs +RUN go build -o /bin main.go + +ENV PORT=8080 + +ENTRYPOINT [ "/bin/main" ] diff --git a/devenv/docker/blocks/stateful_webhook/docker-compose.yaml b/devenv/docker/blocks/stateful_webhook/docker-compose.yaml new file mode 100644 index 00000000000..7217516c4e9 --- /dev/null +++ b/devenv/docker/blocks/stateful_webhook/docker-compose.yaml @@ -0,0 +1,5 @@ + stateful_webhook: + build: + context: docker/blocks/stateful_webhook + ports: + - "8080:8080" diff --git a/devenv/docker/blocks/stateful_webhook/main.go b/devenv/docker/blocks/stateful_webhook/main.go new file mode 100644 index 00000000000..926cf4e593e --- /dev/null +++ b/devenv/docker/blocks/stateful_webhook/main.go @@ -0,0 +1,149 @@ +package main + +import ( + "encoding/json" + "io" + "log" + "net/http" + "strings" + "sync" + "time" +) + +type Event struct { + Status string `json:"status"` + TimeNow time.Time `json:"timeNow"` + StartsAt time.Time `json:"startsAt"` + Node string `json:"node"` + DeltaLastSeconds float64 `json:"deltaLastSeconds"` + DeltaStartSeconds float64 `json:"deltaStartSeconds"` +} + +type Notification struct { + Alerts []Alert `json:"alerts"` + CommonAnnotations map[string]string `json:"commonAnnotations"` + CommonLabels map[string]string `json:"commonLabels"` + ExternalURL string `json:"externalURL"` + GroupKey string `json:"groupKey"` + GroupLabels map[string]string `json:"groupLabels"` + Message string `json:"message"` + OrgID int `json:"orgId"` + Receiver string `json:"receiver"` + State string `json:"state"` + Status string `json:"status"` + Title string `json:"title"` + TruncatedAlerts int `json:"truncatedAlerts"` + Version string `json:"version"` +} + +type Alert struct { + Annotations map[string]string `json:"annotations"` + DashboardURL string `json:"dashboardURL"` + StartsAt time.Time `json:"startsAt"` + EndsAt time.Time `json:"endsAt"` + Fingerprint string `json:"fingerprint"` + GeneratorURL string `json:"generatorURL"` + Labels map[string]string `json:"labels"` + PanelURL string `json:"panelURL"` + SilenceURL string `json:"silenceURL"` + Status string `json:"status"` + ValueString string `json:"valueString"` + Values map[string]any `json:"values"` +} + +type NotificationHandler struct { + startedAt time.Time + stats map[string]int + hist []Event + m sync.Mutex +} + +func NewNotificationHandler() *NotificationHandler { + return &NotificationHandler{ + startedAt: time.Now(), + stats: make(map[string]int), + hist: make([]Event, 0), + } +} + +func (ah *NotificationHandler) Notify(w http.ResponseWriter, r *http.Request) { + b, err := io.ReadAll(r.Body) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + n := Notification{} + if err := json.Unmarshal(b, &n); err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + log.Printf("got notification from: %s. a: %v", r.RemoteAddr, n) + + ah.m.Lock() + defer ah.m.Unlock() + + addr := r.RemoteAddr + if split := strings.Split(r.RemoteAddr, ":"); len(split) > 0 { + addr = split[0] + } + + a := n.Alerts[0] + + timeNow := time.Now() + + ah.stats[n.Status]++ + + var d time.Duration + if len(ah.hist) > 0 { + last := ah.hist[len(ah.hist)-1] + d = timeNow.Sub(last.TimeNow) + } + + ah.hist = append(ah.hist, Event{ + Status: n.Status, + StartsAt: a.StartsAt, + TimeNow: timeNow, + Node: addr, + DeltaLastSeconds: d.Seconds(), + DeltaStartSeconds: timeNow.Sub(ah.startedAt).Seconds(), + }) +} + +func (ah *NotificationHandler) GetNotifications(w http.ResponseWriter, _ *http.Request) { + ah.m.Lock() + defer ah.m.Unlock() + w.Header().Set("Content-Type", "application/json") + + res, err := json.MarshalIndent(map[string]any{"stats": ah.stats, "history": ah.hist}, "", "\t") + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + //nolint:errcheck + w.Write([]byte(`{"error":"failed to marshal alerts"}`)) + log.Printf("failed to marshal alerts: %v\n", err) + return + } + + log.Printf("requested current state\n%v\n", string(res)) + + _, err = w.Write(res) + if err != nil { + log.Printf("failed to write response: %v\n", err) + } +} + +func main() { + ah := NewNotificationHandler() + + http.HandleFunc("/ready", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + http.HandleFunc("/notify", ah.Notify) + http.HandleFunc("/notifications", ah.GetNotifications) + + log.Println("Listening") + //nolint:errcheck + http.ListenAndServe("0.0.0.0:8080", nil) +} diff --git a/devenv/docker/blocks/tempo/tempo.yaml b/devenv/docker/blocks/tempo/tempo.yaml index 7cfb1230a3f..b1130a6e27c 100644 --- a/devenv/docker/blocks/tempo/tempo.yaml +++ b/devenv/docker/blocks/tempo/tempo.yaml @@ -6,15 +6,23 @@ distributor: jaeger: # the receives all come from the OpenTelemetry collector. more configuration information can protocols: # be found there: https://github.com/open-telemetry/opentelemetry-collector/tree/main/receiver thrift_http: # - grpc: # for a production deployment you should only enable the receivers you need! + endpoint: "tempo:14268" # for a production deployment you should only enable the receivers you need! + grpc: + endpoint: "tempo:14250" thrift_binary: + endpoint: "tempo:6832" thrift_compact: + endpoint: "tempo:6831" zipkin: + endpoint: "tempo:9411" otlp: protocols: - http: grpc: + endpoint: "tempo:4317" + http: + endpoint: "tempo:4318" opencensus: + endpoint: "tempo:55678" compactor: compaction: diff --git a/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/alerts/overview.json b/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/alerts/overview.json index b4946cfb6b3..0357c3d10f1 100644 --- a/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/alerts/overview.json +++ b/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/alerts/overview.json @@ -151,17 +151,6 @@ "1h", "2h", "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" ] }, "timezone": "", diff --git a/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/mysql/overview.json b/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/mysql/overview.json index 7643250ec28..2bf789366cc 100644 --- a/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/mysql/overview.json +++ b/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/mysql/overview.json @@ -5376,17 +5376,6 @@ "1d" ], "status": "Stable", - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ], "type": "timepicker" }, "timezone": "browser", diff --git a/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json b/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json index b4946cfb6b3..0357c3d10f1 100644 --- a/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json +++ b/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json @@ -151,17 +151,6 @@ "1h", "2h", "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" ] }, "timezone": "", diff --git a/devenv/docker/ha_test/grafana/provisioning/dashboards/mysql/overview.json b/devenv/docker/ha_test/grafana/provisioning/dashboards/mysql/overview.json index 7643250ec28..2bf789366cc 100644 --- a/devenv/docker/ha_test/grafana/provisioning/dashboards/mysql/overview.json +++ b/devenv/docker/ha_test/grafana/provisioning/dashboards/mysql/overview.json @@ -5376,17 +5376,6 @@ "1d" ], "status": "Stable", - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ], "type": "timepicker" }, "timezone": "browser", diff --git a/docs/docs.mk b/docs/docs.mk index c0aae10ba50..dfb6e9e501c 100644 --- a/docs/docs.mk +++ b/docs/docs.mk @@ -120,3 +120,12 @@ update: ## Fetch the latest version of this Makefile and the `make-docs` script curl -s -LO https://raw.githubusercontent.com/grafana/writers-toolkit/main/docs/docs.mk curl -s -LO https://raw.githubusercontent.com/grafana/writers-toolkit/main/docs/make-docs chmod +x make-docs + +# ls static/templates/ | sed 's/-template\.md//' | xargs +TOPIC_TYPES := concept multiple-tasks reference section task tutorial visualization +.PHONY: $(patsubst %,topic/%,$(TOPIC_TYPES)) +topic/%: ## Create a topic from the Writers' Toolkit template. Specify the topic type as the target, for example, `make topic/task TOPIC_PATH=sources/my-new-topic.md`. +topic/%: + $(if $(TOPIC_PATH),,$(error "You must set the TOPIC_PATH variable to the path where the $(@F) topic will be created. For example: make $(@) TOPIC_PATH=sources/my-new-topic.md")) + mkdir -p $(dir $(TOPIC_PATH)) + curl -s -o $(TOPIC_PATH) https://raw.githubusercontent.com/grafana/writers-toolkit/refs/heads/main/docs/static/templates/$(@F)-template.md diff --git a/docs/make-docs b/docs/make-docs index ba448ce0727..80c89aea7ce 100755 --- a/docs/make-docs +++ b/docs/make-docs @@ -1,4 +1,6 @@ #!/bin/sh +# shellcheck disable=SC2034 +# # The source of this file is https://raw.githubusercontent.com/grafana/writers-toolkit/main/docs/make-docs. # # `make-docs` procedure changelog # @@ -6,6 +8,24 @@ # [Semantic versioning](https://semver.org/) is used to help the reader identify the significance of changes. # Changes are relevant to this script and the support docs.mk GNU Make interface. # +# ## 8.5.1 (2025-02-18) +# +# ### Fixed +# +# - PHONY declaration for topic/ targets. +# +# ## 8.5.0 (2025-02-13) +# +# ### Added +# +# - make topic/ TOPIC_PATH= target to create a new topic from the Writers' Toolkit templates. +# +# ## 8.4.0 (2025-01-27) +# +# ### Fixed +# +# - Correct mount for the /docs/grafana-cloud/send-data/fleet-management/ project. +# # ## 8.3.0 (2024-12-27) # # ### Added @@ -304,6 +324,7 @@ PODMAN="$(if command -v podman >/dev/null 2>&1; then echo podman; else echo dock if ! command -v curl >/dev/null 2>&1; then if ! command -v wget >/dev/null 2>&1; then + # shellcheck disable=SC2016 errr 'either `curl` or `wget` must be installed for this script to work.' exit 1 @@ -311,6 +332,7 @@ if ! command -v curl >/dev/null 2>&1; then fi if ! command -v "${PODMAN}" >/dev/null 2>&1; then + # shellcheck disable=SC2016 errr 'either `podman` or `docker` must be installed for this script to work.' exit 1 @@ -357,6 +379,10 @@ EOF exit 1 fi +# The following variables comprise a pseudo associative array of project names to source repositories. +# You only need to set a SOURCES variable if the project name does not match the source repository name. +# You can get a key identifier using the `identifier` function. +# To look up the value of any pseudo associative array, use the `aget` function. SOURCES_as_code='as-code-docs' SOURCES_enterprise_metrics='backend-enterprise' SOURCES_enterprise_metrics_='backend-enterprise' @@ -366,11 +392,16 @@ SOURCES_grafana_cloud_alerting_and_irm_slo='slo' SOURCES_grafana_cloud_k6='k6-docs' SOURCES_grafana_cloud_data_configuration_integrations='cloud-onboarding' SOURCES_grafana_cloud_frontend_observability_faro_web_sdk='faro-web-sdk' +SOURCES_grafana_cloud_send_data_fleet_management='fleet-management' SOURCES_helm_charts_mimir_distributed='mimir' SOURCES_helm_charts_tempo_distributed='tempo' SOURCES_opentelemetry='opentelemetry-docs' SOURCES_resources='website' +# The following variables comprise a pseudo associative array of project names to versions. +# You only need to set a VERSIONS variable if it is not the default of 'latest'. +# You can get a key identifier using the `identifier` function. +# To look up the value of any pseudo associative array, use the `aget` function. VERSIONS_as_code='UNVERSIONED' VERSIONS_grafana_cloud='UNVERSIONED' VERSIONS_grafana_cloud_alerting_and_irm_machine_learning='UNVERSIONED' @@ -378,12 +409,17 @@ VERSIONS_grafana_cloud_alerting_and_irm_slo='UNVERSIONED' VERSIONS_grafana_cloud_k6='UNVERSIONED' VERSIONS_grafana_cloud_data_configuration_integrations='UNVERSIONED' VERSIONS_grafana_cloud_frontend_observability_faro_web_sdk='UNVERSIONED' +VERSIONS_grafana_cloud_send_data_fleet_management='UNVERSIONED' VERSIONS_opentelemetry='UNVERSIONED' VERSIONS_resources='UNVERSIONED' VERSIONS_technical_documentation='UNVERSIONED' VERSIONS_website='UNVERSIONED' VERSIONS_writers_toolkit='UNVERSIONED' +# The following variables comprise a pseudo associative array of project names to source repository paths. +# You only need to set a PATHS variable if it is not the default of 'docs/sources'. +# You can get a key identifier using the `identifier` function. +# To look up the value of any pseudo associative array, use the `aget` function. PATHS_grafana_cloud='content/docs/grafana-cloud' PATHS_helm_charts_mimir_distributed='docs/sources/helm-charts/mimir-distributed' PATHS_helm_charts_tempo_distributed='docs/sources/helm-charts/tempo-distributed' @@ -816,7 +852,9 @@ EOF case "${OUTPUT_FORMAT}" in human) if ! command -v jq >/dev/null 2>&1; then + # shellcheck disable=SC2016 errr '`jq` must be installed for the `doc-validator` target to work.' + # shellcheck disable=SC2016 note 'To install `jq`, refer to https://jqlang.github.io/jq/download/,' exit 1 diff --git a/docs/sources/administration/data-source-management/_index.md b/docs/sources/administration/data-source-management/_index.md index 71c5d87d625..ce91fc46eae 100644 --- a/docs/sources/administration/data-source-management/_index.md +++ b/docs/sources/administration/data-source-management/_index.md @@ -110,6 +110,10 @@ By reducing the number of queries and requests sent to data sources, caching can Query caching works for Grafana's [built-in data sources]({{< relref "../../datasources/#built-in-core-data-sources" >}}), and [backend data source plugins](https://grafana.com/grafana/plugins/?type=datasource) that extend the `DataSourceWithBackend` class in the plugins SDK. +{{% admonition type="note" %}} +Logs Insights for the CloudWatch data source does not support query caching due to the way logs are requested from AWS. +{{% /admonition %}} + To verify that a data source works with query caching, follow the [instructions below](#enable-and-configure-query-caching) to **Enable and Configure query caching**. If caching is enabled in Grafana but the Caching tab is not visible for the given data source, then query caching is not available for that data source. {{% admonition type="note" %}} diff --git a/docs/sources/administration/data-source-management/teamlbac/_index.md b/docs/sources/administration/data-source-management/teamlbac/_index.md index ac801cf9d18..74470c792bf 100644 --- a/docs/sources/administration/data-source-management/teamlbac/_index.md +++ b/docs/sources/administration/data-source-management/teamlbac/_index.md @@ -20,7 +20,7 @@ Label-Based Access Control (LBAC) allows fine-grained access control to data sou ## Supported Data Sources -LBAC for data sources is currently available for `Loki, Prometheus` with basic authentication. Support for additional data sources may be added in future updates. +LBAC for data sources is currently generally available for `Loki` and in **experimental** for `Prometheus`. Support for additional data sources may be added in future updates. **LBAC for data sources offers:** @@ -28,11 +28,6 @@ LBAC for data sources is currently available for `Loki, Prometheus` with basic a - Simplified data source management by consolidating multiple sources into one. - Dashboard reuse across teams with tailored access. -{{< admonition type="note" >}} -LBAC rules is available for **private preview** in Grafana Cloud. -Report any unexpected behavior to the Grafana Support team. -{{< /admonition >}} - You can configure user access based upon team memberships using `LogQL`. LBAC for data sources controls access to logs or metrics depending on the rules set for each team. @@ -57,7 +52,7 @@ This flexibility allows teams to use the same data source for multiple use cases ## Before you begin -To be able to use LBAC for data sources, you need to enable the feature toggle `teamHttpHeaders` on your Grafana instance. +To be able to use LBAC for data sources metrics, you need to enable the feature toggle `teamHttpHeadersMimir` on your Grafana instance. ## Limitations @@ -95,4 +90,4 @@ To set up LBAC for data sources for a data source, refer to [Configure LBAC for ## Provisioning of LBAC rules -We recommend using our Terraform provider to set up provisioning for [Resource data source config LBAC rules] (https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/data_source_config_lbac_rules). Refer to our provider documentation to learn how to configure rules for a data source. +We recommend using our Terraform provider to set up provisioning for [Resource data source config LBAC rules](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/data_source_config_lbac_rules). Refer to our provider documentation to learn how to configure rules for a data source. diff --git a/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-loki/index.md b/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-loki/index.md index 0e5875eb26c..18a21b24ee8 100644 --- a/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-loki/index.md +++ b/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-loki/index.md @@ -19,8 +19,6 @@ You cannot configure LBAC rules for Grafana-provisioned data sources from the UI ## Before you begin -To be able to use LBAC for data sources rules, you need to enable the feature toggle `teamHttpHeaders` on your Grafana instance. Contact support to enable the feature toggle for you. - - Be sure that you have the permission setup to create a Loki tenant in Grafana Cloud - Be sure that you have admin data source permissions for Grafana. diff --git a/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-prometheus/index.md b/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-prometheus/index.md index aa54d970e6f..29f8467ad6d 100644 --- a/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-prometheus/index.md +++ b/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-prometheus/index.md @@ -18,20 +18,26 @@ weight: 250 LBAC for data sources is available in private preview on Grafana Cloud for Prometheus created with basic authentication. Prometheus data sources for LBAC for data sources can only be created, provisioning is currently not available. -You cannot configure LBAC rules for Grafana-provisioned data sources from the UI. Alternatively, you can replicate the setting of the provisioned data source in a new data source as described in [LBAC Configuration for New Prometheus Data Source](https://grafana.com/docs/grafana/latest/administration/data-source-management/teamlbac/configure-teamlbac-for-Prometheus/#task-1-lbac-configuration-for-new-Prometheus-data-source) and then add the LBAC configuration to the new data source. +You cannot configure LBAC rules for Grafana-provisioned data sources from the UI. Alternatively, you can replicate the setting of the provisioned data source in a new data source as described in [LBAC Configuration for New Prometheus Data Source](#task-1-lbac-configuration-for-new-prometheus-data-source) and then add the LBAC configuration to the new data source. ## Before you begin -To be able to use LBAC for data sources rules, you need to enable the feature toggle `teamHttpHeaders` on your Grafana instance. Contact support to enable the feature toggle for you. +To be able to use LBAC for Prometheus data sources, you need to enable the feature toggle `teamHttpHeadersMimir` on your Grafana instance. Contact support to enable the feature toggle for you. - Be sure that you have the permission setup to create a Prometheus tenant in Grafana Cloud - Be sure that you have admin data source permissions for Grafana. +## Grafana Cloud + +LBAC for data sources is available in private preview on Grafana Cloud for Prometheus created with basic authentication. Prometheus data sources for LBAC for data sources can only be created, provisioning is currently not available. + +You cannot configure LBAC rules for Grafana-provisioned data sources from the UI. We recommend that you replicate the setting of the provisioned data source in a new data source as described in [LBAC Configuration for New Prometheus Data Source](https://grafana.com/docs/grafana/latest/administration/data-source-management/teamlbac/configure-teamlbac-for-Prometheus/#task-1-lbac-configuration-for-new-Prometheus-data-source) and then add the LBAC configuration to the new data source. + ### Permissions We recommend that you remove all permissions for roles and teams that are not required to access the data source. This will help to ensure that only the required teams have access to the data source. The recommended permissions are `Admin` permission and only add the teams `Query` permissions that you want to add LBAC for data sources rules for. -## Task 1: LBAC Configuration for New Prometheus Data Source +## Task 1: LBAC Configuration for new Prometheus data source 1. Access Prometheus data sources details for your stack through grafana.com 1. Copy Prometheus details and create a CAP @@ -53,11 +59,11 @@ For more information on how to setup LBAC for data sources rules for a Prometheu LBAC for data sources is available in Grafana Enterprise for Prometheus created with basic authentication. Prometheus data sources for LBAC for data sources can only be created. -You cannot configure LBAC rules for Grafana-provisioned data sources from the UI. Alternatively, you can replicate the setting of the provisioned data source in a new data source as described in [LBAC Configuration for New Prometheus Data Source](https://grafana.com/docs/grafana/latest/administration/data-source-management/teamlbac/configure-teamlbac-for-Prometheus/#task-1-lbac-configuration-for-new-Prometheus-data-source) and then add the LBAC configuration to the new data source. +You cannot configure LBAC rules for Grafana-provisioned data sources from the UI. Alternatively, you can replicate the setting of the provisioned data source in a new data source as described in [LBAC Configuration for new Prometheus data source](https://grafana.com/docs/grafana//administration/data-source-management/teamlbac/configure-teamlbac-for-prometheus/#task-1-lbac-configuration-for-new-prometheus-data-source) and then add the LBAC configuration to the new data source. ## Before you begin -To be able to use LBAC for data sources rules, you need to enable the feature toggle `teamHttpHeaders` on your Grafana instance. Contact support to enable the feature toggle for you. +To be able to use LBAC for Prometheus data sources, you need to enable the feature toggle `teamHttpHeadersMimir` on your Grafana instance. Contact support to enable the feature toggle for you. - Be sure that you have the permission setup to create a cluster in your Grafana - Be sure that you have admin plugins permissions for Grafana. @@ -79,7 +85,7 @@ We recommend that you remove all permissions for roles and teams that are not re - Ensure the CAP does not include `labels` rules. - Store the `token` to be used as password for authentication. -## Task 1: LBAC Configuration for New Prometheus Data Source +## Task 1: LBAC Configuration for new Prometheus data source 1. Create a new Prometheus data source - In Grafana, proceed to add a new data source and select Prometheus as the type. diff --git a/docs/sources/administration/data-source-management/teamlbac/create-teamlbac-rules/index.md b/docs/sources/administration/data-source-management/teamlbac/create-teamlbac-rules/index.md index 3942490c0ff..f76dd3d64bb 100644 --- a/docs/sources/administration/data-source-management/teamlbac/create-teamlbac-rules/index.md +++ b/docs/sources/administration/data-source-management/teamlbac/create-teamlbac-rules/index.md @@ -17,8 +17,6 @@ LBAC for data sources is available for LBAC-supported data sources created with ## Before you begin -To be able to use LBAC for data sources rules, you need to enable the feature toggle `teamHttpHeaders` on your Grafana instance. Contact support to enable the feature toggle for you. - - Be sure that you have the permission setup to create a Loki tenant in Grafana Cloud. - Be sure that you have admin data source permissions for Grafana. - Be sure that you have a team setup in Grafana. diff --git a/docs/sources/administration/enterprise-licensing/_index.md b/docs/sources/administration/enterprise-licensing/_index.md index 131ec8d0f79..16d7e9899f1 100644 --- a/docs/sources/administration/enterprise-licensing/_index.md +++ b/docs/sources/administration/enterprise-licensing/_index.md @@ -246,6 +246,14 @@ Your license is controlled by the following rules: As the license expiration date approaches, you will see a banner in Grafana that encourages you to renew. To learn about how to renew your license and what happens in Grafana when a license expires, refer to [License expiration]({{< relref "#license-expiration" >}}). +**License token expiration:** Grafana Enterprise requires a valid token, which is automatically renewed. + +A license token is a digital key that activates your license. By default, the license token is renewed every 24 hours by calling the Grafana API. Short-lived license tokens enable more frequent validation that licenses are compliant, and allow for more frequent license updates - for example, adding users or invalidating a compromised license. + +To view the details of your license token, sign in to Grafana Enterprise as a Server Administrator and visit **Administration** > **General** > **Statistics and licensing**. Token details are in the Token section under License Details. + +License token renewal requires internet access, and requires that the `auto_refresh_license` [configuration setting](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/enterprise-configuration/#auto_refresh_license) be set to `true`. If your Grafana Enterprise instance cannot connect to the internet, contact your Grafana Labs account team for additional options for token renewal and license audit. + **Grafana License URL:** Your license does not work with an instance of Grafana with a different root URL. The License URL is the complete URL of your Grafana instance, for example `https://grafana.your-company.com/`. It is defined in the [root_url]({{< relref "../../setup-grafana/configure-grafana/#root_url" >}}) configuration setting. @@ -256,14 +264,24 @@ The system creates a session when a user signs in to Grafana from a new device, When a user reaches the session limit, the fourth connection succeeds and the longest inactive session is signed out. -### Request usage billing - -You can request Grafana Labs to activate usage billing which allows an unlimited number of active users. When usage billing is enabled, Grafana does not enforce active user limits or display warning banners. Instead, you are charged for active users that exceed the limit, according to your customer contract. - -Usage billing involves a contractual agreement between you and Grafana Labs, and it is only available if Grafana Enterprise is configured to [automatically refresh its license token]({{< relref "../../setup-grafana/configure-grafana/enterprise-configuration/#auto_refresh_license" >}}). - ### Request a change to your license To increase the number of licensed users within Grafana, extend a license, or change your licensed URL, contact [Grafana support](/profile/org#support) or your Grafana Labs account team. They will update your license, which you can activate from within Grafana. For instructions about how to activate your license after it is updated, refer to [Activate an Enterprise license]({{< relref "#activate-an-enterprise-license" >}}). + +## Usage billing + +Standard Grafana Enterprise licenses include a certain number of seats that can be used, and prevent more users logging into Grafana than have been licensed. This makes sense if you prefer a predictable bill. It can however be a problem if you anticipate uneven usage patterns over time or when it's critical that no user ever be prevented from logging in to Grafana due to capacity constraints. + +For those use-cases we support usage-based billing, where your license includes a certain number of included users and you are billed on a monthly basis for any excess active users during the month. + +Usage billing involves a contractual agreement between you and Grafana Labs and an update to your license, and it is only available if Grafana Enterprise version 10.0.0 or higher is configured to [automatically refresh its license token]({{< relref "../../setup-grafana/configure-grafana/enterprise-configuration/#auto_refresh_license" >}}). + +### User deduplication + +If your organization has multiple Grafana Enterprise instances with usage billing enabled, then each active user counts only once toward your license, regardless of how many instances that user signs into. Each Grafana Enterprise instance submits a hashed list of users to Grafana Labs via API every day. Each user email address or anonymous device ID is hashed using a one-way sha256 algorithm, and submitted to Grafana where the hashed users are deduplicated across instances. + +### Request usage billing + +To request usage billing, contact your Grafana Labs account team or [submit a support ticket](https://grafana.com/profile/org#support). diff --git a/docs/sources/administration/migration-guide/_index.md b/docs/sources/administration/migration-guide/_index.md new file mode 100644 index 00000000000..deb7de8c533 --- /dev/null +++ b/docs/sources/administration/migration-guide/_index.md @@ -0,0 +1,23 @@ +--- +aliases: + - /docs/grafana-cloud/account-management/e2c-guide/ + - /docs/grafana-cloud/account-management/migration-guide/ +description: Migrate from Grafana OSS/Enterprise to Grafana Cloud +keywords: + - Grafana Cloud + - Grafana Enterprise + - Grafana OSS +menuTitle: Migrate from Grafana OSS/Enterprise to Grafana Cloud +title: Migrate from Grafana OSS/Enterprise to Grafana Cloud +--- + +# Migrate from Grafana OSS/Enterprise to Grafana Cloud + +When you decide to migrate from your self-managed Grafana instance to Grafana Cloud, you can benefit from the convenience of a managed observability platform, additional cloud-only features, and robust security. There are a couple of key approaches to help you transition to Grafana Cloud. + +| Migration type | Tools used | Availability | Migratable resources | +| :------------- | :-------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Manual |
  • Command line utilities
  • The Grafana HTTP API
| Generally available in all versions of Grafana OSS/Enterprise | The entire Grafana instance | +| Automated | The Grafana Cloud Migration Assistant | Available in public preview from Grafana v11.2 using the `onPremToCloudMigrations` feature toggle. This toggle is enabled by default in Grafana v11.5 and later. |
  • Dashboards
  • Folders
  • Data sources
  • App Plugins
  • Panel Plugins
  • Library Panels
  • Grafana Alerting resources
| + +Our detailed [migration guide](https://www.grafana.com/docs/grafana-cloud/account-management/migration-guide/manually-migrate-to-grafana-cloud/) explains the key steps and scripts to manually migrate your resources to Grafana Cloud, covering a comprehensive set of resources in your Grafana instance. Alternatively, the [Grafana Cloud Migration Assistant](https://www.grafana.com/docs/grafana-cloud/account-management/migration-guide/cloud-migration-assistant/), available in public preview in Grafana v11.2 and later, automates the migration process across a broad range of Grafana resources. You can use the migration assistant to migrate a large proportion of your Grafana resources and then, if needed, leverage the migration guide to migrate the rest. diff --git a/docs/sources/administration/migration-guide/cloud-migration-assistant.md b/docs/sources/administration/migration-guide/cloud-migration-assistant.md new file mode 100644 index 00000000000..87a5a47cb5a --- /dev/null +++ b/docs/sources/administration/migration-guide/cloud-migration-assistant.md @@ -0,0 +1,195 @@ +--- +description: Migrate from Grafana OSS/Enterprise to Grafana Cloud using the Grafana Cloud Migration Assistant +keywords: + - Grafana Cloud + - Grafana Enterprise + - Grafana OSS +menuTitle: Migrate to Grafana Cloud using the Grafana Cloud Migration Assistant +title: Migrate from Grafana OSS/Enterprise to Grafana Cloud using the Grafana Cloud Migration Assistant +weight: 400 +--- + +# Grafana Cloud Migration Assistant + +The Grafana Cloud Migration Assistant is available in Grafana 11.2+ as a [public preview feature](https://grafana.com/docs/release-life-cycle/#public-preview) that automatically migrates resources from your Grafana OSS/Enterprise instance to Grafana Cloud. It provides the following functionalities: + +- Securely connect your self-managed instance to a Grafana Cloud instance. +- Seamlessly migrate resources such as dashboards, data sources, and folders to your cloud instance in a few easy steps. +- View the migration status of your resources in real-time. + +Some of the benefits of the migration assistant are: + +Ease of use +: Follow the steps provided by the UI to easily migrate all your resources to Grafana Cloud without using Grafana APIs or scripts. + +Security +: Encrypt and securely migrate your resources to your connected Grafana Cloud instance. + +Speed +: Migrate all of your resources in minutes and accelerate your transition to Grafana Cloud. + +## Supported resources + +The following resources are supported by the migration assistant: + +- Dashboards +- Folders +- Data sources +- App Plugins +- Panel Plugins +- Library Panels +- Grafana Alerting resources + +## Before you begin + +To use the Grafana migration assistant, you need: + +- Grafana v11.2 or above with the `onPremToCloudMigrations` feature toggle enabled. In Grafana 11.5, this is enabled by default. For more information on how to enable a feature toggle, refer to [Configure feature toggles](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/feature-toggles/#configure-feature-toggles). +- A [Grafana Cloud Stack](https://grafana.com/docs/grafana-cloud/get-started/) you intend to migrate your resources to. +- [`Admin`](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/cloud-roles/) access to the Grafana Cloud Stack. To check your access level, go to `https://grafana.com/orgs//members`. +- [Grafana server administrator](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#grafana-server-administrators) access to your existing Grafana OSS/Enterprise instance. To check your access level, go to `https:///admin/users`. +- Internet access from your existing Grafana OSS/Enterprise instance. + +## Access the migration assistant + +In Grafana OSS, access to the migration assistant is limited to the server administrator. + +In Grafana Enterprise, the server administrator has access to the migration assistant by default. It is also possible to grant access to other Admins using a role-based access control (RBAC) role that enables other admins on the Grafana instance to view, build snapshots, and upload resources to Grafana Cloud. + +### Grant access in Grafana Enterprise + +{{< admonition type="important">}} +You must [configure RBAC](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/configure-rbac/) before you can grant other administrators access to the Grafana Migration Assistant. +{{< /admonition >}} + +To grant other Admins access to the migration assistant in Grafana Enterprise: + +1. Sign in to Grafana as a server administrator. +1. Navigate to **Home** > **Administration** > **Users and access** > **Users** in the Grafana sidebar. +1. Click an Admin. +1. In the **Organizations** section, click **Change role**. +1. Select **Organization resource migrator** from the role selector menu under **Migration Assistant**. + + ![The Organization resource migrator role in the role picker](/media/docs/grafana-cloud/account-management/screenshot-grant-migration-assistant-access.png) + +1. Click **Apply**. + +## Use the migration assistant + +You can use the migration assistant to generate a migration token on your Grafana Cloud instance, use that token to connect your self-managed Grafana instance to your Grafana Cloud instance, build snapshots of your self-managed Grafana instance, and upload these snapshots to Grafana Cloud. + +### Generate a migration token on the destination cloud instance: + +1. Navigate to **Home** > **Administration** > **General** > **Migrate to Grafana Cloud** in the cloud instance where you intend to migrate your resources. +1. Click on the **Generate a migration token** button. + + ![The Generate a migration token button in the Migrate to Grafana Cloud page in the intended Grafana Cloud Stack](/media/docs/grafana-cloud/account-management/screenshot-generate-migration-token.png) + +1. Make a copy of the migration token by copying to clipboard. The token is required to authenticate your self-managed instance with the Grafana Cloud Stack. + +### Connect your self-managed Grafana instance to the Grafana Cloud Stack + +1. On your self-managed Grafana instance, navigate to **Home** > **Administration** > **General** > **Migrate to Grafana Cloud**. + +1. Click the **Migrate this instance to Cloud** button. + + ![The Migrate this instance to Cloud button in the Migrate to Grafana Cloud page on a self-managed Grafana instance](/media/docs/grafana-cloud/account-management/screenshot-migrate-to-cloud.png) + +1. Enter your token and click **Connect to this Stack**. + + ![The Migration token field and Connect to this stack button in the Connect to a cloud stack page in a self-managed Grafana instance](/media/docs/grafana-cloud/account-management/screenshot-connect-to-a-stack.png) + +### Build a snapshot + +After connecting to the cloud stack, this is the empty state of the migration assistant. You need to create a snapshot of the self-managed Grafana instance to upload it to the cloud stack. + +- Click **Build snapshot** + + ![The Build snapshot button on the Migrate to Grafana Cloud page in a self-managed Grafana instance](/media/docs/grafana-cloud/account-management/screenshot-build-a-snapshot.png) + +### Upload resources to the cloud + +After a snapshot is created, a list of resources appears with resource Type and Status populated with **Not yet uploaded**. + +![A list of resources with snapshots built but not yet uploaded to Grafana Cloud](/media/docs/grafana-cloud/account-management/screenshot-upload-snapshot.png) + +1. Click on **Upload snapshot** to copy the resources to the Grafana Cloud instance. This also updates statuses for the list of resources. The status changes to 'Uploaded to cloud' for resources successfully copied to the cloud. + + The Snapshot information also updates to inform the user of total resources, errors, and total number of successfully migrated resources. + + ![An updates list of resources with snapshots built after attempting to upload them to Grafana Cloud](/media/docs/grafana-cloud/account-management/screenshot-updated-snapshot-page.png) + +1. Use the assistant's real-time progress tracking to monitor the migration. + +1. Review error details for any issues that need manual resolution. + +## Snapshots created by the migration assistant + +The migration assistant currently supports a subset of all resources available in Grafana. Refer to [Supported Resources](https://wwww.grafana.com/docs/grafana-cloud/account-management/cloud-migration-assistant/#supported-resources) for more details. + +When you create a snapshot, the migration assistant makes a copy of all supported resources and saves them in the snapshot. The snapshot reflects the current state of the resources when the snapshot is built and is stored locally on your instance, ready to be uploaded in the last stage. It is currently not possible to select specific resources to include in the snapshot, such as only dashboards. All supported resources are included by default. + +Resources saved in the snapshot are strictly limited to the resources stored within an organization. This is important to note if there are multiple organizations used in your Grafana instance. If you want to migrate multiple organizations, refer to [Migrate multiple organizations](https://wwww.grafana.com/docs/grafana-cloud/account-management/cloud-migration-assistant/#migrate-multiple-organizations) for more information and guidance. + +## Resource migration details + +During a migration, resource UIDs are preserved, allowing you to correlate your local and cloud resources. If you perform the same migration multiple times, resources in your Grafana Cloud stack that were previously migrated are updated. The assistant never modifies your self-managed resources or cloud resources that didn't come from a snapshot. + +### Dashboards and folders + +Dashboard names and UIDs are preserved along with references to data sources. Folder hierarchy is also preserved, so you can find your dashboards and other resources saved in identical folder locations. + +### Data sources + +Your data sources, including credentials, are migrated securely and seamlessly to your Grafana Cloud instance, so you don't need to find and enter all your data source credentials again. + +### Plugins + +The migration assistant supports any plugins found in the plugins catalog. As long as the plugin is signed or is a core plugin built into Grafana, it is eligible for migration. Due to security reasons, unsigned plugins are not supported in Grafana Cloud. If you are using any unsigned private plugins, Grafana recommends you seek an alternative plugin for the catalog or work on a strategy to deprecate certain functionality from your self-managed instance. + +### Grafana Alerting resources + +The migration assistant can migrate the majority of Grafana Alerting resources to your Grafana Cloud instance. These include: + +- Alert rules +- Notifications +- Contact points +- Mute timings +- Notification policy tree +- Notification templates + +This is sufficient to have your Alerting configuration up and running in Grafana Cloud with minimal effort. + +Migration of Silences is not supported by the migration assistant and needs to be configured manually. Alert History is also not available for migration. + +Successfully migrating Alerting resources to your Grafana Cloud instance could result in 2 sets of notifications being generated; one from your OSS/Enterprise instance and another from the newly migrated alerts in your Grafana Cloud instance. To avoid double notifications, a new `alert_rules_state` configuration option in the `custom.ini` or `grafana.ini` file controls how Alert Rules are migrated to the Grafana Cloud instance and is set to `paused` by default so you can review and test your Alerting resources in your Grafana Cloud instance without duplicate notifications. + +The available options for `alert_rule_state` are: + +`paused` +: Creates all Alert rules in paused state on the Cloud instance. This is helpful to avoid double notifications. + +`unchanged` +: The Alert rules maintain their original state coming from the source instance. + +When you are ready to start using your alert rules and notifications from your Grafana Cloud instance, run the migration again with `alert_rules_state = unchanged`. + +### Resource permissions + +Because the migration assistant does not yet migrate teams or RBAC permissions, your resources are migrated with default permissions. Ensure that you reconfigure permissions in your cloud stack as needed following a migration. For more information, refer to [Grafana Cloud user roles and permissions](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/cloud-roles/). + +## Migrate multiple organizations + +If you are using the [organizations](https://grafana.com/docs/grafana/latest/administration/organization-management/#about-organizations) feature on your Grafana Instance and intend to migrate to Grafana Cloud, you need to plan this aspect of the migration carefully. + +The organizations feature is not supported in Grafana Cloud, but folders and RBAC can be used to protect and grant permissions to resources instead. The recommended path is to migrate multiple organizations to a single cloud stack. This is the simplest option and provides the best user experience. + +The migration assistant creates and uploads snapshots based on the resources within a specific organization. There is no option to migrate an entire Grafana instance with multiple organizations at once. You need to run the migration process for each organization you want to migrate. + +The Grafana server administrator is granted access to the migration assistant by default. The server administrator can perform the migration by switching organizations and running the migration assistant each time. The Grafana server administrator can also grant access to the migration assistant to organization administrators who are members using the RBAC **Migration Assistant:Organization resource migrator** role. This allows those organization administrators to run the migration process for their respective organizations. + +### Access Control and managing resources in the Cloud Instance + +The main driver for setting up organizations in the first place is resource isolation. In order to achieve this in Grafana Cloud, you can organize resources into folders and set up teams and permissions that correspond to your organizations. + +For more information about configuring teams and permissions, refer to [Configure Grafana Teams](https://grafana.com/docs/grafana/latest/administration/team-management/configure-grafana-teams/). diff --git a/docs/sources/administration/migration-guide/manually-migrate-to-grafana-cloud.md b/docs/sources/administration/migration-guide/manually-migrate-to-grafana-cloud.md new file mode 100644 index 00000000000..6049846c108 --- /dev/null +++ b/docs/sources/administration/migration-guide/manually-migrate-to-grafana-cloud.md @@ -0,0 +1,312 @@ +--- +description: Migrate from Grafana OSS/Enterprise to Grafana Cloud manually +keywords: + - Grafana Cloud + - Grafana Enterprise + - Grafana OSS +menuTitle: Manually migrate to Grafana Cloud +title: Migrate from Grafana OSS/Enterprise to Grafana Cloud manually +weight: 300 +--- + +# Migrate from Grafana OSS/Enterprise to Grafana Cloud manually + +This migration guide is designed to assist Grafana OSS/Enterprise users in seamlessly transitioning manually to Grafana Cloud. + +{{< admonition type="note" >}} +There isn't yet a standard method for importing existing data into Grafana Cloud from self-managed databases. +{{< /admonition >}} + +## Plan and perform a manual migration + +If you need to migrate resources beyond what is supported by the Grafana Cloud Migration Assistant, you can migrate them manually with this guide. Moving your team from Grafana OSS/Enterprise to Grafana Cloud manually involves some coordination and communication in addition to the technical migration in the following documentation. + +If you are an existing Grafana OSS/Enterprise customer, contact your account team at Grafana Labs to plan a transition period, arrange licenses, and learn how much your Grafana Cloud subscription costs in comparison to Grafana OSS/Enterprise. The account team can also offer specific guidance and arrange professional services to assist with your migration if needed. + +Evaluate Grafana Cloud's security and compliance policies at the [Grafana Labs Trust Center](https://trust.grafana.com/). + +You may choose to test Grafana Cloud for some time before migrating your entire organization. To do so, set up a “test” stack in Cloud and migrate resources there first. If you use Grafana Alerting, make sure to set up a different contact point so that alerts do not fire twice. + +When you decide to migrate, set aside a day of cutover during which users should not create new dashboards or alerts. Migrate any newly-created resources, turn on your production Alerting contact points and notification policies in Cloud and turn them off in Grafana OSS/Enterprise, and notify your users. You may also choose to redirect the Grafana OSS/Enterprise URL to your Grafana Cloud URL. + +| Component | Migration Effort | Notes | +| ------------ | ---------------- | -------------------------------------------------------------------------- | +| Folders | Low | | +| Dashboards | Low | Data source references might need to be renamed | +| Alerts | Medium | Data source based alerts might need to be adapted | +| Plugins | Medium | Depends on the feature set of the plugin | +| Data sources | High | If the data sources references any secrets, you need to provide them again | + +## Before you begin + +Ensure you have the following: + +- A [Grafana Cloud Stack](https://grafana.com/docs/grafana-cloud/get-started/) and access to a Linux Machine (or a working WSL2 installation) to run the code snippets in this guide. +- Administrator access to a Grafana Cloud stack. To check you access level, Go to `https://grafana.com/orgs//members` +- Administrator access to your existing Grafana OSS/Enterprise instance. To check your access level, Go to `https:///admin/users` +- Access to the credentials used to connect to your data sources. For example, API keys or usernames and passwords. Since this information is encrypted, it cannot be copied from one instance to the other. +- If some of your data sources are only available from inside your network, refer to the requirements for [Private Data Source Connect](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/) +- For Plugins, Reports and Playlists only: The [curl](https://github.com/curl/curl) and [jq](https://jqlang.github.io/jq/download/) command line tools + +## Upgrade Grafana OSS/Enterprise to the latest version + +Grafana Cloud stacks generally run the latest version of Grafana. In order to avoid issues during migration, upgrade Grafana by following our guides [here](https://grafana.com/docs/grafana/latest/upgrade-guide/). + +## Migrate Grafana resources + +In this guide, the term **"resources"** refers to things you create in Grafana, like dashboards, folders, alerts, data sources, and permissions. + +The process of migration works by pulling the existing resources (like dashboards and folders) from the old Grafana instance, modifying them if necessary, and then pushing them to the new Grafana Cloud instance. + +In the provided code snippets throughout this migration guide, you need to substitute specific placeholders with your actual credentials and instance URLs. Make the following replacements before executing the scripts: + +- `$GRAFANA_SOURCE_TOKEN` with the access token from your Grafana OSS/Enterprise instance. +- `$GRAFANA_DEST_TOKEN` with the access token from your Grafana Cloud instance. +- `$GRAFANA_ONPREM_INSTANCE_URL` with the URL of your Grafana OSS/Enterprise instance. For example: `https://grafana.mydomain.com` +- `$GRAFANA_CLOUD_INSTANCE_URL` with the URL of your Grafana Cloud instance. For example: `https://myorganization.grafana.net` + +### Migrate Grafana Plugins + +Migration of plugins is the first step when transitioning from Grafana OSS/Enterprise to Grafana Cloud, given that plugins are integral components that influence the functionality and display of other Grafana resources, such as dashboards. + +1. To retrieve the Plugins installed in your Grafana OSS/Enterprise instance, issue an HTTP GET request to the `/api/plugins` endpoint. Use the following shell command: + + ```shell + response=$(curl -s -H "Accept: application/json" -H "Authorization: Bearer $GRAFANA_SOURCE_TOKEN" "${GRAFANA_ONPREM_INSTANCE_URL}/api/plugins") + + plugins=$(echo $response | jq '[.[] | select(.signatureType == "community" or (.signatureType != "internal" and .signatureType != "")) | {name: .id, version: .info.version}]') + + echo "$plugins" > plugins.json + ``` + + The command provided above will carry out an HTTP request to this endpoint and accomplish several tasks: + + - It issues a GET request to the `/api/plugins` endpoint of your Grafana OSS/Enterprise instance to retrieve a list of installed plugins. + - It filters out the list to only include community plugins and those signed by external parties. + - It extracts the plugin ID and version before storing them in a `plugins.json` file. + +1. To import the plugins in your Grafana Cloud Instance, execute the following command. This command constructs an HTTP POST request to `https://grafana.com/api/instances//plugins` + + ```shell + CLOUD_INSTANCE=$GRAFANA_CLOUD_INSTANCE_URL + + stack_slug="${CLOUD_INSTANCE#*//}" + stack_slug="${stack_slug%%.*}" + jq -c '.[]' plugins.json | while IFS= read -r plugin; do + name=$(echo "$plugin" | jq -r '.name') + version=$(echo "$plugin" | jq -r '.version') + echo "Adding plugin $name with version $version to stack $stack_slug" + response=$(curl -s -X POST "https://grafana.com/api/instances/$stack_slug/plugins" \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d "{\"plugin\": \"$name\", \"version\": \"$version\"}") + echo "POST response for plugin $name version $version: $response" + done + ``` + + Replace `` with your Grafana Cloud Access Policy Token. To create a new one, refer to Grafana Cloud [access policies documentation](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/access-policies/) + + This script iterates through each plugin listed in the `plugins.json` file: + + - It constructs a POST request for each plugin to add it to the specified Grafana Cloud instance. + - It reports back the response for each POST request to give you confirmation or information about any issues that occurred. + +### Migrate resources that are already provisioned as-code + +If you already use tools like [Terraform](https://grafana.com/docs/grafana-cloud/developer-resources/infrastructure-as-code/terraform/), [Ansible](https://grafana.com/docs/grafana-cloud/developer-resources/infrastructure-as-code/ansible/), or [Grafana’s HTTP API](https://grafana.com/docs/grafana-cloud/developer-resources/api-reference/http-api/) to provision resources to Grafana, redirect those to the new Grafana Cloud instance by replacing the Grafana URL and credentials. + +### Migrate dashboards, folders, data sources, library panels, and alert rules using Grizzly + +Grizzly is a command line tool that streamlines working with Grafana resources. Use it to migrate most of the content in your Grafana instance. Follow these steps in your terminal to install Grizzly. If you need to change the os or the architecture, Refer to the Grizzly [releases](https://github.com/grafana/grizzly/releases) and use the binary according to your needs. + +```shell +# download the binary (adapt os and arch as needed) +$ curl -fSL -o "/usr/local/bin/grr" "https://github.com/grafana/grizzly/releases/download/v0.3.1/grr-linux-amd64" + +# make it executable +$ chmod a+x "/usr/local/bin/grr" + +# have fun :) +$ grr --help +``` + +First, create a new folder on your computer and navigate to it to keep your work organized. + +```shell +mkdir grafana-migration +cd grafana-migration +``` + +To give grizzly access to your Grafana OSS/Enterprise instance and the Grafana Cloud Instance, you need to create a [service account](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/) and a corresponding [access token](https://www.grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/#service-account-tokens) on each instance. You can use these tokens to authenticate requests to pull and push resources. Follow these steps on your Grafana OSS/Enterprise instance: + +- Navigate to the **Administration -> Users and access -> Service Accounts** Page within the Grafana OSS/Enterprise instance. +- Click on **Add Service Account** +- Give the Service account a descriptive name like “grizzly-migration” and apply the **Admin** role. +- After creating the account, click on **Add Service Account Token** +- Enter a name for the token +- Select **Set expiration date** and enter an expiry date for the token +- Click **Generate Token** and save this token in a password manager or other secure place. + +Complete the service account creation and token generation process for your Grafana Cloud Instance by following the same steps outlined above for your Grafana OSS/Enterprise instance. This ensures that Grizzly has the necessary access token for both platforms. + +Next, to tell grizzly which instances you’re going to work on, use the following commands: + +```shell +grr config create-context grafana-onprem +grr config use-context grafana-onprem +grr config set output-format json +grr config set grafana.url $GRAFANA_ENT_INSTANCE_URL +grr config set grafana.token $GRAFANA_SOURCE_TOKEN + +grr config create-context grafana-cloud +grr config use-context grafana-cloud +grr config set output-format json +grr config set grafana.url $GRAFANA_CLOUD_INSTANCE_URL +grr config set grafana.token $GRAFANA_DEST_TOKEN +``` + +Afterward, you will have two contexts set up; one for your local Grafana OSS/Enterprise installation and one for Grafana Cloud. The `grr config use-context` command allows you to switch between the two instances while using Grizzly. + +#### Export existing resources + +Switch to the `grafana-onprem` context and use the pull command to fetch the resources you want to migrate: + +```shell +grr config use-context grafana-onprem +grr pull . \ + -t 'Dashboard/*' \ + -t 'Datasource/*' \ + -t 'DashboardFolder/*' \ + -t 'LibraryElement/*' \ + -t 'AlertRuleGroup/*' \ + -t 'AlertContactPoint/*' \ + -t 'AlertNotificationPolicy/*' +``` + +This will fetch the specified resources from Grafana and store them in the current directory. + +#### Push the resources to your Grafana Cloud stack + +With everything in place, switch to the Grafana cloud context and use the following commands to apply the resources to the configured instance: + +```shell +grr config use-context grafana-cloud + +grr apply . -t 'DashboardFolder/*' +grr apply . -t 'LibraryElement/*' +grr apply . -t 'Datasource/*' +grr apply . -t 'Dashboard/*' +grr apply . -t 'AlertRuleGroup/*' +grr apply . -t 'AlertContactPoint/*' +grr apply . -t 'AlertNotificationPolicy/*' +``` + +#### Fill in data source credentials + +After migrating your data sources, you must fill in their credentials, like tokens, usernames, or passwords. For security reasons, grizzly cannot read encrypted data source credentials from the existing Grafana instance. + +To fill in the missing authentication information, go to the **Connections -> Datasources** page in your new Grafana Cloud instance and verify that credentials for all data sources are set. You can skip data sources starting with `grafanacloud` - These are managed by Grafana Cloud directly and provide access to Grafana Cloud databases. + +If one of your data sources can only be accessed from your internal network, take a look at the [Private Data Source Connect documentation](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/). + +After you have configured the data sources, all your dashboards should be available as they were before. + +##### (Optional) Configure Private Data Source Connect (PDC) + +This step only applies if you use Grafana OSS/Enterprise to access network-secured data sources. + +Some data sources, like Prometheus or SQL databases, live on private networks or behind fire wall rules that are not accessible by Grafana Cloud. If your Grafana OSS/Enterprise instance was hosted on the same network as your data source, you might find that Grafana Cloud cannot connect to all of the same data sources that Grafana OSS/Enterprise could access. + +To access these data sources from Grafana Cloud, follow our guide to [configure PDC in your network](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/), and then configure the applicable Grafana data sources to [connect using PDC](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/#configure-a-data-source-to-use-private-data-source-connect-pdc). Note that PDC is only needed for your network-secured data sources, not for data sources like Splunk or CloudWatch that are accessible over the public internet. + +For more information on how PDC works, see our [overview document]({{< relref "../connect-externally-hosted/private-data-source-connect" >}}). + +### Migrate reports and playlists using Grafana’s HTTP API + +Grizzly does not currently support Reports and Playlists as a resource, so you can perform this migration using Grafana’s HTTP API using the `curl` command. + +#### Reports (For Grafana Enterprise only) + +1. To export your Reports, you will need to invoke the `api/reports` endpoint of your Grafana OSS/Enterprise instance. The below shell command accomplishes this by using `curl` to send a request to the endpoint and then stores the retrieved report configuration into a file named `reports.json`. + + ```shell + curl ${GRAFANA_ONPREM_INSTANCE_URL}/api/reports -H "Authorization: Bearer $GRAFANA_SOURCE_TOKEN" > reports.json + ``` + +2. To upload the configuration data you have saved in the `reports.json` file to your new Grafana Cloud instance, run the below command. The command will take the local file `reports.json` and push its contents to the `api/reports` endpoint of your Grafana Cloud instance. + + ```shell + jq -M -r -c '.[]' < reports.json | while read -r json; do curl -XPOST ${GRAFANA_CLOUD_INSTANCE_URL}/api/reports -H"Authorization: Bearer $GRAFANA_DEST_TOKEN" -d"$json" -H 'Content-Type: application/json'; done + ``` + +#### Playlists + +1. To retrieve the Playlists from your Grafana OSS/Enterprise instance, issue an HTTP GET request to the `/api/playlists` endpoint. Use the following shell command: + + ```shell + mkdir playlists + curl "${GRAFANA_ONPREM_INSTANCE_URL}/api/playlists" \ + -H "Authorization: Bearer $GRAFANA_SOURCE_TOKEN" \ + | jq -M -r -c '.[] | .uid' \ + | while read -r uid; do \ + curl "${GRAFANA_ONPREM_INSTANCE_URL}/api/playlists/$uid" \ + -H "Authorization: Bearer $GRAFANA_SOURCE_TOKEN" \ + > playlists/$uid.json; \ + done + ``` + + The command provided above will carry out an HTTP request to this endpoint and accomplish several tasks: + + - It fetches an array of all the playlists available in the Grafana OSS/Enterprise instance. + - It then iterates through each playlist to obtain the complete set of details. + - Finally, it stores each playlist's specification as separate JSON files within a directory named `playlists` + +2. To import the playlists, execute the following command. This command constructs an HTTP POST request targeting the `/api/playlists` endpoint of your Grafana Cloud Instance. + + ```shell + for playlist in playlists/*; do + curl -XPOST "${GRAFANA_CLOUD_INSTANCE_URL}/api/playlists" \ + -H "Authorization: Bearer $GRAFANA_DEST_TOKEN" \ + -H "Content-Type: application/json" \ + -d $playlist > /dev/null; + done + ``` + +### Migrate single sign-on configuration + +Grafana Cloud stacks support all of the same authentication and authorization options as Grafana OSS/Enterprise, except for [anonymous authentication](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/anonymous-auth/) and use of the [Auth proxy](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/auth-proxy/). However, single sign-on settings cannot be exported and imported like dashboards, alerts, and other resources. + +To set up SAML authentication from scratch using Grafana’s UI or API, follow [these instructions](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/saml-ui/) to Configure SAML authentication in Grafana. + +LDAP and OIDC/OAuth2 can only be configured in Grafana Cloud by the Grafana Labs support team. Follow [these instructions](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/) to request SSO configuration from the support team. + +### Migrate custom Grafana configuration + +You may have customized the [configuration](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/) of your Grafana OSS/Enterprise instance, for example with feature toggles, custom auth, or embedding options. Since Grafana configuration is stored in environment variables or the filesystem where Grafana runs, Grafana Cloud users do not have access to it. However, you can open a support ticket to ask a Grafana Labs support engineer for customizations. + +The following customizations are available via support: + +- Enabling [feature toggles](http://www.grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/feature-toggles). +- [Single sign-on and team sync using SAML, LDAP, or OAuth](http://www.grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication). +- Enable [embedding Grafana dashboards in other applications](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#allow_embedding) for Grafana Cloud contracted customers. +- [Audit logging](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/audit-grafana/) ([Usage insights logs and dashboards](https://grafana.com/docs/grafana-cloud/account-management/usage-insights/) are available in Grafana Cloud Pro and Advanced by default). + +Note that the following custom configurations are not supported in Grafana Cloud: + +- [Anonymous user access](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/anonymous-auth/). +- [Auth proxy](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/auth-proxy/). +- [Third-party database encryption](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-database-encryption/) and the [Hashicorp Vault](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-database-encryption/encrypt-secrets-using-hashicorp-key-vault/) integration. +- Running self-signed plugins, like custom-built data sources or visualizations. For more information on plugin signing, refer to our [developer documentation](https://grafana.com/developers/plugin-tools/publish-a-plugin/sign-a-plugin). + +If you have a custom configuration in Grafana OSS/Enterprise that is not listed here, reach out to our support team to find out whether they can help you set it up. + +## Next steps + +After you have successfully migrated resources and configuration from Grafana OSS/Enterprise, consider the following steps to enhance your monitoring experience: + +- **Get started with Grafana Cloud**: learn more about the functionality available in Grafana Cloud, which is not available in the open source or Enterprise editions. Read more in [Get started with Grafana Cloud](https://grafana.com/docs/grafana-cloud/get-started/) +- **AWS PrivateLink for Grafana Cloud**: securely transmit telemetry data from your AWS Virtual Private Cloud (VPC) to Grafana Cloud, entirely within the AWS network. + Learn how to set this up with [AWS PrivateLink Integration](https://grafana.com/docs/grafana-cloud/send-data/aws-privatelink/). +- **Azure PrivateLink for Grafana Cloud**, securely transmit telemetry from your Azure Virtual Network to Grafana Cloud while staying on the Azure network, and avoid exposing your traffic to the public internet. + Learn how to set this up with [AWS PrivateLink Integration](https://grafana.com/docs/grafana-cloud/send-data/azure-privatelink/). +- **[Grafana Integrations](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/)**: ready-made integrations to make monitoring your infrastructure and applications more straightforward. diff --git a/docs/sources/administration/plugin-management/index.md b/docs/sources/administration/plugin-management/index.md index 09566ccf250..21e048ed9ea 100644 --- a/docs/sources/administration/plugin-management/index.md +++ b/docs/sources/administration/plugin-management/index.md @@ -66,7 +66,7 @@ To prevent users from seeing an app plugin, refer to [these permissions scenario ## Plugin catalog -The Grafana plugin catalog allows you to browse and manage plugins from within Grafana. Only Grafana server administrators and Organization administrators can access and use the plugin catalog. For more information about Grafana roles and permissions, refer to [Roles and permissions]({{< relref "../administration/roles-and-permissions" >}}). +The Grafana plugin catalog allows you to browse and manage plugins from within Grafana. Only Grafana server administrators and Organization administrators can access and use the plugin catalog. For more information about Grafana roles and permissions, refer to [Roles and permissions]({{< relref "../roles-and-permissions" >}}). The following access rules apply depending on the user role: @@ -236,6 +236,32 @@ WARN[06-01|16:45:59] Running an unsigned plugin pluginID= If you're developing a plugin, then you can enable development mode to allow all unsigned plugins. {{% /admonition %}} +## Integrate plugins + +You can configure your Grafana instance to let the frontends of installed plugins directly communicate locally with the backends of other installed plugins. By default, you can only communicate with plugin backends remotely. You can use this configuration to, for example, enable a [canvas panel](https://grafana.com/docs/grafana/latest/panels-visualizations/visualizations/canvas/) to call an application resource API that is permitted by the `actions_allow_post_url` option. + +To enable backend communication between plugins: + +1. Set the plugins you want to communicate with. In your configuration file (`grafana.ini` or `custom.ini` depending on your operating system) remove the semicolon to enable and then set the following configuration option: + + ``` + actions_allow_post_url= + ``` + + This is a comma-separated list that uses glob matching. + + - To allow access to all plugins that have a backend: + + ``` + actions_allow_post_url=/api/plugins/* + ``` + + - To access to the backend of only one plugin: + + ``` + actions_allow_post_url=/api/plugins/ + ``` + ## Plugin Frontend Sandbox {{% admonition type="caution" %}} diff --git a/docs/sources/administration/provisioning/index.md b/docs/sources/administration/provisioning/index.md index 5c164a708aa..b06979b4880 100644 --- a/docs/sources/administration/provisioning/index.md +++ b/docs/sources/administration/provisioning/index.md @@ -479,6 +479,7 @@ The following sections detail the supported settings and secure settings for eac | mentionGroups | | | mentionChannel | | | token | yes | +| color | | #### Alert notification `victorops` diff --git a/docs/sources/administration/roles-and-permissions/_index.md b/docs/sources/administration/roles-and-permissions/_index.md index 134482723b4..87457d1d0fa 100644 --- a/docs/sources/administration/roles-and-permissions/_index.md +++ b/docs/sources/administration/roles-and-permissions/_index.md @@ -121,29 +121,6 @@ For more information about assigning dashboard folder permissions, refer to [Gra For more information about assigning dashboard permissions, refer to [Grant dashboard permissions]({{< relref "../user-management/manage-dashboard-permissions/#grant-dashboard-permissions" >}}). -## Editors with administrator permissions - -If you have access to the Grafana server, you can modify the default editor role so that editors can use administrator permissions to manage dashboard folders, dashboards, and teams that they create. - -{{% admonition type="note" %}} -This permission does not allow editors to manage folders, dashboards, and teams that they do not create. -{{% /admonition %}} - -This setting can be used to enable self-organizing teams to administer their own dashboards. - -For more information about assigning administrator permissions to editors, refer to [Grant editors administrator permissions]({{< relref "../user-management/server-user-management/grant-editor-admin-permissions/" >}}). - -## Viewers with dashboard preview and Explore permissions - -If you have access to the Grafana server, you can modify the default viewer role so that viewers can: - -- Edit and preview dashboards, but cannot save their changes or create new dashboards. -- Access and use [Explore]({{< relref "../../explore" >}}). - -Extending the viewer role is useful for public Grafana installations where you want anonymous users to be able to edit panels and queries, but not be able to save or create new dashboards. - -For more information about assigning dashboard preview permissions to viewers, refer to [Enable viewers to preview dashboards and use Explore]({{< relref "../user-management/manage-dashboard-permissions/#enable-viewers-to-edit-but-not-save-dashboards-and-use-explore" >}}). - ## Teams and permissions A team is a group of users within an organization that have common dashboard and data source permission needs. For example, instead of assigning five users access to the same dashboard, you can create a team that consists of those users and assign dashboard permissions to the team. A user can belong to multiple teams. @@ -153,7 +130,7 @@ You can assign a team member one of the following permissions: - **Member**: Includes the user as a member of the team. Members do not have team administrator privileges. - **Admin**: Administrators have permission to manage various aspects of the team, including team membership, permissions, and settings. -Because teams exist inside an organization, the organization administrator can manage all teams. When the `editors_can_admin` setting is enabled, editors can create teams and manage teams that they create. For more information about the `editors_can_admin` setting, refer to [Grant editors administrator permissions]({{< relref "../user-management/server-user-management/grant-editor-admin-permissions/" >}}). +Because teams exist inside an organization, the organization administrator can manage all teams. For details on managing teams, see [Team management]({{< relref "../team-management/" >}}). diff --git a/docs/sources/administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/index.md b/docs/sources/administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/index.md index 09c2fc4c3d4..e04c2fcecf2 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/index.md @@ -369,9 +369,11 @@ Here are two ways to achieve this: # Update the role curl -H 'Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt' -H 'Content-Type: application/json' \ - -X PUT-d @/tmp/basic_viewer.json '/api/access-control/roles/basic_viewer' + -X PUT -d @/tmp/basic_viewer.json '/api/access-control/roles/basic_viewer' ``` + The token that is used in this request is the [service account token](ref:service-accounts). + - Or use the `role > from` list and `permission > state` option of your provisioning file: ```yaml @@ -394,6 +396,20 @@ Here are two ways to achieve this: state: 'present' ``` + If your goal is to remove an access to an app you should remove it from the role and update it. For example: + + ```bash + # Fetch the role, modify it to remove permissions to kentik-connect-app and increment role version + curl -H 'Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt' \ + -X GET '/api/access-control/roles/basic_viewer' | \ + jq 'del(.created)| del(.updated) | del(.permissions[].created) | del(.permissions[].updated) | .version += 1' | \ + jq 'del(.permissions[] | select (.action == "plugins.app:access" and .scope == "plugins:id:kentik-connect-app"))' + + # Update the role + curl -H 'Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt' -H 'Content-Type: application/json' \ + -X PUT -d @/tmp/basic_viewer.json '/api/access-control/roles/basic_viewer' + ``` + ### Manage user permissions through teams In the scenario where you want users to grant access by the team they belong to, we recommend to set users role to `No Basic Role` and let the team assignment assign the role instead. diff --git a/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md b/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md index f651becaa4e..2c328c749b8 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md @@ -58,8 +58,8 @@ The following tables list permissions associated with basic and fixed roles. | ------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | Grafana Admin | `basic_grafana_admin` | `fixed:roles:reader`
`fixed:roles:writer`
`fixed:users:reader`
`fixed:users:writer`
`fixed:org.users:reader`
`fixed:org.users:writer`
`fixed:ldap:reader`
`fixed:ldap:writer`
`fixed:stats:reader`
`fixed:settings:reader`
`fixed:settings:writer`
`fixed:provisioning:writer`
`fixed:organization:reader`
`fixed:organization:maintainer`
`fixed:licensing:reader`
`fixed:licensing:writer`
`fixed:datasources.caching:reader`
`fixed:datasources.caching:writer`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:plugins:maintainer`
`fixed:authentication.config:writer`
`fixed:library.panels:creator`
`fixed:library.panels:reader`
`fixed:library.panels:general.reader`
`fixed:library.panels:writer`
`fixed:library.panels:general.writer`
`fixed:groupsync:writer`
`fixed:migrationassistant:migrator` | Default [Grafana server administrator](/docs/grafana//administration/roles-and-permissions/#grafana-server-administrators) assignments. | | Admin | `basic_admin` | `fixed:reports:reader`
`fixed:reports:writer`
`fixed:datasources:reader`
`fixed:datasources:writer`
`fixed:organization:writer`
`fixed:datasources.permissions:reader`
`fixed:datasources.permissions:writer`
`fixed:teams:writer`
`fixed:dashboards:reader`
`fixed:dashboards:writer`
`fixed:dashboards.permissions:reader`
`fixed:dashboards.permissions:writer`
`fixed:dashboards.public:writer`
`fixed:folders:reader`
`fixed:folders:writer`
`fixed:folders.permissions:reader`
`fixed:folders.permissions:writer`
`fixed:alerting:writer`
`fixed:apikeys:reader`
`fixed:apikeys:writer`
`fixed:alerting.provisioning.secrets:reader`
`fixed:alerting.provisioning:writer`
`fixed:datasources.caching:reader`
`fixed:datasources.caching:writer`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:plugins:writer`
`fixed:library.panels:creator`
`fixed:library.panels:reader`
`fixed:library.panels:general.reader`
`fixed:library.panels:writer`
`fixed:library.panels:general.writer`
`fixed:alerting.provisioning.status:writer`
`fixed:groupsync:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. | -| Editor | `basic_editor` | `fixed:datasources:explorer`
`fixed:dashboards:creator`
`fixed:folders:creator`
`fixed:annotations:writer`
`fixed:teams:creator` if the `editors_can_admin` configuration flag is enabled
`fixed:alerting:writer`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:library.panels:creator`
`fixed:library.panels:general.reader`
`fixed:library.panels:general.writer`
`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. | -| Viewer | `basic_viewer` | `fixed:datasources.id:reader`
`fixed:organization:reader`
`fixed:annotations:reader`
`fixed:annotations.dashboard:writer`
`fixed:alerting:reader`
`fixed:plugins.app:reader`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:library.panels:general.reader`
`fixed:datasources:explorer` if the `viewers_can_edit` configuration flag is enabled | Default [Viewer](ref:rbac-basic-roles) assignments. | +| Editor | `basic_editor` | `fixed:datasources:explorer`
`fixed:dashboards:creator`
`fixed:folders:creator`
`fixed:annotations:writer`
`fixed:alerting:writer`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:library.panels:creator`
`fixed:library.panels:general.reader`
`fixed:library.panels:general.writer`
`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. | +| Viewer | `basic_viewer` | `fixed:datasources.id:reader`
`fixed:organization:reader`
`fixed:annotations:reader`
`fixed:annotations.dashboard:writer`
`fixed:alerting:reader`
`fixed:plugins.app:reader`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:library.panels:general.reader` | Default [Viewer](ref:rbac-basic-roles) assignments. | | No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) | ## Fixed role definitions diff --git a/docs/sources/administration/user-management/manage-dashboard-permissions/index.md b/docs/sources/administration/user-management/manage-dashboard-permissions/index.md index e87d0193fcc..b75f675cc5e 100644 --- a/docs/sources/administration/user-management/manage-dashboard-permissions/index.md +++ b/docs/sources/administration/user-management/manage-dashboard-permissions/index.md @@ -63,27 +63,7 @@ Grant dashboard permissions when you want to restrict or enhance dashboard acces 1. Select the user, service account, team, or role. 1. Select the permission and click **Save**. -## Enable viewers to edit (but not save) dashboards and use Explore - -By default, the viewer organization role does not allow viewers to create dashboards or use the Explore feature. However, by modifying a configuration setting, you can allow viewers to edit a panel and make changes to a dashboard but not save those changes. This setting also enables viewers to use the Explore feature. - -This modification is useful for public Grafana installations where you want anonymous users to be able to edit panels and queries but not save or create new dashboards. - -### Before you begin - -- Ensure that you have access to the Grafana server - -**To enable viewers to preview dashboards and use Explore**: - -1. Open the Grafana configuration file. - - For more information about the Grafana configuration file and its location, refer to [Configuration]({{< relref "../../../setup-grafana/configure-grafana/" >}}). - -1. Locate the `viewers_can_edit` parameter. -1. Set the `viewers_can_edit` value to `true`. -1. Save your changes and restart Grafana. - -## Edit dashboard permissions +# Edit dashboard permissions Edit dashboard permissions when you are want to enhance or restrict a user's access to a dashboard. For more information about dashboard permissions, refer to [Dashboard permissions]({{< relref "../../roles-and-permissions/#dashboard-permissions" >}}). diff --git a/docs/sources/administration/user-management/server-user-management/grant-editor-admin-permissions/index.md b/docs/sources/administration/user-management/server-user-management/grant-editor-admin-permissions/index.md deleted file mode 100644 index cfefd775975..00000000000 --- a/docs/sources/administration/user-management/server-user-management/grant-editor-admin-permissions/index.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -aliases: - - ../../manage-users-and-permissions/manage-server-users/grant-editor-admin-permissions/ -labels: - products: - - enterprise - - oss -title: Grant editors team creator permissions -weight: 60 ---- - -# Grant editors team creator permissions - -By default, the editor organization role does not allow editors to creator and manage teams. You can allow them to do so using the `editors_can_admin` configuration option. - -When `editors_can_admin` is enabled, users with the Editor role in an organization can create teams, and they are Administrators of the teams they create. To learn more about team permissions, refer to [Team management]({{< relref "../../../team-management/" >}}). - -## Before you begin - -- Ensure that you have access to the Grafana server - -**To enable editors with team creator permissions**: - -1. Log in to the Grafana server and open the Grafana configuration file. - - For more information about the Grafana configuration file and its location, refer to [Configuration]({{< relref "../../../../setup-grafana/configure-grafana/" >}}). - -1. Locate the `editors_can_admin` parameter. -1. Set the `editors_can_admin` value to `true`. -1. Save your changes and restart the Grafana server. diff --git a/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md b/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md index 2ad18a60ab6..41c9150b52c 100644 --- a/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md +++ b/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md @@ -148,8 +148,6 @@ You can toggle between the two options. Once you have created an alert rule, the Switching from advanced to default may result in queries and expressions that cannot be converted. In this case, a warning message asks if you want to continue to reset to default settings. -Default and advanced options are enabled by default for Grafana Cloud users and this feature is being rolled out progressively. OSS users can enable them via the [`alertingQueryAndExpressionsStepMode` feature toggle](/setup-grafana/configure-grafana/feature-toggles/). - {{< docs/shared lookup="alerts/configure-alert-rule-name.md" source="grafana" version="" >}} ## Define query and condition @@ -259,16 +257,11 @@ Complete the following steps to set up notifications. 1. You can also optionally select a mute timing as well as groupings and timings to define when not to send notifications. - {{< admonition type="note" >}} - An auto-generated notification policy is generated. Only admins can view these auto-generated policies from the **Notification policies** list view. Any changes have to be made in the alert rules form. {{< /admonition >}} - **Use notification policy** - 1. Choose this option to use the [notification policy tree](ref:notification-policies) to direct your notifications. + 1. Choose this option to use the [notification policy tree](ref:notification-policies) to handle alert notifications. - {{< admonition type="note" >}} - All alert rules and instances, irrespective of their labels, match the default notification policy. If there are no nested policies, or no nested policies match the labels in the alert rule or alert instance, then the default notification policy is the matching policy. - {{< /admonition >}} + All notifications for this alert rule are managed by the notification policy tree, which routes alerts based on their labels. If an alert does not match a specific policy, the default notification policy applies, ensuring all alerts are handled. 1. Preview your alert instance routing set up. diff --git a/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md b/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md index aeaddf1e405..746cb778052 100644 --- a/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md +++ b/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md @@ -44,6 +44,10 @@ Grafana allows you to link an alert rule to a dashboard panel. This can help you An alert rule is linked to a panel by setting the [`dashboardUId` and `panelId` annotations](ref:annotations). Both annotations must be set together. +{{% admonition type="tutorial" %}} +For a hands-on example of integrating alert rules with dashboards, check out [Part 5 of our Get Started with Grafana Alerting tutorial](http://www.grafana.com/tutorials/alerting-get-started-pt5/). +{{% /admonition %}} + ## Link alert rules to panels When configuring the alert rule, you can set the dashboard and panel annotations as shown in this [video](https://youtu.be/ClLp-iSoaSY?si=qKWnvSVaQuvYcuw9&t=170). diff --git a/docs/sources/alerting/configure-notifications/create-silence.md b/docs/sources/alerting/configure-notifications/create-silence.md index 6f781a60ce3..5cf6f4a9c04 100644 --- a/docs/sources/alerting/configure-notifications/create-silence.md +++ b/docs/sources/alerting/configure-notifications/create-silence.md @@ -61,7 +61,7 @@ refs: # Configure silences -Silences stop notifications from getting created and last for only a specified window of time. Use them to temporarily prevent alert notifications, such as during incident response or a maintenance window. +Silences stop notifications from being created for a specified time window but do not interrupt alert evaluation. Use them to temporarily prevent alert notifications, such as during incident response or a maintenance window. {{< admonition type="note" >}} Silences are assigned to a [specific Alertmanager](ref:alertmanager-architecture) and only suppress notifications for alerts managed by that Alertmanager. diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/_index.md b/docs/sources/alerting/configure-notifications/manage-contact-points/_index.md index ce35c4c75e8..215ed1926dc 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/_index.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/_index.md @@ -86,12 +86,7 @@ refs: destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/integrations/configure-mqtt/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/integrations/configure-mqtt/ - alertmanager-architecture: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/configure-notifications/#alertmanager-architecture - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/#alertmanager-architecture - external-alertmanager: + configure-alertmanager: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/set-up/configure-alertmanager/ - pattern: /docs/grafana-cloud/ @@ -101,13 +96,23 @@ refs: destination: /docs/grafana//alerting/configure-notifications/template-notifications/manage-notification-templates/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/manage-notification-templates/ + configure-grafana-alerts: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/create-grafana-managed-rule/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule/ + configure-contact-points: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/ --- # Configure contact points Use contact points to specify where to receive alert notifications. Contact points contain the configuration for sending alert notifications, including destinations like email, Slack, OnCall, webhooks, and their notification messages. -A contact point can have one or multiple destinations, known as [contact point integrations](#list-of-supported-integrations). Alert notifications are sent to each integration within the chosen contact point. +A contact point can have one or multiple destinations, known as [contact point integrations](#supported-contact-point-integrations). Alert notifications are sent to each integration within the chosen contact point. On the **Contact Points** tab, you can: @@ -119,9 +124,42 @@ On the **Contact Points** tab, you can: - Delete contact points. Note that you cannot delete contact points that are in use by a notification policy. To proceed, either delete the notification policy or update it to use another contact point. {{% admonition type="note" %}} -Contact points are assigned to a [specific Alertmanager](ref:alertmanager-architecture) and cannot be used by notification policies in other Alertmanagers. +Contact points are assigned to a [specific Alertmanager](ref:configure-alertmanager) and cannot be used by notification policies in other Alertmanagers. {{% /admonition %}} +## Supported contact point integrations + +Each contact point integration has its own configuration options and setup process. The following list shows the contact point integrations supported by Grafana. + +{{< column-list >}} + +- Alertmanager +- [AWS SNS](ref:sns) +- Cisco Webex Teams +- DingDing +- [Discord](ref:discord) +- [Email](ref:email) +- [Google Chat](ref:gchat) +- [Grafana OnCall](ref:oncall) +- Kafka REST Proxy +- Line +- [Microsoft Teams](ref:teams) +- [MQTT](ref:mqtt) +- [Opsgenie](ref:opsgenie) +- [PagerDuty](ref:pagerduty) +- Pushover +- Sensu Go +- [Slack](ref:slack) +- [Telegram](ref:telegram) +- Threema Gateway +- VictorOps +- [Webhook](ref:webhook) +- WeCom + +{{< /column-list >}} + +Some of the integrations above are not supported by Prometheus Alertmanager. For the list of supported integrations, refer to the [Prometheus Alertmanager receiver settings](https://prometheus.io/docs/alerting/latest/configuration/#receiver-integration-settings). + ## Add a contact point Complete the following steps to add a contact point. @@ -132,7 +170,7 @@ Complete the following steps to add a contact point. 1. On the **Contact Points** tab, click **+ Add contact point**. 1. Enter a descriptive name for the contact point. 1. From **Integration**, select a type and fill out mandatory fields. For example, if you choose email, enter the email addresses. Or if you choose Slack, enter the Slack channel and users who should be contacted. -1. Some contact point integrations, like email or Webhook, have optional settings. In **Optional settings**, specify additional settings for the selected contact point integration. +1. Some [contact point integrations](#supported-contact-point-integrations), like email or Webhook, have optional settings. In **Optional settings**, specify additional settings for the selected contact point integration. 1. In Notification settings, optionally select **Disable resolved message** if you do not want to be notified when an alert resolves. 1. Save your changes. @@ -148,6 +186,16 @@ To add another integration to a contact point, complete the following steps. - In **Optional settings**, specify additional settings for the selected contact point integration. 1. Save your changes. +## Customize notification messages + +In contact points, you can also customize notification messages. For example, when setting up an email contact point integration, click **Message** or **Subject** to modify it. + +By default, notification messages include common alert details, which are usually sufficient for most cases. + +If necessary, you can customize the content and format of notification messages. You can create a custom notification template, which can then be applied to one or more contact points. + +On the **Notification templates** tab, you can view, edit, copy or delete notification templates. Refer to [manage notification templates](ref:manage-notification-templates) for instructions on selecting or creating a template for a contact point. + ## Test a contact point Testing a contact point is only available for Grafana Alertmanager. Complete the following steps to test a contact point. @@ -159,45 +207,9 @@ Testing a contact point is only available for Grafana Alertmanager. Complete the 1. Choose whether to send a predefined test notification or choose custom to add your own custom annotations and labels to include in the notification. 1. Click **Send test notification** to fire the alert. -## Customize notification messages +## Enable notifications for a contact point -In contact points, you can also customize notification messages. For example, when setting up an email contact point integration, click **Message** or **Subject** to modify it. +After creating a contact point, you can enable it to receive alert notifications using one of the following methods: -By default, notification messages include common alert details, which are usually sufficient for most cases. - -If necessary, you can customize the content and format of notification messages. You can create a custom notification template, which can then be applied to one or more contact points. - -On the **Notification templates** tab, you can view, edit, copy or delete notification templates. Refer to [manage notification templates](ref:manage-notification-templates) for instructions on selecting or creating a template for a contact point. - -## List of supported integrations - -Each contact point integration has its own configuration options and setup process. In most cases, this involves providing an API key or a Webhook URL. - -The following table lists the contact point integrations supported by Grafana. - -| Name | Type | -| ---------------------------- | ------------------------- | -| Alertmanager | `prometheus-alertmanager` | -| [Amazon SNS](ref:sns) | `sns` | -| Cisco Webex Teams | `webex` | -| DingDing | `dingding` | -| [Discord](ref:discord) | `discord` | -| [Email](ref:email) | `email` | -| [Google Chat](ref:gchat) | `googlechat` | -| [Grafana Oncall](ref:oncall) | `oncall` | -| Kafka REST Proxy | `kafka` | -| Line | `line` | -| [Microsoft Teams](ref:teams) | `teams` | -| [MQTT](ref:mqtt) | `mqtt` | -| [Opsgenie](ref:opsgenie) | `opsgenie` | -| [Pagerduty](ref:pagerduty) | `pagerduty` | -| Pushover | `pushover` | -| Sensu Go | `sensugo` | -| [Slack](ref:slack) | `slack` | -| [Telegram](ref:telegram) | `telegram` | -| Threema Gateway | `threema` | -| VictorOps | `victorops` | -| [Webhook](ref:webhook) | `webhook` | -| WeCom | `wecom` | - -Some of these integrations are not compatible with [external Alertmanagers](ref:external-alertmanager). For the list of Prometheus Alertmanager integrations, refer to the [Prometheus Alertmanager receiver settings](https://prometheus.io/docs/alerting/latest/configuration/#receiver-integration-settings). +- **Assign it to alert rules** – Select the contact point in the [notifications options for Grafana-managed alert rules](ref:configure-grafana-alerts) to directly associate it with specific alerts. +- **Assign it to notification policies** – Add the contact point to one or more [notification policies](ref:configure-contact-points), which manage the alert notifications you want the contact point to receive. diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-amazon-sns.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-amazon-sns.md index 84274e64043..9c449640511 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-amazon-sns.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-amazon-sns.md @@ -14,11 +14,32 @@ labels: menuTitle: Amazon SNS title: Configure Amazon SNS for Alerting weight: 100 +refs: + notification-templates: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/ + configure-contact-points: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/ + test-contact-point: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/#test-a-contact-point + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/#test-a-contact-point + enable-contact-point-notifications: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/#enable-notifications-for-a-contact-point + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/#enable-notifications-for-a-contact-point --- -# Configure Amazon SNS for Alerting +# Configure Amazon SNS notifications -Use the Grafana Alerting - Amazon SNS integration to send notifications to Amazon SNS when your alerts are firing. You can receive notifications via the various subscriber channels supported by SNS. +Use the Amazon SNS integration in a contact point to send alert notifications to a SNS topic. Then, configure the SNS topic to forward notifications to distinct subscriber channels used in your SNS account. ## Before you begin @@ -27,71 +48,51 @@ Before you begin, ensure you have the following: - **AWS SNS Topic**: An SNS topic to send notifications to. - **AWS IAM Identity with necessary access**: An IAM identity (e.g. user, role) with the necessary permissions to publish messages to the SNS topic. -For an example setup, see [Example Minimal Setup Using Assumed IAM Role]({{< relref "#example-minimal-setup-using-assumed-iam-role" >}}). +For a minimal setup, refer to [Example using an Access Key](#example-using-an-access-key). -## Adding the SNS Contact Point in Grafana +## Configure Amazon SNS for a contact point -With AWS resources configured, proceed to add SNS as a contact point in Grafana. +To create a contact point with a SNS integration, complete the following steps. -- Navigate to **Alerts & IRM** -> **Alerting** -> **Contact points**. -- Click on **"Add contact point"**. -- **Name**: Enter a descriptive name (e.g., `AWS SNS`). -- Choose **"AWS SNS"** from the list of contact point types. +1. Navigate to **Alerts & IRM** -> **Alerting** -> **Contact points**. +1. Click **+ Add contact point**. +1. Enter a name for the contact point. +1. From the **Integration** list, select **AWS SNS**. +1. Set up the required [settings](#sns-settings) for your SNS configuration. +1. Click **Save contact point**. -### 2. Configure SNS Settings +For more details on contact points, including how to test them and enable notifications, refer to [Configure contact points](ref:configure-contact-points). -#### SNS Settings +## SNS Settings - **The Amazon SNS API URL**: (Optional) The SNS API URL, e.g., `https://sns.us-east-2.amazonaws.com`. If not specified, the SNS API URL from the SNS SDK will be used. - **Signature Version (sigv4)**: Configures AWS's Signature Verification 4 signing process to sign requests. - **Region**: (Optional) The AWS region. If blank, the region from the default credentials chain is used. - - **Access Key**: The AWS API access key. - - **Secret Key**: The AWS API secret key. + - **Access Key** : (Optional) The AWS API access key. + - **Secret Key**: (Optional) The AWS API secret key. + > Both `Access Key` and `Secret Key` must be provided together or left blank together. + > + > If left blank, Grafana searches for credentials using the default credentials chain, including environment variables (`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`), the shared credential file, and EC2 instance roles. - **Profile**: (Optional) Named AWS profile used to authenticate. - **Role ARN**: (Optional) The ARN of an AWS IAM role to assume for authentication, serving as an alternative to using AWS API keys. - **SNS topic ARN**: (Optional) If you don't specify this value, you must specify a value for the `Phone number` or `Target ARN`. If you are using a FIFO SNS topic you should set a message group interval longer than 5 minutes to prevent messages with the same group key being deduplicated by the SNS default deduplication window. - **Phone number**: (Optional) Phone number if message is delivered via SMS in E.164 format. If you don't specify this value, you must specify a value for the `SNS topic ARN` or `Target ARN`. - **Target ARN**: (Optional) The mobile platform endpoint ARN if message is delivered via mobile notifications. If you don't specify this value, you must specify a value for the `SNS topic ARN` or `Phone number`. -- **Subject**: (Optional) Customize the subject line or use the default template. This field is templateable. -- **Message**: (Optional) Customize the message content or use the default template. This field is templateable. +- **Subject**: (Optional) Customize the subject. This field supports [notification templates](ref:notification-templates) and, by default, uses the default title template (`default.title`). It **cannot be an empty string**. +- **Message**: (Optional) Customize the message. This field supports [notification templates](ref:notification-templates) and, by default, uses the default message template (`default.message`). - **Attributes**: (Optional) Add any SNS message attributes. -{{< admonition type="note" >}} -Both `Access Key` and `Secret Key` must be provided together or left blank together. If blank it defaults to a chain of credential -providers to search for credentials in environment variables, shared credential file, and EC2 Instance Roles. +## Example using an Access Key -Environment variables: `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. -{{< /admonition >}} +This section outlines a minimal setup to configure Amazon SNS with Alerting. -### 3. Test & Save the Contact Point - -- Click **"Test"** to verify that the SNS configuration is working correctly. -- After the test is successful, click **"Save contact point"** to add the SNS contact point. - -### 4. Next steps - -The SNS contact point is ready to receive alert notifications. - -To add this contact point to your alert, complete the following steps. - -1. In Grafana, navigate to **Alerting** > **Alert rules**. -1. Edit or create a new alert rule. -1. Scroll down to the **Configure labels and notifications** section. -1. Under Notifications click **Select contact point**. -1. From the drop-down menu, select the previously created contact point. -1. **Click Save rule and exit**. - -## Example Minimal Setup Using Assumed IAM Role - -This section outlines a minimal setup to configure SNS with Grafana using an assumed IAM Role. - -### 1. Create an SNS Topic +### 1. Create an SNS Topic and Email Subscriber 1. **Navigate to SNS in AWS Console**: - Go to the [Amazon SNS Console](https://console.aws.amazon.com/sns/v3/home). -2. **Create a new topic** [[AWS Documentation](https://docs.aws.amazon.com/sns/latest/dg/sns-create-topic.html)]: +2. **Create a new topic**: - On the **Topics** page, choose **"Create topic"**. - Select **"Standard"** as the type. @@ -99,105 +100,70 @@ This section outlines a minimal setup to configure SNS with Grafana using an ass - **Encryption**: Leave disabled for this minimal setup. - Click **"Create topic"**. -3. (Optional) **Add an email subscriber to help test** [[AWS Documentation](https://docs.aws.amazon.com/sns/latest/dg/sns-email-notifications.html)]: +3. (Optional) **Add an email subscriber to help test**: - Within your newly created topic, click on **"Create subscription"**. - **Protocol**: Choose `Email`. - **Endpoint**: Enter your email address to receive test notifications. - Click **"Create subscription"**. - **Confirm Subscription**: Check your email and confirm the subscription by clicking the provided link. -### 2. Create an IAM Role +### 2. Create an IAM Policy, User, and Access Key 1. **Navigate to IAM in AWS Console**: - Go to the [IAM Console](https://console.aws.amazon.com/iam/home). -2. **Create a new role** [[AWS Documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user.html)]: +2. **Create a new policy**: - - On the **Roles** page, choose **"Create role"**. - - **Trusted Entity**: Select **"This account"**. - - Click **"Next"** until the end, name it (e.g., `GrafanaSNSRole`), and click **"Create role"**. - -3. **Attach Inline Policy**: - - - After creating the role, select it and navigate to the **"Permissions"** tab. - - Click on **"Add permission"** > **"Create inline policy"**. + - On the **Policies** page, choose **"Create policy"**. - Switch to the **"JSON"** tab and paste the following policy, replacing `Resource` with your SNS topic ARN: - ```json - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": ["sns:Publish", "sns:GetTopicAttributes"], - "Resource": "arn:aws:sns:::" - } - ] - } - ``` + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": ["sns:Publish", "sns:GetTopicAttributes"], + "Resource": "arn:aws:sns:::" + } + ] + } + ``` - Click **"Next"**, name it (e.g., `SNSPublishPolicy`), and click **"Create policy"**. -### 3. Create an IAM Policy - -1. **Create a new policy to allow assuming the above IAM role** [[AWS Documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create-console.html)]: - - - In the IAM Console, on the **Policies** page, choose **"Create policy"**. - - Switch to the **"JSON"** tab and paste the following policy, replacing `Resource` with the ARN of the role you created earlier: - - ```json - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "sts:AssumeRole", - "Resource": "arn:aws:iam:::role/GrafanaSNSRole" - } - ] - } - ``` - -2. **Review and Create**: - - Click **"Next"**, name it (e.g., `AssumeSNSRolePolicy`), and click **"Create policy"**. - -### 4. Create an IAM User - -1. **Create a new IAM user to assume the above role** [[AWS Documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html)]: +3. **Create a new IAM user and assign the policy** - In the IAM Console, on the **Users** page, choose **"Create user"**. - - Enter a **User name**, e.g., `grafana-sns-user`. + - Enter a **User name**, e.g., `alerting-sns-user`. - Click **"Next"**. - - Select **"Attach policies directly"**. - - Search for the policy you created earlier (`AssumeSNSRolePolicy`) and select it. + - In **Set permissions**, select **"Attach policies directly"**. + - Search for the policy you created earlier (`SNSPublishPolicy`) and select it. - Click **"Next"** , and click **"Create user"**. -2. **Generate credentials**: +4. **Create an Access Key**: - Within your newly created user, click on **"Create access key"**. - Select an appropriate use-case, e.g., `Application running outside AWS`. - Click **"Next"** , and click **"Create access key"**. - - **Save Credentials**: Note the **Access key ID** and **Secret access key**. You'll need these for Grafana's configuration. + - **Save Credentials**: Note the **Access key ID** and **Secret access key** that are required in the next step. -### 5. Add the SNS Contact Point in Grafana +### 3. Configure the SNS Contact Point in Grafana -After creating the IAM user and obtaining the necessary credentials, proceed to [configure the SNS contact point in Grafana]({{< relref "#adding-the-sns-contact-point-in-grafana" >}}) using the following details: +Follow the steps in [configure Amazon SNS for a contact point](#configure-amazon-sns-for-a-contact-point), using the settings below and replacing the placeholders with the SNS and IAM values created in the previous steps. -- **The Amazon SNS API URL**: `https://sns.us-east-1.amazonaws.com` +- **The Amazon SNS API URL**: `https://sns..amazonaws.com` - **Signature Version (sigv4)**: - - **Region**: `us-east-1` + - **Region**: `` - **Access Key**: ``. - **Secret Key**: `` - - **Role ARN**: `arn:aws:iam:::role/GrafanaSNSRole` -- **SNS topic ARN**: `arn:aws:sns:::My-Topic` +- **SNS topic ARN**: `arn:aws:sns:::` -{{< admonition type="note" >}} -Replace the placeholder values (`https://sns.us-east-1.amazonaws.com`, `us-east-1`, ``, ``, `arn:aws:iam:::role/GrafanaSNSRole`, `arn:aws:sns:::My-Topic`) with your actual AWS credentials and ARNs. -{{< /admonition >}} +[Test the contact point](ref:test-contact-point) to ensure it's working, or [enable notifications](ref:enable-contact-point-notifications) for it. ## Additional Resources +- [Configure contact points](ref:configure-contact-points) - [Amazon SNS Documentation](https://docs.aws.amazon.com/sns/index.html) -- [AWS IAM Documentation](https://docs.aws.amazon.com/iam/index.html) -- [Prometheus Alertmanager SNS Integration](https://prometheus.io/docs/alerting/configuration/#sns_config) -- [Cloudwatch AWS Authentication]({{< relref "../../../../datasources/aws-cloudwatch/aws-authentication" >}}) +- [Amazon IAM Documentation](https://docs.aws.amazon.com/iam/index.html) +- [Prometheus Alertmanager SNS Configuration](https://prometheus.io/docs/alerting/configuration/#sns_config) diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier.md index 6562b948095..e7741ac3b56 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier.md @@ -28,13 +28,83 @@ refs: destination: /docs/grafana//alerting/configure-notifications/template-notifications/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/ + configure-contact-points: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/ --- -# Configure the webhook notifier for Alerting +# Configure webhook notifications -The webhook notification is a simple way to send information about a state change over HTTP to a custom endpoint. Using this notification you could integrate Grafana into a system of your choosing. +Use the webhook integration in contact points to send alert notifications to your webhook. -## Webhook JSON payload +The webhook integration is a flexible way to integrate alerts into your system. When a notification is triggered, it sends a JSON request with alert details and additional data to the webhook endpoint. + +## Configure webhook for a contact point + +To create a contact point with webhook integration, complete the following steps. + +1. Navigate to **Alerts & IRM** -> **Alerting** -> **Contact points**. +1. Click **+ Add contact point**. +1. Enter a name for the contact point. +1. From the **Integration** list, select **Webhook**. +1. In the **URL** field, copy in your Webhook URL. +1. (Optional) Configure [additional settings](#webhook-settings). +1. Click **Save contact point**. + +For more details on contact points, including how to test them and enable notifications, refer to [Configure contact points](ref:configure-contact-points). + +## Webhook settings + +| Option | Description | +| ------ | ---------------- | +| URL | The Webhook URL. | + +#### Optional settings + +| Option | Description | +| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| HTTP Method | Specifies the HTTP method to use: `POST` or `PUT`. | +| Basic Authentication Username | Username for HTTP Basic Authentication. | +| Basic Authentication Password | Password for HTTP Basic Authentication. | +| Authentication Header Scheme | Scheme for the `Authorization` Request Header. Default is `Bearer`. | +| Authentication Header Credentials | Credentials for the `Authorization` Request header. | +| Max Alerts | Maximum number of alerts to include in a notification. Any alerts exceeding this limit are ignored. `0` means no limit. | +| TLS | TLS configuration options, including CA certificate, client certificate, and client key. | + +{{< admonition type="note" >}} + +You can configure either HTTP Basic Authentication or the Authorization request header, but not both. + +{{< /admonition >}} + +#### Optional settings using templates + +Use the following settings to include custom data within the [JSON payload](#body). Both options support using [notification templates](ref:notification-templates). + +| Option | Description | +| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| Title | Sends the value as a string in the `title` field of the [JSON payload](#body). Supports [notification templates](ref:notification-templates). | +| Message | Sends the value as a string in the `message` field of the [JSON payload](#body). Supports [notification templates](ref:notification-templates). | + +{{< admonition type="note" >}} +You can customize the `title` and `message` options to include custom messages and notification data using notification templates. These fields are always sent as strings in the JSON payload. + +However, you cannot customize the webhook data structure, such as adding or changing other JSON fields and HTTP headers, or sending data in a different format like XML. + +If you need to format these fields as JSON or modify other webhook request options, consider sending webhook notifications to a proxy server that adjusts the webhook request before forwarding it to the final destination. +{{< /admonition >}} + +#### Optional notification settings + +| Option | Description | +| ------------------------ | ------------------------------------------------------------------- | +| Disable resolved message | Enable this option to prevent notifications when an alert resolves. | + +## JSON payload + +The following example shows the payload of a webhook notification containing information about two firing alerts: ```json { @@ -106,76 +176,47 @@ The webhook notification is a simple way to send information about a state chang } ``` -## Webhook fields - ### Body -| Key | Type | Description | -| ----------------- | ------------------------- | ------------------------------------------------------------------------------- | -| receiver | string | Name of the webhook | -| status | string | Current status of the alert, `firing` or `resolved` | -| orgId | number | ID of the organization related to the payload | -| alerts | array of [alerts](#alert) | Alerts that are triggering | -| groupLabels | object | Labels that are used for grouping, map of string keys to string values | -| commonLabels | object | Labels that all alarms have in common, map of string keys to string values | -| commonAnnotations | object | Annotations that all alarms have in common, map of string keys to string values | -| externalURL | string | External URL to the Grafana instance sending this webhook | -| version | string | Version of the payload | -| groupKey | string | Key that is used for grouping | -| truncatedAlerts | number | Number of alerts that were truncated | -| title | string | Custom title | -| state | string | State of the alert group (either `alerting` or `ok`) | -| message | string | Custom message | +The JSON payload of webhook notifications includes the following key-value pairs: + +| Key | Type | Description | +| ------------------- | ------------------------- | -------------------------------------------------------------------------------- | +| `receiver` | string | Name of the contact point. | +| `status` | string | Current status of the alert, `firing` or `resolved`. | +| `orgId` | number | ID of the organization related to the payload. | +| `alerts` | array of [alerts](#alert) | Alerts that are triggering. | +| `groupLabels` | object | Labels that are used for grouping, map of string keys to string values. | +| `commonLabels` | object | Labels that all alarms have in common, map of string keys to string values. | +| `commonAnnotations` | object | Annotations that all alarms have in common, map of string keys to string values. | +| `externalURL` | string | External URL to the Grafana instance sending this webhook. | +| `version` | string | Version of the payload structure. | +| `groupKey` | string | Key that is used for grouping. | +| `truncatedAlerts` | number | Number of alerts that were truncated. | +| `state` | string | State of the alert group (either `alerting` or `ok`). | + +The following key-value pairs are also included in the JSON payload and can be configured in the [webhook settings using notification templates](#optional-settings-using-templates). + +| Key | Type | Description | +| --------- | ------ | -------------------------------------------------------------------------------------------------------------------- | +| `title` | string | Custom title. Configurable in [webhook settings using notification templates](#optional-settings-using-templates). | +| `message` | string | Custom message. Configurable in [webhook settings using notification templates](#optional-settings-using-templates). | ### Alert -| Key | Type | Description | -| ------------ | ------ | ---------------------------------------------------------------------------------- | -| status | string | Current status of the alert, `firing` or `resolved` | -| labels | object | Labels that are part of this alert, map of string keys to string values | -| annotations | object | Annotations that are part of this alert, map of string keys to string values | -| startsAt | string | Start time of the alert | -| endsAt | string | End time of the alert, default value when not resolved is `0001-01-01T00:00:00Z` | -| values | object | Values that triggered the current status | -| generatorURL | string | URL of the alert rule in the Grafana UI | -| fingerprint | string | The labels fingerprint, alarms with the same labels will have the same fingerprint | -| silenceURL | string | URL to silence the alert rule in the Grafana UI | -| dashboardURL | string | A link to the Grafana Dashboard if the alert has a Dashboard UID annotation | -| panelURL | string | A link to the panel if the alert has a Panel ID annotation | -| imageURL | string | URL of a screenshot of a panel assigned to the rule that created this notification | +The Alert object represents an alert included in the notification group, as provided by the [`alerts` field](#body). -{{< admonition type="note" >}} - -You can customize the `title` and `message` fields using [notification templates](ref:notification-templates). - -However, you cannot customize webhook data structure or format, including JSON fields or sending data in XML, nor can you change the webhook HTTP headers. - -{{< /admonition >}} - -## Procedure - -To create your Webhook integration in Grafana Alerting, complete the following steps. - -1. Navigate to **Alerts & IRM** -> **Alerting** -> **Contact points**. -1. Click **+ Add contact point**. -1. Enter a contact point name. -1. From the Integration list, select **Webhook**. -1. In the **URL** field, copy in your Webhook URL. -1. Click **Test** to check that your integration works. - - ** For Grafana Alertmanager only.** - -1. Click **Save contact point**. - -## Next steps - -The Webhook contact point is ready to receive alert notifications. - -To add this contact point to your alert, complete the following steps. - -1. In Grafana, navigate to **Alerting** > **Alert rules**. -1. Edit or create a new alert rule. -1. Scroll down to the **Configure labels and notifications** section. -1. Under Notifications, click **Select contact point**. -1. From the drop-down menu, select the previously created contact point. -1. **Click Save rule and exit**. +| Key | Type | Description | +| -------------- | ------ | ----------------------------------------------------------------------------------- | +| `status` | string | Current status of the alert, `firing` or `resolved`. | +| `labels` | object | Labels that are part of this alert, map of string keys to string values. | +| `annotations` | object | Annotations that are part of this alert, map of string keys to string values. | +| `startsAt` | string | Start time of the alert. | +| `endsAt` | string | End time of the alert, default value when not resolved is `0001-01-01T00:00:00Z`. | +| `values` | object | Values that triggered the current status. | +| `generatorURL` | string | URL of the alert rule in the Grafana UI. | +| `fingerprint` | string | The labels fingerprint, alarms with the same labels will have the same fingerprint. | +| `silenceURL` | string | URL to silence the alert rule in the Grafana UI. | +| `dashboardURL` | string | A link to the Grafana Dashboard if the alert has a Dashboard UID annotation. | +| `panelURL` | string | A link to the panel if the alert has a Panel ID annotation. | +| `imageURL` | string | URL of a screenshot of a panel assigned to the rule that created this notification. | diff --git a/docs/sources/alerting/configure-notifications/mute-timings.md b/docs/sources/alerting/configure-notifications/mute-timings.md index 1ef726267e3..79be30654e0 100644 --- a/docs/sources/alerting/configure-notifications/mute-timings.md +++ b/docs/sources/alerting/configure-notifications/mute-timings.md @@ -39,7 +39,9 @@ refs: # Configure mute timings -A mute timing is a recurring interval of time when no new notifications for a policy are generated or sent. Use them to prevent alerts from firing a specific and reoccurring period, for example, a regular maintenance period or weekends. +A mute timing is a recurring interval that stops notifications for one or multiple notification policies during a specified period. It suppresses notifications but does not interrupt alert evaluation. + +Use mute timings to temporarily pause notifications for a specific recurring period, such as a regular maintenance window or weekends. {{< admonition type="note" >}} Mute timings are assigned to a [specific Alertmanager](ref:alertmanager-architecture) and only suppress notifications for alerts managed by that Alertmanager. diff --git a/docs/sources/alerting/fundamentals/_index.md b/docs/sources/alerting/fundamentals/_index.md index 934f019cfd3..147a5e36d2d 100644 --- a/docs/sources/alerting/fundamentals/_index.md +++ b/docs/sources/alerting/fundamentals/_index.md @@ -132,7 +132,7 @@ Each notification policy decides where to send the alert (contact point) and whe ### Silences and mute timings -[Silences](ref:silences) and [mute timings](ref:mute-timings) allow you to pause notifications for specific alerts or even entire notification policies. Use a silence to pause notifications on an ad-hoc basis, such as during a maintenance window; and use mute timings to pause notifications at regular intervals, such as evenings and weekends. +[Silences](ref:silences) and [mute timings](ref:mute-timings) allow you to pause notifications without interrupting alert rule evaluation. Use a silence to pause notifications on a one-time basis, such as during a maintenance window; and use mute timings to pause notifications at regular intervals, such as evenings and weekends. ### Architecture diff --git a/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md b/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md index cb4f932a5f6..bcfc5b27769 100644 --- a/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md +++ b/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md @@ -87,7 +87,7 @@ In [Configure no data and error handling](ref:no-data-and-error-handling), you c Note that `No Data` and `Error` states are supported only for Grafana-managed alert rules. -{{< figure src="/media/docs/alerting/alert-rule-configure-no-data-and-error.png" alt="A screenshot of the `Configure no data and error handling` option in Grafana Alerting." max-width="500px" >}} +{{< figure src="/media/docs/alerting/alert-rule-configure-no-data-and-error-v2.png" alt="A screenshot of the `Configure no data and error handling` option in Grafana Alerting." max-width="500px" >}} {{< docs/shared lookup="alerts/table-configure-no-data-and-error.md" source="grafana" version="" >}} diff --git a/docs/sources/alerting/fundamentals/alert-rules/_index.md b/docs/sources/alerting/fundamentals/alert-rules/_index.md index 47e410b8564..439c56b130c 100644 --- a/docs/sources/alerting/fundamentals/alert-rules/_index.md +++ b/docs/sources/alerting/fundamentals/alert-rules/_index.md @@ -25,9 +25,17 @@ refs: destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/prometheus/configure-prometheus-data-source/#alerting queries-and-conditions: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#data-source-queries - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#data-source-queries + alert-condition: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#alert-condition + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#alert-condition + recorded-queries: + - pattern: /docs/ + destination: /docs/grafana//administration/recorded-queries/ notification-images: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/configure-notifications/template-notifications/images-in-notifications/ @@ -45,14 +53,9 @@ refs: destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/ expression-queries: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#expression-queries + destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#advanced-options-expressions - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#expression-queries - alert-condition: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#alert-condition - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#alert-condition + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#advanced-options-expressions alert-rule-evaluation: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/fundamentals/alert-rules/rule-evaluation/ @@ -64,8 +67,8 @@ refs: An alert rule is a set of evaluation criteria for when an alert rule should fire. An alert rule consists of: -- Queries and expressions that select the data set to evaluate. -- A condition (the threshold) that the query must meet or exceed to trigger the alert instance. +- [Queries](ref:queries-and-conditions) that select the dataset to evaluate. +- An [alert condition](ref:alert-condition) (the threshold) that the query must meet or exceed to trigger the alert instance. - An interval that specifies the frequency of [alert rule evaluation](ref:alert-rule-evaluation) and a duration indicating how long the condition must be met to trigger the alert instance. - Other customizable options, for example, setting what should happen in the absence of data, notification messages, and more. diff --git a/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md b/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md index 229816db306..175306e8ef1 100644 --- a/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md +++ b/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md @@ -17,21 +17,16 @@ labels: title: Queries and conditions weight: 104 refs: - data-sources: - - pattern: /docs/grafana/ - destination: /docs/grafana//datasources/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/ data-source-alerting: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/fundamentals/alert-rules/#supported-data-sources - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/#supported-data-sources - alert-rule-evaluation: + state-and-health: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/ + destination: /docs/grafana//alerting/fundamentals/state-and-health/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/state-and-health/ query-transform-data: - pattern: /docs/grafana/ destination: /docs/grafana//panels-visualizations/query-transform-data/ @@ -41,71 +36,122 @@ refs: # Queries and conditions -In Grafana, queries fetch and transform data from [data sources,](ref:data-sources) which include databases like MySQL or PostgreSQL, time series databases like Prometheus or InfluxDB, and services like Amazon CloudWatch or Azure Monitor. +In Grafana, queries fetch and transform data from data sources, which include databases like MySQL or PostgreSQL, time series databases like Prometheus or InfluxDB, and services like Amazon CloudWatch or Azure Monitor. -A query specifies the data to extract from a data source, with the syntax varying based on the type of data source used. +An alert rule defines the following components: -In Alerting, an alert rule defines of one or more queries and expressions that select the data you want to measure and a [condition](#alert-condition) that needs to be met before an alert rule fires. +- A [query](#data-source-queries) that specifies the data to retrieve from a data source, with the syntax depending on the type of data source used. +- A [condition](#alert-condition) that must be met before the alert rule fires. +- Optional [expressions](#advanced-options-expressions) to perform transformations on the retrieved data. + +Alerting periodically runs the queries and expressions, evaluating the condition. If the condition is breached, an alert instance is triggered for each time series. ## Data source queries -Alerting queries are the same type of queries available in Grafana panels. Queries in Grafana can be applied in various ways, depending on the data source and query language being used. However, not all [data sources support Alerting](ref:data-source-alerting). +Alerting queries are the same as the queries used in Grafana panels, but Grafana-managed alerts are limited to querying [data sources that have Alerting enabled](ref:data-source-alerting). -Each data source’s query editor provides a customized user interface to help you write queries that take advantage of its unique capabilities. For additional information about queries in Grafana, refer to [Query and transform data](ref:query-transform-data). +Queries in Grafana can be applied in various ways, depending on the data source and query language being used. Each data source’s query editor provides a customized user interface to help you write queries that take advantage of its unique capabilities. -Some common types of query components include: +For more details about queries in Grafana, refer to [Query and transform data](ref:query-transform-data). -**Metrics or data fields**: Specify the specific metrics or data fields you want to retrieve, such as CPU usage, network traffic, or sensor readings. +{{< figure src="/media/docs/alerting/alerting-query-conditions-default-options.png" max-width="750px" caption="Define alert query and alert condition" >}} -**Time range**: Define the time range for which you want to fetch data, such as the last hour, a specific day, or a custom time range. +## Alert condition -**Filters**: Apply filters to narrow down the data based on specific criteria, such as filtering data by a specific tag, host, or application. +The alert condition is the query or expression that determines whether the alert fires or not depending whether the value satisfies the specified comparison. There can be only one condition which determines the triggering of the alert. -**Aggregations**: Perform aggregations on the data to calculate metrics like averages, sums, or counts over a given time period. +If the queried data meets the defined condition, Grafana fires the alert. -**Grouping**: Group the data by specific dimensions or tags to create aggregated views or breakdowns. +When using **Default options**, the `When` input [reduces the query data](#reduce), and the last input defines the threshold condition. -{{% admonition type="note" %}} -Grafana doesn't support alert queries with template variables. More details [here](https://community.grafana.com/t/template-variables-are-not-supported-in-alert-queries-while-setting-up-alert/2514). -{{% /admonition %}} +When using **Advanced options**, you have to choose one of your queries or expressions as the alert condition. -## Expression queries +## Advanced options: Expressions -In Grafana, an expression is used to perform calculations, transformations, or aggregations on the data source queried data. It allows you to create custom metrics or modify existing metrics based on mathematical operations, functions, or logical expressions. +Expressions are only available for Grafana-managed alerts and when the **Advanced options** are enabled. -By leveraging expression queries, users can perform tasks such as calculating the percentage change between two values, applying functions like logarithmic or trigonometric functions, aggregating data over specific time ranges or dimensions, and implementing conditional logic to handle different scenarios. +In Grafana, expressions allow you to perform calculations, transformations, or aggregations on queried data. They modify existing metrics through mathematical operations, functions, or logical expressions. -In Alerting, you can only use expressions for Grafana-managed alert rules. For each expression, you can choose from the math, reduce, and resample expressions. These are called multi-dimensional rules, because they generate an alert instance for each series. +With expression queries, you can perform tasks such as calculating the percentage change between two values, applying functions like logarithmic or trigonometric functions, aggregating data over specific time ranges or dimensions, and implementing conditional logic to handle different scenarios. -**Reduce** +{{< figure src="/media/docs/alerting/alert-rule-expressions.png" max-width="750px" caption="Alert rule expressions" >}} -Aggregates time series values in the selected time range into a single value. It's not necessary for [rules using numeric data](#alert-on-numeric-data). +The following expressions are available: -**Math** +### Reduce -Performs free-form math functions/operations on time series and number data. Can be used to preprocess time series data or to define an alert condition for number data. For example: +Aggregates time series values within the selected time range into a single number. + +Reduce takes one or more time series and transform each series into a single number, which can then be compared in the alert condition. + +The following aggregations functions are included: `Min`, `Max`, `Mean`, `Mediam`, `Sum`, `Count`, and `Last`. + +### Math + +Performs free-form math functions/operations on time series data and numbers. For instance, `$A + 1` or `$A * 100`. + +You can also use a Math expression to define the alert condition for numbers. For example: - `$B > 70` should fire if the value of B (query or expression) is more than 70. - `$B < $C * 100` should fire if the value of B is less than the value of C multiplied by 100. If queries being compared have multiple series in their results, series from different queries are matched if they have the same labels or one is a subset of the other. -**Resample** +### Resample Realigns a time range to a new set of timestamps, this is useful when comparing time series data from different data sources where the timestamps would otherwise not align. -**Threshold** +### Threshold -Checks if any time series data matches the threshold condition. +Compares single numbers from previous queries or expressions (e.g., `$A`, `$B`) to a specified condition. It's often used to define the alert condition. -The threshold expression allows you to compare two single values. It returns `0` when the condition is false and `1` if the condition is true. The following threshold functions are available: +The threshold expression allows the comparison between two single values. Available threshold functions are: -- Is above (x > y) -- Is below (x < y) -- Is within range (x > y1 AND x < y2) -- Is outside range (x < y1 AND x > y2) +- **Is above**: `$A > 5` +- **Is below**: `$B < 3` +- **Is equal to**: `$A == 2` +- **Is not equal to**: `$B =! 4` +- **Is above or equal to**: `$A >= 8` +- **Is below or equal to**: `$B <= 16` +- **Is within range**: `$A > 0 AND $A < 10` +- **Is outside range**: `$B < 0 OR $B > 100` +- **Is within range included**: `$A >= 0 AND $A <= 10` +- **Is outside range included**: `$B <= 0 OR $B >= 100` -**Classic condition (legacy)** +A threshold returns `0` when the condition is false and `1` when true. + +If the threshold is set as the alert condition, the alert fires when the threshold returns `1`. + +#### Recovery threshold + +To reduce the noise from flapping alerts, you can set a recovery threshold different to the alert threshold. + +Flapping alerts occur when a metric hovers around the alert threshold condition and may lead to frequent state changes, resulting in too many notifications. + +The value of a flapping metric can continually go above and below a threshold, resulting in a series of firing-resolved-firing notifications and a noisy alert state history. + +For example, if you have an alert for latency with a threshold of 1000ms and the number fluctuates around 1000 (say 980 -> 1010 -> 990 -> 1020, and so on), then each of those might trigger a notification: + +- 980 -> 1010 triggers a firing alert. +- 1010 -> 990 triggers a resolving alert. +- 990 -> 1020 triggers a firing alert again. + +To prevent this, you can set a recovery threshold to define two thresholds instead of one: + +1. An alert is triggered when the first threshold is crossed. +1. An alert is resolved only when the second (recovery) threshold is crossed. + +In the previous example, setting the recovery threshold to 900ms means the alert only resolves when the latency falls below 900ms: + +- 980 -> 1010 triggers a firing alert. +- 1010 -> 990 does not resolve the alert, keeping it in the firing state. +- 990 -> 1020 keeps the alert in the firing state. + +The recovery threshold mitigates unnecessary alert state changes and reduces alert noise. + +{{< collapse title="Classic condition (legacy)" >}} + +#### Classic condition (legacy) Classic conditions exist mainly for compatibility reasons and should be avoided if possible. @@ -113,66 +159,35 @@ Classic condition checks if any time series data matches the alert condition. It | Condition operators | How it works | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| and | Two conditions before and after must be true for the overall condition to be true. | -| or | If one of conditions before and after are true, the overall condition is true. | -| logic-or | If the condition before `logic-or` is true, the overall condition is immediately true, without evaluating subsequent conditions. For instance, `TRUE and TRUE logic-or FALSE and FALSE` evaluate to `TRUE`, because the preceding condition returns `TRUE`. | +| `and` | Two conditions before and after must be true for the overall condition to be true. | +| `or` | If one of conditions before and after are true, the overall condition is true. | +| `logic-or` | If the condition before `logic-or` is true, the overall condition is immediately true, without evaluating subsequent conditions. For instance, `TRUE and TRUE logic-or FALSE and FALSE` evaluate to `TRUE`, because the preceding condition returns `TRUE`. | -## Aggregations +The following aggregation functions are also available to further refine your query. -Grafana Alerting provides the following aggregation functions to enable you to further refine your query. +| Function | What it does | +| ------------------ | ------------------------------------------------------------------------------- | +| `avg` | Displays the average of the values | +| `min` | Displays the lowest value | +| `max` | Displays the highest value | +| `sum` | Displays the sum of all values | +| `count` | Counts the number of values in the result | +| `last` | Displays the last value | +| `median` | Displays the median value | +| `diff` | Displays the difference between the newest and oldest value | +| `diff_abs` | Displays the absolute value of diff | +| `percent_diff` | Displays the percentage value of the difference between newest and oldest value | +| `percent_diff_abs` | Displays the absolute value of `percent_diff` | +| `count_non_null` | Displays a count of values in the result set that aren't `null` | -These functions are available for **Reduce** and **Classic condition** expressions only. - -| Function | Expression | What it does | -| ---------------- | ---------------- | ------------------------------------------------------------------------------- | -| avg | Reduce / Classic | Displays the average of the values | -| min | Reduce / Classic | Displays the lowest value | -| max | Reduce / Classic | Displays the highest value | -| sum | Reduce / Classic | Displays the sum of all values | -| count | Reduce / Classic | Counts the number of values in the result | -| last | Reduce / Classic | Displays the last value | -| median | Reduce / Classic | Displays the median value | -| diff | Classic | Displays the difference between the newest and oldest value | -| diff_abs | Classic | Displays the absolute value of diff | -| percent_diff | Classic | Displays the percentage value of the difference between newest and oldest value | -| percent_diff_abs | Classic | Displays the absolute value of percent_diff | -| count_non_null | Classic | Displays a count of values in the result set that aren't `null` | - -## Alert condition - -An alert condition is the query or expression that determines whether the alert fires or not depending on the value it yields. There can be only one condition which determines the triggering of the alert. - -After you have defined your queries and expressions, choose one of them as the alert rule condition. By default, the last expression added is used as the alert condition. - -When the queried data satisfies the defined condition, Grafana triggers the associated alert, which can be configured to send notifications through various channels like email, Slack, or PagerDuty. - -For details about how the alert evaluation triggers notifications, refer to [Alert rule evaluation](ref:alert-rule-evaluation). - -## Recovery threshold - -To reduce the noise of flapping alerts, you can set a recovery threshold different to the alert threshold. - -Flapping alerts occur when a metric hovers around the alert threshold condition and may lead to frequent state changes, resulting in too many notifications being generated. - -It can be tricky to create an alert rule for a noisy metric. That is, when the value of a metric continually goes above and below a threshold. This is called flapping and results in a series of firing - resolved - firing notifications and a noisy alert state history. - -For example, if you have an alert for latency with a threshold of 1000ms and the number fluctuates around 1000 (say 980 ->1010 -> 990 -> 1020, and so on) then each of those triggers a notification. - -To solve this problem, you can set a (custom) recovery threshold, which basically means having two thresholds instead of one: - -1. An alert is triggered when the first threshold is crossed. -2. An alert is resolved only when the second threshold is crossed. - -For example, you could set a threshold of 1000ms and a recovery threshold of 900ms. This way, an alert rule only stops firing when it goes under 900ms and flapping is reduced. - -For details about how the alert evaluation triggers notifications, refer to [Alert rule evaluation](ref:alert-rule-evaluation). +{{< /collapse >}} ## Alert on numeric data Among certain data sources numeric data that is not time series can be directly alerted on, or passed into Server Side Expressions (SSE). This allows for more processing and resulting efficiency within the data source, and it can also simplify alert rules. -When alerting on numeric data instead of time series data, there is no need to reduce each labeled time series into a single number. Instead labeled numbers are returned to Grafana instead. +When alerting on numeric data instead of time series data, there is no need to [reduce](#reduce) each labeled time series into a single number. Instead labeled numbers are returned to Grafana instead. -### Tabular Data +#### Tabular Data This feature is supported with backend data sources that query tabular data: diff --git a/docs/sources/alerting/fundamentals/notifications/_index.md b/docs/sources/alerting/fundamentals/notifications/_index.md index 538eaa0d00a..4c85da09ef6 100644 --- a/docs/sources/alerting/fundamentals/notifications/_index.md +++ b/docs/sources/alerting/fundamentals/notifications/_index.md @@ -126,7 +126,7 @@ Grafana Alerting provides advanced notification capabilities that you’ll find For instance, you can customize notifications with shared [templates](ref:templates) that provide actionable alert information and can be reused for multiple notifications. -Additionally, you can use [silences](ref:silences) and [mute timings](ref:mute-timings) to pause notifications for a given time window or at regular intervals, respectively. +Additionally, you can use [silences](ref:silences) and [mute timings](ref:mute-timings) to pause or suppress notifications without interrupting alert evaluation. ## Architecture diff --git a/docs/sources/alerting/fundamentals/notifications/notification-policies.md b/docs/sources/alerting/fundamentals/notifications/notification-policies.md index 48a4bd70e37..ae8d53f1520 100644 --- a/docs/sources/alerting/fundamentals/notifications/notification-policies.md +++ b/docs/sources/alerting/fundamentals/notifications/notification-policies.md @@ -76,7 +76,7 @@ Each policy consists of a set of label matchers (0 or more) that specify which a {{< figure src="/media/docs/alerting/notification-routing.png" max-width="750px" caption="Matching alert instances with notification policies" alt="Example of a notification policy tree" >}} {{< admonition type="tip" >}} -For a practical example of routing with notification policies, refer to our [Getting Started with Alert Instances and Notification Routing tutorial](https://grafana.com/tutorials/alerting-get-started-pt4/). +For a practical example of routing with notification policies, refer to our [Getting Started with Alert Instances and Notification Routing tutorial](https://grafana.com/tutorials/alerting-get-started-pt2/). {{< /admonition >}} ## Routing diff --git a/docs/sources/alerting/set-up/performance-limitations/index.md b/docs/sources/alerting/set-up/performance-limitations/index.md index 69becbad450..b8524e4c8a7 100644 --- a/docs/sources/alerting/set-up/performance-limitations/index.md +++ b/docs/sources/alerting/set-up/performance-limitations/index.md @@ -61,7 +61,13 @@ For more information, refer to [this GitHub issue](https://github.com/grafana/gr If you have a high number of alert instances, it can happen that the load on the database gets very high, as each state transition of an alert instance is saved in the database. -This can be prevented by writing to the database periodically. For this the feature flag `alertingSaveStatePeriodic` needs +### Compressed alert state + +When the `alertingSaveStateCompressed` feature toggle is enabled, Grafana saves the alert rule state in a compressed form, reducing database overhead for alerts with many instances. + +### Save state periodically + +High load can be also prevented by writing to the database periodically. For this the feature flag `alertingSaveStatePeriodic` needs to be enabled. By default, it saves the states every 5 minutes to the database and on each shutdown. The periodic interval can also be configured using the `state_periodic_save_interval` configuration flag. During this process, Grafana deletes all existing alert instances from the database and then writes the entire current set of instances back in batches in a single transacton. diff --git a/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md b/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md index b7eb4b841fa..850600cc074 100644 --- a/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md +++ b/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md @@ -511,6 +511,8 @@ settings: # endpointUrl: https://custom_url/api/chat.postMessage # + color: {{ if eq .Status "firing" }}#D63232{{ else }}#36a64f{{ end }} + # title: | {{ template "slack.default.title" . }} text: | diff --git a/docs/sources/dashboards/build-dashboards/annotate-visualizations/index.md b/docs/sources/dashboards/build-dashboards/annotate-visualizations/index.md index 13220b2f56a..610f74585d1 100644 --- a/docs/sources/dashboards/build-dashboards/annotate-visualizations/index.md +++ b/docs/sources/dashboards/build-dashboards/annotate-visualizations/index.md @@ -209,3 +209,7 @@ When adding or editing an annotation, you can define a repeating time region by The above configuration produces the following result in the Time series panel: {{< figure src="/media/docs/grafana/screenshot-grafana-10-0-timeseries-time-regions.png" max-width="600px" alt="Time series visualization with time regions business hours" >}} + +Toggle the **Advanced** switch and use [Cron syntax](https://crontab.run/) to set more granular time region controls. The following example sets a time region of 9:00 AM, Monday to Friday: + +{{< figure src="/media/docs/grafana/dashboards/screenshot-annotations-cron-option-v11.6.png" max-width="600px" alt="Time region query with cron syntax" >}} diff --git a/docs/sources/dashboards/build-dashboards/view-dashboard-json-model/index.md b/docs/sources/dashboards/build-dashboards/view-dashboard-json-model/index.md index 474320e361d..0fff2d65d9f 100644 --- a/docs/sources/dashboards/build-dashboards/view-dashboard-json-model/index.md +++ b/docs/sources/dashboards/build-dashboards/view-dashboard-json-model/index.md @@ -136,6 +136,18 @@ The grid has a negative gravity that moves panels up if there is empty space abo "now": true, "hidden": false, "nowDelay": "", + "quick_ranges": [ + { + "display": "Last 6 hours", + "from": "now-6h", + "to": "now" + }, + { + "display": "Last 7 days", + "from": "now-7d", + "to": "now" + } + ], "refresh_intervals": [ "5s", "10s", @@ -163,6 +175,7 @@ Usage of the fields is explained below: | **now** | | | **hidden** | whether timepicker is hidden or not | | **nowDelay** | override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. | +| **quick_ranges** | custom quick ranges | | **refresh_intervals** | interval options available in the refresh picker dropdown | | **status** | | | **type** | | diff --git a/docs/sources/dashboards/create-reports/_index.md b/docs/sources/dashboards/create-reports/_index.md index b29c4fe6f72..0b1de37d290 100644 --- a/docs/sources/dashboards/create-reports/_index.md +++ b/docs/sources/dashboards/create-reports/_index.md @@ -19,66 +19,36 @@ title: Create and manage reports description: Generate and share PDF reports from your Grafana dashboards weight: 600 refs: - repeat-panels-or-rows: - - pattern: /docs/grafana/ - destination: /docs/grafana//panels-visualizations/configure-panel-options/#configure-repeating-panels - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/visualizations/panels-visualizations/configure-panel-options/#configure-repeating-panels - http-apis: - - pattern: /docs/grafana/ - destination: /docs/grafana//developers/http_api/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//developers/http_api/ - image-rendering: - - pattern: /docs/grafana/ - destination: /docs/grafana//setup-grafana/image-rendering/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//setup-grafana/image-rendering/ - rbac: - - pattern: /docs/grafana/ - destination: /docs/grafana//administration/roles-and-permissions/access-control/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//administration/roles-and-permissions/access-control/ - permission: - - pattern: /docs/grafana/ - destination: /docs/grafana//administration/roles-and-permissions/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//administration/roles-and-permissions/ - role-based-access-control: - - pattern: /docs/grafana/ - destination: /docs/grafana//administration/roles-and-permissions/access-control/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//administration/roles-and-permissions/access-control/ - configuration: - - pattern: /docs/grafana/ - destination: /docs/grafana//setup-grafana/configure-grafana/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//setup-grafana/configure-grafana/ - image-rendering: - - pattern: /docs/grafana/ - destination: /docs/grafana//setup-grafana/image-rendering/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//setup-grafana/image-rendering/ - templates-and-variables: - - pattern: /docs/grafana/ - destination: /docs/grafana//dashboards/variables/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/visualizations/dashboards/variables/ grafana-enterprise: - pattern: /docs/grafana/ destination: /docs/grafana//introduction/grafana-enterprise/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana//introduction/grafana-enterprise/ - configuration: + image-rendering: + - pattern: /docs/grafana/ + destination: /docs/grafana//setup-grafana/image-rendering/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//setup-grafana/image-rendering/ + max-size-configuration: + - pattern: /docs/grafana/ + destination: /docs/grafana//setup-grafana/configure-grafana/enterprise-configuration/#max_attachment_size_mb + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//setup-grafana/configure-grafana/enterprise-configuration/#max_attachment_size_mb + log-filters: - pattern: /docs/grafana/ destination: /docs/grafana//setup-grafana/configure-grafana/#filters - pattern: /docs/grafana-cloud/ destination: /docs/grafana//setup-grafana/configure-grafana/#filters - time-range-controls: + permission: - pattern: /docs/grafana/ - destination: /docs/grafana//dashboards/use-dashboards/#set-dashboard-time-range + destination: /docs/grafana//administration/roles-and-permissions/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/visualizations/dashboards/use-dashboards/#set-dashboard-time-range + destination: /docs/grafana//administration/roles-and-permissions/ + rbac: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/ send-report: - pattern: /docs/grafana/ destination: /docs/grafana//developers/http_api/reporting/#send-a-report @@ -94,62 +64,61 @@ refs: destination: /docs/grafana//setup-grafana/configure-grafana/#temp_data_lifetime - pattern: /docs/grafana-cloud/ destination: /docs/grafana//setup-grafana/configure-grafana/#temp_data_lifetime - rendering-configuration: + templates-and-variables: - pattern: /docs/grafana/ - destination: /docs/grafana//dashboards/create-reports/report-settings/#rendering-configuration + destination: /docs/grafana//dashboards/variables/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//dashboards/create-reports/report-settings/#rendering-configuration - reporting-configuration: + destination: /docs/grafana-cloud/visualizations/dashboards/variables/ + time-range-controls: - pattern: /docs/grafana/ - destination: /docs/grafana//setup-grafana/configure-grafana/enterprise-configuration/#reporting + destination: /docs/grafana//dashboards/use-dashboards/#set-dashboard-time-range - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//setup-grafana/configure-grafana/enterprise-configuration/#reporting - + destination: /docs/grafana-cloud/visualizations/dashboards/use-dashboards/#set-dashboard-time-range --- # Create and manage reports -**Reporting** allows you to automatically generate PDFs from any of your dashboards and have Grafana email them to interested parties on a schedule. This is available in Grafana Cloud and in Grafana Enterprise. - -{{< admonition type="note" >}} -If you have [Role-based access control](ref:role-based-access-control) enabled, for some actions you would need to have relevant permissions. -Refer to specific guides to understand what permissions are required. -{{< /admonition >}} - -Any changes you make to a dashboard used in a report are reflected the next time the report is sent. For example, if you change the time range in the dashboard, then the time range in the report also changes, unless you've configured a custom time range. +**Reporting** allows you to send automated and scheduled emails from any of your dashboards. +You can configure several elements of these reports and generate PDFs, CSV files, and embedded images. +Any changes you make to a dashboard used in a report are reflected the next time the report is sent. ## Requirements -- SMTP must be configured for reports to be sent. Refer to [SMTP](ref:smtp) in [Configuration](ref:configuration) for more information. -- The Image Renderer plugin must be installed or the remote rendering service must be set up. Refer to [Image rendering](ref:image-rendering) for more information. +For Grafana Enterprise, the Reporting feature has the following requirements: + +- SMTP must be configured for reports to be sent. Refer to [SMTP configuration documentation](ref:smtp) for more information. +- The [Grafana image renderer plugin](/grafana/plugins/grafana-image-renderer) (v3.10+) must be installed or the remote rendering service must be set up. Refer to [Image rendering](ref:image-rendering) for more information. ### Rendering configuration -When generating reports, each panel renders separately before being collected in a PDF. You can configure the per-panel rendering timeout and number of concurrently rendered panels. +By default, attachments (PDFs, CSV files, and embedded images) larger than 10 MB are not sent, which keeps email servers from rejecting the email. +You can increase or decrease this limit in the [reporting configuration](ref:max-size-configuration). -To make a panel more legible, you can set a scale factor for the rendered images. However, a higher scale factor increases the file size of the generated PDF. - -You can also specify custom fonts that support different Unicode scripts. The DejaVu font is the default used for PDF rendering. - -These options are available in the [reporting configuration](ref:reporting-configuration) of the `ini` file for Enterprise Grafana. +When a report file is generated, it's temporarily written to the corresponding folder (`csv`, `pdf`, `png`) in the Grafana `data` folder. +A background job runs every 10 minutes and removes temporary files. +You can set how long a file should be stored before being removed by configuring the [`temp_data_lifetime`](ref:temp-data-lifetime) setting in your `ini` file. ## Access control -When [RBAC](ref:rbac) is enabled, you need to have the relevant [Permissions](ref:permission) to create and manage reports. +Only organization administrators can create reports by default. +You can customize who can create reports with [role-based access control (RBAC)](ref:rbac). + +When [RBAC](ref:rbac) is enabled, you need to have the relevant [permissions](ref:permission) to create and manage reports. +Refer to specific guides to understand what permissions are required. ## Create a report -Only organization administrators can create reports by default. You can customize who can create reports with [Role-based access control](ref:role-based-access-control). +The report creation process is multi-step, but you don't need to complete these steps in order and you can skip steps by clicking a step name at the top of the page. -The report creation process is multi-step, but you don't need to complete these steps in order and you can skip steps by clicking a step name at the top of the page: +You can also save the report as a draft at any step in the process: ![Reporting wizard](/media/docs/grafana/dashboards/screenshot-reporting-wizard-v11.5.png) To create a report, follow these steps: -1. Click **Dashboards > Reporting** in the main menu. +1. In the main menu, click **Dashboards > Reporting**. 1. Click **+ Create a new report**. -1. Complete the report steps, as needed; you don't need to complete these steps in order and you can skip steps by clicking a step name at the top of the page: +1. Complete the report steps, as needed: - [Select dashboard](#1-select-dashboard) - [Format report](#2-format-report) - [Schedule](#3-schedule) @@ -162,16 +131,17 @@ To create a report, follow these steps: ### 1. Select dashboard -At this step, select the dashboard or dashboards on which the report is based, as well as the variables and time range for those dashboards. The options are: +At this step, select the dashboard or dashboards on which the report is based, as well as the variables and time ranges for those dashboards. +The options are: | Option | Description | | ------ | ----------- | -| Source dashboard | Select the dashboard from which you want to generate the report. | -| [Template variables (optional)](#template-variables) | Select the variable values for the selected dashboard. This option is only displayed if the dashboard has variables. | -| [Time range (optional)](#time-range) | By default, reports use the saved time range of the dashboard. Optionally, you can change the time range of the report. | -| Add another dashboard | Add more dashboards to the report. Additional dashboards will be rendered as new pages in the same PDF file, or additional images if you chose to embed images in your report email. You can't add the same dashboard to a report multiple times. | +| Source dashboard (required) | Select the dashboard from which you want to generate the report. | +| [Template variables](#template-variables) | Select the variable values for the selected dashboard. This option is only displayed if the dashboard has variables. | +| [Time range](#time-range) | If you leave the field empty, reports use the saved time range of the dashboard. Optionally, you can change the time range of the report. | +| Add another dashboard | Add more dashboards to the report. | @@ -179,30 +149,39 @@ At this step, select the dashboard or dashboards on which the report is based, a This option is only displayed if the dashboard has variables. -You can configure report-specific template variables for the dashboard on the report page. The variables that you select will override the variables from the dashboard, and they are used when rendering a PDF file of the report. For detailed information about using template variables, refer to the [Templates and variables](ref:templates-and-variables) section. +You can configure report-specific template variables for the dashboard on the report page. +The variables that you select override the variables from the dashboard. +For detailed information about using template variables, refer to [Variables](ref:templates-and-variables). -The query variables saved with a report might become out of date if the results of that query change. For example, if your template variable queries for a list of hostnames and a new hostname is added, then it will not be included in the report. If that occurs, the selected variables must be manually updated in the report. If you select the **All** value for the template variable or if you keep the dashboard's original variable selection, then the report stays up-to-date as new values are added. +The query variables saved with a report might become out-of-date if the results of that query change. +For example, if your template variable queries for a list of hostnames and a new hostname is added, then it won't be included in the report. +If that occurs, the selected variables must be manually updated in the report. +If you select the **All** value for the template variable or if you keep the dashboard's original variable selection, then the report stays up-to-date as new values are added. #### Time range -By default, reports use the saved time range of the dashboard. Optionally, you can change the time range of the report by: - -- Saving a modified time range to the dashboard. Changing the dashboard time range without saving it doesn't change the time zone of the report. -- Setting a time range via the **Time range** field in the report form. If specified, the custom time range overrides the time range from the report's dashboard. +If you leave the **Time range** field empty, reports use the saved time range of the dashboard. +Optionally, you can change the time range of the report by setting it in the **Time range** field. +If specified, the custom time range overrides the time range from the report's dashboard. The page header of the report displays the time range for the dashboard's data queries. ##### Report time zones -Reports use the time zone of the dashboard from which they’re generated. You can control the time zone for your reports by setting the dashboard to a specific time zone. Note that this affects the display of the dashboard for all users. +Reports use the time zone of the dashboard from which they're generated. +You can control the time zone for your reports by setting the dashboard to a specific time zone. +Note that this affects the display of the dashboard for all users. -If a dashboard has the **Browser Time** setting, the reports generated from that dashboard use the time zone of the Grafana server. As a result, this time zone might not match the time zone of users creating or receiving the report. +If a dashboard has the **Browser Time** setting, the reports generated from that dashboard use the time zone of the Grafana server. +As a result, this time zone might not match the time zone of users creating or receiving the report. +If you want to use a specific time zone, save the dashboard with a fixed time zone instead of **Browser Time** -If the time zone is set differently between your Grafana server and its remote image renderer, then the time ranges in the report might be different between the page header and the time axes in the panels. To avoid this, set the time zone to UTC for dashboards when using a remote renderer. Each dashboard's time zone setting is visible in the [time range controls](ref:time-range-controls). +Each dashboard's time zone setting is visible in the [time range controls](ref:time-range-controls). ### 2. Format report -At this step, set the report formatting options. You can select multiple options: +At this step, select one or more report formatting options. +You can select multiple options, but you must select _at least one_: - [Attach the report as a PDF](#attach-the-report-as-a-pdf) - [Include table data as PDF appendix](#table-data-in-pdf) (Public preview only) @@ -212,13 +191,18 @@ At this step, set the report formatting options. You can select multiple options #### Attach the report as a PDF -If you selected the PDF format option, under the **Style the PDF** section, set the following options: +If you selected the PDF format option under the **Style the PDF** section, you can configure the following options: -- **Show template variables** - Click the checkbox to select this option. +- **Configure multiple PDFs** - Click the **Combine all dashboard PDFs in one file** checkbox if you want to generate one PDF file for all the dashboards included in the report. This option is only displayed if your report includes multiple dashboards. +- **Configure report header** - Click the **Show template variables** checkbox to show dashboard variables. - **Orientation** - Set the report orientation in **Portrait** or **Landscape**. Refer to the [Layout and orientation table](#layout-and-orientation) to see examples. - **Layout** - Select one of the following: - - **Simple** - Renders each panel as full-width across the PDF. Refer to the [Layout and orientation table](#layout-and-orientation) to see examples. - - **Grid** - Renders the PDF with the same panel arrangement and width as the source dashboard. Refer to the [Layout and orientation table](#layout-and-orientation) to see examples. + + - **Simple** - Renders each panel as full-width across the PDF. + - **Grid** - Renders the PDF with the same panel arrangement and width as the source dashboard. + + Refer to the [Layout and orientation table](#layout-and-orientation) to see examples. + - **Zoom** - Zoom in to enlarge text in your PDF, or zoom out to see more data (like table columns) per panel. Click **Preview PDF** in the top-right corner of the screen to view a rendered PDF with the options you selected. @@ -238,50 +222,40 @@ Click **Preview PDF** in the top-right corner of the screen to view a rendered P #### Embed a dashboard as an image in the email -You can send a report email with an image of the dashboard embedded in the email instead of attached as a PDF. In this case, the email recipients can see the dashboard at a glance instead of having to open the PDF. +You can send a report email with an image of the dashboard embedded in the email. +This lets the email recipients see the dashboard at a glance. #### Attach a CSV file of the table panel data -{{< admonition type="note" >}} -To use this feature in Grafana Enterprise, you must have [Grafana image renderer plugin](/grafana/plugins/grafana-image-renderer) v3.0. -{{< /admonition >}} +You can attach a CSV file to the report email for each table panel on the selected dashboard. -You can attach a CSV file to the report email for each table panel on the selected dashboard, along with the PDF report. By default, CSV files larger than 10Mb are not sent, which keeps email servers from rejecting the email. You can increase or decrease this limit in the [reporting configuration](ref:rendering-configuration). - -This feature relies on the same plugin that supports the [image rendering](ref:image-rendering) features. - -When the CSV file is generated, it is temporarily written to the `csv` folder in the Grafana `data` folder. - -A background job runs every 10 minutes and removes temporary CSV files. You can configure how long a CSV file should be stored before being removed by configuring the [temp-data-lifetime](ref:temp-data-lifetime) setting. This setting also affects how long a renderer PNG file should be stored. - -Click **Download CSV** in the top-right corner of the screen to see the file. +Click **Download CSV** in the top-right corner of the screen to download a zipped file of the CSV files for your selected dashboard. #### Table data in PDF -{{% admonition type="note" %}} -Available in public preview (`pdfTables` feature toggle) in [Grafana Enterprise](ref:grafana-enterprise) v10.3+ with the [Grafana image renderer plugin](/grafana/plugins/grafana-image-renderer) v3.0+, as well as [Grafana Cloud](/docs/grafana-cloud/). -{{% /admonition %}} +{{< admonition type="note" >}} +Available in public preview (`pdfTables` feature toggle) in [Grafana Enterprise](ref:grafana-enterprise) v10.3+ with the [Grafana image renderer plugin](/grafana/plugins/grafana-image-renderer) v3.0+, as well as in [Grafana Cloud](/docs/grafana-cloud/). +{{< /admonition >}} When there's more data in your table visualizations than can be shown in the dashboard PDF, you can select one of these two options to access all table visualization data as PDF in your reports: -- **Include table data as PDF appendix** - Adds an appendix to the main dashboard PDF. +- **Include table data as PDF appendix** - Adds an appendix to the dashboard PDF. - **Attach a separate PDF of table data** - Generates a separate PDF file. -This feature relies on the same plugin that supports the [image rendering](ref:image-rendering) features. - ### 3. Schedule -At this step, set scheduling information. Options vary depending on the frequency you select. +At this step, set scheduling information. +Options vary depending on the frequency you select. | Option | Description | | ------ | ----------- | -| Frequency | Scheduled reports can be sent once, or repeated on an hourly, daily, weekly, or monthly basis, or sent at custom intervals. You can also disable scheduling by selecting **Never**, for example to send the report using the API. | +| Frequency | You can schedule reports to be sent once, or repeated on an hourly, daily, weekly, or monthly basis, or sent at custom intervals. You can also disable scheduling by selecting **Never**. For example, you might want to [send the report using the API](#send-a-report-using-the-api). | | Time | Choose one of the following:
  • **Send now** sends the report immediately after you save it. To stop sending the report at some point in the future, add an end date.
  • **Send later** schedules a report for a later date. When you select this option, the required **Start date**, **Start time**, and **Time zone** options are displayed.
| -| End date | If you leave the end date empty, the report is sent out indefinitely. | +| End date | If you leave this field empty, the report is sent out indefinitely. | | Send only from Monday to Friday | For reports that have an hourly or daily frequency, you can choose to send them only from Monday to Friday. | -| Send on the last day of the month | When you schedule a report with a monthly frequency, and set the start date between the 29th and the 31st of the month, the report is only sent during the months that have those dates. If you want the report to be sent every month, select this option instead. This way, the report is sent on the last day of every month regardless of how many days there are in any given month. | +| Send on the last day of the month | When you schedule a report with a monthly frequency, and set the start date between the 29th and the 31st of the month, the report is only sent during the months that have those dates. If you want the report to be sent every month, select the **Send on the last day of the month** option. This way, the report is sent on the last day of every month regardless of how many days there are in the month. | @@ -289,23 +263,29 @@ At this step, set scheduling information. Options vary depending on the frequenc At this step, enter information related to sharing the report: + + | Option | Description | | ------ | ----------- | -| Report name | Name of the report as you want it to appear in the **Reports** list. The report name also populates the email subject line. | -| Recipients | Enter the email addresses of the people or teams that you want to receive the report, separated by commas or semicolons. | +| Report name (required) | The name of the report as you want it to appear in the **Reports** list. The report name also populates the email subject line. | +| Recipients (required) | Enter the email addresses of the people or teams that you want to receive the report, separated by commas or semicolons. | | Reply-to email address | The address that appears in the **Reply to** field of the email. | -| Message | Message body in the email with the report. | -| Include a dashboard link | Include a link to the dashboard from within the report email. | +| Message | The body of the message in the email with the report. | +| Include a dashboard link | Include a links to the dashboards in the report email. | -Click **Send test email** in the top-right corner of the screen to verify that the configuration works as expected and to verify that emails are working. You can choose to send this email to the recipients configured for the report, or to a different set of email addresses only used for testing. The last saved version of the report will be sent to selected emails. + + +Click **Send test email** in the top-right corner of the screen to verify that the configuration works as expected and to verify that emails are working. +You can choose to send this email to the recipients configured for the report, or to a different set of email addresses only used for testing. ### 5. Confirm -At this step, the confirmation page displays all your the settings. Review them and confirm that they're correct or click the provided **Edit** links for each section to make updates. +At this step, the confirmation page displays all the report settings. +Review them and confirm that they're correct or click the provided **Edit** links for each section to make updates. Then, click **Send now** or **Schedule send**. @@ -313,7 +293,7 @@ You can also save the report as a draft or discard it. Discarding the report is ## Send a report using the API -You can send reports programmatically with the [send report](ref:send-report) endpoint in the [HTTP APIs](ref:http-apis). +You can send reports programmatically with the [send report](ref:send-report) endpoint using the HTTP API. ## Manage reports @@ -321,7 +301,7 @@ On the **Reports** page, you can view and manage your existing reports or create ![Reports page](/media/docs/grafana/dashboards/screenshot-reports-page-v11.5.png) -### Edit +### Edit reports To edit a report, follow these steps: @@ -331,9 +311,10 @@ To edit a report, follow these steps: 1. When you've finished making changes, click **Confirm** at the top of the screen to go to the last step. 1. Click **Update report**. -### Pause or resume a report +### Pause or resume reports {#pause-a-report} -You can pause and resume sending reports from the report list view. To do this, follow these steps: +You can pause and resume sending reports from the report list view. +To do this, follow these steps: 1. In the main menu, click **Dashboards > Reporting**. 1. On the row of the report you want to update, do one of the following: @@ -341,11 +322,22 @@ You can pause and resume sending reports from the report list view. To do this, - Click the pause icon - The report won't be sent according to its schedule until it's resumed. - Click the resume icon - The report resumes on its previous schedule. +### Delete reports + +To delete a report, follow these steps: + +1. In the main menu, click **Dashboards > Reporting**. +1. On the row of the report you want to update, click the trash can icon. +1. Click **Delete** to confirm. + +Deleting a report is irreversible. + ## Troubleshoot Reporting -To troubleshoot and get more log information, enable debug logging in the configuration file. Refer to [Configuration](ref:configuration) for more information. +To troubleshoot and get more log information, enable debug logging in the configuration file. +Refer to the [log filters configuration documentation](ref:log-filters) for more information. ```bash [log] -filters = report:debug +filters = rendering:debug,report.api:debug,report.render:debug,report.scheduler:debug,report.sender:debug,report.service:debug ``` diff --git a/docs/sources/dashboards/create-reports/report-settings/index.md b/docs/sources/dashboards/create-reports/report-settings/index.md index c442a4346a8..b878953b71a 100644 --- a/docs/sources/dashboards/create-reports/report-settings/index.md +++ b/docs/sources/dashboards/create-reports/report-settings/index.md @@ -12,21 +12,17 @@ title: Reporting settings description: Manage organizational Reporting settings weight: 700 refs: - grafana-enterprise: + change-ui-theme: - pattern: /docs/grafana/ - destination: /docs/grafana//introduction/grafana-enterprise/ + destination: /docs/grafana//administration/organization-preferences/#change-grafana-ui-theme - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//introduction/grafana-enterprise/ - configuration: - - pattern: /docs/grafana/ - destination: /docs/grafana//setup-grafana/configure-grafana/#filters - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//setup-grafana/configure-grafana/#filters + destination: /docs/grafana//administration/organization-preferences/#change-grafana-ui-theme --- # Reporting settings -You can configure organization-wide report settings and branding options in **Dashboards > Reporting > Settings**. Settings are applied to all the reports for the current organization. +You can configure organization-wide report settings and branding options in **Dashboards > Reporting > Settings**. +Settings are applied to all the reports for the current organization. ## Attachment settings @@ -34,13 +30,22 @@ The options in this section control the branding and theming of the report attac ### PDF -- **Company logo** - Company logo displayed in the report PDF. It can be configured by specifying a URL, or by uploading a file. The maximum file size is 16 MB. Defaults to the Grafana logo. +- **Company logo** - Company logo displayed in the report PDF. + Configure it by specifying a URL or uploading a file. + The maximum file size is 16 MB. + Defaults to the Grafana logo. -- **Theme** - Theme of the PDF attached to the report. Defaults to **Light**. The selected theme is also applied to the PDFs generated when you click **Preview PDF** during report creation or select the **Export as PDF** option on a dashboard. If **Current** is selected, the PDF in the report will be in the instance theme of the Admin, but the preview and exported PDFs will be in the user's instance theme. +- **Theme** - Theme of the PDF attached to the report. + The selected theme is also applied to the PDFs generated when you click **Preview PDF** during report creation or select the **Export as PDF** option on a dashboard. + + If **Current** is selected, the PDF in the report is in the instance theme of the report creator, but the preview and exported PDFs are in the user's instance theme. + Defaults to **Light**. ### Embedded Image -- **Theme** - Theme of the dashboard image embedded in the email. Defaults to **Dark**. +- **Theme** - Theme of the dashboard image embedded in the email. + If **Current** is selected, the image in the report is in the instance theme of the report creator. If the report creator doesn't have a theme set, then the team, organization, or server theme is used. For more information refer to [Change Grafana UI theme](ref:change-ui-theme). + Defaults to **Dark**. @@ -48,9 +53,10 @@ The options in this section control the branding and theming of the report attac -- **Company logo** - Company logo displayed in the report email. It can be configured by specifying a URL, or by uploading a file. The maximum file size is 16 MB. Defaults to the Grafana logo. +- **Company logo** - Company logo displayed in the report email. Configure it by specifying a URL or uploading a file. The maximum file size is 16 MB. Defaults to the Grafana logo. - **Email footer** - Toggle to enable the report email footer. Select **Sent by** or **None**. - **Footer link text** - Text of the link in the report email footer. Defaults to `Grafana`. - **Footer link URL** - Link of the report email footer. -Currently, the API does not allow for the simultaneous upload of files with identical names for both the email logo and report logo. You can still upload the same file for each logo separately in two distinct steps. +Currently, the API does not allow for the simultaneous upload of files with identical names for both the email logo and report logo. +You can still upload the same file for each logo separately in two distinct steps. diff --git a/docs/sources/dashboards/share-dashboards-panels/_index.md b/docs/sources/dashboards/share-dashboards-panels/_index.md index b9d84b58ab6..5885b9b8e86 100644 --- a/docs/sources/dashboards/share-dashboards-panels/_index.md +++ b/docs/sources/dashboards/share-dashboards-panels/_index.md @@ -44,9 +44,9 @@ refs: destination: /docs/grafana/grafana-cloud/visualizations/dashboards/share-dashboards-panels/shared-dashboards/ configure-report: - pattern: /docs/grafana/ - destination: /docs/grafana//dashboards/create-reports/#create-or-update-a-report + destination: /docs/grafana//dashboards/create-reports/#create-a-report - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/visualizations/dashboards/create-reports/#create-or-update-a-report + destination: /docs/grafana-cloud/visualizations/dashboards/create-reports/#create-a-report image-rendering-config: - pattern: /docs/grafana/ destination: /docs/grafana//setup-grafana/image-rendering/#configuration diff --git a/docs/sources/dashboards/variables/add-template-variables/index.md b/docs/sources/dashboards/variables/add-template-variables/index.md index fa4e49ef5a4..54dc20e2d47 100644 --- a/docs/sources/dashboards/variables/add-template-variables/index.md +++ b/docs/sources/dashboards/variables/add-template-variables/index.md @@ -89,6 +89,8 @@ refs: # Add variables + + The following table lists the types of variables shipped with Grafana. | Variable type | Description | @@ -134,11 +136,13 @@ To create a variable, follow these steps: - [Interval](#add-an-interval-variable) - [Ad hoc filters](#add-ad-hoc-filters) + + ## Add a query variable Query variables enable you to write a data source query that can return a list of metric names, tag values, or keys. For example, a query variable might return a list of server names, sensor IDs, or data centers. The variable values change as they dynamically fetch options with a data source query. -Query variables are generally only supported for strings. If your query returns numbers or any other data type, you might need to convert them to strings in order to use them as variables. For the Azure data source, for example, you can use the [tostring](https://docs.microsoft.com/en-us/azure/data-explorer/kusto/query/tostringfunction) function for this purpose. +Query variables are generally only supported for strings. If your query returns numbers or any other data type, you might need to convert them to strings to use them as variables. For the Azure data source, for example, you can use the [`tostring`](https://docs.microsoft.com/en-us/azure/data-explorer/kusto/query/tostringfunction) function for this purpose. Query expressions can contain references to other variables and in effect create linked variables. Grafana detects this and automatically refreshes a variable when one of its linked variables change. @@ -168,7 +172,7 @@ Query expressions are different for each data source. For more information, refe - Some data sources let you provide custom "display names" for the values. For instance, the PostgreSQL, MySQL, and Microsoft SQL Server plugins handle this by looking for fields named `__text` and `__value` in the result. Other data sources may look for `text` and `value` or use a different approach. Always remember to double-check the documentation for the data source. - If you need more room in a single input field query editor, then hover your cursor over the lines in the lower right corner of the field and drag downward to expand. -1. (Optional) In the **Regex** field, type a regex expression to filter or capture specific parts of the names returned by your data source query. To see examples, refer to [Filter variables with regex](#filter-variables-with-regex). +1. (Optional) In the **Regex** field, type a regular expression to filter or capture specific parts of the names returned by your data source query. To see examples, refer to [Filter variables with a regular expression](#filter-variables-with-regex). 1. In the **Sort** drop-down list, select the sort order for values to be displayed in the dropdown list. The default option, **Disabled**, means that the order of options returned by your data source query is used. 1. Under **Refresh**, select when the variable should update options: @@ -240,7 +244,7 @@ _Data source_ variables enable you to quickly change the data source for an enti 1. [Enter general options](#enter-general-options). 1. Under the **Data source options** section of the page, in the **Type** drop-down list, select the target data source for the variable. -1. (Optional) In **Instance name filter**, enter a regex filter for which data source instances to choose from in the variable value drop-down list. +1. (Optional) In **Instance name filter**, enter a regular expression filter for which data source instances to choose from in the variable value drop-down list. Leave this field empty to display all instances. @@ -289,6 +293,9 @@ The following example shows a more complex Graphite example, from the [Graphite groupByNode(summarize(movingAverage(apps.$app.$server.counters.requests.count, 5), '$interval', 'sum', false), 2, 'sum') ``` + + + ## Add ad hoc filters _Ad hoc filters_ are one of the most complex and flexible variable options available. @@ -298,10 +305,10 @@ Ad hoc filters let you add label/value filters that are automatically added to a Unlike other variables, you don't use ad hoc filters in queries. Instead, you use ad hoc filters to write filters for existing queries. -{{% admonition type="note" %}} +{{< admonition type="note" >}} Not all data sources support ad hoc filters. Examples of those that do include Prometheus, Loki, InfluxDB, and Elasticsearch. -{{% /admonition %}} +{{< /admonition >}} To create an ad hoc filter, follow these steps: @@ -318,6 +325,9 @@ To create an ad hoc filter, follow these steps: Now you can [filter data on the dashboard](ref:filter-dashboard). + + + ## Configure variable selection options **Selection Options** are a feature you can use to manage variable option selections. All selection options are optional, and they are off by default. @@ -326,9 +336,9 @@ Now you can [filter data on the dashboard](ref:filter-dashboard). Interpolating a variable with multiple values selected is tricky as it is not straight forward how to format the multiple values into a string that is valid in the given context where the variable is used. Grafana tries to solve this by allowing each data source plugin to inform the templating interpolation engine what format to use for multiple values. -{{% admonition type="note" %}} +{{< admonition type="note" >}} The **Custom all value** option on the variable must be blank for Grafana to format all values into a single string. If it is left blank, then Grafana concatenates (adds together) all the values in the query. Something like `value1,value2,value3`. If a custom `all` value is used, then instead the value is something like `*` or `all`. -{{% /admonition %}} +{{< /admonition >}} #### Multi-value variables with a Graphite data source @@ -336,17 +346,17 @@ Graphite uses glob expressions. A variable with multiple values would, in this c #### Multi-value variables with a Prometheus or InfluxDB data source -InfluxDB and Prometheus use regex expressions, so the same variable would be interpolated as `(host1|host2|host3)`. Every value would also be regex escaped. If not, a value with a regex control character would break the regex expression. +InfluxDB and Prometheus use regular expressions, so the same variable would be interpolated as `(host1|host2|host3)`. Every value would also be regular expression escaped. If not, a value with a regular expression control character would break the regular expression. #### Multi-value variables with an Elastic data source -Elasticsearch uses lucene query syntax, so the same variable would be formatted as `("host1" OR "host2" OR "host3")`. In this case, every value must be escaped so that the value only contains lucene control words and quotation marks. +Elasticsearch uses Lucene query syntax, so the same variable would be formatted as `("host1" OR "host2" OR "host3")`. In this case, every value must be escaped so that the value only contains Lucene control words and quotation marks. #### Troubleshoot multi-value variables -Automatic escaping and formatting can cause problems and it can be tricky to grasp the logic behind it. Especially for InfluxDB and Prometheus where the use of regex syntax requires that the variable is used in regex operator context. +Automatic escaping and formatting can cause problems and it can be tricky to grasp the logic behind it. Especially for InfluxDB and Prometheus where the use of regular expression syntax requires that the variable is used in regular expression operator context. -If you do not want Grafana to do this automatic regex escaping and formatting, then you must do one of the following: +If you do not want Grafana to do this automatic regular expression escaping and formatting, then you must do one of the following: - Turn off the **Multi-value** or **Include All option** options. - Use the [raw variable format](ref:raw-variable-format). @@ -359,28 +369,28 @@ Grafana adds an `All` option to the variable dropdown list. If a user selects th This option is only visible if the **Include All option** is selected. -Enter regex, globs, or lucene syntax in the **Custom all value** field to define the value of the `All` option. +Enter regular expressions, globs, or Lucene syntax in the **Custom all value** field to define the value of the `All` option. -By default the `All` value includes all options in combined expression. This can become very long and can have performance problems. Sometimes it can be better to specify a custom all value, like a wildcard regex. +By default the `All` value includes all options in combined expression. This can become very long and can have performance problems. Sometimes it can be better to specify a custom all value, like a wildcard regular expression. -In order to have custom regex, globs, or lucene syntax in the **Custom all value** option, it is never escaped so you have to think about what is a valid value for your data source. +In order to have custom regular expression, globs, or Lucene syntax in the **Custom all value** option, it is never escaped so you have to think about what is a valid value for your data source. ## Global variables Grafana has global built-in variables that can be used in expressions in the query editor. This topic lists them in alphabetical order and defines them. These variables are useful in queries, dashboard links, panel links, and data links. -### $\_\_dashboard +### `$__dashboard` This variable is the name of the current dashboard. -### $\_\_from and $\_\_to +### `$__from` and `$__to` Grafana has two built-in time range variables: `$__from` and `$__to`. They are currently always interpolated as epoch milliseconds by default, but you can control date formatting. | Syntax | Example result | Description | | ------------------------ | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `${__from}` | 1594671549254 | Unix millisecond epoch | -| `${__from:date}` | 2020-07-13T20:19:09.254Z | No args, defaults to ISO 8601/RFC 3339 | +| `${__from:date}` | 2020-07-13T20:19:09.254Z | No arguments, defaults to ISO 8601/RFC 3339 | | `${__from:date:iso}` | 2020-07-13T20:19:09.254Z | ISO 8601/RFC 3339 | | `${__from:date:seconds}` | 1594671549 | Unix seconds epoch | | `${__from:date:YYYY-MM}` | 2020-07 | Any custom [date format](https://momentjs.com/docs/#/displaying/) that does not include the `:` character. Uses browser time. Use `:date` or `:date:iso` for UTC | @@ -389,7 +399,7 @@ The syntax above also works with `${__to}`. You can use this variable in URLs, as well. For example, you can send a user to a dashboard that shows a time range from six hours ago until now: https://play.grafana.org/d/000000012/grafana-play-home?viewPanel=2&orgId=1?from=now-6h&to=now -### $\_\_interval +### `$__interval` You can use the `$__interval` variable as a parameter to group by time (for InfluxDB, MySQL, Postgres, MSSQL), Date histogram interval (for Elasticsearch), or as a _summarize_ function parameter (for Graphite). @@ -403,42 +413,42 @@ In the InfluxDB data source, the legacy variable `$interval` is the same variabl The InfluxDB and Elasticsearch data sources have `Group by time interval` fields that are used to hard code the interval or to set the minimum limit for the `$__interval` variable (by using the `>` syntax -> `>10m`). -### $\_\_interval_ms +### `$__interval_ms` This variable is the `$__interval` variable in milliseconds, not a time interval formatted string. For example, if the `$__interval` is `20m` then the `$__interval_ms` is `1200000`. -### $\_\_name +### `$__name` -This variable is only available in the Singlestat panel and can be used in the prefix or suffix fields on the Options tab. The variable is replaced with the series name or alias. +This variable is only available in the **Singlestat** panel and can be used in the prefix or suffix fields on the Options tab. The variable is replaced with the series name or alias. -{{% admonition type="note" %}} -The Singlestat panel is no longer available from Grafana 8.0. -{{% /admonition %}} +{{< admonition type="note" >}} +The **Singlestat** panel is no longer available from Grafana 8.0. +{{< /admonition >}} -### $\_\_org +### `$__org` This variable is the ID of the current organization. `${__org.name}` is the name of the current organization. -### $\_\_user +### `$__user` `${__user.id}` is the ID of the current user. `${__user.login}` is the login handle of the current user. `${__user.email}` is the email for the current user. -### $\_\_range +### `$__range` Currently only supported for Prometheus and Loki data sources. This variable represents the range for the current dashboard. It is calculated by `to - from`. It has a millisecond and a second representation called `$__range_ms` and `$__range_s`. -### $\_\_rate_interval +### `$__rate_interval` Currently only supported for Prometheus data sources. The `$__rate_interval` variable is meant to be used in the rate function. Refer to [Prometheus query variables](ref:prometheus-query-variables) for details. -### $\_\_rate_interval_ms +### `$__rate_interval_ms` This variable is the `$__rate_interval` variable in milliseconds, not a time-interval-formatted string. For example, if the `$__rate_interval` is `20m` then the `$__rate_interval_ms` is `1200000`. -### $timeFilter or $\_\_timeFilter +### `$timeFilter` or `$__timeFilter` The `$timeFilter` variable returns the currently selected time range as an expression. For example, the time range interval `Last 7 days` expression is `time > now() - 7d`. @@ -449,7 +459,7 @@ This is used in several places, including: - SQL queries in MySQL, Postgres, and MSSQL. - The `$__timeFilter` variable is used in the MySQL data source. -### $\_\_timezone +### `$__timezone` The `$__timezone` variable returns the currently selected time zone, either `utc` or an entry of the IANA time zone database (for example, `America/New_York`). @@ -485,9 +495,9 @@ In this example, there are several applications. Each application has a differen Now, you could make separate variables for each metric source, but then you have to know which server goes with which app. A better solution is to use one variable to filter another. In this example, when the user changes the value of the `app` variable, it changes the dropdown options returned by the `server` variable. Both variables use the **Multi-value** option and **Include all option**, enabling users to select some or all options presented at any time. -##### app variable +##### `app` variable -The query for this variable basically says, "Give me all the applications that exist." +The query for this variable basically says, "Find all the applications that exist." ``` apps.* @@ -495,9 +505,9 @@ apps.* The values returned are `backend`, `country`, `fakesite`, and `All`. -##### server variable +##### `server` variable -The query for this variable basically says, "Give me all servers for the currently chosen application." +The query for this variable basically says, "Find all servers for the currently chosen application." ``` apps.$app.* @@ -521,9 +531,9 @@ The query returns all servers associated with `fakesite`, including `web_server_ ##### More variables -{{% admonition type="note" %}} +{{< admonition type="note" >}} This example is theoretical. The Graphite server used in the example does not contain CPU metrics. -{{% /admonition %}} +{{< /admonition >}} The dashboard stops at two levels, but you could keep going. For example, if you wanted to get CPU metrics for selected servers, you could copy the `server` variable and extend the query so that it reads: @@ -531,7 +541,7 @@ The dashboard stops at two levels, but you could keep going. For example, if you apps.$app.$server.cpu.* ``` -This query basically says, "Show me the CPU metrics for the selected server." +This query basically says, "Find the CPU metrics for the selected server." Depending on what variable options the user selects, you could get queries like: @@ -547,9 +557,9 @@ In this example, you have several data centers. Each data center has a different In this example, when the user changes the value of the `datacenter` variable, it changes the dropdown options returned by the `host` variable. The `host` variable uses the **Multi-value** option and **Include all option**, allowing users to select some or all options presented at any time. The `datacenter` does not use either option, so you can only select one data center at a time. -##### datacenter variable +##### `datacenter` variable -The query for this variable basically says, "Give me all the data centers that exist." +The query for this variable basically says, "Find all the data centers that exist." ``` SHOW TAG VALUES WITH KEY = "datacenter" @@ -557,9 +567,9 @@ SHOW TAG VALUES WITH KEY = "datacenter" The values returned are `America`, `Africa`, `Asia`, and `Europe`. -##### host variable +##### `host` variable -The query for this variable basically says, "Give me all hosts for the currently chosen data center." +The query for this variable basically says, "Find all hosts for the currently chosen data center." ``` SHOW TAG VALUES WITH KEY = "hostname" WHERE "datacenter" =~ /^$datacenter$/ @@ -583,9 +593,9 @@ The query returns all servers associated with `Europe`, including `server3`, `se ##### More variables -{{% admonition type="note" %}} +{{< admonition type="note" >}} This example is theoretical. The InfluxDB server used in the example does not contain CPU metrics. -{{% /admonition %}} +{{< /admonition >}} The dashboard stops at two levels, but you could keep going. For example, if you wanted to get CPU metrics for selected hosts, you could copy the `host` variable and extend the query so that it reads: @@ -593,7 +603,7 @@ The dashboard stops at two levels, but you could keep going. For example, if you SHOW TAG VALUES WITH KEY = "cpu" WHERE "datacenter" =~ /^$datacenter$/ AND "host" =~ /^$host$/ ``` -This query basically says, "Show me the CPU metrics for the selected host." +This query basically says, "Find the CPU metrics for the selected host." Depending on what variable options the user selects, you could get queries like: @@ -607,7 +617,7 @@ SHOW TAG VALUES WITH KEY = "cpu" WHERE "datacenter" =~ /^Europe/ AND "host" =~ / The following practices make your dashboards and variables easier to use. -#### Creating new linked variables +#### New linked variables creation - Chaining variables create parent/child dependencies. You can envision them as a ladder or a tree. - The easiest way to create a new chained variable is to copy the variable that you want to base the new one on. In the variable list, click the **Duplicate variable** icon to the right of the variable entry to create a copy. You can then add on to the query for the parent variable. @@ -627,13 +637,17 @@ The more layers of dependency you have in variables, the longer it takes to upda For example, if you have a series of four linked variables (country, region, server, metric) and you change a root variable value (country), then Grafana must run queries for all the dependent variables before it updates the visualizations in the dashboard. -## Filter variables with regex + -Using the Regex Query option, you filter the list of options returned by the variable query or modify the options returned. +## Filter variables with regular expressions {#filter-variables-with-regex} -This page shows how to use regex to filter/modify values in the variable dropdown. + -Using the Regex Query Option, you filter the list of options returned by the Variable query or modify the options returned. For more information, refer to the Mozilla guide on [Regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions). +Using the **Regex** query option, you filter the list of options returned by the variable query or modify the options returned. + +This page shows how to use a regular expression to filter/modify values in the variable dropdown. + +Using the **Regex** query option, you filter the list of options returned by the Variable query or modify the options returned. For more information, refer to the Mozilla guide on [Regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions). Examples of filtering on the following list of options: @@ -646,7 +660,7 @@ backend_04 ### Filter so that only the options that end with `01` or `02` are returned: -Regex: +**Regex**: ```regex /(01|02)$/ @@ -659,9 +673,9 @@ backend_01 backend_02 ``` -### Filter and modify the options using a regex capture group to return part of the text: +### Filter and modify the options using a regular expression to capture group to return part of the text: -Regex: +**Regex**: ```regex /.*(01|02)/ @@ -684,7 +698,7 @@ up{instance="demo.robustperception.io:9093",job="alertmanager"} 1 1521630638000 up{instance="demo.robustperception.io:9100",job="node"} 1 1521630638000 ``` -Regex: +**Regex**: ```regex /.*instance="([^"]*).*/ @@ -711,7 +725,7 @@ node_hwmon_chip_names{chip="0000:d7:00_0_0000:d8:00_2",chip_name="enp216s0f0np2" node_hwmon_chip_names{chip="0000:d7:00_0_0000:d8:00_3",chip_name="enp216s0f0np3"} 1 ``` -Passed through the following Regex: +Passed through the following regular expression: ```regex /chip_name="(?[^"]+)|chip="(?[^"]+)/g diff --git a/docs/sources/datasources/aws-cloudwatch/_index.md b/docs/sources/datasources/aws-cloudwatch/_index.md index 6e5d3a0618e..414e770d2bc 100644 --- a/docs/sources/datasources/aws-cloudwatch/_index.md +++ b/docs/sources/datasources/aws-cloudwatch/_index.md @@ -356,6 +356,10 @@ The CloudWatch data source can query data from both CloudWatch metrics and Cloud For details, see the [query editor documentation]({{< relref "./query-editor" >}}). +## Query caching + +When you enable [query and resource caching]({{< relref "/administration/data-source-management/#query-and-resource-caching" >}}), Grafana temporarily stores the results of data source queries and resource requests. Query caching is available in CloudWatch Metrics in Grafana Cloud and Grafana Enterprise. It is not available in CloudWatch Logs Insights due to how query results are polled from AWS. + ## Use template variables Instead of hard-coding details such as server, application, and sensor names in metric queries, you can use variables. @@ -438,7 +442,7 @@ For more information, refer to the AWS documentation for [Service Quotas](https: The CloudWatch plugin enables you to monitor and troubleshoot applications across multiple regional accounts. Using cross-account observability, you can seamlessly search, visualize and analyze metrics and logs without worrying about account boundaries. -To use this feature, configure in the [AWS console under Cloudwatch Settings](https://aws.amazon.com/blogs/aws/new-amazon-cloudwatch-cross-account-observability/), a monitoring and source account, and then add the necessary IAM permissions as described above. +To use this feature, configure in the [AWS console under CloudWatch Settings](https://aws.amazon.com/blogs/aws/new-amazon-cloudwatch-cross-account-observability/), a monitoring and source account, and then add the necessary IAM permissions as described above. ## CloudWatch Logs data protection diff --git a/docs/sources/datasources/aws-cloudwatch/query-editor/index.md b/docs/sources/datasources/aws-cloudwatch/query-editor/index.md index b65bd308e77..05b5eb3a7f4 100644 --- a/docs/sources/datasources/aws-cloudwatch/query-editor/index.md +++ b/docs/sources/datasources/aws-cloudwatch/query-editor/index.md @@ -226,7 +226,7 @@ The label field allows you to override the default name of the metric legend usi ## Query CloudWatch Logs The logs query editor helps you write CloudWatch Logs Query Language queries across defined regions and log groups. -It supports querying Cloudwatch logs with Logs Insights Query Language, OpenSearch PPL and OpenSearch SQL. +It supports querying CloudWatch logs with Logs Insights Query Language, OpenSearch PPL and OpenSearch SQL. ### Create a CloudWatch Logs query @@ -237,7 +237,7 @@ It supports querying Cloudwatch logs with Logs Insights Query Language, OpenSear Region and log groups are mandatory fields when querying with Logs Insights QL and OpenSearch PPL. Log group selection is not necessary when querying with OpenSearch SQL. However, selecting log groups simplifies writing logs queries by populating syntax suggestions with discovered log group fields. {{< /admonition >}} -1. Use the main input area to write your logs query. AWS Cloudwatch only supports a subset of OpenSearch SQL and PPL commands. To find out more about the syntax supported, consult [Amazon CloudWatch Logs documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_AnalyzeLogData_Languages.html) +1. Use the main input area to write your logs query. AWS CloudWatch only supports a subset of OpenSearch SQL and PPL commands. To find out more about the syntax supported, consult [Amazon CloudWatch Logs documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_AnalyzeLogData_Languages.html) #### Querying Log groups with OpenSearch SQL diff --git a/docs/sources/datasources/azure-monitor/template-variables/index.md b/docs/sources/datasources/azure-monitor/template-variables/index.md index 5eb2d9f983c..73711ee701a 100644 --- a/docs/sources/datasources/azure-monitor/template-variables/index.md +++ b/docs/sources/datasources/azure-monitor/template-variables/index.md @@ -48,19 +48,24 @@ For an introduction to templating and template variables, refer to the [Templati You can specify these Azure Monitor data source queries in the Variable edit view's **Query Type** field. -| Name | Description | -| ------------------- | ------------------------------------------------------------------------------------------------------------------ | -| **Subscriptions** | Returns subscriptions. | -| **Resource Groups** | Returns resource groups for a specified. Supports multi-value. subscription. | -| **Namespaces** | Returns metric namespaces for the specified subscription and resource group. | -| **Regions** | Returns regions for the specified subscription | -| **Resource Names** | Returns a list of resource names for a specified subscription, resource group and namespace. Supports multi-value. | -| **Metric Names** | Returns a list of metric names for a resource. | -| **Workspaces** | Returns a list of workspaces for the specified subscription. | -| **Logs** | Use a KQL query to return values. | -| **Resource Graph** | Use an ARG query to return values. | +| Name | Description | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------ | +| **Subscriptions** | Returns subscriptions. | +| **Resource Groups** | Returns resource groups for a specified. Supports multi-value. subscription. | +| **Namespaces** | Returns metric namespaces for the specified subscription and resource group. | +| **Regions** | Returns regions for the specified subscription | +| **Resource Names** | Returns a list of resource names for a specified subscription, resource group and namespace. Supports multi-value. | +| **Metric Names** | Returns a list of metric names for a resource. | +| **Workspaces** | Returns a list of workspaces for the specified subscription. | +| **Logs** | Use a KQL query to return values. | +| **Custom Namespaces** | Returns metric namespaces for the specified resource. | +| **Custom Metric Names** | Returns a list of custom metric names for the specified resource. | -Any Log Analytics Kusto Query Language (KQL) query that returns a single list of values can also be used in the Query field. +{{< admonition type="note" >}} +Custom metrics cannot be emitted against a subscription or resource group. Select resources only when you need to retrieve custom metric namespaces or custom metric names associated with a specific resource. +{{< /admonition >}} + +You can use any Log Analytics Kusto Query Language (KQL) query that returns a single list of values in the `Query` field. For example: | Query | List of values returned | diff --git a/docs/sources/datasources/opentsdb/_index.md b/docs/sources/datasources/opentsdb/_index.md index 560ba731bb5..07a5a66add0 100644 --- a/docs/sources/datasources/opentsdb/_index.md +++ b/docs/sources/datasources/opentsdb/_index.md @@ -62,7 +62,7 @@ To configure basic settings for the data source, complete the following steps: | **Default** | Default data source that will be be pre-selected for new panels. | | **URL** | The HTTP protocol, IP, and port of your OpenTSDB server (default port is usually 4242). | | **Allowed cookies** | Listing of cookies to forward to the data source. | -| **Version** | The OpenTSDB version. | +| **Version** | The OpenTSDB version (supported versions are: 2.4, 2.3, 2.2 and versions less than 2.1). | | **Resolution** | Metrics from OpenTSDB may have data points with either second or millisecond resolution. | | **Lookup limit** | Default is 1000. | @@ -98,9 +98,13 @@ can be used to query OpenTSDB. Fill Policy is also introduced in OpenTSDB 2.2. While using OpenTSDB 2.2 data source, make sure you use either Filters or Tags as they are mutually exclusive. If used together, might give you weird results. {{% /admonition %}} +{{% admonition type="note" %}} +When using OpenTSDB 2.4 with alerting, queries are executed with the parameter `arrays=true`. This causes OpenTSDB to return data points as an array of arrays instead of a map of key-value pairs. Grafana then converts this data into the appropriate data frame format. +{{% /admonition %}} + ### Auto complete suggestions -As soon as you start typing metric names, tag names and tag values , you should see highlighted auto complete suggestions for them. +As you begin typing metric names, tag names, or tag values, highlighted autocomplete suggestions will appear. The autocomplete only works if the OpenTSDB suggest API is enabled. ## Templating queries diff --git a/docs/sources/datasources/postgres/_index.md b/docs/sources/datasources/postgres/_index.md index 3489b760f70..868a2bb7d9e 100644 --- a/docs/sources/datasources/postgres/_index.md +++ b/docs/sources/datasources/postgres/_index.md @@ -2,7 +2,7 @@ aliases: - ../data-sources/postgres/ - ../features/datasources/postgres/ -description: Guide for using PostgreSQL in Grafana +description: Introduction to the PostgreSQL data source in Grafana. keywords: - grafana - postgresql @@ -16,506 +16,58 @@ menuTitle: PostgreSQL title: PostgreSQL data source weight: 1200 refs: - provisioning-data-sources: - - pattern: /docs/grafana/ - destination: /docs/grafana//administration/provisioning/#datasources - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//administration/provisioning/#datasources - variables: - - pattern: /docs/grafana/ - destination: /docs/grafana//dashboards/variables/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//dashboards/variables/ - add-template-variables-interval-ms: - - pattern: /docs/grafana/ - destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval_ms - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval_ms - add-template-variables-interval: - - pattern: /docs/grafana/ - destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval annotate-visualizations: - pattern: /docs/grafana/ destination: /docs/grafana//dashboards/build-dashboards/annotate-visualizations/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana//dashboards/build-dashboards/annotate-visualizations/ - configure-standard-options-display-name: + configure-postgres-data-source: - pattern: /docs/grafana/ - destination: /docs/grafana//panels-visualizations/configure-standard-options/#display-name + destination: /docs/grafana//datasources/postgres/configure/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//panels-visualizations/configure-standard-options/#display-name - data-source-management: + destination: /docs/grafana//datasources/postgres/configure/ + postgres-query-editor: - pattern: /docs/grafana/ - destination: /docs/grafana//administration/data-source-management/ + destination: /docs/grafana//datasources/postgres/query-editor/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//administration/data-source-management/ - variable-syntax-advanced-variable-format-options: + destination: /docs/grafana//datasources/postgres/query-editor/ + alerting: - pattern: /docs/grafana/ - destination: /docs/grafana//dashboards/variables/variable-syntax/#advanced-variable-format-options + destination: /docs/grafana//alerting/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//dashboards/variables/variable-syntax/#advanced-variable-format-options + destination: /docs/grafana-cloud/alerting-and-irm/alerting/ + transformations: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/query-transform-data/transform-data/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/transform-data/ + visualizations: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/visualizations/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/ --- # PostgreSQL data source -Grafana ships with a built-in PostgreSQL data source plugin that allows you to query and visualize data from a PostgreSQL compatible database. +Grafana includes a built-in PostgreSQL data source plugin, enabling you to query and visualize data from any PostgreSQL-compatible database. You don't need to install a plugin to add the PostgreSQL data source to your Grafana instance. -For instructions on how to add a data source to Grafana, refer to the [administration documentation](ref:data-source-management). -Only users with the organization administrator role can add data sources. -Administrators can also [configure the data source via YAML](#provision-the-data-source) with Grafana's provisioning system. +Grafana offers several configuration options for this data source as well as a visual and code-based query editor. + +## Get started with the PostgreSQL data source + +The following documents will help you get started with the PostgreSQL data source in Grafana: + +- [Configure the PostgreSQL data source](ref:configure-postgres-data-source) +- [PostgreSQL query editor](ref:postgres-query-editor) + +After you have configured the data source you can: + +- Create a variety of [visualizations](ref:visualizations) +- Add [annotations](ref:annotate-visualizations) +- Set up [alerting](ref:alerting) +- Add [transformations](ref:transformations) + +View a PostgreSQL overview on Grafana Play: {{< docs/play title="PostgreSQL Overview" url="https://play.grafana.org/d/ddvpgdhiwjvuod/postgresql-overview" >}} - -## PostgreSQL settings - -To configure basic settings for the data source, complete the following steps: - -1. Click **Connections** in the left-side menu. -1. Under Your connections, click **Data sources**. -1. Enter `PostgreSQL` in the search bar. -1. Select **PostgreSQL**. - - The **Settings** tab of the data source is displayed. - -1. Set the data source's basic configuration options: - -| Name | Description | -| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Name** | The data source name. This is how you refer to the data source in panels and queries. | -| **Default** | Default data source means that it will be pre-selected for new panels. | -| **Host** | The IP address/hostname and optional port of your PostgreSQL instance. _Do not_ include the database name. The connection string for connecting to Postgres will not be correct and it may cause errors. | -| **Database** | Name of your PostgreSQL database. | -| **User** | Database user's login/username | -| **Password** | Database user's password | -| **SSL Mode** | Determines whether or with what priority a secure SSL TCP/IP connection will be negotiated with the server. When SSL Mode is disabled, SSL Method and Auth Details would not be visible. | -| **SSL Auth Details Method** | Determines whether the SSL Auth details will be configured as a file path or file content. | -| **SSL Auth Details Value** | File path or file content of SSL root certificate, client certificate and client key | -| **Max open** | The maximum number of open connections to the database, default `100`. | -| **Max idle** | The maximum number of connections in the idle connection pool, default `100`. | -| **Auto (max idle)** | If set will set the maximum number of idle connections to the number of maximum open connections. Default is `true`. | -| **Max lifetime** | The maximum amount of time in seconds a connection may be reused, default `14400`/4 hours. | -| **Version** | Determines which functions are available in the query builder. | -| **TimescaleDB** | A time-series database built as a PostgreSQL extension. When enabled, Grafana uses `time_bucket` in the `$__timeGroup` macro to display TimescaleDB specific aggregate functions in the query builder. For more information, see [TimescaleDB documentation](https://docs.timescale.com/timescaledb/latest/tutorials/grafana/grafana-timescalecloud/#connect-timescaledb-and-grafana). | - -### Min time interval - -A lower limit for the [`$__interval`](ref:add-template-variables-interval) and [`$__interval_ms`](ref:add-template-variables-interval-ms) variables. -Recommended to be set to write frequency, for example `1m` if your data is written every minute. -This option can also be overridden/configured in a dashboard panel under data source options. It's important to note that this value **needs** to be formatted as a -number followed by a valid time identifier, e.g. `1m` (1 minute) or `30s` (30 seconds). The following time identifiers are supported: - -| Identifier | Description | -| ---------- | ----------- | -| `y` | year | -| `M` | month | -| `w` | week | -| `d` | day | -| `h` | hour | -| `m` | minute | -| `s` | second | -| `ms` | millisecond | - -### Database user permissions (Important!) - -The database user you specify when you add the data source should only be granted SELECT permissions on -the specified database and tables you want to query. Grafana does not validate that the query is safe. The query -could include any SQL statement. For example, statements like `DELETE FROM user;` and `DROP TABLE user;` would be -executed. To protect against this we **highly** recommend you create a specific PostgreSQL user with restricted permissions. - -Example: - -```sql - CREATE USER grafanareader WITH PASSWORD 'password'; - GRANT USAGE ON SCHEMA schema TO grafanareader; - GRANT SELECT ON schema.table TO grafanareader; -``` - -Make sure the user does not get any unwanted privileges from the public role. - -## Query builder - -{{< figure src="/static/img/docs/screenshot-postgres-query-editor.png" class="docs-image--no-shadow" caption="PostgreSQL query builder" >}} - -The PostgreSQL query builder is available when editing a panel using a PostgreSQL data source. The built query can be run by pressing the `Run query` button in the top right corner of the editor. - -### Format - -The response from PostgreSQL can be formatted as either a table or as a time series. To use the time series format one of the columns must be named `time`. - -### Dataset and table selection - -The dataset dropdown will be populated with the configured database to which the user has access. -The table dropdown is populated with the tables that are available within that database. - -### Columns and Aggregation functions (SELECT) - -Using the dropdown, select a column to include in the data. You can also specify an optional aggregation function. - -Add further value columns by clicking the plus button and another column dropdown appears. - -{{< docs/shared source="grafana" lookup="datasources/sql-query-builder-macros.md" version="" >}} - -### Filter data (WHERE) - -To add a filter, toggle the **Filter** switch at the top of the editor. -This reveals a **Filter by column value** section with two dropdown selectors. - -Use the first dropdown to choose whether all of the filters need to match (`AND`), or if only one of the filters needs to match (`OR`). -Use the second dropdown to choose a filter. - -To filter on more columns, click the plus (`+`) button to the right of the condition dropdown. - -To remove a filter, click the `x` button next to that filter's dropdown. - -After selecting a date type column, you can choose Macros from the operators list and select timeFilter which will add the $\_\_timeFilter macro to the query with the selected date column. - -### Group By - -To group the results by column, flip the group switch at the top of the editor. You can then choose which column to group the results by. The group by clause can be removed by pressing the X button. - -### Preview - -By flipping the preview switch at the top of the editor, you can get a preview of the SQL query generated by the query builder. - -### Provision the data source - -It's now possible to configure data sources using config files with Grafana's provisioning system. You can read more about how it works and all the settings you can set for data sources on the [provisioning docs page](ref:provisioning-data-sources). - -#### Provisioning example - -```yaml -apiVersion: 1 - -datasources: - - name: Postgres - type: postgres - url: localhost:5432 - user: grafana - secureJsonData: - password: 'Password!' - jsonData: - database: grafana - sslmode: 'disable' # disable/require/verify-ca/verify-full - maxOpenConns: 100 - maxIdleConns: 100 - maxIdleConnsAuto: true - connMaxLifetime: 14400 - postgresVersion: 903 # 903=9.3, 904=9.4, 905=9.5, 906=9.6, 1000=10 - timescaledb: false -``` - -{{% admonition type="note" %}} -In the above code, the `postgresVersion` value of `10` refers to version PostgreSQL 10 and above. -{{% /admonition %}} - -#### Troubleshoot provisioning - -If you encounter metric request errors or other issues: - -- Make sure your data source YAML file parameters exactly match the example. This includes parameter names and use of quotation marks. -- Make sure the `database` name is not included in the `url`. - -## Code editor - -{{< figure src="/static/img/docs/v92/sql_code_editor.png" class="docs-image--no-shadow" >}} - -To make advanced queries, switch to the code editor by clicking `code` in the top right corner of the editor. The code editor support autocompletion of tables, columns, SQL keywords, standard sql functions, Grafana template variables and Grafana macros. Columns cannot be completed before a table has been specified. - -You can expand the code editor by pressing the `chevron` pointing downwards in the lower right corner of the code editor. - -`CTRL/CMD + Return` works as a keyboard shortcut to run the query. - -## Macros - -Macros can be used within a query to simplify syntax and allow for dynamic parts. - -| Macro example | Description | -| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `$__time(dateColumn)` | Will be replaced by an expression to convert to a UNIX timestamp and rename the column to `time_sec`. For example, _UNIX_TIMESTAMP(dateColumn) as time_sec_ | -| `$__timeEpoch(dateColumn)` | Will be replaced by an expression to convert to a UNIX timestamp and rename the column to `time_sec`. For example, _UNIX_TIMESTAMP(dateColumn) as time_sec_ | -| `$__timeFilter(dateColumn)` | Will be replaced by a time range filter using the specified column name. For example, _dateColumn BETWEEN FROM_UNIXTIME(1494410783) AND FROM_UNIXTIME(1494410983)_ | -| `$__timeFrom()` | Will be replaced by the start of the currently active time selection. For example, _FROM_UNIXTIME(1494410783)_ | -| `$__timeTo()` | Will be replaced by the end of the currently active time selection. For example, _FROM_UNIXTIME(1494410983)_ | -| `$__timeGroup(dateColumn,'5m')` | Will be replaced by an expression usable in GROUP BY clause. For example, *cast(cast(UNIX_TIMESTAMP(dateColumn)/(300) as signed)*300 as signed),\* | -| `$__timeGroup(dateColumn,'5m', 0)` | Same as above but with a fill parameter so missing points in that series will be added by grafana and 0 will be used as value (only works with time series queries). | -| `$__timeGroup(dateColumn,'5m', NULL)` | Same as above but NULL will be used as value for missing points (only works with time series queries). | -| `$__timeGroup(dateColumn,'5m', previous)` | Same as above but the previous value in that series will be used as fill value if no value has been seen yet NULL will be used (only works with time series queries). | -| `$__timeGroupAlias(dateColumn,'5m')` | Will be replaced identical to $\_\_timeGroup but with an added column alias. | -| `$__unixEpochFilter(dateColumn)` | Will be replaced by a time range filter using the specified column name with times represented as Unix timestamp. For example, _dateColumn > 1494410783 AND dateColumn < 1494497183_ | -| `$__unixEpochFrom()` | Will be replaced by the start of the currently active time selection as Unix timestamp. For example, _1494410783_ | -| `$__unixEpochTo()` | Will be replaced by the end of the currently active time selection as Unix timestamp. For example, _1494497183_ | -| `$__unixEpochNanoFilter(dateColumn)` | Will be replaced by a time range filter using the specified column name with times represented as nanosecond timestamp. For example, _dateColumn > 1494410783152415214 AND dateColumn < 1494497183142514872_ | -| `$__unixEpochNanoFrom()` | Will be replaced by the start of the currently active time selection as nanosecond timestamp. For example, _1494410783152415214_ | -| `$__unixEpochNanoTo()` | Will be replaced by the end of the currently active time selection as nanosecond timestamp. For example, _1494497183142514872_ | -| `$__unixEpochGroup(dateColumn,'5m', [fillmode])` | Same as $\_\_timeGroup but for times stored as Unix timestamp (`fillMode` only works with time series queries). | -| `$__unixEpochGroupAlias(dateColumn,'5m', [fillmode])` | Same as above but also adds a column alias (`fillMode` only works with time series queries). | - -## Table queries - -If the `Format as` query option is set to `Table` then you can basically do any type of SQL query. The table panel will automatically show the results of whatever columns and rows your query returns. - -Query editor with example query: - -![](/static/img/docs/v46/postgres_table_query.png) - -The query: - -```sql -SELECT - title as "Title", - "user".login as "Created By", - dashboard.created as "Created On" -FROM dashboard -INNER JOIN "user" on "user".id = dashboard.created_by -WHERE $__timeFilter(dashboard.created) -``` - -You can control the name of the Table panel columns by using regular `as ` SQL column selection syntax. - -The resulting table panel: - -![postgres table](/static/img/docs/v46/postgres_table.png) - -## Time series queries - -If you set Format as to _Time series_, then the query must have a column named time that returns either a SQL datetime or any numeric datatype representing Unix epoch in seconds. In addition, result sets of time series queries must be sorted by time for panels to properly visualize the result. - -A time series query result is returned in a [wide data frame format](https://grafana.com/developers/plugin-tools/key-concepts/data-frames#wide-format). Any column except time or of type string transforms into value fields in the data frame query result. Any string column transforms into field labels in the data frame query result. - -> For backward compatibility, there's an exception to the above rule for queries that return three columns including a string column named metric. Instead of transforming the metric column into field labels, it becomes the field name, and then the series name is formatted as the value of the metric column. See the example with the metric column below. - -To optionally customize the default series name formatting, refer to [Standard options definitions](ref:configure-standard-options-display-name). - -**Example with `metric` column:** - -```sql -SELECT - $__timeGroupAlias("time_date_time",'5m'), - min("value_double"), - 'min' as metric -FROM test_data -WHERE $__timeFilter("time_date_time") -GROUP BY time -ORDER BY time -``` - -Data frame result: - -```text -+---------------------+-----------------+ -| Name: time | Name: min | -| Labels: | Labels: | -| Type: []time.Time | Type: []float64 | -+---------------------+-----------------+ -| 2020-01-02 03:05:00 | 3 | -| 2020-01-02 03:10:00 | 6 | -+---------------------+-----------------+ -``` - -**Example using the fill parameter in the $\_\_timeGroupAlias macro to convert null values to be zero instead:** - -```sql -SELECT - $__timeGroupAlias("createdAt",'5m',0), - sum(value) as value, - hostname -FROM test_data -WHERE - $__timeFilter("createdAt") -GROUP BY time, hostname -ORDER BY time -``` - -Given the data frame result in the following example and using the graph panel, you will get two series named _value 10.0.1.1_ and _value 10.0.1.2_. To render the series with a name of _10.0.1.1_ and _10.0.1.2_ , use a [Standard options definitions](ref:configure-standard-options-display-name) display value of `${__field.labels.hostname}`. - -Data frame result: - -```text -+---------------------+---------------------------+---------------------------+ -| Name: time | Name: value | Name: value | -| Labels: | Labels: hostname=10.0.1.1 | Labels: hostname=10.0.1.2 | -| Type: []time.Time | Type: []float64 | Type: []float64 | -+---------------------+---------------------------+---------------------------+ -| 2020-01-02 03:05:00 | 3 | 4 | -| 2020-01-02 03:10:00 | 6 | 7 | -+---------------------+---------------------------+---------------------------+ -``` - -**Example with multiple columns:** - -```sql -SELECT - $__timeGroupAlias("time_date_time",'5m'), - min("value_double") as "min_value", - max("value_double") as "max_value" -FROM test_data -WHERE $__timeFilter("time_date_time") -GROUP BY time -ORDER BY time -``` - -Data frame result: - -```text -+---------------------+-----------------+-----------------+ -| Name: time | Name: min_value | Name: max_value | -| Labels: | Labels: | Labels: | -| Type: []time.Time | Type: []float64 | Type: []float64 | -+---------------------+-----------------+-----------------+ -| 2020-01-02 03:04:00 | 3 | 4 | -| 2020-01-02 03:05:00 | 6 | 7 | -+---------------------+-----------------+-----------------+ -``` - -## Templating - -Instead of hard-coding things like server, application and sensor name in your metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns make it easy to change the data being displayed in your dashboard. - -Refer to [Templates and variables](ref:variables) for an introduction to the templating feature and the different types of template variables. - -### Query variable - -If you add a template variable of the type `Query`, you can write a PostgreSQL query that can -return things like measurement names, key names or key values that are shown as a dropdown select box. - -For example, you can have a variable that contains all values for the `hostname` column in a table if you specify a query like this in the templating variable _Query_ setting. - -```sql -SELECT hostname FROM host -``` - -A query can return multiple columns and Grafana will automatically create a list from them. For example, the query below will return a list with values from `hostname` and `hostname2`. - -```sql -SELECT host.hostname, other_host.hostname2 FROM host JOIN other_host ON host.city = other_host.city -``` - -To use time range dependent macros like `$__timeFilter(column)` in your query the refresh mode of the template variable needs to be set to _On Time Range Change_. - -```sql -SELECT event_name FROM event_log WHERE $__timeFilter(time_column) -``` - -Another option is a query that can create a key/value variable. The query should return two columns that are named `__text` and `__value`. The `__text` column value should be unique (if it is not unique then the first value is used). The options in the dropdown will have a text and value that allows you to have a friendly name as text and an id as the value. An example query with `hostname` as the text and `id` as the value: - -```sql -SELECT hostname AS __text, id AS __value FROM host -``` - -You can also create nested variables. Using a variable named `region`, you could have -the hosts variable only show hosts from the current selected region with a query like this (if `region` is a multi-value variable then use the `IN` comparison operator rather than `=` to match against multiple values): - -```sql -SELECT hostname FROM host WHERE region IN($region) -``` - -#### Using `__searchFilter` to filter results in Query Variable - -Using `__searchFilter` in the query field will filter the query result based on what the user types in the dropdown select box. -When nothing has been entered by the user the default value for `__searchFilter` is `%`. - -> Important that you surround the `__searchFilter` expression with quotes as Grafana does not do this for you. - -The example below shows how to use `__searchFilter` as part of the query field to enable searching for `hostname` while the user types in the dropdown select box. - -Query - -```sql -SELECT hostname FROM my_host WHERE hostname LIKE '$__searchFilter' -``` - -### Using Variables in Queries - -Template variable values are only quoted when the template variable is a `multi-value`. - -If the variable is a multi-value variable then use the `IN` comparison operator rather than `=` to match against multiple values. - -There are two syntaxes: - -`$` Example with a template variable named `hostname`: - -```sql -SELECT - atimestamp as time, - aint as value -FROM table -WHERE $__timeFilter(atimestamp) and hostname in($hostname) -ORDER BY atimestamp ASC -``` - -`[[varname]]` Example with a template variable named `hostname`: - -```sql -SELECT - atimestamp as time, - aint as value -FROM table -WHERE $__timeFilter(atimestamp) and hostname in([[hostname]]) -ORDER BY atimestamp ASC -``` - -#### Disabling quoting for multi-value variables - -Grafana automatically creates a quoted, comma-separated string for multi-value variables. For example: if `server01` and `server02` are selected then it will be formatted as: `'server01', 'server02'`. To disable quoting, use the csv formatting option for variables: - -`${servers:csv}` - -Read more about variable formatting options in the [Variables](ref:variable-syntax-advanced-variable-format-options) documentation. - -## Annotations - -[Annotations](ref:annotate-visualizations) allow you to overlay rich event information on top of graphs. You add annotation queries via the Dashboard menu / Annotations view. - -**Example query using time column with epoch values:** - -```sql -SELECT - epoch_time as time, - metric1 as text, - concat_ws(', ', metric1::text, metric2::text) as tags -FROM - public.test_data -WHERE - $__unixEpochFilter(epoch_time) -``` - -**Example region query using time and timeend columns with epoch values:** - -```sql -SELECT - epoch_time as time, - epoch_time_end as timeend, - metric1 as text, - concat_ws(', ', metric1::text, metric2::text) as tags -FROM - public.test_data -WHERE - $__unixEpochFilter(epoch_time) -``` - -**Example query using time column of native SQL date/time data type:** - -```sql -SELECT - native_date_time as time, - metric1 as text, - concat_ws(', ', metric1::text, metric2::text) as tags -FROM - public.test_data -WHERE - $__timeFilter(native_date_time) -``` - -| Name | Description | -| --------- | ----------------------------------------------------------------------------------------------------------------- | -| `time` | The name of the date/time field. Could be a column with a native SQL date/time data type or epoch value. | -| `timeend` | Optional name of the end date/time field. Could be a column with a native SQL date/time data type or epoch value. | -| `text` | Event description field. | -| `tags` | Optional field name to use for event tags as a comma separated string. | - -## Alerting - -Time series queries should work in alerting conditions. Table formatted queries are not yet supported in alert rule -conditions. diff --git a/docs/sources/datasources/postgres/configure/_index.md b/docs/sources/datasources/postgres/configure/_index.md new file mode 100644 index 00000000000..7b6a66c0a70 --- /dev/null +++ b/docs/sources/datasources/postgres/configure/_index.md @@ -0,0 +1,194 @@ +--- +description: This document provides instructions for configuring the PostgreSQL data source. +keywords: + - grafana + - postgresql + - guide +labels: + products: + - cloud + - enterprise + - oss +menuTitle: Configure the PostgreSQL data source +title: Configure the PostgreSQL data source +weight: 10 +refs: + provisioning-data-sources: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/provisioning/#datasources + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//administration/provisioning/#datasources + variables: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/variables/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//dashboards/variables/ + add-template-variables-interval-ms: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval_ms + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval_ms + add-template-variables-interval: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval + data-source-management: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/data-source-management/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//administration/data-source-management/ + variable-syntax-advanced-variable-format-options: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/variables/variable-syntax/#advanced-variable-format-options + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//dashboards/variables/variable-syntax/#advanced-variable-format-options +--- + +# Configure the PostgreSQL data source + +This document provides instructions for configuring the PostgreSQL data source and explains available configuration options. For general information on managing data sources refer to [Data source management](ref:data-source-management). + +## Before you begin + +You must have the `Organization administrator` role to configure the Postgres data source. +Organization administrators can also [configure the data source via YAML](#provision-the-data-source) with the Grafana provisioning system. + +Grafana comes with a built-in PostgreSQL data source plugin, eliminating the need to install a plugin. + +{{< admonition type="note" >}} +When adding a data source, the database user you specify should have only `SELECT` permissions on the relevant database and tables. Grafana does not validate the safety of queries, which means they can include potentially harmful SQL statements, such as `USE otherdb;` or `DROP TABLE user;`, that could be executed. To mitigate this risk, Grafana strongly recommends creating a dedicated PostgreSQL user with restricted permissions. +{{< /admonition >}} + +Example: + +```sql + CREATE USER grafanareader WITH PASSWORD 'password'; + GRANT USAGE ON SCHEMA schema TO grafanareader; + GRANT SELECT ON schema.table TO grafanareader; +``` + +## Add the PostgreSQL data source + +Complete the following steps to set up a new PostgreSQL data source: + +1. Click **Connections** in the left-side menu. +1. Click **Add new connection** +1. Type `PostgreSQL` in the search bar. +1. Select the **PostgreSQL data source**. +1. Click **Add new data source** in the upper right. + +You are taken to the **Settings** tab where you will configure the data source. + +## PostgreSQL configuration options + +Following is a list of PostgreSQL configuration options: + +- **Name** - Sets the name you use to refer to the data source in panels and queries. Examples: `PostgreSQL-DB-1`. +- **Default** - Toggle to set this specific PostgreSQL data source as the default pre-selected data source in panels and visualizations. + +**Connection section:** + +- **Host URL** - The IP address/hostname and optional port of your PostgreSQL instance. +- **Database name** - The name of your PostgreSQL database. + +**Authentication section:** + +- **Username** - Enter the username used to connect to your PostgreSQL database. +- **Password** - Enter the password used to connect to the PostgreSQL database. +- **TLS/SSL Mode** - Determines whether or with what priority a secure SSL TCP/IP connection will be negotiated with the server. When **TLS/SSL Mode** is disabled, **TLS/SSL Method** and **TLS/SSL Auth Details** aren't visible options. +- **TLS/SSL Method** - Determines how TLS/SSL certificates are configured. + - **File system path** - This option allows you to configure certificates by specifying paths to existing certificates on the local file system where Grafana is running. Ensure this file is readable by the user executing the Grafana process. + - **Certificate content** - This option allows you to configure certificate by specifying their content. The content is stored and encrypted in the Grafana database. When connecting to the database, the certificates are saved as files, on the local filesystem, in the Grafana data path. + +**TLS/SSL Auth Details** + +If you select the TLS/SSL Mode options **require**, **verify-ca** or **verify-full** and **file system path** the following are required: + +- **TLS/SSL Root Certificate** - Specify the path to the root certificate file. +- **TLS/SSL Client Certificate** - Specify the path to the client certificate and ensure the file is accessible to the user running the Grafana process. +- **TLS/SSL Client Key** - Specify the path to the client key file and ensure the file is accessible to the user running the Grafana process. + +If you select the TLS/SSL Mode option **require** and TLS/SSL Method certificate content the following are required: + +- **TLS/SSL Client Certificate** - Provide the client certificate. +- **TLS/SSL Client Key** - Provide the client key. + +If you select the TLS/SSL Mode options **verify-ca** or **verify-full** with the TLS/SSL Method certificate content the following are required: + +- **TLS/SSL Client Certificate** - Provide the client certificate. +- **TLS/SSL Root Certificate** - Provide the root certificate. +- **TLS/SSL Client Key** - Provide the client key. + +**PostgreSQL Options:** + +- **Version** - Determines which functions are available in the query builder. The default is the current version. +- **Min time interval** - Defines a lower limit for the auto group by by time interval. Grafana recommends aligning this setting with the data write frequency. For example, set it to `1m` if your data is written every minute. Refer to [Min time interval](#min-time-interval) for format examples. +- **TimescaleDB** - A time-series database built as a PostgreSQL extension. When enabled, Grafana uses `time_bucket` in the `$__timeGroup` macro to display TimescaleDB specific aggregate functions in the query builder. For more information, refer to [TimescaleDB documentation](https://docs.timescale.com/timescaledb/latest/tutorials/grafana/grafana-timescalecloud/#connect-timescaledb-and-grafana). + +**Connection limits:** + +- **Max open** - The maximum number of open connections to the database. The default `100`. +- **Auto max idle** - Toggle to set the maximum number of idle connections to the number of maximum open connections. This setting is toggled on by default. +- **Max idle** - The maximum number of connections in the idle connection pool. The default `100`. +- **Max lifetime** - The maximum amount of time in seconds a connection may be reused. The default is `14400`, or 4 hours. + +**Private data source connect** - _Only for Grafana Cloud users._ Private data source connect, or PDC, allows you to establish a private, secured connection between a Grafana Cloud instance, or stack, and data sources secured within a private network. Click the drop-down to locate the URL for PDC. For more information regarding Grafana PDC refer to [Private data source connect (PDC)](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/). + +Click **Manage private data source connect** to be taken to your PDC connection page, where you’ll find your PDC configuration details. + +After you have added your PostgreSQL connection settings, click **Save & test** to test and save the data source connection. + +### Min time interval + +The **Min time interval** setting defines a lower limit for the [`$__interval`](ref:add-template-variables-interval) and [`$__interval_ms`](ref:add-template-variables-interval-ms) variables. + +This option can also be configured or overridden in the dashboard panel under the data source settings. + +This value must be formatted as a number followed by a valid time identifier: + +| Identifier | Description | +| ---------- | ----------- | +| `y` | year | +| `M` | month | +| `w` | week | +| `d` | day | +| `h` | hour | +| `m` | minute | +| `s` | second | +| `ms` | millisecond | + +## Provision the data source + +You can define and configure the data source in YAML files with [provisioning](/docs/grafana//administration/provisioning/#data-sources). +For more information about provisioning, and available configuration options, refer to [Provision Grafana](ref:provisioning-data-sources). + +### PostgreSQL provisioning example + +```yaml +apiVersion: 1 + +datasources: + - name: Postgres + type: postgres + url: localhost:5432 + user: grafana + secureJsonData: + password: 'Password!' + jsonData: + database: grafana + sslmode: 'disable' # disable/require/verify-ca/verify-full + maxOpenConns: 100 + maxIdleConns: 100 + maxIdleConnsAuto: true + connMaxLifetime: 14400 + postgresVersion: 903 # 903=9.3, 904=9.4, 905=9.5, 906=9.6, 1000=10 + timescaledb: false +``` + +#### Troubleshoot provisioning issues + +If you encounter metric request errors or other issues: + +- Ensure that the parameters in your data source YAML file precisely match the example provided, including parameter names and the correct use of quotation marks. +- Verify that the database name _isn't_ included in the URL. diff --git a/docs/sources/datasources/postgres/query-editor/_index.md b/docs/sources/datasources/postgres/query-editor/_index.md new file mode 100644 index 00000000000..5c37f7047e7 --- /dev/null +++ b/docs/sources/datasources/postgres/query-editor/_index.md @@ -0,0 +1,410 @@ +--- +description: This document describes the PostgreSQL query editor in Grafana. +keywords: + - grafana + - postgresql + - guide +labels: + products: + - cloud + - enterprise + - oss +menuTitle: PostgreSQL query editor +title: PostgreSQL query editor +weight: 20 +refs: + variables: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/variables/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//dashboards/variables/ + add-template-variables-interval: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval + explore: + - pattern: /docs/grafana/ + destination: /docs/grafana//explore/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//explore/ + query-transform-data: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/query-transform-data/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/ + query-editor: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/query-transform-data/#query-editors + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/#query-editors + alert-rules: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/ + template-annotations-and-labels: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/ + templates: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/variables/#templates + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/variables/#templates +--- + +# PostgreSQL query editor + +Grafana query editors are unique for each data source. + +For general information on Grafana query editors, refer to [Query editors](ref:query-editor). + +For general information on querying data sources in Grafana, refer to [Query and transform data](ref:query-transform-data). + +The PostgreSQL query editor is located on the [Explore page](ref:explore). You can also access the PostgreSQL query editor from a dashboard panel. Click the ellipsis in the upper right of the panel and select **Edit**. + +{{< figure src="/static/img/docs/screenshot-postgres-query-editor.png" class="docs-image--no-shadow" caption="PostgreSQL query builder" >}} + +## PostgreSQL query editor components + +The PostgreSQL query editor has two modes: **Builder** and **Code**. + +Builder mode helps you build a query using a visual interface. Code mode allows for advanced querying and offers support for complex SQL query writing. + +### PostgreSQL Builder mode + +The following components will help you build a PostgreSQL query: + +- **Format** - Select a format response from the drop-down for the PostgreSQL query. The default is **Table**. If you use the **Time series** format option, one of the columns must be `time`. Refer to [Time series queries](#time-series-queries) for more information. +- **Table** - Select a table from the drop-down. Tables correspond to the chosen database. +- **Data operations** - _Optional_ Select an aggregation from the drop-down. You can add multiple data operations by clicking the **+ sign**. Click the **garbage can icon** to remove data operations. +- **Column** - Select a column on which to run the aggregation. +- **Alias** - _Optional_ Add an alias from the drop-down. You can also add your own alias by typing it in the box and clicking **Enter**. Remove an alias by clicking the **X**. +- **Filter** - Toggle to add filters. +- **Filter by column value** - _Optional_ If you toggle **Filter** you can add a column to filter by from the drop-down. To filter on more columns, click the **+ sign** to the right of the condition drop-down. You can choose a variety of operators from the drop-down next to the condition. When multiple filters are added you can add an `AND` operator to display all true conditions or an `OR` operator to display any true conditions. Use the second drop-down to choose a filter. To remove a filter, click the `X` button next to that filter's drop-down. After selecting a date type column, you can choose **Macros** from the operators list and select `timeFilter` which will add the `$\_\_timeFilter` macro to the query with the selected date column. +- **Group** - Toggle to add **Group by column**. +- **Group by column** - Select a column to filter by from the drop-down. Click the **+sign** to filter by multiple columns. Click the **X** to remove a filter. +- **Order** - Toggle to add an `ORDER BY` statement. +- **Order by** - Select a column to order by from the drop-down. Select ascending (`ASC`) or descending (`DESC`) order. +- **Limit** - You can add an optional limit on the number of retrieved results. Default is 50. +- **Preview** - Toggle for a preview of the SQL query generated by the query builder. Preview is toggled on by default. + +## PostgreSQL Code mode + +To create advanced queries, switch to **Code mode** by clicking **Code** in the upper right of the editor window. Code mode supports the auto-completion of tables, columns, SQL keywords, standard SQL functions, Grafana template variables, and Grafana macros. Columns cannot be completed before a table has been specified. + +{{< figure src="/static/img/docs/v92/sql_code_editor.png" class="docs-image--no-shadow" >}} + +Select **Table** or **Time Series** as the format. Click the **{}** in the bottom right to format the query. Click the **downward caret** to expand the Code mode editor. **CTRL/CMD + Return** serves as a keyboard shortcut to execute the query. + +{{< admonition type="warning" >}} +Changes made to a query in Code mode will not transfer to Builder mode and will be discarded. You will be prompted to copy your code to the clipboard to save any changes. +{{< /admonition >}} + +## Macros + +You can add macros to your queries to simplify the syntax and enable dynamic elements, such as date range filters. + +| Macro example | Description | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `$__time(dateColumn)` | Replaces the value with an expression to convert to a UNIX timestamp and renames the column to `time_sec`. Example: `UNIX_TIMESTAMP(dateColumn) AS time_sec`. | +| `$__timeEpoch(dateColumn)` | Replaces the value with an expression to convert to a UNIX Epoch timestamp and renames the column to `time_sec`. Example: `UNIX_TIMESTAMP(dateColumn) AS time_sec`. | +| `$__timeFilter(dateColumn)` | Replaces the value a time range filter using the specified column name. Example: `dateColumn BETWEEN FROM_UNIXTIME(1494410783) AND FROM_UNIXTIME(1494410983)` | +| `$__timeFrom()` | Replaces the value with the start of the currently active time selection. Example: `FROM_UNIXTIME(1494410783)` | +| `$__timeTo()` | Replaces the value with the end of the currently active time selection. Example: `FROM_UNIXTIME(1494410983)` | +| `$__timeGroup(dateColumn,'5m')` | Replaces the value with an expression suitable for use in a `GROUP BY` clause. Example: `cast(cast(UNIX_TIMESTAMP(dateColumn)/(300) AS signed)*300 AS signed)` | +| `$__timeGroup(dateColumn,'5m', 0)` | Same as the `$__timeGroup(dateColumn,'5m')` macro, but includes a fill parameter to ensure missing points in the series are added by Grafana, using 0 as the default value. **This applies only to time series queries.** | +| `$__timeGroup(dateColumn,'5m', NULL)` | Same as the `$__timeGroup(dateColumn,'5m', 0)` but `NULL` is used as the value for missing points. _This applies only to time series queries._ | +| `$__timeGroup(dateColumn,'5m', previous)` | Same as the `$__timeGroup(dateColumn,'5m', previous)` macro, but uses the previous value in the series as the fill value. If no previous value exists, it uses `NULL`. _This applies only to time series queries._ | +| `$__timeGroupAlias(dateColumn,'5m')` | Replaces the value identical to `$__timeGroup` but with an added column alias. | +| `$__unixEpochFilter(dateColumn)` | Replaces the value by a time range filter using the specified column name with times represented as a UNIX timestamp. Example: `dateColumn > 1494410783 AND dateColumn < 1494497183` | +| `$__unixEpochFrom()` | Replaces the value with the start of the currently active time selection as a UNIX timestamp. Example: `1494410783` | +| `$__unixEpochTo()` | Replaces the value with the end of the currently active time selection as a UNIX timestamp. Example: `1494497183` | +| `$__unixEpochNanoFilter(dateColumn)` | Replaces the value with a time range filter using the specified column name with time represented as a nanosecond timestamp. Example: `dateColumn > 1494410783152415214 AND dateColumn < 1494497183142514872` | +| `$__unixEpochNanoFrom()` | Replaces the value with the start of the currently active time selection as a nanosecond timestamp. Example: `1494410783152415214` | +| `$__unixEpochNanoTo()` | Replaces the value with the end of the currently active time selection as nanosecond timestamp. Example: `1494497183142514872` | +| `$__unixEpochGroup(dateColumn,'5m', [fillmode])` | Same as `$__timeGroup` but for times stored as Unix timestamp. `fillMode` only works with time series queries. | +| `$__unixEpochGroupAlias(dateColumn,'5m', [fillmode])` | Same as `$__timeGroup` but also adds a column alias. `fillMode` only works with time series queries. | + +## Table SQL queries + +If the **Format** option is set to **Table**, you can execute virtually any type of SQL query. The Table panel will automatically display the resulting columns and rows from your query. + +![Table query](/media/docs/postgres/PostgreSQL-query-editor-v11.4.png) + +You can change or customize the name of a Table panel column by using the SQL keyword `AS` syntax. + +```sql +SELECT + title as "Title", + "user".login as "Created By", + dashboard.created as "Created On" +FROM dashboard +INNER JOIN "user" on "user".id = dashboard.created_by +WHERE $__timeFilter(dashboard.created) +``` + +## Time series queries + +Set the **Format** option to **Time series** to create and run time series queries. + +{{< admonition type="note" >}} +To run a time series query you must include a column named `time` that returns either a SQL `datetime` value or a numeric datatype representing the UNIX epoch time in seconds. Additionally, the query results must be sorted by the `time` column for proper visualization in panels. +{{< /admonition >}} + +The examples in this section refer to the data in the following table: + +```text ++---------------------+--------------+---------------------+----------+ +| time_date_time | value_double | CreatedAt | hostname | ++---------------------+--------------+---------------------+----------+ +| 2020-01-02 03:05:00 | 3.0 | 2020-01-02 03:05:00 | 10.0.1.1 | +| 2020-01-02 03:06:00 | 4.0 | 2020-01-02 03:06:00 | 10.0.1.2 | +| 2020-01-02 03:10:00 | 6.0 | 2020-01-02 03:10:00 | 10.0.1.1 | +| 2020-01-02 03:11:00 | 7.0 | 2020-01-02 03:11:00 | 10.0.1.2 | +| 2020-01-02 03:20:00 | 5.0 | 2020-01-02 03:20:00 | 10.0.1.2 | ++---------------------+--------------+---------------------+----------+ +``` + +Time series query results are returned in [wide data frame format](https://grafana.com/developers/plugin-tools/key-concepts/data-frames#wide-format). In the data frame query result, any column, except for time or string-type columns, transforms into value fields. String columns, on the other hand, become field labels. + +{{< admonition type="note" >}} +For backward compatibility, an exception to this rule applies to queries that return three columns, one of which is a string column named `metric`. Instead of converting the metric column into field labels, it is used as the field name, while the series name is set to its value. See the following example for reference. +{{< /admonition >}} + +**Example with `metric` column:** + +```sql +SELECT + $__timeGroupAlias("time_date_time",'5m'), + min("value_double"), + 'min' as metric +FROM test_data +WHERE $__timeFilter("time_date_time") +GROUP BY time +ORDER BY time +``` + +Data frame result: + +```text ++---------------------+-----------------+ +| Name: time | Name: min | +| Labels: | Labels: | +| Type: []time.Time | Type: []float64 | ++---------------------+-----------------+ +| 2020-01-02 03:05:00 | 3 | +| 2020-01-02 03:10:00 | 6 | ++---------------------+-----------------+ +``` + +To customize default series name formatting, refer to [Standard options definitions](ref:configure-standard-options-display-name). + +Following are time series query examples. + +**Example using the fill parameter in the $\_\_timeGroupAlias macro to convert null values to be zero instead:** + +```sql +SELECT + $__timeGroupAlias("createdAt",'5m',0), + sum(value) as value, + hostname +FROM test_data +WHERE + $__timeFilter("createdAt") +GROUP BY time, hostname +ORDER BY time +``` + +Based on the data frame result in the following example, the time series panel will generate two series named _value 10.0.1.1_ and _value 10.0.1.2_. To display the series names as _10.0.1.1_ and _10.0.1.2_, use the [Standard options definitions](ref:configure-standard-options-display-name) display value `${__field.labels.hostname}`. + +Data frame result: + +```text ++---------------------+---------------------------+---------------------------+ +| Name: time | Name: value | Name: value | +| Labels: | Labels: hostname=10.0.1.1 | Labels: hostname=10.0.1.2 | +| Type: []time.Time | Type: []float64 | Type: []float64 | ++---------------------+---------------------------+---------------------------+ +| 2020-01-02 03:05:00 | 3 | 4 | +| 2020-01-02 03:10:00 | 6 | 7 | ++---------------------+---------------------------+---------------------------+ +``` + +**Example with multiple columns:** + +```sql +SELECT + $__timeGroupAlias("time_date_time",'5m'), + min("value_double") as "min_value", + max("value_double") as "max_value" +FROM test_data +WHERE $__timeFilter("time_date_time") +GROUP BY time +ORDER BY time +``` + +Data frame result: + +```text ++---------------------+-----------------+-----------------+ +| Name: time | Name: min_value | Name: max_value | +| Labels: | Labels: | Labels: | +| Type: []time.Time | Type: []float64 | Type: []float64 | ++---------------------+-----------------+-----------------+ +| 2020-01-02 03:04:00 | 3 | 4 | +| 2020-01-02 03:05:00 | 6 | 7 | ++---------------------+-----------------+-----------------+ +``` + +## Templating + +Instead of hard coding values like server, application, or sensor names in your metric queries, you can use variables. Variables appear as drop-down select boxes at the top of the dashboard. These drop-downs make it easy to change the data being displayed in your dashboard. + +Refer to [Templates](ref:templates) for an introduction to creating template variables as well as the different types. + +### Query variable + +If you add a `Query` template variable you can write a PostgreSQL query to retrieve items such as measurement names, key names, or key values, which will be displayed in the drop-down menu. + +For example, you can use a variable to retrieve all the values from the `hostname` column in a table by creating the following query in the templating variable _Query_ setting. + +```sql +SELECT hostname FROM host +``` + +A query can return multiple columns, and Grafana will automatically generate a list based on the query results. For example, the following query returns a list with values from `hostname` and `hostname2`. + +```sql +SELECT host.hostname, other_host.hostname2 FROM host JOIN other_host ON host.city = other_host.city +``` + +To use time range dependent macros like `$__timeFilter(column)` in your query, you must set the template variable's refresh mode to _On Time Range Change_. + +```sql +SELECT event_name FROM event_log WHERE $__timeFilter(time_column) +``` + +Another option is a query that can create a key/value variable. The query should return two columns that are named `__text` and `__value`. The `__text` column must contain unique values (if not, only the first value is used). This allows the drop-down options to display a text-friendly name as the text while using an ID as the value. For example, a query could use `hostname` as the text and `id` as the value: + +```sql +SELECT hostname AS __text, id AS __value FROM host +``` + +You can also create nested variables. For example, if you have a variable named `region`, you can configure the `hosts` variable to display only the hosts within the currently selected region as shown in the following example. If `region` is a multi-value variable, use the `IN` operator instead of `=` to match multiple values. + +```sql +SELECT hostname FROM host WHERE region IN($region) +``` + +#### Using `__searchFilter` to filter results in Query Variable + +Using `__searchFilter` in the query field allows the query results to be filtered based on the user’s input in the drop-down selection box. If you do not enter anything, the default value for `__searchFilter` is `%`. + +Note that you must enclose the `__searchFilter` expression in quotes as Grafana does not add them automatically. + +The following example demonstrates how to use `__searchFilter` in the query field to enable real-time searching for `hostname` as the user type in the drop-down selection box. + +```sql +SELECT hostname FROM my_host WHERE hostname LIKE '$__searchFilter' +``` + +### Using Variables in Queries + +Template variable values are only quoted when the template variable is a `multi-value`. + +If the variable is a multi-value variable, use the `IN` comparison operator instead of `=` to match against multiple values. + +You can use two different syntaxes: + +`$` Example with a template variable named `hostname`: + +```sql +SELECT + atimestamp as time, + aint as value +FROM table +WHERE $__timeFilter(atimestamp) and hostname in($hostname) +ORDER BY atimestamp ASC +``` + +`[[varname]]` Example with a template variable named `hostname`: + +```sql +SELECT + atimestamp as time, + aint as value +FROM table +WHERE $__timeFilter(atimestamp) and hostname in([[hostname]]) +ORDER BY atimestamp ASC +``` + +#### Disabling quoting for multi-value variables + +Grafana automatically formats multi-value variables as a quoted, comma-separated string. For example, if `server01` and `server02` are selected, they are formatted as `'server01'`, `'server02'`. To remove the quotes, enable the CSV formatting option for the variables: + +`${servers:csv}` + +Read more about variable formatting options in the [Variables](ref:variable-syntax-advanced-variable-format-options) documentation. + +## Annotations + +[Annotations](ref:annotate-visualizations) allow you to overlay rich event information on top of graphs. Add annotation queries via the **Dashboard settings > Annotations view**. + +**Example query using a `time` column with epoch values:** + +```sql +SELECT + epoch_time as time, + metric1 as text, + concat_ws(', ', metric1::text, metric2::text) as tags +FROM + public.test_data +WHERE + $__unixEpochFilter(epoch_time) +``` + +**Example region query using `time` and `timeend` columns with epoch values:** + +```sql +SELECT + epoch_time as time, + epoch_time_end as timeend, + metric1 as text, + concat_ws(', ', metric1::text, metric2::text) as tags +FROM + public.test_data +WHERE + $__unixEpochFilter(epoch_time) +``` + +**Example query using a `time` column with a native SQL date/time data type:** + +```sql +SELECT + native_date_time as time, + metric1 as text, + concat_ws(', ', metric1::text, metric2::text) as tags +FROM + public.test_data +WHERE + $__timeFilter(native_date_time) +``` + +| Name | Description | +| --------- | --------------------------------------------------------------------------------------------------------------------- | +| `time` | The name of the date/time field, which can be a column with a native SQL date/time data type or epoch value. | +| `timeend` | Optional name of the end date/time field, which can be a column with a native SQL date/time data type or epoch value. | +| `text` | Event description field. | +| `tags` | Optional field name to use for event tags as a comma-separated string. | + +## Alerting + +Use time series queries to create alerts. Table formatted queries aren't yet supported in alert rule conditions. + +For more information regarding alerting refer to the following: + +- [Alert rules](ref:alert-rules) +- [Template annotations and labels](ref:template-annotations-and-labels) diff --git a/docs/sources/datasources/prometheus/configure-prometheus-data-source.md b/docs/sources/datasources/prometheus/configure-prometheus-data-source.md index 077b6aeff04..98ef1608f60 100644 --- a/docs/sources/datasources/prometheus/configure-prometheus-data-source.md +++ b/docs/sources/datasources/prometheus/configure-prometheus-data-source.md @@ -48,13 +48,18 @@ Grafana ships with built-in support for Prometheus. If you are new to Prometheus ## Configure the data source +{{< shared id="add-prom-data-source" >}} + To add the Prometheus data source, complete the following steps: 1. Click **Connections** in the left-side menu. 1. Under **Connections**, click **Add new connection**. 1. Enter `Prometheus` in the search bar. -1. Select **Prometheus**. +1. Click **Prometheus data source**. 1. Click **Add new data source** in the upper right. +1. Enter a name for the data source. + +{{< /shared >}} You will be taken to the **Settings** tab where you will set up your Prometheus configuration. @@ -70,7 +75,8 @@ The first option to configure is the name of your connection: ### Connection section -- **Prometheus server URL** - The URL of your Prometheus server. If your Prometheus server is local, use `http://localhost:9090`. If it is on a server within a network, this is the URL with port where you are running Prometheus. Example: `http://prometheus.example.orgname:9090`. +- **Prometheus server URL** - The URL of your Prometheus server. {{< shared id="prom-data-source-url" >}} + If your Prometheus server is local, use `http://localhost:9090`. If it's on a server within a network, this is the URL with the port where you are running Prometheus. Example: `http://prometheus.example.orgname:9090`. {{< admonition type="note" >}} @@ -80,6 +86,8 @@ You should use the IP address of the Prometheus container, or the hostname if yo {{< /admonition >}} +{{< /shared >}} + ### Authentication section There are several authentication methods you can choose in the Authentication section. @@ -171,3 +179,22 @@ Support for exemplars is available only for the Prometheus data source. If this - **Label name** - The name of the field in the `labels` object used to obtain the traceID property. - **Remove exemplar link** - Click to remove existing links. + +### Troubleshooting + +Refer to the following troubleshooting information, as required. + +#### Data doesn't appear in Explore metrics + + + +If metric data doesn't appear in Explore after you've successfully tested a connection to a Prometheus data source or sent +metrics to Grafana Cloud, ensure that you've selected the correct data source in the **Data source** drop-down menu. If +you've used remote_write to send metrics to Grafana Cloud, the data source name follows the convention +`grafanacloud-stackname-prom`. + + + +The following image shows the **Data source** field in Explore metrics. + +![Image that shows Prometheus metrics in Explore](/media/docs/grafana/data-sources/prometheus/troubleshoot-connection-1.png) diff --git a/docs/sources/datasources/pyroscope/_index.md b/docs/sources/datasources/pyroscope/_index.md index 069902bfc0a..60efe8f3f33 100644 --- a/docs/sources/datasources/pyroscope/_index.md +++ b/docs/sources/datasources/pyroscope/_index.md @@ -56,7 +56,7 @@ To use profiling data, you should: - [Configure your application to send profiles](/docs/pyroscope//configure-client/) - [Configure the Grafana Pyroscope data source](./configure-pyroscope-data-source/). -- [View and query profiling data using Explore Profiles or the query editor ](./query-profile-data/) +- [View and query profiling data using Profiles Drilldown or the query editor ](./query-profile-data/) ## Continuous profiling diff --git a/docs/sources/datasources/pyroscope/configure-pyroscope-data-source.md b/docs/sources/datasources/pyroscope/configure-pyroscope-data-source.md index 8a344f6a7e6..0acfd01df3c 100644 --- a/docs/sources/datasources/pyroscope/configure-pyroscope-data-source.md +++ b/docs/sources/datasources/pyroscope/configure-pyroscope-data-source.md @@ -33,11 +33,11 @@ refs: destination: /docs/grafana//datasources/tempo/configure-tempo-data-source/ - pattern: /docs/grafana-cloud/ destination: docs/grafana-cloud/connect-externally-hosted/data-sources/tempo/configure-tempo-data-source/ - provisioning-data-sources: + explore-profiles: - pattern: /docs/grafana/ - destination: /docs/grafana//administration/provisioning/#data-sources - - pattern: /docs/grafana-cloud/provision - destination: /docs/grafana//administration/provisioning/#data-sources + destination: /docs/grafana//explore/simplified-exploration/profiles/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/simplified-exploration/profiles/ --- # Configure the Grafana Pyroscope data source @@ -73,7 +73,7 @@ To configure basic settings for the data source, complete the following steps: 1. On the **Settings** tab, complete the **Name**, **Connection**, and **Authentication** sections. - Use the **Name** field to specify the name used for the data source in panels, queries, and Explore. Toggle the **Default** switch for the data source to be pre-selected for new panels. -- Under **Connection**, enter the **URL** of the Pyroscope instance. For example, `https://example.com:4100`. +- Under **Connection**, enter the **URL** of the Pyroscope instance. For example, `https://example.com:4100`. Refer to [Connection URL](#connection-url) for more information. - Complete the [**Authentication** section](#authentication). 1. Optional: Use **Additional settings** to configure other options. @@ -89,6 +89,24 @@ To modify an existing Pyroscope data source: 1. Optional: Use **Additional settings** to configure or modify other options. 1. After completing your updates, select **Save & test**. +#### Connection URL + +The data source connection URL should point to a location of a running Pyroscope backend. + +**Grafana Cloud Profiles** + +Your Grafana Cloud instance automatically includes a fully provisioned data source. + +If you are running a self-managed Grafana instance or need to configure an additional Pyroscope data source pointing to Grafana Cloud Profiles, you can find the Pyroscope URL under the **Manage your stack** section for your organization. + +**Self-managed Pyroscope backend** + +The connection URL for a self-managed Pyroscope backend depends on how Pyroscope is deployed. +Refer to the steps under [Query profiles in Grafana](https://grafana.com/docs/pyroscope//deploy-kubernetes/helm/#query-profiles-in-grafana) for more information on how to configure the data source. + +If you plan to use the [Profiles Drilldown](ref:explore-profiles) application and you are running a self-managed Pyroscope backend in microservices mode, the data source connection URL should point to a gateway or proxy that routes requests to the corresponding Pyroscope service. +Refer to the [Helm ingress configuration](https://github.com/grafana/pyroscope/blob/main/operations/pyroscope/helm/pyroscope/templates/ingress.yaml) for specific routing requirements. + ## Authentication Use this section to select an authentication method to access the data source. diff --git a/docs/sources/datasources/pyroscope/query-profile-data.md b/docs/sources/datasources/pyroscope/query-profile-data.md index d56a629d42d..d0917ccfe12 100644 --- a/docs/sources/datasources/pyroscope/query-profile-data.md +++ b/docs/sources/datasources/pyroscope/query-profile-data.md @@ -1,5 +1,5 @@ --- -description: Explore your profiling data using Explore Profiles or the Pyroscope query editor. +description: Explore your profiling data using Profiles Drilldown or the Pyroscope query editor. keywords: - query - profiling @@ -33,7 +33,7 @@ refs: destination: /docs/grafana//explore/simplified-exploration/profiles/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/visualizations/simplified-exploration/profiles/ - explore-profile-install: + explore-profiles-install: - pattern: /docs/grafana/ destination: /docs/grafana//explore/simplified-exploration/profiles/access/ - pattern: /docs/grafana-cloud/ @@ -54,22 +54,22 @@ https://grafana.com/docs/grafana-cloud/visualizations/panels-visualizations/visu # Query profile data -You can query your profile data using the open source Grafana Explore Profiles app or the built-in Grafana Pyroscope data source query editor. +You can query your profile data using the open source Grafana Profiles Drilldown app or the built-in Grafana Pyroscope data source query editor. -- Explore Profiles provides a queryless experience for inspecting your profiling data with opinionated workflows to assist your investigation. +- Profiles Drilldown provides a queryless experience for inspecting your profiling data with opinionated workflows to assist your investigation. - Data source query editor provides complete control over your data exploration and is recommended for experienced users. In addition, you can embed flame graph panels in Grafana dashboards. Refer to the [Flame graph panel](ref:flame-graph-panel) documentation for details. -## Explore Profiles +## Profiles Drilldown -[Explore Profiles](ref:explore-profiles) is a native Grafana application designed to integrate seamlessly with Pyroscope, the open source continuous profiling platform, providing a smooth, queryless experience for browsing and analyzing profiling data. +[Grafana Profiles Drilldown](ref:explore-profiles) is a native Grafana application designed to integrate seamlessly with Pyroscope, the open source continuous profiling platform, providing a smooth, queryless experience for browsing and analyzing profiling data. -You can use Explore Profiles in Grafana Cloud or in your own Grafana instance. -For more information, refer to [Access or install Explore Profiles](ref:explore-profiles-install). +You can use Profiles Drilldown in Grafana Cloud or in your own Grafana instance. +For more information, refer to [Access or install Profiles Drilldown](ref:explore-profiles-install). -![Explore Profiles home screen](/media/docs/explore-profiles/explore-profiles-homescreen-v0.1.17.png) +![Profiles Drilldown home screen](/media/docs/explore-profiles/explore-profiles-homescreen-v0.1.17.png) ### Use cases @@ -80,10 +80,10 @@ The main use cases are the following: - Proactive: Cutting costs, addressing latency issues, or optimizing memory usage for applications - Reactive: Resolving incidents with line-level accuracy or debugging active latency/memory issues -Explore Profiles provides an intuitive interface to specifically support proactivee and reactive use cases. +Profiles Drilldown provides an intuitive interface to specifically support proactivee and reactive use cases. You get a holistic view of all of your services and how they're functioning, but also the ability to drill down for more targeted root cause analysis. -Explore Profiles offers a convenient platform to analyze profiles and get insights that are impossible to get from using other traditional signals like logs, metrics, or tracing. +Profiles Drilldown offers a convenient platform to analyze profiles and get insights that are impossible to get from using other traditional signals like logs, metrics, or tracing. {{< youtube id="x9aPw_CbIQc" >}} @@ -93,7 +93,7 @@ Explore Profiles offers a convenient platform to analyze profiles and get insigh The Pyroscope data source query editor gives you access to a profile type selector, a label selector, and collapsible options. -Like Explore Profiles, the query editor also provides a flame graph to visualize data. +Like Profiles Drilldown, the query editor also provides a flame graph to visualize data. ![Query editor](/media/docs/pyroscope/query-editor/query-editor.png 'Query editor') diff --git a/docs/sources/datasources/tempo/_index.md b/docs/sources/datasources/tempo/_index.md index 8b2123b5cd4..836db6cac14 100644 --- a/docs/sources/datasources/tempo/_index.md +++ b/docs/sources/datasources/tempo/_index.md @@ -71,7 +71,14 @@ refs: # Tempo data source -Grafana ships with built-in support for [Tempo](https://grafana.com/docs/tempo/latest/), a high-volume, minimal-dependency trace storage, open source tracing solution from Grafana Labs. This topic explains configuration and queries specific to the Tempo data source. +Grafana ships with built-in support for [Tempo](https://grafana.com/docs/tempo//), a high-volume, minimal-dependency trace storage, open source tracing solution from Grafana Labs. + +To learn more about traces, refer to [Introduction to tracing](https://grafana.com/docs/tempo//introduction/). + +To use traces, you need you have an application or service that is instrumented to emit traces. +Refer to the [Instrument for tracing](https://grafana.com/docs/tempo//getting-started/instrumentation/) for more information. + +## Add a data source For instructions on how to add a data source to Grafana, refer to the [administration documentation](ref:data-source-management). Only users with the organization administrator role can add data sources. @@ -81,6 +88,8 @@ This video explains how to add data sources, including Loki, Tempo, and Mimir, t {{< youtube id="cqHO0oYW6Ic" start="298" >}} -Once you've added the data source, you can [configure it]({{< relref "./configure-tempo-data-source/" >}}) so that your Grafana instance's users can create queries in its [query editor]({{< relref "./query-editor/" >}}) when they [build dashboards](ref:build-dashboards) and use [Explore](ref:explore). +## Learn more + +After you've added the data source, you can [configure it](./configure-tempo-data-source/) so that your Grafana instance's users can create queries in its [query editor](./query-editor/) when they [build dashboards](ref:build-dashboards) and use [Explore](ref:explore). {{< section withDescriptions="true">}} diff --git a/docs/sources/datasources/tempo/configure-tempo-data-source.md b/docs/sources/datasources/tempo/configure-tempo-data-source.md index 157238122f1..39d3cdf751a 100644 --- a/docs/sources/datasources/tempo/configure-tempo-data-source.md +++ b/docs/sources/datasources/tempo/configure-tempo-data-source.md @@ -131,7 +131,7 @@ To use streaming, you need to: - Run Tempo version 2.2 or later, or Grafana Enterprise Traces (GET) version 2.2 or later, or use Grafana Cloud Traces. - Tempo must have `stream_over_http_enabled: true` for streaming to work. - For more information, refer to [Tempo GRPC API](https://grafana.com/docs/tempo//api_docs/#tempo-grpc-api). + For more information, refer to [Tempo gRPC API](https://grafana.com/docs/tempo//api_docs/#tempo-grpc-api). - For self-managed Tempo or GET instances: If your Tempo or GET instance is behind a load balancer or proxy that doesn't supporting gRPC or HTTP2, streaming may not work and should be deactivated. @@ -333,6 +333,10 @@ You can choose one of three options: | **Duration** | _(Default)_ Displays the span duration on the span bar row. | | **Tag** | Displays the span tag on the span bar row. You must also specify which tag key to use to get the tag value, such as `component`. | +### Tag limit + +The **Tag limit** setting modifies the max number of tags and tag values to retrieve from Tempo. Default: 5000 + ### Private data source connect [//]: # 'Shared content for authentication section procedure in data sources' diff --git a/docs/sources/datasources/tempo/query-editor/_index.md b/docs/sources/datasources/tempo/query-editor/_index.md index b561a54bdcd..a6b85cfd436 100644 --- a/docs/sources/datasources/tempo/query-editor/_index.md +++ b/docs/sources/datasources/tempo/query-editor/_index.md @@ -61,7 +61,7 @@ The queries use [TraceQL](/docs/tempo/latest/traceql), the query language design For general documentation on querying data sources in Grafana, refer to [Query and transform data](ref:query-transform-data). {{< admonition type="tip" >}} -Don't know TraceQL? Try [Explore Traces](ref:explore-traces-app), an intuitive, queryless app that lets you explore your tracing data using RED metrics. +Don't know TraceQL? Try [Grafana Traces Drilldown](ref:explore-traces-app), an intuitive, queryless app that lets you explore your tracing data using RED metrics. {{< /admonition >}} ## Before you begin @@ -105,7 +105,7 @@ Refer to the [Search using the TraceQL query builder documentation]({{< relref " The **TraceQL** query editor lets you search by trace ID and write TraceQL queries using autocomplete. -Refer to the [TraceQL query editor documentation]({{< relref "./traceql-editor" >}}) to learn more about constructing queries using a code-editor-like experience. +Refer to the [TraceQL query editor documentation](./traceql-editor) to learn more about constructing queries using a code-editor-like experience. ![The TraceQL query editor](/media/docs/grafana/data-sources/tempo/query-editor/tempo-ds-query-traceql-v11.png) @@ -113,8 +113,8 @@ You can also search for a trace ID by entering it into the query field. ### Service graph view -Grafana’s **Service Graph** view uses metrics to display span request rates, error rates, and durations, as well as service graphs. -Once the requirements are set up, this preconfigured view is immediately available. +The **Service Graph** view uses metrics to display span request rates, error rates, and durations, as well as service graphs. +After the requirements are set up, this preconfigured view is immediately available. Using the service graph view, you can: diff --git a/docs/sources/datasources/tempo/query-editor/traceql-editor.md b/docs/sources/datasources/tempo/query-editor/traceql-editor.md index 7752a290229..cc5b38beaaa 100644 --- a/docs/sources/datasources/tempo/query-editor/traceql-editor.md +++ b/docs/sources/datasources/tempo/query-editor/traceql-editor.md @@ -12,7 +12,7 @@ labels: - oss menuTitle: Write TraceQL queries title: Write TraceQL queries with the editor -weight: 300 +weight: 400 refs: explore: - pattern: /docs/grafana/ @@ -41,4 +41,4 @@ refs: [//]: # 'Shared content for the TraceQL query editor' [//]: # 'This content is located in /docs/sources/shared/datasources/tempo-editor-traceql.md' -{{< docs/shared source="grafana" lookup="datasources/tempo-editor-traceql.md" version="" >}} +{{< docs/shared source="grafana" lookup="datasources/tempo-editor-traceql.md" version="" >}} diff --git a/docs/sources/datasources/tempo/query-editor/traceql-search.md b/docs/sources/datasources/tempo/query-editor/traceql-search.md index b885f1239d8..84ac0b396c4 100644 --- a/docs/sources/datasources/tempo/query-editor/traceql-search.md +++ b/docs/sources/datasources/tempo/query-editor/traceql-search.md @@ -56,4 +56,4 @@ To enable the TraceQL query builder in self-hosted Grafana through version 10.1, [//]: # 'Shared content for the Search - TraceQL query builder' -{{< docs/shared source="grafana" lookup="datasources/tempo-search-traceql.md" leveloffset="+1" version="" >}} +{{< docs/shared source="grafana" lookup="datasources/tempo-search-traceql.md" leveloffset="+1" version="" >}} diff --git a/docs/sources/datasources/tempo/query-editor/traceql-structure.md b/docs/sources/datasources/tempo/query-editor/traceql-structure.md new file mode 100644 index 00000000000..b67d57cedf3 --- /dev/null +++ b/docs/sources/datasources/tempo/query-editor/traceql-structure.md @@ -0,0 +1,21 @@ +--- +description: Learn how to create TraceQL queries are structured. +keywords: + - queries + - TraceQL +labels: + products: + - cloud + - enterprise + - oss +menuTitle: TraceQL query structure +title: TraceQL query structure +weight: 200 +--- + +# TraceQL query structure + +[//]: # 'Shared content for best practices for traces' +[//]: # 'This content is located in /tempo/docs/sources/shared/trace-structure.md' + +{{< docs/shared source="tempo" lookup="traceql-query-structure.md" version="" >}} diff --git a/docs/sources/datasources/tempo/service-graph.md b/docs/sources/datasources/tempo/service-graph.md index 0e6c1f8b657..d1644d41b73 100644 --- a/docs/sources/datasources/tempo/service-graph.md +++ b/docs/sources/datasources/tempo/service-graph.md @@ -72,8 +72,8 @@ You use the Service Graph to detect performance issues; track increases in error ## Display the Service Graph -1. [Configure Grafana Alloy](https://grafana.com/docs/tempo/latest/configuration/grafana-alloy/) or [Tempo or GET](https://grafana.com/docs/tempo/latest/metrics-generator/service_graphs/#tempo) to generate Service Graph data. -1. Link a Prometheus data source in the Tempo data source's [Service Graph]({{< relref "./configure-tempo-data-source#configure-service-graph" >}}) settings. +1. [Configure Grafana Alloy](https://grafana.com/docs/tempo//configuration/grafana-alloy/) or [Tempo or GET](https://grafana.com/docs/tempo//metrics-generator/service_graphs/#tempo) to generate Service Graph data. +1. Link a Prometheus data source in the Tempo data source's [Service Graph](./configure-tempo-data-source#configure-service-graph) settings. 1. Navigate to [Explore](ref:explore). 1. Select the Tempo data source. 1. Select the **Service Graph** query type. @@ -113,10 +113,10 @@ To open the Service Graph view: 1. Run the query. 1. _(Optional)_ Filter your results. -{{% admonition type="note" %}} +{{< admonition type="note" >}} Grafana uses the `traces_spanmetrics_calls_total` metric to display the name, rate, and error rate columns, and `traces_spanmetrics_latency_bucket` to display the duration column. These metrics must exist in your Prometheus data source. -{{% /admonition %}} +{{< /admonition >}} To open a query in Prometheus with the span name of that row automatically set in the query, click a row in the **rate**, **error rate**, or **duration** columns. diff --git a/docs/sources/datasources/tempo/traces-in-grafana/_index.md b/docs/sources/datasources/tempo/traces-in-grafana/_index.md new file mode 100644 index 00000000000..c3f622bb57d --- /dev/null +++ b/docs/sources/datasources/tempo/traces-in-grafana/_index.md @@ -0,0 +1,23 @@ +--- +description: Learn about how you can use tracing data in Grafana Cloud to query data, + generate metrics, and link your tracing data with logs, metrics, and profiles. +keywords: + - Grafana + - traces + - tracing +title: Use traces in Grafana +weight: 275 +--- + +# Use traces in Grafana + +Using traces, you can search for traces, generate metrics from spans, and link your tracing data with logs, metrics, and profiles. + +This page provides a summary of how you can use tracing data in Grafana. + +For general documentation on querying data sources in Grafana, refer to [Query and transform data](https://grafana.com/docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/). + +[//]: # 'Shared content for best practices for traces' +[//]: # 'This content is located in /tempo/docs/sources/shared/tempo-in-grafana.md' + +{{< docs/shared source="tempo" lookup="tempo-in-grafana.md" version="" >}} diff --git a/docs/sources/datasources/tempo/json-trace-file.md b/docs/sources/datasources/tempo/traces-in-grafana/json-trace-file.md similarity index 82% rename from docs/sources/datasources/tempo/json-trace-file.md rename to docs/sources/datasources/tempo/traces-in-grafana/json-trace-file.md index 0387eab883a..77b45011a2f 100644 --- a/docs/sources/datasources/tempo/json-trace-file.md +++ b/docs/sources/datasources/tempo/traces-in-grafana/json-trace-file.md @@ -12,7 +12,9 @@ labels: - oss menuTitle: Upload JSON trace file title: Upload a JSON trace file -weight: 400 +weight: 900 +aliases: + - ../json-trace-file/ # /docs/grafana/latest/datasources/tempo/json-trace-file --- # Upload a JSON trace file @@ -20,7 +22,15 @@ weight: 400 You can upload a JSON file that contains a single trace and visualize it. If the file has multiple traces, Grafana visualizes the first trace. -**To download a trace or Service Graph through the inspector:** +To upload a trace file: + +1. Select **Explore** in Grafana. +1. Select **Import trace** in the right corner. +1. Upload your JSON trace file. + +## Download a trace or service graph + +To download a trace or Service Graph through the [Inspector panel](https://grafana.com/docs/grafana//explore/explore-inspector/): 1. Open the inspector. 1. Navigate to the **Data** tab. diff --git a/docs/sources/datasources/tempo/link-trace-id.md b/docs/sources/datasources/tempo/traces-in-grafana/link-trace-id.md similarity index 87% rename from docs/sources/datasources/tempo/link-trace-id.md rename to docs/sources/datasources/tempo/traces-in-grafana/link-trace-id.md index d53496e3784..07c7f6f2bec 100644 --- a/docs/sources/datasources/tempo/link-trace-id.md +++ b/docs/sources/datasources/tempo/traces-in-grafana/link-trace-id.md @@ -12,7 +12,10 @@ labels: - oss menuTitle: Link to a trace ID title: Link to a trace ID -weight: 700 +weight: 800 +aliases: + - ../link-trace-id/ # /docs/grafana/latest/datasources/tempo/link-trace-id/ + refs: configure-grafana-feature-toggles: - pattern: /docs/grafana/ @@ -69,10 +72,10 @@ You can link to Tempo traces from logs or metrics. You can link to Tempo traces from logs in Loki, Elasticsearch, Splunk, and other logs data sources by configuring an internal link. -To configure this feature, see the [Derived fields]({{< relref "../loki#configure-derived-fields" >}}) section of the Loki data source docs or the [Data links]({{< relref "../elasticsearch#data-links" >}}) section of the Elasticsearch or Splunk data source docs. +To configure this feature, refer to the [Derived fields](../../loki#configure-derived-fields) section of the Loki data source docs or the [Data links](../../elasticsearch#data-links) section of the Elasticsearch or Splunk data source docs. ## Link to a trace ID from metrics You can link to Tempo traces from metrics in Prometheus data sources by configuring an exemplar. -To configure this feature, see the [introduction to exemplars](ref:exemplars) documentation. +To configure this feature, refer to the [Exemplars](ref:exemplars) documentation. diff --git a/docs/sources/developers/http_api/dashboard.md b/docs/sources/developers/http_api/dashboard.md index d37b0e080c9..54bfaf63156 100644 --- a/docs/sources/developers/http_api/dashboard.md +++ b/docs/sources/developers/http_api/dashboard.md @@ -139,7 +139,6 @@ The **412** status code is used for explaining that you cannot create the dashbo There can be different reasons for this: - The dashboard has been changed by someone else, `status=version-mismatch` -- A dashboard with the same name in the folder already exists, `status=name-exists` - A dashboard with the same uid already exists, `status=name-exists` - The dashboard belongs to plugin ``, `status=plugin-dashboard` @@ -156,8 +155,6 @@ Content-Length: 97 } ``` -In case of title already exists the `status` property will be `name-exists`. - ## Get dashboard by uid `GET /api/dashboards/uid/:uid` @@ -358,7 +355,7 @@ Content-Type: application/json Status Codes: -- **200** – Deleted +- **200** – Restored - **401** – Unauthorized - **403** – Access denied - **404** – Not found diff --git a/docs/sources/developers/http_api/team.md b/docs/sources/developers/http_api/team.md index f7eec3d6260..e94d80be650 100644 --- a/docs/sources/developers/http_api/team.md +++ b/docs/sources/developers/http_api/team.md @@ -26,8 +26,6 @@ Access to these API endpoints is restricted as follows: - All authenticated users are able to view details of teams they are a member of. - Organization Admins are able to manage all teams and team members. -- If you enable `editors_can_admin` configuration flag, then Organization Editors can create teams and manage teams where they are Admin. - - If you enable `editors_can_admin` configuration flag, Editors can find out whether a team that they are not members of exists by trying to create a team with the same name. > If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/access-control/custom-role-actions-scopes" >}}) for more information. @@ -179,7 +177,7 @@ Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt HTTP/1.1 200 Content-Type: application/json -{"message":"Team created","teamId":2} +{"message":"Team created","teamId":2,"uid":"ceaulqadfoav4e"} ``` Status Codes: diff --git a/docs/sources/explore/correlations-editor-in-explore.md b/docs/sources/explore/correlations-editor-in-explore.md index 50053226158..dd0a0ce1936 100644 --- a/docs/sources/explore/correlations-editor-in-explore.md +++ b/docs/sources/explore/correlations-editor-in-explore.md @@ -3,6 +3,7 @@ labels: products: - enterprise - oss + - cloud title: Correlations Editor in Explore weight: 20 --- @@ -13,22 +14,22 @@ weight: 20 The Explore editor is available in 10.1 and later versions. In the editor, transformations is available in Grafana 10.3 and later versions. {{% /admonition %}} -Correlations allow users to build a link between any two data sources. For more information about correlations in general, please see the [correlations]({{< relref "../administration/correlations" >}}) topic in the administration page. +Correlations allow users to build a link between any two data sources. For more information about correlations in general, please see the [correlations](/docs/grafana//administration/correlations/) topic in the administration page. ## Create a correlation 1. In Grafana, navigate to the Explore page. -1. Select a data source that you would like to be [the source data source]({{< relref "../administration/correlations/correlation-configuration#source-data-source-and-result-field" >}}) for a new correlation. -1. Run a query producing data in [a supported visualization]({{< relref "../administration/correlations#correlations" >}}). -1. Click **+ Add** in the top toolbar and select **Add correlation** (you can also select **Correlations Editor** from the [Command Palette]({{< relref "../search#command-palette" >}})). +1. Select a data source that you would like to be [the source data source](/docs/grafana//administration/correlations/correlation-configuration/#source-data-source-and-result-field) for a new correlation. +1. Run a query producing data in [a supported visualization](/docs/grafana//administration/correlations/#correlations). +1. Click **+ Add** in the top toolbar and select **Add correlation** (you can also select **Correlations Editor** from the [Command Palette](/docs/grafana//search/#command-palette)). 1. Explore is now in Correlations Editor mode indicated by a blue border and top bar. You can exit Correlations Editor by clicking **Exit** in the top bar. 1. You can now create the following new correlations for the visualization with links that are attached to the data that you can use to build a new query: - Logs: links are displayed next to field values inside log details for each log row - Table: every table cell is a link 1. Click on a link to add a new correlation. - Links are associated with a field that is used as a [result field of a correlation]({{< relref "../administration/correlations/correlation-configuration" >}}). -1. In the split view that opens, use the right pane to set up [the target query source of the correlation]({{< relref "../administration/correlations/correlation-configuration#target-query" >}}). -1. Build a target query using [variables syntax]({{< relref "../dashboards/variables/variable-syntax" >}}) with variables from the list provided at the top of the pane. The list contains sample values from the selected data row. + Links are associated with a field that is used as a [result field of a correlation](/docs/grafana//administration/correlations/correlation-configuration/). +1. In the split view that opens, use the right pane to set up [the target query source of the correlation](/docs/grafana//administration/correlations/correlation-configuration/#target-query). +1. Build a target query using [variables syntax](/docs/grafana//dashboards/variables/variable-syntax/) with variables from the list provided at the top of the pane. The list contains sample values from the selected data row. 1. Provide a label and description (optional). A label will be used as the name of the link inside the visualization and can contain variables. 1. Provide transformations (optional; see below for details). @@ -37,7 +38,7 @@ Correlations allow users to build a link between any two data sources. For more ## Transformations -Transformations allow you to extract values that exist in a field with other data. For example, using a transformation, you can extract one portion of a log line to use in a correlation. For more details on transformations in correlations, see [Correlations]({{< relref "../administration/correlations/correlation-configuration/#correlation-transformations" >}}). +Transformations allow you to extract values that exist in a field with other data. For example, using a transformation, you can extract one portion of a log line to use in a correlation. For more details on transformations in correlations, see [Correlations](/docs/grafana//explore/correlations-editor-in-explore/#transformations). After clicking one of the generated links in the editor mode, you can add transformations by clicking **Add transformation** in the Transformations dropdown menu. @@ -47,7 +48,7 @@ You can use a transformation in your correlation with the following steps: Select the portion of the field that you want to use for the transformation. For example, a log line. Once selected, the value of this field will be used to assist you in building the transformation. 1. Select the type of the transformation. - See [correlations]({{< relref "../administration/correlations/correlation-configuration/#correlation-transformations" >}}) for the options and relevant settings. + See [correlations](/docs/grafana//explore/correlations-editor-in-explore/#transformations) for the options and relevant settings. 1. Based on your selection, you might see one or more variables populate, or you might need to provide more specifications in options that are displayed. 1. Select **Add transformation to correlation** to add the specified variables to the list of available variables. @@ -57,7 +58,7 @@ For regular expressions in this dialog box, the `mapValue` referred to in other ## Correlations examples -The following examples show how to create correlations using the Correlations Editor in Explore. If you'd like to follow these examples, make sure to set up a [test data source]({{< relref "../datasources/testdata#testdata-data-source" >}}). +The following examples show how to create correlations using the Correlations Editor in Explore. If you'd like to follow these examples, make sure to set up a [test data source](/docs/grafana//datasources/testdata/#testdata-data-source). ### Create a text to graph correlation @@ -65,7 +66,7 @@ This example shows how to create a correlation using Correlations Editor in Expl Correlations allow you to use results of one query to run a new query in any data source. In this example, you will run a query that renders tabular data. The data will be used to run a different query that yields a graph result. -To follow this example, make sure you have set up [a test data source]({{< relref "../datasources/testdata#testdata-data-source" >}}). +To follow this example, make sure you have set up [a test data source](/docs/grafana//datasources/testdata/#testdata-data-source). 1. In Grafana, navigate to **Explore**. 1. Select the **test data source** from the dropdown menu at the top left of the page. @@ -100,7 +101,7 @@ You can apply the same steps to any data source. Correlations allow you to creat In this example, you will create a correlation to demonstrate how to use transformations to extract values from the log line and another field. -To follow this example, make sure you have set up [a test data source]({{< relref "../datasources/testdata#testdata-data-source" >}}). +To follow this example, make sure you have set up [a test data source](/docs/grafana//datasources/testdata/#testdata-data-source). 1. In Grafana, navigate to **Explore**. 1. Select the **test data source** from the dropdown menu at the top left of the page. diff --git a/docs/sources/explore/get-started-with-explore.md b/docs/sources/explore/get-started-with-explore.md index 7e78e4e80c3..b25f855bac9 100644 --- a/docs/sources/explore/get-started-with-explore.md +++ b/docs/sources/explore/get-started-with-explore.md @@ -38,14 +38,10 @@ Watch the following video to get started using Explore: ## Before you begin -In order to access Explore, you must have either the `editor` or `administrator` role, unless the [`viewers_can_edit` option](https://grafana.com/docs/grafana//setup-grafana/configure-grafana/#viewers_can_edit) is enabled. Refer to [Role and permissions](https://grafana.com/docs/grafana//administration/roles-and-permissions/) for more information on what each role can access. +In order to access Explore, you must have either the `editor` or `administrator` basic role or the `data sources explore` role. Refer to [Role and permissions](https://grafana.com/docs/grafana//administration/roles-and-permissions/) for more information on what each role can access. Refer to [Role-based access control (RBAC)](https://grafana.com/docs/grafana//administration/roles-and-permissions/access-control/) in Grafana Enterprise to understand how you can manage Explore with role-based permissions. -{{< admonition type="note" >}} -If you are using Grafana Cloud, open a [support ticket in the Cloud Portal](https://grafana.com/auth/sign-in) to enable the `viewers_can_edit` option. -{{< /admonition >}} - ## Explore elements Explore consists of a toolbar, outline, query editor, the ability to add multiple queries, a query history and a query inspector. diff --git a/docs/sources/explore/simplified-exploration/_index.md b/docs/sources/explore/simplified-exploration/_index.md index 60612ae02ba..360b37aee5a 100644 --- a/docs/sources/explore/simplified-exploration/_index.md +++ b/docs/sources/explore/simplified-exploration/_index.md @@ -8,27 +8,27 @@ title: Simplified exploration menuTitle: Simplified exploration weight: 100 hero: - title: Simplified exploration with the Explore apps + title: Simplified exploration with the Drilldown apps level: 1 width: 100 height: 100 - description: Use Explore Profiles to investigate and identify issues using profiling data. + description: Use the Grafana Drilldown apps to investigate and identify issues using telemetry data. cards: title_class: pt-0 lh-1 items: - - title: Explore Metrics + - title: Grafana Metrics Drilldown href: ./metrics/ description: Quickly find related metrics with a few clicks, without needing to write PromQL queries to retrieve metrics. height: 24 - - title: Explore Logs + - title: Grafana Logs Drilldown href: ./logs/ description: Visualize log volumes to easily detect anomalies or significant changes over time, without needing to compose LogQL queries. height: 24 - - title: Explore Traces + - title: Grafana Traces Drilldown href: ./traces/ description: Use Rate, Errors, and Duration (RED) metrics derived from traces to investigate and understand errors and latency issues within complex distributed systems. height: 24 - - title: Explore Profiles + - title: Grafana Profiles Drilldown href: ./profiles/ description: View and analyze high-level service performance, identify problem processes for optimization, and diagnose issues to determine root causes. height: 24 @@ -36,14 +36,16 @@ cards: # Simplified exploration -The Grafana Explore apps are designed for effortless data exploration through intuitive, queryless interactions. +The Grafana Drilldown apps are designed for effortless data exploration through intuitive, queryless interactions. Easily explore telemetry signals with these specialized tools, tailored specifically for the Grafana databases to provide quick and accurate insights. +{{< docs/shared source="grafana" lookup="plugins/rename-note.md" version="" >}} + To learn more, read: - [From multi-line queries to no-code investigations: meeting Grafana users where they are](https://grafana.com/blog/2024/10/22/from-multi-line-queries-to-no-code-investigations-meeting-grafana-users-where-they-are/) -- [A queryless experience for exploring metrics, logs, traces, and profiles: Introducing the Explore apps suite for Grafana](https://grafana.com/blog/2024/09/24/queryless-metrics-logs-traces-profiles/). +- [A queryless experience for exploring metrics, logs, traces, and profiles: Introducing the Drilldown apps suite for Grafana](https://grafana.com/blog/2024/09/24/queryless-metrics-logs-traces-profiles/). {{< youtube id="MSHeWWsHaIA" >}} diff --git a/docs/sources/explore/simplified-exploration/metrics/index.md b/docs/sources/explore/simplified-exploration/metrics/index.md index 127dd6cb265..eb6d23509e5 100644 --- a/docs/sources/explore/simplified-exploration/metrics/index.md +++ b/docs/sources/explore/simplified-exploration/metrics/index.md @@ -4,19 +4,21 @@ labels: - cloud - enterprise - oss -title: Explore Metrics +title: Metrics Drilldown aliases: - ../explore-metrics/ # /docs/grafana/latest/explore/explore-metrics/ canonical: https://grafana.com/docs/grafana/latest/explore/simplified-exploration/metrics/ -description: Explore Metrics lets you browse Prometheus-compatible metrics using an intuitive, queryless experience. +description: Grafana Metrics Drilldown lets you browse Prometheus-compatible metrics using an intuitive, queryless experience. weight: 200 --- -# Grafana Explore Metrics +# Grafana Metrics Drilldown -Grafana Explore Metrics is a query-less experience for browsing **Prometheus-compatible** metrics. Quickly find related metrics with just a few simple clicks, without needing to write PromQL queries to retrieve metrics. +Grafana Metrics Drilldown is a query-less experience for browsing **Prometheus-compatible** metrics. Quickly find related metrics with just a few simple clicks, without needing to write PromQL queries to retrieve metrics. -With Explore Metrics, you can: +{{< docs/shared source="grafana" lookup="plugins/rename-note.md" version="" >}} + +With Metrics Drilldown, you can: - Easily segment metrics based on their labels, so you can immediately spot anomalies and identify issues. - Automatically display the optimal visualization for each metric type (gauge vs. counter, for example) without manual setup. @@ -25,26 +27,25 @@ With Explore Metrics, you can: - View a history of user steps when navigating through metrics and their filters. - Seamlessly pivot to related telemetry, including log data. -{{< docs/play title="Explore Metrics" url="https://play.grafana.org/explore/metrics/trail?from=now-1h&to=now&var-ds=grafanacloud-demoinfra-prom&var-filters=&refresh=&metricPrefix=all" >}} +{{< docs/play title="Metrics Drilldown" url="https://play.grafana.org/explore/metrics/trail?from=now-1h&to=now&var-ds=grafanacloud-demoinfra-prom&var-filters=&refresh=&metricPrefix=all" >}} -You can access Explore Metrics either as a standalone experience or as part of Grafana dashboards. +You can access Metrics Drilldown either as a standalone experience or as part of Grafana dashboards. ## Standalone experience -To access Explore Metrics as a standalone experience: +To access Metrics Drilldown as a standalone experience: -1. Click the arrow next to **Explore** in the Grafana left-side menu and click **Metrics**. You are taken to an overview page that shows recent metrics, bookmarks, and the option to select a new metric exploration. -1. To get started with a new exploration, click **+ New metric exploration**. +1. Click the arrow next to **Drilldown** in the Grafana left-side menu and click **Metrics**. You are taken to an overview page that shows recent metrics, bookmarks, and the option to select a new metric exploration. +1. To get started with a new exploration, click **Let's start!**. 1. Select **Prometheus** or any Prometheus-compatible data source available in the drop-down menu under **Data source**. -1. Click **+ Add label** to select a label-value pair from the drop-down menu. You can add multiple label-value pairs. A label type will appear above the selected label with a drop-down list of options from which to choose. For example, if you select the label `container` a drop-down list of available containers appears. +1. Click **+ Add label** to select a label-value pair from the drop-down menu. You can add multiple label-value pairs. A label type appears above the selected label with a drop-down list of options from which to choose. For example, if you select the label `container` a drop-down list of available containers appears. 1. You can also search for metrics using keywords under **Search metrics** in the search bar. 1. Use the time picker to select a date and time range from the drop-down menu or use an absolute time range. 1. Click the down arrow next to the **Refresh** icon to set a refresh rate from the drop-down menu. The default is `Off`. -1. Click the **Settings** icon and toggle **Always keep selected metric graph in-view** to keep your main graph always in view on the Breakdown drill-down tab. The **History** button in the upper left corner tracks every step navigating through metric exploration. -![show metrics explore overview](/media/metrics-explore/metrics-explore-overview.png) +![show metrics explore overview](/media/metrics-explore/metrics-drilldown-overview.png) ### Metrics exploration @@ -54,19 +55,19 @@ To further explore a metric, click **Select** in the upper right corner of the m - The **Overview** tab provides a description for each metric, as well as the metric `type` and `unit` associated with the metric. It also provides a list of labels associated with the metric. Click on any label to view drill-down visualizations. - The **Breakdown** tab depicts time series visualizations for each of the label-value pairs for the selected metric. You can further drill down on each label and click **Add to filter** to add the label/value pair into your filters. You can also change the **View** from grid to rows. -- The **Related metrics** tab depicts related metrics with relevant key words. You can repeat the drill down process for any related metric. Toggle **SHow previews** to preview visualizations. +- The **Related metrics** tab depicts related metrics with relevant key words. You can repeat the drill down process for any related metric. Toggle **Show previews** to preview visualizations. -Once you have gathered your metrics exploration data you can: +After you have gathered your metrics exploration data you can: -- Click the **Explore** icon on the right side to open the graph in Explore, where you can modify the query or add the graph to a dashboard or incident. -- Click the **Share** icon on the right side to copy the metric drill down URL to the clipboard so it can be shared. +- Click the **Open in Explore** icon on the right side to open the graph in Explore, where you can modify the query or add the graph to a dashboard or incident. +- Click the **Copy URL** icon on the right side to copy the metric drill down URL to the clipboard so it can be shared. - Click the **Star** icon on the right side to bookmark and save the metrics exploration. ## Dashboard experience -To access Explore Metrics via a dashboard: +To access Metrics Drilldown via a dashboard: 1. Navigate to your dashboard. 1. Select a time series panel. -1. Click the panel menu in the upper right and select **Explore Metrics**. If there are multiple metrics, click on the one you want to explore. -1. You will see a slide out drawer with the Metrics Experience, starting with the drill down. You can access the standalone experience by clicking **Open** in the upper right. +1. Click the panel menu in the upper right and select **Metrics Drilldown**. If there are multiple metrics, click on the one you want to explore. +1. You see a slide out drawer with the Metrics Experience, starting with the drill down. You can access the standalone experience by clicking **Open** in the upper right. diff --git a/docs/sources/explore/trace-integration.md b/docs/sources/explore/trace-integration.md index f9b0779b12d..532bb3ca8f2 100644 --- a/docs/sources/explore/trace-integration.md +++ b/docs/sources/explore/trace-integration.md @@ -157,7 +157,8 @@ The node graph requires data to be returned from the data source in a specific f ## Service graph -A service graph visualizes span metrics, including rates, error rates, and durations (RED), along with service relationships. Once the requirements are configured, this pre-configured view is immediately available. +A service graph visualizes rates, error rates, and durations (RED), along with service relationships. +After the requirements are configured, this pre-configured view is immediately available. For additional information refer to the following documentation: diff --git a/docs/sources/fundamentals/exemplars/index.md b/docs/sources/fundamentals/exemplars/index.md index 363bc122b47..4181c50cac5 100644 --- a/docs/sources/fundamentals/exemplars/index.md +++ b/docs/sources/fundamentals/exemplars/index.md @@ -20,21 +20,31 @@ weight: 800 # Introduction to exemplars -An exemplar is a specific trace representative of measurement taken in a given time interval. While metrics excel at giving you an aggregated view of your system, traces give you a fine grained view of a single request; exemplars are a way to link the two. +An exemplar is a specific trace representative of measurement taken in a given time interval. +While metrics excel at giving you an aggregated view of your system, traces give you a fine grained view of a single request; exemplars are a way to link the two. -Suppose your company website is experiencing a surge in traffic volumes. While more than eighty percent of the users are able to access the website in under two seconds, some users are experiencing a higher than normal response time resulting in bad user experience. +Suppose your company website is experiencing a surge in traffic volumes. +While more than eighty percent of the users are able to access the website in under two seconds, some users are experiencing a higher than normal response time resulting in bad user experience. -To identify the factors that are contributing to the latency, you must compare a trace for a fast response against a trace for a slow response. Given the vast amount of data in a typical production environment, it will be extremely laborious and time-consuming effort. +To identify the factors that are contributing to the latency, you must compare a trace for a fast response against a trace for a slow response. +Given the vast amount of data in a typical production environment, it's an extremely laborious and time-consuming effort. -Use exemplars to help isolate problems within your data distribution by pinpointing query traces exhibiting high latency within a time interval. Once you localize the latency problem to a few exemplar traces, you can combine it with additional system based information or location properties to perform a root cause analysis faster, leading to quick resolutions to performance issues. +Use exemplars to help isolate problems within your data distribution by pinpointing query traces exhibiting high latency within a time interval. +After you localize the latency problem to a few exemplar traces, you can combine it with additional system based information or location properties to perform a root cause analysis faster, leading to quick resolutions to performance issues. -Support for exemplars is available for the Prometheus data source only. Once you enable the functionality, exemplar data is available by default. For more information on exemplar configuration and how to enable exemplars, refer to [configuring exemplars in the Prometheus data source]({{< relref "../../datasources/prometheus/configure-prometheus-data-source#exemplars" >}}). +Support for exemplars is available for the Prometheus data source only. +After you enable the functionality, exemplar data is available by default. +For more information on exemplar configuration and how to enable exemplars, refer to [configuring exemplars in the Prometheus data source](../../datasources/prometheus/configure-prometheus-data-source/#exemplars). -Grafana shows exemplars alongside a metric in the Explore view and in dashboards. Each exemplar displays as a highlighted star. You can hover your cursor over an exemplar to view the unique trace ID, which is a combination of a key value pair. To investigate further, click the blue button next to the `traceID` property. +Grafana shows exemplars alongside a metric in the Explore view and in dashboards. +Each exemplar displays as a highlighted star. +You can hover your cursor over an exemplar to view the unique trace ID, which is a combination of a key value pair. +To investigate further, click the blue button next to the `traceID` property. {{< figure src="/media/docs/grafana/exemplars/screenshot-exemplars.png" class="docs-image--no-shadow" max-width= "750px" caption="Screenshot showing the detail window of an exemplar" >}} -Refer to [View exemplar data]({{< relref "#view-exemplar-data" >}}) for instructions on how to drill down and view exemplar trace details from metrics and logs. To know more about exemplars, refer to the blogpost [Intro to exemplars, which enable Grafana Tempo’s distributed tracing at massive scale](/blog/2021/03/31/intro-to-exemplars-which-enable-grafana-tempos-distributed-tracing-at-massive-scale/). +Refer to [View exemplar data](#view-exemplar-data) for instructions on how to drill down and view exemplar trace details from metrics and logs. +To know more about exemplars, refer to the blog post [Intro to exemplars, which enable Grafana Tempo’s distributed tracing at massive scale](/blog/2021/03/31/intro-to-exemplars-which-enable-grafana-tempos-distributed-tracing-at-massive-scale/). ## View exemplar data @@ -42,15 +52,19 @@ When support for exemplar support is enabled for a Prometheus data source, you c ### In Explore -Explore visualizes exemplar traces as highlighted stars alongside metrics data. For more information on how Explore visualizes trace data, refer to [Tracing in Explore]({{< relref "../../explore/trace-integration" >}}). +Explore visualizes exemplar traces as highlighted stars alongside metrics data. +For more information on how Explore visualizes trace data, refer to [Tracing in Explore](../../explore/trace-integration/). To examine the details of an exemplar trace: -1. Place your cursor over an exemplar (highlighted star). Depending on the trace data source you are using, you will see a blue button with the label `Query with `. In the following example, the tracing data source is Tempo. +1. Place your cursor over an exemplar (highlighted star). + Depending on the trace data source you are using, you may see a blue button with the label `Query with `. + In the following example, the tracing data source is Tempo. {{< figure src="/media/docs/grafana/exemplars/screenshot-exemplar-details.png" class="docs-image--no-shadow" max-width= "350px" caption="Screenshot showing exemplar details" >}} -1. Click the **Query with Tempo** option next to the `traceID` property. The trace details, including the spans within the trace are listed in a separate panel on the right. +1. Click the **Query with Tempo** option next to the `traceID` property. + The trace details, including the spans within the trace are listed in a separate panel on the right. {{< figure src="/media/docs/grafana/exemplars/screenshot-exemplar-explore-view.png" class="docs-image--no-shadow" max-width= "900px" caption="Explorer view with panel showing trace details" >}} @@ -58,13 +72,20 @@ For more information on how to drill down and analyze the trace and span details ### In logs -You can also view exemplar trace details from the Loki logs in Explore. Use regex within the Derived fields links for Loki to extract the `traceID` information. Now when you expand Loki logs, you can see a `traceID` property under the **Detected fields** section. To learn more about how to extract a part of a log message into an internal or external link, refer to [using derived fields in Loki]({{< relref "../../explore/logs-integration" >}}). +You can also view exemplar trace details from the Loki logs in Explore. +Use regular expressions within the Derived fields links for Loki to extract the `traceID` information. +Now when you expand Loki logs, you can see a `traceID` property under the **Detected fields** section. +To learn more about how to extract a part of a log message into an internal or external link, refer to [using derived fields in Loki](../../explore/logs-integration/). To view the details of an exemplar trace: -1. Expand a log line and scroll down to the `Fields` section. Depending on your backend trace data source, you will see a blue button with the label ``. +1. Expand a log line and scroll down to the `Fields` section. + Depending on your backend trace data source, you may see a blue button with the label ``. -1. Click the blue button next to the `traceID` property. Typically, it will have the name of the backend data source. In the following example, the tracing data source is Tempo. The trace details, including the spans within the trace are listed in a separate panel on the right. +1. Click the blue button next to the `traceID` property. + Typically, it has the name of the backend data source. + In the following example, the tracing data source is Tempo. + The trace details, including the spans within the trace are listed in a separate panel on the right. {{< figure src="/media/docs/grafana/exemplars/screenshot-exemplar-loki-logs.png" class="docs-image--no-shadow" max-width= "750px" caption="Explorer view with panel showing trace details" >}} @@ -78,16 +99,20 @@ This panel shows the details of the trace in different segments. You can add more traces to the results using the `Add query` button. -- The next segment shows the entire span for the specific trace as a narrow strip. All levels of the trace from the client all the way down to database query is displayed, which provides a bird's eye view of the time distribution across all layers over which the HTTP request was processed. +- The next segment shows the entire span for the specific trace as a narrow strip. + All levels of the trace from the client all the way down to database query is displayed, which provides a bird's eye view of the time distribution across all layers over which the HTTP request was processed. 1. You can click within this strip view to display a magnified view of a smaller time segment within the span. This magnified view shows up in the bottom segment of the panel. 1. In the magnified view, you can expand or collapse the various levels of the trace to drill down to the specific span of interest. - For example, if the strip view shows that most of the latency was within the app layer, you can expand the trace down the app layer to investigate the problem further. To expand a particular layer of span, click the left icon. The same button can collapse an expanded span. + For example, if the strip view shows that most of the latency was within the app layer, you can expand the trace down the app layer to investigate the problem further. + To expand a particular layer of span, click the left icon. + The same button can collapse an expanded span. - To see the details of the span at any level, click the span itself. - This displays additional metadata associated with the span. The metadata itself is initially shown in a narrow strip but you can see more details by clicking the metadata strip. + This displays additional metadata associated with the span. + The metadata itself is initially shown in a narrow strip but you can see more details by clicking the metadata strip. {{< figure src="/media/docs/grafana/exemplars/screenshot-exemplar-span-details.png" class="docs-image--no-shadow" max-width= "600px" caption="Span details" >}} diff --git a/docs/sources/getting-started/get-started-grafana-ms-sql-server.md b/docs/sources/getting-started/get-started-grafana-ms-sql-server.md index 2ca4dcac2b4..4518d2adfc3 100644 --- a/docs/sources/getting-started/get-started-grafana-ms-sql-server.md +++ b/docs/sources/getting-started/get-started-grafana-ms-sql-server.md @@ -24,7 +24,7 @@ MS SQL Server can be installed on Windows or Linux operating systems and also on You can install MS SQL Server on the host running Grafana or on a remote server. To install the software from the [downloads page](https://www.microsoft.com/en-us/sql-server/sql-server-downloads), follow their setup prompts. -If you are on a Windows host but want to use Grafana and MS SQL data source on a Linux environment, refer to the [WSL to set up your Grafana development environment](/blog/2021/03/03/.how-to-set-up-a-grafana-development-environment-on-a-windows-pc-using-wsl). This will allow you to leverage the resources available in [grafana/grafana](https://github.com/grafana/grafana) GitHub repository. Here you will find a collection of supported data sources, including MS SQL Server, along with test data and pre-configured dashboards for use. +If you are on a Windows host but want to use Grafana and MS SQL data source on a Linux environment, refer to the [WSL to set up your Grafana development environment](/blog/2021/03/03/how-to-set-up-a-grafana-development-environment-on-a-windows-pc-using-wsl) blog post. This will allow you to leverage the resources available in [grafana/grafana](https://github.com/grafana/grafana) GitHub repository. Here you will find a collection of supported data sources, including MS SQL Server, along with test data and pre-configured dashboards for use. #### Add the MS SQL data source diff --git a/docs/sources/introduction/grafana-enterprise.md b/docs/sources/introduction/grafana-enterprise.md index 7de85993dc6..15db695b9bc 100644 --- a/docs/sources/introduction/grafana-enterprise.md +++ b/docs/sources/introduction/grafana-enterprise.md @@ -13,7 +13,7 @@ weight: 200 Grafana Enterprise is a commercial edition of Grafana that includes additional features not found in the open source version. -Building on everything you already know and love about Grafana open source, Grafana Enterprise includes [exclusive datasource plugins]({{< relref "#enterprise-data-sources" >}}) and [additional features]({{< relref "#enterprise-features" >}}). You also get 24x7x365 support and training from the core Grafana team. +Building on everything you already know and love about Grafana open source, Grafana Enterprise includes [exclusive data source plugins](#enterprise-data-sources) and [additional features](#enterprise-features). You also get 24x7x365 support and training from the core Grafana team. To learn more about Grafana Enterprise, refer to [our product page](/enterprise). @@ -21,7 +21,7 @@ To learn more about Grafana Enterprise, refer to [our product page](/enterprise) Many Grafana Enterprise features are also available in [Grafana Cloud](/docs/grafana-cloud) Free, Pro, and Advanced accounts. For details, refer to [Grafana Cloud pricing](/pricing/#featuresTable). -To migrate to Grafana Cloud, refer to [Migrate from Grafana Enterprise to Grafana Cloud](/docs/grafana-cloud/account-management/e2c-guide/). +To migrate to Grafana Cloud, refer to [Migrate from Grafana Enterprise to Grafana Cloud](/docs/grafana//administration/migration-guide/) ## Authentication @@ -29,48 +29,48 @@ Grafana Enterprise includes integrations with more ways to authenticate your use ### Team sync -[Team sync]({{< relref "../setup-grafana/configure-security/configure-team-sync" >}}) allows you to set up synchronization between teams in Grafana and teams in your auth provider so that your users automatically end up in the right team. +[Team sync](/docs/grafana//setup-grafana/configure-security/configure-team-sync/) allows you to set up synchronization between teams in Grafana and teams in your auth provider so that your users automatically end up in the right team. Supported auth providers: -- [Auth Proxy]({{< relref "../setup-grafana/configure-security/configure-authentication/auth-proxy#team-sync-enterprise-only" >}}) -- [Azure AD](https://grafana.com/docs/grafana//configure-security/configure-authentication/azuread#group-sync-enterprise-only) -- [Generic OAuth integration](https://grafana.com/docs/grafana//configure-security/configure-authentication/generic-oauth#configure-group-synchronization" >}}) -- [GitHub OAuth](https://grafana.com/docs/grafana//configure-security/configure-authentication/github#configure-group-synchronization) -- [GitLab OAuth](https://grafana.com/docs/grafana//configure-security/configure-authentication/gitlab#configure-group-synchronization) -- [Google OAuth](https://grafana.com/docs/grafana//configure-security/configure-authentication/google#configure-group-synchronization) -- [LDAP](https://grafana.com/docs/grafana//configure-security/configure-authentication/enhanced-ldap#ldap-group-synchronization) -- [Okta](https://grafana.com/docs/grafana//configure-security/configure-authentication/okta#configure-group-synchronization-enterprise-only) -- [SAML](https://grafana.com/docs/grafana//configure-security/configure-authentication/saml#configure-group-synchronization) +- [Auth Proxy](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/auth-proxy#team-sync-enterprise-only) +- [Azure AD](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/azuread#group-sync-enterprise-only) +- [Generic OAuth integration](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/generic-oauth#configure-group-synchronization) +- [GitHub OAuth](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/github#configure-group-synchronization) +- [GitLab OAuth](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/gitlab#configure-group-synchronization) +- [Google OAuth](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/google#configure-group-synchronization) +- [LDAP](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/enhanced-ldap#ldap-group-synchronization) +- [Okta](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/okta#configure-group-synchronization-enterprise-only) +- [SAML](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/saml#configure-group-synchronization) ### Enhanced LDAP integration -With [enhanced LDAP integration]({{< relref "../setup-grafana/configure-security/configure-authentication/enhanced-ldap" >}}), you can set up active LDAP synchronization. +With [enhanced LDAP integration](/docs/grafana//setup-grafana/configure-security/configure-authentication/enhanced-ldap/), you can set up active LDAP synchronization. ### SAML authentication -[SAML authentication]({{< relref "../setup-grafana/configure-security/configure-authentication/saml" >}}) enables users to authenticate with single sign-on services that use Security Assertion Markup Language (SAML). +[SAML authentication](/docs/grafana//setup-grafana/configure-security/configure-authentication/saml/) enables users to authenticate with single sign-on services that use Security Assertion Markup Language (SAML). ### Protected roles -With [protected roles]({{< relref "../setup-grafana/configure-security/configure-authentication#protected-roles" >}}), you can define user roles that are exempt from being converted from one authentication type to another when changing auth providers. +With [protected roles](/docs/grafana//setup-grafana/configure-security/configure-authentication/#protected-roles), you can define user roles that are exempt from being converted from one authentication type to another when changing auth providers. ## Enterprise features Grafana Enterprise adds the following features: -- [Role-based access control]({{< relref "../administration/roles-and-permissions/access-control" >}}) to control access with role-based permissions. -- [Data source permissions]({{< relref "../administration/data-source-management#data-source-permissions" >}}) to restrict query access to specific teams and users. -- [Data source query and resource caching]({{< relref "../administration/data-source-management#query-and-resource-caching" >}}) to temporarily store query results in Grafana to reduce data source load and rate limiting. -- [Reporting]({{< relref "../dashboards/create-reports" >}}) to generate a PDF report from any dashboard and set up a schedule to have it emailed to whomever you choose. -- [Export dashboard as PDF]({{< relref "../dashboards/share-dashboards-panels#export-a-dashboard-as-pdf" >}}) -- [Custom branding]({{< relref "../setup-grafana/configure-grafana/configure-custom-branding" >}}) to customize Grafana from the brand and logo to the footer links. -- [Usage insights]({{< relref "../dashboards/assess-dashboard-usage" >}}) to understand how your Grafana instance is used. -- [Recorded queries]({{< relref "../administration/recorded-queries" >}}) to see trends over time for your data sources. -- [Vault integration]({{< relref "../setup-grafana/configure-security/configure-database-encryption#encrypting-your-database-with-a-key-from-a-key-management-service-kms" >}}) to manage your configuration or provisioning secrets with Vault. -- [Auditing]({{< relref "../setup-grafana/configure-security/audit-grafana" >}}) tracks important changes to your Grafana instance to help you manage and mitigate suspicious activity and meet compliance requirements. -- [Request security]({{< relref "../setup-grafana/configure-security/configure-request-security" >}}) makes it possible to restrict outgoing requests from the Grafana server. -- [Settings updates at runtime]({{< relref "../setup-grafana/configure-grafana/settings-updates-at-runtime" >}}) allows you to update Grafana settings at runtime without requiring a restart. +- [Role-based access control](https://grafana.com/docs/grafana//administration/roles-and-permissions/access-control/) to control access with role-based permissions. +- [Data source permissions](https://grafana.com/docs/grafana//administration/data-source-management/#data-source-permissions) to restrict query access to specific teams and users. +- [Data source query and resource caching](https://grafana.com/docs/grafana//administration/data-source-management/#query-and-resource-caching) to temporarily store query results in Grafana to reduce data source load and rate limiting. +- [Reporting](https://grafana.com/docs/grafana//dashboards/create-reports/) to generate a PDF report from any dashboard and set up a schedule to have it emailed to whomever you choose. +- [Export dashboard as PDF](https://grafana.com/docs/grafana//dashboards/share-dashboards-panels/#export-a-dashboard-as-pdf) +- [Custom branding](https://grafana.com/docs/grafana//setup-grafana/configure-grafana/configure-custom-branding/) to customize Grafana from the brand and logo to the footer links. +- [Usage insights](https://grafana.com/docs/grafana//dashboards/assess-dashboard-usage/) to understand how your Grafana instance is used. +- [Recorded queries](https://grafana.com/docs/grafana//administration/recorded-queries/) to see trends over time for your data sources. +- [Vault integration](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-database-encryption/#encrypting-your-database-with-a-key-from-a-key-management-service-kms) to manage your configuration or provisioning secrets with Vault. +- [Auditing](https://grafana.com/docs/grafana//setup-grafana/configure-security/audit-grafana/) tracks important changes to your Grafana instance to help you manage and mitigate suspicious activity and meet compliance requirements. +- [Request security](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-request-security/) makes it possible to restrict outgoing requests from the Grafana server. +- [Settings updates at runtime](https://grafana.com/docs/grafana//setup-grafana/configure-grafana/settings-updates-at-runtime/) allows you to update Grafana settings at runtime without requiring a restart. ## Enterprise data sources diff --git a/docs/sources/panels-visualizations/configure-data-links/index.md b/docs/sources/panels-visualizations/configure-data-links/index.md index 31bf6df9b67..2b057694e6c 100644 --- a/docs/sources/panels-visualizations/configure-data-links/index.md +++ b/docs/sources/panels-visualizations/configure-data-links/index.md @@ -18,16 +18,21 @@ labels: - cloud - enterprise - oss -menuTitle: Configure data links -title: Configure data links +menuTitle: Configure data links and actions +title: Configure data links and actions description: Configure data links to create links between dashboards and link to external resources weight: 80 refs: + api-settings: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/visualizations/canvas/#button-api-options + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana/latest/panels-visualizations/visualizations/canvas/#button-api-options global-variables: - pattern: /docs/grafana/ - destination: /docs/grafana//dashboards/variables/add-template-variables/#**from-and-**to + destination: /docs/grafana//dashboards/variables/add-template-variables/#__from-and-__to - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/visualizations/dashboards/variables/add-template-variables/#**from-and-**to + destination: /docs/grafana-cloud/visualizations/dashboards/variables/add-template-variables/#__from-and-__to heatmap: - pattern: /docs/grafana/ destination: /docs/grafana//panels-visualizations/visualizations/heatmap/ @@ -125,9 +130,14 @@ refs: destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/bar-chart/ --- -# Configure data links +# Configure data links and actions -Data links allow you to link to other panels, dashboards, and external resources while maintaining the context of the source panel. You can create links that include the series name or even the value under the cursor. For example, if your visualization shows four servers, you can add a data link to one or two of them. +_Data links_ allow you to link to other panels, dashboards, and external resources and _actions_ let you trigger basic, unauthenticated, API calls. +In both cases, you can carry out these tasks while maintaining the context of the source panel. + +## Data links + +With data links, you can create links that include the series name or even the value under the cursor. For example, if your visualization shows four servers, you can add a data link to one or two of them. The link itself is accessible in different ways depending on the visualization. For the time series visualization you need to click a data point or line: @@ -139,7 +149,7 @@ For visualizations like stat, gauge, or bar gauge you can click anywhere on the If there's only one data link in the visualization, clicking anywhere on the visualization opens the link rather than the context menu. -## Supported visualizations +### Supported visualizations You can configure data links for the following visualizations: @@ -164,11 +174,32 @@ You can configure data links for the following visualizations: {{< /column-list >}} -## Data link variables +## Actions -Variables in data links let you send people to a detailed dashboard with preserved data filters. For example, you could use variables to specify a label, time range, series, or variable selection. +Using actions, you can trigger processes like starting or shutting down a server, directly from a dashboard panel. [API settings](ref:api-settings) are configured in the **Add action** dialog box. You can also pass variables in the API editor. -To see a list of available variables, enter `$` in the data link **URL** field. +### Supported visualizations + +You can configure actions for the following visualizations: + +{{< column-list >}} + +- [Bar chart](ref:bar-chart) +- [Candlestick](ref:candlestick) +- [State timeline](ref:state-timeline) +- [Status history](ref:status-history) +- [Table](ref:table) +- [Time series](ref:time-series) +- [Trend](ref:trend) +- [XY chart](ref:xy-chart) + +{{< /column-list >}} + +## Data link and action variables {#data-link-variables} + +Variables in data links and actions let you send people to a detailed dashboard or trigger an API call with preserved data filters. For example, you could use variables to specify a label, time range, series, or variable selection. + +To see a list of available variables, enter `$` in the data link or action **URL** field. {{% admonition type="note" %}} These variables changed in 6.4 so if you have an older version of Grafana, then use the version picker to select docs for an older version of Grafana. @@ -176,11 +207,11 @@ These variables changed in 6.4 so if you have an older version of Grafana, then Azure Monitor, [CloudWatch](ref:cloudwatch), and [Google Cloud Monitoring](ref:google-cloud-monitoring) have pre-configured data links called _deep links_. -You can also use template variables in your data links URLs. For more information, refer to [Templates and variables](ref:templates-and-variables). +You can also use template variables in your data links or actions URLs. For more information, refer to [Templates and variables](ref:templates-and-variables). ### Time range panel variables -These variables allow you to include the current time range in the data link URL: +These variables allow you to include the current time range in the data link or action URL: | Variable | Description | | ------------------ | ------------------------------------------------------------------------ | @@ -188,7 +219,7 @@ These variables allow you to include the current time range in the data link URL | `__from` | For more information, refer to [Global variables](ref:global-variables). | | `__to` | For more information, refer to [Global variables](ref:global-variables). | -When you create data links using time range variables like `__url_time_range` in the URL, you have to form the query parameter syntax yourself; that is, you must format the URL by appending query parameters using the question mark (`?`) and ampersand (`&`) syntax. These characters aren't automatically generated. +When you create data links and actions using time range variables like `__url_time_range` in the URL, you have to form the query parameter syntax yourself; that is, you must format the URL by appending query parameters using the question mark (`?`) and ampersand (`&`) syntax. These characters aren't automatically generated. ### Series variables @@ -219,9 +250,9 @@ Value-specific variables are available under `__value` namespace: | `__value.text` | Text representation of a value | | `__value.calc` | Calculation name if the value is result of calculation | -Using value-specific variables in data links can show different results depending on the set option of Tooltip mode. +Using value-specific variables in data links and actions can show different results depending on the set option of Tooltip mode. -When you create data links using time range variables like `__value.time` in the URL, you have to form the query parameter syntax yourself; that is, you must add the question mark (`?`) and ampersand (`&`). These characters aren't automatically generated. +When you create data links and actions using time range variables like `__value.time` in the URL, you have to form the query parameter syntax yourself; that is, you must add the question mark (`?`) and ampersand (`&`). These characters aren't automatically generated. ### Data variables @@ -247,21 +278,67 @@ When linking to another dashboard that uses template variables, select variable If you want to add all of the current dashboard's variables to the URL, then use `${__all_variables}`. -## Add a data link +## Add data links or actions {#add-a-data-link} + +The following tasks describe how to configure data links and actions. + +{{< tabs >}} +{{< tab-content name="Add data links" >}} +To add a data link, follow these steps: 1. Navigate to the panel to which you want to add the data link. 1. Hover over any part of the panel to display the menu icon in the upper-right corner. 1. Click the menu icon and select **Edit** to open the panel editor. -1. In the panel edit pane, scroll down to the **Data links** section and expand it. -1. Click **Add link**. -1. In the dialog box that opens, enter a **Title**. This is a human-readable label for the link, which will be displayed in the UI. -1. Enter the **URL** or variable to which you want to link. +1. Scroll down to the **Data links and actions** section and expand it. +1. Click **+ Add link**. +1. In the dialog box that opens, enter a **Title**. - To add a data link variable, click in the **URL** field and enter `$` or press Ctrl+Space or Cmd+Space to see a list of available variables. + This is a human-readable label for the link displayed in the UI. This is a required field. + +1. Enter the **URL** to which you want to link. + + To add a data link variable, click in the **URL** field and enter `$` or press Ctrl+Space or Cmd+Space to see a list of available variables. This is a required field. 1. If you want the link to open in a new tab, toggle the **Open in a new tab** switch. +1. If you want the data link to open with a single click on the visualization, toggle the **One click** switch. + + Only one data link can have **One click** enabled at a time. **One click** is only supported for some visualizations. + 1. Click **Save** to save changes and close the dialog box. 1. Click **Save dashboard**. 1. Click **Back to dashboard** and then **Exit edit**. -If you add multiple data links, you can control the order in which they appear in the visualization. To do this, click and drag the data link to the desired position. + {{< /tab-content >}} + {{< tab-content name="Add actions" >}} + + {{< admonition type="note">}} + Actions are not supported for all visualizations. For the list of supported visualizations, refer to [Supported visualizations](#supported-visualizations-1). + {{< /admonition >}} + + To add an action, by follow these steps: + +1. Navigate to the panel to which you want to add the action. +1. Hover over any part of the panel to display the menu icon in the upper-right corner. +1. Click the menu icon and select **Edit** to open the panel editor. +1. Scroll down to the **Data links and actions** section and expand it. +1. Click **+ Add action**. +1. In the dialog box that opens, define the API call settings: + + | Option | Description | + | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | + | Title | A human-readable label for the action that's displayed in the UI. | + | Confirmation message | A descriptive prompt to confirm or cancel the action. | + | Method | Select from **POST**, **PUT**, or **GET**. | + | URL | The request URL.

To add a variable, click in the **URL** field and enter `$` or press Ctrl+Space or Cmd+Space to see a list of available variables. | + | Query parameters | **Key** and **Value** pairs. Click the **+** icon to add as many key/value pairs as you need. | + | Headers | Comprised of **Key** and **Value** pairs and a **Content-Type**.

Click the **+** icon to add as many key/value pairs as you need. | + | Content-Type | Select from the following: **application/json**, **text/plain**, **application/XML**, and **application/x-www-form-urlencoded**. | + | Body | The body of the request. | + +1. Click **Save** to save changes and close the dialog box. +1. Click **Save dashboard**. +1. Click **Back to dashboard** and then **Exit edit**. + {{< /tab-content >}} + {{< /tabs >}} + +If you add multiple data links or actions, you can control the order in which they appear in the visualization. To do this, click and drag the data link or action to the desired position. diff --git a/docs/sources/panels-visualizations/visualizations/bar-chart/index.md b/docs/sources/panels-visualizations/visualizations/bar-chart/index.md index 3d7f982d554..6de1af95513 100644 --- a/docs/sources/panels-visualizations/visualizations/bar-chart/index.md +++ b/docs/sources/panels-visualizations/visualizations/bar-chart/index.md @@ -199,9 +199,9 @@ You can set standard min/max options to define hard limits of the Y-axis. For mo {{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="" >}} -### Data links +### Data links and actions -{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} +{{< docs/shared lookup="visualizations/datalink-options-2.md" source="grafana" version="" >}} ### Value mappings diff --git a/docs/sources/panels-visualizations/visualizations/bar-gauge/index.md b/docs/sources/panels-visualizations/visualizations/bar-gauge/index.md index f4059970634..3fd9b5c24ea 100644 --- a/docs/sources/panels-visualizations/visualizations/bar-gauge/index.md +++ b/docs/sources/panels-visualizations/visualizations/bar-gauge/index.md @@ -211,9 +211,9 @@ This option only applies when bar size is set to manual. {{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="" >}} -## Data links +## Data links and actions -{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} +{{< docs/shared lookup="visualizations/datalink-options-1.md" source="grafana" version="" >}} ## Value mappings diff --git a/docs/sources/panels-visualizations/visualizations/candlestick/index.md b/docs/sources/panels-visualizations/visualizations/candlestick/index.md index ee51937afc9..bbaccb65254 100644 --- a/docs/sources/panels-visualizations/visualizations/candlestick/index.md +++ b/docs/sources/panels-visualizations/visualizations/candlestick/index.md @@ -136,9 +136,9 @@ The candlestick visualization is based on the time series visualization. It can {{< docs/shared lookup="visualizations/tooltip-options-2.md" source="grafana" version="" >}} -## Data links +## Data links and actions -{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} +{{< docs/shared lookup="visualizations/datalink-options-2.md" source="grafana" version="" >}} ## Thresholds diff --git a/docs/sources/panels-visualizations/visualizations/canvas/index.md b/docs/sources/panels-visualizations/visualizations/canvas/index.md index ebd7162047e..3313fb3b225 100644 --- a/docs/sources/panels-visualizations/visualizations/canvas/index.md +++ b/docs/sources/panels-visualizations/visualizations/canvas/index.md @@ -258,6 +258,13 @@ You can enable panning and zooming in a canvas. This allows you to both create a {{< docs/public-preview product="Canvas pan and zoom" featureFlag="`canvasPanelPanZoom`" >}} +Use the following pointer and keyboard strokes: + +- **Zoom in** - Scroll up +- **Zoom out** - Scroll down +- **Pan** - Middle mouse/wheel + drag OR Control + right-click + drag +- **Reset** - Double-click + {{< video-embed src="/media/docs/grafana/2024-01-05-Canvas-Pan-&-Zoom-Enablement-Video.mp4" max-width="750px" alt="Canvas pan and zoom enablement video" >}} ##### Infinite panning @@ -323,14 +330,14 @@ Use the following options to control the border of the canvas: The following options allow you to control the appearance of the element you've selected. To access an element so that you can edit it, expand the **Layer** section and select the desired element. -| Option | Description | -| ------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| [Element type](#element-type) | Change the selected element type. | -| [Element](#element) | Control the appearance of text on the element. This section is named based on the element type. | -| [Layout](#layout) | Control the placement of elements on the canvas. | -| [Background (element)](#background-element) | Set the background of the element. | -| [Border (element)](#border-element) | Set the border of the element. | -| [Data links](#data-links) | Configure data links for elements. | +| Option | Description | +| ------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| [Element type](#element-type) | Change the selected element type. | +| [Element](#element) | Control the appearance of text on the element. This section is named based on the element type. | +| [Layout](#layout) | Control the placement of elements on the canvas. | +| [Background (element)](#background-element) | Set the background of the element. | +| [Border (element)](#border-element) | Set the border of the element. | +| [Data links and actions](#data-links-and-actions) | Configure data links and actions for elements. | #### Element type @@ -398,40 +405,66 @@ Use the following options to set the border of the element: - **Color** - Set the border color. This option is only displayed when the border width is greater than zero. - **Radius** - Add rounded corners to the element border and control the degree of curve. -#### Data links +#### Data links and actions -Canvases support [data links](ref:data-links) for all elements except drone and button elements. You can add a data link by following these steps: +Canvases support [data links](ref:data-links) and actions for all elements. -1. Enable inline editing. -1. Click the element you to which you want to add the data link. -1. In either the inline editor or panel editor, expand the **Selected element** editor. -1. Scroll down to the **Data links** section and expand it. -1. Click **Add link**. -1. In the dialog box that opens, enter a **Title**. This is a human-readable label for the link, which will be displayed in the UI. -1. Enter the **URL** or variable to which you want to link. +If you add multiple data links or actions to an element, you can control the order in which they appear in the element tooltip. +To do this, click and drag the link or action to the desired position. - To add a data link variable, click in the **URL** field and enter `$` or press Ctrl+Space or Cmd+Space to see a list of available variables. +The following tasks describe how to configure data links and actions. -1. If you want the link to open in a new tab, toggle the **Open in a new tab** switch. -1. Click **Save** to save changes and close the dialog box. -1. Disable inline editing. - -If you add multiple data links, you can control the order in which they appear in the visualization. To do this, click and drag the data link to the desired position. - -##### One-click data link - -You can configure a canvas data link to open with a single click on the element. To enable this feature, follow these steps: +{{< tabs >}} +{{< tab-content name="Add data links" >}} +To add a data link, follow these steps: 1. Enable inline editing. 1. Click the element to which you want to add the data link. 1. In either the inline editor or panel editor, expand the **Selected element** editor. -1. Scroll down to the **Data links** section and expand it. -1. In the **One-click** section, choose **Link**. +1. Scroll down to the **Data links and actions** section and expand it. +1. Click **+ Add link**. +1. In the dialog box that opens, enter a **Title**. + + This is a human-readable label for the link displayed in the UI. This is a required field. + +1. Enter the **URL** or variable to which you want to link. + + To add a data link variable, click in the **URL** field and enter `$` or press Ctrl+Space or Cmd+Space to see a list of available variables. This is a required field. + +1. If you want the link to open in a new tab, toggle the **Open in a new tab** switch. +1. If you want the data link to open with a single click on the element, toggle the **One click** switch. + + Only one data link or action can have **One click** enabled at a time. + +1. Click **Save** to save changes and close the dialog box. 1. Disable inline editing. + {{< /tab-content >}} + {{< tab-content name="Add actions" >}} + To add an action, by follow these steps: -The first data link in the list will be configured as your one-click data link. If you want to change the one-click data link, simply drag the desired data link to the top of the list. +1. Enable inline editing. +1. Click the element to which you want to add the data link. +1. In either the inline editor or panel editor, expand the **Selected element** editor. +1. Scroll down to the **Data links and actions** section and expand it. +1. Click **+ Add action**. +1. In the dialog box that opens, set the action options: -{{< video-embed src="/media/docs/grafana/panels-visualizations/canvas-one-click-datalink-.mp4" >}} + | Option | Description | + | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | Title | A human-readable label for the action that's displayed in the UI. | + | Confirmation message | A descriptive prompt to confirm or cancel the action. | + | One click | If you want the action to be triggered by a single click on the element, toggle the switch.

Only one data link or action can have **One click** enabled at a time. | + | Method | Select from **POST**, **PUT**, or **GET**. | + | URL | The request URL or variable to which you want to link.

To add a variable, click in the **URL** field and enter `$` or press Ctrl+Space or Cmd+Space to see a list of available variables. | + | Query parameters | **Key** and **Value** pairs. Click the **+** icon to add as many key/value pairs as you need. | + | Headers | Comprised of **Key** and **Value** pairs and a **Content-Type**.

Click the **+** icon to add as many key/value pairs as you need. | + | Content-Type | Select from the following: **application/json**, **text/plain**, **application/XML**, and **application/x-www-form-urlencoded**. | + | Body | The body of the request. | + +1. Click **Save** to save changes and close the dialog box. +1. Disable inline editing. + {{< /tab-content >}} + {{< /tabs >}} ### Selected connection options diff --git a/docs/sources/panels-visualizations/visualizations/gauge/index.md b/docs/sources/panels-visualizations/visualizations/gauge/index.md index bfcd1c89549..fbb1cb922ae 100644 --- a/docs/sources/panels-visualizations/visualizations/gauge/index.md +++ b/docs/sources/panels-visualizations/visualizations/gauge/index.md @@ -171,9 +171,9 @@ Adjust the sizes of the gauge text. {{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="" >}} -### Data links +### Data links and actions -{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} +{{< docs/shared lookup="visualizations/datalink-options-1.md" source="grafana" version="" >}} ### Value mappings diff --git a/docs/sources/panels-visualizations/visualizations/geomap/index.md b/docs/sources/panels-visualizations/visualizations/geomap/index.md index b42d1f78a6c..64b20d33dd7 100644 --- a/docs/sources/panels-visualizations/visualizations/geomap/index.md +++ b/docs/sources/panels-visualizations/visualizations/geomap/index.md @@ -711,7 +711,7 @@ Displays debug information in the upper right corner. This can be useful for deb {{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="" >}} -### Data links +### Data links and actions {{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} diff --git a/docs/sources/panels-visualizations/visualizations/heatmap/index.md b/docs/sources/panels-visualizations/visualizations/heatmap/index.md index 99bd1f53e68..a51479e4e7a 100644 --- a/docs/sources/panels-visualizations/visualizations/heatmap/index.md +++ b/docs/sources/panels-visualizations/visualizations/heatmap/index.md @@ -208,7 +208,7 @@ Choose whether you want to display the heatmap legend on the visualization by to Set the color used to show exemplar data. -### Data links +### Data links and actions {{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} diff --git a/docs/sources/panels-visualizations/visualizations/histogram/index.md b/docs/sources/panels-visualizations/visualizations/histogram/index.md index 70fcc675d02..f005c8ad7cb 100644 --- a/docs/sources/panels-visualizations/visualizations/histogram/index.md +++ b/docs/sources/panels-visualizations/visualizations/histogram/index.md @@ -157,7 +157,7 @@ Choose from the following: {{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="" >}} -## Data links +## Data links and actions {{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} diff --git a/docs/sources/panels-visualizations/visualizations/pie-chart/index.md b/docs/sources/panels-visualizations/visualizations/pie-chart/index.md index bb1d2cd02aa..679f4e61f3b 100644 --- a/docs/sources/panels-visualizations/visualizations/pie-chart/index.md +++ b/docs/sources/panels-visualizations/visualizations/pie-chart/index.md @@ -174,9 +174,9 @@ Use these settings to define how the legend appears in your visualization. For m {{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="" >}} -### Data links +### Data links and actions -{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} +{{< docs/shared lookup="visualizations/datalink-options-1.md" source="grafana" version="" >}} ### Value mappings diff --git a/docs/sources/panels-visualizations/visualizations/stat/index.md b/docs/sources/panels-visualizations/visualizations/stat/index.md index eb989ae6d4e..e1ad7fada4a 100644 --- a/docs/sources/panels-visualizations/visualizations/stat/index.md +++ b/docs/sources/panels-visualizations/visualizations/stat/index.md @@ -172,9 +172,9 @@ Adjust the sizes of the gauge text. {{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="" >}} -### Data links +### Data links and actions -{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} +{{< docs/shared lookup="visualizations/datalink-options-1.md" source="grafana" version="" >}} ### Value mappings diff --git a/docs/sources/panels-visualizations/visualizations/state-timeline/index.md b/docs/sources/panels-visualizations/visualizations/state-timeline/index.md index 1ef99435336..192bcb8fdb1 100644 --- a/docs/sources/panels-visualizations/visualizations/state-timeline/index.md +++ b/docs/sources/panels-visualizations/visualizations/state-timeline/index.md @@ -149,13 +149,17 @@ The **Page size** option lets you paginate the state timeline visualization to l {{< docs/shared lookup="visualizations/tooltip-options-1.md" source="grafana" version="" leveloffset="+1" >}} +### Axis options + +{{< docs/shared lookup="visualizations/axis-options-state-status.md" source="grafana" version="" leveloffset="+1" >}} + ### Standard options {{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="" >}} -### Data links +### Data links and actions -{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} +{{< docs/shared lookup="visualizations/datalink-options-2.md" source="grafana" version="" >}} ### Value mappings diff --git a/docs/sources/panels-visualizations/visualizations/status-history/index.md b/docs/sources/panels-visualizations/visualizations/status-history/index.md index b7825b94b82..0382deffd77 100644 --- a/docs/sources/panels-visualizations/visualizations/status-history/index.md +++ b/docs/sources/panels-visualizations/visualizations/status-history/index.md @@ -111,6 +111,10 @@ Controls whether values are rendered inside the value boxes. Auto will render va Controls the height of boxes. 1 = maximum space and 0 = minimum space. +### Page size (enable pagination) + +The **Page size** option lets you paginate the status history visualization to limit how many series are visible at once. This is useful when you have many series. + ### Column width Controls the width of boxes. 1 = maximum space and 0 = minimum space. @@ -131,13 +135,17 @@ Controls the opacity of state regions. {{< docs/shared lookup="visualizations/tooltip-options-1.md" source="grafana" version="" >}} +## Axis options + +{{< docs/shared lookup="visualizations/axis-options-state-status.md" source="grafana" version="" leveloffset="+1" >}} + ## Standard options {{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="" >}} -## Data links +## Data links and actions -{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} +{{< docs/shared lookup="visualizations/datalink-options-2.md" source="grafana" version="" >}} ## Value mappings diff --git a/docs/sources/panels-visualizations/visualizations/table/index.md b/docs/sources/panels-visualizations/visualizations/table/index.md index 42635aa733f..e99622019d8 100644 --- a/docs/sources/panels-visualizations/visualizations/table/index.md +++ b/docs/sources/panels-visualizations/visualizations/table/index.md @@ -246,6 +246,7 @@ If you want to apply a cell type to only some fields instead of all fields, you | Data links | If you've configured data links, when the cell type is **Auto**, the cell text becomes clickable. If you change the cell type to **Data links**, the cell text reflects the titles of the configured data links. To control the application of data link text more granularly, use a **Cell option > Cell type > Data links** field override. | | [JSON View](#json-view) | Shows values formatted as code. | | [Image](#image) | If the field value is an image URL or a base64 encoded image, the table displays the image. | +| [Actions](#actions) | The cell displays a button that triggers a basic, unauthenticated API call when clicked. | ##### Sparkline @@ -333,6 +334,23 @@ Set the following options: - **Alt text** - Set the alternative text of an image. The text will be available for screen readers and in cases when images can't be loaded. - **Title text** - Set the text that's displayed when the image is hovered over with a cursor. +##### Actions + +The cell displays a button that triggers a basic, unauthenticated API call when clicked. +Configure the API call with the following options: + + +| Option | Description | +| ------- | ------------ | +| Endpoint | Enter the endpoint URL. | +| Method | Choose from **GET**, **POST**, and **PUT**. | +| Content-Type | Select an option in the drop-down list. Choose from: JSON, Text, JavaScript, HTML, XML, and x-www-form-urlencoded. | +| Query parameters | Enter as many **Key**, **Value** pairs as you need. | +| Header parameters | Enter as many **Key**, **Value** pairs as you need. | +| Payload | Enter the body of the API call. | + + + #### Wrap text {{< admonition type="note" >}} @@ -362,9 +380,9 @@ If you want to apply this setting to only some fields instead of all fields, you {{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="" >}} -### Data links +### Data links and actions -{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} +{{< docs/shared lookup="visualizations/datalink-options-3.md" source="grafana" version="" >}} ### Value mappings diff --git a/docs/sources/panels-visualizations/visualizations/time-series/index.md b/docs/sources/panels-visualizations/visualizations/time-series/index.md index e7f21000554..7b3dec5e5bf 100644 --- a/docs/sources/panels-visualizations/visualizations/time-series/index.md +++ b/docs/sources/panels-visualizations/visualizations/time-series/index.md @@ -209,21 +209,7 @@ The following example shows three series: Min, Max, and Value. The Min and Max s ### Axis options -Options under the **Axis** section control how the x- and y-axes are rendered. Some options don't take effect until you click outside of the field option box you're editing. You can also press `Enter`. - -| Option | Description | -| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Time zone | Set the desired time zones to display along the x-axis. | -| [Placement](#placement) | Select the placement of the y-axis. | -| Label | Set a y-axis text label. If you have more than one y-axis, then you can assign different labels using an override. | -| Width | Set a fixed width of the axis. By default, Grafana dynamically calculates the width of an axis. By setting the width of the axis, data with different axes types can share the same display proportions. This setting makes it easier for you to compare more than one graph’s worth of data because the axes aren't shifted or stretched within visual proximity to each other. | -| Show grid lines | Set the axis grid line visibility.
| -| Color | Set the color of the axis. | -| Show border | Set the axis border visibility. | -| Scale | Set the y-axis values scale.
| -| Centered zero | Set the y-axis so it's centered on zero. | -| [Soft min](#soft-min-and-soft-max) | Set a soft min to better control the y-axis limits. zero. | -| [Soft max](#soft-min-and-soft-max) | Set a soft max to better control the y-axis limits. zero. | +{{< docs/shared lookup="visualizations/axis-options-all.md" source="grafana" version="" leveloffset="+1" >}} #### Placement @@ -376,9 +362,9 @@ Set the position of the bar relative to a data point. In the examples below, **S {{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="" >}} -### Data links +### Data links and actions -{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} +{{< docs/shared lookup="visualizations/datalink-options-2.md" source="grafana" version="" >}} ### Value mappings diff --git a/docs/sources/panels-visualizations/visualizations/trend/index.md b/docs/sources/panels-visualizations/visualizations/trend/index.md index 7c8900b0f4f..e8cc7fa8d30 100644 --- a/docs/sources/panels-visualizations/visualizations/trend/index.md +++ b/docs/sources/panels-visualizations/visualizations/trend/index.md @@ -60,9 +60,9 @@ For example, you could represent engine power and torque versus speed where spee {{< docs/shared lookup="visualizations/tooltip-options-2.md" source="grafana" version="" >}} -## Data links +## Data links and actions -{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} +{{< docs/shared lookup="visualizations/datalink-options-2.md" source="grafana" version="" >}} ## Thresholds diff --git a/docs/sources/panels-visualizations/visualizations/xy-chart/index.md b/docs/sources/panels-visualizations/visualizations/xy-chart/index.md index 1e3f4d341dd..83becc1b7df 100644 --- a/docs/sources/panels-visualizations/visualizations/xy-chart/index.md +++ b/docs/sources/panels-visualizations/visualizations/xy-chart/index.md @@ -308,9 +308,9 @@ You can customize the following standard options: To learn more, refer to [Configure standard options](ref:configure-standard-options). -## Data links +## Data links and actions -{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} +{{< docs/shared lookup="visualizations/datalink-options-2.md" source="grafana" version="" >}} ## Field overrides diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index 6469a11891b..babe3619b35 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -301,6 +301,10 @@ Path to the certificate file (if `protocol` is set to `https` or `h2`). Path to the certificate key file (if `protocol` is set to `https` or `h2`). +#### `cert_pass` + +Optional. Password to decrypt encrypted certificates. + #### `certs_watch_interval` Controls whether `cert_key` and `cert_file` are periodically watched for changes. @@ -696,6 +700,10 @@ An existing user's account is unable to login for five minutes if all login atte Configure how many login attempts a user can have within a five minute window before their account is locked. Default is `5`. +#### `disable_ip_address_login_protection` + +Set to `true` to disable [brute force login protection by IP address](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html#account-lockout). Default is `true`. Anyone from the IP address will be unable to login for 5 minutes if all login attempts are spent within a 5 minute window. + #### `cookie_secure` Set to `true` if you host Grafana behind HTTPS. Default is `false`. @@ -942,10 +950,18 @@ If you manage users externally you can replace the user invite button for organi #### `viewers_can_edit` +{{< admonition type="note" >}} +This option is deprecated - assign your viewers as editors, if you are using RBAC assign the data sources explorer role to your users. +{{< /admonition >}} + Viewers can access and use [Explore]({{< relref "../../explore" >}}) and perform temporary edits on panels in dashboards they have access to. They cannot save their changes. Default is `false`. #### `editors_can_admin` +{{< admonition type="note" >}} +This option is deprecated - assign your editors as admins, if you are using RBAC assign the team creator role to your users. +{{< /admonition >}} + Editors can administrate dashboards, folders and teams they create. Default is `false`. @@ -1361,7 +1377,13 @@ Override token URL for Azure Active Directory. By default is the same as token URL configured for AAD authentication settings. -#### `user_identity_client_id` +### `user_identity_client_authentication` + +Override client authentication method for Azure Active Directory. Currently supported values are `client_secret_post` and `managed_identity`. + +By default is the same as client authentication method configured for AAD authentication settings. + +### `user_identity_client_id` Override ADD application ID which would be used to exchange users token to an access token for the data source. @@ -1373,7 +1395,19 @@ Override the AAD application client secret. By default is the same as used in AAD authentication or can be set to another application (for OBO flow). -#### `forward_settings_to_plugins` +### `user_identity_managed_identity_client_id` + +Override the AAD application managed identity client ID of the federated credential configured as a user-assigned managed identity. + +By default is the same as used in AAD authentication or can be set to another managed identity (for OBO flow). + +### `user_identity_federated_credential_audience` + +Override the AAD federated credential audience of the federated credential configured as a user-assigned managed identity. + +By default is the same as used in AAD authentication or can be set to another audience (for OBO flow). + +### `forward_settings_to_plugins` Set plugins to receive Azure settings via plugin context. @@ -1489,6 +1523,16 @@ Options are `debug`, `info`, `warn`, `error`, and `critical`. Default is `info`. Optional settings to set different levels for specific loggers. For example: `filters = sqlstore:debug` +You can use multiple filters with a comma-seperated list: +For example: `filters = sqlstore:debug,plugins:info` + +The equivalent for a `docker-compose.yaml` looks like this: + +``` +GF_LOG_FILTERS: sqlstore:debug,plugins:info +GF_LOG_LEVEL: error +``` + #### `user_facing_default_error` Use this configuration option to set the default error message shown to users. This message is displayed instead of sensitive backend errors, which should be obfuscated. The default message is `Please inspect the Grafana server log for details.`. @@ -1795,7 +1839,7 @@ The timeout string is a possibly signed sequence of decimal numbers, followed by #### `max_attempts` -Sets a maximum number of times Grafana attempts to evaluate an alert rule before giving up on that evaluation. The default value is `1`. +Sets a maximum number of times Grafana attempts to evaluate an alert rule before giving up on that evaluation. The default value is `3`. #### `min_interval` @@ -1916,6 +1960,10 @@ Enable or disable the Explore section. Default is `enabled`. Set a default time offset from now on the time picker. Default is 1 hour. This setting should be expressed as a duration. Examples: 1h (hour), 1d (day), 1w (week), 1M (month). +#### `hide_logs_download` + +Show or hide the button to download logs in Explore. Default is `false`, so that the button will be visible. + ### `[help]` Configures the help section. @@ -2330,6 +2378,8 @@ URL to a remote HTTP image renderer service, for example, `http://localhost:8081 If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable, for example, http://grafana.domain/. +The `callback_url` can also be configured to support usage of the image renderer running as a plugin with support for SSL / HTTPS. For example https://localhost:3000/. + #### `concurrent_render_request_limit` Concurrent render request limit affects when the /render HTTP endpoint is used. Rendering many images at the same time can overload the server, diff --git a/docs/sources/setup-grafana/configure-grafana/enterprise-configuration/index.md b/docs/sources/setup-grafana/configure-grafana/enterprise-configuration/index.md index c75522253e2..a7ef4fa0733 100644 --- a/docs/sources/setup-grafana/configure-grafana/enterprise-configuration/index.md +++ b/docs/sources/setup-grafana/configure-grafana/enterprise-configuration/index.md @@ -144,7 +144,7 @@ Enable or disable the reporting feature. When disabled, no reports are generated ### rendering_timeout -Timeout for the following reporting rendering requests: generating PDFs, generating embedded dashboard images for report emails, and generating attached CSV files. Default is 10 seconds. +Timeout for the following reporting rendering requests: generating PDFs, generating embedded dashboard images for report emails, and generating attached CSV files. Default is 10 seconds (`10s`). ### concurrent_render_limit diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index 2bd6d4eef14..f40ad6a1766 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -28,7 +28,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `featureHighlights` | Highlight Grafana Enterprise features | | | `correlations` | Correlations page | Yes | | `cloudWatchCrossAccountQuerying` | Enables cross-account querying in CloudWatch datasources | Yes | -| `accessControlOnCall` | Access control primitives for OnCall | Yes | | `nestedFolders` | Enable folder nesting | Yes | | `logsContextDatasourceUi` | Allow datasource to provide custom UI for context view | Yes | | `lokiQuerySplitting` | Split large interval queries into subqueries with smaller time intervals | Yes | @@ -46,11 +45,9 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `alertingInsights` | Show the new alerting insights landing page | Yes | | `panelMonitoring` | Enables panel monitoring through logs and measurements | Yes | | `formatString` | Enable format string transformer | Yes | -| `transformationsVariableSupport` | Allows using variables in transformations | Yes | | `kubernetesPlaylists` | Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s | Yes | | `recoveryThreshold` | Enables feature recovery threshold (aka hysteresis) for threshold server-side expression | Yes | | `lokiStructuredMetadata` | Enables the loki data source to request structured metadata from the Loki server | Yes | -| `managedPluginsInstall` | Install managed plugins directly from plugins catalog | Yes | | `addFieldFromCalculationStatFunctions` | Add cumulative and window functions to the add field from calculation transformation | Yes | | `annotationPermissionUpdate` | Change the way annotation permissions work by scoping them to folders and dashboards. | Yes | | `dashboardSceneForViewers` | Enables dashboard rendering using Scenes for viewer roles | Yes | @@ -58,7 +55,7 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `dashboardScene` | Enables dashboard rendering using scenes for all roles | Yes | | `ssoSettingsApi` | Enables the SSO settings API and the OAuth configuration UIs in Grafana | Yes | | `logsInfiniteScrolling` | Enables infinite scrolling for the Logs panel in Explore and Dashboards | Yes | -| `exploreMetrics` | Enables the new Explore Metrics core app | Yes | +| `exploreMetrics` | Enables the new Grafana Metrics Drilldown core app | Yes | | `alertingSimplifiedRouting` | Enables users to easily configure alert notifications by specifying a contact point directly when editing or creating an alert rule | Yes | | `logRowsPopoverMenu` | Enable filtering menu displayed when text of a log line is selected | Yes | | `lokiQueryHints` | Enables query hints for Loki | Yes | @@ -72,7 +69,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `newDashboardSharingComponent` | Enables the new sharing drawer design | Yes | | `pluginProxyPreserveTrailingSlash` | Preserve plugin proxy trailing slash. | | | `pinNavItems` | Enables pinning of nav items | Yes | -| `openSearchBackendFlowEnabled` | Enables the backend query flow for Open Search datasource plugin | Yes | | `alertingApiServer` | Register Alerting APIs with the K8s API server | Yes | | `cloudWatchRoundUpEndTime` | Round up end time for metric queries to the next minute to avoid missing data | Yes | | `newFiltersUI` | Enables new combobox style UI for the Ad hoc filters variable in scenes architecture | Yes | @@ -81,7 +77,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `userStorageAPI` | Enables the user storage API | Yes | | `azureMonitorDisableLogLimit` | Disables the log limit restriction for Azure Monitor when true. The limit is enabled by default. | | | `preinstallAutoUpdate` | Enables automatic updates for pre-installed plugins | Yes | -| `zipkinBackendMigration` | Enables querying Zipkin data source without the proxy | Yes | | `reportingUseRawTimeRange` | Uses the original report or dashboard time range instead of making an absolute transformation | Yes | | `alertingUIOptimizeReducer` | Enables removing the reducer from the alerting UI when creating a new alert rule and using instant query | Yes | | `azureMonitorEnableUserAuth` | Enables user auth for Azure Monitor datasource only | Yes | @@ -103,7 +98,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `autoMigrateStatPanel` | Migrate old stat panel to supported stat panel - broken out from autoMigrateOldPanels to enable granular tracking | | `disableAngular` | Dynamic flag to disable angular at runtime. The preferred method is to set `angular_support_enabled` to `false` in the [security] settings, which allows you to change the state at runtime. | | `grpcServer` | Run the GRPC server | -| `alertingNoNormalState` | Stop maintaining state of alerts that are not firing | | `renderAuthJWT` | Uses JWT-based auth for rendering instead of relying on remote cache | | `refactorVariablesTimeRange` | Refactor time range variables flow to reduce number of API calls made when query variables are chained | | `faroDatasourceSelector` | Enable the data source selector within the Frontend Apps section of the Frontend Observability | @@ -112,11 +106,11 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `reportingRetries` | Enables rendering retries for the reporting feature | | `externalServiceAccounts` | Automatic service account and token setup for plugins | | `cloudWatchBatchQueries` | Runs CloudWatch metrics queries as separate batches | -| `teamHttpHeaders` | Enables LBAC for datasources to apply LogQL filtering of logs to the client requests for users in teams | | `pdfTables` | Enables generating table data as PDF in reporting | | `canvasPanelPanZoom` | Allow pan and zoom in canvas panel | | `regressionTransformation` | Enables regression analysis transformation | | `onPremToCloudMigrations` | Enable the Grafana Migration Assistant, which helps you easily migrate on-prem resources, such as dashboards, folders, and data source configurations, to your Grafana Cloud stack. | +| `alertingSaveStateCompressed` | Enables the compressed protobuf-based alert state storage | | `ssoSettingsSAML` | Use the new SSO Settings API to configure the SAML connector | | `azureMonitorPrometheusExemplars` | Allows configuration of Azure Monitor as a data source that can provide Prometheus exemplars | | `ssoSettingsLDAP` | Use the new SSO Settings API to configure LDAP | @@ -129,113 +123,109 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- [Experimental](https://grafana.com/docs/release-life-cycle/#experimental) features are early in their development lifecycle and so are not yet supported in Grafana Cloud. Experimental features might be changed or removed without prior notice. -| Feature toggle name | Description | -| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `live-service-web-worker` | This will use a webworker thread to processes events rather than the main thread | -| `queryOverLive` | Use Grafana Live WebSocket to execute backend queries | -| `lokiExperimentalStreaming` | Support new streaming approach for loki (prototype, needs special loki build) | -| `storage` | Configurable storage for dashboards, datasources, and resources | -| `canvasPanelNesting` | Allow elements nesting | -| `vizActions` | Allow actions in visualizations | -| `disableSecretsCompatibility` | Disable duplicated secret storage in legacy tables | -| `logRequestsInstrumentedAsUnknown` | Logs the path for requests that are instrumented as unknown | -| `showDashboardValidationWarnings` | Show warnings when dashboards do not validate against the schema | -| `mysqlAnsiQuotes` | Use double quotes to escape keyword in a MySQL query | -| `alertingBacktesting` | Rule backtesting API for alerting | -| `editPanelCSVDragAndDrop` | Enables drag and drop for CSV and Excel files | -| `lokiShardSplitting` | Use stream shards to split queries into smaller subqueries | -| `lokiQuerySplittingConfig` | Give users the option to configure split durations for Loki queries | -| `individualCookiePreferences` | Support overriding cookie preferences per user | -| `influxqlStreamingParser` | Enable streaming JSON parser for InfluxDB datasource InfluxQL query language | -| `lokiLogsDataplane` | Changes logs responses from Loki to be compliant with the dataplane specification. | -| `disableSSEDataplane` | Disables dataplane specific processing in server side expressions. | -| `alertStateHistoryLokiSecondary` | Enable Grafana to write alert state history to an external Loki instance in addition to Grafana annotations. | -| `alertStateHistoryLokiPrimary` | Enable a remote Loki instance as the primary source for state history reads. | -| `alertStateHistoryLokiOnly` | Disable Grafana alerts from emitting annotations when a remote Loki instance is available. | -| `extraThemes` | Enables extra themes | -| `lokiPredefinedOperations` | Adds predefined query operations to Loki query editor | -| `frontendSandboxMonitorOnly` | Enables monitor only in the plugin frontend sandbox (if enabled) | -| `pluginsDetailsRightPanel` | Enables right panel for the plugins details page | -| `awsDatasourcesTempCredentials` | Support temporary security credentials in AWS plugins for Grafana Cloud customers | -| `mlExpressions` | Enable support for Machine Learning in server-side expressions | -| `metricsSummary` | Enables metrics summary queries in the Tempo data source | -| `datasourceAPIServers` | Expose some datasources as apiservers. | -| `provisioning` | Next generation provisioning... and git | -| `permissionsFilterRemoveSubquery` | Alternative permission filter implementation that does not use subqueries for fetching the dashboard folder | -| `aiGeneratedDashboardChanges` | Enable AI powered features for dashboards to auto-summary changes when saving | -| `sseGroupByDatasource` | Send query to the same datasource in a single request when using server side expressions. The `cloudWatchBatchQueries` feature toggle should be enabled if this used with CloudWatch. | -| `libraryPanelRBAC` | Enables RBAC support for library panels | -| `wargamesTesting` | Placeholder feature flag for internal testing | -| `externalCorePlugins` | Allow core plugins to be loaded as external | -| `pluginsAPIMetrics` | Sends metrics of public grafana packages usage by plugins | -| `enableNativeHTTPHistogram` | Enables native HTTP Histograms | -| `disableClassicHTTPHistogram` | Disables classic HTTP Histogram (use with enableNativeHTTPHistogram) | -| `kubernetesSnapshots` | Routes snapshot requests from /api to the /apis endpoint | -| `kubernetesDashboards` | Use the kubernetes API in the frontend for dashboards | -| `kubernetesCliDashboards` | Use the k8s client to retrieve dashboards internally | -| `kubernetesRestore` | Allow restoring objects in k8s | -| `kubernetesFolders` | Use the kubernetes API in the frontend for folders, and route /api/folders requests to k8s | -| `kubernetesFoldersServiceV2` | Use the Folders Service V2, and route Folder Service requests to k8s | -| `grafanaAPIServerTestingWithExperimentalAPIs` | Facilitate integration testing of experimental APIs | -| `datasourceQueryTypes` | Show query type endpoints in datasource API servers (currently hardcoded for testdata, expressions, and prometheus) | -| `queryService` | Register /apis/query.grafana.app/ -- will eventually replace /api/ds/query | -| `queryServiceRewrite` | Rewrite requests targeting /ds/query to the query service | -| `queryServiceFromUI` | Routes requests to the new query service | -| `cachingOptimizeSerializationMemoryUsage` | If enabled, the caching backend gradually serializes query responses for the cache, comparing against the configured `[caching]max_value_mb` value as it goes. This can can help prevent Grafana from running out of memory while attempting to cache very large query responses. | -| `prometheusPromQAIL` | Prometheus and AI/ML to assist users in creating a query | -| `prometheusCodeModeMetricNamesSearch` | Enables search for metric names in Code Mode, to improve performance when working with an enormous number of metric names | -| `alertmanagerRemoteSecondary` | Enable Grafana to sync configuration and state with a remote Alertmanager. | -| `alertmanagerRemotePrimary` | Enable Grafana to have a remote Alertmanager instance as the primary Alertmanager. | -| `alertmanagerRemoteOnly` | Disable the internal Alertmanager and only use the external one defined. | -| `extractFieldsNameDeduplication` | Make sure extracted field names are unique in the dataframe | -| `dashboardNewLayouts` | Enables experimental new dashboard layouts | -| `pluginsSkipHostEnvVars` | Disables passing host environment variable to plugin processes | -| `tableSharedCrosshair` | Enables shared crosshair in table panel | -| `kubernetesFeatureToggles` | Use the kubernetes API for feature toggle management in the frontend | -| `newFolderPicker` | Enables the nested folder picker without having nested folders enabled | -| `onPremToCloudMigrationsAuthApiMig` | Enables the use of auth api instead of gcom for internal token services. Requires `onPremToCloudMigrations` to be enabled in conjunction. | -| `scopeApi` | In-development feature flag for the scope api using the app platform. | -| `sqlExpressions` | Enables using SQL and DuckDB functions as Expressions. | -| `nodeGraphDotLayout` | Changed the layout algorithm for the node graph | -| `kubernetesAggregator` | Enable grafana's embedded kube-aggregator | -| `expressionParser` | Enable new expression parser | -| `disableNumericMetricsSortingInExpressions` | In server-side expressions, disable the sorting of numeric-kind metrics by their metric name or labels. | -| `queryLibrary` | Enables Query Library feature in Explore | -| `logsExploreTableDefaultVisualization` | Sets the logs table as default visualisation in logs explore | -| `alertingListViewV2` | Enables the new alert list view design | -| `dashboardRestore` | Enables deleted dashboard restore feature | -| `alertingCentralAlertHistory` | Enables the new central alert history. | -| `sqlQuerybuilderFunctionParameters` | Enables SQL query builder function parameters | -| `failWrongDSUID` | Throws an error if a datasource has an invalid UIDs | -| `dataplaneAggregator` | Enable grafana dataplane aggregator | -| `lokiSendDashboardPanelNames` | Send dashboard and panel names to Loki when querying | -| `alertingPrometheusRulesPrimary` | Uses Prometheus rules as the primary source of truth for ruler-enabled data sources | -| `exploreLogsShardSplitting` | Used in Explore Logs to split queries into multiple queries based on the number of shards | -| `exploreLogsAggregatedMetrics` | Used in Explore Logs to query by aggregated metrics | -| `exploreLogsLimitedTimeRange` | Used in Explore Logs to limit the time range | -| `homeSetupGuide` | Used in Home for users who want to return to the onboarding flow or quickly find popular config pages | -| `appSidecar` | Enable the app sidecar feature that allows rendering 2 apps at the same time | -| `rolePickerDrawer` | Enables the new role picker drawer design | -| `pluginsSriChecks` | Enables SRI checks for plugin assets | -| `unifiedStorageBigObjectsSupport` | Enables to save big objects in blob storage | -| `timeRangeProvider` | Enables time pickers sync | -| `prometheusUsesCombobox` | Use new combobox component for Prometheus query editor | -| `playlistsReconciler` | Enables experimental reconciler for playlists | -| `prometheusSpecialCharsInLabelValues` | Adds support for quotes and special characters in label values for Prometheus queries | -| `enableExtensionsAdminPage` | Enables the extension admin page regardless of development mode | -| `enableSCIM` | Enables SCIM support for user and group management | -| `crashDetection` | Enables browser crash detection reporting to Faro. | -| `jaegerBackendMigration` | Enables querying the Jaeger data source without the proxy | -| `useV2DashboardsAPI` | Use the v2 kubernetes API in the frontend for dashboards | -| `unifiedHistory` | Displays the navigation history so the user can navigate back to previous pages | -| `investigationsBackend` | Enable the investigations backend API | -| `k8SFolderCounts` | Enable folder's api server counts | -| `k8SFolderMove` | Enable folder's api server move | -| `teamHttpHeadersMimir` | Enables LBAC for datasources for Mimir to apply LBAC filtering of metrics to the client requests for users in teams | -| `queryLibraryDashboards` | Enables Query Library feature in Dashboards | -| `grafanaAdvisor` | Enables Advisor app | -| `elasticsearchImprovedParsing` | Enables less memory intensive Elasticsearch result parsing | -| `datasourceConnectionsTab` | Shows defined connections for a data source in the plugins detail page | +| Feature toggle name | Description | +| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `live-service-web-worker` | This will use a webworker thread to processes events rather than the main thread | +| `queryOverLive` | Use Grafana Live WebSocket to execute backend queries | +| `lokiExperimentalStreaming` | Support new streaming approach for loki (prototype, needs special loki build) | +| `storage` | Configurable storage for dashboards, datasources, and resources | +| `canvasPanelNesting` | Allow elements nesting | +| `disableSecretsCompatibility` | Disable duplicated secret storage in legacy tables | +| `logRequestsInstrumentedAsUnknown` | Logs the path for requests that are instrumented as unknown | +| `showDashboardValidationWarnings` | Show warnings when dashboards do not validate against the schema | +| `mysqlAnsiQuotes` | Use double quotes to escape keyword in a MySQL query | +| `alertingBacktesting` | Rule backtesting API for alerting | +| `editPanelCSVDragAndDrop` | Enables drag and drop for CSV and Excel files | +| `lokiShardSplitting` | Use stream shards to split queries into smaller subqueries | +| `lokiQuerySplittingConfig` | Give users the option to configure split durations for Loki queries | +| `individualCookiePreferences` | Support overriding cookie preferences per user | +| `influxqlStreamingParser` | Enable streaming JSON parser for InfluxDB datasource InfluxQL query language | +| `lokiLogsDataplane` | Changes logs responses from Loki to be compliant with the dataplane specification. | +| `disableSSEDataplane` | Disables dataplane specific processing in server side expressions. | +| `alertStateHistoryLokiSecondary` | Enable Grafana to write alert state history to an external Loki instance in addition to Grafana annotations. | +| `alertStateHistoryLokiPrimary` | Enable a remote Loki instance as the primary source for state history reads. | +| `alertStateHistoryLokiOnly` | Disable Grafana alerts from emitting annotations when a remote Loki instance is available. | +| `extraThemes` | Enables extra themes | +| `lokiPredefinedOperations` | Adds predefined query operations to Loki query editor | +| `frontendSandboxMonitorOnly` | Enables monitor only in the plugin frontend sandbox (if enabled) | +| `pluginsDetailsRightPanel` | Enables right panel for the plugins details page | +| `awsDatasourcesTempCredentials` | Support temporary security credentials in AWS plugins for Grafana Cloud customers | +| `mlExpressions` | Enable support for Machine Learning in server-side expressions | +| `metricsSummary` | Enables metrics summary queries in the Tempo data source | +| `datasourceAPIServers` | Expose some datasources as apiservers. | +| `provisioning` | Next generation provisioning... and git | +| `permissionsFilterRemoveSubquery` | Alternative permission filter implementation that does not use subqueries for fetching the dashboard folder | +| `aiGeneratedDashboardChanges` | Enable AI powered features for dashboards to auto-summary changes when saving | +| `sseGroupByDatasource` | Send query to the same datasource in a single request when using server side expressions. The `cloudWatchBatchQueries` feature toggle should be enabled if this used with CloudWatch. | +| `libraryPanelRBAC` | Enables RBAC support for library panels | +| `wargamesTesting` | Placeholder feature flag for internal testing | +| `externalCorePlugins` | Allow core plugins to be loaded as external | +| `pluginsAPIMetrics` | Sends metrics of public grafana packages usage by plugins | +| `enableNativeHTTPHistogram` | Enables native HTTP Histograms | +| `disableClassicHTTPHistogram` | Disables classic HTTP Histogram (use with enableNativeHTTPHistogram) | +| `kubernetesSnapshots` | Routes snapshot requests from /api to the /apis endpoint | +| `kubernetesDashboards` | Use the kubernetes API in the frontend for dashboards | +| `kubernetesRestore` | Allow restoring objects in k8s | +| `kubernetesClientDashboardsFolders` | Route the folder and dashboard service requests to k8s | +| `datasourceQueryTypes` | Show query type endpoints in datasource API servers (currently hardcoded for testdata, expressions, and prometheus) | +| `queryService` | Register /apis/query.grafana.app/ -- will eventually replace /api/ds/query | +| `queryServiceRewrite` | Rewrite requests targeting /ds/query to the query service | +| `queryServiceFromUI` | Routes requests to the new query service | +| `cachingOptimizeSerializationMemoryUsage` | If enabled, the caching backend gradually serializes query responses for the cache, comparing against the configured `[caching]max_value_mb` value as it goes. This can can help prevent Grafana from running out of memory while attempting to cache very large query responses. | +| `prometheusCodeModeMetricNamesSearch` | Enables search for metric names in Code Mode, to improve performance when working with an enormous number of metric names | +| `alertmanagerRemoteSecondary` | Enable Grafana to sync configuration and state with a remote Alertmanager. | +| `alertmanagerRemotePrimary` | Enable Grafana to have a remote Alertmanager instance as the primary Alertmanager. | +| `alertmanagerRemoteOnly` | Disable the internal Alertmanager and only use the external one defined. | +| `extractFieldsNameDeduplication` | Make sure extracted field names are unique in the dataframe | +| `dashboardNewLayouts` | Enables experimental new dashboard layouts | +| `pluginsSkipHostEnvVars` | Disables passing host environment variable to plugin processes | +| `tableSharedCrosshair` | Enables shared crosshair in table panel | +| `kubernetesFeatureToggles` | Use the kubernetes API for feature toggle management in the frontend | +| `newFolderPicker` | Enables the nested folder picker without having nested folders enabled | +| `scopeApi` | In-development feature flag for the scope api using the app platform. | +| `sqlExpressions` | Enables using SQL and DuckDB functions as Expressions. | +| `nodeGraphDotLayout` | Changed the layout algorithm for the node graph | +| `kubernetesAggregator` | Enable grafana's embedded kube-aggregator | +| `expressionParser` | Enable new expression parser | +| `disableNumericMetricsSortingInExpressions` | In server-side expressions, disable the sorting of numeric-kind metrics by their metric name or labels. | +| `queryLibrary` | Enables Query Library feature in Explore | +| `logsExploreTableDefaultVisualization` | Sets the logs table as default visualisation in logs explore | +| `alertingListViewV2` | Enables the new alert list view design | +| `dashboardRestore` | Enables deleted dashboard restore feature | +| `alertingCentralAlertHistory` | Enables the new central alert history. | +| `failWrongDSUID` | Throws an error if a datasource has an invalid UIDs | +| `dataplaneAggregator` | Enable grafana dataplane aggregator | +| `lokiSendDashboardPanelNames` | Send dashboard and panel names to Loki when querying | +| `alertingPrometheusRulesPrimary` | Uses Prometheus rules as the primary source of truth for ruler-enabled data sources | +| `exploreLogsShardSplitting` | Used in Logs Drilldown to split queries into multiple queries based on the number of shards | +| `exploreLogsAggregatedMetrics` | Used in Logs Drilldown to query by aggregated metrics | +| `exploreLogsLimitedTimeRange` | Used in Logs Drilldown to limit the time range | +| `homeSetupGuide` | Used in Home for users who want to return to the onboarding flow or quickly find popular config pages | +| `appSidecar` | Enable the app sidecar feature that allows rendering 2 apps at the same time | +| `rolePickerDrawer` | Enables the new role picker drawer design | +| `pluginsSriChecks` | Enables SRI checks for plugin assets | +| `unifiedStorageBigObjectsSupport` | Enables to save big objects in blob storage | +| `timeRangeProvider` | Enables time pickers sync | +| `prometheusUsesCombobox` | Use new combobox component for Prometheus query editor | +| `playlistsReconciler` | Enables experimental reconciler for playlists | +| `exploreMetricsRelatedLogs` | Display Related Logs in Grafana Metrics Drilldown | +| `prometheusSpecialCharsInLabelValues` | Adds support for quotes and special characters in label values for Prometheus queries | +| `enableExtensionsAdminPage` | Enables the extension admin page regardless of development mode | +| `enableSCIM` | Enables SCIM support for user and group management | +| `crashDetection` | Enables browser crash detection reporting to Faro. | +| `jaegerBackendMigration` | Enables querying the Jaeger data source without the proxy | +| `useV2DashboardsAPI` | Use the v2 kubernetes API in the frontend for dashboards | +| `unifiedHistory` | Displays the navigation history so the user can navigate back to previous pages | +| `investigationsBackend` | Enable the investigations backend API | +| `k8SFolderCounts` | Enable folder's api server counts | +| `k8SFolderMove` | Enable folder's api server move | +| `teamHttpHeadersMimir` | Enables LBAC for datasources for Mimir to apply LBAC filtering of metrics to the client requests for users in teams | +| `templateVariablesUsesCombobox` | Use new combobox component for template variables | +| `grafanaAdvisor` | Enables Advisor app | +| `elasticsearchImprovedParsing` | Enables less memory intensive Elasticsearch result parsing | +| `datasourceConnectionsTab` | Shows defined connections for a data source in the plugins detail page | +| `newLogsPanel` | Enables the new logs panel in Explore | +| `pluginsCDNSyncLoader` | Load plugins from CDN synchronously | ## Development feature toggles diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/_index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/_index.md index 1c6b0ce9256..3e20606bb1e 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/_index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/_index.md @@ -25,6 +25,7 @@ The following table shows all supported authentication methods and the features | [Auth Proxy]({{< relref "./auth-proxy" >}}) | no | yes | yes | no | yes | no | N/A | no | N/A | N/A | | [Azure AD OAuth]({{< relref "./azuread" >}}) | yes | yes | yes | yes | yes | yes | N/A | yes | yes | yes | | [Basic auth]({{< relref "./grafana" >}}) | yes | N/A | yes | yes | N/A | N/A | N/A | N/A | N/A | N/A | +| [Passwordless auth]({{< relref "./passwordless" >}}) | yes | N/A | yes | yes | N/A | N/A | N/A | N/A | N/A | N/A | | [Generic OAuth]({{< relref "./generic-oauth" >}}) | yes | yes | yes | yes | yes | no | N/A | yes | yes | yes | | [GitHub OAuth]({{< relref "./github" >}}) | yes | yes | yes | yes | yes | yes | N/A | yes | yes | yes | | [GitLab OAuth]({{< relref "./gitlab" >}}) | yes | yes | yes | yes | yes | yes | N/A | yes | yes | yes | diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/passwordless/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/passwordless/index.md new file mode 100644 index 00000000000..4a4874ecb2a --- /dev/null +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/passwordless/index.md @@ -0,0 +1,48 @@ +--- +description: Learn how to configure passwordless authentication with magic links in Grafana +labels: + products: + - enterprise + - oss +menuTitle: Passwordless +title: Configure passwordless authentication with magic links +weight: 200 +--- + +# Configure passwordless authentication with magic links + +Passwordless authentication lets Grafana users authenticate with a magic link or one-time password (OTP) sent via email. + +## Enable passwordless authentication + +{{% admonition type="note" %}} +Passwordless authentication is an experimental feature. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the `passwordlessMagicLinkAuthentication` feature toggle in Grafana to use this feature. +{{% /admonition %}} + +To enable passwordless authentication, use the following configuration: + +```bash +[auth.passwordless] +enabled = true +``` + +## Code expiration + +By default, the one-time password (OTP) sent to a user's email is valid for 20 minutes. Use the `code_expiration` option to change the duration that the OTP is valid. + +```bash +[auth.passwordless] +enabled = true +code_expiration = 20m +``` + +## Enable SMTP server + +The SMTP server must be enabled so that Grafana can send emails. +The following configuration enables the SMTP server. +For more information on configuring the SMTP server, refer to [SMTP](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#smtp). + +```bash +[smtp] +enabled = true +``` diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/saml-ui/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/saml-ui/index.md index 1be5e2d993e..0a192c80baf 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/saml-ui/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/saml-ui/index.md @@ -113,8 +113,26 @@ Sign in to Grafana and navigate to **Administration > Authentication > Configure 1. If you wish to [map user information from SAML assertions]({{< relref "../saml#assertion-mapping" >}}), complete the **Assertion attributes mappings** section. - You also need to configure the **Groups attribute** field if you want to use group synchronization. Group sync allows you to automatically map users to Grafana teams or role-based access control roles based on their SAML group membership. - To learn more about how to configure group synchronization, refer to [Configure team sync]({{< relref "../../configure-team-sync" >}}) and [Configure group attribute sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-group-attribute-sync) documentation. +If Azure is the Identity Provider over SAML there are caveats for the assertion attribute mappings. Due to how Azure interprets these attributes the full URL will need to be entered in the corresponding fields within the UI, which should match the URLs from the metadata XML. There are differences depending on whether it's a Role or Group claim vs other assertions which Microsoft has [documented](https://learn.microsoft.com/en-us/entra/identity-platform/reference-claims-customization#table-2-saml-restricted-claim-set). + +Group and Role: + +``` +http://schemas.microsoft.com/ws/2008/06/identity/claims/role +http://schemas.microsoft.com/ws/2008/06/identity/claims/groups +http://schemas.microsoft.com/identity/claims/displayname +``` + +Other Assertions: + +``` +http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress +``` + +![image](https://github.com/user-attachments/assets/23910ab8-20ec-4dfd-8ef6-7dbaec51ac90) + +You also need to configure the **Groups attribute** field if you want to use group synchronization. Group sync allows you to automatically map users to Grafana teams or role-based access control roles based on their SAML group membership. +To learn more about how to configure group synchronization, refer to [Configure team sync]({{< relref "../../configure-team-sync" >}}) and [Configure group attribute sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-group-attribute-sync) documentation. 1. If you want to automatically assign users' roles based on their SAML roles, complete the **Role mapping** section. diff --git a/docs/sources/setup-grafana/configure-security/configure-team-sync.md b/docs/sources/setup-grafana/configure-security/configure-team-sync.md index f5d50cff311..89665525574 100644 --- a/docs/sources/setup-grafana/configure-security/configure-team-sync.md +++ b/docs/sources/setup-grafana/configure-security/configure-team-sync.md @@ -27,16 +27,15 @@ This mechanism allows Grafana to remove an existing synchronized user from a tea ## Supported providers -- [Auth Proxy]({{< relref "./configure-authentication/auth-proxy#team-sync-enterprise-only" >}}) -- [Azure AD](https://grafana.com/docs/grafana//configure-authentication/azuread#group-sync-enterprise-only) -- [Azure AD](https://grafana.com/docs/grafana//configure-security/configure-authentication/azuread#group-sync-enterprise-only) -- [Generic OAuth integration](https://grafana.com/docs/grafana//configure-security/configure-authentication/generic-oauth#configure-group-synchronization) -- [GitHub OAuth](https://grafana.com/docs/grafana//configure-security/configure-authentication/github#configure-group-synchronization) -- [GitLab OAuth](https://grafana.com/docs/grafana//configure-security/configure-authentication/gitlab#configure-group-synchronization) -- [Google OAuth](https://grafana.com/docs/grafana//configure-security/configure-authentication/google#configure-group-synchronization) -- [LDAP](https://grafana.com/docs/grafana//configure-security/configure-authentication/enhanced-ldap#ldap-group-synchronization) -- [Okta](https://grafana.com/docs/grafana//configure-security/configure-authentication/okta#configure-group-synchronization-enterprise-only) -- [SAML](https://grafana.com/docs/grafana//configure-security/configure-authentication/saml#configure-group-synchronization) +- [Auth Proxy](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/auth-proxy/#team-sync-enterprise-only) +- [Azure AD](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/azuread#group-sync-enterprise-only) +- [Generic OAuth integration](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/generic-oauth#configure-group-synchronization) +- [GitHub OAuth](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/github#configure-group-synchronization) +- [GitLab OAuth](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/gitlab#configure-group-synchronization) +- [Google OAuth](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/google#configure-group-synchronization) +- [LDAP](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/enhanced-ldap#ldap-group-synchronization) +- [Okta](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/okta#configure-group-synchronization-enterprise-only) +- [SAML](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/saml#configure-group-synchronization) ## Synchronize a Grafana team with an external group diff --git a/docs/sources/setup-grafana/image-rendering/_index.md b/docs/sources/setup-grafana/image-rendering/_index.md index 639f00b6f79..c3f1fc0425a 100644 --- a/docs/sources/setup-grafana/image-rendering/_index.md +++ b/docs/sources/setup-grafana/image-rendering/_index.md @@ -20,8 +20,6 @@ weight: 1000 Grafana supports automatic rendering of panels as PNG images. This allows Grafana to automatically generate images of your panels to include in alert notifications, [PDF export]({{< relref "../../dashboards/create-reports#export-dashboard-as-pdf" >}}), and [Reporting]({{< relref "../../dashboards/create-reports" >}}). PDF Export and Reporting are available only in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise" >}}) and [Grafana Cloud](/docs/grafana-cloud/). -> **Note:** Image rendering of dashboards is not supported at this time. - While an image is being rendered, the PNG image is temporarily written to the file system. When the image is rendered, the PNG image is temporarily written to the `png` folder in the Grafana `data` folder. A background job runs every 10 minutes and removes temporary images. You can configure how long an image should be stored before being removed by configuring the [temp_data_lifetime]({{< relref "../configure-grafana#temp_data_lifetime" >}}) setting. @@ -223,10 +221,14 @@ HTTPS protocol is supported in the image renderer v3.11.0 and later. Change the protocol of the server, it can be `http` or `https`. Default is `http`. +```bash +HTTP_PROTOCOL=https +``` + ```json { "service": { - "protocol": "http" + "protocol": "https" } } ``` @@ -235,6 +237,11 @@ Change the protocol of the server, it can be `http` or `https`. Default is `http Path to the image renderer certificate and key file used to start an HTTPS server. +```bash +HTTP_CERT_FILE=./path/to/cert +HTTP_CERT_KEY=./path/to/key +``` + ```json { "service": { @@ -248,6 +255,10 @@ Path to the image renderer certificate and key file used to start an HTTPS serve Minimum TLS version allowed. Accepted values are: `TLSv1.2`, `TLSv1.3`. Default is `TLSv1.2`. +```bash +HTTP_MIN_TLS_VERSION=TLSv1.2 +``` + ```json { "service": { diff --git a/docs/sources/setup-grafana/installation/_index.md b/docs/sources/setup-grafana/installation/_index.md index 940a80aa91c..44c69f8ab47 100644 --- a/docs/sources/setup-grafana/installation/_index.md +++ b/docs/sources/setup-grafana/installation/_index.md @@ -32,7 +32,7 @@ Grafana relies on other open source software to operate. For a list of open sour Grafana supports the following operating systems: - [Debian or Ubuntu]({{< relref "./debian" >}}) -- [Red Hat, RHEL, or Fedora]({{< relref "./redhat-rhel-fedora" >}}) +- [RHEL or Fedora]({{< relref "./rhel-fedora" >}}) - [SUSE or openSUSE]({{< relref "./suse-opensuse" >}}) - [macOS]({{< relref "./mac" >}}) - [Windows]({{< relref "./windows" >}}) diff --git a/docs/sources/setup-grafana/installation/debian/index.md b/docs/sources/setup-grafana/installation/debian/index.md index 1aeb3203e4b..09965c27678 100644 --- a/docs/sources/setup-grafana/installation/debian/index.md +++ b/docs/sources/setup-grafana/installation/debian/index.md @@ -18,9 +18,9 @@ This topic explains how to install Grafana dependencies, install Grafana on Linu There are multiple ways to install Grafana: using the Grafana Labs APT repository, by downloading a `.deb` package, or by downloading a binary `.tar.gz` file. Choose only one of the methods below that best suits your needs. -{{% admonition type="note" %}} +{{< admonition type="note" >}} If you install via the `.deb` package or `.tar.gz` file, then you must manually update Grafana for each new version. -{{% /admonition %}} +{{< /admonition >}} The following video demonstrates how to install Grafana on Debian and Ubuntu as outlined in this document: @@ -37,9 +37,9 @@ If you install from the APT repository, Grafana automatically updates when you r | Grafana OSS | grafana | `https://apt.grafana.com stable main` | | Grafana OSS (Beta) | grafana | `https://apt.grafana.com beta main` | -{{% admonition type="note" %}} +{{< admonition type="note" >}} Grafana Enterprise is the recommended and default edition. It is available for free and includes all the features of the OSS edition. You can also upgrade to the [full Enterprise feature set](/products/enterprise/?utm_source=grafana-install-page), which has support for [Enterprise plugins](/grafana/plugins/?enterprise=1&utcm_source=grafana-install-page). -{{% /admonition %}} +{{< /admonition >}} Complete the following steps to install Grafana from the APT repository: @@ -89,11 +89,11 @@ Complete the following steps to install Grafana from the APT repository: sudo apt-get install grafana-enterprise ``` -## Install Grafana using a deb package or as a standalone binary +## Install Grafana using a deb package -If you choose not to install Grafana using APT, you can download and install Grafana using the deb package or as a standalone binary. +If you install Grafana manually using the deb package, then you must manually update Grafana for each new version. -Complete the following steps to install Grafana using DEB or the standalone binaries: +Complete the following steps to install Grafana using a deb package: 1. Navigate to the [Grafana download page](/grafana/download). 1. Select the Grafana version you want to install. @@ -105,6 +105,80 @@ Complete the following steps to install Grafana using DEB or the standalone bina 1. Depending on which system you are running, click the **Linux** or **ARM** tab on the [download page](/grafana/download). 1. Copy and paste the code from the [download page](/grafana/download) into your command line and run. +## Install Grafana as a standalone binary + +Complete the following steps to install Grafana using the standalone binaries: + +1. Navigate to the [Grafana download page](/grafana/download). +1. Select the Grafana version you want to install. + - The most recent Grafana version is selected by default. + - The **Version** field displays only tagged releases. If you want to install a nightly build, click **Nightly Builds** and then select a version. +1. Select an **Edition**. + - **Enterprise:** This is the recommended version. It is functionally identical to the open source version but includes features you can unlock with a license if you so choose. + - **Open Source:** This version is functionally identical to the Enterprise version, but you will need to download the Enterprise version if you want Enterprise features. +1. Depending on which system you are running, click the **Linux** or **ARM** tab on the [download page](/grafana/download). +1. Copy and paste the code from the [download page](/grafana/download) page into your command line and run. +1. Create a user account for Grafana on your system: + + ```shell + sudo useradd -r -s /bin/false grafana + ``` + +1. Move the unpacked binary to `/usr/local/grafana`: + + ```shell + sudo mv /usr/local/grafana + ``` + +1. Change the owner of `/usr/local/grafana` to Grafana users: + + ```shell + sudo chown -R grafana:users /usr/local/grafana + ``` + +1. Create a Grafana server systemd unit file: + + ```shell + sudo touch /etc/systemd/system/grafana-server.service + ``` + +1. Add the following to the unit file in a text editor of your choice: + + ```ini + [Unit] + Description=Grafana Server + After=network.target + + [Service] + Type=simple + User=grafana + Group=users + ExecStart=/usr/local/grafana/bin/grafana server --config=/usr/local/grafana/conf/grafana.ini --homepath=/usr/local/grafana + Restart=on-failure + + [Install] + WantedBy=multi-user.target + ``` + +1. Use the binary to manually start the Grafana server: + + ```shell + /usr/local/grafana/bin/grafana-server --homepath /usr/local/grafana + ``` + + {{< admonition type="note" >}} + Manually invoking the binary in this step automatically creates the `/usr/local/grafana/data` directory, which needs to be created and configured before the installation can be considered complete. + {{< /admonition >}} + +1. Press `CTRL+C` to stop the Grafana server. +1. Change the owner of `/usr/local/grafana` to Grafana users again to apply the ownership to the newly created `/usr/local/grafana/data` directory: + + ```shell + sudo chown -R grafana:users /usr/local/grafana + ``` + +1. [Configure the Grafana server to start at boot time using systemd](https://grafana.com/docs/grafana/latest/setup-grafana/start-restart-grafana/#configure-the-grafana-server-to-start-at-boot-using-systemd). + ## Uninstall on Debian or Ubuntu Complete any of the following steps to uninstall Grafana. diff --git a/docs/sources/setup-grafana/installation/redhat-rhel-fedora/index.md b/docs/sources/setup-grafana/installation/redhat-rhel-fedora/index.md index 2a24eadc392..4a23ffb832c 100644 --- a/docs/sources/setup-grafana/installation/redhat-rhel-fedora/index.md +++ b/docs/sources/setup-grafana/installation/redhat-rhel-fedora/index.md @@ -32,15 +32,15 @@ If you install from the RPM repository, then Grafana is automatically updated ev | Grafana OSS | grafana | `https://rpm.grafana.com` | | Grafana OSS (Beta) | grafana | `https://rpm-beta.grafana.com` | -{{% admonition type="note" %}} +{{< admonition type="note" >}} Grafana Enterprise is the recommended and default edition. It is available for free and includes all the features of the OSS edition. You can also upgrade to the [full Enterprise feature set](/products/enterprise/?utm_source=grafana-install-page), which has support for [Enterprise plugins](/grafana/plugins/?enterprise=1&utcm_source=grafana-install-page). -{{% /admonition %}} +{{< /admonition >}} To install Grafana from the RPM repository, complete the following steps: -{{% admonition type="note" %}} +{{< admonition type="note" >}} If you wish to install beta versions of Grafana, substitute the repository URL for the beta URL listed above. -{{% /admonition %}} +{{< /admonition >}} 1. Import the GPG key: @@ -96,6 +96,8 @@ If you install Grafana manually using YUM or RPM, then you must manually update ## Install Grafana as a standalone binary +If you install Grafana manually using the standalone binaries, then you must manually update Grafana for each new version. + Complete the following steps to install Grafana using the standalone binaries: 1. Navigate to the [Grafana download page](/grafana/download). @@ -103,10 +105,70 @@ Complete the following steps to install Grafana using the standalone binaries: - The most recent Grafana version is selected by default. - The **Version** field displays only tagged releases. If you want to install a nightly build, click **Nightly Builds** and then select a version. 1. Select an **Edition**. - - **Enterprise:** This is the recommended version. It is functionally identical to the open-source version but includes features you can unlock with a license if you so choose. + - **Enterprise:** This is the recommended version. It is functionally identical to the open source version but includes features you can unlock with a license if you so choose. - **Open Source:** This version is functionally identical to the Enterprise version, but you will need to download the Enterprise version if you want Enterprise features. 1. Depending on which system you are running, click the **Linux** or **ARM** tab on the [download page](/grafana/download). 1. Copy and paste the code from the [download page](/grafana/download) page into your command line and run. +1. Create a user account for Grafana on your system: + + ```shell + sudo useradd -r -s /bin/false grafana + ``` + +1. Move the unpacked binary to `/usr/local/grafana`: + + ```shell + sudo mv /usr/local/grafana + ``` + +1. Change the owner of `/usr/local/grafana` to Grafana users: + + ```shell + sudo chown -R grafana:users /usr/local/grafana + ``` + +1. Create a Grafana server systemd unit file: + + ```shell + sudo touch /etc/systemd/system/grafana-server.service + ``` + +1. Add the following to the unit file in a text editor of your choice: + + ```ini + [Unit] + Description=Grafana Server + After=network.target + + [Service] + Type=simple + User=grafana + Group=users + ExecStart=/usr/local/grafana/bin/grafana server --config=/usr/local/grafana/conf/grafana.ini --homepath=/usr/local/grafana + Restart=on-failure + + [Install] + WantedBy=multi-user.target + ``` + +1. Use the binary to manually start the Grafana server: + + ```shell + /usr/local/grafana/bin/grafana-server --homepath /usr/local/grafana + ``` + + {{< admonition type="note" >}} + Manually invoking the binary in this step automatically creates the `/usr/local/grafana/data` directory, which needs to be created and configured before the installation can be considered complete. + {{< /admonition >}} + +1. Press `CTRL+C` to stop the Grafana server. +1. Change the owner of `/usr/local/grafana` to Grafana users again to apply the ownership to the newly created `/usr/local/grafana/data` directory: + + ```shell + sudo chown -R grafana:users /usr/local/grafana + ``` + +1. [Configure the Grafana server to start at boot time using systemd]({{< relref "../../start-restart-grafana#configure-the-grafana-server-to-start-at-boot-using-systemd" >}}). ## Uninstall on RHEL or Fedora diff --git a/docs/sources/setup-grafana/installation/suse-opensuse/index.md b/docs/sources/setup-grafana/installation/suse-opensuse/index.md index 48b505ba52d..09f76c8dd5e 100644 --- a/docs/sources/setup-grafana/installation/suse-opensuse/index.md +++ b/docs/sources/setup-grafana/installation/suse-opensuse/index.md @@ -30,9 +30,9 @@ If you install from the RPM repository, then Grafana is automatically updated ev | Grafana Enterprise | grafana-enterprise | `https://rpm.grafana.com` | | Grafana OSS | grafana | `https://rpm.grafana.com` | -{{% admonition type="note" %}} +{{< admonition type="note" >}} Grafana Enterprise is the recommended and default edition. It is available for free and includes all the features of the OSS edition. You can also upgrade to the [full Enterprise feature set](/products/enterprise/?utm_source=grafana-install-page), which has support for [Enterprise plugins](/grafana/plugins/?enterprise=1&utcm_source=grafana-install-page). -{{% /admonition %}} +{{< /admonition >}} To install Grafana using the RPM repository, complete the following steps: @@ -84,6 +84,8 @@ If you install Grafana manually using RPM, then you must manually update Grafana ## Install Grafana as a standalone binary +If you install Grafana manually using the standalone binaries, then you must manually update Grafana for each new version. + Complete the following steps to install Grafana using the standalone binaries: 1. Navigate to the [Grafana download page](/grafana/download). @@ -91,10 +93,70 @@ Complete the following steps to install Grafana using the standalone binaries: - The most recent Grafana version is selected by default. - The **Version** field displays only tagged releases. If you want to install a nightly build, click **Nightly Builds** and then select a version. 1. Select an **Edition**. - - **Enterprise:** This is the recommended version. It is functionally identical to the open-source version but includes features you can unlock with a license if you so choose. + - **Enterprise:** This is the recommended version. It is functionally identical to the open source version but includes features you can unlock with a license if you so choose. - **Open Source:** This version is functionally identical to the Enterprise version, but you will need to download the Enterprise version if you want Enterprise features. 1. Depending on which system you are running, click the **Linux** or **ARM** tab on the [download page](/grafana/download). 1. Copy and paste the code from the [download page](/grafana/download) into your command line and run. +1. Create a user account for Grafana on your system: + + ```shell + sudo useradd -r -s /bin/false grafana + ``` + +1. Move the unpacked binary to `/usr/local/grafana`: + + ```shell + sudo mv /usr/local/grafana + ``` + +1. Change the owner of `/usr/local/grafana` to Grafana users: + + ```shell + sudo chown -R grafana:users /usr/local/grafana + ``` + +1. Create a Grafana server systemd unit file: + + ```shell + sudo touch /etc/systemd/system/grafana-server.service + ``` + +1. Add the following to the unit file in a text editor of your choice: + + ```ini + [Unit] + Description=Grafana Server + After=network.target + + [Service] + Type=simple + User=grafana + Group=users + ExecStart=/usr/local/grafana/bin/grafana server --config=/usr/local/grafana/conf/grafana.ini --homepath=/usr/local/grafana + Restart=on-failure + + [Install] + WantedBy=multi-user.target + ``` + +1. Use the binary to manually start the Grafana server: + + ```shell + /usr/local/grafana/bin/grafana-server --homepath /usr/local/grafana + ``` + + {{< admonition type="note" >}} + Manually invoking the binary in this step automatically creates the `/usr/local/grafana/data` directory, which needs to be created and configured before the installation can be considered complete. + {{< /admonition >}} + +1. Press `CTRL+C` to stop the Grafana server. +1. Change the owner of `/usr/local/grafana` to Grafana users again to apply the ownership to the newly created `/usr/local/grafana/data` directory: + + ```shell + sudo chown -R grafana:users /usr/local/grafana + ``` + +1. [Configure the Grafana server to start at boot time using systemd](https://grafana.com/docs/grafana/latest/setup-grafana/start-restart-grafana/#configure-the-grafana-server-to-start-at-boot-using-systemd). ## Uninstall on SUSE or openSUSE diff --git a/docs/sources/setup-grafana/set-up-https.md b/docs/sources/setup-grafana/set-up-https.md index 20ce98bb2a0..cb508a87898 100644 --- a/docs/sources/setup-grafana/set-up-https.md +++ b/docs/sources/setup-grafana/set-up-https.md @@ -48,13 +48,13 @@ This section shows you how to use `openssl` tooling to generate all necessary fi 1. Run the following command to generate a 2048-bit RSA private key, which is used to decrypt traffic: ```bash - $ sudo openssl genrsa -out /etc/grafana/grafana.key 2048 + sudo openssl genrsa -out /etc/grafana/grafana.key 2048 ``` 1. Run the following command to generate a certificate, using the private key from the previous step. ```bash - $ sudo openssl req -new -key /etc/grafana/grafana.key -out /etc/grafana/grafana.csr + sudo openssl req -new -key /etc/grafana/grafana.key -out /etc/grafana/grafana.csr ``` When prompted, answer the questions, which might include your fully-qualified domain name, email address, country code, and others. The following example is similar to the prompts you will see. @@ -209,7 +209,7 @@ To adjust permissions, perform the following steps: $ sudo chgrp -R grafana /etc/letsencrypt/* $ sudo chmod -R g+rx /etc/letsencrypt/* $ sudo chgrp -R grafana /etc/grafana/grafana.crt /etc/grafana/grafana.key - $ sudo chmod 400 /etc/grafana/grafana.crt /etc/grafana/grafana.key + $ sudo chmod 440 /etc/grafana/grafana.crt /etc/grafana/grafana.key ``` 1. Run the following command to verify that the `grafana` group can read the symlinks: @@ -244,6 +244,8 @@ To configure Grafana HTTPS and restart Grafana, complete the following steps. > **Note**: The standard port for SSL traffic is 443, which you can use instead of Grafana's default port 3000. This change might require additional operating system privileges or configuration to bind to lower-numbered privileged ports. +1. Optional. From Grafana v11.2, edit the `cert_pass` configuration option with the decryption password if you are using encrypted certificates. + 1. [Restart the Grafana server]({{< relref "./start-restart-grafana#linux" >}}) using `systemd`, `init.d`, or the binary as appropriate for your environment. ## Troubleshooting diff --git a/docs/sources/shared/datasources/sql-query-builder-macros.md b/docs/sources/shared/datasources/sql-query-builder-macros.md index 10425a73ab4..686302fee78 100644 --- a/docs/sources/shared/datasources/sql-query-builder-macros.md +++ b/docs/sources/shared/datasources/sql-query-builder-macros.md @@ -10,8 +10,6 @@ labels: You can enable macros support in the select clause to create time-series queries. -{{< docs/experimental product="Macros support in visual query builder" featureFlag="`sqlQuerybuilderFunctionParameters`" >}} - Use the **Data operations** drop-down to select a macro like `$__timeGroup` or `$__timeGroupAlias`. Select a time column from the **Column** drop-down and a time interval from the **Interval** drop-down to create a time-series query. diff --git a/docs/sources/shared/plugins/rename-note.md b/docs/sources/shared/plugins/rename-note.md new file mode 100644 index 00000000000..5eaa9a1adf4 --- /dev/null +++ b/docs/sources/shared/plugins/rename-note.md @@ -0,0 +1,19 @@ +--- +headless: true +labels: + products: + - enterprise + - oss +--- + +[//]: # 'This file contains a rename note for Explore to Drilldown apps.' +[//]: # 'This shared file is included in a lot of files. Check the app docs in' +[//]: # 'drilldown-traces, drilldown-logs, drilldown-profiles, grafana, and website/grafana-cloud.' +[//]: # 'If you make changes to this file, verify that the meaning and content are not changed in any place where the file is included.' +[//]: # 'Any links should be fully qualified and not relative: /docs/grafana/ instead of ../grafana/.' + +{{< admonition type="note" >}} +The Grafana Explore apps have changed to Grafana Drilldown apps. +For example, Explore Logs is now Logs Drilldown. +To learn more, read [Grafana Drilldown apps: the improved queryless experience known as the Explore apps](https://grafana.com/blog/2025/02/20/grafana-drilldown-apps-the-improved-queryless-experience-formerly-known-as-the-explore-apps/). +{{< /admonition >}} diff --git a/docs/sources/shared/visualizations/axis-options-all.md b/docs/sources/shared/visualizations/axis-options-all.md new file mode 100644 index 00000000000..e2c833ab9e2 --- /dev/null +++ b/docs/sources/shared/visualizations/axis-options-all.md @@ -0,0 +1,21 @@ +--- +title: Axis options +comments: | + This file is used in the following visualizations: time series. +--- + +Options under the **Axis** section control how the x- and y-axes are rendered. Some options don't take effect until you click outside of the field option box you're editing. You can also press `Enter`. + +| Option | Description | +| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| Time zone | Set the desired time zones to display along the x-axis. | +| [Placement](#placement) | Select the placement of the y-axis. | +| Label | Set a y-axis text label. If you have more than one y-axis, then you can assign different labels using an override. | +| Width | Set a fixed width for the axis. By default, Grafana dynamically calculates the width of an axis. | +| Show grid lines | Set the axis grid line visibility.
| +| Color | Set the color of the axis. | +| Show border | Set the axis border visibility. | +| Scale | Set the y-axis values scale.
| +| Centered zero | Set the y-axis so it's centered on zero. | +| [Soft min](#soft-min-and-soft-max) | Set a soft min to better control the y-axis limits. zero. | +| [Soft max](#soft-min-and-soft-max) | Set a soft max to better control the y-axis limits. zero. | diff --git a/docs/sources/shared/visualizations/axis-options-state-status.md b/docs/sources/shared/visualizations/axis-options-state-status.md new file mode 100644 index 00000000000..8b1bacfd78c --- /dev/null +++ b/docs/sources/shared/visualizations/axis-options-state-status.md @@ -0,0 +1,10 @@ +--- +title: Axis options +comments: | + This file is used in the following visualizations: state timeline, status history. +--- + +| Option | Description | +| --------- | ------------------------------------------------------------------------------------------------ | +| Placement | Control the visibility of series names along the y-axis or time values along the x-axis. | +| Width | Set a fixed width for the axis. By default, Grafana dynamically calculates the width of an axis. | diff --git a/docs/sources/shared/visualizations/datalink-options-1.md b/docs/sources/shared/visualizations/datalink-options-1.md new file mode 100644 index 00000000000..5cfe706abf6 --- /dev/null +++ b/docs/sources/shared/visualizations/datalink-options-1.md @@ -0,0 +1,22 @@ +--- +title: Data links and actions options +comments: | + This file is used in the following visualizations: bar gauge, gauge, pie chart, stat +--- + +Data links allow you to link to other panels, dashboards, and external resources while maintaining the context of the source panel. +You can create links that include the series name or even the value under the cursor. +To learn more, refer to [Configure data links and actions](../../configure-data-links/). + +{{< admonition type="note" >}} +Actions are not supported for this visualization. +{{< /admonition >}} + +For each data link, set the following options: + +- **Title** +- **URL** +- **Open in new tab** + +Data links for this visualization don't include the **One click** switch, however, if there's only one data link configured, that data link has single-click functionality. +If multiple data links are configured, then clicking the visualization opens a menu that displays all the data links. diff --git a/docs/sources/shared/visualizations/datalink-options-2.md b/docs/sources/shared/visualizations/datalink-options-2.md new file mode 100644 index 00000000000..b74b753b4e6 --- /dev/null +++ b/docs/sources/shared/visualizations/datalink-options-2.md @@ -0,0 +1,34 @@ +--- +title: Data links and actions options +comments: | + This file is used in the following visualizations: bar chart, candlestick, state timeline, status history, time series, trend, xy chart +--- + +_Data links_ allow you to link to other panels, dashboards, and external resources and _actions_ let you trigger basic, unauthenticated, API calls. +In both cases, you can carry out these tasks while maintaining the context of the source panel. + +For each data link, set the following options: + +- **Title** +- **URL** +- **Open in new tab** +- **One click** - Opens the data link with a single click. Only one data link can have **One click** enabled at a time. + +For each action, define the following API call settings: + + + +| Option | Description | +| -------------------- | ----------------------------------------------------------------- | +| Title | A human-readable label for the action that's displayed in the UI. | +| Confirmation message | A descriptive prompt to confirm or cancel the action. | +| Method | Select from **POST**, **PUT**, or **GET**. | +| URL | The request URL.

To add a variable, click in the **URL** field and enter `$` or press Ctrl+Space or Cmd+Space to see a list of available variables. | +| Query parameters | **Key** and **Value** pairs. Click the **+** icon to add as many key/value pairs as you need. | +| Headers | Comprised of **Key** and **Value** pairs and a **Content-Type**.

Click the **+** icon to add as many key/value pairs as you need. | +| Content-Type | Select from the following: **application/json**, **text/plain**, **application/XML**, and **application/x-www-form-urlencoded**. | +| Body | The body of the request. | + + + +To learn more, refer to [Configure data links and actions](../../configure-data-links/). diff --git a/docs/sources/shared/visualizations/datalink-options-3.md b/docs/sources/shared/visualizations/datalink-options-3.md new file mode 100644 index 00000000000..738a5898596 --- /dev/null +++ b/docs/sources/shared/visualizations/datalink-options-3.md @@ -0,0 +1,36 @@ +--- +title: Data links and actions options +comments: | + This file is used in the following visualizations: table +--- + +_Data links_ allow you to link to other panels, dashboards, and external resources and _actions_ let you trigger basic, unauthenticated, API calls. +In both cases, you can carry out these tasks while maintaining the context of the source panel. + +For each data link, set the following options: + +- **Title** +- **URL** +- **Open in new tab** + +Data links for this visualization don't include the **One click** switch, however, if there's only one data link configured, that data link has single-click functionality. +If multiple data links are configured, then clicking the visualization opens a menu that displays all the data links. + +For each action, define the following API call settings: + + + +| Option | Description | +| -------------------- | ----------------------------------------------------------------- | +| Title | A human-readable label for the action that's displayed in the UI. | +| Confirmation message | A descriptive prompt to confirm or cancel the action. | +| Method | Select from **POST**, **PUT**, or **GET**. | +| URL | The request URL.

To add a variable, click in the **URL** field and enter `$` or press Ctrl+Space or Cmd+Space to see a list of available variables. | +| Query parameters | **Key** and **Value** pairs. Click the **+** icon to add as many key/value pairs as you need. | +| Headers | Comprised of **Key** and **Value** pairs and a **Content-Type**.

Click the **+** icon to add as many key/value pairs as you need. | +| Content-Type | Select from the following: **application/json**, **text/plain**, **application/XML**, and **application/x-www-form-urlencoded**. | +| Body | The body of the request. | + + + +To learn more, refer to [Configure data links and actions](../../configure-data-links/). diff --git a/docs/sources/shared/visualizations/datalink-options.md b/docs/sources/shared/visualizations/datalink-options.md index e3d77551cf5..131640d86ee 100644 --- a/docs/sources/shared/visualizations/datalink-options.md +++ b/docs/sources/shared/visualizations/datalink-options.md @@ -1,10 +1,15 @@ --- -title: Data link options +title: Data links and actions options comments: | - This file is used in the following visualizations: bar chart, bar gauge, candlestick, gauge, geomap, heatmap, histogram, stat, state timeline, status history, table, time series, trend, xy chart + This file is used in the following visualizations: geomap, heatmap, histogram --- -Data links allow you to link to other panels, dashboards, and external resources while maintaining the context of the source panel. You can create links that include the series name or even the value under the cursor. +Data links allow you to link to other panels, dashboards, and external resources while maintaining the context of the source panel. +You can create links that include the series name or even the value under the cursor. + +{{< admonition type="note" >}} +Actions are not supported for this visualization. +{{< /admonition >}} For each data link, set the following options: @@ -12,4 +17,4 @@ For each data link, set the following options: - **URL** - **Open in new tab** -To learn more, refer to [Configure data links](../../configure-data-links/). +To learn more, refer to [Configure data links and actions](../../configure-data-links/). diff --git a/docs/sources/tutorials/alerting-get-started-pt2/index.md b/docs/sources/tutorials/alerting-get-started-pt2/index.md index bcc77632336..3beabdf0369 100644 --- a/docs/sources/tutorials/alerting-get-started-pt2/index.md +++ b/docs/sources/tutorials/alerting-get-started-pt2/index.md @@ -10,10 +10,10 @@ labels: - cloud tags: - beginner -title: Get started with Grafana Alerting - Part 2 of 4 +title: Get started with Grafana Alerting - Part 2 weight: 50 killercoda: - title: Get started with Grafana Alerting - Part 2 of 4 + title: Get started with Grafana Alerting - Part 2 description: Learn to use alert instances and route notifications by labels to contacts, building on your alerting skills in Grafana for more advanced workflows — Part 2. backend: imageid: ubuntu @@ -21,7 +21,7 @@ killercoda: -# Get started with Grafana Alerting - Part 2 of 4 +# Get started with Grafana Alerting - Part 2 The Get started with Grafana Alerting tutorial Part 2 is a continuation of [Get started with Grafana Alerting tutorial Part 1](http://www.grafana.com/tutorials/alerting-get-started/). @@ -188,7 +188,7 @@ Create a notification policy if you want to handle metrics returned by alert rul This new child policy routes alerts that match the label `device=desktop` to the Webhook contact point. -1. **Repeat the steps above to create a second child policy** to match another alert instance. For labels use: `device=mobile`. Use the Webhook integration for the contact point. Alternatively, experiment by using a different Webhook endpoint or a [different integration](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/manage-contact-points/#list-of-supported-integrations). +1. **Repeat the steps above to create a second child policy** to match another alert instance. For labels use: `device=mobile`. Use the Webhook integration for the contact point. Alternatively, experiment by using a different Webhook endpoint or a [different integration](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/manage-contact-points/#supported-contact-point-integrations). @@ -206,7 +206,7 @@ Create a notification policy if you want to handle metrics returned by alert rul This new child policy routes alerts that match the label `device=desktop` to the Webhook contact point. -1. **Repeat the steps above to create a second child policy** to match another alert instance. For labels use: `device=mobile`. Use the Webhook integration for the contact point. Alternatively, experiment by using a different Webhook endpoint or a [different integration](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/manage-contact-points/#list-of-supported-integrations). +1. **Repeat the steps above to create a second child policy** to match another alert instance. For labels use: `device=mobile`. Use the Webhook integration for the contact point. Alternatively, experiment by using a different Webhook endpoint or a [different integration](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/manage-contact-points/#supported-contact-point-integrations). {{< /docs/ignore >}} diff --git a/docs/sources/tutorials/alerting-get-started-pt3/index.md b/docs/sources/tutorials/alerting-get-started-pt3/index.md index 7445758c22b..e2c29a3acab 100644 --- a/docs/sources/tutorials/alerting-get-started-pt3/index.md +++ b/docs/sources/tutorials/alerting-get-started-pt3/index.md @@ -10,10 +10,10 @@ labels: - cloud tags: - intermediate -title: Get started with Grafana Alerting - Part 3 of 4 +title: Get started with Grafana Alerting - Part 3 weight: 60 killercoda: - title: Get started with Grafana Alerting - Part 3 of 4 + title: Get started with Grafana Alerting - Part 3 description: Learn how to group alert notifications effectively to reduce noise and streamline communication in Grafana Alerting — Part 3. backend: imageid: ubuntu @@ -37,7 +37,7 @@ refs: -# Get started with Grafana Alerting - Part 3 of 4 +# Get started with Grafana Alerting - Part 3 The Get started with Grafana Alerting tutorial Part 3 is a continuation of [Get started with Grafana Alerting tutorial Part 2](http://www.grafana.com/tutorials/alerting-get-started-pt2/). diff --git a/docs/sources/tutorials/alerting-get-started-pt4/index.md b/docs/sources/tutorials/alerting-get-started-pt4/index.md index 4ea4477e655..1b480d375b8 100644 --- a/docs/sources/tutorials/alerting-get-started-pt4/index.md +++ b/docs/sources/tutorials/alerting-get-started-pt4/index.md @@ -420,4 +420,22 @@ To deepen your understanding of Grafana’s templating, explore the following re - [**Notification template reference**](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/template-notifications/reference/): Lists the data available for use in notification templates and explores specific functions. - [**Alert rule template reference**](https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templates/reference/): Covers the specifics of creating dynamic labels and annotations for alert rules using elements such as variables and functions. +## Learn more in [Grafana Alerting Part 5](http://www.grafana.com/tutorials/alerting-get-started-pt5/) + + + +{{< admonition type="tip" >}} + +In [Get started with Grafana Alerting - Part 5](http://www.grafana.com/tutorials/alerting-get-started-pt5/) you learn how to dynamically route alerts and link them to dashboards. + +{{< /admonition >}} + + + +{{< docs/ignore >}} + +In [Get started with Grafana Alerting - Part 5](http://www.grafana.com/tutorials/alerting-get-started-pt5/) you learn how to dynamically route alerts and link them to dashboards. + +{{< /docs/ignore >}} + diff --git a/docs/sources/tutorials/alerting-get-started-pt5/index.md b/docs/sources/tutorials/alerting-get-started-pt5/index.md new file mode 100644 index 00000000000..e2049b494ca --- /dev/null +++ b/docs/sources/tutorials/alerting-get-started-pt5/index.md @@ -0,0 +1,383 @@ +--- +Feedback Link: https://github.com/grafana/tutorials/issues/new +categories: + - alerting +description: Learn how to dynamically route alerts and link them to dashboards — Part 5. +labels: + products: + - enterprise + - oss + - cloud +tags: + - intermediate +title: Get started with Grafana Alerting - Part 5 +weight: 60 +killercoda: + title: Get started with Grafana Alerting - Part 5 + description: Learn how to dynamically route alerts and link them to dashboards — Part 5. + backend: + imageid: ubuntu +--- + +# Get started with Grafana Alerting - Part 5 + + + +The Get started with Grafana Alerting tutorial Part 5 is a continuation of [Get started with Grafana Alerting tutorial Part 4](http://www.grafana.com/tutorials/alerting-get-started-pt4/). + +In this tutorial, we focus on optimizing your alerting strategy using Grafana for monitoring system health, particularly when working with [Prometheus](https://grafana.com/docs/grafana/latest/datasources/prometheus/). Imagine you are managing a web application or a fleet of servers, tracking critical metrics such as CPU, memory, and disk usage. While monitoring is essential, managing alerts allows your team to act on issues without necessarily feeling overwhelmed by the noise. + +In this tutorial you will learn how to: + +- Leverage notification policies for **dynamic routing based on query values**: Use notification policies to route alerts based on dynamically generated labels, in a way that critical alerts reach the on-call team and less urgent ones go to a general monitoring channel. +- Set **mute timings** to suppress certain alerts during maintenance or weekends. +- **Link alerts to dashboards** to provide more context to resolve issues. + + + + + + +## Before you begin + +- **Interactive learning environment** + + - Alternatively, you can [try out this example in our interactive learning environment](https://killercoda.com/grafana-labs/course/grafana/alerting-get-started-pt5/). It’s a fully configured environment with all the dependencies already installed. + +- **Grafana OSS** + - If you opt to run a Grafana stack locally, ensure you have the following applications installed: + - Docker Compose (included in Docker for Desktop for macOS and Windows) + - Git + + + +### Set up the Grafana stack + +To observe data using the Grafana stack, download and run the following files. + +1. Clone the [tutorial environment repository](https://github.com/tonypowa/grafana-prometheus-alerting-demo.git). + + ```bash + git clone https://github.com/tonypowa/grafana-prometheus-alerting-demo.git + ``` + +1. Change to the directory where you cloned the repository: + + ```bash + cd grafana-prometheus-alerting-demo + ``` + +1. Build the Grafana stack: + + + + ``` + docker compose build + ``` + + + + {{< docs/ignore >}} + + + + ```bash + docker-compose build + ``` + + + + {{< /docs/ignore >}} + +1. Bring up the containers: + + + + ``` + docker compose up -d + ``` + + + + {{< docs/ignore >}} + + + + ```bash + docker-compose up -d + ``` + + + + {{< /docs/ignore >}} + + The first time you run `docker compose up -d`, Docker downloads all the necessary resources for the tutorial. This might take a few minutes, depending on your internet connection. + + + +{{< admonition type="note" >}} +If you already have Grafana, Loki, or Prometheus running on your system, you might see errors, because the Docker image is trying to use ports that your local installations are already using. If this is the case, stop the services, then run the command again. +{{< /admonition >}} + + + +{{< docs/ignore >}} + +NOTE: + +If you already have Grafana, Loki, or Prometheus running on your system, you might see errors, because the Docker image is trying to use ports that your local installations are already using. If this is the case, stop the services, then run the command again. + +{{< /docs/ignore >}} + + + + +## Use case: Monitoring and alerting for system health with Prometheus and Grafana + +In this use case, we focus on monitoring the system's CPU, memory, and disk usage as part of a monitoring setup. This example is based on the [Grafana Prometheus Alerting Demo](https://github.com/tonypowa/grafana-prometheus-alerting-demo), which collects and visualizes system metrics via Prometheus and Grafana. + +Your team is responsible for ensuring the health of your servers, and you want to leverage advanced alerting features in Grafana to: + +- Set who should receive an alert notification based on query value. +- Suppress alerts based on query value. +- Integrate alert rules into visualizations for better context. + +### Scenario + +In the provided demo setup, you're monitoring: + +- CPU Usage. +- Memory Consumption. + +You have a mixture of critical alerts (e.g., CPU usage over `75%`) and warning alerts (e.g., memory usage over `60%`). + +At times, you also have scheduled maintenance windows, where you might temporarily suppress certain alerts during planned downtime. + + + + +## Create a visualization to monitor metrics + +To keep track of these metrics and understand system behavior across different environments, you can set up a visualization for CPU usage and memory consumption. This will make it easier to see how the system is performing and how alerts are distributed based on the environment label, including during scheduled maintenance windows. + +The time-series visualization supports alert rules to provide more context in the form of annotations and alert rule state. Follow these steps to create a visualization to monitor the application’s metrics. + +1. Log in to Grafana: + + - Navigate to [http://localhost:3000](http://localhost:3000), where Grafana should be running. + - Username and password: `admin` + +1. Create a time series panel: + + - Navigate to **Dashboards**. + - Click **New**. + - Select **New Dashboard**. + - Click **+ Add visualization**. + - Select **Prometheus** as the data source (provided with the demo). + - Enter a title for your panel, e.g., **CPU and Memory Usage**. + +1. Add queries for metrics: + + - In the query area, copy and paste the following PromQL query: + + ** switch to **Code** mode if not already selected ** + + ```promql + flask_app_cpu_usage{environment="prod"} + ``` + + - Click **Run queries**. + + This query should display the simulated CPU usage data in the **prod** environment. + +1. Add memory usage query: + + - Click **+ Add query**. + - In the query area, paste the following PromQL query: + + ```promql + flask_app_memory_usage{environment="prod"} + ``` + + {{< figure src="/media/docs/alerting/time-series_cpu_mem_usage_metrics.png" max-width="1200px" caption="Time-series panel displaying CPU and memory usage metrics in production." >}} + + Both metrics return labels that we’ll use later to link alert instances with the appropriate routing. These labels help define how alerts are routed based on their environment or other criteria. + +1. Click **Save dashboard**. + + We have our time-series panel ready. Feel free to combine metrics with labels such as `environment = “staging”`. + + + + +## Create Notification Policies + +Notification policies route alert instances to contact points via label matchers. Since we know what labels our application returns (i.e., `environment`, `job`, `instance`), we can use these labels to match alert rules. + +1. Navigate to **Alerts & IRM > Alerting > Notification Policies**. + +1. Add a child policy: + + - In the **Default policy**, click **+ New child policy**. + - **Label**: `environment`. + - **Operator**: `=`. + - **Value**: `production`. + - This label matches alert rules where the environment label is `prod`. + +1. Choose a **contact point**: + + - If you don’t have any contact points, add a [Contact point](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/manage-contact-points/#add-a-contact-point). + + For a quick test, you can use a public webhook from [webhook.site](https://webhook.site/) to capture and inspect alert notifications. If you choose this method, select **Webhook** from the drop-down menu in contact points. + +1. Enable continue matching: + + - Turn on **Continue matching subsequent sibling nodes** so the evaluation continues even after one or more labels (i.e., _environment_ labels) match. + +1. Save and repeat + + - Create another child policy by following the same steps. + - Use `environment = staging` as the label/value pair. + - Feel free to use a different contact point. + +Now that the labels are defined, we can create alert rules for CPU and memory metrics. These alert rules will use the labels from the collected and stored metrics in Prometheus. + + + + +## Create alert rules to monitor CPU and memory usage + +Follow these steps to manually create alert rules and link them to a visualization. + +## Create an alert rule for CPU usage + +1. Navigate to **Alerts & IRM > Alerting > Alert rules** from the Grafana sidebar. +1. Click **+ New alert rule** rule to create a new alert. + +### Enter alert rule name + +Make it short and descriptive, as this will appear in your alert notification. For instance, `CPU usage` . + +### Define query and alert condition + +1. Select **Prometheus** data source from the drop-down menu. +1. In the query section, enter the following query: + + ** switch to **Code** mode if not already selected ** + + ``` + flask_app_cpu_usage{} + ``` + +1. **Alert condition** section: + + - Enter 75 as the value for **WHEN QUERY IS ABOVE** to set the threshold for the alert. + - Click **Preview alert rule condition** to run the queries. + + {{< figure src="/media/docs/alerting/promql-returning-metrics.png" max-width="1200px" caption="Preview of a query returning alert instances in Grafana." >}} + + Among the labels returned for `flask_app_cpu_usage`, the environment label is particularly important, as it enables dynamic alert routing based on the environment value, ensuring the right team receives the relevant notifications. + +### Add folders and labels + +In this section we add a [templated label based on query value](https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templates/examples/#based-on-query-value) to map to the notification policies. + +1. In **Folder**, click _+ New folder_ and enter a name. For example: `App metrics` . This folder contains our alerts. +1. Click **+ Add labels**. +1. **Key** field: `environment` . +1. In the **value** field copy in the following template: + + ```go + {{- if eq $labels.environment "prod" -}} + production + {{- else if eq $labels.environment "staging" -}} + staging + {{- else -}} + development + {{- end -}} + ``` + + In this context, the template is used to route alert notifications based on the `environment` label. When a metric like CPU usage exceeds a threshold, the template checks the environment (e.g., `prod`, `staging`, or any other value). It then generates a label based on query value (e.g., _production_, _staging_, or _development_). This label is used in the alert notification policy to route alerts to the appropriate team, so that notifications are directed to the right group, making the process more efficient and avoiding unnecessary overlap. + +### Set evaluation behaviour + +1. Click + **New evaluation group**. Name it `System usage`. +1. Choose an **Evaluation interval** (how often the alert will be evaluated). Choose `1m`. Click Create. +1. Set the **pending period** to `0s` (zero seconds), so the alert rule fires the moment the condition is met (this minimizes the waiting time for the demonstration.). + +### Configure notifications + +Select who should receive a notification when an alert rule fires. + +1. Toggle the **Advance options** button. +1. Click **Preview routing**. + The preview should display which firing alerts are routed to contact points based on notification policies that match the `environment` label. + + {{< figure src="/media/docs/alerting/routing-preview-cpu-metrics.png" max-width="1200px" caption="Notification policies matched by the environment label matcher." >}} + + The environment label matcher should map to the notification policies created earlier. This makes sure that firing alert instances are routed to the appropriate contact points associated with each policy. + +### Configure notification message + +Link your dashboard panel to this alert rule to display alert annotations in your visualization whenever the alert rule triggers or resolves. + +1. Click **Link dashboard and panel**. +1. Find the panel that you created earlier. +1. Click **Confirm**. + +## Create a second alert rule for memory usage + +1. Duplicate the existing alert rule (**More > Duplicate**), or create a new alert rule for memory usage, defining a threshold condition (e.g., memory usage exceeding `60%`). +1. Query: `flask_app_memory_usage{}` +1. Link to the same visualization to obtain memory usage annotations whenever the alert rule triggers or resolves. + +Now that the CPU and memory alert rules are set up, they are linked to the notification policies through the custom label matcher we added. The value of the label dynamically changes based on the environment template, using `$labels.environment`. This ensures that the label value will be set to production, staging, or development, depending on the environment. + + + + +## Visualizing metrics and alert annotations + +Check how your dashboard looks now that both alerts have been linked to your dashboard panel. + +{{< figure src="/media/docs/alerting/time-series_cpu_with_alert.png" max-width="1200px" caption="Time series panel displaying health indicators and annotations." >}} + +After the alert rules are created, they should appear as **health indicators** (colored heart icons: red heart when the alert is in **Alerting** state, and green heart when in **Normal** state.) on the linked panel. In addition, the annotations include helpful context, such as the time the alert was triggered. + + + + +## Create mute timings + +Now that we've set up notification policies, we can demonstrate how to mute alerts for recurring periods of time. You can mute notifications for either the production or staging policies, depending on your needs. + +Mute timings are useful for suppressing alerts with certain labels during maintenance windows or weekends. + +1. Navigate to **Alerts & IRM > Alerting > Notification Policies**. + - Enter a name, e.g. `Planned downtime` or `Non-business hours`. + - Select **Sat** and **Sun**, to apply the mute timing to all Saturdays and Sundays. + - Click **Save mute timing**. +1. Add mute timing to the desired policy: + - Go to the notification policy that routes instances with the `staging` label. + - Select **More > Edit**. + - Choose the mute timing from the drop-down menu + - Click **Update policy**. + +This mute timing will apply to any alerts from the staging environment that trigger on Saturdays and Sundays. + + + + +## Conclusion + +By using notification policies, you can route alerts based on query values, directing them to the appropriate teams. Integrating alerts into dashboards provides more context, and mute timings allow you to suppress alerts during maintenance or low-priority periods. + +## Learn more + +Explore related topics covered in this tutorial: + +- Understand how alert routing works in [Get started with Grafana Alerting - Part 2](http://www.grafana.com/tutorials/alerting-get-started-pt2/). +- Learn how templating works in [Get started with Grafana Alerting - Part 4](http://www.grafana.com/tutorials/alerting-get-started-pt4/). + - More [examples on templating labels](https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templates/examples/). + + diff --git a/docs/sources/tutorials/alerting-get-started/index.md b/docs/sources/tutorials/alerting-get-started/index.md index c77a4b0693f..507bc4050f7 100644 --- a/docs/sources/tutorials/alerting-get-started/index.md +++ b/docs/sources/tutorials/alerting-get-started/index.md @@ -11,10 +11,10 @@ labels: - cloud tags: - beginner -title: Get started with Grafana Alerting - Part 1 of 4 +title: Get started with Grafana Alerting - Part 1 weight: 50 killercoda: - title: Get started with Grafana Alerting - Part 1 of 4 + title: Get started with Grafana Alerting - Part 1 description: Get started with Grafana Alerting by creating your first alert rule, sending notifications to a webhook, and generating data to test it live — Part 1. backend: imageid: ubuntu @@ -22,7 +22,7 @@ killercoda: -# Get started with Grafana Alerting - Part 1 of 4 +# Get started with Grafana Alerting - Part 1 In this guide, we walk you through the process of setting up your first alert in just a few minutes. You'll witness your alert in action with real-time data, as well as sending alert notifications. diff --git a/docs/sources/upgrade-guide/upgrade-v11.5/index.md b/docs/sources/upgrade-guide/upgrade-v11.5/index.md new file mode 100644 index 00000000000..633dfa3b7e0 --- /dev/null +++ b/docs/sources/upgrade-guide/upgrade-v11.5/index.md @@ -0,0 +1,22 @@ +--- +description: Guide for upgrading to Grafana v11.5 +keywords: + - grafana + - configuration + - documentation + - upgrade + - '11.5' +title: Upgrade to Grafana v11.5 +menuTitle: Upgrade to v11.5 +weight: 700 +--- + +# Upgrade to Grafana v11.5 + +{{< docs/shared lookup="upgrade/intro.md" source="grafana" version="" >}} + +{{< docs/shared lookup="back-up/back-up-grafana.md" source="grafana" version="" leveloffset="+1" >}} + +{{< docs/shared lookup="upgrade/upgrade-common-tasks.md" source="grafana" version="" >}} + +## Technical notes diff --git a/docs/sources/upgrade-guide/when-to-upgrade/index.md b/docs/sources/upgrade-guide/when-to-upgrade/index.md index 052107861c4..31d6385ef9f 100644 --- a/docs/sources/upgrade-guide/when-to-upgrade/index.md +++ b/docs/sources/upgrade-guide/when-to-upgrade/index.md @@ -99,6 +99,7 @@ Here is an overview of projected version support through 2024: | 11.2 | August 2024 | May 2025 | | 11.3 | October 2024 | July 2025 | | 11.4 | December 2024 | September 2025 | +| 11.5 | January 2025 | October 2025 | {{< admonition type="note" >}} Grafana 9.5.x was the last supported minor for the 9.0 major release and is no longer supported as of July 2024. diff --git a/docs/sources/whatsnew/_index.md b/docs/sources/whatsnew/_index.md index 0de15d17301..db397d8a537 100644 --- a/docs/sources/whatsnew/_index.md +++ b/docs/sources/whatsnew/_index.md @@ -76,6 +76,7 @@ For a complete list of every change, with links to pull requests and related iss ## Grafana 11 +- [What's new in 11.5](https://grafana.com/docs/grafana//whatsnew/whats-new-in-v11-5/) - [What's new in 11.4](https://grafana.com/docs/grafana//whatsnew/whats-new-in-v11-4/) - [What's new in 11.3](https://grafana.com/docs/grafana//whatsnew/whats-new-in-v11-3/) - [What's new in 11.2](https://grafana.com/docs/grafana//whatsnew/whats-new-in-v11-2/) diff --git a/docs/sources/whatsnew/whats-new-in-v11-5.md b/docs/sources/whatsnew/whats-new-in-v11-5.md new file mode 100644 index 00000000000..f532c4a82cb --- /dev/null +++ b/docs/sources/whatsnew/whats-new-in-v11-5.md @@ -0,0 +1,62 @@ +--- +description: Feature and improvement highlights for Grafana v11.5 +keywords: + - grafana + - new + - documentation + - '11.5' + - release notes +labels: +products: + - cloud + - enterprise + - oss +title: What's new in Grafana v11.5 +posts: + - title: Cloud Migration Assistant + items: + - docs/grafana-cloud/whats-new/2025-01-10-grafana-cloud-migration-assistant-supports-all-plugins-and-grafana-alerting.md + - title: Dashboards and visualizations + items: + - docs/grafana-cloud/whats-new/2024-10-16-redesigned-ad-hoc-filters-for-dashboards.md + - docs/grafana-cloud/whats-new/2024-11-19-new-regular-expression-option-for-extract-fields-transformation.md + - docs/grafana-cloud/whats-new/2024-09-04-sharing-drawer.md + - docs/grafana-cloud/whats-new/2024-12-16-customizable-shareable-dashboard-panel-images.md + - title: Reporting + items: + - docs/grafana-cloud/whats-new/2024-10-21-theme-options-for-reporting.md + - docs/grafana-cloud/whats-new/2024-12-02-pdf-export-improvements-in-ga.md + - title: Alerting + items: + - docs/grafana-cloud/whats-new/2025-01-22-rbac-for-alerting-notifications.md + - docs/grafana-cloud/whats-new/2025-01-22-rbac-for-notification-policies.md + - title: Data sources + items: + - docs/grafana-cloud/whats-new/2025-01-09-elasticsearch-cross-cluster-search-support.md + - docs/grafana-cloud/whats-new/2024-11-12-open-search-datasource-now-supports-private-datasource-connect.md + - docs/grafana-cloud/whats-new/2024-12-04-time-series-macro-support-in-visual-query-builder-for-sql-data-sources.md + - title: Authentication and authorization + items: + - docs/grafana-cloud/whats-new/2025-01-07-oauth-and-saml-session-handling-improvements.md + - title: Plugins + items: + - docs/grafana-cloud/whats-new/2025-01-10-plugin-frontend-sandbox.md + - title: Public dashboards + items: + - docs/grafana-cloud/whats-new/2024-09-09-public-dashboards-are-now-shared-dashboards.md +whats_new_grafana_version: 11.5 +weight: -47 +--- + +# What’s new in Grafana v11.5 + +Welcome to Grafana 11.5! +Read on to learn about new sharing, reporting, and export options, cross-cluster search for Elasticsearch, PDC support for several new data sources, and more. +The Grafana Cloud Migration Assistant is in public preview and now supports all plugins and Grafana Alerts, in addition to dashboards, folders, and data sources. +We've also made it more secure to run third-party apps and data sources, and improved user session handling for OAuth 2.0 and SAML. + +{{< youtube id="RGiktzfhRd0" >}} + +For even more detail about all the changes in this release, refer to the [changelog](https://github.com/grafana/grafana/blob/main/CHANGELOG.md). For the specific steps we recommend when you upgrade to v11.5, check out our [Upgrade Guide](https://grafana.com/docs/grafana//upgrade-guide/upgrade-v11.5/). + +{{< docs/whats-new >}} diff --git a/e2e/cypress/plugins/smtpTester.js b/e2e/cypress/plugins/smtpTester.js index 67a8798a6aa..b1a7a4e1a4c 100644 --- a/e2e/cypress/plugins/smtpTester.js +++ b/e2e/cypress/plugins/smtpTester.js @@ -1,3 +1,6 @@ +const fs = require('fs'); +const { Jimp, diff } = require('jimp'); +const pdf = require('pdf-parse'); const ms = require('smtp-tester'); const PORT = 7777; @@ -29,6 +32,97 @@ const initialize = (on, config) => { return lastEmail[email] || null; }, }); + + on('task', { + async compareImages({ expectedImageFilepath, newImageContent, updateExpectedImage = false }) { + const inputBuffer = Buffer.from(newImageContent.data); + if (updateExpectedImage) { + fs.writeFileSync(expectedImageFilepath, inputBuffer); + return true; + } + + const inputImage = await Jimp.read(inputBuffer); + const expectedImage = await Jimp.read(expectedImageFilepath); + + const pixelDiff = diff(expectedImage, inputImage, 0.3); + return pixelDiff.percent <= 0.1; + }, + }); + + on('task', { + async compareCSVs({ expectedCSVFilepath, newCSVContent, updateExpectedCSV = false }) { + const inputBuffer = Buffer.from(newCSVContent.data); + if (updateExpectedCSV) { + await fs.writeFileSync(expectedCSVFilepath, inputBuffer); + return true; + } + + const inputCSV = toCSV(inputBuffer); + const expectedCSV = toCSV(fs.readFileSync(expectedCSVFilepath)); + + if (inputCSV.length !== expectedCSV.length) { + return false; + } + + for (let i = 0; i < expectedCSV.length; i++) { + const line = expectedCSV[i]; + for (let j = 0; j < line.length; j++) { + if (line[j] !== inputCSV[i][j]) { + return false; + } + } + } + + return true; + }, + }); + + on('task', { + async comparePDFs({ expectedPDFFilepath, newPDFContent, updateExpectedPDF = false }) { + const inputBuffer = Buffer.from(newPDFContent.data); + if (updateExpectedPDF) { + fs.writeFileSync(expectedPDFFilepath, inputBuffer); + return true; + } + + const inputDoc = await pdf(inputBuffer); + const expectedDoc = await pdf(expectedPDFFilepath); + + removePDFGeneratedOnDate(inputDoc); + removePDFGeneratedOnDate(expectedDoc); + + if (inputDoc.numpages !== expectedDoc.numpages) { + console.log('PDFs do not contain the same number of pages'); + return false; + } + + if (inputDoc.text !== expectedDoc.text) { + console.log('PDFs do not contain the same text'); + console.log('PDF expected text: ', expectedDoc.text); + console.log('PDF input text: ', inputDoc.text); + return false; + } + + return true; + }, + }); +}; + +const toCSV = (buffer) => { + return buffer + .toString() + .split('\n') + .map((e) => e.trim()) + .map((e) => e.split(',').map((e) => e.trim())); +}; + +// remove the date part of the "Generated on " header in PDFs as it's too complicated to set it to a fixed date +const removePDFGeneratedOnDate = (pdfDoc) => { + const regex = /(Generated on )(.*)(Data time range)/; + // removes the text in the second set of parenthesis of the regex above + pdfDoc.text = pdfDoc.text.replace(regex, (match, p1, p2, p3) => { + return `${p1} ${p3}`; + }); }; exports.initialize = initialize; diff --git a/e2e/cypress/support/e2e.js b/e2e/cypress/support/e2e.js index 3e03d0f4344..ddd50a58844 100644 --- a/e2e/cypress/support/e2e.js +++ b/e2e/cypress/support/e2e.js @@ -50,4 +50,9 @@ beforeEach(() => { cy.logToConsole('disabling dashboardScene feature toggle in localstorage'); cy.setLocalStorage('grafana.featureToggles', 'dashboardScene=false'); } + + if (Cypress.env('useV2DashboardsAPI')) { + cy.logToConsole('enabling v2 dashboards API in localstorage'); + cy.setLocalStorage('grafana.featureToggles', 'useV2DashboardsAPI=true'); + } }); diff --git a/e2e/dashboards-suite/new-query-variable.spec.ts b/e2e/dashboards-suite/new-query-variable.spec.ts index d6ead2937c4..daed9222d17 100644 --- a/e2e/dashboards-suite/new-query-variable.spec.ts +++ b/e2e/dashboards-suite/new-query-variable.spec.ts @@ -72,7 +72,7 @@ describe('Variables - Query - Add variable', () => { cy.get('input[type="checkbox"]').should('not.be.checked'); }); - e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should('not.have.text'); + e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should('not.exist'); e2e.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput().should('not.exist'); }); diff --git a/e2e/dashboards-suite/utils/makeDashboard.ts b/e2e/dashboards-suite/utils/makeDashboard.ts index 4deaaa773ce..2ada48db7a9 100644 --- a/e2e/dashboards-suite/utils/makeDashboard.ts +++ b/e2e/dashboards-suite/utils/makeDashboard.ts @@ -48,8 +48,8 @@ export function makeNewDashboardRequestBody(dashboardName: string, folderUid?: s timezone: '', title: dashboardName, version: 0, - weekStart: '', uid: '', + weekStart: '', }, message: '', overwrite: false, diff --git a/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.linux-amd64.tar.gz b/e2e/extensions/.keep similarity index 100% rename from scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.linux-amd64.tar.gz rename to e2e/extensions/.keep diff --git a/e2e/old-arch/dashboards-suite/import-dashboard.spec.ts b/e2e/old-arch/dashboards-suite/import-dashboard.spec.ts index 6d4bfed5e68..55d58705111 100644 --- a/e2e/old-arch/dashboards-suite/import-dashboard.spec.ts +++ b/e2e/old-arch/dashboards-suite/import-dashboard.spec.ts @@ -6,7 +6,7 @@ describe('Import Dashboards Test', () => { e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); }); - it('Ensure you can import a number of json test dashboards from a specific test directory', () => { + it.skip('Ensure you can import a number of json test dashboards from a specific test directory', () => { e2e.flows.importDashboard(testDashboard, 1000); }); }); diff --git a/e2e/old-arch/various-suite/prometheus-editor.spec.ts b/e2e/old-arch/various-suite/prometheus-editor.spec.ts index d04d502a706..64993979a9a 100644 --- a/e2e/old-arch/various-suite/prometheus-editor.spec.ts +++ b/e2e/old-arch/various-suite/prometheus-editor.spec.ts @@ -159,19 +159,6 @@ describe('Prometheus query editor', () => { e2e.components.DataSource.Prometheus.queryEditor.builder.metricsExplorer().should('exist'); }); - - // NEED TO COMPLETE QUEY ADVISOR WORK OR FIGURE OUT HOW TO ENABLE EXPERIMENTAL FEATURE TOGGLES - // it('should have a query advisor when enabled with feature toggle', () => { - // cy.window().then((win) => { - // win.localStorage.setItem('grafana.featureToggles', 'prometheusPromQAIL=0'); - - // navigateToEditor('Builder', 'prometheusBuilder'); - - // getResources(); - - // e2e.components.DataSource.Prometheus.queryEditor.builder.queryAdvisor().should('exist'); - // }); - // }); }); }); diff --git a/e2e/old-arch/various-suite/solo-route.spec.ts b/e2e/old-arch/various-suite/solo-route.spec.ts index cfea359cff2..415257ead7c 100644 --- a/e2e/old-arch/various-suite/solo-route.spec.ts +++ b/e2e/old-arch/various-suite/solo-route.spec.ts @@ -5,7 +5,7 @@ describe('Solo Route', () => { e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); }); - it('Can view panels with shared queries in fullsceen', () => { + it('Can view panels with shared queries in fullscreen', () => { // open Panel Tests - Bar Gauge e2e.pages.SoloPanel.visit('ZqZnVvFZz/datasource-tests-shared-queries?orgId=1&panelId=4'); @@ -25,20 +25,20 @@ describe('Solo Route', () => { it('Can view solo repeated panel in scenes', () => { // open Panel Tests - Graph NG e2e.pages.SoloPanel.visit( - 'templating-repeating-panels/templating-repeating-panels?orgId=1&from=1699934989607&to=1699956589607&panelId=panel-2-clone-1&__feature.dashboardSceneSolo=true' + 'templating-repeating-panels/templating-repeating-panels?orgId=1&from=1699934989607&to=1699956589607&panelId=panel-2-clone-0&__feature.dashboardSceneSolo=true' ); - e2e.components.Panels.Panel.title('server=B').should('exist'); + e2e.components.Panels.Panel.title('server=A').should('exist'); cy.contains('uplot-main-div').should('not.exist'); }); - it('Can view solo in repeaterd row and panel in scenes', () => { + it('Can view solo in repeated row and panel in scenes', () => { // open Panel Tests - Graph NG e2e.pages.SoloPanel.visit( - 'Repeating-rows-uid/repeating-rows?orgId=1&var-server=A&var-server=B&var-server=D&var-pod=1&var-pod=2&var-pod=3&panelId=panel-2-clone-D-clone-2&__feature.dashboardSceneSolo=true' + 'Repeating-rows-uid/repeating-rows?orgId=1&var-server=A&var-server=B&var-server=D&var-pod=1&var-pod=2&var-pod=3&panelId=panel-16-clone-1/grid-item-2/panel-2-clone-1&__feature.dashboardSceneSolo=true' ); - e2e.components.Panels.Panel.title('server = D, pod = Sod').should('exist'); + e2e.components.Panels.Panel.title('server = B, pod = Rob').should('exist'); cy.contains('uplot-main-div').should('not.exist'); }); }); diff --git a/e2e/plugin-e2e/jaeger/jaeger.spec.ts b/e2e/plugin-e2e/jaeger/jaeger.spec.ts new file mode 100644 index 00000000000..c7e1bc88951 --- /dev/null +++ b/e2e/plugin-e2e/jaeger/jaeger.spec.ts @@ -0,0 +1,8 @@ +import { test, expect } from '@grafana/plugin-e2e'; + +test('Smoke test: plugin loads', async ({ createDataSourceConfigPage, page }) => { + await createDataSourceConfigPage({ type: 'jaeger' }); + + await expect(await page.getByText('Type: Jaeger', { exact: true })).toBeVisible(); + await expect(await page.getByRole('heading', { name: 'Connection', exact: true })).toBeVisible(); +}); diff --git a/e2e/plugin-e2e/mysql/visual-query-builder.spec.ts b/e2e/plugin-e2e/mysql/visual-query-builder.spec.ts index 706364df9d8..e87c06bea31 100644 --- a/e2e/plugin-e2e/mysql/visual-query-builder.spec.ts +++ b/e2e/plugin-e2e/mysql/visual-query-builder.spec.ts @@ -6,8 +6,6 @@ import { mockDataSourceRequest } from './utils'; test.beforeEach(mockDataSourceRequest); -test.use({ featureToggles: { sqlQuerybuilderFunctionParameters: true } }); - test('visual query builder should handle macros', async ({ explorePage, page }) => { await explorePage.getByGrafanaSelector(selectors.components.SQLQueryEditor.headerTableSelector).click(); await page.getByText(normalTableName, { exact: true }).click(); diff --git a/e2e/plugin-e2e/plugin-e2e-api-tests/as-admin-user/panelEditPage.spec.ts b/e2e/plugin-e2e/plugin-e2e-api-tests/as-admin-user/panelEditPage.spec.ts index b700c247006..2de0a9e57fd 100644 --- a/e2e/plugin-e2e/plugin-e2e-api-tests/as-admin-user/panelEditPage.spec.ts +++ b/e2e/plugin-e2e/plugin-e2e-api-tests/as-admin-user/panelEditPage.spec.ts @@ -6,6 +6,7 @@ import { scenarios } from '../mocks/resources'; const PANEL_TITLE = 'Table panel E2E test'; const TABLE_VIZ_NAME = 'Table'; +const TIME_SERIES_VIZ_NAME = 'Time series'; const STANDARD_OTIONS_CATEGORY = 'Standard options'; const DISPLAY_NAME_LABEL = 'Display name'; const REACT_TABLE_DASHBOARD = { uid: 'U_bZIMRMk' }; @@ -84,6 +85,118 @@ test.describe('edit panel plugin settings', () => { formatExpectError('Expected section to be collapsed') ).toBeVisible(); }); + + test('Select time zone in timezone picker', async ({ panelEditPage }) => { + await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME); + const axisOptions = await panelEditPage.getCustomOptions('Axis'); + const timeZonePicker = axisOptions.getSelect('Time zone'); + + await timeZonePicker.selectOption('Europe/Stockholm'); + await expect(timeZonePicker).toHaveSelected('Europe/Stockholm'); + }); + + test('select unit in unit picker', async ({ panelEditPage }) => { + await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME); + const standardOptions = panelEditPage.getStandardOptions(); + const unitPicker = standardOptions.getUnitPicker('Unit'); + + await unitPicker.selectOption('Misc > Pixels'); + + await expect(unitPicker).toHaveSelected('Pixels'); + }); + + test('enter value in number input', async ({ panelEditPage }) => { + await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME); + const axisOptions = panelEditPage.getCustomOptions('Axis'); + const lineWith = axisOptions.getNumberInput('Soft min'); + + await lineWith.fill('10'); + + await expect(lineWith).toHaveValue('10'); + }); + + test('enter value in slider', async ({ panelEditPage }) => { + await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME); + const graphOptions = panelEditPage.getCustomOptions('Graph styles'); + const lineWidth = graphOptions.getSliderInput('Line width'); + + await lineWidth.fill('10'); + + await expect(lineWidth).toHaveValue('10'); + }); + + test('select value in single value select', async ({ panelEditPage }) => { + await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME); + const standardOptions = panelEditPage.getStandardOptions(); + const colorSchemeSelect = standardOptions.getSelect('Color scheme'); + + await colorSchemeSelect.selectOption('Classic palette'); + await expect(colorSchemeSelect).toHaveSelected('Classic palette'); + }); + + test('clear input', async ({ panelEditPage }) => { + await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME); + const panelOptions = panelEditPage.getPanelOptions(); + const title = panelOptions.getTextInput('Title'); + + await expect(title).toHaveValue('Panel Title'); + await title.clear(); + await expect(title).toHaveValue(''); + }); + + test('enter value in input', async ({ panelEditPage }) => { + await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME); + const panelOptions = panelEditPage.getPanelOptions(); + const description = panelOptions.getTextInput('Description'); + + await expect(description).toHaveValue(''); + await description.fill('This is a panel'); + await expect(description).toHaveValue('This is a panel'); + }); + + test('unchecking switch', async ({ panelEditPage }) => { + await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME); + const axisOptions = panelEditPage.getCustomOptions('Axis'); + const showBorder = axisOptions.getSwitch('Show border'); + + await expect(showBorder).toBeChecked({ checked: false }); + await showBorder.check(); + await expect(showBorder).toBeChecked(); + + await showBorder.uncheck(); + await expect(showBorder).toBeChecked({ checked: false }); + }); + + test('checking switch', async ({ panelEditPage }) => { + await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME); + const axisOptions = panelEditPage.getCustomOptions('Axis'); + const showBorder = axisOptions.getSwitch('Show border'); + + await expect(showBorder).toBeChecked({ checked: false }); + await showBorder.check(); + await expect(showBorder).toBeChecked(); + }); + + test('re-selecting value in radio button group', async ({ panelEditPage }) => { + await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME); + const axisOptions = panelEditPage.getCustomOptions('Axis'); + const placement = axisOptions.getRadioGroup('Placement'); + + await placement.check('Right'); + await expect(placement).toHaveChecked('Right'); + + await placement.check('Auto'); + await expect(placement).toHaveChecked('Auto'); + }); + + test('selecting value in radio button group', async ({ panelEditPage }) => { + await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME); + const axisOptions = panelEditPage.getCustomOptions('Axis'); + const placement = axisOptions.getRadioGroup('Placement'); + + await placement.check('Right'); + await expect(placement).toHaveChecked('Right'); + }); }); test('backToDashboard method should navigate to dashboard page', async ({ gotoPanelEditPage, page }) => { diff --git a/e2e/plugin-e2e/postgres/postgres.spec.ts b/e2e/plugin-e2e/postgres/postgres.spec.ts new file mode 100644 index 00000000000..aafdcbee40b --- /dev/null +++ b/e2e/plugin-e2e/postgres/postgres.spec.ts @@ -0,0 +1,8 @@ +import { test, expect } from '@grafana/plugin-e2e'; + +test('Smoke test: plugin loads', async ({ createDataSourceConfigPage, page }) => { + await createDataSourceConfigPage({ type: 'grafana-postgresql-datasource' }); + + await expect(await page.getByText('Type: PostgreSQL', { exact: true })).toBeVisible(); + await expect(await page.getByRole('heading', { name: 'Connection', exact: true })).toBeVisible(); +}); diff --git a/e2e/plugin-e2e/zipkin/zipkin.spec.ts b/e2e/plugin-e2e/zipkin/zipkin.spec.ts new file mode 100644 index 00000000000..5a58dc656fb --- /dev/null +++ b/e2e/plugin-e2e/zipkin/zipkin.spec.ts @@ -0,0 +1,8 @@ +import { test, expect } from '@grafana/plugin-e2e'; + +test('Smoke test: plugin loads', async ({ createDataSourceConfigPage, page }) => { + await createDataSourceConfigPage({ type: 'zipkin' }); + + await expect(await page.getByText('Type: Zipkin', { exact: true })).toBeVisible(); + await expect(await page.getByRole('heading', { name: 'Connection', exact: true })).toBeVisible(); +}); diff --git a/e2e/run-suite b/e2e/run-suite index 11569b43982..bc0eebd1567 100755 --- a/e2e/run-suite +++ b/e2e/run-suite @@ -28,6 +28,7 @@ declare -A env=( testFilesForSingleSuite="*.spec.ts" rootForEnterpriseSuite="./e2e/extensions-suite" rootForOldArch="./e2e/old-arch" +rootForDashboardsSchemaV2="./e2e/dashboards-suite" declare -A cypressConfig=( [screenshotsFolder]=./e2e/"${args[0]}"/screenshots @@ -111,6 +112,24 @@ case "$1" in cypressConfig[video]=${args[1]} env[DISABLE_SCENES]=true ;; + "dashboards-schema-v2") + env[useV2DashboardsAPI]=true + cypressConfig[specPattern]=$rootForDashboardsSchemaV2/$testFilesForSingleSuite + cypressConfig[video]=false + case "$2" in + "debug") + echo -e "Debug mode" + env[SLOWMO]=1 + PARAMS="--no-exit" + enterpriseSuite=$(basename "${args[2]}") + ;; + "dev") + echo "Dev mode" + CMD="cypress open" + enterpriseSuite=$(basename "${args[2]}") + ;; + esac + ;; "enterprise-smtp") env[SMTP_PLUGIN_ENABLED]=true cypressConfig[specPattern]=./e2e/extensions/enterprise/smtp-suite/$testFilesForSingleSuite diff --git a/e2e/test-plugins/grafana-extensionstest-app/package.json b/e2e/test-plugins/grafana-extensionstest-app/package.json index d100164ce4a..0bef81cbf48 100644 --- a/e2e/test-plugins/grafana-extensionstest-app/package.json +++ b/e2e/test-plugins/grafana-extensionstest-app/package.json @@ -1,6 +1,6 @@ { "name": "@test-plugins/extensions-test-app", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "private": true, "scripts": { "build": "webpack -c ./webpack.config.ts --env production", diff --git a/e2e/test-plugins/grafana-test-datasource/package.json b/e2e/test-plugins/grafana-test-datasource/package.json index 0eff2b9f271..e4472e7a923 100644 --- a/e2e/test-plugins/grafana-test-datasource/package.json +++ b/e2e/test-plugins/grafana-test-datasource/package.json @@ -1,6 +1,6 @@ { "name": "@test-plugins/grafana-e2etest-datasource", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "private": true, "scripts": { "build": "webpack -c ./webpack.config.ts --env production", @@ -11,7 +11,7 @@ "author": "Grafana", "license": "Apache-2.0", "devDependencies": { - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/plugin-configs": "workspace:*", "@types/lodash": "4.17.7", "@types/node": "22.10.2", "@types/prismjs": "1.26.4", diff --git a/e2e/various-suite/loki-table-explore-to-dash.spec.ts b/e2e/various-suite/loki-table-explore-to-dash.spec.ts index 1477f6f0165..f4867855ca3 100644 --- a/e2e/various-suite/loki-table-explore-to-dash.spec.ts +++ b/e2e/various-suite/loki-table-explore-to-dash.spec.ts @@ -175,6 +175,7 @@ describe('Loki Query Editor', () => { // One row with two cells cy.get('[role="cell"]').should('have.length', 2); + cy.contains('label', 'targetLabelName').scrollIntoView(); cy.contains('label', 'targetLabelName').should('be.visible'); cy.contains('label', 'targetLabelName').click(); cy.contains('label', 'targetLabelName').within(() => { diff --git a/e2e/various-suite/prometheus-editor.spec.ts b/e2e/various-suite/prometheus-editor.spec.ts index 09ae51e4f7f..e1746edf6b7 100644 --- a/e2e/various-suite/prometheus-editor.spec.ts +++ b/e2e/various-suite/prometheus-editor.spec.ts @@ -159,19 +159,6 @@ describe.skip('Prometheus query editor', () => { e2e.components.DataSource.Prometheus.queryEditor.builder.metricsExplorer().should('exist'); }); - - // NEED TO COMPLETE QUEY ADVISOR WORK OR FIGURE OUT HOW TO ENABLE EXPERIMENTAL FEATURE TOGGLES - // it('should have a query advisor when enabled with feature toggle', () => { - // cy.window().then((win) => { - // win.localStorage.setItem('grafana.featureToggles', 'prometheusPromQAIL=0'); - - // navigateToEditor('Builder', 'prometheusBuilder'); - - // getResources(); - - // e2e.components.DataSource.Prometheus.queryEditor.builder.queryAdvisor().should('exist'); - // }); - // }); }); }); diff --git a/e2e/various-suite/solo-route.spec.ts b/e2e/various-suite/solo-route.spec.ts index cfea359cff2..415257ead7c 100644 --- a/e2e/various-suite/solo-route.spec.ts +++ b/e2e/various-suite/solo-route.spec.ts @@ -5,7 +5,7 @@ describe('Solo Route', () => { e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); }); - it('Can view panels with shared queries in fullsceen', () => { + it('Can view panels with shared queries in fullscreen', () => { // open Panel Tests - Bar Gauge e2e.pages.SoloPanel.visit('ZqZnVvFZz/datasource-tests-shared-queries?orgId=1&panelId=4'); @@ -25,20 +25,20 @@ describe('Solo Route', () => { it('Can view solo repeated panel in scenes', () => { // open Panel Tests - Graph NG e2e.pages.SoloPanel.visit( - 'templating-repeating-panels/templating-repeating-panels?orgId=1&from=1699934989607&to=1699956589607&panelId=panel-2-clone-1&__feature.dashboardSceneSolo=true' + 'templating-repeating-panels/templating-repeating-panels?orgId=1&from=1699934989607&to=1699956589607&panelId=panel-2-clone-0&__feature.dashboardSceneSolo=true' ); - e2e.components.Panels.Panel.title('server=B').should('exist'); + e2e.components.Panels.Panel.title('server=A').should('exist'); cy.contains('uplot-main-div').should('not.exist'); }); - it('Can view solo in repeaterd row and panel in scenes', () => { + it('Can view solo in repeated row and panel in scenes', () => { // open Panel Tests - Graph NG e2e.pages.SoloPanel.visit( - 'Repeating-rows-uid/repeating-rows?orgId=1&var-server=A&var-server=B&var-server=D&var-pod=1&var-pod=2&var-pod=3&panelId=panel-2-clone-D-clone-2&__feature.dashboardSceneSolo=true' + 'Repeating-rows-uid/repeating-rows?orgId=1&var-server=A&var-server=B&var-server=D&var-pod=1&var-pod=2&var-pod=3&panelId=panel-16-clone-1/grid-item-2/panel-2-clone-1&__feature.dashboardSceneSolo=true' ); - e2e.components.Panels.Panel.title('server = D, pod = Sod').should('exist'); + e2e.components.Panels.Panel.title('server = B, pod = Rob').should('exist'); cy.contains('uplot-main-div').should('not.exist'); }); }); diff --git a/eslint.config.js b/eslint.config.js index cf427ade626..8c4910b7310 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -117,6 +117,11 @@ module.exports = [ importNames: ['Trans', 't'], message: 'Please import from app/core/internationalization instead', }, + { + name: 'i18next', + importNames: ['t'], + message: 'Please import from app/core/internationalization instead', + }, ], }, ], diff --git a/go.mod b/go.mod index e4140a19173..d32c2843663 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( buf.build/gen/go/parca-dev/parca/connectrpc/go v1.17.0-20240902100956-02fd72488966.1 // @grafana/observability-traces-and-profiling buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.2-20240902100956-02fd72488966.2 // @grafana/observability-traces-and-profiling cloud.google.com/go/kms v1.20.0 // @grafana/grafana-backend-group + cloud.google.com/go/spanner v1.70.0 // @grafana/grafana-search-and-storage cloud.google.com/go/storage v1.43.0 // @grafana/grafana-backend-group connectrpc.com/connect v1.17.0 // @grafana/observability-traces-and-profiling cuelang.org/go v0.11.1 // @grafana/grafana-as-code @@ -39,12 +40,14 @@ require ( github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // @grafana/grafana-backend-group github.com/bwmarrin/snowflake v0.3.0 // @grafan/grafana-app-platform-squad github.com/centrifugal/centrifuge v0.33.3 // @grafana/grafana-app-platform-squad - github.com/crewjam/saml v0.4.13 // @grafana/identity-access-team + github.com/crewjam/saml v0.4.14 // @grafana/identity-access-team github.com/dlmiddlecote/sqlstats v1.0.2 // @grafana/grafana-backend-group + github.com/dolthub/go-mysql-server v0.19.1-0.20250206012855-c216e59c21a7 // @grafana/grafana-datasources-core-services + github.com/dolthub/vitess v0.0.0-20250123002143-3b45b8cacbfa // @grafana/grafana-datasources-core-services github.com/fatih/color v1.17.0 // @grafana/grafana-backend-group github.com/fullstorydev/grpchan v1.1.1 // @grafana/grafana-backend-group github.com/gchaincl/sqlhooks v1.3.0 // @grafana/grafana-search-and-storage - github.com/getkin/kin-openapi v0.128.0 // @grafana/grafana-app-platform-squad + github.com/getkin/kin-openapi v0.129.0 // @grafana/grafana-app-platform-squad github.com/go-jose/go-jose/v3 v3.0.3 // @grafana/identity-access-team github.com/go-kit/log v0.2.1 // @grafana/grafana-backend-group github.com/go-ldap/ldap/v3 v3.4.4 // @grafana/identity-access-team @@ -52,7 +55,7 @@ require ( github.com/go-openapi/runtime v0.28.0 // @grafana/alerting-backend github.com/go-openapi/strfmt v0.23.0 // @grafana/alerting-backend github.com/go-redis/redis/v8 v8.11.5 // @grafana/grafana-backend-group - github.com/go-sourcemap/sourcemap v2.1.3+incompatible // @grafana/grafana-backend-group + github.com/go-sourcemap/sourcemap v2.1.4+incompatible // @grafana/grafana-backend-group github.com/go-sql-driver/mysql v1.8.1 // @grafana/grafana-search-and-storage github.com/go-stack/stack v1.8.1 // @grafana/grafana-backend-group github.com/gobwas/glob v0.2.3 // @grafana/grafana-backend-group @@ -69,23 +72,23 @@ require ( github.com/googleapis/gax-go/v2 v2.14.1 // @grafana/grafana-backend-group github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group github.com/gorilla/websocket v1.5.3 // @grafana/grafana-app-platform-squad - github.com/grafana/alerting v0.0.0-20250117230852-a5e8136407d4 // @grafana/alerting-backend - github.com/grafana/authlib v0.0.0-20250120145936-5f0e28e7a87c // @grafana/identity-access-team - github.com/grafana/authlib/types v0.0.0-20250120145936-5f0e28e7a87c // @grafana/identity-access-team + github.com/grafana/alerting v0.0.0-20250224133628-2acbeef29642 // @grafana/alerting-backend + github.com/grafana/authlib v0.0.0-20250225105729-99e678595501 // @grafana/identity-access-team + github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82 // @grafana/identity-access-team github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics github.com/grafana/dataplane/sdata v0.0.9 // @grafana/observability-metrics github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 // @grafana/grafana-backend-group github.com/grafana/e2e v0.1.1 // @grafana-app-platform-squad github.com/grafana/gofpdf v0.0.0-20231002120153-857cc45be447 // @grafana/sharing-squad github.com/grafana/gomemcache v0.0.0-20240805133030-fdaf6a95408e // @grafana/grafana-operator-experience-squad - github.com/grafana/grafana-app-sdk v0.30.0 // @grafana/grafana-app-platform-squad - github.com/grafana/grafana-app-sdk/logging v0.29.0 // @grafana/grafana-app-platform-squad + github.com/grafana/grafana-app-sdk v0.31.0 // @grafana/grafana-app-platform-squad + github.com/grafana/grafana-app-sdk/logging v0.30.0 // @grafana/grafana-app-platform-squad github.com/grafana/grafana-aws-sdk v0.31.5 // @grafana/aws-datasources - github.com/grafana/grafana-azure-sdk-go/v2 v2.1.5 // @grafana/partner-datasources + github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // @grafana/partner-datasources github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 // @grafana/grafana-operator-experience-squad github.com/grafana/grafana-google-sdk-go v0.2.1 // @grafana/partner-datasources github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 // @grafana/grafana-backend-group - github.com/grafana/grafana-plugin-sdk-go v0.262.0 // @grafana/plugins-platform-backend + github.com/grafana/grafana-plugin-sdk-go v0.266.0 // @grafana/plugins-platform-backend github.com/grafana/loki/v3 v3.2.1 // @grafana/observability-logs github.com/grafana/otel-profiling-go v0.5.1 // @grafana/grafana-backend-group github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // @grafana/observability-traces-and-profiling @@ -96,7 +99,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // @grafana/identity-access-team github.com/hashicorp/go-hclog v1.6.3 // @grafana/plugins-platform-backend github.com/hashicorp/go-multierror v1.1.1 // @grafana/alerting-squad - github.com/hashicorp/go-plugin v1.6.2 // @grafana/plugins-platform-backend + github.com/hashicorp/go-plugin v1.6.3 // @grafana/plugins-platform-backend github.com/hashicorp/go-version v1.7.0 // @grafana/grafana-backend-group github.com/hashicorp/golang-lru/v2 v2.0.7 // @grafana/alerting-backend github.com/hashicorp/hcl/v2 v2.17.0 // @grafana/alerting-backend @@ -104,13 +107,11 @@ require ( github.com/influxdata/influxdb-client-go/v2 v2.13.0 // @grafana/partner-datasources github.com/influxdata/influxql v1.4.0 // @grafana/partner-datasources github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // @grafana/grafana-app-platform-squad - github.com/jeremywohl/flatten v1.0.1 // @grafana/grafana-app-platform-squad github.com/jmespath-community/go-jmespath v1.1.1 // @grafana/identity-access-team github.com/jmespath/go-jmespath v0.4.0 // indirect; // @grafana/grafana-backend-group github.com/jmoiron/sqlx v1.3.5 // @grafana/grafana-backend-group github.com/json-iterator/go v1.1.12 // @grafana/grafana-backend-group github.com/lib/pq v1.10.9 // @grafana/grafana-backend-group - github.com/linkedin/goavro/v2 v2.10.0 // @grafana/grafana-backend-group github.com/m3db/prometheus_remote_client_golang v0.4.4 // @grafana/grafana-backend-group github.com/madflojo/testcerts v1.1.1 // @grafana/alerting-backend github.com/magefile/mage v1.15.0 // @grafana/grafana-developer-enablement-squad @@ -133,7 +134,7 @@ require ( github.com/prometheus/alertmanager v0.27.0 // @grafana/alerting-backend github.com/prometheus/client_golang v1.20.5 // @grafana/alerting-backend github.com/prometheus/client_model v0.6.1 // @grafana/grafana-backend-group - github.com/prometheus/common v0.61.0 // @grafana/alerting-backend + github.com/prometheus/common v0.62.0 // @grafana/alerting-backend github.com/prometheus/prometheus v0.301.0 // @grafana/alerting-backend github.com/redis/go-redis/v9 v9.7.0 // @grafana/alerting-backend github.com/robfig/cron/v3 v3.0.1 // @grafana/grafana-backend-group @@ -144,6 +145,7 @@ require ( github.com/spyzhov/ajson v0.9.0 // @grafana/grafana-app-platform-squad github.com/stretchr/testify v1.10.0 // @grafana/grafana-backend-group github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf // @grafana/grafana-backend-group + github.com/tjhop/slog-gokit v0.1.3 // @grafana/grafana-app-platform-squad github.com/ua-parser/uap-go v0.0.0-20211112212520-00c877edfe0f // @grafana/grafana-backend-group github.com/urfave/cli v1.22.16 // indirect; @grafana/grafana-backend-group github.com/urfave/cli/v2 v2.27.1 // @grafana/grafana-backend-group @@ -152,41 +154,41 @@ require ( github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // @grafana/grafana-operator-experience-squad github.com/yudai/gojsondiff v1.0.0 // @grafana/grafana-backend-group go.opentelemetry.io/collector/pdata v1.22.0 // @grafana/grafana-backend-group - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // @grafana/plugins-platform-backend - go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.58.0 // @grafana/grafana-operator-experience-squad - go.opentelemetry.io/contrib/propagators/jaeger v1.33.0 // @grafana/grafana-backend-group - go.opentelemetry.io/contrib/samplers/jaegerremote v0.27.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel v1.33.0 // @grafana/grafana-backend-group + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // @grafana/plugins-platform-backend + go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0 // @grafana/grafana-operator-experience-squad + go.opentelemetry.io/contrib/propagators/jaeger v1.34.0 // @grafana/grafana-backend-group + go.opentelemetry.io/contrib/samplers/jaegerremote v0.28.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel v1.34.0 // @grafana/grafana-backend-group go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel/sdk v1.33.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel/trace v1.33.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel/sdk v1.34.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel/trace v1.34.0 // @grafana/grafana-backend-group go.uber.org/atomic v1.11.0 // @grafana/alerting-backend go.uber.org/goleak v1.3.0 // @grafana/grafana-search-and-storage go.uber.org/zap v1.27.0 // @grafana/identity-access-team gocloud.dev v0.40.0 // @grafana/grafana-app-platform-squad - golang.org/x/crypto v0.32.0 // @grafana/grafana-backend-group + golang.org/x/crypto v0.35.0 // @grafana/grafana-backend-group golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // @grafana/alerting-backend golang.org/x/mod v0.22.0 // indirect; @grafana/grafana-backend-group - golang.org/x/net v0.34.0 // @grafana/oss-big-tent @grafana/partner-datasources - golang.org/x/oauth2 v0.25.0 // @grafana/identity-access-team - golang.org/x/sync v0.10.0 // @grafana/alerting-backend - golang.org/x/text v0.21.0 // @grafana/grafana-backend-group + golang.org/x/net v0.35.0 // @grafana/oss-big-tent @grafana/partner-datasources + golang.org/x/oauth2 v0.27.0 // @grafana/identity-access-team + golang.org/x/sync v0.11.0 // @grafana/alerting-backend + golang.org/x/text v0.22.0 // @grafana/grafana-backend-group golang.org/x/time v0.9.0 // @grafana/grafana-backend-group golang.org/x/tools v0.29.0 // indirect; @grafana/grafana-as-code gonum.org/v1/gonum v0.15.1 // @grafana/oss-big-tent google.golang.org/api v0.216.0 // @grafana/grafana-backend-group - google.golang.org/grpc v1.69.4 // @grafana/plugins-platform-backend - google.golang.org/protobuf v1.36.1 // @grafana/plugins-platform-backend + google.golang.org/grpc v1.70.0 // @grafana/plugins-platform-backend + google.golang.org/protobuf v1.36.4 // @grafana/plugins-platform-backend gopkg.in/ini.v1 v1.67.0 // @grafana/alerting-backend gopkg.in/mail.v2 v2.3.1 // @grafana/grafana-backend-group gopkg.in/yaml.v3 v3.0.1 // @grafana/alerting-backend - k8s.io/api v0.32.0 // @grafana/grafana-app-platform-squad - k8s.io/apimachinery v0.32.0 // @grafana/grafana-app-platform-squad - k8s.io/apiserver v0.32.0 // @grafana/grafana-app-platform-squad - k8s.io/client-go v0.32.0 // @grafana/grafana-app-platform-squad - k8s.io/component-base v0.32.0 // @grafana/grafana-app-platform-squad + k8s.io/api v0.32.1 // @grafana/grafana-app-platform-squad + k8s.io/apimachinery v0.32.1 // @grafana/grafana-app-platform-squad + k8s.io/apiserver v0.32.1 // @grafana/grafana-app-platform-squad + k8s.io/client-go v0.32.1 // @grafana/grafana-app-platform-squad + k8s.io/component-base v0.32.1 // @grafana/grafana-app-platform-squad k8s.io/klog/v2 v2.130.1 // @grafana/grafana-app-platform-squad k8s.io/kube-aggregator v0.32.0 // @grafana/grafana-app-platform-squad k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // @grafana/grafana-app-platform-squad @@ -198,31 +200,34 @@ require ( ) require ( - github.com/grafana/grafana/apps/advisor v0.0.0-20250121115006-c1eac9f9973f // @grafana/plugins-platform-backend - github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250121113133-e747350fee2d // @grafana/alerting-backend - github.com/grafana/grafana/apps/investigation v0.0.0-20250121113133-e747350fee2d // @fcjack @matryer - github.com/grafana/grafana/apps/playlist v0.0.0-20250121113133-e747350fee2d // @grafana/grafana-app-platform-squad - github.com/grafana/grafana/pkg/aggregator v0.0.0-20250121113133-e747350fee2d // @grafana/grafana-app-platform-squad - github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250121113133-e747350fee2d // @grafana/grafana-app-platform-squad - github.com/grafana/grafana/pkg/apiserver v0.0.0-20250121113133-e747350fee2d // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/apps/advisor v0.0.0-20250220163425-b4c4b9abbdc8 // @grafana/plugins-platform-backend + github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250220163425-b4c4b9abbdc8 // @grafana/alerting-backend + github.com/grafana/grafana/apps/investigations v0.0.0-20250220163425-b4c4b9abbdc8 // @fcjack @matryer + github.com/grafana/grafana/apps/playlist v0.0.0-20250220164708-c8d4ff28a450 // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/pkg/aggregator v0.0.0-20250220163425-b4c4b9abbdc8 // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250220154326-6e5de80ef295 // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/pkg/apiserver v0.0.0-20250220154326-6e5de80ef295 // @grafana/grafana-app-platform-squad // This needs to be here for other projects that import grafana/grafana // For local development grafana/grafana will always use the local files // Check go.work file for details - github.com/grafana/grafana/pkg/promlib v0.0.7 // @grafana/oss-big-tent - github.com/grafana/grafana/pkg/semconv v0.0.0-20250121113133-e747350fee2d // @grafana/grafana-app-platform-squad - github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250121113133-e747350fee2d // @grafana/grafana-search-and-storage - github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20250121113133-e747350fee2d // @grafana/grafana-search-and-storage + github.com/grafana/grafana/pkg/promlib v0.0.8 // @grafana/oss-big-tent + github.com/grafana/grafana/pkg/semconv v0.0.0-20250220164708-c8d4ff28a450 // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250220154326-6e5de80ef295 // @grafana/grafana-search-and-storage + github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20250220154326-6e5de80ef295 // @grafana/grafana-search-and-storage ) +require github.com/grafana/grafana-api-golang-client v0.27.0 // @grafana/alerting-backend + require ( - cel.dev/expr v0.18.0 // indirect + cel.dev/expr v0.19.0 // indirect cloud.google.com/go v0.116.0 // indirect cloud.google.com/go/auth v0.13.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect cloud.google.com/go/iam v1.2.1 // indirect cloud.google.com/go/longrunning v0.6.1 // indirect + cloud.google.com/go/monitoring v1.21.1 // indirect cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 // indirect dario.cat/mergo v1.0.1 // indirect github.com/Azure/azure-pipeline-go v0.2.3 // indirect @@ -238,7 +243,10 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect github.com/FZambia/eagle v0.1.0 // indirect + github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect + github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect @@ -248,6 +256,7 @@ require ( github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect + github.com/apache/thrift v0.21.0 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect @@ -299,12 +308,14 @@ require ( github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 // indirect github.com/caio/go-tdigest v3.1.0+incompatible // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/centrifugal/protocol v0.13.4 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect github.com/cloudflare/circl v1.3.7 // indirect + github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect @@ -314,13 +325,17 @@ require ( github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 // indirect + github.com/dolthub/go-icu-regex v0.0.0-20241215010122-db690dd53c90 // indirect + github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 // indirect github.com/dolthub/maphash v0.1.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/edsrzf/mmap-go v1.2.0 // indirect - github.com/elazarl/goproxy v1.3.0 // indirect + github.com/elazarl/goproxy v1.7.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emicklei/proto v1.13.2 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/envoyproxy/go-control-plane v0.13.1 // indirect github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -358,7 +373,7 @@ require ( github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grafana/sqlds/v4 v4.1.3 // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // @grafana/grafana-search-and-storage github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect github.com/hashicorp/consul/api v1.30.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -375,7 +390,6 @@ require ( github.com/hashicorp/yamux v0.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/jsonschema v0.13.0 // indirect - github.com/invopop/yaml v0.3.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgx/v5 v5.7.2 // indirect @@ -393,11 +407,13 @@ require ( github.com/jpillora/backoff v1.0.0 // indirect github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/klauspost/asmfmt v1.3.2 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/lestrrat-go/strftime v1.0.4 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38 // indirect @@ -411,6 +427,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mfridman/interpolate v0.0.2 // indirect github.com/miekg/dns v1.1.62 // indirect + github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect + github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect @@ -428,14 +446,16 @@ require ( github.com/natefinch/wrap v0.2.0 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/oapi-codegen/runtime v1.0.0 // indirect + github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect + github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect github.com/oklog/run v1.1.0 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/oklog/ulid/v2 v2.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect - github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e // indirect + github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e // @grafana/grafana-search-and-storage github.com/opentracing-contrib/go-stdlib v1.0.0 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/opentracing/opentracing-go v1.2.0 // @grafana/grafana-search-and-storage github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect @@ -465,9 +485,10 @@ require ( github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect github.com/shadowspore/fossil-delta v0.0.0-20240102155221-e3a8590b820b // indirect - github.com/shopspring/decimal v1.4.0 // indirect + github.com/shopspring/decimal v1.4.0 // @grafana/grafana-datasources-core-services github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect @@ -476,6 +497,7 @@ require ( github.com/stoewer/go-strcase v1.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/tetratelabs/wazero v1.8.2 // indirect github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 // indirect @@ -497,28 +519,31 @@ require ( go.mongodb.org/mongo-driver v1.16.1 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.32.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/mock v0.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/term v0.28.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/term v0.29.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/src-d/go-errors.v1 v1.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiextensions-apiserver v0.32.0 // indirect - k8s.io/kms v0.32.0 // indirect + k8s.io/apiextensions-apiserver v0.32.1 // indirect + k8s.io/kms v0.32.1 // indirect modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect modernc.org/libc v1.55.3 // indirect modernc.org/mathutil v1.6.0 // indirect diff --git a/go.sum b/go.sum index 2198c1a9589..96e57fa263f 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.2-20240902100956-02fd7 buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.2-20240902100956-02fd72488966.2/go.mod h1:w3CrNzdvwGJ4FwUlhshojc2FDXDN+3ou5nlcLTu7dHs= c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0= c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w= -cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo= -cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0= +cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= @@ -187,6 +187,7 @@ cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvj cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= @@ -390,6 +391,8 @@ cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhI cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= +cloud.google.com/go/monitoring v1.21.1 h1:zWtbIoBMnU5LP9A/fz8LmWMGHpk4skdfeiaa66QdFGc= +cloud.google.com/go/monitoring v1.21.1/go.mod h1:Rj++LKrlht9uBi8+Eb530dIrzG/cU/lB8mt+lbeFK1c= cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= @@ -536,6 +539,8 @@ cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+ cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= +cloud.google.com/go/spanner v1.70.0 h1:nj6p/GJTgMDiSQ1gQ034ItsKuJgHiMOjtOlONOg8PSo= +cloud.google.com/go/spanner v1.70.0/go.mod h1:X5T0XftydYp0K1adeJQDJtdWpbrOeJ7wHecM4tK6FiE= cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= @@ -706,8 +711,13 @@ github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/FZambia/eagle v0.1.0 h1:9gyX6x+xjoIfglgyPTcYm7dvY7FJ93us1QY5De4CyXA= github.com/FZambia/eagle v0.1.0/go.mod h1:YjGSPVkQTNcVLfzEUQJNgW9ScPR0K4u/Ky0yeFa4oDA= +github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 h1:oVLqHXhnYtUwM89y9T1fXGaK9wTkXHgNp8/ZNMQzUxE= +github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -953,6 +963,7 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/centrifugal/centrifuge v0.33.3 h1:uyqBc27oM+qnC3NX5imvZxuk9+u2ze6QGWHDICZeoSc= github.com/centrifugal/centrifuge v0.33.3/go.mod h1:GaOF4CiREY5x6lW7zYUz46Qrc6aUZ6J/AMV/kROc2+U= @@ -991,6 +1002,7 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= @@ -1057,8 +1069,18 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 h1:u3PMzfF8RkKd3lB9pZ2bfn0qEG+1Gms9599cr0REMww= +github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2/go.mod h1:mIEZOHnFx4ZMQeawhw9rhsj+0zwQj7adVsnBX7t+eKY= +github.com/dolthub/go-icu-regex v0.0.0-20241215010122-db690dd53c90 h1:Sni8jrP0sy/w9ZYXoff4g/ixe+7bFCZlfCqXKJSU+zM= +github.com/dolthub/go-icu-regex v0.0.0-20241215010122-db690dd53c90/go.mod h1:ylU4XjUpsMcvl/BKeRRMXSH7e7WBrPXdSLvnRJYrxEA= +github.com/dolthub/go-mysql-server v0.19.1-0.20250206012855-c216e59c21a7 h1:b9ygz2zMlMv2AcJ9mLL0b60tJ9Qd+bzfsAY5ZDTu9VY= +github.com/dolthub/go-mysql-server v0.19.1-0.20250206012855-c216e59c21a7/go.mod h1:jYEJ8tNkA7K3k39X8iMqaX3MSMmViRgh222JSLHDgVc= +github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 h1:bMGS25NWAGTEtT5tOBsCuCrlYnLRKpbJVJkDbrTRhwQ= +github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71/go.mod h1:2/2zjLQ/JOOSbbSboojeg+cAwcRV0fDLzIiWch/lhqI= github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ= github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4= +github.com/dolthub/vitess v0.0.0-20250123002143-3b45b8cacbfa h1:kyoPzxViSXAyqfO0Mab7Qo1UogFIrxZKKyBU6kBOl+E= +github.com/dolthub/vitess v0.0.0-20250123002143-3b45b8cacbfa/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= @@ -1072,8 +1094,8 @@ github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8E github.com/efficientgo/core v1.0.0-rc.3 h1:X6CdgycYWDcbYiJr1H1+lQGzx13o7bq3EUkbB9DsSPc= github.com/efficientgo/core v1.0.0-rc.3/go.mod h1:FfGdkzWarkuzOlY04VY+bGfb1lWrjaL6x/GLcQ4vJps= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/elazarl/goproxy v1.3.0 h1:hpDH1r1qJgM3eusz7lP+BiMPnLiWPa6hDjIFF5WVCjE= -github.com/elazarl/goproxy v1.3.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/elazarl/goproxy v1.7.0 h1:EXv2nV4EjM60ZtsEVLYJG4oBXhDGutMKperpHsZ/v+0= +github.com/elazarl/goproxy v1.7.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= @@ -1092,12 +1114,14 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= +github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE= github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -1133,8 +1157,8 @@ github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0 github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU= github.com/gchaincl/sqlhooks v1.3.0 h1:yKPXxW9a5CjXaVf2HkQn6wn7TZARvbAOAelr3H8vK2Y= github.com/gchaincl/sqlhooks v1.3.0/go.mod h1:9BypXnereMT0+Ys8WGWHqzgkkOfHIhyeUCqXC24ra34= -github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4= -github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= +github.com/getkin/kin-openapi v0.129.0 h1:QGYTNcmyP5X0AtFQ2Dkou9DGBJsUETeLH9rFrJXZh30= +github.com/getkin/kin-openapi v0.129.0/go.mod h1:gmWI+b/J45xqpyK5wJmRRZse5wefA5H0RDMK46kLUtI= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= @@ -1270,8 +1294,8 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8= github.com/go-resty/resty/v2 v2.15.3/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU= -github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= -github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= +github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -1286,6 +1310,8 @@ github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= github.com/go-zookeeper/zk v1.0.4 h1:DPzxraQx7OrPyXq2phlGlNSIyWEsAox0RJmjTseMV6I= github.com/go-zookeeper/zk v1.0.4/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA= +github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= @@ -1498,12 +1524,12 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/alerting v0.0.0-20250117230852-a5e8136407d4 h1:616HUg7WVyLJLtVvk2pZ845M4Gk0fswgTwEZSuAZbJU= -github.com/grafana/alerting v0.0.0-20250117230852-a5e8136407d4/go.mod h1:QsnoKX/iYZxA4Cv+H+wC7uxutBD8qi8ZW5UJvD2TYmU= -github.com/grafana/authlib v0.0.0-20250120145936-5f0e28e7a87c h1:duHQ8Bih3nt9p68aQdp+22a6mFBLpK4IOURhEq+Cvk8= -github.com/grafana/authlib v0.0.0-20250120145936-5f0e28e7a87c/go.mod h1:/gYfphsNu9v1qYWXxpv1NSvMEMSwvdf8qb8YlgwIRl8= -github.com/grafana/authlib/types v0.0.0-20250120145936-5f0e28e7a87c h1:b0sPDtt33uFdmvUJjSCld3kwE2E49dUvevuUDSJsEuo= -github.com/grafana/authlib/types v0.0.0-20250120145936-5f0e28e7a87c/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM= +github.com/grafana/alerting v0.0.0-20250224133628-2acbeef29642 h1:iQ0h/h+QoguSZDF+ZpPxcM/C+m1kjh+aXjMpxywowPA= +github.com/grafana/alerting v0.0.0-20250224133628-2acbeef29642/go.mod h1:hdGB3dSl8Ma9Rjo2YiAEAjMkZ5HiNJbNDqRKDefRZrM= +github.com/grafana/authlib v0.0.0-20250225105729-99e678595501 h1:FTuDRy/Shw8yOdG+v1DnkeuaCAl8fvwgcfaG9Wccuhg= +github.com/grafana/authlib v0.0.0-20250225105729-99e678595501/go.mod h1:XVpdLhaeYqz414FmGnW00/0vTe1x8c0GRH3KaeRtyg0= +github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82 h1:DnRUYiAotHXnrfYJCvhH1NkiyWVcPm5Pd+P7Ugqt/d8= +github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM= github.com/grafana/dataplane/examples v0.0.1 h1:K9M5glueWyLoL4//H+EtTQq16lXuHLmOhb6DjSCahzA= github.com/grafana/dataplane/examples v0.0.1/go.mod h1:h5YwY8s407/17XF5/dS8XrUtsTVV2RnuW8+m1Mp46mg= github.com/grafana/dataplane/sdata v0.0.9 h1:AGL1LZnCUG4MnQtnWpBPbQ8ZpptaZs14w6kE/MWfg7s= @@ -1516,44 +1542,46 @@ github.com/grafana/gofpdf v0.0.0-20231002120153-857cc45be447 h1:jxJJ5z0GxqhWFbQU github.com/grafana/gofpdf v0.0.0-20231002120153-857cc45be447/go.mod h1:IxsY6mns6Q5sAnWcrptrgUrSglTZJXH/kXr9nbpb/9I= github.com/grafana/gomemcache v0.0.0-20240805133030-fdaf6a95408e h1:UlEET0InuoFautfaFp8lDrNF7rPHYXuBMrzwWx9XqFY= github.com/grafana/gomemcache v0.0.0-20240805133030-fdaf6a95408e/go.mod h1:IGRj8oOoxwJbHBYl1+OhS9UjQR0dv6SQOep7HqmtyFU= -github.com/grafana/grafana-app-sdk v0.30.0 h1:Hqn2pETu2mQ4RpWkZYEQfu01P7xd1Z1Gj+HX/8aB0tw= -github.com/grafana/grafana-app-sdk v0.30.0/go.mod h1:jhfqNIovb+Mes2vdMf9iMCWQkp1GTNtyNuExONtiNuk= -github.com/grafana/grafana-app-sdk/logging v0.29.0 h1:mgbXaAf33aFwqwGVeaX30l8rkeAJH0iACgX5Rn6YkN4= -github.com/grafana/grafana-app-sdk/logging v0.29.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= +github.com/grafana/grafana-api-golang-client v0.27.0 h1:zIwMXcbCB4n588i3O2N6HfNcQogCNTd/vPkEXTr7zX8= +github.com/grafana/grafana-api-golang-client v0.27.0/go.mod h1:uNLZEmgKtTjHBtCQMwNn3qsx2mpMb8zU+7T4Xv3NR9Y= +github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDRgZwHkksFk= +github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= +github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= +github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE= github.com/grafana/grafana-aws-sdk v0.31.5/go.mod h1:5p4Cjyr5ZiR6/RT2nFWkJ8XpIKgX4lAUmUMu70m2yCM= -github.com/grafana/grafana-azure-sdk-go/v2 v2.1.5 h1:CaAVAEMeyDktBNCPBsDykf9kfcYuG3Q/f7pmk0KWt1w= -github.com/grafana/grafana-azure-sdk-go/v2 v2.1.5/go.mod h1:i0uiuu9/sMFBJnpFbjvviH0KOZzdWkti9Q9Ck1HkFWM= +github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 h1:OfCkitCuomzZKW1WYHrG8MxKwtMhALb7jqoj+487eTg= +github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo= github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 h1:S4kHwr//AqhtL9xHBtz1gqVgZQeCRGTxjgsRBAkpjKY= github.com/grafana/grafana-cloud-migration-snapshot v1.6.0/go.mod h1:rWNhyxYkgiXgV7xZ4yOQzMV08yikO8L8S8M5KNoQNpA= github.com/grafana/grafana-google-sdk-go v0.2.1 h1:XeFdKnkXBjOJjXc1gf4iMx4h5aCHTAZ2CG7476qMC+8= github.com/grafana/grafana-google-sdk-go v0.2.1/go.mod h1:RiITSHwBhqVTTd3se3HQq5Ncs/wzzhTB9OK5N0J0PEU= github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 h1:r+mU5bGMzcXCRVAuOrTn54S80qbfVkvTdUJZfSfTNbs= github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79/go.mod h1:wc6Hbh3K2TgCUSfBC/BOzabItujtHMESZeFk5ZhdxhQ= -github.com/grafana/grafana-plugin-sdk-go v0.262.0 h1:R2DV6lwBQE5zaogxX3PorD9Seo8CXA8YuStf84oqwkk= -github.com/grafana/grafana-plugin-sdk-go v0.262.0/go.mod h1:U43Cnrj/9DNYyvFcNdeUWNjMXTKNB0jcTcQGpWKd2gw= -github.com/grafana/grafana/apps/advisor v0.0.0-20250121115006-c1eac9f9973f h1:c8IkbxPvM6+lscVOLgtbt8Gnro4Liltd+E2eqkoAeZA= -github.com/grafana/grafana/apps/advisor v0.0.0-20250121115006-c1eac9f9973f/go.mod h1:goSDiy3jtC2cp8wjpPZdUHRENcoSUHae1/Px/MDfddA= -github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250121113133-e747350fee2d h1:NRVOtiG1aUwOazBj9KM7X2o2shsM6TchqisezzoH1gw= -github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250121113133-e747350fee2d/go.mod h1:AvleS6icyPmcBjihtx5jYEvdzLmHGBp66NuE0AMR57A= -github.com/grafana/grafana/apps/investigation v0.0.0-20250121113133-e747350fee2d h1:oNc/aDfDucQxLbRZK25yz3Cwc+dGo1C0Xmm2LaliWUQ= -github.com/grafana/grafana/apps/investigation v0.0.0-20250121113133-e747350fee2d/go.mod h1:HQprw3MmiYj5OUV9CZnkwA1FKDZBmYACuAB3oDvUOmI= -github.com/grafana/grafana/apps/playlist v0.0.0-20250121113133-e747350fee2d h1:cJ+9jgHw8Z17rM+DCdxvyWxPqH9OB9fkPLbnWEO+QGg= -github.com/grafana/grafana/apps/playlist v0.0.0-20250121113133-e747350fee2d/go.mod h1:DjJe5osrW/BKrzN9hAAOSElNWutj1bcriExa7iDP7kA= -github.com/grafana/grafana/pkg/aggregator v0.0.0-20250121113133-e747350fee2d h1:aBD5kzsIAh50vjNqUkWK9mNpLGIBYAnKkWtUepGNAiQ= -github.com/grafana/grafana/pkg/aggregator v0.0.0-20250121113133-e747350fee2d/go.mod h1:1sq0guad+G4SUTlBgx7SXfhnzy7D86K/LcVOtiQCiMA= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250121113133-e747350fee2d h1:0WJ4uZ3gtbX7S5RGMarA+0TccmB7qEPhhNm34NcfBzs= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250121113133-e747350fee2d/go.mod h1:PNmbi49lVrv2b2I8pdu46dTs2728lKEbnVuQD8I5MnM= -github.com/grafana/grafana/pkg/apiserver v0.0.0-20250121113133-e747350fee2d h1:vVaLOibSj5lDwCb+pa0fly9uods8nTq1NJPYrTuPAGE= -github.com/grafana/grafana/pkg/apiserver v0.0.0-20250121113133-e747350fee2d/go.mod h1:srJ6H0DL7c9Cz4CmK0h4AOlC1sm8UDwn9jBZ+RHxBt4= -github.com/grafana/grafana/pkg/promlib v0.0.7 h1:BdpanKOKnID/l1BJZLhE7TRNtmq7aOVdou1LBFWaMmU= -github.com/grafana/grafana/pkg/promlib v0.0.7/go.mod h1:rnwJXCA2xRwb7F27NB35iO/JsLL/H/+eVXECk/hrEhQ= -github.com/grafana/grafana/pkg/semconv v0.0.0-20250121113133-e747350fee2d h1:cYBjuhb3m5oC6Z00Kw8DdySFaNhwb38SMxx0oXnz5vQ= -github.com/grafana/grafana/pkg/semconv v0.0.0-20250121113133-e747350fee2d/go.mod h1:tfLnBpPYgwrBMRz4EXqPCZJyCjEG4Ev37FSlXnocJ2c= -github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250121113133-e747350fee2d h1:4w4x8g6xCSRj06er81atNSQVL0fTSwLekDYsRxpz+M0= -github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250121113133-e747350fee2d/go.mod h1:CXpwZ3Mkw6xVlGKc0SqUxqXCP3Uv182q6qAQnLaLxRg= -github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20250121113133-e747350fee2d h1:xzJi+ioYZpiGYjiox9eZYqu1zczNV2PBnG6Y5XK7Ar0= -github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20250121113133-e747350fee2d/go.mod h1:2jv9CnelxFIH0q4FBaKt8BqaJyjruLmGk/31TzgsrPo= +github.com/grafana/grafana-plugin-sdk-go v0.266.0 h1:YP+iEpXH3HRX9Xo4NHjsrJhN2W7uVTtkLNzMHYbmiLI= +github.com/grafana/grafana-plugin-sdk-go v0.266.0/go.mod h1:bxkXrBQ4QSmOncsWdIOcpgP+M6wajQNMAPXlbWrqAWY= +github.com/grafana/grafana/apps/advisor v0.0.0-20250220163425-b4c4b9abbdc8 h1:mG/6nDlEBVxWlo2GQJVASzucw3ByPIBsec06XcPrjgQ= +github.com/grafana/grafana/apps/advisor v0.0.0-20250220163425-b4c4b9abbdc8/go.mod h1:9I1dKV3Dqr0NPR9Af0WJGxOytp5/6W3JLiNChOz8r+c= +github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250220163425-b4c4b9abbdc8 h1:w42GlvkmHG4nM/p1kb2nKmROVP+AHtL3qWEYMhnhCVM= +github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250220163425-b4c4b9abbdc8/go.mod h1:dHhFF484qs1cmdIShKCB3kl+tMJyc4yuwgTQ3Afz37o= +github.com/grafana/grafana/apps/investigations v0.0.0-20250220163425-b4c4b9abbdc8 h1:jRcI2fE/u0tHfCmPLIt21M3DYOt3L6BmZcu/LeV2sRw= +github.com/grafana/grafana/apps/investigations v0.0.0-20250220163425-b4c4b9abbdc8/go.mod h1:ygFcJP2McdSeMJVj/3YrKafZMc/lZBsp54HO51MtJYw= +github.com/grafana/grafana/apps/playlist v0.0.0-20250220164708-c8d4ff28a450 h1:h3HsylGnuZiBgT5Q/N6bH+LazY3+nA98R76Xwcd0zXg= +github.com/grafana/grafana/apps/playlist v0.0.0-20250220164708-c8d4ff28a450/go.mod h1:KKIsWpbv88Lwwcvdjon73zFL7vNJvuXLtsSoUjJErTw= +github.com/grafana/grafana/pkg/aggregator v0.0.0-20250220163425-b4c4b9abbdc8 h1:9qOLpC21AmXZqZ6rUhrBWl2mVqS3CzV53pzw0BCuHt0= +github.com/grafana/grafana/pkg/aggregator v0.0.0-20250220163425-b4c4b9abbdc8/go.mod h1:deLQ/ywLvpVGbncRGUA4UDGt8a5Ei9sivOP+x6AQ2ko= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250220154326-6e5de80ef295 h1:bOKkAzmkCnK2EN0LGsHoq5HyfXMLKXrr8+q35Fh+XQI= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250220154326-6e5de80ef295/go.mod h1:1iWqntBW91TsiyqneHrqTFV12vd1Tei+f5VY2ovkNAo= +github.com/grafana/grafana/pkg/apiserver v0.0.0-20250220154326-6e5de80ef295 h1:ivbywO8ZnmzDDkn169qUb9REsCGYAA7H8W6VfqSmCEw= +github.com/grafana/grafana/pkg/apiserver v0.0.0-20250220154326-6e5de80ef295/go.mod h1:OugmKouuvgWeOI8Kghram2Pv8b/1SuXRR8/iW068uLk= +github.com/grafana/grafana/pkg/promlib v0.0.8 h1:VUWsqttdf0wMI4j9OX9oNrykguQpZcruudDAFpJJVw0= +github.com/grafana/grafana/pkg/promlib v0.0.8/go.mod h1:U1ezG/MGaEPoThqsr3lymMPN5yIPdVTJnDZ+wcXT+ao= +github.com/grafana/grafana/pkg/semconv v0.0.0-20250220164708-c8d4ff28a450 h1:wSqgLKFwI7fyeqf3djRXGClBLb/UPjZ4XPm/UsKFDB0= +github.com/grafana/grafana/pkg/semconv v0.0.0-20250220164708-c8d4ff28a450/go.mod h1:HGz9/wKeN6U48g4F8RxHzpsFxJAR9anVltZp07A86UA= +github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250220154326-6e5de80ef295 h1:mDRND2VJaVzhIHrcAfOCV1X39QHWGcVByQkUeqBjpNs= +github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250220154326-6e5de80ef295/go.mod h1:b4i60JgZZjH8haVc4Uw8ye0FzxX4r28Y0EvduqxBdWo= +github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20250220154326-6e5de80ef295 h1:AtOyCicDN4xqF2ZHCXaVBQj/BQpFtb/RfYD8KlwGkaY= +github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20250220154326-6e5de80ef295/go.mod h1:QAzEkmHuxP5RPdc9H+l/Nxl+VOUtV075/Daqf77Nhf0= github.com/grafana/grafana/pkg/util/xorm v0.0.1 h1:72QZjxWIWpSeOF8ob4aMV058kfgZyeetkAB8dmeti2o= github.com/grafana/grafana/pkg/util/xorm v0.0.1/go.mod h1:eNfbB9f2jM8o9RfwqwjY8SYm5tvowJ8Ly+iE4P9rXII= github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32 h1:NznuPwItog+rwdVg8hAuGKP29ndRSzJAwhxKldkP8oQ= @@ -1630,8 +1658,8 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog= -github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q= +github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= +github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= @@ -1705,8 +1733,6 @@ github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf h1:7JTmne github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= -github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= -github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= github.com/ionos-cloud/sdk-go/v6 v6.3.0 h1:/lTieTH9Mo/CWm3cTlFLnK10jgxjUGkAqRffGqvPteY= github.com/ionos-cloud/sdk-go/v6 v6.3.0/go.mod h1:SXrO9OGyWjd2rZhAhEpdYN6VUAODzzqRdqA9BCviQtI= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= @@ -1731,8 +1757,6 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6 github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/jeremywohl/flatten v1.0.1 h1:LrsxmB3hfwJuE+ptGOijix1PIfOoKLJ3Uee/mzbgtrs= -github.com/jeremywohl/flatten v1.0.1/go.mod h1:4AmD/VxjWcI5SRB0n6szE2A6s2fsNHDLO0nAlMHgfLQ= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= @@ -1826,12 +1850,14 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6Fm github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 h1:X/79QL0b4YJVO5+OsPH9rF2u428CIrGL/jLmPsoOQQ4= github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= +github.com/lestrrat-go/strftime v1.0.4 h1:T1Rb9EPkAhgxKqbcMIPguPq8glqXTA1koF8n9BHElA8= +github.com/lestrrat-go/strftime v1.0.4/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/linkedin/goavro/v2 v2.10.0 h1:eTBIRoInBM88gITGXYtUSqqxLTFXfOsJBiX8ZMW0o4U= -github.com/linkedin/goavro/v2 v2.10.0/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA= github.com/linode/linodego v1.43.0 h1:sGeBB3caZt7vKBoPS5p4AVzmlG4JoqQOdigIibx3egk= github.com/linode/linodego v1.43.0/go.mod h1:n4TMFu1UVNala+icHqrTEFFaicYSF74cSAUG5zkTwfA= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= @@ -1987,6 +2013,10 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo= github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A= +github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 h1:nZspmSkneBbtxU9TopEAE0CY+SBJLxO8LPUlw2vG4pU= +github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80/go.mod h1:7tFDb+Y51LcDpn26GccuUgQXUk6t0CXZsivKjyimYX8= +github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 h1:t05Ww3DxZutOqbMN+7OIuqDwXbhl32HiZGpLy26BAPc= +github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= @@ -2123,8 +2153,8 @@ github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= -github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/common/assets v0.2.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= @@ -2299,6 +2329,7 @@ github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= @@ -2308,10 +2339,14 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA= github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= +github.com/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4= +github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= github.com/thanos-io/objstore v0.0.0-20240818203309-0363dadfdfb1 h1:z0v9BB/p7s4J6R//+0a5M3wCld8KzNjrGRLIwXfrAZk= github.com/thanos-io/objstore v0.0.0-20240818203309-0363dadfdfb1/go.mod h1:3ukSkG4rIRUGkKM4oIz+BSuUx2e3RlQVVv3Cc3W+Tv4= github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tjhop/slog-gokit v0.1.3 h1:6SdexP3UIeg93KLFeiM1Wp1caRwdTLgsD/THxBUy1+o= +github.com/tjhop/slog-gokit v0.1.3/go.mod h1:Bbu5v2748qpAWH7k6gse/kw3076IJf6owJmh7yArmJs= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= @@ -2427,47 +2462,49 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/collector/pdata v1.22.0 h1:3yhjL46NLdTMoP8rkkcE9B0pzjf2973crn0KKhX5UrI= go.opentelemetry.io/collector/pdata v1.22.0/go.mod h1:nLLf6uDg8Kn5g3WNZwGyu8+kf77SwOqQvMTb5AXEbEY= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0= +go.opentelemetry.io/contrib/detectors/gcp v1.32.0 h1:P78qWqkLSShicHmAzfECaTgvslqHxblNE9j62Ws1NK8= +go.opentelemetry.io/contrib/detectors/gcp v1.32.0/go.mod h1:TVqo0Sda4Cv8gCIixd7LuLwW4EylumVWfhjZJjDD4DU= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.55.0/go.mod h1:rsg1EO8LXSs2po50PB5CeY/MSVlhghuKBgXlKnqm6ks= -go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.58.0 h1:xwH3QJv6zL4u+gkPUu59NeT1Gyw9nScWT8FQpKLUJJI= -go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.58.0/go.mod h1:uosvgpqTcTXtcPQORTbEkZNDQTCDOgTz1fe6aLSyqrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0 h1:iQZYNQ7WwIcYXzOPR46FQv9O0dS1PW16RjvR0TjDOe8= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0/go.mod h1:54CaSNqYEXvpzDh8KPjiMVoWm60t5R0dZRt0leEPgAs= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/contrib/propagators/jaeger v1.33.0 h1:Jok/dG8kfp+yod29XKYV/blWgYPlMuRUoRHljrXMF5E= -go.opentelemetry.io/contrib/propagators/jaeger v1.33.0/go.mod h1:ku/EpGk44S5lyVMbtJRK2KFOnXEehxf6SDnhu1eZmjA= -go.opentelemetry.io/contrib/samplers/jaegerremote v0.27.0 h1:Co7WDtZosbvNcG4Nqbs3AEVuHNsN6EMc1/1uGKAvyJk= -go.opentelemetry.io/contrib/samplers/jaegerremote v0.27.0/go.mod h1:IohbtCIY5Erb6wKnDddXOMNlG7GwyZnkrgcqjPmhpaA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= +go.opentelemetry.io/contrib/propagators/jaeger v1.34.0 h1:D3htJISCUU/wOVlKwisVKancWm+2U4h9xDEaiMkiyRE= +go.opentelemetry.io/contrib/propagators/jaeger v1.34.0/go.mod h1:DAX1bsj+uDm2ZuOQH/RgZRx7RQZWyzV5W2WR/0UX8JA= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.28.0 h1:Xx1N6cDr8iWy1Cz6OcY7oS0ACdt/6HDYTdu4KskuC7s= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.28.0/go.mod h1:iWS+NvC948FyfnJbVfPN9h/8+vr8CR2FPn6XsLRkvH8= go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= -go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= -go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4= go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= -go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= -go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= -go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= -go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= -go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= -go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= -go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -2531,8 +2568,8 @@ golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2678,8 +2715,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2715,8 +2752,8 @@ golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4 golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= -golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2738,8 +2775,8 @@ golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2845,6 +2882,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2865,8 +2903,8 @@ golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2882,8 +2920,8 @@ golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2903,8 +2941,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -3239,15 +3277,15 @@ google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go. google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA= -google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -3292,11 +3330,13 @@ google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= -google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= -google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -3317,8 +3357,8 @@ google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= @@ -3344,6 +3384,8 @@ gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/src-d/go-errors.v1 v1.0.0 h1:cooGdZnCjYbeS1zb1s6pVAAimTdKceRrpn7aKOnNIfc= +gopkg.in/src-d/go-errors.v1 v1.0.0/go.mod h1:q1cBlomlw2FnDBDNGlnh6X0jPihy+QxZfMMNxPCbdYg= gopkg.in/telebot.v3 v3.2.1/go.mod h1:GJKwwWqp9nSkIVN51eRKU78aB5f5OnQuWdwiIZfPbko= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -3373,19 +3415,19 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/api v0.0.0-20190813020757-36bff7324fb7/go.mod h1:3Iy+myeAORNCLgjd/Xu9ebwN7Vh59Bw0vh9jhoX+V58= -k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= -k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= -k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0= -k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw= +k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= +k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= +k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw= +k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010/go.mod h1:Waf/xTS2FGRrgXCkO5FP3XxTOWh0qLf2QhL1qFZZ/R8= -k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= -k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/apiserver v0.32.0 h1:VJ89ZvQZ8p1sLeiWdRJpRD6oLozNZD2+qVSLi+ft5Qs= -k8s.io/apiserver v0.32.0/go.mod h1:HFh+dM1/BE/Hm4bS4nTXHVfN6Z6tFIZPi649n83b4Ag= -k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= -k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= -k8s.io/component-base v0.32.0 h1:d6cWHZkCiiep41ObYQS6IcgzOUQUNpywm39KVYaUqzU= -k8s.io/component-base v0.32.0/go.mod h1:JLG2W5TUxUu5uDyKiH2R/7NnxJo1HlPoRIIbVLkK5eM= +k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= +k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/apiserver v0.32.1 h1:oo0OozRos66WFq87Zc5tclUX2r0mymoVHRq8JmR7Aak= +k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw= +k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= +k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= +k8s.io/component-base v0.32.1 h1:/5IfJ0dHIKBWysGV0yKTFfacZ5yNV1sulPh3ilJjRZk= +k8s.io/component-base v0.32.1/go.mod h1:j1iMMHi/sqAHeG5z+O9BFNCF698a1u0186zkjMZQ28w= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= @@ -3393,8 +3435,8 @@ k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kms v0.32.0 h1:jwOfunHIrcdYl5FRcA+uUKKtg6qiqoPCwmS2T3XTYL4= -k8s.io/kms v0.32.0/go.mod h1:Bk2evz/Yvk0oVrvm4MvZbgq8BD34Ksxs2SRHn4/UiOM= +k8s.io/kms v0.32.1 h1:TW6cswRI/fawoQRFGWLmEceO37rZXupdoRdmO019jCc= +k8s.io/kms v0.32.1/go.mod h1:Bk2evz/Yvk0oVrvm4MvZbgq8BD34Ksxs2SRHn4/UiOM= k8s.io/kube-aggregator v0.32.0 h1:5ZyMW3QwAbmkasQrROcpa5we3et938DQuyUYHeXSPao= k8s.io/kube-aggregator v0.32.0/go.mod h1:6OKivf6Ypx44qu2v1ZUMrxH8kRp/8LKFKeJU72J18lU= k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4= diff --git a/go.work b/go.work index ec2b99cae04..4403fc62dd9 100644 --- a/go.work +++ b/go.work @@ -7,7 +7,7 @@ use ( . // skip:golangci-lint ./apps/advisor ./apps/alerting/notifications - ./apps/investigation + ./apps/investigations ./apps/playlist ./pkg/aggregator ./pkg/apimachinery diff --git a/go.work.sum b/go.work.sum index 8c884eb3a77..0e79edfd1d2 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,977 +1,242 @@ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1 h1:tdpHgTbmbvEIARu+bixzmleMi14+3imnpoFXz+Qzjp4= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew= -cel.dev/expr v0.16.2/go.mod h1:gXngZQMkWJoSbE8mOzehJlXQyubn/Vg0vR9/F3W7iw8= -cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= -cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM= -cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= -cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= -cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= -cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms= -cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= -cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= -cloud.google.com/go/accessapproval v1.7.1/go.mod h1:JYczztsHRMK7NTXb6Xw+dwbs/WnOJxbo/2mTI+Kgg68= -cloud.google.com/go/accessapproval v1.7.2/go.mod h1:/gShiq9/kK/h8T/eEn1BTzalDvk0mZxJlhfw0p+Xuc0= -cloud.google.com/go/accessapproval v1.7.3/go.mod h1:4l8+pwIxGTNqSf4T3ds8nLO94NQf0W/KnMNuQ9PbnP8= -cloud.google.com/go/accessapproval v1.7.4/go.mod h1:/aTEh45LzplQgFYdQdwPMR9YdX0UlhBmvB84uAmQKUc= -cloud.google.com/go/accessapproval v1.7.5/go.mod h1:g88i1ok5dvQ9XJsxpUInWWvUBrIZhyPDPbk4T01OoJ0= +cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go/accessapproval v1.8.1 h1:WC6pA5Gyqkrvdc18AHvriShwk8wgMe9EWvBAQSLxTc8= cloud.google.com/go/accessapproval v1.8.1/go.mod h1:3HAtm2ertsWdwgjSGObyas6fj3ZC/3zwV2WVZXO53sU= -cloud.google.com/go/accesscontextmanager v1.8.0/go.mod h1:uI+AI/r1oyWK99NN8cQ3UK76AMelMzgZCvJfsi2c+ps= -cloud.google.com/go/accesscontextmanager v1.8.1/go.mod h1:JFJHfvuaTC+++1iL1coPiG1eu5D24db2wXCDWDjIrxo= -cloud.google.com/go/accesscontextmanager v1.8.2/go.mod h1:E6/SCRM30elQJ2PKtFMs2YhfJpZSNcJyejhuzoId4Zk= -cloud.google.com/go/accesscontextmanager v1.8.3/go.mod h1:4i/JkF2JiFbhLnnpnfoTX5vRXfhf9ukhU1ANOTALTOQ= -cloud.google.com/go/accesscontextmanager v1.8.4/go.mod h1:ParU+WbMpD34s5JFEnGAnPBYAgUHozaTmDJU7aCU9+M= -cloud.google.com/go/accesscontextmanager v1.8.5/go.mod h1:TInEhcZ7V9jptGNqN3EzZ5XMhT6ijWxTGjzyETwmL0Q= cloud.google.com/go/accesscontextmanager v1.9.1 h1:+C7HM05/h80znK+8VNu25wAimueda6/NGNdus+jxaHI= cloud.google.com/go/accesscontextmanager v1.9.1/go.mod h1:wUVSoz8HmG7m9miQTh6smbyYuNOJrvZukK5g6WxSOp0= -cloud.google.com/go/aiplatform v1.45.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= -cloud.google.com/go/aiplatform v1.48.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= -cloud.google.com/go/aiplatform v1.50.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= -cloud.google.com/go/aiplatform v1.51.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= -cloud.google.com/go/aiplatform v1.51.1/go.mod h1:kY3nIMAVQOK2XDqDPHaOuD9e+FdMA6OOpfBjsvaFSOo= -cloud.google.com/go/aiplatform v1.51.2/go.mod h1:hCqVYB3mY45w99TmetEoe8eCQEwZEp9WHxeZdcv9phw= -cloud.google.com/go/aiplatform v1.52.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.54.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.57.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.58.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.58.2/go.mod h1:c3kCiVmb6UC1dHAjZjcpDj6ZS0bHQ2slL88ZjC2LtlA= -cloud.google.com/go/aiplatform v1.60.0/go.mod h1:eTlGuHOahHprZw3Hio5VKmtThIOak5/qy6pzdsqcQnM= cloud.google.com/go/aiplatform v1.68.0 h1:EPPqgHDJpBZKRvv+OsB3cr0jYz3EL2pZ+802rBPcG8U= cloud.google.com/go/aiplatform v1.68.0/go.mod h1:105MFA3svHjC3Oazl7yjXAmIR89LKhRAeNdnDKJczME= -cloud.google.com/go/analytics v0.21.2/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.3/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.4/go.mod h1:zZgNCxLCy8b2rKKVfC1YkC2vTrpfZmeRCySM3aUbskA= -cloud.google.com/go/analytics v0.21.5/go.mod h1:BQtOBHWTlJ96axpPPnw5CvGJ6i3Ve/qX2fTxR8qWyr8= -cloud.google.com/go/analytics v0.21.6/go.mod h1:eiROFQKosh4hMaNhF85Oc9WO97Cpa7RggD40e/RBy8w= -cloud.google.com/go/analytics v0.22.0/go.mod h1:eiROFQKosh4hMaNhF85Oc9WO97Cpa7RggD40e/RBy8w= -cloud.google.com/go/analytics v0.23.0/go.mod h1:YPd7Bvik3WS95KBok2gPXDqQPHy08TsCQG6CdUCb+u0= cloud.google.com/go/analytics v0.25.1 h1:tMlK9KGTwHYASagAHXXbIPUVCRknA0Yv4jquim5HdRE= cloud.google.com/go/analytics v0.25.1/go.mod h1:hrAWcN/7tqyYwF/f60Nph1yz5UE3/PxOPzzFsJgtU+Y= -cloud.google.com/go/apigateway v1.6.1/go.mod h1:ufAS3wpbRjqfZrzpvLC2oh0MFlpRJm2E/ts25yyqmXA= -cloud.google.com/go/apigateway v1.6.2/go.mod h1:CwMC90nnZElorCW63P2pAYm25AtQrHfuOkbRSHj0bT8= -cloud.google.com/go/apigateway v1.6.3/go.mod h1:k68PXWpEs6BVDTtnLQAyG606Q3mz8pshItwPXjgv44Y= -cloud.google.com/go/apigateway v1.6.4/go.mod h1:0EpJlVGH5HwAN4VF4Iec8TAzGN1aQgbxAWGJsnPCGGY= -cloud.google.com/go/apigateway v1.6.5/go.mod h1:6wCwvYRckRQogyDDltpANi3zsCDl6kWi0b4Je+w2UiI= cloud.google.com/go/apigateway v1.7.1 h1:BeR+5NtpGxsUoK8wa/IPkanORjqZdlyNmXZ8ke3tOhc= cloud.google.com/go/apigateway v1.7.1/go.mod h1:5JBcLrl7GHSGRzuDaISd5u0RKV05DNFiq4dRdfrhCP0= -cloud.google.com/go/apigeeconnect v1.6.1/go.mod h1:C4awq7x0JpLtrlQCr8AzVIzAaYgngRqWf9S5Uhg+wWs= -cloud.google.com/go/apigeeconnect v1.6.2/go.mod h1:s6O0CgXT9RgAxlq3DLXvG8riw8PYYbU/v25jqP3Dy18= -cloud.google.com/go/apigeeconnect v1.6.3/go.mod h1:peG0HFQ0si2bN15M6QSjEW/W7Gy3NYkWGz7pFz13cbo= -cloud.google.com/go/apigeeconnect v1.6.4/go.mod h1:CapQCWZ8TCjnU0d7PobxhpOdVz/OVJ2Hr/Zcuu1xFx0= -cloud.google.com/go/apigeeconnect v1.6.5/go.mod h1:MEKm3AiT7s11PqTfKE3KZluZA9O91FNysvd3E6SJ6Ow= cloud.google.com/go/apigeeconnect v1.7.1 h1:yMWIb/lv69K7Qz6Brv63u6gIACefIPKQSiI2aFXnJxo= cloud.google.com/go/apigeeconnect v1.7.1/go.mod h1:olkn1lOhIA/aorreenFzfEcEXmFN2pyAwkaUFbug9ZY= -cloud.google.com/go/apigeeregistry v0.7.1/go.mod h1:1XgyjZye4Mqtw7T9TsY4NW10U7BojBvG4RMD+vRDrIw= -cloud.google.com/go/apigeeregistry v0.7.2/go.mod h1:9CA2B2+TGsPKtfi3F7/1ncCCsL62NXBRfM6iPoGSM+8= -cloud.google.com/go/apigeeregistry v0.8.1/go.mod h1:MW4ig1N4JZQsXmBSwH4rwpgDonocz7FPBSw6XPGHmYw= -cloud.google.com/go/apigeeregistry v0.8.2/go.mod h1:h4v11TDGdeXJDJvImtgK2AFVvMIgGWjSb0HRnBSjcX8= -cloud.google.com/go/apigeeregistry v0.8.3/go.mod h1:aInOWnqF4yMQx8kTjDqHNXjZGh/mxeNlAf52YqtASUs= cloud.google.com/go/apigeeregistry v0.9.1 h1:AfMllcPbJ+qMgbYK2bC5QDPd8SmE8wQ5msiDILuxVm4= cloud.google.com/go/apigeeregistry v0.9.1/go.mod h1:XCwK9CS65ehi26z7E8/Vl4PEX5c/JJxpfxlB1QEyrZw= cloud.google.com/go/apikeys v0.6.0 h1:B9CdHFZTFjVti89tmyXXrO+7vSNo2jvZuHG8zD5trdQ= -cloud.google.com/go/appengine v1.8.1/go.mod h1:6NJXGLVhZCN9aQ/AEDvmfzKEfoYBlfB80/BHiKVputY= -cloud.google.com/go/appengine v1.8.2/go.mod h1:WMeJV9oZ51pvclqFN2PqHoGnys7rK0rz6s3Mp6yMvDo= -cloud.google.com/go/appengine v1.8.3/go.mod h1:2oUPZ1LVZ5EXi+AF1ihNAF+S8JrzQ3till5m9VQkrsk= -cloud.google.com/go/appengine v1.8.4/go.mod h1:TZ24v+wXBujtkK77CXCpjZbnuTvsFNT41MUaZ28D6vg= -cloud.google.com/go/appengine v1.8.5/go.mod h1:uHBgNoGLTS5di7BvU25NFDuKa82v0qQLjyMJLuPQrVo= cloud.google.com/go/appengine v1.9.1 h1:mQMmn1Dv0DDLsDjYxfS+cVwQa8+ue++ymVeD1jkXze0= cloud.google.com/go/appengine v1.9.1/go.mod h1:jtguveqRWFfjrk3k/7SlJz1FpDBZhu5CWSRu+HBgClk= -cloud.google.com/go/area120 v0.8.1/go.mod h1:BVfZpGpB7KFVNxPiQBuHkX6Ed0rS51xIgmGyjrAfzsg= -cloud.google.com/go/area120 v0.8.2/go.mod h1:a5qfo+x77SRLXnCynFWPUZhnZGeSgvQ+Y0v1kSItkh4= -cloud.google.com/go/area120 v0.8.3/go.mod h1:5zj6pMzVTH+SVHljdSKC35sriR/CVvQZzG/Icdyriw0= -cloud.google.com/go/area120 v0.8.4/go.mod h1:jfawXjxf29wyBXr48+W+GyX/f8fflxp642D/bb9v68M= -cloud.google.com/go/area120 v0.8.5/go.mod h1:BcoFCbDLZjsfe4EkCnEq1LKvHSK0Ew/zk5UFu6GMyA0= cloud.google.com/go/area120 v0.9.1 h1:YfDWbKHRHmhpd8ejTmAeK6eYi3n0qJKvPNEj1ON19PY= cloud.google.com/go/area120 v0.9.1/go.mod h1:foV1BSrnjVL/KydBnAlUQFSy85kWrMwGSmRfIraC+JU= -cloud.google.com/go/artifactregistry v1.14.1/go.mod h1:nxVdG19jTaSTu7yA7+VbWL346r3rIdkZ142BSQqhn5E= -cloud.google.com/go/artifactregistry v1.14.2/go.mod h1:Xk+QbsKEb0ElmyeMfdHAey41B+qBq3q5R5f5xD4XT3U= -cloud.google.com/go/artifactregistry v1.14.3/go.mod h1:A2/E9GXnsyXl7GUvQ/2CjHA+mVRoWAXC0brg2os+kNI= -cloud.google.com/go/artifactregistry v1.14.4/go.mod h1:SJJcZTMv6ce0LDMUnihCN7WSrI+kBSFV0KIKo8S8aYU= -cloud.google.com/go/artifactregistry v1.14.6/go.mod h1:np9LSFotNWHcjnOgh8UVK0RFPCTUGbO0ve3384xyHfE= -cloud.google.com/go/artifactregistry v1.14.7/go.mod h1:0AUKhzWQzfmeTvT4SjfI4zjot72EMfrkvL9g9aRjnnM= cloud.google.com/go/artifactregistry v1.15.1 h1:ANE2nBEqP2vGGA/5plRRUpatT3E/3ydSK8Z+lXiV69s= cloud.google.com/go/artifactregistry v1.15.1/go.mod h1:ExJb4VN+IMTQWO5iY+mjcY19Rz9jUxCVGZ1YuyAgPBw= -cloud.google.com/go/asset v1.14.1/go.mod h1:4bEJ3dnHCqWCDbWJ/6Vn7GVI9LerSi7Rfdi03hd+WTQ= -cloud.google.com/go/asset v1.15.0/go.mod h1:tpKafV6mEut3+vN9ScGvCHXHj7FALFVta+okxFECHcg= -cloud.google.com/go/asset v1.15.1/go.mod h1:yX/amTvFWRpp5rcFq6XbCxzKT8RJUam1UoboE179jU4= -cloud.google.com/go/asset v1.15.2/go.mod h1:B6H5tclkXvXz7PD22qCA2TDxSVQfasa3iDlM89O2NXs= -cloud.google.com/go/asset v1.15.3/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= -cloud.google.com/go/asset v1.16.0/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= -cloud.google.com/go/asset v1.17.0/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= -cloud.google.com/go/asset v1.17.1/go.mod h1:byvDw36UME5AzGNK7o4JnOnINkwOZ1yRrGrKIahHrng= -cloud.google.com/go/asset v1.17.2/go.mod h1:SVbzde67ehddSoKf5uebOD1sYw8Ab/jD/9EIeWg99q4= cloud.google.com/go/asset v1.20.2 h1:wAGSAzAmMC/KEFGZ6Z0zv3jOlz1fjBxuO7SiRX9FMuQ= cloud.google.com/go/asset v1.20.2/go.mod h1:IM1Kpzzo3wq7R/GEiktitzZyXx2zVpWqs9/5EGYs0GY= -cloud.google.com/go/assuredworkloads v1.11.1/go.mod h1:+F04I52Pgn5nmPG36CWFtxmav6+7Q+c5QyJoL18Lry0= -cloud.google.com/go/assuredworkloads v1.11.2/go.mod h1:O1dfr+oZJMlE6mw0Bp0P1KZSlj5SghMBvTpZqIcUAW4= -cloud.google.com/go/assuredworkloads v1.11.3/go.mod h1:vEjfTKYyRUaIeA0bsGJceFV2JKpVRgyG2op3jfa59Zs= -cloud.google.com/go/assuredworkloads v1.11.4/go.mod h1:4pwwGNwy1RP0m+y12ef3Q/8PaiWrIDQ6nD2E8kvWI9U= -cloud.google.com/go/assuredworkloads v1.11.5/go.mod h1:FKJ3g3ZvkL2D7qtqIGnDufFkHxwIpNM9vtmhvt+6wqk= cloud.google.com/go/assuredworkloads v1.12.1 h1:B+hWc62fYL8NdntPjx0rzJJ67qx99w6dCeIVDpHf7QE= cloud.google.com/go/assuredworkloads v1.12.1/go.mod h1:nBnkK2GZNSdtjU3ER75oC5fikub5/+QchbolKgnMI/I= -cloud.google.com/go/auth v0.2.0/go.mod h1:+yb+oy3/P0geX6DLKlqiGHARGR6EX2GRtYCzWOCQSbU= -cloud.google.com/go/auth v0.3.0/go.mod h1:lBv6NKTWp8E3LPzmO1TbiiRKc4drLOfHsgmlH9ogv5w= -cloud.google.com/go/auth v0.6.1/go.mod h1:eFHG7zDzbXHKmjJddFG/rBlcGp6t25SwRUiEQSlO4x4= -cloud.google.com/go/auth v0.9.3/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk= -cloud.google.com/go/auth v0.9.4/go.mod h1:SHia8n6//Ya940F1rLimhJCjjx7KE17t0ctFEci3HkA= -cloud.google.com/go/auth/oauth2adapt v0.2.0/go.mod h1:AfqujpDAlTfLfeCIl/HJZZlIxD8+nJoZ5e0x1IxGq5k= -cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= -cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= -cloud.google.com/go/automl v1.13.1/go.mod h1:1aowgAHWYZU27MybSCFiukPO7xnyawv7pt3zK4bheQE= -cloud.google.com/go/automl v1.13.2/go.mod h1:gNY/fUmDEN40sP8amAX3MaXkxcqPIn7F1UIIPZpy4Mg= -cloud.google.com/go/automl v1.13.3/go.mod h1:Y8KwvyAZFOsMAPqUCfNu1AyclbC6ivCUF/MTwORymyY= -cloud.google.com/go/automl v1.13.4/go.mod h1:ULqwX/OLZ4hBVfKQaMtxMSTlPx0GqGbWN8uA/1EqCP8= -cloud.google.com/go/automl v1.13.5/go.mod h1:MDw3vLem3yh+SvmSgeYUmUKqyls6NzSumDm9OJ3xJ1Y= cloud.google.com/go/automl v1.14.1 h1:IrNnM7oClTzfFcf5XgaZCGwicETU2aCmrGzE8U2DlVs= cloud.google.com/go/automl v1.14.1/go.mod h1:BocG5mhT32cjmf5CXxVsdSM04VXzJW7chVT7CpSL2kk= -cloud.google.com/go/baremetalsolution v1.1.1/go.mod h1:D1AV6xwOksJMV4OSlWHtWuFNZZYujJknMAP4Qa27QIA= -cloud.google.com/go/baremetalsolution v1.2.0/go.mod h1:68wi9AwPYkEWIUT4SvSGS9UJwKzNpshjHsH4lzk8iOw= -cloud.google.com/go/baremetalsolution v1.2.1/go.mod h1:3qKpKIw12RPXStwQXcbhfxVj1dqQGEvcmA+SX/mUR88= -cloud.google.com/go/baremetalsolution v1.2.2/go.mod h1:O5V6Uu1vzVelYahKfwEWRMaS3AbCkeYHy3145s1FkhM= -cloud.google.com/go/baremetalsolution v1.2.3/go.mod h1:/UAQ5xG3faDdy180rCUv47e0jvpp3BFxT+Cl0PFjw5g= -cloud.google.com/go/baremetalsolution v1.2.4/go.mod h1:BHCmxgpevw9IEryE99HbYEfxXkAEA3hkMJbYYsHtIuY= cloud.google.com/go/baremetalsolution v1.3.1 h1:Zbsrhw8vm4Byki+ynVuACZ6jxYiKzi1f8Hac5zXGD8Y= cloud.google.com/go/baremetalsolution v1.3.1/go.mod h1:D1djGGmBl4M6VlyjOMc1SEzDYlO4EeEG1TCUv5mCPi0= -cloud.google.com/go/batch v1.3.1/go.mod h1:VguXeQKXIYaeeIYbuozUmBR13AfL4SJP7IltNPS+A4A= -cloud.google.com/go/batch v1.4.1/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= -cloud.google.com/go/batch v1.5.0/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= -cloud.google.com/go/batch v1.5.1/go.mod h1:RpBuIYLkQu8+CWDk3dFD/t/jOCGuUpkpX+Y0n1Xccs8= -cloud.google.com/go/batch v1.6.1/go.mod h1:urdpD13zPe6YOK+6iZs/8/x2VBRofvblLpx0t57vM98= -cloud.google.com/go/batch v1.6.3/go.mod h1:J64gD4vsNSA2O5TtDB5AAux3nJ9iV8U3ilg3JDBYejU= -cloud.google.com/go/batch v1.7.0/go.mod h1:J64gD4vsNSA2O5TtDB5AAux3nJ9iV8U3ilg3JDBYejU= -cloud.google.com/go/batch v1.8.0/go.mod h1:k8V7f6VE2Suc0zUM4WtoibNrA6D3dqBpB+++e3vSGYc= cloud.google.com/go/batch v1.11.1 h1:50TRhaaZv7QDCb60KcZUPkGx1oO46srDp5076wZkgI8= cloud.google.com/go/batch v1.11.1/go.mod h1:4GbJXfdxU8GH6uuo8G47y5tEFOgTLCL9pMKCUcn7VxE= -cloud.google.com/go/beyondcorp v0.6.1/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.0/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.1/go.mod h1:zl/rWWAFVeV+kx+X2Javly7o1EIQThU4WlkynffL/lk= -cloud.google.com/go/beyondcorp v1.0.2/go.mod h1:m8cpG7caD+5su+1eZr+TSvF6r21NdLJk4f9u4SP2Ntc= -cloud.google.com/go/beyondcorp v1.0.3/go.mod h1:HcBvnEd7eYr+HGDd5ZbuVmBYX019C6CEXBonXbCVwJo= -cloud.google.com/go/beyondcorp v1.0.4/go.mod h1:Gx8/Rk2MxrvWfn4WIhHIG1NV7IBfg14pTKv1+EArVcc= cloud.google.com/go/beyondcorp v1.1.1 h1:owviaab14M9ySEvCj3EZdfzkRLnE+5j4JIkqVaQtEUU= cloud.google.com/go/beyondcorp v1.1.1/go.mod h1:L09o0gLkgXMxCZs4qojrgpI2/dhWtasMc71zPPiHMn4= -cloud.google.com/go/bigquery v1.52.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= -cloud.google.com/go/bigquery v1.53.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= -cloud.google.com/go/bigquery v1.55.0/go.mod h1:9Y5I3PN9kQWuid6183JFhOGOW3GcirA5LpsKCUn+2ec= -cloud.google.com/go/bigquery v1.56.0/go.mod h1:KDcsploXTEY7XT3fDQzMUZlpQLHzE4itubHrnmhUrZA= -cloud.google.com/go/bigquery v1.57.1/go.mod h1:iYzC0tGVWt1jqSzBHqCr3lrRn0u13E8e+AqowBsDgug= -cloud.google.com/go/bigquery v1.58.0/go.mod h1:0eh4mWNY0KrBTjUzLjoYImapGORq9gEPT7MWjCy9lik= -cloud.google.com/go/bigquery v1.59.1/go.mod h1:VP1UJYgevyTwsV7desjzNzDND5p6hZB+Z8gZJN1GQUc= cloud.google.com/go/bigquery v1.63.1 h1:/6syiWrSpardKNxdvldS5CUTRJX1iIkSPXCjLjiGL+g= cloud.google.com/go/bigquery v1.63.1/go.mod h1:ufaITfroCk17WTqBhMpi8CRjsfHjMX07pDrQaRKKX2o= cloud.google.com/go/bigtable v1.33.0 h1:2BDaWLRAwXO14DJL/u8crbV2oUbMZkIa2eGq8Yao1bk= cloud.google.com/go/bigtable v1.33.0/go.mod h1:HtpnH4g25VT1pejHRtInlFPnN5sjTxbQlsYBjh9t5l0= -cloud.google.com/go/billing v1.16.0/go.mod h1:y8vx09JSSJG02k5QxbycNRrN7FGZB6F3CAcgum7jvGA= -cloud.google.com/go/billing v1.17.0/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= -cloud.google.com/go/billing v1.17.1/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= -cloud.google.com/go/billing v1.17.2/go.mod h1:u/AdV/3wr3xoRBk5xvUzYMS1IawOAPwQMuHgHMdljDg= -cloud.google.com/go/billing v1.17.3/go.mod h1:z83AkoZ7mZwBGT3yTnt6rSGI1OOsHSIi6a5M3mJ8NaU= -cloud.google.com/go/billing v1.17.4/go.mod h1:5DOYQStCxquGprqfuid/7haD7th74kyMBHkjO/OvDtk= -cloud.google.com/go/billing v1.18.0/go.mod h1:5DOYQStCxquGprqfuid/7haD7th74kyMBHkjO/OvDtk= -cloud.google.com/go/billing v1.18.2/go.mod h1:PPIwVsOOQ7xzbADCwNe8nvK776QpfrOAUkvKjCUcpSE= cloud.google.com/go/billing v1.19.1 h1:BtbMCM9QDWiszfNXEAcq0MB6vgCuc0/yzP3vye2Kz3U= cloud.google.com/go/billing v1.19.1/go.mod h1:c5l7ORJjOLH/aASJqUqNsEmwrhfjWZYHX+z0fIhuVpo= -cloud.google.com/go/binaryauthorization v1.6.1/go.mod h1:TKt4pa8xhowwffiBmbrbcxijJRZED4zrqnwZ1lKH51U= -cloud.google.com/go/binaryauthorization v1.7.0/go.mod h1:Zn+S6QqTMn6odcMU1zDZCJxPjU2tZPV1oDl45lWY154= -cloud.google.com/go/binaryauthorization v1.7.1/go.mod h1:GTAyfRWYgcbsP3NJogpV3yeunbUIjx2T9xVeYovtURE= -cloud.google.com/go/binaryauthorization v1.7.2/go.mod h1:kFK5fQtxEp97m92ziy+hbu+uKocka1qRRL8MVJIgjv0= -cloud.google.com/go/binaryauthorization v1.7.3/go.mod h1:VQ/nUGRKhrStlGr+8GMS8f6/vznYLkdK5vaKfdCIpvU= -cloud.google.com/go/binaryauthorization v1.8.0/go.mod h1:VQ/nUGRKhrStlGr+8GMS8f6/vznYLkdK5vaKfdCIpvU= -cloud.google.com/go/binaryauthorization v1.8.1/go.mod h1:1HVRyBerREA/nhI7yLang4Zn7vfNVA3okoAR9qYQJAQ= cloud.google.com/go/binaryauthorization v1.9.1 h1:fVtOG5rVU0eaVh2G2ORdT7nigsnK1R1JpqfGzW861OM= cloud.google.com/go/binaryauthorization v1.9.1/go.mod h1:jqBzP68bfzjoiMFT6Q1EdZtKJG39zW9ywwzHuv7V8ms= -cloud.google.com/go/certificatemanager v1.7.1/go.mod h1:iW8J3nG6SaRYImIa+wXQ0g8IgoofDFRp5UMzaNk1UqI= -cloud.google.com/go/certificatemanager v1.7.2/go.mod h1:15SYTDQMd00kdoW0+XY5d9e+JbOPjp24AvF48D8BbcQ= -cloud.google.com/go/certificatemanager v1.7.3/go.mod h1:T/sZYuC30PTag0TLo28VedIRIj1KPGcOQzjWAptHa00= -cloud.google.com/go/certificatemanager v1.7.4/go.mod h1:FHAylPe/6IIKuaRmHbjbdLhGhVQ+CWHSD5Jq0k4+cCE= -cloud.google.com/go/certificatemanager v1.7.5/go.mod h1:uX+v7kWqy0Y3NG/ZhNvffh0kuqkKZIXdvlZRO7z0VtM= cloud.google.com/go/certificatemanager v1.9.1 h1:fULhIdwsz3SoZfiXw8XaxSJBpRTR0xwsJleO+wEbbKA= cloud.google.com/go/certificatemanager v1.9.1/go.mod h1:a6bXZULtd6iQTRuSVs1fopcHLMJ/T3zSpIB7aJaq/js= -cloud.google.com/go/channel v1.16.0/go.mod h1:eN/q1PFSl5gyu0dYdmxNXscY/4Fi7ABmeHCJNf/oHmc= -cloud.google.com/go/channel v1.17.0/go.mod h1:RpbhJsGi/lXWAUM1eF4IbQGbsfVlg2o8Iiy2/YLfVT0= -cloud.google.com/go/channel v1.17.1/go.mod h1:xqfzcOZAcP4b/hUDH0GkGg1Sd5to6di1HOJn/pi5uBQ= -cloud.google.com/go/channel v1.17.2/go.mod h1:aT2LhnftnyfQceFql5I/mP8mIbiiJS4lWqgXA815zMk= -cloud.google.com/go/channel v1.17.3/go.mod h1:QcEBuZLGGrUMm7kNj9IbU1ZfmJq2apotsV83hbxX7eE= -cloud.google.com/go/channel v1.17.4/go.mod h1:QcEBuZLGGrUMm7kNj9IbU1ZfmJq2apotsV83hbxX7eE= -cloud.google.com/go/channel v1.17.5/go.mod h1:FlpaOSINDAXgEext0KMaBq/vwpLMkkPAw9b2mApQeHc= cloud.google.com/go/channel v1.19.0 h1:YdCa/Y6lhGVeR058gQGhTunEuR9zVuheukKL+pcldgI= cloud.google.com/go/channel v1.19.0/go.mod h1:8BEvuN5hWL4tT0rmJR4N8xsZHdfGof+KwemjQH6oXsw= -cloud.google.com/go/cloudbuild v1.10.1/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.13.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.14.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.14.1/go.mod h1:K7wGc/3zfvmYWOWwYTgF/d/UVJhS4pu+HAy7PL7mCsU= -cloud.google.com/go/cloudbuild v1.14.2/go.mod h1:Bn6RO0mBYk8Vlrt+8NLrru7WXlQ9/RDWz2uo5KG1/sg= -cloud.google.com/go/cloudbuild v1.14.3/go.mod h1:eIXYWmRt3UtggLnFGx4JvXcMj4kShhVzGndL1LwleEM= -cloud.google.com/go/cloudbuild v1.15.0/go.mod h1:eIXYWmRt3UtggLnFGx4JvXcMj4kShhVzGndL1LwleEM= -cloud.google.com/go/cloudbuild v1.15.1/go.mod h1:gIofXZSu+XD2Uy+qkOrGKEx45zd7s28u/k8f99qKals= cloud.google.com/go/cloudbuild v1.18.0 h1:82f6g0AzacK1bbO0E5ZqixWc4nRzWu4ichIQ0QKNtAQ= cloud.google.com/go/cloudbuild v1.18.0/go.mod h1:KCHWGIoS/5fj+By9YmgIQnUiDq8P6YURWOjX3hoc6As= -cloud.google.com/go/clouddms v1.6.1/go.mod h1:Ygo1vL52Ov4TBZQquhz5fiw2CQ58gvu+PlS6PVXCpZI= -cloud.google.com/go/clouddms v1.7.0/go.mod h1:MW1dC6SOtI/tPNCciTsXtsGNEM0i0OccykPvv3hiYeM= -cloud.google.com/go/clouddms v1.7.1/go.mod h1:o4SR8U95+P7gZ/TX+YbJxehOCsM+fe6/brlrFquiszk= -cloud.google.com/go/clouddms v1.7.2/go.mod h1:Rk32TmWmHo64XqDvW7jgkFQet1tUKNVzs7oajtJT3jU= -cloud.google.com/go/clouddms v1.7.3/go.mod h1:fkN2HQQNUYInAU3NQ3vRLkV2iWs8lIdmBKOx4nrL6Hc= -cloud.google.com/go/clouddms v1.7.4/go.mod h1:RdrVqoFG9RWI5AvZ81SxJ/xvxPdtcRhFotwdE79DieY= cloud.google.com/go/clouddms v1.8.1 h1:vf5R4/FoLHxEP2BBKEafLHfYFWa6Zd9gwrXe/FjrwUg= cloud.google.com/go/clouddms v1.8.1/go.mod h1:bmW2eDFH1LjuwkHcKKeeppcmuBGS0r6Qz6TXanehKP0= -cloud.google.com/go/cloudtasks v1.11.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.2/go.mod h1:A7nYkjNlW2gUoROg1kvJrQGhJP/38UaWwsnuBDOBVUk= -cloud.google.com/go/cloudtasks v1.12.3/go.mod h1:GPVXhIOSGEaR+3xT4Fp72ScI+HjHffSS4B8+BaBB5Ys= -cloud.google.com/go/cloudtasks v1.12.4/go.mod h1:BEPu0Gtt2dU6FxZHNqqNdGqIG86qyWKBPGnsb7udGY0= -cloud.google.com/go/cloudtasks v1.12.6/go.mod h1:b7c7fe4+TJsFZfDyzO51F7cjq7HLUlRi/KZQLQjDsaY= cloud.google.com/go/cloudtasks v1.13.1 h1:s1JTLBD+WbzQwxYPAwa2WIxPT3kOiv7MSKyvSEgNQtg= cloud.google.com/go/cloudtasks v1.13.1/go.mod h1:dyRD7tEEkLMbHLagb7UugkDa77UVJp9d/6O9lm3ModI= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= -cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= -cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns= -cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= -cloud.google.com/go/compute v1.23.4/go.mod h1:/EJMj55asU6kAFnuZET8zqgwgJ9FvXWXOkkfQZa4ioI= -cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= cloud.google.com/go/compute v1.28.1 h1:XwPcZjgMCnU2tkwY10VleUjSAfpTj9RDn+kGrbYsi8o= cloud.google.com/go/compute v1.28.1/go.mod h1:b72iXMY4FucVry3NR3Li4kVyyTvbMDE7x5WsqvxjsYk= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= -cloud.google.com/go/contactcenterinsights v1.9.1/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/contactcenterinsights v1.10.0/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/contactcenterinsights v1.11.0/go.mod h1:hutBdImE4XNZ1NV4vbPJKSFOnQruhC5Lj9bZqWMTKiU= -cloud.google.com/go/contactcenterinsights v1.11.1/go.mod h1:FeNP3Kg8iteKM80lMwSk3zZZKVxr+PGnAId6soKuXwE= -cloud.google.com/go/contactcenterinsights v1.11.2/go.mod h1:A9PIR5ov5cRcd28KlDbmmXE8Aay+Gccer2h4wzkYFso= -cloud.google.com/go/contactcenterinsights v1.11.3/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= -cloud.google.com/go/contactcenterinsights v1.12.0/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= -cloud.google.com/go/contactcenterinsights v1.12.1/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= -cloud.google.com/go/contactcenterinsights v1.13.0/go.mod h1:ieq5d5EtHsu8vhe2y3amtZ+BE+AQwX5qAy7cpo0POsI= cloud.google.com/go/contactcenterinsights v1.15.0 h1:jHwyL2TQTaLauRRz5Uv7/sL7PNAK1VAMy/UIT9vsFzk= cloud.google.com/go/contactcenterinsights v1.15.0/go.mod h1:6bJGBQrJsnATv2s6Dh/c6HCRanq2kCZ0kIIjRV1G0mI= -cloud.google.com/go/container v1.22.1/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= -cloud.google.com/go/container v1.24.0/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= -cloud.google.com/go/container v1.26.0/go.mod h1:YJCmRet6+6jnYYRS000T6k0D0xUXQgBSaJ7VwI8FBj4= -cloud.google.com/go/container v1.26.1/go.mod h1:5smONjPRUxeEpDG7bMKWfDL4sauswqEtnBK1/KKpR04= -cloud.google.com/go/container v1.26.2/go.mod h1:YlO84xCt5xupVbLaMY4s3XNE79MUJ+49VmkInr6HvF4= -cloud.google.com/go/container v1.27.1/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= -cloud.google.com/go/container v1.28.0/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= -cloud.google.com/go/container v1.29.0/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= -cloud.google.com/go/container v1.30.1/go.mod h1:vkbfX0EnAKL/vgVECs5BZn24e1cJROzgszJirRKQ4Bg= -cloud.google.com/go/container v1.31.0/go.mod h1:7yABn5s3Iv3lmw7oMmyGbeV6tQj86njcTijkkGuvdZA= cloud.google.com/go/container v1.40.0 h1:JVoEg/4RvoGW37r2Eja/cTBc3X9c2loGWYq7QDsRDuI= cloud.google.com/go/container v1.40.0/go.mod h1:wNI1mOUivm+ZkpHMbouutgbD4sQxyphMwK31X5cThY4= -cloud.google.com/go/containeranalysis v0.10.1/go.mod h1:Ya2jiILITMY68ZLPaogjmOMNkwsDrWBSTyBubGXO7j0= -cloud.google.com/go/containeranalysis v0.11.0/go.mod h1:4n2e99ZwpGxpNcz+YsFT1dfOHPQFGcAC8FN2M2/ne/U= -cloud.google.com/go/containeranalysis v0.11.1/go.mod h1:rYlUOM7nem1OJMKwE1SadufX0JP3wnXj844EtZAwWLY= -cloud.google.com/go/containeranalysis v0.11.2/go.mod h1:xibioGBC1MD2j4reTyV1xY1/MvKaz+fyM9ENWhmIeP8= -cloud.google.com/go/containeranalysis v0.11.3/go.mod h1:kMeST7yWFQMGjiG9K7Eov+fPNQcGhb8mXj/UcTiWw9U= -cloud.google.com/go/containeranalysis v0.11.4/go.mod h1:cVZT7rXYBS9NG1rhQbWL9pWbXCKHWJPYraE8/FTSYPE= cloud.google.com/go/containeranalysis v0.13.1 h1:opZRo0HEVLm4ylTbbXw/H68M3vQjdkYOSMfUY63+D+0= cloud.google.com/go/containeranalysis v0.13.1/go.mod h1:bmd9H880BNR4Hc8JspEg8ge9WccSQfO+/N+CYvU3sEA= -cloud.google.com/go/datacatalog v1.14.0/go.mod h1:h0PrGtlihoutNMp/uvwhawLQ9+c63Kz65UFqh49Yo+E= -cloud.google.com/go/datacatalog v1.14.1/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= -cloud.google.com/go/datacatalog v1.16.0/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= -cloud.google.com/go/datacatalog v1.17.1/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= -cloud.google.com/go/datacatalog v1.18.0/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= -cloud.google.com/go/datacatalog v1.18.1/go.mod h1:TzAWaz+ON1tkNr4MOcak8EBHX7wIRX/gZKM+yTVsv+A= -cloud.google.com/go/datacatalog v1.18.2/go.mod h1:SPVgWW2WEMuWHA+fHodYjmxPiMqcOiWfhc9OD5msigk= -cloud.google.com/go/datacatalog v1.18.3/go.mod h1:5FR6ZIF8RZrtml0VUao22FxhdjkoG+a0866rEnObryM= -cloud.google.com/go/datacatalog v1.19.0/go.mod h1:5FR6ZIF8RZrtml0VUao22FxhdjkoG+a0866rEnObryM= -cloud.google.com/go/datacatalog v1.19.2/go.mod h1:2YbODwmhpLM4lOFe3PuEhHK9EyTzQJ5AXgIy7EDKTEE= -cloud.google.com/go/datacatalog v1.19.3/go.mod h1:ra8V3UAsciBpJKQ+z9Whkxzxv7jmQg1hfODr3N3YPJ4= cloud.google.com/go/datacatalog v1.22.1 h1:i0DyKb/o7j+0vgaFtimcRFjYsD6wFw1jpnODYUyiYRs= cloud.google.com/go/datacatalog v1.22.1/go.mod h1:MscnJl9B2lpYlFoxRjicw19kFTwEke8ReKL5Y/6TWg8= -cloud.google.com/go/dataflow v0.9.1/go.mod h1:Wp7s32QjYuQDWqJPFFlnBKhkAtiFpMTdg00qGbnIHVw= -cloud.google.com/go/dataflow v0.9.2/go.mod h1:vBfdBZ/ejlTaYIGB3zB4T08UshH70vbtZeMD+urnUSo= -cloud.google.com/go/dataflow v0.9.3/go.mod h1:HI4kMVjcHGTs3jTHW/kv3501YW+eloiJSLxkJa/vqFE= -cloud.google.com/go/dataflow v0.9.4/go.mod h1:4G8vAkHYCSzU8b/kmsoR2lWyHJD85oMJPHMtan40K8w= -cloud.google.com/go/dataflow v0.9.5/go.mod h1:udl6oi8pfUHnL0z6UN9Lf9chGqzDMVqcYTcZ1aPnCZQ= cloud.google.com/go/dataflow v0.10.1 h1:RoVpCZ1BjJBH/5mzaXCgNg+l9FgTIYQ7C9xBRGvhkzo= cloud.google.com/go/dataflow v0.10.1/go.mod h1:zP4/tNjONFRcS4NcI9R94YDQEkPalimdbPkijVNJt/g= -cloud.google.com/go/dataform v0.8.1/go.mod h1:3BhPSiw8xmppbgzeBbmDvmSWlwouuJkXsXsb8UBih9M= -cloud.google.com/go/dataform v0.8.2/go.mod h1:X9RIqDs6NbGPLR80tnYoPNiO1w0wenKTb8PxxlhTMKM= -cloud.google.com/go/dataform v0.8.3/go.mod h1:8nI/tvv5Fso0drO3pEjtowz58lodx8MVkdV2q0aPlqg= -cloud.google.com/go/dataform v0.9.1/go.mod h1:pWTg+zGQ7i16pyn0bS1ruqIE91SdL2FDMvEYu/8oQxs= -cloud.google.com/go/dataform v0.9.2/go.mod h1:S8cQUwPNWXo7m/g3DhWHsLBoufRNn9EgFrMgne2j7cI= cloud.google.com/go/dataform v0.10.1 h1:FkOPrxf8sN9J2TMc4CIBhVivhMiO8D0eYN33s5A5Uo4= cloud.google.com/go/dataform v0.10.1/go.mod h1:c5y0hIOBCfszmBcLJyxnELF30gC1qC/NeHdmkzA7TNQ= -cloud.google.com/go/datafusion v1.7.1/go.mod h1:KpoTBbFmoToDExJUso/fcCiguGDk7MEzOWXUsJo0wsI= -cloud.google.com/go/datafusion v1.7.2/go.mod h1:62K2NEC6DRlpNmI43WHMWf9Vg/YvN6QVi8EVwifElI0= -cloud.google.com/go/datafusion v1.7.3/go.mod h1:eoLt1uFXKGBq48jy9LZ+Is8EAVLnmn50lNncLzwYokE= -cloud.google.com/go/datafusion v1.7.4/go.mod h1:BBs78WTOLYkT4GVZIXQCZT3GFpkpDN4aBY4NDX/jVlM= -cloud.google.com/go/datafusion v1.7.5/go.mod h1:bYH53Oa5UiqahfbNK9YuYKteeD4RbQSNMx7JF7peGHc= cloud.google.com/go/datafusion v1.8.1 h1:QqiQs3mSXl4gfeHGOTbK0v1y+tUOnxWJgXm6YWvoqY0= cloud.google.com/go/datafusion v1.8.1/go.mod h1:I5+nRt6Lob4g1eCbcxP4ayRNx8hyOZ8kA3PB/vGd9Lo= -cloud.google.com/go/datalabeling v0.8.1/go.mod h1:XS62LBSVPbYR54GfYQsPXZjTW8UxCK2fkDciSrpRFdY= -cloud.google.com/go/datalabeling v0.8.2/go.mod h1:cyDvGHuJWu9U/cLDA7d8sb9a0tWLEletStu2sTmg3BE= -cloud.google.com/go/datalabeling v0.8.3/go.mod h1:tvPhpGyS/V7lqjmb3V0TaDdGvhzgR1JoW7G2bpi2UTI= -cloud.google.com/go/datalabeling v0.8.4/go.mod h1:Z1z3E6LHtffBGrNUkKwbwbDxTiXEApLzIgmymj8A3S8= -cloud.google.com/go/datalabeling v0.8.5/go.mod h1:IABB2lxQnkdUbMnQaOl2prCOfms20mcPxDBm36lps+s= cloud.google.com/go/datalabeling v0.9.1 h1:FrnZKagECxQy1bL+GQ1bjgwK9+szi1l7gqw7zp+Raqs= cloud.google.com/go/datalabeling v0.9.1/go.mod h1:umplHuZX+x5DItNPV5BFBXau5TDsljLNzEj5AB5uRUM= -cloud.google.com/go/dataplex v1.8.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.9.0/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.9.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.10.1/go.mod h1:1MzmBv8FvjYfc7vDdxhnLFNskikkB+3vl475/XdCDhs= -cloud.google.com/go/dataplex v1.10.2/go.mod h1:xdC8URdTrCrZMW6keY779ZT1cTOfV8KEPNsw+LTRT1Y= -cloud.google.com/go/dataplex v1.11.1/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.11.2/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.13.0/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.14.0/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.14.1/go.mod h1:bWxQAbg6Smg+sca2+Ex7s8D9a5qU6xfXtwmq4BVReps= -cloud.google.com/go/dataplex v1.14.2/go.mod h1:0oGOSFlEKef1cQeAHXy4GZPB/Ife0fz/PxBf+ZymA2U= cloud.google.com/go/dataplex v1.19.1 h1:0pgI0DwijXZq8vyLuGnQXSi9JB6eUaVqzpzhN2veUeE= cloud.google.com/go/dataplex v1.19.1/go.mod h1:WzoQ+vcxrAyM0cjJWmluEDVsg7W88IXXCfuy01BslKE= cloud.google.com/go/dataproc v1.12.0 h1:W47qHL3W4BPkAIbk4SWmIERwsWBaNnWm0P2sdx3YgGU= -cloud.google.com/go/dataproc/v2 v2.0.1/go.mod h1:7Ez3KRHdFGcfY7GcevBbvozX+zyWGcwLJvvAMwCaoZ4= -cloud.google.com/go/dataproc/v2 v2.2.0/go.mod h1:lZR7AQtwZPvmINx5J87DSOOpTfof9LVZju6/Qo4lmcY= -cloud.google.com/go/dataproc/v2 v2.2.1/go.mod h1:QdAJLaBjh+l4PVlVZcmrmhGccosY/omC1qwfQ61Zv/o= -cloud.google.com/go/dataproc/v2 v2.2.2/go.mod h1:aocQywVmQVF4i8CL740rNI/ZRpsaaC1Wh2++BJ7HEJ4= -cloud.google.com/go/dataproc/v2 v2.2.3/go.mod h1:G5R6GBc9r36SXv/RtZIVfB8SipI+xVn0bX5SxUzVYbY= -cloud.google.com/go/dataproc/v2 v2.3.0/go.mod h1:G5R6GBc9r36SXv/RtZIVfB8SipI+xVn0bX5SxUzVYbY= -cloud.google.com/go/dataproc/v2 v2.4.0/go.mod h1:3B1Ht2aRB8VZIteGxQS/iNSJGzt9+CA0WGnDVMEm7Z4= cloud.google.com/go/dataproc/v2 v2.9.0 h1:9fSMjWgFKQfmfKu7V10C5foxU/2iDa8bVkiBB8uh1EU= cloud.google.com/go/dataproc/v2 v2.9.0/go.mod h1:i4365hSwNP6Bx0SAUnzCC6VloeNxChDjJWH6BfVPcbs= -cloud.google.com/go/dataqna v0.8.1/go.mod h1:zxZM0Bl6liMePWsHA8RMGAfmTG34vJMapbHAxQ5+WA8= -cloud.google.com/go/dataqna v0.8.2/go.mod h1:KNEqgx8TTmUipnQsScOoDpq/VlXVptUqVMZnt30WAPs= -cloud.google.com/go/dataqna v0.8.3/go.mod h1:wXNBW2uvc9e7Gl5k8adyAMnLush1KVV6lZUhB+rqNu4= -cloud.google.com/go/dataqna v0.8.4/go.mod h1:mySRKjKg5Lz784P6sCov3p1QD+RZQONRMRjzGNcFd0c= -cloud.google.com/go/dataqna v0.8.5/go.mod h1:vgihg1mz6n7pb5q2YJF7KlXve6tCglInd6XO0JGOlWM= cloud.google.com/go/dataqna v0.9.1 h1:ptKKT+CNwp9Q+9Zxr+npUO7qUwKfyq/oF7/nS7CC6sc= cloud.google.com/go/dataqna v0.9.1/go.mod h1:86DNLE33yEfNDp5F2nrITsmTYubMbsF7zQRzC3CcZrY= -cloud.google.com/go/datastore v1.12.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.12.1/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.13.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.14.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= -cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= cloud.google.com/go/datastore v1.19.0 h1:p5H3bUQltOa26GcMRAxPoNwoqGkq5v8ftx9/ZBB35MI= cloud.google.com/go/datastore v1.19.0/go.mod h1:KGzkszuj87VT8tJe67GuB+qLolfsOt6bZq/KFuWaahc= -cloud.google.com/go/datastream v1.9.1/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.0/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.1/go.mod h1:7ngSYwnw95YFyTd5tOGBxHlOZiL+OtpjheqU7t2/s/c= -cloud.google.com/go/datastream v1.10.2/go.mod h1:W42TFgKAs/om6x/CdXX5E4oiAsKlH+e8MTGy81zdYt0= -cloud.google.com/go/datastream v1.10.3/go.mod h1:YR0USzgjhqA/Id0Ycu1VvZe8hEWwrkjuXrGbzeDOSEA= -cloud.google.com/go/datastream v1.10.4/go.mod h1:7kRxPdxZxhPg3MFeCSulmAJnil8NJGGvSNdn4p1sRZo= cloud.google.com/go/datastream v1.11.1 h1:YKY2qGKoxPpAvsDMtmJlIwL59SzhEm1DHM2uM4ib0TY= cloud.google.com/go/datastream v1.11.1/go.mod h1:a4j5tnptIxdZ132XboR6uQM/ZHcuv/hLqA6hH3NJWgk= -cloud.google.com/go/deploy v1.11.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.13.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.13.1/go.mod h1:8jeadyLkH9qu9xgO3hVWw8jVr29N1mnW42gRJT8GY6g= -cloud.google.com/go/deploy v1.14.1/go.mod h1:N8S0b+aIHSEeSr5ORVoC0+/mOPUysVt8ae4QkZYolAw= -cloud.google.com/go/deploy v1.14.2/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= -cloud.google.com/go/deploy v1.15.0/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= -cloud.google.com/go/deploy v1.16.0/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= -cloud.google.com/go/deploy v1.17.0/go.mod h1:XBr42U5jIr64t92gcpOXxNrqL2PStQCXHuKK5GRUuYo= -cloud.google.com/go/deploy v1.17.1/go.mod h1:SXQyfsXrk0fBmgBHRzBjQbZhMfKZ3hMQBw5ym7MN/50= cloud.google.com/go/deploy v1.23.0 h1:Bmh5UYEeakXtjggRkjVIawXfSBbQsTgDlm96pCw9D3k= cloud.google.com/go/deploy v1.23.0/go.mod h1:O7qoXcg44Ebfv9YIoFEgYjPmrlPsXD4boYSVEiTqdHY= -cloud.google.com/go/dialogflow v1.38.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dialogflow v1.40.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dialogflow v1.43.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= -cloud.google.com/go/dialogflow v1.44.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= -cloud.google.com/go/dialogflow v1.44.1/go.mod h1:n/h+/N2ouKOO+rbe/ZnI186xImpqvCVj2DdsWS/0EAk= -cloud.google.com/go/dialogflow v1.44.2/go.mod h1:QzFYndeJhpVPElnFkUXxdlptx0wPnBWLCBT9BvtC3/c= -cloud.google.com/go/dialogflow v1.44.3/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= -cloud.google.com/go/dialogflow v1.47.0/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= -cloud.google.com/go/dialogflow v1.48.0/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= -cloud.google.com/go/dialogflow v1.48.1/go.mod h1:C1sjs2/g9cEwjCltkKeYp3FFpz8BOzNondEaAlCpt+A= -cloud.google.com/go/dialogflow v1.48.2/go.mod h1:7A2oDf6JJ1/+hdpnFRfb/RjJUOh2X3rhIa5P8wQSEX4= -cloud.google.com/go/dialogflow v1.49.0/go.mod h1:dhVrXKETtdPlpPhE7+2/k4Z8FRNUp6kMV3EW3oz/fe0= cloud.google.com/go/dialogflow v1.58.0 h1:RTpoVCJHkgNLK8Co/f7F8ipyg3h8fJIaQzdaAbyg788= cloud.google.com/go/dialogflow v1.58.0/go.mod h1:sWcyFLdUrg+TWBJVq/OtwDyjcyDOfirTF0Gx12uKy7o= -cloud.google.com/go/dlp v1.10.1/go.mod h1:IM8BWz1iJd8njcNcG0+Kyd9OPnqnRNkDV8j42VT5KOI= -cloud.google.com/go/dlp v1.10.2/go.mod h1:ZbdKIhcnyhILgccwVDzkwqybthh7+MplGC3kZVZsIOQ= -cloud.google.com/go/dlp v1.10.3/go.mod h1:iUaTc/ln8I+QT6Ai5vmuwfw8fqTk2kaz0FvCwhLCom0= -cloud.google.com/go/dlp v1.11.1/go.mod h1:/PA2EnioBeXTL/0hInwgj0rfsQb3lpE3R8XUJxqUNKI= -cloud.google.com/go/dlp v1.11.2/go.mod h1:9Czi+8Y/FegpWzgSfkRlyz+jwW6Te9Rv26P3UfU/h/w= cloud.google.com/go/dlp v1.19.0 h1:AJB26PpDG0gOkf6wxQqbBXs9G+jOVnCjCagOlNiroKM= cloud.google.com/go/dlp v1.19.0/go.mod h1:cr8dKBq8un5LALiyGkz4ozcwzt3FyTlOwA4/fFzJ64c= -cloud.google.com/go/documentai v1.20.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= -cloud.google.com/go/documentai v1.22.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= -cloud.google.com/go/documentai v1.22.1/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= -cloud.google.com/go/documentai v1.23.0/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= -cloud.google.com/go/documentai v1.23.2/go.mod h1:Q/wcRT+qnuXOpjAkvOV4A+IeQl04q2/ReT7SSbytLSo= -cloud.google.com/go/documentai v1.23.4/go.mod h1:4MYAaEMnADPN1LPN5xboDR5QVB6AgsaxgFdJhitlE2Y= -cloud.google.com/go/documentai v1.23.5/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= -cloud.google.com/go/documentai v1.23.6/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= -cloud.google.com/go/documentai v1.23.7/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= -cloud.google.com/go/documentai v1.23.8/go.mod h1:Vd/y5PosxCpUHmwC+v9arZyeMfTqBR9VIwOwIqQYYfA= -cloud.google.com/go/documentai v1.25.0/go.mod h1:ftLnzw5VcXkLItp6pw1mFic91tMRyfv6hHEY5br4KzY= cloud.google.com/go/documentai v1.34.0 h1:gmBmrTLzbpZkllu2xExISZg2Hh/ai0y605SWdheWHvI= cloud.google.com/go/documentai v1.34.0/go.mod h1:onJlbHi4ZjQTsANSZJvW7fi2M8LZJrrupXkWDcy4gLY= -cloud.google.com/go/domains v0.9.1/go.mod h1:aOp1c0MbejQQ2Pjf1iJvnVyT+z6R6s8pX66KaCSDYfE= -cloud.google.com/go/domains v0.9.2/go.mod h1:3YvXGYzZG1Temjbk7EyGCuGGiXHJwVNmwIf+E/cUp5I= -cloud.google.com/go/domains v0.9.3/go.mod h1:29k66YNDLDY9LCFKpGFeh6Nj9r62ZKm5EsUJxAl84KU= -cloud.google.com/go/domains v0.9.4/go.mod h1:27jmJGShuXYdUNjyDG0SodTfT5RwLi7xmH334Gvi3fY= -cloud.google.com/go/domains v0.9.5/go.mod h1:dBzlxgepazdFhvG7u23XMhmMKBjrkoUNaw0A8AQB55Y= cloud.google.com/go/domains v0.10.1 h1:HvZOm7Bx1fQY/MHQAbE5f8YwfJlc0NJVOGh0A0eWckc= cloud.google.com/go/domains v0.10.1/go.mod h1:RjDl3K8iq/ZZHMVqfZzRuBUr5t85gqA6LEXQBeBL5F4= -cloud.google.com/go/edgecontainer v1.1.1/go.mod h1:O5bYcS//7MELQZs3+7mabRqoWQhXCzenBu0R8bz2rwk= -cloud.google.com/go/edgecontainer v1.1.2/go.mod h1:wQRjIzqxEs9e9wrtle4hQPSR1Y51kqN75dgF7UllZZ4= -cloud.google.com/go/edgecontainer v1.1.3/go.mod h1:Ll2DtIABzEfaxaVSbwj3QHFaOOovlDFiWVDu349jSsA= -cloud.google.com/go/edgecontainer v1.1.4/go.mod h1:AvFdVuZuVGdgaE5YvlL1faAoa1ndRR/5XhXZvPBHbsE= -cloud.google.com/go/edgecontainer v1.1.5/go.mod h1:rgcjrba3DEDEQAidT4yuzaKWTbkTI5zAMu3yy6ZWS0M= cloud.google.com/go/edgecontainer v1.3.1 h1:loDGWu/sdqnCP3Xlvj4OWHL7i0wocbcLg8ApQ9BE66E= cloud.google.com/go/edgecontainer v1.3.1/go.mod h1:qyz5+Nk/UAs6kXp6wiux9I2U4A2R624K15QhHYovKKM= cloud.google.com/go/errorreporting v0.3.1 h1:E/gLk+rL7u5JZB9oq72iL1bnhVlLrnfslrgcptjJEUE= cloud.google.com/go/errorreporting v0.3.1/go.mod h1:6xVQXU1UuntfAf+bVkFk6nld41+CPyF2NSPCyXE3Ztk= -cloud.google.com/go/essentialcontacts v1.6.2/go.mod h1:T2tB6tX+TRak7i88Fb2N9Ok3PvY3UNbUsMag9/BARh4= -cloud.google.com/go/essentialcontacts v1.6.3/go.mod h1:yiPCD7f2TkP82oJEFXFTou8Jl8L6LBRPeBEkTaO0Ggo= -cloud.google.com/go/essentialcontacts v1.6.4/go.mod h1:iju5Vy3d9tJUg0PYMd1nHhjV7xoCXaOAVabrwLaPBEM= -cloud.google.com/go/essentialcontacts v1.6.5/go.mod h1:jjYbPzw0x+yglXC890l6ECJWdYeZ5dlYACTFL0U/VuM= -cloud.google.com/go/essentialcontacts v1.6.6/go.mod h1:XbqHJGaiH0v2UvtuucfOzFXN+rpL/aU5BCZLn4DYl1Q= cloud.google.com/go/essentialcontacts v1.7.1 h1:qeZAOxqWFfD7sDd1vKYaNhjGh1eckkCkSJyx/OC5egE= cloud.google.com/go/essentialcontacts v1.7.1/go.mod h1:F/MMWNLRW7b42WwWklOsnx4zrMOWDYWqWykBf1jXKPY= -cloud.google.com/go/eventarc v1.12.1/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.0/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.1/go.mod h1:EqBxmGHFrruIara4FUQ3RHlgfCn7yo1HYsu2Hpt/C3Y= -cloud.google.com/go/eventarc v1.13.2/go.mod h1:X9A80ShVu19fb4e5sc/OLV7mpFUKZMwfJFeeWhcIObM= -cloud.google.com/go/eventarc v1.13.3/go.mod h1:RWH10IAZIRcj1s/vClXkBgMHwh59ts7hSWcqD3kaclg= -cloud.google.com/go/eventarc v1.13.4/go.mod h1:zV5sFVoAa9orc/52Q+OuYUG9xL2IIZTbbuTHC6JSY8s= cloud.google.com/go/eventarc v1.14.1 h1:Tw1DsE1OO9NZ3LZlAtxsi4otVl5qjQ3Y3QD9dCxtAyo= cloud.google.com/go/eventarc v1.14.1/go.mod h1:NG0YicE+z9MDcmh2u4tlzLDVLRjq5UHZlibyQlPhcxY= -cloud.google.com/go/filestore v1.7.1/go.mod h1:y10jsorq40JJnjR/lQ8AfFbbcGlw3g+Dp8oN7i7FjV4= -cloud.google.com/go/filestore v1.7.2/go.mod h1:TYOlyJs25f/omgj+vY7/tIG/E7BX369triSPzE4LdgE= -cloud.google.com/go/filestore v1.7.3/go.mod h1:Qp8WaEERR3cSkxToxFPHh/b8AACkSut+4qlCjAmKTV0= -cloud.google.com/go/filestore v1.7.4/go.mod h1:S5JCxIbFjeBhWMTfIYH2Jx24J6BqjwpkkPl+nBA5DlI= -cloud.google.com/go/filestore v1.8.0/go.mod h1:S5JCxIbFjeBhWMTfIYH2Jx24J6BqjwpkkPl+nBA5DlI= -cloud.google.com/go/filestore v1.8.1/go.mod h1:MbN9KcaM47DRTIuLfQhJEsjaocVebNtNQhSLhKCF5GM= cloud.google.com/go/filestore v1.9.1 h1:s8DPPSV80FzIB7rduoMJAgknktms9hZGE3+X9KFUlK8= cloud.google.com/go/filestore v1.9.1/go.mod h1:g/FNHBABpxjL1M9nNo0nW6vLYIMVlyOKhBKtYGgcKUI= -cloud.google.com/go/firestore v1.11.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= -cloud.google.com/go/firestore v1.12.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= -cloud.google.com/go/firestore v1.13.0/go.mod h1:QojqqOh8IntInDUSTAh0c8ZsPYAr68Ma8c5DWOy8xb8= -cloud.google.com/go/firestore v1.14.0/go.mod h1:96MVaHLsEhbvkBEdZgfN+AS/GIkco1LRpH9Xp9YZfzQ= cloud.google.com/go/firestore v1.17.0 h1:iEd1LBbkDZTFsLw3sTH50eyg4qe8eoG6CjocmEXO9aQ= cloud.google.com/go/firestore v1.17.0/go.mod h1:69uPx1papBsY8ZETooc71fOhoKkD70Q1DwMrtKuOT/Y= -cloud.google.com/go/functions v1.15.1/go.mod h1:P5yNWUTkyU+LvW/S9O6V+V423VZooALQlqoXdoPz5AE= -cloud.google.com/go/functions v1.15.2/go.mod h1:CHAjtcR6OU4XF2HuiVeriEdELNcnvRZSk1Q8RMqy4lE= -cloud.google.com/go/functions v1.15.3/go.mod h1:r/AMHwBheapkkySEhiZYLDBwVJCdlRwsm4ieJu35/Ug= -cloud.google.com/go/functions v1.15.4/go.mod h1:CAsTc3VlRMVvx+XqXxKqVevguqJpnVip4DdonFsX28I= -cloud.google.com/go/functions v1.16.0/go.mod h1:nbNpfAG7SG7Duw/o1iZ6ohvL7mc6MapWQVpqtM29n8k= cloud.google.com/go/functions v1.19.1 h1:eWjTZohtJX/9rckZYXaYVViGi06JkNJRKvm0aO+ce+g= cloud.google.com/go/functions v1.19.1/go.mod h1:18RszySpwRg6aH5UTTVsRfdCwDooSf/5mvSnU7NAk4A= cloud.google.com/go/gaming v1.9.0 h1:7vEhFnZmd931Mo7sZ6pJy7uQPDxF7m7v8xtBheG08tc= -cloud.google.com/go/gaming v1.10.1 h1:5qZmZEWzMf8GEFgm9NeC3bjFRpt7x4S6U7oLbxaf7N8= -cloud.google.com/go/gaming v1.10.1/go.mod h1:XQQvtfP8Rb9Rxnxm5wFVpAp9zCQkJi2bLIb7iHGwB3s= -cloud.google.com/go/gkebackup v1.3.0/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= -cloud.google.com/go/gkebackup v1.3.1/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= -cloud.google.com/go/gkebackup v1.3.2/go.mod h1:OMZbXzEJloyXMC7gqdSB+EOEQ1AKcpGYvO3s1ec5ixk= -cloud.google.com/go/gkebackup v1.3.3/go.mod h1:eMk7/wVV5P22KBakhQnJxWSVftL1p4VBFLpv0kIft7I= -cloud.google.com/go/gkebackup v1.3.4/go.mod h1:gLVlbM8h/nHIs09ns1qx3q3eaXcGSELgNu1DWXYz1HI= -cloud.google.com/go/gkebackup v1.3.5/go.mod h1:KJ77KkNN7Wm1LdMopOelV6OodM01pMuK2/5Zt1t4Tvc= cloud.google.com/go/gkebackup v1.6.1 h1:bV1go067LF5XaobFXXvgW2rsuvR974ajirDjD9oXFWg= cloud.google.com/go/gkebackup v1.6.1/go.mod h1:CEnHQCsNBn+cyxcxci0qbAPYe8CkivNEitG/VAZ08ms= -cloud.google.com/go/gkeconnect v0.8.1/go.mod h1:KWiK1g9sDLZqhxB2xEuPV8V9NYzrqTUmQR9shJHpOZw= -cloud.google.com/go/gkeconnect v0.8.2/go.mod h1:6nAVhwchBJYgQCXD2pHBFQNiJNyAd/wyxljpaa6ZPrY= -cloud.google.com/go/gkeconnect v0.8.3/go.mod h1:i9GDTrfzBSUZGCe98qSu1B8YB8qfapT57PenIb820Jo= -cloud.google.com/go/gkeconnect v0.8.4/go.mod h1:84hZz4UMlDCKl8ifVW8layK4WHlMAFeq8vbzjU0yJkw= -cloud.google.com/go/gkeconnect v0.8.5/go.mod h1:LC/rS7+CuJ5fgIbXv8tCD/mdfnlAadTaUufgOkmijuk= cloud.google.com/go/gkeconnect v0.11.1 h1:X7UpDP2Qg8JfaQ6vsJeFsTo4NcrGprk9Tg4Pf7MK8Qg= cloud.google.com/go/gkeconnect v0.11.1/go.mod h1:Vu3UoOI2c0amGyv4dT/EmltzscPH41pzS4AXPqQLej0= -cloud.google.com/go/gkehub v0.14.1/go.mod h1:VEXKIJZ2avzrbd7u+zeMtW00Y8ddk/4V9511C9CQGTY= -cloud.google.com/go/gkehub v0.14.2/go.mod h1:iyjYH23XzAxSdhrbmfoQdePnlMj2EWcvnR+tHdBQsCY= -cloud.google.com/go/gkehub v0.14.3/go.mod h1:jAl6WafkHHW18qgq7kqcrXYzN08hXeK/Va3utN8VKg8= -cloud.google.com/go/gkehub v0.14.4/go.mod h1:Xispfu2MqnnFt8rV/2/3o73SK1snL8s9dYJ9G2oQMfc= -cloud.google.com/go/gkehub v0.14.5/go.mod h1:6bzqxM+a+vEH/h8W8ec4OJl4r36laxTs3A/fMNHJ0wA= cloud.google.com/go/gkehub v0.15.1 h1:VMXUz3q9Vfhe+dtSjb/yqmiDmGbcEUTuXDyk0pj2GyU= cloud.google.com/go/gkehub v0.15.1/go.mod h1:cyUwa9iFQYd/pI7IQYl6A+OF6M8uIbhmJr090v9Z4UU= -cloud.google.com/go/gkemulticloud v0.6.1/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.0/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.1/go.mod h1:AcrGoin6VLKT/fwZEYuqvVominLriQBCKmbjtnbMjG8= -cloud.google.com/go/gkemulticloud v1.0.2/go.mod h1:+ee5VXxKb3H1l4LZAcgWB/rvI16VTNTrInWxDjAGsGo= -cloud.google.com/go/gkemulticloud v1.0.3/go.mod h1:7NpJBN94U6DY1xHIbsDqB2+TFZUfjLUKLjUX8NGLor0= -cloud.google.com/go/gkemulticloud v1.1.0/go.mod h1:7NpJBN94U6DY1xHIbsDqB2+TFZUfjLUKLjUX8NGLor0= -cloud.google.com/go/gkemulticloud v1.1.1/go.mod h1:C+a4vcHlWeEIf45IB5FFR5XGjTeYhF83+AYIpTy4i2Q= cloud.google.com/go/gkemulticloud v1.4.0 h1:t2HXXYrICui+rZXScietjU1YdrQDLXpfqqrTo7zWSYQ= cloud.google.com/go/gkemulticloud v1.4.0/go.mod h1:rg8YOQdRKEtMimsiNCzZUP74bOwImhLRv9wQ0FwBUP4= -cloud.google.com/go/grafeas v0.2.0 h1:CYjC+xzdPvbV65gi6Dr4YowKcmLo045pm18L0DhdELM= -cloud.google.com/go/grafeas v0.3.0/go.mod h1:P7hgN24EyONOTMyeJH6DxG4zD7fwiYa5Q6GUgyFSOU8= -cloud.google.com/go/grafeas v0.3.4 h1:D4x32R/cHX3MTofKwirz015uEdVk4uAxvZkZCZkOrF4= -cloud.google.com/go/grafeas v0.3.4/go.mod h1:A5m316hcG+AulafjAbPKXBO/+I5itU4LOdKO2R/uDIc= -cloud.google.com/go/gsuiteaddons v1.6.1/go.mod h1:CodrdOqRZcLp5WOwejHWYBjZvfY0kOphkAKpF/3qdZY= -cloud.google.com/go/gsuiteaddons v1.6.2/go.mod h1:K65m9XSgs8hTF3X9nNTPi8IQueljSdYo9F+Mi+s4MyU= -cloud.google.com/go/gsuiteaddons v1.6.3/go.mod h1:sCFJkZoMrLZT3JTb8uJqgKPNshH2tfXeCwTFRebTq48= -cloud.google.com/go/gsuiteaddons v1.6.4/go.mod h1:rxtstw7Fx22uLOXBpsvb9DUbC+fiXs7rF4U29KHM/pE= -cloud.google.com/go/gsuiteaddons v1.6.5/go.mod h1:Lo4P2IvO8uZ9W+RaC6s1JVxo42vgy+TX5a6hfBZ0ubs= +cloud.google.com/go/grafeas v0.3.10 h1:D9uP/DjVHq9ZzCekVd+aNvQEHb3Hkwp8ki9FDnhRRJ0= +cloud.google.com/go/grafeas v0.3.10/go.mod h1:Mz/AoXmxNhj74VW0fz5Idc3kMN2VZMi4UT5+UPx5Pq0= cloud.google.com/go/gsuiteaddons v1.7.1 h1:YLh58kzaK+1Q/CHe8Cjp3hf9ZjNdJkQMavjrJUDgi9o= cloud.google.com/go/gsuiteaddons v1.7.1/go.mod h1:SxM63xEPFf0p/plgh4dP82mBSKtp2RWskz5DpVo9jh8= -cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8= -cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= -cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= -cloud.google.com/go/iam v1.1.4/go.mod h1:l/rg8l1AaA+VFMho/HYx2Vv6xinPSLMF8qfhRPIZ0L8= -cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= -cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= -cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE= -cloud.google.com/go/iam v1.2.0/go.mod h1:zITGuWgsLZxd8OwAlX+eMFgZDXzBm7icj1PVTYG766Q= -cloud.google.com/go/iap v1.8.1/go.mod h1:sJCbeqg3mvWLqjZNsI6dfAtbbV1DL2Rl7e1mTyXYREQ= -cloud.google.com/go/iap v1.9.0/go.mod h1:01OFxd1R+NFrg78S+hoPV5PxEzv22HXaNqUUlmNHFuY= -cloud.google.com/go/iap v1.9.1/go.mod h1:SIAkY7cGMLohLSdBR25BuIxO+I4fXJiL06IBL7cy/5Q= -cloud.google.com/go/iap v1.9.2/go.mod h1:GwDTOs047PPSnwRD0Us5FKf4WDRcVvHg1q9WVkKBhdI= -cloud.google.com/go/iap v1.9.3/go.mod h1:DTdutSZBqkkOm2HEOTBzhZxh2mwwxshfD/h3yofAiCw= -cloud.google.com/go/iap v1.9.4/go.mod h1:vO4mSq0xNf/Pu6E5paORLASBwEmphXEjgCFg7aeNu1w= cloud.google.com/go/iap v1.10.1 h1:YF4jmMwEWXYrbfZZz024ozBXnWxUxJHzmkM6ccIzM0A= cloud.google.com/go/iap v1.10.1/go.mod h1:UKetCEzOZ4Zj7l9TSN/wzRNwbgIYzm4VM4bStaQ/tFc= -cloud.google.com/go/ids v1.4.1/go.mod h1:np41ed8YMU8zOgv53MMMoCntLTn2lF+SUzlM+O3u/jw= -cloud.google.com/go/ids v1.4.2/go.mod h1:3vw8DX6YddRu9BncxuzMyWn0g8+ooUjI2gslJ7FH3vk= -cloud.google.com/go/ids v1.4.3/go.mod h1:9CXPqI3GedjmkjbMWCUhMZ2P2N7TUMzAkVXYEH2orYU= -cloud.google.com/go/ids v1.4.4/go.mod h1:z+WUc2eEl6S/1aZWzwtVNWoSZslgzPxAboS0lZX0HjI= -cloud.google.com/go/ids v1.4.5/go.mod h1:p0ZnyzjMWxww6d2DvMGnFwCsSxDJM666Iir1bK1UuBo= cloud.google.com/go/ids v1.5.1 h1:UkHpZnlW46WulDVNtzKN+SEntZoOoHoG/Ob1GtuVCGQ= cloud.google.com/go/ids v1.5.1/go.mod h1:d/9jTtY506mTxw/nHH3UN4TFo80jhAX+tESwzj42yFo= -cloud.google.com/go/iot v1.7.1/go.mod h1:46Mgw7ev1k9KqK1ao0ayW9h0lI+3hxeanz+L1zmbbbk= -cloud.google.com/go/iot v1.7.2/go.mod h1:q+0P5zr1wRFpw7/MOgDXrG/HVA+l+cSwdObffkrpnSg= -cloud.google.com/go/iot v1.7.3/go.mod h1:t8itFchkol4VgNbHnIq9lXoOOtHNR3uAACQMYbN9N4I= -cloud.google.com/go/iot v1.7.4/go.mod h1:3TWqDVvsddYBG++nHSZmluoCAVGr1hAcabbWZNKEZLk= -cloud.google.com/go/iot v1.7.5/go.mod h1:nq3/sqTz3HGaWJi1xNiX7F41ThOzpud67vwk0YsSsqs= cloud.google.com/go/iot v1.8.1 h1:PySjOJ2Nni1IDk0LqcNhUCKOGe0yPP4rM/Nc5yA/cjI= cloud.google.com/go/iot v1.8.1/go.mod h1:FNceQ9/EGvbE2az7RGoGPY0aqrsyJO3/LqAL0h83fZw= -cloud.google.com/go/kms v1.11.0/go.mod h1:hwdiYC0xjnWsKQQCQQmIQnS9asjYVSK6jtXm+zFqXLM= -cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= -cloud.google.com/go/kms v1.15.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= -cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w= -cloud.google.com/go/kms v1.15.3/go.mod h1:AJdXqHxS2GlPyduM99s9iGqi2nwbviBbhV/hdmt4iOQ= -cloud.google.com/go/kms v1.15.4/go.mod h1:L3Sdj6QTHK8dfwK5D1JLsAyELsNMnd3tAIwGS4ltKpc= -cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= -cloud.google.com/go/kms v1.15.6/go.mod h1:yF75jttnIdHfGBoE51AKsD/Yqf+/jICzB9v1s1acsms= -cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI= -cloud.google.com/go/language v1.10.1/go.mod h1:CPp94nsdVNiQEt1CNjF5WkTcisLiHPyIbMhvR8H2AW0= -cloud.google.com/go/language v1.11.0/go.mod h1:uDx+pFDdAKTY8ehpWbiXyQdz8tDSYLJbQcXsCkjYyvQ= -cloud.google.com/go/language v1.11.1/go.mod h1:Xyid9MG9WOX3utvDbpX7j3tXDmmDooMyMDqgUVpH17U= -cloud.google.com/go/language v1.12.1/go.mod h1:zQhalE2QlQIxbKIZt54IASBzmZpN/aDASea5zl1l+J4= -cloud.google.com/go/language v1.12.2/go.mod h1:9idWapzr/JKXBBQ4lWqVX/hcadxB194ry20m/bTrhWc= -cloud.google.com/go/language v1.12.3/go.mod h1:evFX9wECX6mksEva8RbRnr/4wi/vKGYnAJrTRXU8+f8= cloud.google.com/go/language v1.14.1 h1:lyBks2W2k7bVPvfEECH08eMOP3Vd7zkHCATt/Vy0sLM= cloud.google.com/go/language v1.14.1/go.mod h1:WaAL5ZdLLBjiorXl/8vqgb6/Fyt2qijl96c1ZP/vdc8= -cloud.google.com/go/lifesciences v0.9.1/go.mod h1:hACAOd1fFbCGLr/+weUKRAJas82Y4vrL3O5326N//Wc= -cloud.google.com/go/lifesciences v0.9.2/go.mod h1:QHEOO4tDzcSAzeJg7s2qwnLM2ji8IRpQl4p6m5Z9yTA= -cloud.google.com/go/lifesciences v0.9.3/go.mod h1:gNGBOJV80IWZdkd+xz4GQj4mbqaz737SCLHn2aRhQKM= -cloud.google.com/go/lifesciences v0.9.4/go.mod h1:bhm64duKhMi7s9jR9WYJYvjAFJwRqNj+Nia7hF0Z7JA= -cloud.google.com/go/lifesciences v0.9.5/go.mod h1:OdBm0n7C0Osh5yZB7j9BXyrMnTRGBJIZonUMxo5CzPw= cloud.google.com/go/lifesciences v0.10.1 h1:sGTR+IW9I85VhP789GMHNYOyCo7dkmvWRYh0uOfmWdo= cloud.google.com/go/lifesciences v0.10.1/go.mod h1:5D6va5/Gq3gtJPKSsE6vXayAigfOXK2eWLTdFUOTCDs= -cloud.google.com/go/logging v1.8.1/go.mod h1:TJjR+SimHwuC8MZ9cjByQulAMgni+RkXeI3wwctHJEI= -cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE= cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk= cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM= -cloud.google.com/go/longrunning v0.4.2/go.mod h1:OHrnaYyLUV6oqwh0xiS7e5sLQhP1m0QU9R+WhGDMgIQ= -cloud.google.com/go/longrunning v0.5.0/go.mod h1:0JNuqRShmscVAhIACGtskSAWtqtOoPkwP0YF1oVEchc= -cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= -cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= -cloud.google.com/go/longrunning v0.5.3/go.mod h1:y/0ga59EYu58J6SHmmQOvekvND2qODbu8ywBBW7EK7Y= -cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= -cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= -cloud.google.com/go/longrunning v0.5.6/go.mod h1:vUaDrWYOMKRuhiv6JBnn49YxCPz2Ayn9GqyjaBT8/mA= -cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng= -cloud.google.com/go/longrunning v0.6.0/go.mod h1:uHzSZqW89h7/pasCWNYdUpwGz3PcVWhrWupreVPYLts= -cloud.google.com/go/managedidentities v1.6.1/go.mod h1:h/irGhTN2SkZ64F43tfGPMbHnypMbu4RB3yl8YcuEak= -cloud.google.com/go/managedidentities v1.6.2/go.mod h1:5c2VG66eCa0WIq6IylRk3TBW83l161zkFvCj28X7jn8= -cloud.google.com/go/managedidentities v1.6.3/go.mod h1:tewiat9WLyFN0Fi7q1fDD5+0N4VUoL0SCX0OTCthZq4= -cloud.google.com/go/managedidentities v1.6.4/go.mod h1:WgyaECfHmF00t/1Uk8Oun3CQ2PGUtjc3e9Alh79wyiM= -cloud.google.com/go/managedidentities v1.6.5/go.mod h1:fkFI2PwwyRQbjLxlm5bQ8SjtObFMW3ChBGNqaMcgZjI= cloud.google.com/go/managedidentities v1.7.1 h1:9hC4E7JnWn/jSUls022Sj9ri+vriGnLzvDXo0cs1zcA= cloud.google.com/go/managedidentities v1.7.1/go.mod h1:iK4qqIBOOfePt5cJR/Uo3+uol6oAVIbbG7MGy917cYM= -cloud.google.com/go/maps v1.3.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= -cloud.google.com/go/maps v1.4.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= -cloud.google.com/go/maps v1.4.1/go.mod h1:BxSa0BnW1g2U2gNdbq5zikLlHUuHW0GFWh7sgML2kIY= -cloud.google.com/go/maps v1.5.1/go.mod h1:NPMZw1LJwQZYCfz4y+EIw+SI+24A4bpdFJqdKVr0lt4= -cloud.google.com/go/maps v1.6.1/go.mod h1:4+buOHhYXFBp58Zj/K+Lc1rCmJssxxF4pJ5CJnhdz18= -cloud.google.com/go/maps v1.6.2/go.mod h1:4+buOHhYXFBp58Zj/K+Lc1rCmJssxxF4pJ5CJnhdz18= -cloud.google.com/go/maps v1.6.3/go.mod h1:VGAn809ADswi1ASofL5lveOHPnE6Rk/SFTTBx1yuOLw= -cloud.google.com/go/maps v1.6.4/go.mod h1:rhjqRy8NWmDJ53saCfsXQ0LKwBHfi6OSh5wkq6BaMhI= cloud.google.com/go/maps v1.14.0 h1:bLT2nvuOm4ye6YRgIJQ0L9zbKcbBj+TCg8k2g3c2Qlk= cloud.google.com/go/maps v1.14.0/go.mod h1:UepOes9un0UP7i8JBiaqgh8jqUaZAHVRXCYjrVlhSC8= -cloud.google.com/go/mediatranslation v0.8.1/go.mod h1:L/7hBdEYbYHQJhX2sldtTO5SZZ1C1vkapubj0T2aGig= -cloud.google.com/go/mediatranslation v0.8.2/go.mod h1:c9pUaDRLkgHRx3irYE5ZC8tfXGrMYwNZdmDqKMSfFp8= -cloud.google.com/go/mediatranslation v0.8.3/go.mod h1:F9OnXTy336rteOEywtY7FOqCk+J43o2RF638hkOQl4Y= -cloud.google.com/go/mediatranslation v0.8.4/go.mod h1:9WstgtNVAdN53m6TQa5GjIjLqKQPXe74hwSCxUP6nj4= -cloud.google.com/go/mediatranslation v0.8.5/go.mod h1:y7kTHYIPCIfgyLbKncgqouXJtLsU+26hZhHEEy80fSs= cloud.google.com/go/mediatranslation v0.9.1 h1:7X1cA4TWO0+r1RT0JTT0RE+SyO41eoFUmBDw17Oi9T8= cloud.google.com/go/mediatranslation v0.9.1/go.mod h1:vQH1amULNhSGryBjbjLb37g54rxrOwVxywS8WvUCsIU= -cloud.google.com/go/memcache v1.10.1/go.mod h1:47YRQIarv4I3QS5+hoETgKO40InqzLP6kpNLvyXuyaA= -cloud.google.com/go/memcache v1.10.2/go.mod h1:f9ZzJHLBrmd4BkguIAa/l/Vle6uTHzHokdnzSWOdQ6A= -cloud.google.com/go/memcache v1.10.3/go.mod h1:6z89A41MT2DVAW0P4iIRdu5cmRTsbsFn4cyiIx8gbwo= -cloud.google.com/go/memcache v1.10.4/go.mod h1:v/d8PuC8d1gD6Yn5+I3INzLR01IDn0N4Ym56RgikSI0= -cloud.google.com/go/memcache v1.10.5/go.mod h1:/FcblbNd0FdMsx4natdj+2GWzTq+cjZvMa1I+9QsuMA= cloud.google.com/go/memcache v1.11.1 h1:2FGuyd3WY7buNDAkMBdmeIOheNWA3gwaXrttLrEdabI= cloud.google.com/go/memcache v1.11.1/go.mod h1:3zF+dEqmEmElHuO4NtHiShekQY5okQtssjPBv7jpmZ8= -cloud.google.com/go/metastore v1.11.1/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/metastore v1.12.0/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/metastore v1.13.0/go.mod h1:URDhpG6XLeh5K+Glq0NOt74OfrPKTwS62gEPZzb5SOk= -cloud.google.com/go/metastore v1.13.1/go.mod h1:IbF62JLxuZmhItCppcIfzBBfUFq0DIB9HPDoLgWrVOU= -cloud.google.com/go/metastore v1.13.2/go.mod h1:KS59dD+unBji/kFebVp8XU/quNSyo8b6N6tPGspKszA= -cloud.google.com/go/metastore v1.13.3/go.mod h1:K+wdjXdtkdk7AQg4+sXS8bRrQa9gcOr+foOMF2tqINE= -cloud.google.com/go/metastore v1.13.4/go.mod h1:FMv9bvPInEfX9Ac1cVcRXp8EBBQnBcqH6gz3KvJ9BAE= cloud.google.com/go/metastore v1.14.1 h1:kGx+IUSSYCVn8LisCT4fpxCC9rauEVonzi7RlygdqWY= cloud.google.com/go/metastore v1.14.1/go.mod h1:WDvsAcbQLl9M4xL+eIpbKogH7aEaPWMhO9aRBcFOnJE= -cloud.google.com/go/monitoring v1.15.1/go.mod h1:lADlSAlFdbqQuwwpaImhsJXu1QSdd3ojypXrFSMr2rM= -cloud.google.com/go/monitoring v1.16.0/go.mod h1:Ptp15HgAyM1fNICAojDMoNc/wUmn67mLHQfyqbw+poY= -cloud.google.com/go/monitoring v1.16.1/go.mod h1:6HsxddR+3y9j+o/cMJH6q/KJ/CBTvM/38L/1m7bTRJ4= -cloud.google.com/go/monitoring v1.16.2/go.mod h1:B44KGwi4ZCF8Rk/5n+FWeispDXoKSk9oss2QNlXJBgc= -cloud.google.com/go/monitoring v1.16.3/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= -cloud.google.com/go/monitoring v1.17.0/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= -cloud.google.com/go/monitoring v1.17.1/go.mod h1:SJzPMakCF0GHOuKEH/r4hxVKF04zl+cRPQyc3d/fqII= -cloud.google.com/go/monitoring v1.18.0/go.mod h1:c92vVBCeq/OB4Ioyo+NbN2U7tlg5ZH41PZcdvfc+Lcg= cloud.google.com/go/monitoring v1.21.1 h1:zWtbIoBMnU5LP9A/fz8LmWMGHpk4skdfeiaa66QdFGc= cloud.google.com/go/monitoring v1.21.1/go.mod h1:Rj++LKrlht9uBi8+Eb530dIrzG/cU/lB8mt+lbeFK1c= -cloud.google.com/go/networkconnectivity v1.12.1/go.mod h1:PelxSWYM7Sh9/guf8CFhi6vIqf19Ir/sbfZRUwXh92E= -cloud.google.com/go/networkconnectivity v1.13.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= -cloud.google.com/go/networkconnectivity v1.14.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= -cloud.google.com/go/networkconnectivity v1.14.1/go.mod h1:LyGPXR742uQcDxZ/wv4EI0Vu5N6NKJ77ZYVnDe69Zug= -cloud.google.com/go/networkconnectivity v1.14.2/go.mod h1:5UFlwIisZylSkGG1AdwK/WZUaoz12PKu6wODwIbFzJo= -cloud.google.com/go/networkconnectivity v1.14.3/go.mod h1:4aoeFdrJpYEXNvrnfyD5kIzs8YtHg945Og4koAjHQek= -cloud.google.com/go/networkconnectivity v1.14.4/go.mod h1:PU12q++/IMnDJAB+3r+tJtuCXCfwfN+C6Niyj6ji1Po= cloud.google.com/go/networkconnectivity v1.15.1 h1:EizN+cFGHzRAyiFTK8jT1PqTo+cSnbc2IGh6OmllS7Y= cloud.google.com/go/networkconnectivity v1.15.1/go.mod h1:tYAcT4Ahvq+BiePXL/slYipf/8FF0oNJw3MqFhBnSPI= -cloud.google.com/go/networkmanagement v1.8.0/go.mod h1:Ho/BUGmtyEqrttTgWEe7m+8vDdK74ibQc+Be0q7Fof0= -cloud.google.com/go/networkmanagement v1.9.0/go.mod h1:UTUaEU9YwbCAhhz3jEOHr+2/K/MrBk2XxOLS89LQzFw= -cloud.google.com/go/networkmanagement v1.9.1/go.mod h1:CCSYgrQQvW73EJawO2QamemYcOb57LvrDdDU51F0mcI= -cloud.google.com/go/networkmanagement v1.9.2/go.mod h1:iDGvGzAoYRghhp4j2Cji7sF899GnfGQcQRQwgVOWnDw= -cloud.google.com/go/networkmanagement v1.9.3/go.mod h1:y7WMO1bRLaP5h3Obm4tey+NquUvB93Co1oh4wpL+XcU= -cloud.google.com/go/networkmanagement v1.9.4/go.mod h1:daWJAl0KTFytFL7ar33I6R/oNBH8eEOX/rBNHrC/8TA= cloud.google.com/go/networkmanagement v1.14.1 h1:0x3hVI6xbp3N/choffKPHMSxbzaPdHSD92cBElebXEk= cloud.google.com/go/networkmanagement v1.14.1/go.mod h1:3Ds8FZ3ZHjTVEedsBoZi9ef9haTE14iS6swTSqM39SI= -cloud.google.com/go/networksecurity v0.9.1/go.mod h1:MCMdxOKQ30wsBI1eI659f9kEp4wuuAueoC9AJKSPWZQ= -cloud.google.com/go/networksecurity v0.9.2/go.mod h1:jG0SeAttWzPMUILEHDUvFYdQTl8L/E/KC8iZDj85lEI= -cloud.google.com/go/networksecurity v0.9.3/go.mod h1:l+C0ynM6P+KV9YjOnx+kk5IZqMSLccdBqW6GUoF4p/0= -cloud.google.com/go/networksecurity v0.9.4/go.mod h1:E9CeMZ2zDsNBkr8axKSYm8XyTqNhiCHf1JO/Vb8mD1w= -cloud.google.com/go/networksecurity v0.9.5/go.mod h1:KNkjH/RsylSGyyZ8wXpue8xpCEK+bTtvof8SBfIhMG8= cloud.google.com/go/networksecurity v0.10.1 h1:dHN1la6xnta3E4QtWGqtc8ZAPKIZH5m8UQceIIuXZIs= cloud.google.com/go/networksecurity v0.10.1/go.mod h1:tatO1hYJ9nNChLHOFdsjex5FeqZBlPQgKdKOex7REpU= -cloud.google.com/go/notebooks v1.9.1/go.mod h1:zqG9/gk05JrzgBt4ghLzEepPHNwE5jgPcHZRKhlC1A8= -cloud.google.com/go/notebooks v1.10.0/go.mod h1:SOPYMZnttHxqot0SGSFSkRrwE29eqnKPBJFqgWmiK2k= -cloud.google.com/go/notebooks v1.10.1/go.mod h1:5PdJc2SgAybE76kFQCWrTfJolCOUQXF97e+gteUUA6A= -cloud.google.com/go/notebooks v1.11.1/go.mod h1:V2Zkv8wX9kDCGRJqYoI+bQAaoVeE5kSiz4yYHd2yJwQ= -cloud.google.com/go/notebooks v1.11.2/go.mod h1:z0tlHI/lREXC8BS2mIsUeR3agM1AkgLiS+Isov3SS70= -cloud.google.com/go/notebooks v1.11.3/go.mod h1:0wQyI2dQC3AZyQqWnRsp+yA+kY4gC7ZIVP4Qg3AQcgo= cloud.google.com/go/notebooks v1.12.1 h1:0g61C2qdWcq2p8OFH3NiLzyneS1LFfsveC5+MnpM4p8= cloud.google.com/go/notebooks v1.12.1/go.mod h1:RJCyRkLjj8UnvLEKaDl9S6//xUCa+r+d/AsxZnYBl50= -cloud.google.com/go/optimization v1.4.1/go.mod h1:j64vZQP7h9bO49m2rVaTVoNM0vEBEN5eKPUPbZyXOrk= -cloud.google.com/go/optimization v1.5.0/go.mod h1:evo1OvTxeBRBu6ydPlrIRizKY/LJKo/drDMMRKqGEUU= -cloud.google.com/go/optimization v1.5.1/go.mod h1:NC0gnUD5MWVAF7XLdoYVPmYYVth93Q6BUzqAq3ZwtV8= -cloud.google.com/go/optimization v1.6.1/go.mod h1:hH2RYPTTM9e9zOiTaYPTiGPcGdNZVnBSBxjIAJzUkqo= -cloud.google.com/go/optimization v1.6.2/go.mod h1:mWNZ7B9/EyMCcwNl1frUGEuY6CPijSkz88Fz2vwKPOY= -cloud.google.com/go/optimization v1.6.3/go.mod h1:8ve3svp3W6NFcAEFr4SfJxrldzhUl4VMUJmhrqVKtYA= cloud.google.com/go/optimization v1.7.1 h1:E3/1qRZvGxqQpapaac/EKuzusxUauXLnpirWWXXzP5k= cloud.google.com/go/optimization v1.7.1/go.mod h1:s2AjwwQEv6uExFmgS4Bf1gidI07w7jCzvvs8exqR1yk= -cloud.google.com/go/orchestration v1.8.1/go.mod h1:4sluRF3wgbYVRqz7zJ1/EUNc90TTprliq9477fGobD8= -cloud.google.com/go/orchestration v1.8.2/go.mod h1:T1cP+6WyTmh6LSZzeUhvGf0uZVmJyTx7t8z7Vg87+A0= -cloud.google.com/go/orchestration v1.8.3/go.mod h1:xhgWAYqlbYjlz2ftbFghdyqENYW+JXuhBx9KsjMoGHs= -cloud.google.com/go/orchestration v1.8.4/go.mod h1:d0lywZSVYtIoSZXb0iFjv9SaL13PGyVOKDxqGxEf/qI= -cloud.google.com/go/orchestration v1.8.5/go.mod h1:C1J7HesE96Ba8/hZ71ISTV2UAat0bwN+pi85ky38Yq8= cloud.google.com/go/orchestration v1.11.0 h1:yyi0kM47UZaJ3EEFYsBwfrkvqyPmvHwsoc3asxDmLuo= cloud.google.com/go/orchestration v1.11.0/go.mod h1:s3L89jinQaUHclqgWYw8JhBbzGSidVt5rVBxGrXeheI= -cloud.google.com/go/orgpolicy v1.11.0/go.mod h1:2RK748+FtVvnfuynxBzdnyu7sygtoZa1za/0ZfpOs1M= -cloud.google.com/go/orgpolicy v1.11.1/go.mod h1:8+E3jQcpZJQliP+zaFfayC2Pg5bmhuLK755wKhIIUCE= -cloud.google.com/go/orgpolicy v1.11.2/go.mod h1:biRDpNwfyytYnmCRWZWxrKF22Nkz9eNVj9zyaBdpm1o= -cloud.google.com/go/orgpolicy v1.11.3/go.mod h1:oKAtJ/gkMjum5icv2aujkP4CxROxPXsBbYGCDbPO8MM= -cloud.google.com/go/orgpolicy v1.11.4/go.mod h1:0+aNV/nrfoTQ4Mytv+Aw+stBDBjNf4d8fYRA9herfJI= -cloud.google.com/go/orgpolicy v1.12.0/go.mod h1:0+aNV/nrfoTQ4Mytv+Aw+stBDBjNf4d8fYRA9herfJI= -cloud.google.com/go/orgpolicy v1.12.1/go.mod h1:aibX78RDl5pcK3jA8ysDQCFkVxLj3aOQqrbBaUL2V5I= cloud.google.com/go/orgpolicy v1.14.0 h1:UuLmi1+94lIS3tCoeuinuwx4oxdx58nECiAvfwCW0SM= cloud.google.com/go/orgpolicy v1.14.0/go.mod h1:S6Pveh1JOxpSbs6+2ToJG7h3HwqC6Uf1YQ6JYG7wdM8= -cloud.google.com/go/osconfig v1.12.0/go.mod h1:8f/PaYzoS3JMVfdfTubkowZYGmAhUCjjwnjqWI7NVBc= -cloud.google.com/go/osconfig v1.12.1/go.mod h1:4CjBxND0gswz2gfYRCUoUzCm9zCABp91EeTtWXyz0tE= -cloud.google.com/go/osconfig v1.12.2/go.mod h1:eh9GPaMZpI6mEJEuhEjUJmaxvQ3gav+fFEJon1Y8Iw0= -cloud.google.com/go/osconfig v1.12.3/go.mod h1:L/fPS8LL6bEYUi1au832WtMnPeQNT94Zo3FwwV1/xGM= -cloud.google.com/go/osconfig v1.12.4/go.mod h1:B1qEwJ/jzqSRslvdOCI8Kdnp0gSng0xW4LOnIebQomA= -cloud.google.com/go/osconfig v1.12.5/go.mod h1:D9QFdxzfjgw3h/+ZaAb5NypM8bhOMqBzgmbhzWViiW8= cloud.google.com/go/osconfig v1.14.1 h1:67ISL0vZVfq0se+1cPRMYgwTjsES2k9vmSmn8ZS0O5g= cloud.google.com/go/osconfig v1.14.1/go.mod h1:Rk62nyQscgy8x4bICaTn0iWiip5EpwEfG2UCBa2TP/s= -cloud.google.com/go/oslogin v1.10.1/go.mod h1:x692z7yAue5nE7CsSnoG0aaMbNoRJRXO4sn73R+ZqAs= -cloud.google.com/go/oslogin v1.11.0/go.mod h1:8GMTJs4X2nOAUVJiPGqIWVcDaF0eniEto3xlOxaboXE= -cloud.google.com/go/oslogin v1.11.1/go.mod h1:OhD2icArCVNUxKqtK0mcSmKL7lgr0LVlQz+v9s1ujTg= -cloud.google.com/go/oslogin v1.12.1/go.mod h1:VfwTeFJGbnakxAY236eN8fsnglLiVXndlbcNomY4iZU= -cloud.google.com/go/oslogin v1.12.2/go.mod h1:CQ3V8Jvw4Qo4WRhNPF0o+HAM4DiLuE27Ul9CX9g2QdY= -cloud.google.com/go/oslogin v1.13.0/go.mod h1:xPJqLwpTZ90LSE5IL1/svko+6c5avZLluiyylMb/sRA= -cloud.google.com/go/oslogin v1.13.1/go.mod h1:vS8Sr/jR7QvPWpCjNqy6LYZr5Zs1e8ZGW/KPn9gmhws= cloud.google.com/go/oslogin v1.14.1 h1:HPPg7FWPwt7pKrbl+8VFI9UuJTbVrG2rSMHl4HkDAG4= cloud.google.com/go/oslogin v1.14.1/go.mod h1:mM/isJYnohyD3EfM12Fhy8uye46gxA1WjHRCwbkmlVw= -cloud.google.com/go/phishingprotection v0.8.1/go.mod h1:AxonW7GovcA8qdEk13NfHq9hNx5KPtfxXNeUxTDxB6I= -cloud.google.com/go/phishingprotection v0.8.2/go.mod h1:LhJ91uyVHEYKSKcMGhOa14zMMWfbEdxG032oT6ECbC8= -cloud.google.com/go/phishingprotection v0.8.3/go.mod h1:3B01yO7T2Ra/TMojifn8EoGd4G9jts/6cIO0DgDY9J8= -cloud.google.com/go/phishingprotection v0.8.4/go.mod h1:6b3kNPAc2AQ6jZfFHioZKg9MQNybDg4ixFd4RPZZ2nE= -cloud.google.com/go/phishingprotection v0.8.5/go.mod h1:g1smd68F7mF1hgQPuYn3z8HDbNre8L6Z0b7XMYFmX7I= cloud.google.com/go/phishingprotection v0.9.1 h1:oUEGd4dttG5gIUmICdCh8A1U9iVQiw0TGwvYIGQ2I7U= cloud.google.com/go/phishingprotection v0.9.1/go.mod h1:LRiflQnCpYKCMhsmhNB3hDbW+AzQIojXYr6q5+5eRQk= -cloud.google.com/go/policytroubleshooter v1.7.1/go.mod h1:0NaT5v3Ag1M7U5r0GfDCpUFkWd9YqpubBWsQlhanRv0= -cloud.google.com/go/policytroubleshooter v1.8.0/go.mod h1:tmn5Ir5EToWe384EuboTcVQT7nTag2+DuH3uHmKd1HU= -cloud.google.com/go/policytroubleshooter v1.9.0/go.mod h1:+E2Lga7TycpeSTj2FsH4oXxTnrbHJGRlKhVZBLGgU64= -cloud.google.com/go/policytroubleshooter v1.9.1/go.mod h1:MYI8i0bCrL8cW+VHN1PoiBTyNZTstCg2WUw2eVC4c4U= -cloud.google.com/go/policytroubleshooter v1.10.1/go.mod h1:5C0rhT3TDZVxAu8813bwmTvd57Phbl8mr9F4ipOsxEs= -cloud.google.com/go/policytroubleshooter v1.10.2/go.mod h1:m4uF3f6LseVEnMV6nknlN2vYGRb+75ylQwJdnOXfnv0= -cloud.google.com/go/policytroubleshooter v1.10.3/go.mod h1:+ZqG3agHT7WPb4EBIRqUv4OyIwRTZvsVDHZ8GlZaoxk= cloud.google.com/go/policytroubleshooter v1.11.1 h1:/b3wruB/KvmCpy9Jfducc8TQmM3bsoPaeCs5z7TRodA= cloud.google.com/go/policytroubleshooter v1.11.1/go.mod h1:9nJIpgQ2vloJbB8y1JkPL5vxtaSdJnJYPCUvt6PpfRs= -cloud.google.com/go/privatecatalog v0.9.1/go.mod h1:0XlDXW2unJXdf9zFz968Hp35gl/bhF4twwpXZAW50JA= -cloud.google.com/go/privatecatalog v0.9.2/go.mod h1:RMA4ATa8IXfzvjrhhK8J6H4wwcztab+oZph3c6WmtFc= -cloud.google.com/go/privatecatalog v0.9.3/go.mod h1:K5pn2GrVmOPjXz3T26mzwXLcKivfIJ9R5N79AFCF9UE= -cloud.google.com/go/privatecatalog v0.9.4/go.mod h1:SOjm93f+5hp/U3PqMZAHTtBtluqLygrDrVO8X8tYtG0= -cloud.google.com/go/privatecatalog v0.9.5/go.mod h1:fVWeBOVe7uj2n3kWRGlUQqR/pOd450J9yZoOECcQqJk= cloud.google.com/go/privatecatalog v0.10.1 h1:Ew51FHLLQsUYUDJY57eMB/mVUOoWLIji957MRw4kumw= cloud.google.com/go/privatecatalog v0.10.1/go.mod h1:mFmn5bjE9J8MEjQuu1fOc4AxOP2MoEwDLMJk04xqQCQ= -cloud.google.com/go/pubsub v1.32.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsub v1.34.0/go.mod h1:alj4l4rBg+N3YTFDDC+/YyFTs6JAjam2QfYsddcAW4c= -cloud.google.com/go/pubsub v1.36.1/go.mod h1:iYjCa9EzWOoBiTdd4ps7QoMtMln5NwaZQpK1hbRfBDE= cloud.google.com/go/pubsub v1.44.0 h1:pLaMJVDTlnUDIKT5L0k53YyLszfBbGoUBo/IqDK/fEI= cloud.google.com/go/pubsub v1.44.0/go.mod h1:BD4a/kmE8OePyHoa1qAHEw1rMzXX+Pc8Se54T/8mc3I= -cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0= cloud.google.com/go/pubsublite v1.8.2 h1:jLQozsEVr+c6tOU13vDugtnaBSUy/PD5zK6mhm+uF1Y= cloud.google.com/go/pubsublite v1.8.2/go.mod h1:4r8GSa9NznExjuLPEJlF1VjOPOpgf3IT6k8x/YgaOPI= cloud.google.com/go/recaptchaenterprise v1.3.1 h1:u6EznTGzIdsyOsvm+Xkw0aSuKFXQlyjGE9a4exk6iNQ= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.2/go.mod h1:kR0KjsJS7Jt1YSyWFkseQ756D45kaYNTlDPPaRAvDBU= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.0/go.mod h1:QuE8EdU9dEnesG8/kG3XuJyNsjEqMlMzg3v3scCJ46c= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.1/go.mod h1:JZYZJOeZjgSSTGP4uz7NlQ4/d1w5hGmksVgM0lbEij0= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.2/go.mod h1:kpaDBOpkwD4G0GVMzG1W6Doy1tFFC97XAV3xy+Rd/pw= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.3/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.4/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= -cloud.google.com/go/recaptchaenterprise/v2 v2.9.0/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= -cloud.google.com/go/recaptchaenterprise/v2 v2.9.2/go.mod h1:trwwGkfhCmp05Ll5MSJPXY7yvnO0p4v3orGANAFHAuU= cloud.google.com/go/recaptchaenterprise/v2 v2.17.2 h1:tHFLYu+8w0jjjGf63D4qgVEKS9R3lw4XP4Q1P4df2g8= cloud.google.com/go/recaptchaenterprise/v2 v2.17.2/go.mod h1:iigNZOnUpf++xlm8RdMZJTX/PihYVMrHidRLjHuekec= -cloud.google.com/go/recommendationengine v0.8.1/go.mod h1:MrZihWwtFYWDzE6Hz5nKcNz3gLizXVIDI/o3G1DLcrE= -cloud.google.com/go/recommendationengine v0.8.2/go.mod h1:QIybYHPK58qir9CV2ix/re/M//Ty10OxjnnhWdaKS1Y= -cloud.google.com/go/recommendationengine v0.8.3/go.mod h1:m3b0RZV02BnODE9FeSvGv1qibFo8g0OnmB/RMwYy4V8= -cloud.google.com/go/recommendationengine v0.8.4/go.mod h1:GEteCf1PATl5v5ZsQ60sTClUE0phbWmo3rQ1Js8louU= -cloud.google.com/go/recommendationengine v0.8.5/go.mod h1:A38rIXHGFvoPvmy6pZLozr0g59NRNREz4cx7F58HAsQ= cloud.google.com/go/recommendationengine v0.9.1 h1:TQne3UMow6joFVRtTpd9kDYyYr3Jkpq+o0vJkpQgZYI= cloud.google.com/go/recommendationengine v0.9.1/go.mod h1:FfWa3OnsnDab4unvTZM2VJmvoeGn1tnntF3n+vmfyzU= -cloud.google.com/go/recommender v1.10.1/go.mod h1:XFvrE4Suqn5Cq0Lf+mCP6oBHD/yRMA8XxP5sb7Q7gpA= -cloud.google.com/go/recommender v1.11.0/go.mod h1:kPiRQhPyTJ9kyXPCG6u/dlPLbYfFlkwHNRwdzPVAoII= -cloud.google.com/go/recommender v1.11.1/go.mod h1:sGwFFAyI57v2Hc5LbIj+lTwXipGu9NW015rkaEM5B18= -cloud.google.com/go/recommender v1.11.2/go.mod h1:AeoJuzOvFR/emIcXdVFkspVXVTYpliRCmKNYDnyBv6Y= -cloud.google.com/go/recommender v1.11.3/go.mod h1:+FJosKKJSId1MBFeJ/TTyoGQZiEelQQIZMKYYD8ruK4= -cloud.google.com/go/recommender v1.12.0/go.mod h1:+FJosKKJSId1MBFeJ/TTyoGQZiEelQQIZMKYYD8ruK4= -cloud.google.com/go/recommender v1.12.1/go.mod h1:gf95SInWNND5aPas3yjwl0I572dtudMhMIG4ni8nr+0= cloud.google.com/go/recommender v1.13.1 h1:aQIUpMynK1pU1Q+EiuL7VJssLLjLwnfhL7px0vgM6xA= cloud.google.com/go/recommender v1.13.1/go.mod h1:l+n8rNMC6jZacckzLvVG/2LzKawlwAJYNO8Vl2pBlxc= -cloud.google.com/go/redis v1.13.1/go.mod h1:VP7DGLpE91M6bcsDdMuyCm2hIpB6Vp2hI090Mfd1tcg= -cloud.google.com/go/redis v1.13.2/go.mod h1:0Hg7pCMXS9uz02q+LoEVl5dNHUkIQv+C/3L76fandSA= -cloud.google.com/go/redis v1.13.3/go.mod h1:vbUpCKUAZSYzFcWKmICnYgRAhTFg9r+djWqFxDYXi4U= -cloud.google.com/go/redis v1.14.1/go.mod h1:MbmBxN8bEnQI4doZPC1BzADU4HGocHBk2de3SbgOkqs= -cloud.google.com/go/redis v1.14.2/go.mod h1:g0Lu7RRRz46ENdFKQ2EcQZBAJ2PtJHJLuiiRuEXwyQw= cloud.google.com/go/redis v1.17.1 h1:E7TeGsvyoFB+m59bqFKrQ5GSH7+uW8cUDk6Y7iqGjJ0= cloud.google.com/go/redis v1.17.1/go.mod h1:YJHeYfSoW/agIMeCvM5rszxu75mVh5DOhbu3AEZEIQM= -cloud.google.com/go/resourcemanager v1.9.1/go.mod h1:dVCuosgrh1tINZ/RwBufr8lULmWGOkPS8gL5gqyjdT8= -cloud.google.com/go/resourcemanager v1.9.2/go.mod h1:OujkBg1UZg5lX2yIyMo5Vz9O5hf7XQOSV7WxqxxMtQE= -cloud.google.com/go/resourcemanager v1.9.3/go.mod h1:IqrY+g0ZgLsihcfcmqSe+RKp1hzjXwG904B92AwBz6U= -cloud.google.com/go/resourcemanager v1.9.4/go.mod h1:N1dhP9RFvo3lUfwtfLWVxfUWq8+KUQ+XLlHLH3BoFJ0= -cloud.google.com/go/resourcemanager v1.9.5/go.mod h1:hep6KjelHA+ToEjOfO3garMKi/CLYwTqeAw7YiEI9x8= cloud.google.com/go/resourcemanager v1.10.1 h1:fO/QoSJ1lepmTM9dCbSXYWgTIhecmQkpY0mM1X9OGN0= cloud.google.com/go/resourcemanager v1.10.1/go.mod h1:A/ANV/Sv7y7fcjd4LSH7PJGTZcWRkO/69yN5UhYUmvE= -cloud.google.com/go/resourcesettings v1.6.1/go.mod h1:M7mk9PIZrC5Fgsu1kZJci6mpgN8o0IUzVx3eJU3y4Jw= -cloud.google.com/go/resourcesettings v1.6.2/go.mod h1:mJIEDd9MobzunWMeniaMp6tzg4I2GvD3TTmPkc8vBXk= -cloud.google.com/go/resourcesettings v1.6.3/go.mod h1:pno5D+7oDYkMWZ5BpPsb4SO0ewg3IXcmmrUZaMJrFic= -cloud.google.com/go/resourcesettings v1.6.4/go.mod h1:pYTTkWdv2lmQcjsthbZLNBP4QW140cs7wqA3DuqErVI= -cloud.google.com/go/resourcesettings v1.6.5/go.mod h1:WBOIWZraXZOGAgoR4ukNj0o0HiSMO62H9RpFi9WjP9I= cloud.google.com/go/resourcesettings v1.8.1 h1:whJgmR9I5V9TSZiaoCPVDgbYD1jghYoauHVfBG8TvHI= cloud.google.com/go/resourcesettings v1.8.1/go.mod h1:6V87tIXUpvJMskim6YUa+TRDTm7v6OH8FxLOIRYosl4= -cloud.google.com/go/retail v1.14.1/go.mod h1:y3Wv3Vr2k54dLNIrCzenyKG8g8dhvhncT2NcNjb/6gE= -cloud.google.com/go/retail v1.14.2/go.mod h1:W7rrNRChAEChX336QF7bnMxbsjugcOCPU44i5kbLiL8= -cloud.google.com/go/retail v1.14.3/go.mod h1:Omz2akDHeSlfCq8ArPKiBxlnRpKEBjUH386JYFLUvXo= -cloud.google.com/go/retail v1.14.4/go.mod h1:l/N7cMtY78yRnJqp5JW8emy7MB1nz8E4t2yfOmklYfg= -cloud.google.com/go/retail v1.15.1/go.mod h1:In9nSBOYhLbDGa87QvWlnE1XA14xBN2FpQRiRsUs9wU= -cloud.google.com/go/retail v1.16.0/go.mod h1:LW7tllVveZo4ReWt68VnldZFWJRzsh9np+01J9dYWzE= cloud.google.com/go/retail v1.19.0 h1:OrXxtP/asKi7vFReWmQH5kXrMRPZ2R9Zw92x8O93PMA= cloud.google.com/go/retail v1.19.0/go.mod h1:QMhO+nkvN6Mns1lu6VXmteY0I3mhwPj9bOskn6PK5aY= -cloud.google.com/go/run v1.2.0/go.mod h1:36V1IlDzQ0XxbQjUx6IYbw8H3TJnWvhii963WW3B/bo= -cloud.google.com/go/run v1.3.0/go.mod h1:S/osX/4jIPZGg+ssuqh6GNgg7syixKe3YnprwehzHKU= -cloud.google.com/go/run v1.3.1/go.mod h1:cymddtZOzdwLIAsmS6s+Asl4JoXIDm/K1cpZTxV4Q5s= -cloud.google.com/go/run v1.3.2/go.mod h1:SIhmqArbjdU/D9M6JoHaAqnAMKLFtXaVdNeq04NjnVE= -cloud.google.com/go/run v1.3.3/go.mod h1:WSM5pGyJ7cfYyYbONVQBN4buz42zFqwG67Q3ch07iK4= -cloud.google.com/go/run v1.3.4/go.mod h1:FGieuZvQ3tj1e9GnzXqrMABSuir38AJg5xhiYq+SF3o= cloud.google.com/go/run v1.6.0 h1:LRJvntufFKJ0Jcwt7BbIHwf/0Ipq4twzyJcH1qSEs84= cloud.google.com/go/run v1.6.0/go.mod h1:DXkPPa8bZ0jfRGLT+EKIlPbHvosBYBMdxTgo9EBbXZE= -cloud.google.com/go/scheduler v1.10.1/go.mod h1:R63Ldltd47Bs4gnhQkmNDse5w8gBRrhObZ54PxgR2Oo= -cloud.google.com/go/scheduler v1.10.2/go.mod h1:O3jX6HRH5eKCA3FutMw375XHZJudNIKVonSCHv7ropY= -cloud.google.com/go/scheduler v1.10.3/go.mod h1:8ANskEM33+sIbpJ+R4xRfw/jzOG+ZFE8WVLy7/yGvbc= -cloud.google.com/go/scheduler v1.10.4/go.mod h1:MTuXcrJC9tqOHhixdbHDFSIuh7xZF2IysiINDuiq6NI= -cloud.google.com/go/scheduler v1.10.5/go.mod h1:MTuXcrJC9tqOHhixdbHDFSIuh7xZF2IysiINDuiq6NI= -cloud.google.com/go/scheduler v1.10.6/go.mod h1:pe2pNCtJ+R01E06XCDOJs1XvAMbv28ZsQEbqknxGOuE= cloud.google.com/go/scheduler v1.11.1 h1:uGaM4mRrGkJ0LLBMyxD8qbvIko4y+UlSOwJQqRd/lW8= cloud.google.com/go/scheduler v1.11.1/go.mod h1:ptS76q0oOS8hCHOH4Fb/y8YunPEN8emaDdtw0D7W1VE= -cloud.google.com/go/secretmanager v1.11.1/go.mod h1:znq9JlXgTNdBeQk9TBW/FnR/W4uChEKGeqQWAJ8SXFw= -cloud.google.com/go/secretmanager v1.11.2/go.mod h1:MQm4t3deoSub7+WNwiC4/tRYgDBHJgJPvswqQVB1Vss= -cloud.google.com/go/secretmanager v1.11.3/go.mod h1:0bA2o6FabmShrEy328i67aV+65XoUFFSmVeLBn/51jI= -cloud.google.com/go/secretmanager v1.11.4/go.mod h1:wreJlbS9Zdq21lMzWmJ0XhWW2ZxgPeahsqeV/vZoJ3w= -cloud.google.com/go/secretmanager v1.11.5/go.mod h1:eAGv+DaCHkeVyQi0BeXgAHOU0RdrMeZIASKc+S7VqH4= cloud.google.com/go/secretmanager v1.14.1 h1:xlWSIg8rtBn5qCr2f3XtQP19+5COyf/ll49SEvi/0vM= cloud.google.com/go/secretmanager v1.14.1/go.mod h1:L+gO+u2JA9CCyXpSR8gDH0o8EV7i/f0jdBOrUXcIV0U= -cloud.google.com/go/security v1.15.1/go.mod h1:MvTnnbsWnehoizHi09zoiZob0iCHVcL4AUBj76h9fXA= -cloud.google.com/go/security v1.15.2/go.mod h1:2GVE/v1oixIRHDaClVbHuPcZwAqFM28mXuAKCfMgYIg= -cloud.google.com/go/security v1.15.3/go.mod h1:gQ/7Q2JYUZZgOzqKtw9McShH+MjNvtDpL40J1cT+vBs= -cloud.google.com/go/security v1.15.4/go.mod h1:oN7C2uIZKhxCLiAAijKUCuHLZbIt/ghYEo8MqwD/Ty4= -cloud.google.com/go/security v1.15.5/go.mod h1:KS6X2eG3ynWjqcIX976fuToN5juVkF6Ra6c7MPnldtc= cloud.google.com/go/security v1.18.1 h1:w7XbMR90Ir0y8NUxKJ3uyRHuHYWPUxVI5Z/sGqbrdAQ= cloud.google.com/go/security v1.18.1/go.mod h1:5P1q9rqwt0HuVeL9p61pTqQ6Lgio1c64jL2ZMWZV21Y= -cloud.google.com/go/securitycenter v1.23.0/go.mod h1:8pwQ4n+Y9WCWM278R8W3nF65QtY172h4S8aXyI9/hsQ= -cloud.google.com/go/securitycenter v1.23.1/go.mod h1:w2HV3Mv/yKhbXKwOCu2i8bCuLtNP1IMHuiYQn4HJq5s= -cloud.google.com/go/securitycenter v1.24.1/go.mod h1:3h9IdjjHhVMXdQnmqzVnM7b0wMn/1O/U20eWVpMpZjI= -cloud.google.com/go/securitycenter v1.24.2/go.mod h1:l1XejOngggzqwr4Fa2Cn+iWZGf+aBLTXtB/vXjy5vXM= -cloud.google.com/go/securitycenter v1.24.3/go.mod h1:l1XejOngggzqwr4Fa2Cn+iWZGf+aBLTXtB/vXjy5vXM= -cloud.google.com/go/securitycenter v1.24.4/go.mod h1:PSccin+o1EMYKcFQzz9HMMnZ2r9+7jbc+LvPjXhpwcU= cloud.google.com/go/securitycenter v1.35.1 h1:unUyFDeSHv89W7FPBMk10mf3R7+taAJ+1ow+0zpCzGw= cloud.google.com/go/securitycenter v1.35.1/go.mod h1:UDeknPuHWi15TaxrJCIv3aN1VDTz9nqWVUmW2vGayTo= cloud.google.com/go/servicecontrol v1.11.1 h1:d0uV7Qegtfaa7Z2ClDzr9HJmnbJW7jn0WhZ7wOX6hLE= -cloud.google.com/go/servicedirectory v1.10.1/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.0/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.1/go.mod h1:tJywXimEWzNzw9FvtNjsQxxJ3/41jseeILgwU/QLrGI= -cloud.google.com/go/servicedirectory v1.11.2/go.mod h1:KD9hCLhncWRV5jJphwIpugKwM5bn1x0GyVVD4NO8mGg= -cloud.google.com/go/servicedirectory v1.11.3/go.mod h1:LV+cHkomRLr67YoQy3Xq2tUXBGOs5z5bPofdq7qtiAw= -cloud.google.com/go/servicedirectory v1.11.4/go.mod h1:Bz2T9t+/Ehg6x+Y7Ycq5xiShYLD96NfEsWNHyitj1qM= cloud.google.com/go/servicedirectory v1.12.1 h1:LjbIXEZiyqsIADrj6Y81FnbSlaHPQHJ8UDQQnUegowc= cloud.google.com/go/servicedirectory v1.12.1/go.mod h1:d2H6joDMjnTQ4cUUCZn6k9NgZFbXjLVJbHETjoJR9k0= cloud.google.com/go/servicemanagement v1.8.0 h1:fopAQI/IAzlxnVeiKn/8WiV6zKndjFkvi+gzu+NjywY= cloud.google.com/go/serviceusage v1.6.0 h1:rXyq+0+RSIm3HFypctp7WoXxIA563rn206CfMWdqXX4= -cloud.google.com/go/shell v1.7.1/go.mod h1:u1RaM+huXFaTojTbW4g9P5emOrrmLE69KrxqQahKn4g= -cloud.google.com/go/shell v1.7.2/go.mod h1:KqRPKwBV0UyLickMn0+BY1qIyE98kKyI216sH/TuHmc= -cloud.google.com/go/shell v1.7.3/go.mod h1:cTTEz/JdaBsQAeTQ3B6HHldZudFoYBOqjteev07FbIc= -cloud.google.com/go/shell v1.7.4/go.mod h1:yLeXB8eKLxw0dpEmXQ/FjriYrBijNsONpwnWsdPqlKM= -cloud.google.com/go/shell v1.7.5/go.mod h1:hL2++7F47/IfpfTO53KYf1EC+F56k3ThfNEXd4zcuiE= cloud.google.com/go/shell v1.8.1 h1:etoJal+LB7Pn8+5vE2aAh6QcFbBmerIOh5MxNDoXykw= cloud.google.com/go/shell v1.8.1/go.mod h1:jaU7OHeldDhTwgs3+clM0KYEDYnBAPevUI6wNLf7ycE= -cloud.google.com/go/spanner v1.47.0/go.mod h1:IXsJwVW2j4UKs0eYDqodab6HgGuA1bViSqW4uH9lfUI= -cloud.google.com/go/spanner v1.49.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= -cloud.google.com/go/spanner v1.50.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= -cloud.google.com/go/spanner v1.51.0/go.mod h1:c5KNo5LQ1X5tJwma9rSQZsXNBDNvj4/n8BVc3LNahq0= -cloud.google.com/go/spanner v1.53.0/go.mod h1:liG4iCeLqm5L3fFLU5whFITqP0e0orsAW1uUSrd4rws= -cloud.google.com/go/spanner v1.53.1/go.mod h1:liG4iCeLqm5L3fFLU5whFITqP0e0orsAW1uUSrd4rws= -cloud.google.com/go/spanner v1.54.0/go.mod h1:wZvSQVBgngF0Gq86fKup6KIYmN2be7uOKjtK97X+bQU= -cloud.google.com/go/spanner v1.55.0/go.mod h1:HXEznMUVhC+PC+HDyo9YFG2Ajj5BQDkcbqB9Z2Ffxi0= -cloud.google.com/go/spanner v1.56.0/go.mod h1:DndqtUKQAt3VLuV2Le+9Y3WTnq5cNKrnLb/Piqcj+h0= -cloud.google.com/go/spanner v1.57.0/go.mod h1:aXQ5QDdhPRIqVhYmnkAdwPYvj/DRN0FguclhEWw+jOo= cloud.google.com/go/spanner v1.70.0 h1:nj6p/GJTgMDiSQ1gQ034ItsKuJgHiMOjtOlONOg8PSo= cloud.google.com/go/spanner v1.70.0/go.mod h1:X5T0XftydYp0K1adeJQDJtdWpbrOeJ7wHecM4tK6FiE= -cloud.google.com/go/speech v1.17.1/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.19.0/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.19.1/go.mod h1:WcuaWz/3hOlzPFOVo9DUsblMIHwxP589y6ZMtaG+iAA= -cloud.google.com/go/speech v1.19.2/go.mod h1:2OYFfj+Ch5LWjsaSINuCZsre/789zlcCI3SY4oAi2oI= -cloud.google.com/go/speech v1.20.1/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWoikBEWavY= -cloud.google.com/go/speech v1.21.0/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWoikBEWavY= -cloud.google.com/go/speech v1.21.1/go.mod h1:E5GHZXYQlkqWQwY5xRSLHw2ci5NMQNG52FfMU1aZrIA= cloud.google.com/go/speech v1.25.1 h1:iGZJS3wrdkje/Vqiacx1+r+zVwUZoXVMdklYIVsvfNw= cloud.google.com/go/speech v1.25.1/go.mod h1:WgQghvghkZ1htG6BhYn98mP7Tg0mti8dBFDLMVXH/vM= -cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= -cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= -cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= -cloud.google.com/go/storage v1.37.0/go.mod h1:i34TiT2IhiNDmcj65PqwCjcoUX7Z5pLzS8DEmoiFq1k= -cloud.google.com/go/storagetransfer v1.10.0/go.mod h1:DM4sTlSmGiNczmV6iZyceIh2dbs+7z2Ayg6YAiQlYfA= -cloud.google.com/go/storagetransfer v1.10.1/go.mod h1:rS7Sy0BtPviWYTTJVWCSV4QrbBitgPeuK4/FKa4IdLs= -cloud.google.com/go/storagetransfer v1.10.2/go.mod h1:meIhYQup5rg9juQJdyppnA/WLQCOguxtk1pr3/vBWzA= -cloud.google.com/go/storagetransfer v1.10.3/go.mod h1:Up8LY2p6X68SZ+WToswpQbQHnJpOty/ACcMafuey8gc= -cloud.google.com/go/storagetransfer v1.10.4/go.mod h1:vef30rZKu5HSEf/x1tK3WfWrL0XVoUQN/EPDRGPzjZs= cloud.google.com/go/storagetransfer v1.11.1 h1:Hd7H1zXGQGEWyWXxWVXDMuNCGasNQim1y9CIaMZIBX8= cloud.google.com/go/storagetransfer v1.11.1/go.mod h1:xnJo9pWysRIha8MgZxhrBEwLYbEdvdmEedhNsP5NINM= -cloud.google.com/go/talent v1.6.2/go.mod h1:CbGvmKCG61mkdjcqTcLOkb2ZN1SrQI8MDyma2l7VD24= -cloud.google.com/go/talent v1.6.3/go.mod h1:xoDO97Qd4AK43rGjJvyBHMskiEf3KulgYzcH6YWOVoo= -cloud.google.com/go/talent v1.6.4/go.mod h1:QsWvi5eKeh6gG2DlBkpMaFYZYrYUnIpo34f6/V5QykY= -cloud.google.com/go/talent v1.6.5/go.mod h1:Mf5cma696HmE+P2BWJ/ZwYqeJXEeU0UqjHFXVLadEDI= -cloud.google.com/go/talent v1.6.6/go.mod h1:y/WQDKrhVz12WagoarpAIyKKMeKGKHWPoReZ0g8tseQ= cloud.google.com/go/talent v1.7.1 h1:J3iZU+HPfoD18Lx8JsgIpwe8llQ9Fu/evcQudQCB+pk= cloud.google.com/go/talent v1.7.1/go.mod h1:X8UKtTgcP+h51MtDO/b+y3X1GxTTc7gPJ2y0aX3X1hM= -cloud.google.com/go/texttospeech v1.7.1/go.mod h1:m7QfG5IXxeneGqTapXNxv2ItxP/FS0hCZBwXYqucgSk= -cloud.google.com/go/texttospeech v1.7.2/go.mod h1:VYPT6aTOEl3herQjFHYErTlSZJ4vB00Q2ZTmuVgluD4= -cloud.google.com/go/texttospeech v1.7.3/go.mod h1:Av/zpkcgWfXlDLRYob17lqMstGZ3GqlvJXqKMp2u8so= -cloud.google.com/go/texttospeech v1.7.4/go.mod h1:vgv0002WvR4liGuSd5BJbWy4nDn5Ozco0uJymY5+U74= -cloud.google.com/go/texttospeech v1.7.5/go.mod h1:tzpCuNWPwrNJnEa4Pu5taALuZL4QRRLcb+K9pbhXT6M= cloud.google.com/go/texttospeech v1.8.1 h1:LpX9xKoGObltmT6+RGxqUeSJIq0uqPzo+fcbbOmujbY= cloud.google.com/go/texttospeech v1.8.1/go.mod h1:WoTykB+4mfSDDYPuk7smrdXNRGoJJS6dXRR6l4XqD9g= -cloud.google.com/go/tpu v1.6.1/go.mod h1:sOdcHVIgDEEOKuqUoi6Fq53MKHJAtOwtz0GuKsWSH3E= -cloud.google.com/go/tpu v1.6.2/go.mod h1:NXh3NDwt71TsPZdtGWgAG5ThDfGd32X1mJ2cMaRlVgU= -cloud.google.com/go/tpu v1.6.3/go.mod h1:lxiueqfVMlSToZY1151IaZqp89ELPSrk+3HIQ5HRkbY= -cloud.google.com/go/tpu v1.6.4/go.mod h1:NAm9q3Rq2wIlGnOhpYICNI7+bpBebMJbh0yyp3aNw1Y= -cloud.google.com/go/tpu v1.6.5/go.mod h1:P9DFOEBIBhuEcZhXi+wPoVy/cji+0ICFi4TtTkMHSSs= cloud.google.com/go/tpu v1.7.1 h1:MP2GYTVEPkg1KlhY3A4CF9Do8eklQOOfgbIYNINcVaE= cloud.google.com/go/tpu v1.7.1/go.mod h1:kgvyq1Z1yuBJSk5ihUaYxX58YMioCYg1UPuIHSxBX3M= -cloud.google.com/go/trace v1.10.1/go.mod h1:gbtL94KE5AJLH3y+WVpfWILmqgc6dXcqgNXdOPAQTYk= -cloud.google.com/go/trace v1.10.2/go.mod h1:NPXemMi6MToRFcSxRl2uDnu/qAlAQ3oULUphcHGh1vA= -cloud.google.com/go/trace v1.10.3/go.mod h1:Ke1bgfc73RV3wUFml+uQp7EsDw4dGaETLxB7Iq/r4CY= -cloud.google.com/go/trace v1.10.4/go.mod h1:Nso99EDIK8Mj5/zmB+iGr9dosS/bzWCJ8wGmE6TXNWY= -cloud.google.com/go/trace v1.10.5/go.mod h1:9hjCV1nGBCtXbAE4YK7OqJ8pmPYSxPA0I67JwRd5s3M= cloud.google.com/go/trace v1.11.1 h1:UNqdP+HYYtnm6lb91aNA5JQ0X14GnxkABGlfz2PzPew= cloud.google.com/go/trace v1.11.1/go.mod h1:IQKNQuBzH72EGaXEodKlNJrWykGZxet2zgjtS60OtjA= -cloud.google.com/go/translate v1.8.1/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.8.2/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.9.0/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.9.1/go.mod h1:TWIgDZknq2+JD4iRcojgeDtqGEp154HN/uL6hMvylS8= -cloud.google.com/go/translate v1.9.2/go.mod h1:E3Tc6rUTsQkVrXW6avbUhKJSr7ZE3j7zNmqzXKHqRrY= -cloud.google.com/go/translate v1.9.3/go.mod h1:Kbq9RggWsbqZ9W5YpM94Q1Xv4dshw/gr/SHfsl5yCZ0= -cloud.google.com/go/translate v1.10.0/go.mod h1:Kbq9RggWsbqZ9W5YpM94Q1Xv4dshw/gr/SHfsl5yCZ0= -cloud.google.com/go/translate v1.10.1/go.mod h1:adGZcQNom/3ogU65N9UXHOnnSvjPwA/jKQUMnsYXOyk= -cloud.google.com/go/translate v1.10.3/go.mod h1:GW0vC1qvPtd3pgtypCv4k4U8B7EdgK9/QEF2aJEUovs= cloud.google.com/go/translate v1.12.1 h1:Vws9BGpVcaOeI6HodyWdvysUzHUBFvk7ymHu1tzFvuM= cloud.google.com/go/translate v1.12.1/go.mod h1:5f4RvC7/hh76qSl6LYuqOJaKbIzEpR1Sj+CMA6gSgIk= -cloud.google.com/go/video v1.17.1/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= -cloud.google.com/go/video v1.19.0/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= -cloud.google.com/go/video v1.20.0/go.mod h1:U3G3FTnsvAGqglq9LxgqzOiBc/Nt8zis8S+850N2DUM= -cloud.google.com/go/video v1.20.1/go.mod h1:3gJS+iDprnj8SY6pe0SwLeC5BUW80NjhwX7INWEuWGU= -cloud.google.com/go/video v1.20.2/go.mod h1:lrixr5JeKNThsgfM9gqtwb6Okuqzfo4VrY2xynaViTA= -cloud.google.com/go/video v1.20.3/go.mod h1:TnH/mNZKVHeNtpamsSPygSR0iHtvrR/cW1/GDjN5+GU= -cloud.google.com/go/video v1.20.4/go.mod h1:LyUVjyW+Bwj7dh3UJnUGZfyqjEto9DnrvTe1f/+QrW0= cloud.google.com/go/video v1.23.1 h1:U+fu5Jwi3q8WDDOh1hr8kcdXVUJGmP3vWsZ13jwkWFA= cloud.google.com/go/video v1.23.1/go.mod h1:ncFS3D2plMLhXkWkob/bH4bxQkubrpAlln5x7RWluXA= -cloud.google.com/go/videointelligence v1.11.1/go.mod h1:76xn/8InyQHarjTWsBR058SmlPCwQjgcvoW0aZykOvo= -cloud.google.com/go/videointelligence v1.11.2/go.mod h1:ocfIGYtIVmIcWk1DsSGOoDiXca4vaZQII1C85qtoplc= -cloud.google.com/go/videointelligence v1.11.3/go.mod h1:tf0NUaGTjU1iS2KEkGWvO5hRHeCkFK3nPo0/cOZhZAo= -cloud.google.com/go/videointelligence v1.11.4/go.mod h1:kPBMAYsTPFiQxMLmmjpcZUMklJp3nC9+ipJJtprccD8= -cloud.google.com/go/videointelligence v1.11.5/go.mod h1:/PkeQjpRponmOerPeJxNPuxvi12HlW7Em0lJO14FC3I= cloud.google.com/go/videointelligence v1.12.1 h1:4XScHLWL/1Q1FVczlxiZT+kSynUQPUktIUTqpIkOMeU= cloud.google.com/go/videointelligence v1.12.1/go.mod h1:C9bQom4KOeBl7IFPj+NiOS6WKEm1P6OOkF/ahFfE1Eg= cloud.google.com/go/vision v1.2.0 h1:/CsSTkbmO9HC8iQpxbK8ATms3OQaX3YQUeTMGCxlaK4= -cloud.google.com/go/vision/v2 v2.7.2/go.mod h1:jKa8oSYBWhYiXarHPvP4USxYANYUEdEsQrloLjrSwJU= -cloud.google.com/go/vision/v2 v2.7.3/go.mod h1:V0IcLCY7W+hpMKXK1JYE0LV5llEqVmj+UJChjvA1WsM= -cloud.google.com/go/vision/v2 v2.7.4/go.mod h1:ynDKnsDN/0RtqkKxQZ2iatv3Dm9O+HfRb5djl7l4Vvw= -cloud.google.com/go/vision/v2 v2.7.5/go.mod h1:GcviprJLFfK9OLf0z8Gm6lQb6ZFUulvpZws+mm6yPLM= -cloud.google.com/go/vision/v2 v2.7.6/go.mod h1:ZkvWTVNPBU3YZYzgF9Y1jwEbD1NBOCyJn0KFdQfE6Bw= -cloud.google.com/go/vision/v2 v2.8.0/go.mod h1:ocqDiA2j97pvgogdyhoxiQp2ZkDCyr0HWpicywGGRhU= cloud.google.com/go/vision/v2 v2.9.1 h1:jpK/E7/SJXpbnQVgfr2nGsIIzSQ9GkOsBf2iak1O8nc= cloud.google.com/go/vision/v2 v2.9.1/go.mod h1:keORalKMowhEZB5hEWi1XSVnGALMjLlRwZbDiCPFuQY= -cloud.google.com/go/vmmigration v1.7.1/go.mod h1:WD+5z7a/IpZ5bKK//YmT9E047AD+rjycCAvyMxGJbro= -cloud.google.com/go/vmmigration v1.7.2/go.mod h1:iA2hVj22sm2LLYXGPT1pB63mXHhrH1m/ruux9TwWLd8= -cloud.google.com/go/vmmigration v1.7.3/go.mod h1:ZCQC7cENwmSWlwyTrZcWivchn78YnFniEQYRWQ65tBo= -cloud.google.com/go/vmmigration v1.7.4/go.mod h1:yBXCmiLaB99hEl/G9ZooNx2GyzgsjKnw5fWcINRgD70= -cloud.google.com/go/vmmigration v1.7.5/go.mod h1:pkvO6huVnVWzkFioxSghZxIGcsstDvYiVCxQ9ZH3eYI= cloud.google.com/go/vmmigration v1.8.1 h1:dyK3bFJVx28FInAkzeLVANpChwWgAmiaUM4GNtEQS/Q= cloud.google.com/go/vmmigration v1.8.1/go.mod h1:MB7vpxl6Oz2w+CecyITUTDFkhWSMQmRTgREwkBZFyZk= -cloud.google.com/go/vmwareengine v0.4.1/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.0/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.1/go.mod h1:aT3Xsm5sNx0QShk1Jc1B8OddrxAScYLwzVoaiXfdzzk= -cloud.google.com/go/vmwareengine v1.0.2/go.mod h1:xMSNjIk8/itYrz1JA8nV3Ajg4L4n3N+ugP8JKzk3OaA= -cloud.google.com/go/vmwareengine v1.0.3/go.mod h1:QSpdZ1stlbfKtyt6Iu19M6XRxjmXO+vb5a/R6Fvy2y4= -cloud.google.com/go/vmwareengine v1.1.1/go.mod h1:nMpdsIVkUrSaX8UvmnBhzVzG7PPvNYc5BszcvIVudYs= cloud.google.com/go/vmwareengine v1.3.1 h1:CCdTFQnOatMPbtbMnCja//K4slk5Tjt0u3XEb1T9Qlw= cloud.google.com/go/vmwareengine v1.3.1/go.mod h1:mSYu3wnGKJqvvhIhs7VA47/A/kLoMiJz3gfQAh7cfaI= -cloud.google.com/go/vpcaccess v1.7.1/go.mod h1:FogoD46/ZU+JUBX9D606X21EnxiszYi2tArQwLY4SXs= -cloud.google.com/go/vpcaccess v1.7.2/go.mod h1:mmg/MnRHv+3e8FJUjeSibVFvQF1cCy2MsFaFqxeY1HU= -cloud.google.com/go/vpcaccess v1.7.3/go.mod h1:YX4skyfW3NC8vI3Fk+EegJnlYFatA+dXK4o236EUCUc= -cloud.google.com/go/vpcaccess v1.7.4/go.mod h1:lA0KTvhtEOb/VOdnH/gwPuOzGgM+CWsmGu6bb4IoMKk= -cloud.google.com/go/vpcaccess v1.7.5/go.mod h1:slc5ZRvvjP78c2dnL7m4l4R9GwL3wDLcpIWz6P/ziig= cloud.google.com/go/vpcaccess v1.8.1 h1:e1wJ1wQGMqOf44Gw44PU9G6NYITKm0f2We4eKzMwyEs= cloud.google.com/go/vpcaccess v1.8.1/go.mod h1:cWlLCpLOuMH8oaNmobaymgmLesasLd9w1isrKpiGwIc= -cloud.google.com/go/webrisk v1.9.1/go.mod h1:4GCmXKcOa2BZcZPn6DCEvE7HypmEJcJkr4mtM+sqYPc= -cloud.google.com/go/webrisk v1.9.2/go.mod h1:pY9kfDgAqxUpDBOrG4w8deLfhvJmejKB0qd/5uQIPBc= -cloud.google.com/go/webrisk v1.9.3/go.mod h1:RUYXe9X/wBDXhVilss7EDLW9ZNa06aowPuinUOPCXH8= -cloud.google.com/go/webrisk v1.9.4/go.mod h1:w7m4Ib4C+OseSr2GL66m0zMBywdrVNTDKsdEsfMl7X0= -cloud.google.com/go/webrisk v1.9.5/go.mod h1:aako0Fzep1Q714cPEM5E+mtYX8/jsfegAuS8aivxy3U= cloud.google.com/go/webrisk v1.10.1 h1:mYYjXXMILCwIEqtChUDNGamMBgJKnoJXa9Os2e76uzk= cloud.google.com/go/webrisk v1.10.1/go.mod h1:VzmUIag5P6V71nVAuzc7Hu0VkIDKjDa543K7HOulH/k= -cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv5/rGpFCsBOvPbYgszpg= -cloud.google.com/go/websecurityscanner v1.6.2/go.mod h1:7YgjuU5tun7Eg2kpKgGnDuEOXWIrh8x8lWrJT4zfmas= -cloud.google.com/go/websecurityscanner v1.6.3/go.mod h1:x9XANObUFR+83Cya3g/B9M/yoHVqzxPnFtgF8yYGAXw= -cloud.google.com/go/websecurityscanner v1.6.4/go.mod h1:mUiyMQ+dGpPPRkHgknIZeCzSHJ45+fY4F52nZFDHm2o= -cloud.google.com/go/websecurityscanner v1.6.5/go.mod h1:QR+DWaxAz2pWooylsBF854/Ijvuoa3FCyS1zBa1rAVQ= cloud.google.com/go/websecurityscanner v1.7.1 h1:VyJObL4Pzd4ypF2814rKlesrVibrf1WpZ2yp4jJvKyw= cloud.google.com/go/websecurityscanner v1.7.1/go.mod h1:vAZ6hyqECDhgF+gyVRGzfXMrURQN5NH75Y9yW/7sSHU= -cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g= -cloud.google.com/go/workflows v1.12.0/go.mod h1:PYhSk2b6DhZ508tj8HXKaBh+OFe+xdl0dHF/tJdzPQM= -cloud.google.com/go/workflows v1.12.1/go.mod h1:5A95OhD/edtOhQd/O741NSfIMezNTbCwLM1P1tBRGHM= -cloud.google.com/go/workflows v1.12.2/go.mod h1:+OmBIgNqYJPVggnMo9nqmizW0qEXHhmnAzK/CnBqsHc= -cloud.google.com/go/workflows v1.12.3/go.mod h1:fmOUeeqEwPzIU81foMjTRQIdwQHADi/vEr1cx9R1m5g= -cloud.google.com/go/workflows v1.12.4/go.mod h1:yQ7HUqOkdJK4duVtMeBCAOPiN1ZF1E9pAMX51vpwB/w= cloud.google.com/go/workflows v1.13.1 h1:DkxrZ4HyXvjQLZWsYAUOV1w7d2a43XscM9dmkIGmrDc= cloud.google.com/go/workflows v1.13.1/go.mod h1:xNdYtD6Sjoug+khNCAtBMK/rdh8qkjyL6aBas2XlkNc= contrib.go.opencensus.io/exporter/aws v0.0.0-20230502192102-15967c811cec h1:CSNP8nIEQt4sZEo2sGUiWSmVJ9c5QdyIQvwzZAsn+8Y= @@ -991,50 +256,17 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/azure-amqp-common-go/v3 v3.2.3 h1:uDF62mbd9bypXWi19V1bN5NZEO84JqgmI5G73ibAmrk= github.com/Azure/azure-amqp-common-go/v3 v3.2.3/go.mod h1:7rPmbSfszeovxGfc5fSAXE4ehlXQZHpMja2OtxC2Tas= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0/go.mod h1:3Ug6Qzto9anB6mGlEdgYMDF5zHQ+wwhEaYR4s17PHMw= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0/go.mod h1:uReU2sSxZExRPBAg3qKzmAucSi51+SP1OhohieR821Q= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2/go.mod h1:5FDJtLEO/GxwNgUxbwrY3LP0pEoThTQJtk2oysdXHxM= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.0/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2/go.mod h1:aiYBYui4BJ/BJCAIKs92XiPyQfTaBWqvHujDwKb6CBU= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc= github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.7.1 h1:o/Ws6bEqMeKZUfj1RRm3mQ51O8JGU5w+Qdg2AhHib6A= github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.7.1/go.mod h1:6QAMYBAbQeeKX+REFJMZ1nFWu9XLw/PPcjYpuc9RDFs= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.6.0/go.mod h1:gZmgV+qBqygoznvqo2J9oKZAFziqhLZ2xE/WVUmzkHA= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2/go.mod h1:FbdwsQ2EzwvXxOPcMFYO8ogEc9uMMIj3YkmCdXdAFmk= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1 h1:7CBQ+Ei8SP2c6ydQTGCCrS35bDxgTMfoP2miAwK++OU= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1/go.mod h1:c/wcGeGx5FUPbM/JltUYHZcKmigwyVLJlDq+4HdtXaw= github.com/Azure/go-amqp v1.0.5 h1:po5+ljlcNSU8xtapHTe8gIc8yHxCzC03E8afH2g1ftU= github.com/Azure/go-amqp v1.0.5/go.mod h1:vZAogwdrkbyK3Mla8m/CxSc/aKdnTZ4IbPxl51Y5WZE= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 h1:Ov8avRZi2vmrE2JcXw+tu5K/yB41r7xK9GZDiBF7NdM= github.com/Azure/go-autorest/autorest/azure/auth v0.5.13/go.mod h1:5BAVfWLWXihP47vYrPuBKKf4cS0bXI+KM9Qx6ETDJYo= github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= -github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= -github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0/go.mod h1:BDJ5qMFKx9DugEg3+uQSDCdbYPr5s9vBTrL9P8TpqOU= -github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4= github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg= -github.com/ClickHouse/clickhouse-go/v2 v2.28.3 h1:SkFzPULX6nzgfNZd1YD1XTECivjTMrCtD09ZPKcVLFQ= -github.com/ClickHouse/clickhouse-go/v2 v2.28.3/go.mod h1:vzn73hp+3JwxtFU4RjPCQ7r6fP2pMKVwdi8E1/Tkua8= github.com/ClickHouse/clickhouse-go/v2 v2.30.0 h1:AG4D/hW39qa58+JHQIFOSnxyL46H6h2lrmGGk17dhFo= github.com/ClickHouse/clickhouse-go/v2 v2.30.0/go.mod h1:i9ZQAojcayW3RsdCb3YR+n+wC2h65eJsZCscZ1Z1wyo= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= @@ -1048,29 +280,23 @@ github.com/DmitriyVTitov/size v1.5.0 h1:/PzqxYrOyOUX1BXj6J9OuVRVGe+66VL4D9FlUaW5 github.com/DmitriyVTitov/size v1.5.0/go.mod h1:le6rNI4CoLQV1b9gzp1+3d7hMAD/uu2QcJ+aYbNgiU0= github.com/GoogleCloudPlatform/cloudsql-proxy v1.36.0 h1:kAtNAWwvTt5+iew6baV0kbOrtjYTXPtWNSyOFlcxkBU= github.com/GoogleCloudPlatform/cloudsql-proxy v1.36.0/go.mod h1:VRKXU8C7Y/aUKjRBTGfw0Ndv4YqNxlB8zAPJJDxbASE= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.2 h1:cZpsGsWTIFKymTA0je7IIvi1O7Es7apb9CF3EQlOcfE= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.2/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k= -github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 h1:oVLqHXhnYtUwM89y9T1fXGaK9wTkXHgNp8/ZNMQzUxE= +github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= github.com/IBM/go-sdk-core/v5 v5.17.4 h1:VGb9+mRrnS2HpHZFM5hy4J6ppIWnwNrw0G+tLSgcJLc= github.com/IBM/go-sdk-core/v5 v5.17.4/go.mod h1:KsAAI7eStAWwQa4F96MLy+whYSh39JzNjklZRbN/8ns= github.com/IBM/ibm-cos-sdk-go v1.11.0 h1:Jp55NLN3OvBwucMGpP5wNybyjncsmTZ9+GPHai/1cE8= github.com/IBM/ibm-cos-sdk-go v1.11.0/go.mod h1:FnWOym0CvrPM0nHoXvceClOEvGVXecPpmVIO5RFjlFk= github.com/IBM/sarama v1.43.2 h1:HABeEqRUh32z8yzY2hGB/j8mHSzC/HA9zlEjqFNCzSw= github.com/IBM/sarama v1.43.2/go.mod h1:Kyo4WkF24Z+1nz7xeVUFWIuKVV8RS3wM8mkvPKMdXFQ= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk= github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM= -github.com/KimMachineGun/automemlimit v0.6.0 h1:p/BXkH+K40Hax+PuWWPQ478hPjsp9h1CPDhLlA3Z37E= -github.com/KimMachineGun/automemlimit v0.6.0/go.mod h1:T7xYht7B8r6AG/AqFcUdc7fzd2bIdBKmepfP2S1svPY= github.com/KimMachineGun/automemlimit v0.6.1 h1:ILa9j1onAAMadBsyyUJv5cack8Y1WT26yLj/V+ulKp8= github.com/KimMachineGun/automemlimit v0.6.1/go.mod h1:T7xYht7B8r6AG/AqFcUdc7fzd2bIdBKmepfP2S1svPY= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k= github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -1085,29 +311,19 @@ github.com/Shopify/sarama v1.38.1/go.mod h1:iwv9a67Ha8VNa+TifujYoWGxWnu2kNVAQdSd github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM= github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19 h1:iXUgAaqDcIUGbRoy2TdeofRG/j1zpGRSEmNK05T+bi8= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw= -github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= -github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0= -github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= github.com/alecthomas/kong v0.8.0 h1:ryDCzutfIqJPnNn0omnrgHLbAggDQM2VWHikE1xqK7s= github.com/alecthomas/kong v0.8.0/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= -github.com/alecthomas/participle/v2 v2.0.0/go.mod h1:rAKZdJldHu8084ojcWevWAL8KmEU+AT+Olodb+WoN2Y= -github.com/alecthomas/participle/v2 v2.1.0/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8= github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= -github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= -github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= -github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alexflint/go-arg v1.4.2 h1:lDWZAXxpAnZUq4qwb86p/3rIJJ2Li81EoMbTMujhVa0= github.com/alexflint/go-arg v1.4.2/go.mod h1:9iRbDxne7LcR/GSvEr7ma++GLpdIU1zrghf2y2768kM= github.com/alexflint/go-scalar v1.0.0 h1:NGupf1XV/Xb04wXskDFzS0KWOLH632W/EO4fAFi+A70= @@ -1116,7 +332,6 @@ github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= github.com/aliyun/aliyun-oss-go-sdk v2.2.10+incompatible h1:ROMcuN61gI8SfQ+AEMh4d7GZ3gwTZLIhPjtd05TQCG4= github.com/aliyun/aliyun-oss-go-sdk v2.2.10+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= @@ -1127,24 +342,12 @@ github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 h1:q4dksr6IC github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs= github.com/apache/arrow/go/v10 v10.0.1 h1:n9dERvixoC/1JjDmBcs9FPaEryoANa2sCgVFo6ez9cI= github.com/apache/arrow/go/v11 v11.0.0 h1:hqauxvFQxww+0mEU/2XHG6LT7eZternCZq+A5Yly2uM= -github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= -github.com/apache/arrow/go/v12 v12.0.1 h1:JsR2+hzYYjgSUkBSaahpqCetqZMr76djX80fF/DiJbg= -github.com/apache/arrow/go/v12 v12.0.1/go.mod h1:weuTY7JvTG/HDPtMQxEUp7pU73vkLWMLpY67QwZ/WWw= -github.com/apache/arrow/go/v14 v14.0.2 h1:N8OkaJEOfI3mEZt07BIkvo4sC6XDbL+48MBPWO5IONw= -github.com/apache/arrow/go/v14 v14.0.2/go.mod h1:u3fgh3EdgN/YQ8cVQRguVW3R+seMybFg8QBQ5LU+eBY= github.com/apache/arrow/go/v15 v15.0.2 h1:60IliRbiyTWCWjERBCkO1W4Qun9svcYoZrSLcyOsMLE= github.com/apache/arrow/go/v15 v15.0.2/go.mod h1:DGXsR3ajT524njufqf95822i+KTh+yea1jass9YXgjA= -github.com/apache/thrift v0.17.0/go.mod h1:OLxhMRJxomX+1I/KUw03qoV3mMz16BwaKI+d4fPBx7Q= github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA= github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= -github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.51.25/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1 h1:w/fPGB0t5rWwA43mux4e9ozFSH5zF1moQemlA131PWc= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= github.com/aws/aws-sdk-go-v2/service/kms v1.35.3 h1:UPTdlTOwWUX49fVi7cymEN6hDqCwe3LNv1vi7TXUutk= github.com/aws/aws-sdk-go-v2/service/kms v1.35.3/go.mod h1:gjDP16zn+WWalyaUqwCCioQ8gU8lzttCCc9jYsiQI/8= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.4 h1:NgRFYyFpiMD62y4VPXh4DosPFbZd4vdMVBWKk0VmWXc= @@ -1155,12 +358,10 @@ github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3 h1:Vjqy5BZCOIsn4Pj8xzyqgGmsSqzz github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3/go.mod h1:L0enV3GCRd5iG9B64W35C4/hwsCB00Ib+DKVGTadKHI= github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4 h1:hgSBvRT7JEWx2+vEGI9/Ld5rZtl7M5lu8PqdvOmbRHw= github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4/go.mod h1:v7NIzEFIHBiicOMaMTuEmbnzGnqW0d+6ulNALul6fYE= -github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/baidubce/bce-sdk-go v0.9.188 h1:8MA7ewe4VpX01uYl7Kic6ZvfIReUFdSKbY46ZqlQM7U= github.com/baidubce/bce-sdk-go v0.9.188/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= -github.com/bazelbuild/rules_go v0.49.0/go.mod h1:Dhcz716Kqg1RHNWos+N6MlXNkjNP2EwZQ0LukRKJfMs= github.com/benbjohnson/immutable v0.4.0 h1:CTqXbEerYso8YzVPxmWxh2gnoRQbbB9X1quUC8+vGZA= github.com/benbjohnson/immutable v0.4.0/go.mod h1:iAr8OjJGLnLmVUr9MZ/rz4PWUy6Ouc2JLYuMArmvAJM= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= @@ -1186,51 +387,35 @@ github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9Mwe github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY= github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= -github.com/casbin/casbin/v2 v2.37.0 h1:/poEwPSovi4bTOcP752/CsTQiRz2xycyVKFG7GUhbDw= -github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= -github.com/chromedp/chromedp v0.9.2 h1:dKtNz4kApb06KuSXoTQIyUC2TrA0fhGDwNZf3bcgfKw= -github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= +github.com/chromedp/cdproto v0.0.0-20220208224320-6efb837e6bc2/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U= github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= -github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= -github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= -github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= -github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= -github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY= github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y= github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA= -github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= -github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= -github.com/cncf/xds/go v0.0.0-20230428030218-4003588d1b74/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c h1:2zRrJWIt/f9c9HhNHAgrRgq0San5gRRUJTBXLkchal0= +github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= +github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coder/quartz v0.1.0 h1:cLL+0g5l7xTf6ordRnUMMiZtRE8Sq5LxpghS63vEXrQ= github.com/coder/quartz v0.1.0/go.mod h1:vsiCc+AHViMKH2CQpGIpFgdHIEQsxwm8yCscqKmzbRA= github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= -github.com/containerd/cgroups/v3 v3.0.1/go.mod h1:/vtwk1VXrtoa5AaZLkypuOJgA/6DyPMZHJPGQNtlHnw= github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= github.com/containerd/containerd v1.6.18 h1:qZbsLvmyu+Vlty0/Ex5xc0z2YtKpIsb5n45mAMI+2Ns= @@ -1251,8 +436,6 @@ github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiG github.com/couchbase/moss v0.2.0 h1:VCYrMzFwEryyhRSeI+/b3tRBSeTpi/8gn5Kf6dxqn+o= github.com/couchbase/moss v0.2.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk= @@ -1292,15 +475,10 @@ github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4 github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA= github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b h1:Yqiad0+sloMPdd/0Fg22actpFx0dekpzt1xJmVNVkU0= github.com/dhui/dktest v0.3.0 h1:kwX5a7EkLcjo7VpsPQSYJcKGbXBXdjI9FGjuUj1jn6I= -github.com/digitalocean/godo v1.113.0/go.mod h1:Z2mTP848Vi3IXXl5YbPekUgr4j4tOePomA+OE1Ag98w= -github.com/digitalocean/godo v1.118.0 h1:lkzGFQmACrVCp7UqH1sAi4JK/PWwlc5aaxubgorKmC4= -github.com/digitalocean/godo v1.118.0/go.mod h1:Vk0vpCot2HOAJwc5WE8wljZGtJ3ZtWIc8MQ8rF38sdo= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= @@ -1311,6 +489,8 @@ github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8 h1:IMfrF github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8/go.mod h1:LFyLie6XcDbyKGeVK6bHe+9aJTYCxWLBg5IrJZOaXKA= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= +github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81 h1:7/v8q9XGFa6q5Ap4Z/OhNkAMBaK5YeuEzwJt+NZdhiE= +github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81/go.mod h1:siLfyv2c92W1eN/R4QqG/+RjjX5W2+gCTRjZxBjI3TY= github.com/dolthub/swiss v0.2.1 h1:gs2osYs5SJkAaH5/ggVJqXQxRXtWshF6uE0lgR/Y3Gw= github.com/dolthub/swiss v0.2.1/go.mod h1:8AhKZZ1HK7g18j7v7k6c5cYIGEZJcPn0ARsai8cUrh0= github.com/drone/funcmap v0.0.0-20220929084810-72602997d16f h1:/jEs7lulqVO2u1+XI5rW4oFwIIusxuDOVKD9PAzlW2E= @@ -1323,48 +503,26 @@ github.com/eapache/go-resiliency v1.6.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6 github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws= github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= -github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= github.com/efficientgo/tools/core v0.0.0-20220225185207-fe763185946b h1:ZHiD4/yE4idlbqvAO6iYCOYRzOMRpxkW+FKasRA3tsQ= github.com/efficientgo/tools/core v0.0.0-20220225185207-fe763185946b/go.mod h1:OmVcnJopJL8d3X3sSXTiypGoUSgFq1aDGmlrdi9dn/M= -github.com/elastic/go-sysinfo v1.8.1/go.mod h1:JfllUnzoQV/JRYymbH3dO1yggI3mV2oTKSXsDHM+uIM= github.com/elastic/go-sysinfo v1.11.2 h1:mcm4OSYVMyws6+n2HIVMGkln5HOpo5Ie1ZmbbNn0jg4= github.com/elastic/go-sysinfo v1.11.2/go.mod h1:GKqR8bbMK/1ITnez9NIsIfXQr25aLhRJa7AfT8HpBFQ= -github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0= github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss= +github.com/elazarl/goproxy v1.3.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= -github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= -github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= -github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/fgprof v0.9.4 h1:ocDNwMFlnA0NU0zSB3I52xkO4sFXk80VK9lXjLClu88= github.com/felixge/fgprof v0.9.4/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/fluent/fluent-bit-go v0.0.0-20230731091245-a7a013e2473c h1:yKN46XJHYC/gvgH2UsisJ31+n4K3S7QYZSfU2uAWjuI= github.com/fluent/fluent-bit-go v0.0.0-20230731091245-a7a013e2473c/go.mod h1:L92h+dgwElEyUuShEwjbiHjseW410WIcNz+Bjutc8YQ= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= -github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2 h1:cZqz+yOJ/R64LcKjNQOdARott/jP7BnUQ9Ah7KaZCvw= -github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 h1:a9ENSRDFBUPkJ5lCgVZh26+ZbGyoVJG7yb5SSzF5H54= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= -github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsouza/fake-gcs-server v1.7.0 h1:Un0BXUXrRWYSmYyC1Rqm2e2WJfTPyDy/HGMz31emTi8= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= @@ -1387,14 +545,11 @@ github.com/go-fonts/liberation v0.3.2/go.mod h1:N0QsDLVUQPy3UYg9XAc3Uh3UDMp2Z7M1 github.com/go-fonts/stix v0.1.0 h1:UlZlgrvvmT/58o573ot7NFw0vZasZ5I6bcIft/oMdgg= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= -github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= github.com/go-latex/latex v0.0.0-20231108140139-5c1ce85aa4ea h1:DfZQkvEbdmOe+JK2TMtBM+0I9GSdzE2y/L1/AmD8xKc= github.com/go-latex/latex v0.0.0-20231108140139-5c1ce85aa4ea/go.mod h1:Y7Vld91/HRbTBm7JwoI7HejdDB0u+e9AUBO9MB7yuZk= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -1411,190 +566,129 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-resty/resty/v2 v2.12.0 h1:rsVL8P90LFvkUYq/V5BTVe203WfRIU4gvcf+yfzJzGA= -github.com/go-resty/resty/v2 v2.12.0/go.mod h1:o0yGPrkS3lOe1+eFajk6kBW8ScXzwU3hD69/gt2yB/0= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= -github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= -github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= -github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= -github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= -github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= -github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= -github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccmack/gocc v0.0.0-20230228185258-2292f9e40198 h1:FSii2UQeSLngl3jFoR4tUKZLprO7qUlh/TKKticc0BM= github.com/goccmack/gocc v0.0.0-20230228185258-2292f9e40198/go.mod h1:DTh/Y2+NbnOVVoypCCQrovMPDKUGp4yZpSbWg5D0XIM= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE= github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54= github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng= github.com/gocql/gocql v0.0.0-20200526081602-cd04bd7f22a7 h1:TvUE5vjfoa7fFHMlmGOk0CsauNj1w4yJjR9+/GnWVCw= github.com/gocql/gocql v0.0.0-20200526081602-cd04bd7f22a7/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= +github.com/gocraft/dbr/v2 v2.7.2 h1:ccUxMuz6RdZvD7VPhMRRMSS/ECF3gytPhPtcavjktHk= +github.com/gocraft/dbr/v2 v2.7.2/go.mod h1:5bCqyIXO5fYn3jEp/L06QF4K1siFdhxChMjdNu6YJrg= github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= -github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.3/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12 h1:uK3X/2mt4tbSGoHvbLBHUny7CKiuwUip3MArtukol4E= github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= -github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386 h1:EcQR3gusLHN46TAD+G+EbaaqJArt5vHhNpXAa12PQf4= -github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= -github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-jsonnet v0.18.0 h1:/6pTy6g+Jh1a1I2UMoAODkqELFiVIdOxbNwv0DDzoOg= github.com/google/go-jsonnet v0.18.0/go.mod h1:C3fTzyVJDslXdiTqw/bTFk7vSGyCtH3MGRbDfvEwGd0= -github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= -github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/go-pkcs11 v0.3.0 h1:PVRnTgtArZ3QQqTGtbtjtnIkzl2iY2kt24yqbrf7td8= github.com/google/go-pkcs11 v0.3.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= -github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= -github.com/google/pprof v0.0.0-20240416155748-26353dc0451f/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/enterprise-certificate-proxy v0.3.3/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= -github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= -github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= -github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= -github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E= -github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= -github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk= +github.com/googleapis/cloud-bigtable-clients-test v0.0.2 h1:S+sCHWAiAc+urcEnvg5JYJUOdlQEm/SEzQ/c/IdAH5M= +github.com/googleapis/cloud-bigtable-clients-test v0.0.2/go.mod h1:mk3CrkrouRgtnhID6UZQDK3DrFFa7cYCAJcEmNsHYrY= github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0= github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA= github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720 h1:zC34cGQu69FG7qzJ3WiKW244WfhDC3xxYMeNOX2gtUQ= github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gophercloud/gophercloud v1.11.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= -github.com/gophercloud/gophercloud v1.13.0 h1:8iY9d1DAbzMW6Vok1AxbbK5ZaUjzMp0tdyt4fX9IeJ0= -github.com/gophercloud/gophercloud v1.13.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/alerting v0.0.0-20250117230852-a5e8136407d4 h1:616HUg7WVyLJLtVvk2pZ845M4Gk0fswgTwEZSuAZbJU= -github.com/grafana/alerting v0.0.0-20250117230852-a5e8136407d4/go.mod h1:QsnoKX/iYZxA4Cv+H+wC7uxutBD8qi8ZW5UJvD2TYmU= -github.com/grafana/authlib v0.0.0-20250120144156-d6737a7dc8f5/go.mod h1:V63rh3udd7sqXJeaG+nGUmViwVnM/bY6t8U9Tols2GU= +github.com/grafana/alerting v0.0.0-20250129195454-3e5b80036b7a/go.mod h1:QsnoKX/iYZxA4Cv+H+wC7uxutBD8qi8ZW5UJvD2TYmU= +github.com/grafana/authlib v0.0.0-20250123104008-e99947858901/go.mod h1:/gYfphsNu9v1qYWXxpv1NSvMEMSwvdf8qb8YlgwIRl8= github.com/grafana/authlib/types v0.0.0-20250120144156-d6737a7dc8f5/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM= +github.com/grafana/authlib/types v0.0.0-20250120145936-5f0e28e7a87c/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM= github.com/grafana/cloudflare-go v0.0.0-20230110200409-c627cf6792f2 h1:qhugDMdQ4Vp68H0tp/0iN17DM2ehRo1rLEdOFe/gB8I= github.com/grafana/cloudflare-go v0.0.0-20230110200409-c627cf6792f2/go.mod h1:w/aiO1POVIeXUQyl0VQSZjl5OAGDTL5aX+4v0RA1tcw= +github.com/grafana/cog v0.0.23 h1:/0CCJ24Z8XXM2DnboSd2FzoIswUroqIZzVr8oJWmMQs= +github.com/grafana/cog v0.0.23/go.mod h1:jrS9indvWuDs60RHEZpLaAkmZdgyoLKMOEUT0jiB1t0= github.com/grafana/go-gelf/v2 v2.0.1 h1:BOChP0h/jLeD+7F9mL7tq10xVkDG15he3T1zHuQaWak= github.com/grafana/go-gelf/v2 v2.0.1/go.mod h1:lexHie0xzYGwCgiRGcvZ723bSNyNI8ZRD4s0CLobh90= -github.com/grafana/grafana-app-sdk v0.29.0/go.mod h1:XLt308EmK6kvqPlzjUyXxbwZKEk2vur/eiypUNDay5I= -github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= +github.com/grafana/grafana-plugin-sdk-go v0.263.0/go.mod h1:U43Cnrj/9DNYyvFcNdeUWNjMXTKNB0jcTcQGpWKd2gw= +github.com/grafana/grafana/apps/advisor v0.0.0-20250123151950-b066a6313173/go.mod h1:goSDiy3jtC2cp8wjpPZdUHRENcoSUHae1/Px/MDfddA= +github.com/grafana/grafana/apps/advisor v0.0.0-20250220154326-6e5de80ef295/go.mod h1:9I1dKV3Dqr0NPR9Af0WJGxOytp5/6W3JLiNChOz8r+c= +github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250121113133-e747350fee2d/go.mod h1:AvleS6icyPmcBjihtx5jYEvdzLmHGBp66NuE0AMR57A= +github.com/grafana/grafana/apps/investigation v0.0.0-20250121113133-e747350fee2d/go.mod h1:HQprw3MmiYj5OUV9CZnkwA1FKDZBmYACuAB3oDvUOmI= +github.com/grafana/grafana/apps/playlist v0.0.0-20250121113133-e747350fee2d/go.mod h1:DjJe5osrW/BKrzN9hAAOSElNWutj1bcriExa7iDP7kA= +github.com/grafana/grafana/pkg/aggregator v0.0.0-20250121113133-e747350fee2d/go.mod h1:1sq0guad+G4SUTlBgx7SXfhnzy7D86K/LcVOtiQCiMA= +github.com/grafana/grafana/pkg/build v0.0.0-20250220114259-be81314e2118/go.mod h1:STVpVboMYeBAfyn6Zw6XHhTHqUxzMy7pzRiVgk1l0W0= +github.com/grafana/grafana/pkg/semconv v0.0.0-20250121113133-e747350fee2d/go.mod h1:tfLnBpPYgwrBMRz4EXqPCZJyCjEG4Ev37FSlXnocJ2c= +github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250121113133-e747350fee2d/go.mod h1:CXpwZ3Mkw6xVlGKc0SqUxqXCP3Uv182q6qAQnLaLxRg= github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0 h1:bjh0PVYSVVFxzINqPFYJmAmJNrWPgnVjuSdYJGHmtFU= github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0/go.mod h1:7t5XR+2IA8P2qggOAHTj/GCZfoLBle3OvNSYh1VkRBU= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hamba/avro/v2 v2.27.0 h1:IAM4lQ0VzUIKBuo4qlAiLKfqALSrFC+zi1iseTtbBKU= github.com/hamba/avro/v2 v2.27.0/go.mod h1:jN209lopfllfrz7IGoZErlDz+AyUJ3vrBePQFZwYf5I= -github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/api v1.28.2/go.mod h1:KyzqzgMEya+IZPcD65YFoOVAgPpbfERu4I/tzG6/ueE= -github.com/hashicorp/consul/sdk v0.16.0/go.mod h1:7pxqqhqoaPqnBnzXD1StKed62LqJeClzVsUEy85Zr0A= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-msgpack/v2 v2.1.1 h1:xQEY9yB2wnHitoSzk/B9UjXWRQ67QKu5AOm8aFp8N3I= github.com/hashicorp/go-msgpack/v2 v2.1.1/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= -github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= -github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= -github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/nomad/api v0.0.0-20240418183417-ea5f2f6748c7 h1:pjE59CS2C9Bg+Xby0ROrnZSSBWtKwx3Sf9gqsrvIFSA= -github.com/hashicorp/nomad/api v0.0.0-20240418183417-ea5f2f6748c7/go.mod h1:svtxn6QnrQ69P23VvIWMR34tg3vmwLz4UdUzm1dSCgE= github.com/hashicorp/raft v1.7.0 h1:4u24Qn6lQ6uwziM++UgsyiT64Q8GyRn43CV41qPiz1o= github.com/hashicorp/raft v1.7.0/go.mod h1:N1sKh6Vn47mrWvEArQgILTyng8GoDRNYlgKyK7PMjs0= github.com/hashicorp/raft-wal v0.4.1 h1:aU8XZ6x8R9BAIB/83Z1dTDtXvDVmv9YVYeXxd/1QBSA= github.com/hashicorp/raft-wal v0.4.1/go.mod h1:A6vP5o8hGOs1LHfC1Okh9xPwWDcmb6Vvuz/QyqUXlOE= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/heroku/x v0.0.61 h1:yfoAAtnFWSFZj+UlS+RZL/h8QYEp1R4wHVEg0G+Hwh4= github.com/heroku/x v0.0.61/go.mod h1:C7xYbpMdond+s6L5VpniDUSVPRwm3kZum1o7XiD5ZHk= -github.com/hetznercloud/hcloud-go/v2 v2.7.2 h1:UlE7n1GQZacCfyjv9tDVUN7HZfOXErPIfM/M039u9A0= -github.com/hetznercloud/hcloud-go/v2 v2.7.2/go.mod h1:49tIV+pXRJTUC7fbFZ03s45LKqSQdOPP5y91eOnJo/k= -github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hudl/fargo v1.4.0 h1:ZDDILMbB37UlAVLlWcJ2Iz1XuahZZTDZfdCKeclfq2s= -github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 h1:KwWnWVWCNtNq/ewIX7HIKnELmEx2nDP42yskD/pi7QE= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= -github.com/influxdata/influxdb v1.7.6 h1:8mQ7A/V+3noMGCt/P9pD09ISaiz9XvgCk303UYA3gcs= github.com/influxdata/influxdb v1.7.7 h1:UvNzAPfBrKMENVbQ4mr4ccA9sW+W1Ihl0Yh1s0BiVAg= -github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= -github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/tdigest v0.0.2-0.20210216194612-fc98d27c9e8b h1:i44CesU68ZBRvtCjBi3QSosCIKrjmMbYlQMFAwVLds4= github.com/influxdata/tdigest v0.0.2-0.20210216194612-fc98d27c9e8b/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= github.com/influxdata/telegraf v1.16.3 h1:x0qeuSGGMg5y+YqP/5ZHwXZu3bcBrO8AAQOTNlYEb1c= github.com/influxdata/telegraf v1.16.3/go.mod h1:fX/6k7qpIqzVPWyeIamb0wN5hbwc0ANUaTS80lPYFB8= -github.com/ionos-cloud/sdk-go/v6 v6.1.11 h1:J/uRN4UWO3wCyGOeDdMKv8LWRzKu6UIkLEaes38Kzh8= -github.com/ionos-cloud/sdk-go/v6 v6.1.11/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= +github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw= github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= -github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= +github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY= github.com/jackspirou/syscerts v0.0.0-20160531025014-b68f5469dff1 h1:9Xm8CKtMZIXgcopfdWk/qZ1rt0HjMgfMR9nxxSeK6vk= github.com/jackspirou/syscerts v0.0.0-20160531025014-b68f5469dff1/go.mod h1:zuHl3Hh+e9P6gmBPvcqR1HjkaWHC/csgyskg6IaFKFo= github.com/jaegertracing/jaeger v1.57.0 h1:3wDtUUPs6NRYH7+d+y8MilDkLHdpPrVlQ2wbcsA62bs= github.com/jaegertracing/jaeger v1.57.0/go.mod h1:p/1fxIU9hKHl7qEhKC72p2ZYVhvvZvNB73y6V7YyuTs= -github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= -github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= -github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= github.com/jedib0t/go-pretty/v6 v6.2.4 h1:wdaj2KHD2W+mz8JgJ/Q6L/T5dB7kyqEFI16eLq7GEmk= github.com/jedib0t/go-pretty/v6 v6.2.4/go.mod h1:+nE9fyyHGil+PuISTCrp7avEdo6bqoMwqZnuiK2r2a0= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.1-0.20181029123624-5de817a9aa20/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jeremywohl/flatten v1.0.1 h1:LrsxmB3hfwJuE+ptGOijix1PIfOoKLJ3Uee/mzbgtrs= +github.com/jeremywohl/flatten v1.0.1/go.mod h1:4AmD/VxjWcI5SRB0n6szE2A6s2fsNHDLO0nAlMHgfLQ= github.com/jhump/gopoet v0.1.0 h1:gYjOPnzHd2nzB37xYQZxj4EIQNpBrBskRqQQ3q4ZgSg= github.com/jhump/goprotoc v0.5.0 h1:Y1UgUX+txUznfqcGdDef8ZOVlyQvnV0pKWZH08RmZuo= -github.com/jmattheis/goverter v1.4.0 h1:SrboBYMpGkj1XSgFhWwqzdP024zIa1+58YzUm+0jcBE= -github.com/jmattheis/goverter v1.4.0/go.mod h1:iVIl/4qItWjWj2g3vjouGoYensJbRqDHpzlEVMHHFeY= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= @@ -1610,15 +704,12 @@ github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d h1:c93kUJDtVAXFEhsCh5 github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5 h1:PJr+ZMXIecYc1Ey2zucXdR73SMBtgjPgwa31099IMv0= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= -github.com/karlseguin/ccache/v3 v3.0.5/go.mod h1:qxC372+Qn+IBj8Pe3KvGjHPj0sWwEF7AeZVhsNPZ6uY= github.com/kataras/blocks v0.0.7 h1:cF3RDY/vxnSRezc7vLFlQFTYXG/yAr1o7WImJuZbzC4= github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I= github.com/kataras/golog v0.1.9 h1:vLvSDpP7kihFGKFAvBSofYo7qZNULYSHOH2D7rPTKJk= github.com/kataras/golog v0.1.9/go.mod h1:jlpk/bOaYCyqDqH18pgDHdaJab72yBE6i0O3s30hpWY= github.com/kataras/iris/v12 v12.2.5 h1:R5UzUW4MIByBM6tKMG3UqJ7hL1JCEE+dkqQ8L72f6PU= github.com/kataras/iris/v12 v12.2.5/go.mod h1:bf3oblPF8tQmRgyPCzPZr0mLazvEDFgImdaGZYuN4hw= -github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9 h1:Vx8kDVhO2qepK8w44lBtp+RzN3ld743i+LYPzODJSpQ= -github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9/go.mod h1:ldkoR3iXABBeqlTibQ3MYaviA1oSlPvim6f55biwBh4= github.com/kataras/pio v0.0.12 h1:o52SfVYauS3J5X08fNjlGS5arXHjW/ItLkyLcKjoH6w= github.com/kataras/pio v0.0.12/go.mod h1:ODK/8XBhhQ5WqrAhKy+9lTPS7sBf6O3KcLhc9klfRcY= github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY= @@ -1632,15 +723,9 @@ github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3ro github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46 h1:veS9QfglfvqAw2e+eeNT/SbGySq8ajECXJ9e4fPoLhY= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= -github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= @@ -1650,33 +735,24 @@ github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkX github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= -github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4= github.com/kshvakov/clickhouse v1.3.5 h1:PDTYk9VYgbjPAWry3AoDREeMgOVUFij6bh6IjlloHL0= github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4= github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= -github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8= -github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= -github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= -github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= -github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= -github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-syslog/v4 v4.1.0 h1:Wsl194qyWXr7V6DrGWC3xmxA9Ra6XgWO+toNt2fmCaI= github.com/leodido/go-syslog/v4 v4.1.0/go.mod h1:eJ8rUfDN5OS6dOkCOBYlg2a+hbAg6pJa99QXXgMrd98= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b h1:11UHH39z1RhZ5dc4y4r/4koJo6IYFgTRMe/LlwRTEw0= github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b/go.mod h1:WZxr2/6a/Ar9bMDc2rN/LJrE/hF6bXE4LPyDSIxwAfg= -github.com/linode/linodego v1.32.0 h1:OmZzB3iON6uu84VtLFf64uKmAQqJJarvmsVguroioPI= -github.com/linode/linodego v1.32.0/go.mod h1:y8GDP9uLVH4jTB9qyrgw79qfKdYJmNCGUOJmfuiOcmI= +github.com/linkedin/goavro/v2 v2.10.0/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA= github.com/logrusorgru/aurora/v3 v3.0.0 h1:R6zcoZZbvVcGMvDCKo45A9U/lzYyzl5NfYIvznmDfE4= github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMnOEbMWQtSEUgU64U4s+GHk7hZK+jtY= github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= github.com/lyft/protoc-gen-star v0.6.1 h1:erE0rdztuaDq3bpGifD95wfoPrSZc95nGA6tbiNYh6M= -github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/lyft/protoc-gen-star/v2 v2.0.4-0.20230330145011-496ad1ac90a4 h1:sIXJOMrYnQZJu7OB7ANSF4MYri2fTEGIsRLz6LwI4xE= github.com/lyft/protoc-gen-star/v2 v2.0.4-0.20230330145011-496ad1ac90a4/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw= @@ -1685,23 +761,12 @@ github.com/matryer/moq v0.3.3 h1:pScMH9VyrdT4S93yiLpVyU8rCDqGQr24uOyBxmktG5Q= github.com/matryer/moq v0.3.3/go.mod h1:RJ75ZZZD71hejp39j4crZLsEDszGk6iH4v4YsWFKH4s= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= -github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= -github.com/mfridman/xflag v0.0.0-20240825232106-efb77353e578 h1:CRrqlUmLebb/QjzRDWE0E66+YyN/v95+w6WyH9ju8/Y= -github.com/mfridman/xflag v0.0.0-20240825232106-efb77353e578/go.mod h1:/483ywM5ZO5SuMVjrIGquYNE5CzLrj5Ux/LxWWnjRaE= github.com/mfridman/xflag v0.1.0 h1:TWZrZwG1QklFX5S4j1vxfF1sZbZeZSGofMwPMLAF29M= github.com/mfridman/xflag v0.1.0/go.mod h1:/483ywM5ZO5SuMVjrIGquYNE5CzLrj5Ux/LxWWnjRaE= github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= -github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= -github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng= @@ -1709,37 +774,20 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2Em github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mithrandie/readline-csvq v1.3.0 h1:VTJEOGouJ8j27jJCD4kBBbNTxM0OdBvE1aY1tMhlqE8= github.com/mithrandie/readline-csvq v1.3.0/go.mod h1:FKyYqDgf/G4SNov7SMFXRWO6LQLXIOeTog/NB97FZl0= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5 h1:8Q0qkMVC/MmWkpIdlvZgcv2o2jrlF6zqVOh7W5YHdMA= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= -github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= -github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/mostynb/go-grpc-compression v1.2.3 h1:42/BKWMy0KEJGSdWvzqIyOZ95YcR9mLPqKctH7Uo//I= github.com/mostynb/go-grpc-compression v1.2.3/go.mod h1:AghIxF3P57umzqM9yz795+y1Vjs47Km/Y2FE6ouQ7Lg= github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8 h1:P48LjvUQpTReR3TQRbxSeSBsMXzfK0uol7eRcr7VBYQ= github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4 h1:dnMxwus89s86tI8rcGVp2HwZzlz7c5o92VOy7dSckBQ= -github.com/nats-io/jwt v1.2.2 h1:w3GMTO969dFg+UOKTmmyuu7IGdusK+7Ytlt//OYH/uU= -github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q= -github.com/nats-io/jwt/v2 v2.0.3 h1:i/O6cmIsjpcQyWDYNcq2JyZ3/VTF8SJ4JWluI5OhpvI= -github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= -github.com/nats-io/nats-server/v2 v2.5.0 h1:wsnVaaXH9VRSg+A2MVg5Q727/CqxnmPLGFQ3YZYKTQg= -github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g= -github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= github.com/nats-io/nats.go v1.34.0 h1:fnxnPCNiwIG5w08rlMcEKTUw4AV/nKyGCOJE8TdhSPk= github.com/nats-io/nats.go v1.34.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= -github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= -github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= @@ -1751,46 +799,6 @@ github.com/ncw/swift/v2 v2.0.2/go.mod h1:z0A9RVdYPjNjXVo2pDOPxZ4eu3oarO1P91fTItc github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0= -github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= -github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= -github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= -github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc= -github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= -github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= -github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= -github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= -github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= -github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= -github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= -github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= -github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= -github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= -github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= -github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= -github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= -github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= -github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= -github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.102.0 h1:R70PpK14trQfL/Vj5oAiGRqX09s2gOWuf6t1Ae5fevQ= github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.102.0/go.mod h1:xmy/yFFmB1Epy+czrYMbA+4xeOKvhFqNqYWU6qINeis= github.com/open-telemetry/opentelemetry-collector-contrib/exporter/zipkinexporter v0.102.0 h1:N3vWsp3xealy4AX8TovfHG5EKi/k7z+F/8LFP4SVAgo= @@ -1829,46 +837,26 @@ github.com/open-telemetry/opentelemetry-collector-contrib/receiver/opencensusrec github.com/open-telemetry/opentelemetry-collector-contrib/receiver/opencensusreceiver v0.102.0/go.mod h1:WNFjuquVqyi+WEoa6L0J3DzPLRsP24ZlbZYwKv49VwY= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.102.0 h1:Pemo9pZa3VMYdrM/bss3f0qqVyBzPSulOBQL8VQcgN8= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.102.0/go.mod h1:fvjAM+jOQdiXCmAENKH/eWxBBqTaImbq3lpoBI4X5Ek= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/openfga/api/proto v0.0.0-20240905181937-3583905f61a6/go.mod h1:gil5LBD8tSdFQbUkCQdnXsoeU9kDJdJgbGdHkgJfcd0= -github.com/openfga/api/proto v0.0.0-20240906203051-102620ef2a66/go.mod h1:gil5LBD8tSdFQbUkCQdnXsoeU9kDJdJgbGdHkgJfcd0= -github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20240926131254-992b301a003f/go.mod h1:ll/hN6kS4EE6B/7J/PbZqac9Nuv7ZHpI+Jfh36JLrbs= -github.com/openfga/openfga v1.6.2/go.mod h1:jzbEpheazf6MFjtanQt1rpxexSRzfa9057F7JlkMv2I= -github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= -github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= -github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w= github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo= github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU= github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o= -github.com/ovh/go-ovh v1.4.3 h1:Gs3V823zwTFpzgGLZNI6ILS4rmxZgJwJCz54Er9LwD0= -github.com/ovh/go-ovh v1.4.3/go.mod h1:AkPXVtgwB6xlKblMjRKJJmjRp+ogrE7fz2lVgcQY8SY= github.com/parquet-go/parquet-go v0.23.0 h1:dyEU5oiHCtbASyItMCD2tXtT2nPmoPbKpqf0+nnGrmk= github.com/parquet-go/parquet-go v0.23.0/go.mod h1:MnwbUcFHU6uBYMymKAlPPAw9yh3kE1wWl6Gl1uLdkNk= github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= -github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30 h1:BHT1/DKsYDGkUgQ2jmMaozVcdk+sVfz0+1ZJq4zkWgw= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/performancecopilot/speed/v4 v4.0.0 h1:VxEDCmdkfbQYDlcr/GC9YoN9PQ6p8ulk9xVsepYy9ZY= -github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phpdave11/gofpdf v1.4.2 h1:KPKiIbfwbvC/wOncwhrpRdXVj2CZTCFlw4wnoyjtHfQ= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= -github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= -github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= @@ -1876,18 +864,10 @@ github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c h1:NRoLoZvkB github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= -github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/pressly/goose/v3 v3.22.1/go.mod h1:xtMpbstWyCpyH+0cxLTMCENWBG+0CSxvTsXhW95d5eo= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= -github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/common/assets v0.2.0 h1:0P5OrzoHrYBOSM1OigWL3mY8ZvV2N4zIE/5AahrSrfM= -github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/prometheus v0.52.0 h1:f7kHJgr7+zShpWdTCeKqbCWR7nKTScgLYQwRux9h1V0= -github.com/prometheus/prometheus v0.52.0/go.mod h1:3z74cVsmVH0iXOR5QBjB7Pa6A0KJeEAK5A6UsmAFb1g= github.com/prometheus/statsd_exporter v0.26.0 h1:SQl3M6suC6NWQYEzOvIv+EF6dAMYEqIuZy+o4H9F5Ig= github.com/prometheus/statsd_exporter v0.26.0/go.mod h1:GXFLADOmBTVDrHc7b04nX8ooq3azG61pnECNqT7O5DM= github.com/rabbitmq/amqp091-go v1.9.0 h1:qrQtyzB4H8BQgEuJwhmVQqVHB9O4+MNDJCCAcpc3Aoo= @@ -1903,14 +883,11 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99 github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245 h1:K1Xf3bKttbF+koVGaX5xngRIZ5bVjbmPnaxE/dR08uY= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk= github.com/sagikazarmark/crypt v0.19.0 h1:WMyLTjHBo64UvNcWqpzY3pbZTYgnemZU8FBZigKc42E= github.com/sagikazarmark/crypt v0.19.0/go.mod h1:c6vimRziqqERhtSe0MhIvzE1w54FrCHtrXb5NH/ja78= github.com/samuel/go-zookeeper v0.0.0-20190810000440-0ceca61e4d75 h1:cA+Ubq9qEVIQhIWvP2kNuSZ2CmnfBJFSRq+kO1pu2cc= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26 h1:F+GIVtGqCFxPxO46ujf8cEOP574MBoRm3gNbPXECbxs= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/schollz/progressbar/v3 v3.14.6 h1:GyjwcWBAf+GFDMLziwerKvpuS7ZF+mNTAXIB2aspiZs= @@ -1923,41 +900,22 @@ github.com/shirou/gopsutil/v4 v4.24.0-alpha.1 h1:lLPAdP4TpfgJ5byoc3EFwNSKZj8kCnD github.com/shirou/gopsutil/v4 v4.24.0-alpha.1/go.mod h1:GVpYUxBee6CTWux2/JslZ7fYPwqkQ8YDJSXmGAryYy4= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= -github.com/shoenig/test v1.7.1 h1:UJcjSAI3aUKx52kfcfhblgyhZceouhvvs3OYdWgn+PY= -github.com/shoenig/test v1.7.1/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stoewer/parquet-cli v0.0.7 h1:rhdZODIbyMS3twr4OM3am8BPPT5pbfMcHLH93whDM5o= github.com/stoewer/parquet-cli v0.0.7/go.mod h1:bskxHdj8q3H1EmfuCqjViFoeO3NEvs5lzZAQvI8Nfjk= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= -github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e h1:mOtuXaRAbVZsxAHVdPR3IjfmN8T1h2iczJLynhLybf8= -github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/substrait-io/substrait v0.57.1 h1:GW8nnYfSowMseHR8Os82/X6lNtQGIK7p4p+lr6r+auw= github.com/substrait-io/substrait v0.57.1/go.mod h1:q9s+tjo+gK0lsA+SqYB0lhojNuxvdPdfYlGUP0hjbrA= -github.com/substrait-io/substrait-go v0.4.2/go.mod h1:qhpnLmrcvAnlZsUyPXZRqldiHapPTXC3t7xFgDi3aQg= github.com/substrait-io/substrait-go v1.2.0 h1:3ZNRkc8FYD7ifCagKEOZQtUcgMceMQfwo2N1NGaK4Q4= github.com/substrait-io/substrait-go v1.2.0/go.mod h1:IPsy24rdjp/buXR+T8ENl6QCnSCS6h+uM8P+GaZez7c= github.com/tdewolff/minify/v2 v2.12.8 h1:Q2BqOTmlMjoutkuD/OPCnJUpIqrzT3nRPkw+q+KpXS0= github.com/tdewolff/minify/v2 v2.12.8/go.mod h1:YRgk7CC21LZnbuke2fmYnCTq+zhCgpb0yJACOTUNJ1E= -github.com/tdewolff/minify/v2 v2.12.9 h1:dvn5MtmuQ/DFMwqf5j8QhEVpPX6fi3WGImhv8RUB4zA= -github.com/tdewolff/minify/v2 v2.12.9/go.mod h1:qOqdlDfL+7v0/fyymB+OP497nIxJYSvX4MQWA8OoiXU= github.com/tdewolff/parse/v2 v2.6.7 h1:WrFllrqmzAcrKHzoYgMupqgUBIfBVOb0yscFzDf8bBg= github.com/tdewolff/parse/v2 v2.6.7/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM= -github.com/tdewolff/parse/v2 v2.6.8 h1:mhNZXYCx//xG7Yq2e/kVLNZw4YfYmeHbhx+Zc0OvFMA= -github.com/tdewolff/parse/v2 v2.6.8/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM= -github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94= -github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -1985,8 +943,6 @@ github.com/uber-go/atomic v1.4.0 h1:yOuPqEq4ovnhEjpHmfFwsqBXDYbQeT6Nb0bwD6XnD5o= github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/vburenin/ifacemaker v1.2.1 h1:3Vq8B/bfBgjWTkv+jDg4dVL1KHt3k1K4lO7XRxYA2sk= -github.com/vburenin/ifacemaker v1.2.1/go.mod h1:5WqrzX2aD7/hi+okBjcaEQJMg4lDGrpuEX3B8L4Wgrs= github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw= github.com/vertica/vertica-sql-go v1.3.3/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4= github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= @@ -1999,9 +955,7 @@ github.com/willf/bloom v2.0.3+incompatible h1:QDacWdqcAUI1MPOwIQZRy9kOR7yxfyEmxX github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8= github.com/xanzy/go-gitlab v0.15.0 h1:rWtwKTgEnXyNUGrOArN7yyc3THRkpYcKXIXia9abywQ= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= @@ -2014,12 +968,8 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ github.com/xhit/go-str2duration v1.2.0 h1:BcV5u025cITWxEQKGWr1URRzrcXtu7uk8+luz3Yuhwc= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= -github.com/ydb-platform/ydb-go-genproto v0.0.0-20240528144234-5d5a685e41f7 h1:nL8XwD6fSst7xFUirkaWJmE7kM0CdWRYgu6+YQer1d4= -github.com/ydb-platform/ydb-go-genproto v0.0.0-20240528144234-5d5a685e41f7/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I= github.com/ydb-platform/ydb-go-genproto v0.0.0-20241112172322-ea1f63298f77 h1:LY6cI8cP4B9rrpTleZk95+08kl2gF4rixG7+V/dwL6Q= github.com/ydb-platform/ydb-go-genproto v0.0.0-20241112172322-ea1f63298f77/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I= -github.com/ydb-platform/ydb-go-sdk/v3 v3.80.2 h1:qmZGJQCNx09/r0HDIT2cDDogiOvWikELy13ubM2CFS8= -github.com/ydb-platform/ydb-go-sdk/v3 v3.80.2/go.mod h1:IHwuXyolaAmGK2Dp7+dlhsnXphG1pwCoaP/OITT3+tU= github.com/ydb-platform/ydb-go-sdk/v3 v3.95.3 h1:pgsQPKSFfRFy3JSZMUReCF4CSEwgxA+a5GymvtyRJO0= github.com/ydb-platform/ydb-go-sdk/v3 v3.95.3/go.mod h1:WiezFS4YCi2vHqbYGQkeu/2MDBYFLix6dIs/pd87Yck= github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA= @@ -2032,15 +982,10 @@ github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRK github.com/zenazn/goji v1.0.1 h1:4lbD8Mx2h7IvloP7r2C0D6ltZP6Ufip8Hn0wmSK5LR8= github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/96gOZa3F5dpJr/vEiDQYlNb/y2uNs= -go.einride.tech/aip v0.66.0 h1:XfV+NQX6L7EOYK11yoHHFtndeaWh3KbD9/cN/6iWEt8= -go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= +go.einride.tech/aip v0.68.0 h1:4seM66oLzTpz50u4K1zlJyOXQ3tCzcJN7I22tKkjipw= +go.einride.tech/aip v0.68.0/go.mod h1:7y9FF8VtPWqpxuAxl0KQWqaULxW4zFIesD6zF5RIHHg= go.etcd.io/gofail v0.1.0 h1:XItAMIhOojXFQMgrxjnd2EIIHun/d5qL0Pf7FzVTkFg= go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M= -go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opentelemetry.io/collector v0.102.1 h1:M/ciCcReQsSDYG9bJ2Qwqk7pQILDJ2bM/l0MdeCAvJE= go.opentelemetry.io/collector v0.102.1/go.mod h1:yF1lDRgL/Eksb4/LUnkMjvLvHHpi6wqBVlzp+dACnPM= @@ -2092,21 +1037,16 @@ go.opentelemetry.io/collector/extension v0.102.1 h1:gAvE3w15q+Vv0Tj100jzcDpeMTyc go.opentelemetry.io/collector/extension v0.102.1/go.mod h1:XBxUOXjZpwYLZYOK5u3GWlbBTOKmzStY5eU1R/aXkIo= go.opentelemetry.io/collector/extension/auth v0.102.1 h1:GP6oBmpFJjxuVruPb9X40bdf6PNu9779i8anxa+wW6U= go.opentelemetry.io/collector/extension/auth v0.102.1/go.mod h1:U2JWz8AW1QXX2Ap3ofzo5Dn2fZU/Lglld97Vbh8BZS0= -go.opentelemetry.io/collector/featuregate v1.5.0/go.mod h1:w7nUODKxEi3FLf1HslCiE6YWtMtOOrMnSwsDam8Mg9w= go.opentelemetry.io/collector/featuregate v1.9.0 h1:mC4/HnR5cx/kkG1RKOQAvHxxg5Ktmd9gpFdttPEXQtA= go.opentelemetry.io/collector/featuregate v1.9.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= go.opentelemetry.io/collector/otelcol v0.102.1 h1:JdRG3ven+c5k703QpZG5bxJi4JJOnWaNP/EJvN+oYnI= go.opentelemetry.io/collector/otelcol v0.102.1/go.mod h1:kHf9KBXOLZXajR1On8XJbBBGcgh2I2+/mVVroPzOLJU= -go.opentelemetry.io/collector/pdata v1.5.0/go.mod h1:TYj8aKRWZyT/KuKQXKyqSEvK/GV+slFaDMEI+Ke64Yw= go.opentelemetry.io/collector/processor v0.102.1 h1:79NWs7kTgmgxOIQacuZyDf+mYWuoJZS07SHwZT7sZ4Y= go.opentelemetry.io/collector/processor v0.102.1/go.mod h1:sNM41tEHgv3YA/Dz9/6F8oCeObrqnKCGOMs7wS6Ldus= go.opentelemetry.io/collector/receiver v0.102.1 h1:353t4U3o0RdU007JcQ4sRRzl72GHCJZwXDr8cCOcEbI= go.opentelemetry.io/collector/receiver v0.102.1/go.mod h1:pYjMzUkvUlxJ8xt+VbI1to8HMtVlv8AW/K/2GQQOTB0= go.opentelemetry.io/collector/receiver/otlpreceiver v0.102.1 h1:65/8lkVmOu6gwBw99W+QUQBeDC2qVTwlaiqy7/SpauY= go.opentelemetry.io/collector/receiver/otlpreceiver v0.102.1/go.mod h1:0hmxfFSSqKJjRGvgYjp/XvptbAgLhLguwNgJqMp7zd0= -go.opentelemetry.io/collector/semconv v0.98.0/go.mod h1:8ElcRZ8Cdw5JnvhTOQOdYizkJaQ10Z2fS+R6djOnj6A= -go.opentelemetry.io/collector/semconv v0.105.0 h1:8p6dZ3JfxFTjbY38d8xlQGB1TQ3nPUvs+D0RERniZ1g= -go.opentelemetry.io/collector/semconv v0.105.0/go.mod h1:yMVUCNoQPZVq/IPfrHrnntZTWsLf5YGZ7qwKulIl5hw= go.opentelemetry.io/collector/semconv v0.116.0 h1:63xCZomsKJAWmKGWD3lnORiE3WKW6AO4LjnzcHzGx3Y= go.opentelemetry.io/collector/semconv v0.116.0/go.mod h1:N6XE8Q0JKgBN2fAhkUQtqK9LT7rEGR6+Wu/Rtbal1iI= go.opentelemetry.io/collector/service v0.102.1 h1:Lg7qrC4Zctd/OAlkpdsaZaUY+jLEGLLnOigfBLP2GW8= @@ -2115,31 +1055,22 @@ go.opentelemetry.io/contrib/bridges/prometheus v0.53.0 h1:BdkKDtcrHThgjcEia1737O go.opentelemetry.io/contrib/bridges/prometheus v0.53.0/go.mod h1:ZkhVxcJgeXlL/lVyT/vxNHVFiSG5qOaDwYaSgD8IfZo= go.opentelemetry.io/contrib/config v0.7.0 h1:b1rK5tGTuhhPirJiMxOcyQfZs76j2VapY6ODn3b2Dbs= go.opentelemetry.io/contrib/config v0.7.0/go.mod h1:8tdiFd8N5etOi3XzBmAoMxplEzI3TcL8dU5rM5/xcOQ= -go.opentelemetry.io/contrib/detectors/gcp v1.31.0 h1:G1JQOreVrfhRkner+l4mrGxmfqYCAuy76asTDAo0xsA= -go.opentelemetry.io/contrib/detectors/gcp v1.31.0/go.mod h1:tzQL6E1l+iV44YFTkcAeNQqzXUiekSYP9jjJjXwEd00= +go.opentelemetry.io/contrib/detectors/gcp v1.32.0 h1:P78qWqkLSShicHmAzfECaTgvslqHxblNE9j62Ws1NK8= +go.opentelemetry.io/contrib/detectors/gcp v1.32.0/go.mod h1:TVqo0Sda4Cv8gCIixd7LuLwW4EylumVWfhjZJjDD4DU= go.opentelemetry.io/contrib/exporters/autoexport v0.53.0 h1:13K+tY7E8GJInkrvRiPAhC0gi/7vKjzDNhtmCf+QXG8= go.opentelemetry.io/contrib/exporters/autoexport v0.53.0/go.mod h1:lyQF6xQ4iDnMg4sccNdFs1zf62xd79YI8vZqKjOTwMs= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.58.0/go.mod h1:uosvgpqTcTXtcPQORTbEkZNDQTCDOgTz1fe6aLSyqrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= go.opentelemetry.io/contrib/propagators/b3 v1.27.0 h1:IjgxbomVrV9za6bRi8fWCNXENs0co37SZedQilP2hm0= go.opentelemetry.io/contrib/propagators/b3 v1.27.0/go.mod h1:Dv9obQz25lCisDvvs4dy28UPh974CxkahRDUPsY7y9E= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= -go.opentelemetry.io/otel v1.23.0/go.mod h1:YCycw9ZeKhcJFrb34iVSkyT0iczq/zYDtZYFufObyB0= +go.opentelemetry.io/contrib/propagators/jaeger v1.33.0/go.mod h1:ku/EpGk44S5lyVMbtJRK2KFOnXEehxf6SDnhu1eZmjA= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.27.0/go.mod h1:IohbtCIY5Erb6wKnDddXOMNlG7GwyZnkrgcqjPmhpaA= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= -go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= -go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= -go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= go.opentelemetry.io/otel/bridge/opencensus v1.27.0 h1:ao9aGGHd+G4YfjBpGs6vbkvt5hoC67STlJA9fCnOAcs= go.opentelemetry.io/otel/bridge/opencensus v1.27.0/go.mod h1:uRvWtAAXzyVOST0WMPX5JHGBaAvBws+2F8PcC5gMnTk= go.opentelemetry.io/otel/bridge/opentracing v1.26.0 h1:Q/dHj0DOhfLMAs5u5ucAbC7gy66x9xxsZRLpHCJ4XhI= @@ -2148,10 +1079,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2g go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0/go.mod h1:yeGZANgEcpdx/WK0IvvRFC+2oLiMS2u4L/0Rj2M2Qr0= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 h1:aLmmtjRke7LPDQ3lvpFz+kNEH43faFhzW7v8BFIEydg= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0/go.mod h1:TC1pyCt6G9Sjb4bQpShH+P5R53pO6ZuGnHuuln9xMeE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.25.0/go.mod h1:8GlBGcDk8KKi7n+2S4BT/CPZQYH3erLu0/k64r1MYgo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0/go.mod h1:e7ciERRhZaOZXVjx5MiL8TK5+Xv7G5Gv5PA2ZDEJdL8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= go.opentelemetry.io/otel/exporters/prometheus v0.50.0 h1:2Ewsda6hejmbhGFyUvWZjUThC98Cf8Zy6g0zkIimOng= go.opentelemetry.io/otel/exporters/prometheus v0.50.0/go.mod h1:pMm5PkUo5YwbLiuEf7t2xg4wbP0/eSJrMxIMxKosynY= go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.4.0 h1:0MH3f8lZrflbUWXVxyBg/zviDFdGE062uKh5+fu8Vv0= @@ -2160,106 +1089,47 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0 h1:BJee2iLkfRfl9l go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0/go.mod h1:DIzlHs3DRscCIBU3Y9YSzPfScwnYnzfnCd4g8zA7bZc= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 h1:EVSnY9JbEEW92bEkIYOVMw4q1WJxIAGoFTrtYOzWuRQ= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0/go.mod h1:Ea1N1QQryNXpCD0I1fdLibBAIpQuBkznMmkdKrapk1Y= -go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= -go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms20Jb7Bbp+HiTo= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= -go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= -go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= -go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= -go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= -go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= -go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= -go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5UkggkflQwDScNUsk= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= -go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= -go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= -go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= -go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= -go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= -go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= -go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/exp v0.0.0-20220328175248-053ad81199eb/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= -golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= -golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= -golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220818022119-ed83ed61efb9/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= @@ -2267,354 +1137,101 @@ golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= -golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= -golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= -golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= -golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= -golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/plot v0.14.0 h1:+LBDVFYwFe4LHhdP8coW6296MBEY4nQ+Y4vuUpJopcE= gonum.org/v1/plot v0.14.0/go.mod h1:MLdR9424SJed+5VqC6MsouEpig9pZX2VZ57H9ko2bXU= -google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= -google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750= -google.golang.org/api v0.139.0/go.mod h1:CVagp6Eekz9CjGZ718Z+sloknzkDJE7Vc1Ckj9+viBk= -google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= -google.golang.org/api v0.150.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg= -google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= -google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= -google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g= -google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= -google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= -google.golang.org/api v0.164.0/go.mod h1:2OatzO7ZDQsoS7IFf3rvsE17/TldiU3F/zxFHeqUB5o= -google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg= -google.golang.org/api v0.174.0/go.mod h1:aC7tB6j0HR1Nl0ni5ghpx6iLasmAX78Zkh/wgxAAjLg= -google.golang.org/api v0.177.0/go.mod h1:srbhue4MLjkjbkux5p3dw/ocYOSZTaIEvf7bCOnFQDw= -google.golang.org/api v0.187.0/go.mod h1:KIHlTc4x7N7gKKuVsdmfBXN13yEEWXWFURWY6SBp2gk= -google.golang.org/api v0.196.0/go.mod h1:g9IL21uGkYgvQ5BZg6BAtoGJQIm8r6EgaAbpNey5wBE= -google.golang.org/api v0.197.0/go.mod h1:AuOuo20GoQ331nq7DquGHlU6d+2wN2fZ8O0ta60nRNw= -google.golang.org/api v0.211.0/go.mod h1:XOloB4MXFH4UTlQSGuNUxw0UT74qdENK8d6JNsXKLi0= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= -google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= -google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= -google.golang.org/genproto v0.0.0-20230821184602-ccc8af3d0e93/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:CCviP9RmpZ1mxVr8MUjCnSiY09IbAXZxhLE6EhHIdPU= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= -google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= -google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= -google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY= -google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= -google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= -google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= -google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= -google.golang.org/genproto v0.0.0-20240205150955-31a09d347014/go.mod h1:xEgQu1e4stdSSsxPDK8Azkrk/ECl5HvdPf6nbZrTS5M= -google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:s7iA721uChleev562UJO2OYB0PPT9CMFjV+Ce7VJH5M= -google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:hL97c3SYopEHblzpxRL4lSs523++l8DYxGM1FQiYmb4= -google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= -google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= -google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= -google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= -google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405/go.mod h1:oT32Z4o8Zv2xPQTg0pbVaPr0MPOH6f14RgXt7zfIpwg= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI= -google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3/go.mod h1:k2dtGpRrbsSyKcNPKKI5sstZkrNCZwpU/ns96JoHbGg= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= -google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= -google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= -google.golang.org/genproto/googleapis/api v0.0.0-20240122161410-6c6643bf1457/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I= -google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:PVreiBMirk8ypES6aw9d4p6iiBNSIfZEBqr3UGoAi2E= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= -google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be/go.mod h1:dvdCTIoAGbkWbcIKBniID56/7XHTt6WfxXNMxuziJ+w= -google.golang.org/genproto/googleapis/api v0.0.0-20240429193739-8cf5692501f6/go.mod h1:10yRODfgim2/T8csjQsMPgZOMvtytXKTDRzH6HRGzRw= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= -google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4/go.mod h1:px9SlOOZBg1wM1zdnr8jEL4CNGUBZ+ZKYtNPApNQc4c= google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= -google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= -google.golang.org/genproto/googleapis/api v0.0.0-20241216192217-9240e9c98484/go.mod h1:KRUmxRI4JmbpAm8gcZM4Jsffi859fo5LQjILwuqj9z8= +google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY= google.golang.org/genproto/googleapis/api v0.0.0-20241219192143-6b3ec007d9bb/go.mod h1:E5//3O5ZIG2l71Xnt+P/CYUY8Bxs8E7WMoZ9tlcMbAY= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20231212172506-995d672761c0/go.mod h1:guYXGPwC6jwxgWKW5Y405fKWOFNwlvUlUnzyp9i0uqo= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:ZSvZ8l+AWJwXw91DoTjWjaVLpWU6o0eZ4YLYpH8aLeQ= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:SCz6T5xjNXM4QFPRwxHcfChp7V+9DcXR3ay2TkHR8Tg= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240205150955-31a09d347014/go.mod h1:EhZbXt+eY4Yr3YVaEGLdNZF5viWowOJZ8KTPqjYMKzg= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:vh/N7795ftP0AkN1w8XKqN4w1OdUKXW5Eummda+ofv8= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:IN9OQUXZ0xT+26MDwZL8fJcYw+y99b0eYPA2U15Jt8o= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250102185135-69823020774d h1:NZBSeFsuFS5YrgHMW/8xfTbzNXMshQPNgq2Yb7xipEs= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250102185135-69823020774d/go.mod h1:s4mHJ3FfG8P6A3O+gZ8TVqB3ufjOl9UG3ANCMMwCHmo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o= google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920183334-c177e329c48b/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240122161410-6c6643bf1457/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014/go.mod h1:SaPjaZGWb0lPqs6Ittu0spdfrOArqji4ZdeP5IC/9N4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240228201840-1f18d85a4ec2/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415141817-7cd4c1c1f9ec/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/genproto/googleapis/rpc v0.0.0-20241219192143-6b3ec007d9bb/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= -google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= -google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= -google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= -google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= -google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= -google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= -google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= -google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= -google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/telebot.v3 v3.2.1 h1:3I4LohaAyJBiivGmkfB+CiVu7QFOWkuZ4+KHgO/G3rs= -gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.3.2 h1:ytYb4rOqyp1TSa2EPvNVwtPQJctSELKaMyLfqNP4+34= honnef.co/go/tools v0.3.2/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= -howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= -k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= -k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= -k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= -k8s.io/code-generator v0.32.0 h1:s0lNN8VSWny8LBz5t5iy7MCdgwdOhdg7vAGVxvS+VWU= -k8s.io/code-generator v0.32.0/go.mod h1:b7Q7KMZkvsYFy72A79QYjiv4aTz3GvW0f1T3UfhFq4s= +k8s.io/code-generator v0.32.1 h1:4lw1kFNDuFYXquTkB7Sl5EwPMUP2yyW9hh6BnFfRZFY= +k8s.io/code-generator v0.32.1/go.mod h1:zaILfm00CVyP/6/pJMJ3zxRepXkxyDfUV5SNG4CjZI4= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6 h1:4s3/R4+OYYYUKptXPhZKjQ04WJ6EhQQVFdjOFvCazDk= -k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks= -k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 h1:si3PfKm8dDYxgfbeA6orqrtLkvvIeH8UqffFJDl0bz4= k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= -k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= -lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= -lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg= -modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= -modernc.org/cc/v3 v3.38.1/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= -modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= -modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= -modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI= -modernc.org/ccgo/v3 v3.0.0-20220910160915-348f15de615a/go.mod h1:8p47QxPkdugex9J4n9P2tLZ9bK01yngIVp00g4nomW0= modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM= -modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g= -modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= -modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= -modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA= -modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0= -modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= -modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= -modernc.org/libc v1.21.2/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= -modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= -modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= -modernc.org/libc v1.22.4/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= -modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/sqlite v1.18.2/go.mod h1:kvrTLEWgxUcHa2GfHBQtanR1H9ht3hTJNtKpzH9k1u0= -modernc.org/sqlite v1.21.2/go.mod h1:cxbLkB5WS32DnQqeH4h4o1B0eMr8W/y8/RGuxQ3JsC0= -modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k= -modernc.org/sqlite v1.34.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k= modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao= -modernc.org/tcl v1.13.2/go.mod h1:7CLiGIPo1M8Rv1Mitpv5akc2+8fxUd2y2UzC/MfMzy0= -modernc.org/tcl v1.15.1 h1:mOQwiEK4p7HruMZcwKTZPw/aqtGM4aY00uzWhlKKYws= -modernc.org/tcl v1.15.1/go.mod h1:aEjeGJX2gz1oWKOLDVZ2tnEWLUrIn8H+GFu+akoDhqs= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM= -modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE= -modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ= rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index 4df77aec6c3..57841bbbeb2 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -87,4 +87,8 @@ grafana::codegen:run pkg grafana::codegen:run pkg/apimachinery grafana::codegen:run pkg/aggregator +if [ -d "pkg/extensions/apis" ]; then + grafana::codegen:run pkg/extensions +fi + echo "done." diff --git a/kinds/accesspolicy/access_policy_kind.cue b/kinds/accesspolicy/access_policy_kind.cue deleted file mode 100644 index ed9d85a7d82..00000000000 --- a/kinds/accesspolicy/access_policy_kind.cue +++ /dev/null @@ -1,51 +0,0 @@ -package kind - -name: "AccessPolicy" -maturity: "merged" -description: "Access rules for a scope+role. NOTE there is a unique constraint on role+scope" -pluralName: "AccessPolicies" -machineName: "accesspolicy" -pluralMachineName: "accesspolicies" - -lineage: schemas: [{ - version: [0, 0] - schema: { - spec: { - // The scope where these policies should apply - scope: #ResourceRef - - // The role that must apply this policy - role: #RoleRef - - // The set of rules to apply. Note that * is required to modify - // access policy rules, and that "none" will reject all actions - rules: [...#AccessRule] - } @cuetsy(kind="interface") - - #RoleRef: { - // Policies can apply to roles, teams, or users - // Applying policies to individual users is supported, but discouraged - kind: "Role" | "BuiltinRole" | "Team" | "User" - name: string - xname: string // temporary - } @cuetsy(kind="interface") - - #ResourceRef: { - kind: string // explicit resource or folder will cascade - name: string - } @cuetsy(kind="interface") - - #AccessRule: { - // The kind this rule applies to (dashboards, alert, etc) - kind: "*" | string - - // READ, WRITE, CREATE, DELETE, ... - // should move to k8s style verbs like: "get", "list", "watch", "create", "update", "patch", "delete" - verb: "*" | "none" | string - - // Specific sub-elements like "alert.rules" or "dashboard.permissions"???? - target?: string - } @cuetsy(kind="interface") - } -}, -] diff --git a/kinds/dashboard/dashboard_kind.cue b/kinds/dashboard/dashboard_kind.cue index 9ed6b5cc0c1..bbf3cb24321 100644 --- a/kinds/dashboard/dashboard_kind.cue +++ b/kinds/dashboard/dashboard_kind.cue @@ -74,7 +74,7 @@ lineage: schemas: [{ // Version of the JSON schema, incremented each time a Grafana update brings // changes to said schema. - schemaVersion: uint16 | *39 + schemaVersion: uint16 | *41 // Version of the dashboard, incremented each time the dashboard is updated. version?: uint32 @@ -459,6 +459,13 @@ lineage: schemas: [{ options: _ } @cuetsy(kind="interface") @grafana(TSVeneer="type") + // Counterpart for TypeScript's TimeOption type. + #TimeOption: { + display: string + from: string + to: string + } @cuetsy(kind="interface") @grafana(TSVeneer="type") + // Time picker configuration // It defines the default config for the time picker and the refresh picker for the specific dashboard. #TimePickerConfig: { @@ -466,8 +473,8 @@ lineage: schemas: [{ hidden?: bool | *false // Interval options available in the refresh picker dropdown. refresh_intervals?: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] - // Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard. - time_options?: [...string] | *["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + // Quick ranges for time picker. + quick_ranges?: [...#TimeOption] // Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. nowDelay?: string } @cuetsy(kind="interface") @grafana(TSVeneer="type") diff --git a/kinds/role/role_kind.cue b/kinds/role/role_kind.cue deleted file mode 100644 index 6e80d2abbde..00000000000 --- a/kinds/role/role_kind.cue +++ /dev/null @@ -1,25 +0,0 @@ -package kind - -name: "Role" -maturity: "merged" -description: "Roles represent a set of users+teams that should share similar access" - -lineage: schemas: [{ - version: [0, 0] - schema: { - spec: { - // The role identifier `managed:builtins:editor:permissions` - name: string - // Optional display - displayName?: string - // Name of the team. - groupName?: string - // Role description - description?: string - - // Do not show this role - hidden: bool | false - } @cuetsy(kind="interface") - } -}, -] diff --git a/kinds/rolebinding/role_binding_kind.cue b/kinds/rolebinding/role_binding_kind.cue deleted file mode 100644 index c76a633a721..00000000000 --- a/kinds/rolebinding/role_binding_kind.cue +++ /dev/null @@ -1,36 +0,0 @@ -package kind - -name: "RoleBinding" -maturity: "merged" -description: "Role bindings links a user|team to a configured role" - -lineage: schemas: [{ - version: [0, 0] - schema: { - spec: { - // The role we are discussing - role: #BuiltinRoleRef | #CustomRoleRef - - // The team or user that has the specified role - subject: #RoleBindingSubject - } @cuetsy(kind="interface") - - #CustomRoleRef: { - kind: "Role" - name: string - } @cuetsy(kind="interface") - - #BuiltinRoleRef: { - kind: "BuiltinRole" - name: "viewer" | "editor" | "admin" - } @cuetsy(kind="interface") - - #RoleBindingSubject: { - kind: "Team" | "User" - - // The team/user identifier name - name: string - } @cuetsy(kind="interface") - } -}, -] diff --git a/lerna.json b/lerna.json index 1f9cd412ea5..f39bf8763c4 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", "npmClient": "yarn", - "version": "11.5.0-pre" + "version": "11.6.0-pre" } diff --git a/package.json b/package.json index 5a117590e23..012ee9da149 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "license": "AGPL-3.0-only", "private": true, "name": "grafana", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "repository": "github:grafana/grafana", "scripts": { "build": "NODE_ENV=production nx exec --verbose -- webpack --config scripts/webpack/webpack.prod.js --progress", @@ -11,7 +11,8 @@ "build:stats": "NODE_ENV=production webpack --progress --config scripts/webpack/webpack.stats.js", "dev": "NODE_ENV=dev nx exec -- webpack --config scripts/webpack/webpack.dev.js", "e2e": "./e2e/start-and-run-suite", - "e2e:scenes": "./e2e/start-and-run-suite scenes", + "e2e:old-arch": "./e2e/start-and-run-suite old-arch", + "e2e:schema-v2": "./e2e/start-and-run-suite dashboards-schema-v2", "e2e:debug": "./e2e/start-and-run-suite debug", "e2e:dev": "./e2e/start-and-run-suite dev", "e2e:benchmark:live": "./e2e/start-and-run-suite benchmark live", @@ -55,33 +56,34 @@ "ci:test-frontend": "yarn run test:ci", "i18n:stats": "node ./scripts/cli/reportI18nStats.mjs", "betterer": "betterer --tsconfig ./scripts/cli/tsconfig.json", - "betterer:merge": "betterer merge --tsconfig ./scripts/cli/tsconfig.json", - "betterer:stats": "node ./scripts/cli/reportBettererStats.mjs", + "betterer:stats": "ts-node --transpile-only --project ./scripts/cli/tsconfig.json ./scripts/cli/reportBettererStats.ts", "betterer:issues": "ts-node --transpile-only --project ./scripts/cli/tsconfig.json ./scripts/cli/generateBettererIssues.ts", + "betterer:ci": "betterer ci --tsconfig ./scripts/cli/tsconfig.json", "plugin:build": "nx run-many -t build --projects='tag:scope:plugin'", "plugin:build:commit": "nx run-many -t build:commit --projects='tag:scope:plugin'", "plugin:build:dev": "nx run-many -t dev --projects='tag:scope:plugin' --maxParallel=100", "generate-icons": "nx run grafana-icons:generate", - "generate-apis": "rtk-query-codegen-openapi ./scripts/generate-rtk-apis.ts" + "process-specs": "node --experimental-strip-types scripts/process-specs.ts", + "generate-apis": "yarn process-specs && rtk-query-codegen-openapi ./scripts/generate-rtk-apis.ts" }, "grafana": { - "whatsNewUrl": "https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-v11-4/", + "whatsNewUrl": "https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-v%[1]s-%[2]s/", "releaseNotesUrl": "https://grafana.com/docs/grafana/next/release-notes/" }, "devDependencies": { - "@babel/core": "7.26.0", - "@babel/preset-env": "7.26.0", - "@babel/runtime": "7.26.0", + "@babel/core": "7.26.7", + "@babel/preset-env": "7.26.7", + "@babel/runtime": "7.26.7", "@betterer/betterer": "5.4.0", "@betterer/cli": "5.4.0", "@cypress/webpack-preprocessor": "6.0.2", "@emotion/eslint-plugin": "11.12.0", "@grafana/eslint-config": "8.0.0", "@grafana/eslint-plugin": "link:./packages/grafana-eslint-rules", - "@grafana/plugin-e2e": "^1.11.0", + "@grafana/plugin-e2e": "1.17.1", "@grafana/tsconfig": "^2.0.0", "@manypkg/get-packages": "^2.2.0", - "@playwright/test": "1.49.1", + "@playwright/test": "1.50.1", "@pmmmwh/react-refresh-webpack-plugin": "0.5.15", "@react-types/button": "3.10.2", "@react-types/menu": "3.9.14", @@ -90,13 +92,13 @@ "@rsdoctor/webpack-plugin": "^0.4.6", "@rtk-query/codegen-openapi": "^2.0.0", "@rtsao/plugin-proposal-class-properties": "7.0.1-patch.1", - "@stylistic/eslint-plugin-ts": "^2.9.0", - "@swc/core": "1.10.9", + "@stylistic/eslint-plugin-ts": "^3.0.0", + "@swc/core": "1.10.12", "@swc/helpers": "0.5.15", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.6.3", - "@testing-library/react": "16.1.0", - "@testing-library/user-event": "14.5.2", + "@testing-library/react": "16.2.0", + "@testing-library/user-event": "14.6.1", "@types/angular": "1.8.9", "@types/angular-route": "1.7.6", "@types/babel__core": "^7", @@ -120,10 +122,10 @@ "@types/jquery": "3.5.32", "@types/js-yaml": "^4.0.5", "@types/jsurl": "^1.2.28", - "@types/lodash": "4.17.14", + "@types/lodash": "4.17.15", "@types/logfmt": "^1.2.3", "@types/lucene": "^2", - "@types/node": "22.10.7", + "@types/node": "22.12.0", "@types/node-forge": "^1", "@types/ol-ext": "npm:@siedlerchr/types-ol-ext@3.2.4", "@types/pluralize": "^0.0.33", @@ -145,15 +147,15 @@ "@types/slate": "0.47.11", "@types/slate-plain-serializer": "0.7.5", "@types/slate-react": "0.22.9", - "@types/swagger-ui-react": "4.18.3", + "@types/swagger-ui-react": "5.18.0", "@types/systemjs": "6.15.1", "@types/tinycolor2": "1.4.6", "@types/uuid": "10.0.0", "@types/webpack-assets-manifest": "^5", "@types/webpack-env": "^1.18.4", "@types/yargs": "17.0.33", - "@typescript-eslint/eslint-plugin": "8.18.1", - "@typescript-eslint/parser": "8.18.1", + "@typescript-eslint/eslint-plugin": "8.22.0", + "@typescript-eslint/parser": "8.22.0", "autoprefixer": "10.4.20", "babel-loader": "9.2.1", "blob-polyfill": "9.0.20240710", @@ -169,15 +171,15 @@ "cypress": "13.10.0", "cypress-file-upload": "5.0.8", "cypress-recurse": "^1.35.3", - "esbuild": "0.24.2", - "esbuild-loader": "4.2.2", - "esbuild-plugin-browserslist": "^0.15.0", - "eslint": "9.18.0", + "esbuild": "0.25.0", + "esbuild-loader": "4.3.0", + "esbuild-plugin-browserslist": "^0.16.0", + "eslint": "9.19.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "28.11.0", "eslint-plugin-jest-dom": "^5.4.0", - "eslint-plugin-jsdoc": "50.6.2", + "eslint-plugin-jsdoc": "50.6.3", "eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-lodash": "8.0.0", "eslint-plugin-no-barrel-files": "^1.1.1", @@ -194,7 +196,7 @@ "html-loader": "5.1.0", "html-webpack-plugin": "5.6.3", "http-server": "14.1.1", - "i18next-parser": "9.1.0", + "i18next-parser": "9.3.0", "ini": "^5.0.0", "jest": "29.7.0", "jest-canvas-mock": "2.5.2", @@ -204,8 +206,8 @@ "jest-junit": "16.0.0", "jest-matcher-utils": "29.7.0", "jest-watch-typeahead": "^2.2.2", + "jimp": "^1.6.0", "jsdom-testing-mocks": "^1.13.1", - "knip": "^5.10.0", "lerna": "8.1.8", "mini-css-extract-plugin": "2.9.2", "msw": "2.7.0", @@ -213,6 +215,8 @@ "ngtemplate-loader": "2.1.0", "node-notifier": "10.0.1", "nx": "19.8.2", + "openapi-types": "^12.1.3", + "pdf-parse": "^1.1.1", "postcss": "8.5.1", "postcss-loader": "8.1.1", "postcss-reporter": "7.1.0", @@ -223,12 +227,12 @@ "react-select-event": "5.5.1", "redux-mock-store": "1.5.5", "rimraf": "6.0.1", - "rudder-sdk-js": "2.48.43", + "rudder-sdk-js": "2.48.44", "sass": "1.83.4", "sass-loader": "16.0.4", "smtp-tester": "^2.1.0", "style-loader": "4.0.0", - "stylelint": "16.12.0", + "stylelint": "16.14.1", "stylelint-config-sass-guidelines": "12.1.0", "terser-webpack-plugin": "5.3.11", "testing-library-selector": "0.3.1", @@ -259,20 +263,21 @@ "@grafana/azure-sdk": "0.0.5", "@grafana/data": "workspace:*", "@grafana/e2e-selectors": "workspace:*", - "@grafana/faro-core": "^1.3.6", - "@grafana/faro-web-sdk": "^1.3.6", - "@grafana/faro-web-tracing": "^1.8.2", + "@grafana/faro-core": "^1.13.2", + "@grafana/faro-web-sdk": "^1.13.2", + "@grafana/faro-web-tracing": "^1.13.2", "@grafana/flamegraph": "workspace:*", "@grafana/google-sdk": "0.1.2", "@grafana/lezer-logql": "0.2.7", + "@grafana/llm": "0.12.0", "@grafana/monaco-logql": "^0.0.8", "@grafana/o11y-ds-frontend": "workspace:*", - "@grafana/plugin-ui": "^0.9.6", + "@grafana/plugin-ui": "0.10.1", "@grafana/prometheus": "workspace:*", "@grafana/runtime": "workspace:*", "@grafana/saga-icons": "workspace:*", - "@grafana/scenes": "5.37.1", - "@grafana/scenes-react": "5.37.1", + "@grafana/scenes": "6.0.2", + "@grafana/scenes-react": "6.0.2", "@grafana/schema": "workspace:*", "@grafana/sql": "workspace:*", "@grafana/ui": "workspace:*", @@ -296,7 +301,7 @@ "@react-aria/overlays": "3.25.0", "@react-aria/utils": "3.27.0", "@react-awesome-query-builder/ui": "6.6.4", - "@reduxjs/toolkit": "2.5.0", + "@reduxjs/toolkit": "2.5.1", "@visx/event": "3.12.0", "@visx/gradient": "3.12.0", "@visx/group": "3.12.0", @@ -310,11 +315,12 @@ "ansicolor": "2.0.3", "baron": "3.0.3", "brace": "0.11.1", - "centrifuge": "5.3.0", + "centrifuge": "5.3.2", "classnames": "2.5.1", "combokeys": "^3.0.0", "comlink": "4.4.2", "common-tags": "1.8.2", + "croner": "^9.0.0", "d3": "7.9.0", "d3-force": "3.0.0", "d3-scale-chromatic": "3.1.0", @@ -349,13 +355,13 @@ "ml-regression-polynomial": "^3.0.0", "ml-regression-simple-linear": "^3.0.0", "moment": "2.30.1", - "moment-timezone": "0.5.46", + "moment-timezone": "0.5.47", "monaco-editor": "0.34.1", "moveable": "0.53.0", "nanoid": "^5.0.4", "node-forge": "^1.3.1", "ol": "7.4.0", - "ol-ext": "4.0.24", + "ol-ext": "4.0.26", "pluralize": "^8.0.0", "prismjs": "1.29.0", "rc-slider": "11.1.8", @@ -367,19 +373,19 @@ "react-draggable": "4.4.6", "react-dropzone": "^14.2.3", "react-grid-layout": "patch:react-grid-layout@npm%3A1.4.4#~/.yarn/patches/react-grid-layout-npm-1.4.4-4024c5395b.patch", - "react-highlight-words": "0.20.0", + "react-highlight-words": "0.21.0", "react-hook-form": "^7.49.2", "react-i18next": "^15.0.0", - "react-inlinesvg": "4.1.5", + "react-inlinesvg": "4.2.0", "react-loading-skeleton": "3.5.0", "react-moveable": "0.56.0", "react-redux": "9.2.0", "react-resizable": "3.0.5", "react-responsive-carousel": "^3.2.23", - "react-router": "5.3.3", - "react-router-dom": "5.3.3", + "react-router": "5.3.4", + "react-router-dom": "5.3.4", "react-router-dom-v5-compat": "^6.26.1", - "react-select": "5.9.0", + "react-select": "5.10.0", "react-split-pane": "0.1.92", "react-table": "7.8.0", "react-transition-group": "4.4.5", @@ -387,7 +393,7 @@ "react-virtual": "2.10.4", "react-virtualized-auto-sizer": "1.0.25", "react-window": "1.8.11", - "react-window-infinite-loader": "1.0.9", + "react-window-infinite-loader": "1.0.10", "react-zoom-pan-pinch": "^3.3.0", "reduce-reducers": "^1.0.4", "redux": "5.0.1", @@ -396,11 +402,11 @@ "reselect": "5.1.1", "rxjs": "7.8.1", "selecto": "1.26.3", - "semver": "7.6.3", + "semver": "7.7.0", "slate": "0.47.9", "slate-plain-serializer": "0.7.13", "slate-react": "0.22.10", - "swagger-ui-react": "5.18.2", + "swagger-ui-react": "5.18.3", "symbol-observable": "4.0.0", "systemjs": "6.15.1", "systemjs-cjs-extra": "0.2.1", @@ -412,8 +418,7 @@ "uplot": "1.6.31", "uuid": "11.0.5", "visjs-network": "4.25.0", - "whatwg-fetch": "3.6.20", - "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" + "whatwg-fetch": "3.6.20" }, "resolutions": { "underscore": "1.13.7", diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index 06400ab720c..61124640e72 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/data", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "description": "Grafana Data Library", "keywords": [ "typescript" @@ -36,12 +36,12 @@ }, "dependencies": { "@braintree/sanitize-url": "7.0.1", - "@grafana/schema": "11.5.0-pre", + "@grafana/schema": "11.6.0-pre", "@types/d3-interpolate": "^3.0.0", "@types/string-hash": "1.1.3", "d3-interpolate": "3.0.1", "date-fns": "4.1.0", - "dompurify": "3.2.3", + "dompurify": "3.2.4", "eventemitter3": "5.0.1", "fast_array_intersect": "1.1.0", "history": "4.10.1", @@ -49,9 +49,9 @@ "marked": "15.0.6", "marked-mangle": "1.1.10", "moment": "2.30.1", - "moment-timezone": "0.5.46", + "moment-timezone": "0.5.47", "ol": "7.4.0", - "papaparse": "5.5.1", + "papaparse": "5.5.2", "react-use": "17.6.0", "rxjs": "7.8.1", "string-hash": "^1.1.3", @@ -64,19 +64,19 @@ "@grafana/tsconfig": "^2.0.0", "@rollup/plugin-node-resolve": "16.0.0", "@types/history": "4.7.11", - "@types/lodash": "4.17.14", - "@types/node": "22.10.7", + "@types/lodash": "4.17.15", + "@types/node": "22.12.0", "@types/papaparse": "5.3.15", "@types/react": "18.3.18", "@types/react-dom": "18.3.5", "@types/tinycolor2": "1.4.6", - "esbuild": "0.24.2", + "esbuild": "0.25.0", "react": "18.3.1", "react-dom": "18.3.1", "rimraf": "6.0.1", "rollup": "^4.22.4", "rollup-plugin-dts": "^6.1.1", - "rollup-plugin-esbuild": "6.1.1", + "rollup-plugin-esbuild": "6.2.0", "rollup-plugin-node-externals": "^8.0.0", "typescript": "5.7.3" }, diff --git a/packages/grafana-data/rollup.config.ts b/packages/grafana-data/rollup.config.ts index 9894024f99c..5a913449466 100644 --- a/packages/grafana-data/rollup.config.ts +++ b/packages/grafana-data/rollup.config.ts @@ -1,53 +1,15 @@ -import resolve from '@rollup/plugin-node-resolve'; import { createRequire } from 'node:module'; -import path from 'path'; -import dts from 'rollup-plugin-dts'; -import esbuild from 'rollup-plugin-esbuild'; -import { nodeExternals } from 'rollup-plugin-node-externals'; + +import { entryPoint, plugins, esmOutput, cjsOutput, tsDeclarationOutput } from '../rollup.config.parts'; const rq = createRequire(import.meta.url); const pkg = rq('./package.json'); -const legacyOutputDefaults = { - esModule: true, - interop: 'compat', -}; - export default [ { - input: 'src/index.ts', - plugins: [ - nodeExternals({ deps: true, packagePath: './package.json' }), - resolve(), - esbuild({ - target: 'es2018', - tsconfig: 'tsconfig.build.json', - }), - ], - output: [ - { - format: 'cjs', - sourcemap: true, - dir: path.dirname(pkg.publishConfig.main), - ...legacyOutputDefaults, - }, - { - format: 'esm', - sourcemap: true, - dir: path.dirname(pkg.publishConfig.module), - preserveModules: true, - // @ts-expect-error (TS cannot assure that `process.env.PROJECT_CWD` is a string) - preserveModulesRoot: path.join(process.env.PROJECT_CWD, `packages/grafana-data/src`), - ...legacyOutputDefaults, - }, - ], - }, - { - input: './compiled/index.d.ts', - plugins: [dts()], - output: { - file: pkg.publishConfig.types, - format: 'es', - }, + input: entryPoint, + plugins, + output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-data')], }, + tsDeclarationOutput(pkg), ]; diff --git a/packages/grafana-data/src/datetime/durationutil.ts b/packages/grafana-data/src/datetime/durationutil.ts index 547303134cd..a87d2aca97b 100644 --- a/packages/grafana-data/src/datetime/durationutil.ts +++ b/packages/grafana-data/src/datetime/durationutil.ts @@ -26,6 +26,10 @@ export function intervalToAbbreviatedDurationString(interval: Interval, includeS } const duration = intervalToDuration(interval); + return reverseParseDuration(duration, includeSeconds); +} + +export function reverseParseDuration(duration: Duration, includeSeconds: boolean): string { return Object.entries(duration).reduce((str, [unit, value]) => { if (value && value !== 0 && !(unit === 'seconds' && !includeSeconds && str)) { const padding = str !== '' ? ' ' : ''; diff --git a/packages/grafana-data/src/datetime/rangeutil.ts b/packages/grafana-data/src/datetime/rangeutil.ts index d984321c532..3168e24dc6f 100644 --- a/packages/grafana-data/src/datetime/rangeutil.ts +++ b/packages/grafana-data/src/datetime/rangeutil.ts @@ -1,5 +1,3 @@ -import { each } from 'lodash'; - import { RawTimeRange, TimeRange, TimeZone, IntervalValues, RelativeTimeRange, TimeOption } from '../types/time'; import * as dateMath from './datemath'; @@ -17,7 +15,7 @@ const spans: { [key: string]: { display: string; section?: number } } = { y: { display: 'year' }, }; -const rangeOptions: TimeOption[] = [ +const BASE_RANGE_OPTIONS: TimeOption[] = [ { from: 'now/d', to: 'now/d', display: 'Today' }, { from: 'now/d', to: 'now', display: 'Today so far' }, { from: 'now/w', to: 'now/w', display: 'This week' }, @@ -66,7 +64,7 @@ const rangeOptions: TimeOption[] = [ { from: 'now/fy', to: 'now/fy', display: 'This fiscal year' }, ]; -const hiddenRangeOptions: TimeOption[] = [ +const HIDDEN_RANGE_OPTIONS: TimeOption[] = [ { from: 'now', to: 'now+1m', display: 'Next minute' }, { from: 'now', to: 'now+5m', display: 'Next 5 minutes' }, { from: 'now', to: 'now+15m', display: 'Next 15 minutes' }, @@ -86,13 +84,11 @@ const hiddenRangeOptions: TimeOption[] = [ { from: 'now', to: 'now+5y', display: 'Next 5 years' }, ]; -const rangeIndex: Record = {}; -each(rangeOptions, (frame) => { - rangeIndex[frame.from + ' to ' + frame.to] = frame; -}); -each(hiddenRangeOptions, (frame) => { - rangeIndex[frame.from + ' to ' + frame.to] = frame; -}); +const STANDARD_RANGE_OPTIONS = BASE_RANGE_OPTIONS.concat(HIDDEN_RANGE_OPTIONS); + +function findRangeInOptions(range: RawTimeRange, options: TimeOption[]) { + return options.find((option) => option.from === range.from && option.to === range.to); +} // handles expressions like // 5m @@ -106,7 +102,7 @@ export function describeTextRange(expr: string): TimeOption { expr = (isLast ? 'now-' : 'now') + expr; } - let opt = rangeIndex[expr + ' to now']; + let opt = findRangeInOptions({ from: expr, to: 'now' }, STANDARD_RANGE_OPTIONS); if (opt) { return opt; } @@ -141,17 +137,15 @@ export function describeTextRange(expr: string): TimeOption { /** * Use this function to get a properly formatted string representation of a {@link @grafana/data:RawTimeRange | range}. * - * @example - * ``` - * // Prints "2": - * console.log(add(1,1)); - * ``` * @category TimeUtils * @param range - a time range (usually specified by the TimePicker) + * @param timeZone - optional time zone. + * @param quickRanges - optional dashboard's custom quick ranges to pick range names from. * @alpha */ -export function describeTimeRange(range: RawTimeRange, timeZone?: TimeZone): string { - const option = rangeIndex[range.from.toString() + ' to ' + range.to.toString()]; +export function describeTimeRange(range: RawTimeRange, timeZone?: TimeZone, quickRanges?: TimeOption[]): string { + const rangeOptions = quickRanges ? quickRanges.concat(STANDARD_RANGE_OPTIONS) : STANDARD_RANGE_OPTIONS; + const option = findRangeInOptions(range, rangeOptions); if (option) { return option.display; diff --git a/packages/grafana-data/src/field/fieldComparers.ts b/packages/grafana-data/src/field/fieldComparers.ts index bdcf8eb32a5..94bb4312b14 100644 --- a/packages/grafana-data/src/field/fieldComparers.ts +++ b/packages/grafana-data/src/field/fieldComparers.ts @@ -51,11 +51,16 @@ const numericComparer = (a: number, b: number): number => { return a - b; }; +// Using the Intl.Collator object compare method results in much faster +// string sorting than .localeCompare +const compare = new Intl.Collator('en', { sensitivity: 'base' }).compare; + const stringComparer = (a: string, b: string): number => { if (!a || !b) { return falsyComparer(a, b); } - return a.localeCompare(b); + + return compare(String(a), String(b)); }; const booleanComparer = (a: boolean, b: boolean): number => { diff --git a/packages/grafana-data/src/field/fieldOverrides.ts b/packages/grafana-data/src/field/fieldOverrides.ts index 0924a417231..7e9aa1052aa 100644 --- a/packages/grafana-data/src/field/fieldOverrides.ts +++ b/packages/grafana-data/src/field/fieldOverrides.ts @@ -501,6 +501,7 @@ export const getLinksSupplier = }); }, origin: field, + oneClick: link.oneClick ?? false, }; } else { linkModel = { @@ -508,6 +509,7 @@ export const getLinksSupplier = title: replaceVariables(link.title || '', dataLinkScopedVars), target: link.targetBlank ? '_blank' : undefined, origin: field, + oneClick: link.oneClick ?? false, }; } diff --git a/packages/grafana-data/src/field/overrides/processors.ts b/packages/grafana-data/src/field/overrides/processors.ts index 8c8542fd6ff..e80a73de866 100644 --- a/packages/grafana-data/src/field/overrides/processors.ts +++ b/packages/grafana-data/src/field/overrides/processors.ts @@ -50,7 +50,9 @@ export interface SliderFieldConfigSettings { ariaLabelForHandle?: string; } -export interface DataLinksFieldConfigSettings {} +export interface DataLinksFieldConfigSettings { + showOneClick?: boolean; +} export const dataLinksOverrideProcessor = ( value: any, diff --git a/packages/grafana-data/src/field/standardFieldConfigEditorRegistry.ts b/packages/grafana-data/src/field/standardFieldConfigEditorRegistry.ts index 83b7f5050e3..584c3779d23 100644 --- a/packages/grafana-data/src/field/standardFieldConfigEditorRegistry.ts +++ b/packages/grafana-data/src/field/standardFieldConfigEditorRegistry.ts @@ -16,6 +16,7 @@ export interface StandardEditorContext { options?: TOptions; instanceState?: TState; isOverride?: boolean; + annotations?: DataFrame[]; } export interface StandardEditorProps { diff --git a/packages/grafana-data/src/index.ts b/packages/grafana-data/src/index.ts index 76195d7d8c0..ec84e8f0d7b 100644 --- a/packages/grafana-data/src/index.ts +++ b/packages/grafana-data/src/index.ts @@ -403,6 +403,7 @@ export { type DateTimeOptionsWhenParsing, dateTimeParse } from './datetime/parse export { intervalToAbbreviatedDurationString, parseDuration, + reverseParseDuration, addDurationToDate, durationToMilliseconds, isValidDate, @@ -548,7 +549,9 @@ export { type PluginExtension, type PluginExtensionLink, type PluginExtensionComponent, + type PluginExtensionComponentMeta, type PluginExtensionConfig, + type PluginExtensionFunction, type PluginExtensionLinkConfig, type PluginExtensionComponentConfig, type PluginExtensionEventHelpers, @@ -559,6 +562,7 @@ export { type PluginExtensionExposedComponentConfig, type PluginExtensionAddedComponentConfig, type PluginExtensionAddedLinkConfig, + type PluginExtensionAddedFunctionConfig, } from './types/pluginExtensions'; export { type ScopeDashboardBindingSpec, diff --git a/packages/grafana-data/src/panel/registryFactories.ts b/packages/grafana-data/src/panel/registryFactories.ts index a312d0c128e..98d8acb660d 100644 --- a/packages/grafana-data/src/panel/registryFactories.ts +++ b/packages/grafana-data/src/panel/registryFactories.ts @@ -55,7 +55,7 @@ export function createFieldConfigRegistry( const customDefault = config.standardOptions[id]?.defaultValue; const customSettings = config.standardOptions[id]?.settings; - if (customHideFromDefaults) { + if (customHideFromDefaults !== undefined) { fieldConfigProp = { ...fieldConfigProp, hideFromDefaults: customHideFromDefaults, diff --git a/packages/grafana-data/src/themes/createColors.ts b/packages/grafana-data/src/themes/createColors.ts index bffd7e3f9d5..9f9afcbfaa9 100644 --- a/packages/grafana-data/src/themes/createColors.ts +++ b/packages/grafana-data/src/themes/createColors.ts @@ -34,6 +34,11 @@ export interface ThemeColorsBase { primary: string; /** Cards and elements that need to stand out on the primary background */ secondary: string; + /** + * For popovers and menu backgrounds. This is the same color as primary in most light themes but in dark + * themes it has a brighter shade to help give it contrast against the primary background. + **/ + elevated: string; }; border: { @@ -143,6 +148,7 @@ class DarkColors implements ThemeColorsBase> { canvas: palette.gray05, primary: palette.gray10, secondary: palette.gray15, + elevated: palette.gray15, }; action = { @@ -225,6 +231,7 @@ class LightColors implements ThemeColorsBase> { canvas: palette.gray90, primary: palette.white, secondary: palette.gray100, + elevated: palette.white, }; action = { diff --git a/packages/grafana-data/src/themes/createComponents.ts b/packages/grafana-data/src/themes/createComponents.ts index e19a16b327c..e810a94b86e 100644 --- a/packages/grafana-data/src/themes/createComponents.ts +++ b/packages/grafana-data/src/themes/createComponents.ts @@ -80,10 +80,10 @@ export function createComponents(colors: ThemeColors, shadows: ThemeShadows): Th input, panel, dropdown: { - background: input.background, + background: colors.background.elevated, }, tooltip: { - background: colors.background.secondary, + background: colors.background.elevated, text: colors.text.primary, }, dashboard: { diff --git a/packages/grafana-data/src/themes/createTheme.ts b/packages/grafana-data/src/themes/createTheme.ts index a224f1be11b..554e567c7c4 100644 --- a/packages/grafana-data/src/themes/createTheme.ts +++ b/packages/grafana-data/src/themes/createTheme.ts @@ -41,7 +41,7 @@ export function createTheme(options: NewThemeOptions = {}): GrafanaTheme2 { const visualization = createVisualizationColors(colors); const theme = { - name: (name ?? colors.mode === 'dark') ? 'Dark' : 'Light', + name: name ?? (colors.mode === 'dark' ? 'Dark' : 'Light'), isDark: colors.mode === 'dark', isLight: colors.mode === 'light', colors, diff --git a/packages/grafana-data/src/themes/registry.ts b/packages/grafana-data/src/themes/registry.ts index 240e5b75717..c4cf352b622 100644 --- a/packages/grafana-data/src/themes/registry.ts +++ b/packages/grafana-data/src/themes/registry.ts @@ -22,9 +22,12 @@ export function getThemeById(id: string): GrafanaTheme2 { * @internal * For internal use only */ -export function getBuiltInThemes(includeExtras?: boolean) { +export function getBuiltInThemes(allowedExtras: string[]) { const themes = themeRegistry.list().filter((item) => { - return includeExtras ? true : !item.isExtra; + if (item.isExtra) { + return allowedExtras.includes(item.id); + } + return true; }); // sort themes alphabetically, but put built-in themes (default, dark, light, system) first const sortedThemes = themes.sort((a, b) => { diff --git a/packages/grafana-data/src/themes/themeDefinitions/aubergine.ts b/packages/grafana-data/src/themes/themeDefinitions/aubergine.ts new file mode 100644 index 00000000000..aaaa79f51a1 --- /dev/null +++ b/packages/grafana-data/src/themes/themeDefinitions/aubergine.ts @@ -0,0 +1,56 @@ +import { NewThemeOptions } from '../createTheme'; + +const aubergineTheme: NewThemeOptions = { + name: 'Aubergine', + colors: { + mode: 'dark', + border: { + weak: '#4F2A3D', + medium: '#6A3C4B', + strong: '#8C5A69', + }, + text: { + primary: '#E5D0D6', + secondary: '#D1A8C4', + disabled: '#B7A0A6', + link: '#A56BB6', + maxContrast: '#FFFFFF', + }, + primary: { + main: '#8C5A69', + }, + secondary: { + main: '#6A3C4B', + text: '#D1A8C4', + border: '#8C5A69', + }, + background: { + canvas: '#2E1F2D', + primary: '#3C2136', + secondary: '#4A2D47', + elevated: '#4A2D47', + }, + action: { + hover: '#6A3C4B', + selected: '#8C5A69', + selectedBorder: '#FFB300', + focus: '#A56BB6', + hoverOpacity: 0.1, + disabledText: '#B7A0A6', + disabledBackground: '#4A2D47', + disabledOpacity: 0.38, + }, + gradients: { + brandHorizontal: 'linear-gradient(270deg, #6A3C4B 0%, #A56BB6 100%)', + brandVertical: 'linear-gradient(0deg, #6A3C4B 0%, #A56BB6 100%)', + }, + contrastThreshold: 4, + hoverFactor: 0.07, + tonalOffset: 0.15, + }, + shape: { + borderRadius: 6, + }, +}; + +export default aubergineTheme; diff --git a/packages/grafana-data/src/themes/themeDefinitions/debug.ts b/packages/grafana-data/src/themes/themeDefinitions/debug.ts index 589ca4ce86b..22e577faf2c 100644 --- a/packages/grafana-data/src/themes/themeDefinitions/debug.ts +++ b/packages/grafana-data/src/themes/themeDefinitions/debug.ts @@ -16,6 +16,7 @@ const debugTheme: NewThemeOptions = { canvas: '#000033', primary: '#000044', secondary: '#000055', + elevated: '#000055', }, text: { primary: '#bbbb00', diff --git a/packages/grafana-data/src/themes/themeDefinitions/desertbloom.ts b/packages/grafana-data/src/themes/themeDefinitions/desertbloom.ts new file mode 100644 index 00000000000..0d8ce37e045 --- /dev/null +++ b/packages/grafana-data/src/themes/themeDefinitions/desertbloom.ts @@ -0,0 +1,77 @@ +import { NewThemeOptions } from '../createTheme'; + +const desertBloomTheme: NewThemeOptions = { + name: 'Desert bloom', + colors: { + mode: 'light', + border: { + weak: 'rgba(0, 0, 0, 0.12)', + medium: 'rgba(0, 0, 0, 0.20)', + strong: 'rgba(0, 0, 0, 0.30)', + }, + text: { + primary: '#333333', + secondary: '#555555', + disabled: 'rgba(0, 0, 0, 0.5)', + link: '#1A82E2', + maxContrast: '#000000', + }, + primary: { + main: '#FF6F61', + text: '#FE6F61', + border: '#E55B4D', + name: 'primary', + shade: '#E55B4D', + transparent: '#FF6F6126', + contrastText: '#FFFFFF', + borderTransparent: '#FF6F6140', + }, + secondary: { + main: '#FFFFFF', + text: '#695f53', + border: '#d9cec0', + name: 'secondary', + shade: '#d9cec0', + transparent: '#FFFFFF26', + contrastText: '#4c4339', + borderTransparent: '#FFFFFF40', + }, + info: { + main: '#1A82E2', + }, + success: { + main: '#4CAF50', + }, + warning: { + main: '#FFC107', + }, + background: { + canvas: '#FFF8F0', + primary: '#FFFFFF', + secondary: '#f9f3e8', + elevated: '#FFFFFF', + }, + action: { + hover: 'rgba(168, 156, 134, 0.12)', + selected: 'rgba(168, 156, 134, 0.36)', + selectedBorder: '#FF6F61', + focus: 'rgba(168, 156, 134, 0.50)', + hoverOpacity: 0.08, + disabledText: 'rgba(168, 156, 134, 0.5)', + disabledBackground: 'rgba(168, 156, 134, 0.06)', + disabledOpacity: 0.38, + }, + gradients: { + brandHorizontal: 'linear-gradient(270deg, #FF6F61 0%, #ece0d1 100%)', + brandVertical: 'linear-gradient(0.01deg, #FF6F61 0.01%, #ece0d1 99.99%)', + }, + contrastThreshold: 3, + hoverFactor: 0.03, + tonalOffset: 0.15, + }, + shape: { + borderRadius: 6, + }, +}; + +export default desertBloomTheme; diff --git a/packages/grafana-data/src/themes/themeDefinitions/gildedgrove.ts b/packages/grafana-data/src/themes/themeDefinitions/gildedgrove.ts new file mode 100644 index 00000000000..6492aaa906f --- /dev/null +++ b/packages/grafana-data/src/themes/themeDefinitions/gildedgrove.ts @@ -0,0 +1,68 @@ +import { NewThemeOptions } from '../createTheme'; + +const gildedGroveTheme: NewThemeOptions = { + name: 'Gilded grove', + colors: { + mode: 'dark', + border: { + weak: 'rgba(200, 200, 180, 0.12)', + medium: 'rgba(200, 200, 180, 0.20)', + strong: 'rgba(200, 200, 180, 0.30)', + }, + text: { + primary: 'rgb(250, 250, 239)', + secondary: 'rgba(200, 200, 180, 0.85)', + disabled: 'rgba(200, 200, 180, 0.6)', + link: '#FEAC34', + maxContrast: '#FFFFFF', + }, + primary: { + main: '#FEAC34', + text: '#FFD783', + border: '#FFD783', + name: 'primary', + shade: 'rgb(255, 173, 80)', + transparent: '#FEAC3426', + contrastText: '#111614', + borderTransparent: '#FFD78340', + }, + secondary: { + main: 'rgba(200, 200, 180, 0.10)', + shade: 'rgba(200, 200, 180, 0.14)', + transparent: 'rgba(200, 200, 180, 0.08)', + text: 'rgb(200, 200, 180)', + contrastText: 'rgb(200, 200, 180)', + border: 'rgba(200, 200, 180, 0.08)', + name: 'secondary', + borderTransparent: 'rgba(200, 200, 180, 0.25)', + }, + background: { + canvas: '#111614', + primary: '#1d2220', + secondary: '#27312E', + elevated: '#27312E', + }, + action: { + hover: 'rgba(200, 200, 180, 0.16)', + selected: 'rgba(200, 200, 180, 0.12)', + selectedBorder: '#FEAC34', + focus: 'rgba(200, 200, 180, 0.16)', + hoverOpacity: 0.08, + disabledText: 'rgba(200, 200, 180, 0.6)', + disabledBackground: 'rgba(200, 200, 180, 0.04)', + disabledOpacity: 0.38, + }, + gradients: { + brandHorizontal: 'linear-gradient(270deg, #FEAC34 0%, #FFD783 100%)', + brandVertical: 'linear-gradient(0.01deg, #FEAC34 0.01%, #FFD783 99.99%)', + }, + contrastThreshold: 3, + hoverFactor: 0.03, + tonalOffset: 0.15, + }, + shape: { + borderRadius: 5, + }, +}; + +export default gildedGroveTheme; diff --git a/packages/grafana-data/src/themes/themeDefinitions/gloom.ts b/packages/grafana-data/src/themes/themeDefinitions/gloom.ts new file mode 100644 index 00000000000..7c4f497e95b --- /dev/null +++ b/packages/grafana-data/src/themes/themeDefinitions/gloom.ts @@ -0,0 +1,83 @@ +import { NewThemeOptions } from '../createTheme'; + +/** + * Torkel's GrafanaCon theme + * very WIP state + */ + +const whiteBase = `210, 210, 220`; +const secondaryBase = `210, 210, 220`; +//const brandMain = '#3d71d9'; +//const brandText = '#6e9fff'; +const brandMain = '#ff934d'; +const brandText = '#f99a5c'; + +const disabledText = `rgba(${whiteBase}, 0.6)`; + +const gloomTheme: NewThemeOptions = { + name: 'Gloom', + colors: { + mode: 'dark', + border: { + weak: `rgba(${whiteBase}, 0.08)`, + medium: `rgba(${whiteBase}, 0.15)`, + strong: `rgba(${whiteBase}, 0.30)`, + }, + + text: { + primary: `rgb(${whiteBase})`, + secondary: `rgba(${whiteBase}, 0.65)`, + disabled: disabledText, + link: brandText, + maxContrast: '#FFF', + }, + + primary: { + main: brandMain, + text: brandText, + border: brandMain, + name: 'primary', + }, + + secondary: { + main: `rgba(${secondaryBase}, 0.10)`, + shade: `rgba(${secondaryBase}, 0.14)`, + transparent: `rgba(${secondaryBase}, 0.08)`, + text: `rgba(${secondaryBase})`, + contrastText: `rgb(${secondaryBase})`, + border: `rgba(${secondaryBase}, 0.08)`, + }, + + background: { + canvas: '#000', + primary: '#0d0b14', + secondary: '#19171f', + elevated: '#19171f', + }, + + action: { + hover: `rgba(${secondaryBase}, 0.10)`, + selected: `rgba(${secondaryBase}, 0.12)`, + selectedBorder: brandMain, + focus: `rgba(${secondaryBase}, 0.10)`, + hoverOpacity: 0.05, + disabledText: disabledText, + disabledBackground: `rgba(${whiteBase}, 0.04)`, + disabledOpacity: 0.38, + }, + + // gradients: { + // brandHorizontal: 'linear-gradient(270deg, #ff934d 0%, #FEAC34 100%)', + // brandVertical: 'linear-gradient(0.01deg, #ff934d 0.01%, #FEAC34 99.99%)', + // }, + + contrastThreshold: 3, + hoverFactor: 0.03, + tonalOffset: 0.15, + }, + shape: { + borderRadius: 5, + }, +}; + +export default gloomTheme; diff --git a/packages/grafana-data/src/themes/themeDefinitions/index.ts b/packages/grafana-data/src/themes/themeDefinitions/index.ts index a88efba4add..151ae00593e 100644 --- a/packages/grafana-data/src/themes/themeDefinitions/index.ts +++ b/packages/grafana-data/src/themes/themeDefinitions/index.ts @@ -1 +1,12 @@ +export { default as aubergine } from './aubergine'; export { default as debug } from './debug'; +export { default as desertbloom } from './desertbloom'; +export { default as gildedgrove } from './gildedgrove'; +export { default as mars } from './mars'; +export { default as matrix } from './matrix'; +export { default as sapphiredusk } from './sapphiredusk'; +export { default as synthwave } from './synthwave'; +export { default as tron } from './tron'; +export { default as victorian } from './victorian'; +export { default as zen } from './zen'; +export { default as gloom } from './gloom'; diff --git a/packages/grafana-data/src/themes/themeDefinitions/mars.ts b/packages/grafana-data/src/themes/themeDefinitions/mars.ts new file mode 100644 index 00000000000..2d3680aacfe --- /dev/null +++ b/packages/grafana-data/src/themes/themeDefinitions/mars.ts @@ -0,0 +1,56 @@ +import { NewThemeOptions } from '../createTheme'; + +const marsTheme: NewThemeOptions = { + name: 'Mars', + colors: { + mode: 'dark', + border: { + weak: 'rgba(210, 90, 60, 0.2)', + medium: 'rgba(210, 90, 60, 0.35)', + strong: 'rgba(210, 90, 60, 0.5)', + }, + text: { + primary: '#DDDDDD', + secondary: '#BBBBBB', + disabled: 'rgba(221, 221, 221, 0.5)', + link: '#FF6F61', + maxContrast: '#FFFFFF', + }, + primary: { + main: '#FF6F61', + }, + secondary: { + main: '#6a2f2f', + text: '#BBBBBB', + border: 'rgba(210, 90, 60, 0.2)', + }, + background: { + canvas: '#3C1E1E', + primary: '#522626', + secondary: '#6A2F2F', + elevated: '#6A2F2F', + }, + action: { + hover: 'rgba(210, 90, 60, 0.16)', + selected: 'rgba(210, 90, 60, 0.12)', + selectedBorder: '#FF6F61', + focus: 'rgba(210, 90, 60, 0.16)', + hoverOpacity: 0.08, + disabledText: 'rgba(221, 221, 221, 0.5)', + disabledBackground: 'rgba(210, 90, 60, 0.08)', + disabledOpacity: 0.38, + }, + gradients: { + brandHorizontal: 'linear-gradient(270deg, #FF6F61 0%, #D25A3C 100%)', + brandVertical: 'linear-gradient(0.01deg, #FF6F61 0.01%, #D25A3C 99.99%)', + }, + contrastThreshold: 3, + hoverFactor: 0.05, + tonalOffset: 0.2, + }, + shape: { + borderRadius: 4, + }, +}; + +export default marsTheme; diff --git a/packages/grafana-data/src/themes/themeDefinitions/matrix.ts b/packages/grafana-data/src/themes/themeDefinitions/matrix.ts new file mode 100644 index 00000000000..51c58b9b394 --- /dev/null +++ b/packages/grafana-data/src/themes/themeDefinitions/matrix.ts @@ -0,0 +1,44 @@ +import { NewThemeOptions } from '../createTheme'; + +const matrixTheme: NewThemeOptions = { + name: 'Matrix', + colors: { + mode: 'dark', + background: { + canvas: '#000000', + primary: '#020202', + secondary: '#080808', + elevated: '#080808', + }, + text: { + primary: '#00c017', + secondary: '#008910', + disabled: '#006a0c', + link: '#00ff41', + maxContrast: '#00ff41', + }, + border: { + weak: '#008f1144', + medium: '#008f1188', + strong: '#008910', + }, + primary: { + main: '#008910', + }, + secondary: { + text: '#008910', + }, + gradients: { + brandVertical: 'linear-gradient(0deg, #008910 0%, #00ff41 100%)', + brandHorizontal: 'linear-gradient(90deg, #008910 0%, #00ff41 100%)', + }, + }, + shape: { + borderRadius: 0, + }, + typography: { + fontFamily: 'monospace', + }, +}; + +export default matrixTheme; diff --git a/packages/grafana-data/src/themes/themeDefinitions/sapphiredusk.ts b/packages/grafana-data/src/themes/themeDefinitions/sapphiredusk.ts new file mode 100644 index 00000000000..596ef029490 --- /dev/null +++ b/packages/grafana-data/src/themes/themeDefinitions/sapphiredusk.ts @@ -0,0 +1,82 @@ +import { NewThemeOptions } from '../createTheme'; + +const sapphireDuskTheme: NewThemeOptions = { + name: 'Sapphire dusk', + colors: { + mode: 'dark', + border: { + weak: '#232e47', + medium: '#2c3853', + strong: '#404d6b', + }, + text: { + primary: '#FFFFFF', + secondary: '#bcccdd', + disabled: '#838da5', + link: '#93EBF0', + maxContrast: '#FFFFFF', + }, + primary: { + main: '#93EBF0', + text: '#a8e9ed', + border: '#93ebf0', + name: 'primary', + shade: '#c0f5d9', + transparent: '#93EBF029', + contrastText: '#111614', + borderTransparent: '#93ebf040', + }, + secondary: { + main: '#2c364f', + shade: '#36415e', + transparent: 'rgba(200, 200, 180, 0.08)', + text: '#d1dfff', + contrastText: '#acfeff', + border: 'rgba(200, 200, 180, 0.08)', + name: 'secondary', + borderTransparent: 'rgba(200, 200, 180, 0.25)', + }, + info: { + main: '#4d4593', + text: '#a8e9ed', + border: '#5d54a7', + }, + error: { + main: '#c63370', + }, + success: { + main: '#1A7F4B', + }, + warning: { + main: '#D448EA', + }, + background: { + canvas: '#1e273d', + primary: '#12192e', + secondary: '#212c47', + elevated: '#212c47', + }, + action: { + hover: '#364057', + selected: '#364260', + selectedBorder: '#D448EA', + focus: '#364057', + hoverOpacity: 0.08, + disabledText: '#838da5', + disabledBackground: 'rgba(54, 64, 87, 0.2)', + disabledOpacity: 0.38, + }, + gradients: { + brandHorizontal: 'linear-gradient(270deg, #D346EF 0%, #2C83FE 100%)', + brandVertical: 'linear-gradient(0deg, #D346EF 0%, #2C83FE 100%)', + }, + contrastThreshold: 3, + hoverFactor: 0.03, + tonalOffset: 0.15, + }, + shape: { + borderRadius: 5, + }, +}; + +export default sapphireDuskTheme; diff --git a/packages/grafana-data/src/themes/themeDefinitions/synthwave.ts b/packages/grafana-data/src/themes/themeDefinitions/synthwave.ts new file mode 100644 index 00000000000..9e7e227621d --- /dev/null +++ b/packages/grafana-data/src/themes/themeDefinitions/synthwave.ts @@ -0,0 +1,56 @@ +import { NewThemeOptions } from '../createTheme'; + +const synthwaveTheme: NewThemeOptions = { + name: 'Synthwave', + colors: { + mode: 'dark', + border: { + weak: 'rgba(255, 20, 147, 0.12)', + medium: 'rgba(255, 20, 147, 0.20)', + strong: 'rgba(255, 20, 147, 0.30)', + }, + text: { + primary: '#E0E0E0', + secondary: 'rgba(224, 224, 224, 0.75)', + disabled: 'rgba(224, 224, 224, 0.5)', + link: '#FF69B4', + maxContrast: '#FFFFFF', + }, + primary: { + main: '#FF1493', + }, + secondary: { + main: '#37183a', + text: 'rgba(224, 224, 224, 0.75)', + border: 'rgba(255, 20, 147, 0.10)', + }, + background: { + canvas: '#1A1A2E', + primary: '#16213E', + secondary: '#0F3460', + elevated: '#0F3460', + }, + action: { + hover: 'rgba(255, 20, 147, 0.16)', + selected: 'rgba(255, 20, 147, 0.12)', + selectedBorder: '#FF1493', + focus: 'rgba(255, 20, 147, 0.16)', + hoverOpacity: 0.08, + disabledText: 'rgba(224, 224, 224, 0.5)', + disabledBackground: 'rgba(255, 20, 147, 0.08)', + disabledOpacity: 0.38, + }, + gradients: { + brandHorizontal: 'linear-gradient(270deg, #FF1493 0%, #1E90FF 100%)', + brandVertical: 'linear-gradient(0.01deg, #FF1493 0.01%, #1E90FF 99.99%)', + }, + contrastThreshold: 3, + hoverFactor: 0.03, + tonalOffset: 0.15, + }, + shape: { + borderRadius: 6, + }, +}; + +export default synthwaveTheme; diff --git a/packages/grafana-data/src/themes/themeDefinitions/tron.ts b/packages/grafana-data/src/themes/themeDefinitions/tron.ts new file mode 100644 index 00000000000..c95c9dea4fa --- /dev/null +++ b/packages/grafana-data/src/themes/themeDefinitions/tron.ts @@ -0,0 +1,56 @@ +import { NewThemeOptions } from '../createTheme'; + +const tronTheme: NewThemeOptions = { + name: 'Tron', + colors: { + mode: 'dark', + border: { + weak: 'rgba(0, 255, 255, 0.12)', + medium: 'rgba(0, 255, 255, 0.20)', + strong: 'rgba(0, 255, 255, 0.30)', + }, + text: { + primary: '#E0E0E0', + secondary: 'rgba(224, 224, 224, 0.75)', + disabled: 'rgba(224, 224, 224, 0.5)', + link: '#00FFFF', + maxContrast: '#FFFFFF', + }, + primary: { + main: '#00FFFF', + }, + secondary: { + main: '#0b2e36', + text: 'rgba(224, 224, 224, 0.75)', + border: 'rgba(0, 255, 255, 0.10)', + }, + background: { + canvas: '#0A0F18', + primary: '#0F1B2A', + secondary: '#152234', + elevated: '#152234', + }, + action: { + hover: 'rgba(0, 255, 255, 0.16)', + selected: 'rgba(0, 255, 255, 0.12)', + selectedBorder: '#00FFFF', + focus: 'rgba(0, 255, 255, 0.16)', + hoverOpacity: 0.08, + disabledText: 'rgba(224, 224, 224, 0.5)', + disabledBackground: 'rgba(0, 255, 255, 0.08)', + disabledOpacity: 0.38, + }, + gradients: { + brandHorizontal: 'linear-gradient(270deg, #00FFFF 0%, #29ABE2 100%)', + brandVertical: 'linear-gradient(0.01deg, #00FFFF 0.01%, #29ABE2 99.99%)', + }, + contrastThreshold: 3, + hoverFactor: 0.05, + tonalOffset: 0.2, + }, + shape: { + borderRadius: 6, + }, +}; + +export default tronTheme; diff --git a/packages/grafana-data/src/themes/themeDefinitions/victorian.ts b/packages/grafana-data/src/themes/themeDefinitions/victorian.ts new file mode 100644 index 00000000000..504b06f80ad --- /dev/null +++ b/packages/grafana-data/src/themes/themeDefinitions/victorian.ts @@ -0,0 +1,60 @@ +import { NewThemeOptions } from '../createTheme'; + +const victorianTheme: NewThemeOptions = { + name: 'Victorian', + colors: { + mode: 'dark', + border: { + weak: '#3A2C22', + medium: '#3A2C22', + strong: '#4B3D32', + }, + text: { + primary: '#D9D0A2', + secondary: '#C4B89B', + disabled: '#A89F91', + link: '#C28A4D', + maxContrast: '#FFFFFF', + }, + primary: { + main: '#C28A4D', + }, + secondary: { + main: '#3A2C22', + text: '#C4B89B', + border: '#4B3D32', + }, + background: { + canvas: '#1F1510', + primary: '#2C1A13', + secondary: '#402A21', + elevated: '#402A21', + }, + action: { + hover: '#3A2C22', + selected: '#4B3D32', + selectedBorder: '#C28A4D', + focus: '#C28A4D', + hoverOpacity: 0.1, + disabledText: '#A89F91', + disabledBackground: '#402A21', + disabledOpacity: 0.38, + }, + gradients: { + brandHorizontal: 'linear-gradient(270deg, #D9D0a1 0%, #C28A4D 100%)', + brandVertical: 'linear-gradient(0.01deg, #D9D0a1 0.01%, #C28A4D 99.99%)', + }, + contrastThreshold: 4, + hoverFactor: 0.07, + tonalOffset: 0.15, + }, + shape: { + borderRadius: 6, + }, + typography: { + fontFamily: '"Georgia", "Times New Roman", serif', + fontFamilyMonospace: "'Courier New', monospace", + }, +}; + +export default victorianTheme; diff --git a/packages/grafana-data/src/themes/themeDefinitions/zen.ts b/packages/grafana-data/src/themes/themeDefinitions/zen.ts new file mode 100644 index 00000000000..8fd12542c21 --- /dev/null +++ b/packages/grafana-data/src/themes/themeDefinitions/zen.ts @@ -0,0 +1,56 @@ +import { NewThemeOptions } from '../createTheme'; + +const zenTheme: NewThemeOptions = { + name: 'Zen', + colors: { + mode: 'light', + text: { + primary: '#333333', + secondary: '#666666', + disabled: '#B8B8B8', + link: '#4F9F6E', + maxContrast: '#000000', + }, + border: { + weak: '#B1B7B3', + medium: '#A2A8A2', + strong: '#7C7F7A', + }, + primary: { + main: '#6D8E6D', + }, + secondary: { + main: '#E0E0E0', + text: '#666666', + border: '#A2A8A2', + }, + background: { + canvas: '#F4F4F4', + primary: '#E9E9E9', + secondary: '#D8D8D8', + elevated: '#E9E9E9', + }, + action: { + hover: '#D1D1D1', + selected: '#B8B8B8', + selectedBorder: '#88B88B', + hoverOpacity: 0.1, + focus: '#D1D1D1', + disabledBackground: '#E0E0E0', + disabledText: '#B8B8B8', + disabledOpacity: 0.5, + }, + gradients: { + brandHorizontal: 'linear-gradient(270deg, #88B88B 0%, #6D8E6D 100%)', + brandVertical: 'linear-gradient(0.01deg, #88B88B 0.01%, #6D8E6D 99.99%)', + }, + contrastThreshold: 3, + hoverFactor: 0.03, + tonalOffset: 0.2, + }, + shape: { + borderRadius: 8, + }, +}; + +export default zenTheme; diff --git a/packages/grafana-data/src/transformations/transformers/calculateField.test.ts b/packages/grafana-data/src/transformations/transformers/calculateField.test.ts index 59872c2019b..f9d049f309b 100644 --- a/packages/grafana-data/src/transformations/transformers/calculateField.test.ts +++ b/packages/grafana-data/src/transformations/transformers/calculateField.test.ts @@ -1,8 +1,22 @@ +import { + SceneDataNode, + SceneDataTransformer, + SceneDeactivationHandler, + SceneFlexItem, + SceneFlexLayout, + sceneGraph, + SceneObject, + SceneObjectBase, + SceneVariable, + SceneVariableSet, + TestVariable, +} from '@grafana/scenes'; +import { DataTransformerConfig, LoadingState } from '@grafana/schema'; + import { DataFrameView } from '../../dataframe/DataFrameView'; import { toDataFrame } from '../../dataframe/processDataFrame'; -import { ScopedVars } from '../../types/ScopedVars'; -import { FieldType } from '../../types/dataFrame'; -import { DataTransformContext } from '../../types/transformations'; +import { DataFrame, FieldType } from '../../types/dataFrame'; +import { getDefaultTimeRange } from '../../types/time'; import { BinaryOperationID } from '../../utils/binaryOperators'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; import { UnaryOperationID } from '../../utils/unaryOperators'; @@ -565,51 +579,26 @@ describe('calculateField transformer w/ timeseries', () => { replaceFields: true, }, }; - const context: DataTransformContext = { - interpolate: (target: string | undefined, scopedVars?: ScopedVars, format?: string | Function): string => { - if (!target) { - return ''; - } - const variables: ScopedVars = { - var1: { - value: 'Test', - text: 'Test', - }, - var2: { - value: 5, - text: '5', - }, - __interval: { - value: 10000, - text: '10000', - }, - }; - for (const key in variables) { - if (target === `$${key}`) { - return variables[key]!.value + ''; - } - } - return target; - }, - }; - await expect(transformDataFrame([cfg], [seriesA], context)).toEmitValuesWith((received) => { - const data = received[0]; - const filtered = data[0]; - const rows = new DataFrameView(filtered).toArray(); - expect(rows).toMatchInlineSnapshot(` - [ - { - "Test": 6, - "TheTime": 1000, - }, - { - "Test": 105, - "TheTime": 2000, - }, - ] - `); - }); + const data = setupTransformationScene(seriesA, cfg, [ + new TestVariable({ name: 'var1', value: 'Test' }), + new TestVariable({ name: 'var2', value: 5 }), + ]); + + const filtered = data[0]; + const rows = new DataFrameView(filtered).toArray(); + expect(rows).toMatchInlineSnapshot(` + [ + { + "Test": 6, + "TheTime": 1000, + }, + { + "Test": 105, + "TheTime": 2000, + }, + ] + `); }); it('calculates centered moving average on odd window size', async () => { @@ -750,8 +739,6 @@ describe('calculateField transformer w/ timeseries', () => { await expect(transformDataFrame([cfg], [series])).toEmitValuesWith((received) => { const data = received[0][0]; - //console.log(data.fields); - expect(data.fields.length).toEqual(2); expect(data.fields[1].values).toEqual([1, 1, 3, 3.5, 4.5]); }); @@ -1185,3 +1172,63 @@ describe('calculateField transformer w/ timeseries', () => { }); }); }); + +function activateFullSceneTree(scene: SceneObject): SceneDeactivationHandler { + const deactivationHandlers: SceneDeactivationHandler[] = []; + + // Important that variables are activated before other children + if (scene.state.$variables) { + deactivationHandlers.push(activateFullSceneTree(scene.state.$variables)); + } + + scene.forEachChild((child) => { + // For query runners which by default use the container width for maxDataPoints calculation we are setting a width. + // In real life this is done by the React component when VizPanel is rendered. + if ('setContainerWidth' in child) { + // @ts-expect-error + child.setContainerWidth(500); + } + deactivationHandlers.push(activateFullSceneTree(child)); + }); + + deactivationHandlers.push(scene.activate()); + + return () => { + for (const handler of deactivationHandlers) { + handler(); + } + }; +} + +function setupTransformationScene( + inputData: DataFrame, + cfg: DataTransformerConfig, + variables: SceneVariable[] +): DataFrame[] { + class TestSceneObject extends SceneObjectBase<{}> {} + const dataNode = new SceneDataNode({ + data: { + state: LoadingState.Loading, + timeRange: getDefaultTimeRange(), + series: [inputData], + }, + }); + + const transformationNode = new SceneDataTransformer({ + transformations: [cfg], + }); + + const consumer = new TestSceneObject({ + $data: transformationNode, + }); + + const scene = new SceneFlexLayout({ + $data: dataNode, + $variables: new SceneVariableSet({ variables }), + children: [new SceneFlexItem({ body: consumer })], + }); + + activateFullSceneTree(scene); + + return sceneGraph.getData(consumer).state.data?.series!; +} diff --git a/packages/grafana-data/src/transformations/transformers/calculateField.ts b/packages/grafana-data/src/transformations/transformers/calculateField.ts index 80f41cc985e..f7b8989c159 100644 --- a/packages/grafana-data/src/transformations/transformers/calculateField.ts +++ b/packages/grafana-data/src/transformations/transformers/calculateField.ts @@ -139,10 +139,6 @@ export const calculateFieldTransformer: DataTransformerInfo { @@ -573,7 +569,8 @@ function findFieldValuesWithNameOrConstant( } if (value.matcher && value.matcher.id === FieldMatcherID.byName) { - const name = ctx.interpolate(value.matcher.options ?? ''); + const name = value.matcher.options ?? ''; + for (const f of frame.fields) { if (name === getFieldDisplayName(f, frame, allFrames)) { if (f.type === FieldType.boolean) { @@ -584,7 +581,7 @@ function findFieldValuesWithNameOrConstant( } } - const v = parseFloat(value.fixed ?? ctx.interpolate(value.matcher?.options ?? '')); + const v = parseFloat(value.fixed ?? value.matcher?.options ?? ''); if (!isNaN(v)) { return new Array(frame.length).fill(v); } diff --git a/packages/grafana-data/src/transformations/transformers/filter.ts b/packages/grafana-data/src/transformations/transformers/filter.ts index 86cd60d03cf..3be485146c2 100644 --- a/packages/grafana-data/src/transformations/transformers/filter.ts +++ b/packages/grafana-data/src/transformations/transformers/filter.ts @@ -28,15 +28,15 @@ export const filterFieldsTransformer: DataTransformerInfo = { } if (typeof options.include?.options === 'string') { - options.include.options = ctx.interpolate(options.include?.options); + options.include.options = options.include?.options; } else if (typeof options.include?.options?.pattern === 'string') { - options.include.options.pattern = ctx.interpolate(options.include?.options.pattern); + options.include.options.pattern = options.include?.options.pattern; } if (typeof options.exclude?.options === 'string') { - options.exclude.options = ctx.interpolate(options.exclude?.options); + options.exclude.options = options.exclude?.options; } else if (typeof options.exclude?.options?.pattern === 'string') { - options.exclude.options.pattern = ctx.interpolate(options.exclude?.options.pattern); + options.exclude.options.pattern = options.exclude?.options.pattern; } return source.pipe( diff --git a/packages/grafana-data/src/transformations/transformers/filterByName.test.ts b/packages/grafana-data/src/transformations/transformers/filterByName.test.ts index 82fe567ee3d..04929528e05 100644 --- a/packages/grafana-data/src/transformations/transformers/filterByName.test.ts +++ b/packages/grafana-data/src/transformations/transformers/filterByName.test.ts @@ -1,6 +1,21 @@ +import { + SceneDataNode, + SceneDataTransformer, + SceneDeactivationHandler, + SceneFlexItem, + SceneFlexLayout, + sceneGraph, + SceneObject, + SceneObjectBase, + SceneVariable, + SceneVariableSet, + TestVariable, +} from '@grafana/scenes'; +import { DataTransformerConfig, LoadingState } from '@grafana/schema'; + import { toDataFrame } from '../../dataframe/processDataFrame'; -import { ScopedVars } from '../../types/ScopedVars'; -import { FieldType } from '../../types/dataFrame'; +import { DataFrame, FieldType } from '../../types/dataFrame'; +import { getDefaultTimeRange } from '../../types/time'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; import { transformDataFrame } from '../transformDataFrame'; @@ -196,6 +211,7 @@ describe('filterByName transformer', () => { expect(filtered.fields[0].name).toBe('B'); }); }); + it('it can use a variable with multiple comma separated', async () => { const cfg = { id: DataTransformerID.filterFieldsByName, @@ -207,31 +223,13 @@ describe('filterByName transformer', () => { }, }; - const ctx = { - interpolate: (target: string | undefined, scopedVars?: ScopedVars, format?: string | Function): string => { - if (!target) { - return ''; - } - const variables: ScopedVars = { - var: { - value: 'B,D', - text: 'Test', - }, - }; - for (const key in variables) { - return target.replace(`$${key}`, variables[key]!.value); - } - return target; - }, - }; - - await expect(transformDataFrame([cfg], [seriesWithNamesToMatch], ctx)).toEmitValuesWith((received) => { - const data = received[0]; - const filtered = data[0]; - expect(filtered.fields.length).toBe(2); - expect(filtered.fields[0].name).toBe('B'); - expect(filtered.fields[1].name).toBe('D'); - }); + const data = setupTransformationScene(seriesWithNamesToMatch, cfg, [ + new TestVariable({ name: 'var', value: 'B,D' }), + ]); + const filtered = data[0]; + expect(filtered.fields.length).toBe(2); + expect(filtered.fields[0].name).toBe('B'); + expect(filtered.fields[1].name).toBe('D'); }); it('it can use a variable with multiple comma separated values in {}', async () => { @@ -245,31 +243,14 @@ describe('filterByName transformer', () => { }, }; - const ctx = { - interpolate: (target: string | undefined, scopedVars?: ScopedVars, format?: string | Function): string => { - if (!target) { - return ''; - } - const variables: ScopedVars = { - var: { - value: '{B,D}', - text: 'Test', - }, - }; - for (const key in variables) { - return target.replace(`$${key}`, variables[key]!.value); - } - return target; - }, - }; + const data = setupTransformationScene(seriesWithNamesToMatch, cfg, [ + new TestVariable({ name: 'var', value: 'B,D' }), + ]); - await expect(transformDataFrame([cfg], [seriesWithNamesToMatch], ctx)).toEmitValuesWith((received) => { - const data = received[0]; - const filtered = data[0]; - expect(filtered.fields.length).toBe(2); - expect(filtered.fields[0].name).toBe('B'); - expect(filtered.fields[1].name).toBe('D'); - }); + const filtered = data[0]; + expect(filtered.fields.length).toBe(2); + expect(filtered.fields[0].name).toBe('B'); + expect(filtered.fields[1].name).toBe('D'); }); it('uses template variable substitution', async () => { @@ -277,35 +258,78 @@ describe('filterByName transformer', () => { id: DataTransformerID.filterFieldsByName, options: { include: { - pattern: '/^$var1/', + pattern: '/^$var/', }, }, }; - const ctx = { - interpolate: (target: string | undefined, scopedVars?: ScopedVars, format?: string | Function): string => { - if (!target) { - return ''; - } - const variables: ScopedVars = { - var1: { - value: 'startsWith', - text: 'Test', - }, - }; - for (const key in variables) { - return target.replace(`$${key}`, variables[key]!.value); - } - return target; - }, - }; + const data = setupTransformationScene(seriesWithNamesToMatch, cfg, [ + new TestVariable({ name: 'var', value: 'startsWith' }), + ]); - await expect(transformDataFrame([cfg], [seriesWithNamesToMatch], ctx)).toEmitValuesWith((received) => { - const data = received[0]; - const filtered = data[0]; - expect(filtered.fields.length).toBe(2); - expect(filtered.fields[0].name).toBe('startsWithA'); - }); + const filtered = data[0]; + expect(filtered.fields.length).toBe(2); + expect(filtered.fields[0].name).toBe('startsWithA'); }); }); }); + +function activateFullSceneTree(scene: SceneObject): SceneDeactivationHandler { + const deactivationHandlers: SceneDeactivationHandler[] = []; + + // Important that variables are activated before other children + if (scene.state.$variables) { + deactivationHandlers.push(activateFullSceneTree(scene.state.$variables)); + } + + scene.forEachChild((child) => { + // For query runners which by default use the container width for maxDataPoints calculation we are setting a width. + // In real life this is done by the React component when VizPanel is rendered. + if ('setContainerWidth' in child) { + // @ts-expect-error + child.setContainerWidth(500); + } + deactivationHandlers.push(activateFullSceneTree(child)); + }); + + deactivationHandlers.push(scene.activate()); + + return () => { + for (const handler of deactivationHandlers) { + handler(); + } + }; +} + +export function setupTransformationScene( + inputData: DataFrame, + cfg: DataTransformerConfig, + variables: SceneVariable[] +): DataFrame[] { + class TestSceneObject extends SceneObjectBase<{}> {} + const dataNode = new SceneDataNode({ + data: { + state: LoadingState.Loading, + timeRange: getDefaultTimeRange(), + series: [inputData], + }, + }); + + const transformationNode = new SceneDataTransformer({ + transformations: [cfg], + }); + + const consumer = new TestSceneObject({ + $data: transformationNode, + }); + + const scene = new SceneFlexLayout({ + $data: dataNode, + $variables: new SceneVariableSet({ variables }), + children: [new SceneFlexItem({ body: consumer })], + }); + + activateFullSceneTree(scene); + + return sceneGraph.getData(consumer).state.data?.series!; +} diff --git a/packages/grafana-data/src/transformations/transformers/filterByName.ts b/packages/grafana-data/src/transformations/transformers/filterByName.ts index 0ecc422e866..0f58d0b3f95 100644 --- a/packages/grafana-data/src/transformations/transformers/filterByName.ts +++ b/packages/grafana-data/src/transformations/transformers/filterByName.ts @@ -1,4 +1,4 @@ -import { DataTransformContext, DataTransformerInfo, MatcherConfig } from '../../types/transformations'; +import { DataTransformerInfo, MatcherConfig } from '../../types/transformations'; import { FieldMatcherID } from '../matchers/ids'; import { RegexpOrNamesMatcherOptions } from '../matchers/nameMatcher'; @@ -25,8 +25,8 @@ export const filterFieldsByNameTransformer: DataTransformerInfo { @@ -46,7 +45,8 @@ export const getMatcherConfig = ( const { names, pattern, variable } = options; if (byVariable && variable) { - const stringOfNames = ctx.interpolate(variable); + const stringOfNames = variable; + if (/\{.*\}/.test(stringOfNames)) { const namesFromString = stringOfNames.slice(1).slice(0, -1).split(','); return { id: FieldMatcherID.byNames, options: { names: namesFromString } }; diff --git a/packages/grafana-data/src/transformations/transformers/filterByValue.test.ts b/packages/grafana-data/src/transformations/transformers/filterByValue.test.ts index 05517cf70c4..83d223a0603 100644 --- a/packages/grafana-data/src/transformations/transformers/filterByValue.test.ts +++ b/packages/grafana-data/src/transformations/transformers/filterByValue.test.ts @@ -13,10 +13,6 @@ import { FilterByValueType, } from './filterByValue'; import { DataTransformerID } from './ids'; -import * as utils from './utils'; - -const mockTransformationsVariableSupport = jest.spyOn(utils, 'transformationsVariableSupport'); -mockTransformationsVariableSupport.mockReturnValue(false); const seriesAWithSingleField = toDataFrame({ name: 'A', @@ -260,139 +256,6 @@ describe('FilterByValue transformer', () => { }); }); - it('should interpolate dashboard variables', async () => { - mockTransformationsVariableSupport.mockReturnValue(true); - - const lower: MatcherConfig> = { - id: ValueMatcherID.lower, - options: { value: 'thiswillinterpolateto6' }, - }; - - const cfg: DataTransformerConfig = { - id: DataTransformerID.filterByValue, - options: { - type: FilterByValueType.exclude, - match: FilterByValueMatch.all, - filters: [ - { - fieldName: 'numbers', - config: lower, - }, - ], - }, - }; - - const ctxmock = { interpolate: jest.fn(() => '6') }; - - await expect(transformDataFrame([cfg], [seriesAWithSingleField], ctxmock)).toEmitValuesWith((received) => { - const processed = received[0]; - - expect(processed.length).toEqual(1); - expect(processed[0].fields).toEqual([ - { - name: 'time', - type: FieldType.time, - values: [6000, 7000], - state: {}, - }, - { - name: 'numbers', - type: FieldType.number, - values: [6, 7], - state: {}, - }, - ]); - }); - }); - - it('should interpolate dashboard variables for regex matcher', async () => { - mockTransformationsVariableSupport.mockReturnValue(true); - - const regex: MatcherConfig> = { - id: ValueMatcherID.regex, - options: { value: '.*thiswillinterpolateto6' }, - }; - - const cfg: DataTransformerConfig = { - id: DataTransformerID.filterByValue, - options: { - type: FilterByValueType.include, - match: FilterByValueMatch.all, - filters: [ - { - fieldName: 'numbers', - config: regex, - }, - ], - }, - }; - - const ctxmock = { interpolate: jest.fn(() => '6') }; - - await expect(transformDataFrame([cfg], [seriesAWithSingleField], ctxmock)).toEmitValuesWith((received) => { - const processed = received[0]; - - expect(processed.length).toEqual(1); - expect(processed[0].fields).toEqual([ - { - name: 'time', - type: FieldType.time, - values: [6000], - state: {}, - }, - { - name: 'numbers', - type: FieldType.number, - values: [6], - state: {}, - }, - ]); - }); - }); - - it('should not interpolate dashboard variables when feature toggle is off', async () => { - mockTransformationsVariableSupport.mockReturnValue(false); - - const lower: MatcherConfig> = { - id: ValueMatcherID.lower, - options: { value: 'notinterpolating' }, - }; - - const cfg: DataTransformerConfig = { - id: DataTransformerID.filterByValue, - options: { - type: FilterByValueType.exclude, - match: FilterByValueMatch.all, - filters: [ - { - fieldName: 'numbers', - config: lower, - }, - ], - }, - }; - - await expect(transformDataFrame([cfg], [seriesAWithSingleField])).toEmitValuesWith((received) => { - const processed = received[0]; - - expect(processed.length).toEqual(1); - expect(processed[0].fields).toEqual([ - { - name: 'time', - type: FieldType.time, - values: [1000, 2000, 3000, 4000, 5000, 6000, 7000], - state: {}, - }, - { - name: 'numbers', - type: FieldType.number, - values: [1, 2, 3, 4, 5, 6, 7], - state: {}, - }, - ]); - }); - }); - it('should match any condition', async () => { const lowerOrEqual: MatcherConfig> = { id: ValueMatcherID.lowerOrEqual, diff --git a/packages/grafana-data/src/transformations/transformers/filterByValue.ts b/packages/grafana-data/src/transformations/transformers/filterByValue.ts index 285a0aa293c..0db3ecab179 100644 --- a/packages/grafana-data/src/transformations/transformers/filterByValue.ts +++ b/packages/grafana-data/src/transformations/transformers/filterByValue.ts @@ -4,11 +4,9 @@ import { getFieldDisplayName } from '../../field/fieldState'; import { DataFrame, Field } from '../../types/dataFrame'; import { DataTransformerInfo, MatcherConfig } from '../../types/transformations'; import { getValueMatcher } from '../matchers'; -import { ValueMatcherID } from '../matchers/ids'; import { DataTransformerID } from './ids'; import { noopTransformer } from './noop'; -import { transformationsVariableSupport } from './utils'; export enum FilterByValueType { exclude = 'exclude', @@ -50,50 +48,6 @@ export const filterByValueTransformer: DataTransformerInfo { - if (filter.config.id === ValueMatcherID.between) { - let valueFrom = filter.config.options.from; - let valueTo = filter.config.options.to; - - if (typeof filter.config.options.from === 'string') { - valueFrom = ctx.interpolate(valueFrom); - } - if (typeof filter.config.options.to === 'string') { - valueTo = ctx.interpolate(valueTo); - } - - return { - ...filter, - config: { - ...filter.config, - options: { - ...filter.config.options, - to: valueTo, - from: valueFrom, - }, - }, - }; - } else if (filter.config.options.value) { - let value = filter.config.options.value; - if (typeof filter.config.options.value === 'string') { - value = ctx.interpolate(value); - } - - return { - ...filter, - config: { ...filter.config, options: { ...filter.config.options, value } }, - }; - } - - return filter; - }) - ); - } - return source.pipe( map((data) => { if (data.length === 0) { @@ -106,12 +60,7 @@ export const filterByValueTransformer: DataTransformerInfo(); const fieldIndexByName = groupFieldIndexByName(frame, data); - let matchers; - if (transformationsVariableSupport()) { - matchers = createFilterValueMatchers(interpolatedFilters, fieldIndexByName); - } else { - matchers = createFilterValueMatchers(filters, fieldIndexByName); - } + const matchers = createFilterValueMatchers(filters, fieldIndexByName); for (let index = 0; index < frame.length; index++) { if (rows.has(index)) { diff --git a/packages/grafana-data/src/transformations/transformers/groupingToMatrix.test.ts b/packages/grafana-data/src/transformations/transformers/groupingToMatrix.test.ts index debc814c89e..cba642d8340 100644 --- a/packages/grafana-data/src/transformations/transformers/groupingToMatrix.test.ts +++ b/packages/grafana-data/src/transformations/transformers/groupingToMatrix.test.ts @@ -31,7 +31,7 @@ describe('Grouping to Matrix', () => { const expected: Field[] = [ { name: 'Time\\Time', - type: FieldType.string, + type: FieldType.time, values: [1000, 1001, 1002], config: {}, }, @@ -133,7 +133,7 @@ describe('Grouping to Matrix', () => { const expected: Field[] = [ { name: 'Time\\Time', - type: FieldType.string, + type: FieldType.time, values: [1000, 1001], config: {}, }, diff --git a/packages/grafana-data/src/transformations/transformers/groupingToMatrix.ts b/packages/grafana-data/src/transformations/transformers/groupingToMatrix.ts index 94ce41f79e2..d39a64cfdce 100644 --- a/packages/grafana-data/src/transformations/transformers/groupingToMatrix.ts +++ b/packages/grafana-data/src/transformations/transformers/groupingToMatrix.ts @@ -1,7 +1,7 @@ import { map } from 'rxjs/operators'; import { getFieldDisplayName } from '../../field/fieldState'; -import { DataFrame, Field, FieldType } from '../../types/dataFrame'; +import { DataFrame, Field } from '../../types/dataFrame'; import { SpecialValue, DataTransformerInfo, @@ -65,9 +65,9 @@ export const groupingToMatrixTransformer: DataTransformerInfo (source) => source.pipe( map((data) => { - const columnFieldMatch = ctx.interpolate(options.columnField || DEFAULT_COLUMN_FIELD); - const rowFieldMatch = ctx.interpolate(options.rowField || DEFAULT_ROW_FIELD); - const valueFieldMatch = ctx.interpolate(options.valueField || DEFAULT_VALUE_FIELD); + const columnFieldMatch = options.columnField || DEFAULT_COLUMN_FIELD; + const rowFieldMatch = options.rowField || DEFAULT_ROW_FIELD; + const valueFieldMatch = options.valueField || DEFAULT_VALUE_FIELD; const emptyValue = options.emptyValue || DEFAULT_EMPTY_VALUE; // Accept only single queries @@ -106,7 +106,7 @@ export const groupingToMatrixTransformer: DataTransformerInfo(values: T[]): T[] { - const unique = new Set(); - - for (let index = 0; index < values.length; index++) { - unique.add(values[index]); - } - + const unique = new Set(values); return Array.from(unique); } diff --git a/packages/grafana-data/src/transformations/transformers/histogram.test.ts b/packages/grafana-data/src/transformations/transformers/histogram.test.ts index 7bbd03397a0..f8aa3f9c991 100644 --- a/packages/grafana-data/src/transformations/transformers/histogram.test.ts +++ b/packages/grafana-data/src/transformations/transformers/histogram.test.ts @@ -1,8 +1,14 @@ import { toDataFrame } from '../../dataframe/processDataFrame'; -import { FieldType } from '../../types/dataFrame'; +import { Field, FieldType } from '../../types/dataFrame'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; -import { histogramTransformer, buildHistogram, histogramFieldsToFrame } from './histogram'; +import { + histogramTransformer, + buildHistogram, + histogramFieldsToFrame, + HistogramFields, + joinHistograms, +} from './histogram'; describe('histogram frames frames', () => { beforeAll(() => { @@ -280,3 +286,110 @@ describe('histogram frames frames', () => { `); }); }); + +describe('joinHistograms', () => { + type TestHistogram = { + xMin: number[]; + xMax: number[]; + counts: number[][]; + }; + + function toField(name: string, values: number[]): Field { + return { + config: {}, + name, + type: FieldType.number, + values, + }; + } + + function testHistogramToHistogram(test: TestHistogram): HistogramFields { + return { + xMin: toField('xMin', test.xMin), + xMax: toField('xMax', test.xMax), + counts: test.counts.map((values) => toField(`count`, values)), + }; + } + + type TestCase = { + name: string; + histograms: TestHistogram[]; + expected: TestHistogram; + }; + + const testCases: TestCase[] = [ + { + name: 'just one histogram', + histograms: [ + { + xMin: [1, 2, 3], + xMax: [2, 3, 4], + counts: [[1, 2, 3]], + }, + ], + expected: { + xMin: [1, 2, 3], + xMax: [2, 3, 4], + counts: [[1, 2, 3]], + }, + }, + { + name: 'two histograms with same bucket sizes', + histograms: [ + { + xMin: [1, 3, 4], + xMax: [2, 4, 5], + counts: [[1, 2, 3]], + }, + { + xMin: [1, 3, 4], + xMax: [2, 4, 5], + counts: [[4, 5, 6]], + }, + ], + expected: { + xMin: [1, 3, 4], + xMax: [2, 4, 5], + counts: [ + [1, 2, 3], + [4, 5, 6], + ], + }, + }, + { + name: 'two histograms with same bucket sizes but counts in some different buckets', + histograms: [ + { + xMin: [1, 3, 4], + xMax: [2, 4, 5], + counts: [[1, 2, 3]], + }, + { + xMin: [2, 3, 6], + xMax: [3, 4, 7], + counts: [[4, 5, 6]], + }, + ], + expected: { + xMin: [1, 2, 3, 4, 6], + xMax: [2, 3, 4, 5, 7], + counts: [ + [1, 0, 2, 3, 0], + [0, 4, 5, 0, 6], + ], + }, + }, + ]; + + testCases.forEach((tc) => { + it(tc.name, () => { + const result = joinHistograms(tc.histograms.map(testHistogramToHistogram)); + + expect({ + xMin: result.xMin.values, + xMax: result.xMax.values, + counts: result.counts.map((f) => f.values), + }).toEqual(tc.expected); + }); + }); +}); diff --git a/packages/grafana-data/src/transformations/transformers/histogram.ts b/packages/grafana-data/src/transformations/transformers/histogram.ts index 6d534d22a4b..8fa69f64a40 100644 --- a/packages/grafana-data/src/transformations/transformers/histogram.ts +++ b/packages/grafana-data/src/transformations/transformers/histogram.ts @@ -1,5 +1,6 @@ import { map } from 'rxjs/operators'; +import { outerJoinDataFrames } from '../..'; import { getDisplayProcessor } from '../../field/displayProcessor'; import { createTheme } from '../../themes/createTheme'; import { GrafanaTheme2 } from '../../themes/types'; @@ -11,7 +12,6 @@ import { roundDecimals } from '../../utils/numbers'; import { DataTransformerID } from './ids'; import { AlignedData, join } from './joinDataFrames'; import { nullToValueField } from './nulls/nullToValue'; -import { transformationsVariableSupport } from './utils'; /** * @internal @@ -112,9 +112,6 @@ export const histogramTransformer: SynchronousDataTransformerInfo ({ + length: h.xMax.values.length, + fields: [h.xMax, h.xMin, ...h.counts], + })), + joinBy: (field) => field.name === 'xMax', + })!; + + let xMaxField: Field | null = null; + let xMinField: Field | null = null; + let countFields: Field[] = []; + + // merge all xMin fields into first xMin field + // and default all count fields to 0 + joined.fields.forEach((f) => { + if (f.name === 'xMax') { + xMaxField = f; + } else if (f.name === 'xMin') { + if (xMinField == null) { + xMinField = f; + } else { + for (let i = 0; i < f.values.length; i++) { + xMinField.values[i] ??= f.values[i]; + } + } + } else { + countFields.push({ + ...f, + values: f.values.map((v) => v ?? 0), + }); + } + }); + + const result: HistogramFields = { + xMin: xMinField!, + xMax: xMaxField!, + counts: countFields, + }; + + return result; +} diff --git a/packages/grafana-data/src/transformations/transformers/joinByField.ts b/packages/grafana-data/src/transformations/transformers/joinByField.ts index 371044c0783..e70a0355820 100644 --- a/packages/grafana-data/src/transformations/transformers/joinByField.ts +++ b/packages/grafana-data/src/transformations/transformers/joinByField.ts @@ -38,7 +38,7 @@ export const joinByFieldTransformer: SynchronousDataTransformerInfo { if (data.length > 1) { if (options.byField && !joinBy) { - joinBy = fieldMatchers.get(FieldMatcherID.byName).get(ctx.interpolate(options.byField)); + joinBy = fieldMatchers.get(FieldMatcherID.byName).get(options.byField); } const joined = joinDataFrames({ frames: data, joinBy, mode: options.mode }); if (joined) { diff --git a/packages/grafana-data/src/transformations/transformers/limit.ts b/packages/grafana-data/src/transformations/transformers/limit.ts index ee51e159543..230e66d9dd3 100644 --- a/packages/grafana-data/src/transformations/transformers/limit.ts +++ b/packages/grafana-data/src/transformations/transformers/limit.ts @@ -3,7 +3,6 @@ import { map } from 'rxjs/operators'; import { DataTransformerInfo } from '../../types/transformations'; import { DataTransformerID } from './ids'; -import { transformationsVariableSupport } from './utils'; export interface LimitTransformerOptions { limitField?: number | string; @@ -25,11 +24,7 @@ export const limitTransformer: DataTransformerInfo = { let limit = DEFAULT_LIMIT_FIELD; if (options.limitField !== undefined) { if (typeof options.limitField === 'string') { - if (transformationsVariableSupport()) { - limit = parseInt(ctx.interpolate(options.limitField), 10); - } else { - limit = parseInt(options.limitField, 10); - } + limit = parseInt(options.limitField, 10); } else { limit = options.limitField; } diff --git a/packages/grafana-data/src/transformations/transformers/sortBy.ts b/packages/grafana-data/src/transformations/transformers/sortBy.ts index 8df10fd7aaa..5238a3b8e9c 100644 --- a/packages/grafana-data/src/transformations/transformers/sortBy.ts +++ b/packages/grafana-data/src/transformations/transformers/sortBy.ts @@ -6,7 +6,6 @@ import { DataFrame } from '../../types/dataFrame'; import { DataTransformContext, DataTransformerInfo } from '../../types/transformations'; import { DataTransformerID } from './ids'; -import { transformationsVariableSupport } from './utils'; export interface SortByField { field: string; @@ -59,12 +58,7 @@ function attachFieldIndex(frame: DataFrame, sort: SortByField[], ctx: DataTransf // null or undefined return s; } - if (transformationsVariableSupport()) { - return { - ...s, - index: frame.fields.findIndex((f) => ctx.interpolate(s.field) === getFieldDisplayName(f, frame)), - }; - } + return { ...s, index: frame.fields.findIndex((f) => s.field === getFieldDisplayName(f, frame)), diff --git a/packages/grafana-data/src/transformations/transformers/utils.ts b/packages/grafana-data/src/transformations/transformers/utils.ts index 699cb3517ca..c10b1e8a1d7 100644 --- a/packages/grafana-data/src/transformations/transformers/utils.ts +++ b/packages/grafana-data/src/transformations/transformers/utils.ts @@ -6,11 +6,6 @@ declare global { grafanaBootData?: BootData; } } - -export const transformationsVariableSupport = () => { - return window?.grafanaBootData?.settings?.featureToggles?.transformationsVariableSupport; -}; - /** * Retrieve the maximum number of fields in a series of a dataframe. */ diff --git a/packages/grafana-data/src/types/action.ts b/packages/grafana-data/src/types/action.ts index a3cb099ebe8..fc17aa25046 100644 --- a/packages/grafana-data/src/types/action.ts +++ b/packages/grafana-data/src/types/action.ts @@ -15,6 +15,8 @@ export interface Action { // Currently this is required because there is only one valid type (fetch) // once multiple types are valid, usage of this will need to be optional [ActionType.Fetch]: FetchOptions; + confirmation?: string; + oneClick?: boolean; } /** @@ -23,6 +25,8 @@ export interface Action { export interface ActionModel { title: string; onClick: (event: any, origin?: any) => void; + confirmation?: string; + oneClick?: boolean; } interface FetchOptions { diff --git a/packages/grafana-data/src/types/app.ts b/packages/grafana-data/src/types/app.ts index 3b33452bd03..38a20ad12b8 100644 --- a/packages/grafana-data/src/types/app.ts +++ b/packages/grafana-data/src/types/app.ts @@ -9,6 +9,7 @@ import { PluginExtensionExposedComponentConfig, PluginExtensionAddedComponentConfig, PluginExtensionAddedLinkConfig, + PluginExtensionAddedFunctionConfig, } from './pluginExtensions'; /** @@ -60,6 +61,7 @@ export class AppPlugin extends GrafanaPlugin>; @@ -113,6 +115,10 @@ export class AppPlugin extends GrafanaPlugin(linkConfig: PluginExtensionAddedLinkConfig) { this._addedLinkConfigs.push(linkConfig as PluginExtensionAddedLinkConfig); @@ -125,6 +131,12 @@ export class AppPlugin extends GrafanaPlugin(addedFunctionConfig: PluginExtensionAddedFunctionConfig) { + this._addedFunctionConfigs.push(addedFunctionConfig); + + return this; + } + exposeComponent(componentConfig: PluginExtensionExposedComponentConfig) { this._exposedComponentConfigs.push(componentConfig as PluginExtensionExposedComponentConfig); @@ -167,4 +179,6 @@ export enum FeatureState { privatePreview = 'private preview', /** used to mark features that are in public preview with low/medium risk, or as a shared badge for public and private previews */ preview = 'preview', + /** used to mark new GA features */ + new = 'new', } diff --git a/packages/grafana-data/src/types/config.ts b/packages/grafana-data/src/types/config.ts index b1f6ca4b28a..4e57dd61656 100644 --- a/packages/grafana-data/src/types/config.ts +++ b/packages/grafana-data/src/types/config.ts @@ -228,6 +228,7 @@ export interface GrafanaConfig { rudderstackConfigUrl: string | undefined; rudderstackIntegrationsUrl: string | undefined; analyticsConsoleReporting: boolean; + dashboardPerformanceMetrics: string[]; sqlConnectionLimits: SqlConnectionLimits; sharedWithMeFolderUID?: string; rootFolderUID?: string; @@ -237,6 +238,7 @@ export interface GrafanaConfig { listScopesEndpoint?: string; reportingStaticContext?: Record; exploreDefaultTimeOffset?: string; + exploreHideLogsDownload?: boolean; // The namespace to use for kubernetes apiserver requests namespace: string; diff --git a/packages/grafana-data/src/types/dataLink.ts b/packages/grafana-data/src/types/dataLink.ts index 476f2441ed7..16d4edbff43 100644 --- a/packages/grafana-data/src/types/dataLink.ts +++ b/packages/grafana-data/src/types/dataLink.ts @@ -1,3 +1,4 @@ +import { ScopedVars } from './ScopedVars'; import { ExploreCorrelationHelperData, ExplorePanelsState } from './explore'; import { InterpolateFunction } from './panel'; import { DataQuery } from './query'; @@ -55,6 +56,8 @@ export interface DataLink { correlationData?: ExploreCorrelationHelperData; transformations?: DataLinkTransformationConfig[]; }; + + oneClick?: boolean; } /** @@ -78,7 +81,7 @@ export interface DataLinkTransformationConfig { /** @internal */ export interface InternalDataLink { - query: T; + query: T | ((options: { replaceVariables: InterpolateFunction; scopedVars: ScopedVars }) => T); datasourceUid: string; datasourceName: string; // used as a title if `DataLink.title` is empty panelsState?: ExplorePanelsState; @@ -98,6 +101,7 @@ export interface LinkModel { // When a click callback exists, this is passed the raw mouse|react event onClick?: (e: any, origin?: any) => void; + oneClick?: boolean; } /** diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index e26b79a8056..87c5ff7c0d1 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -36,18 +36,15 @@ export interface FeatureToggles { autoMigrateStatPanel?: boolean; disableAngular?: boolean; canvasPanelNesting?: boolean; - vizActions?: boolean; disableSecretsCompatibility?: boolean; logRequestsInstrumentedAsUnknown?: boolean; grpcServer?: boolean; cloudWatchCrossAccountQuerying?: boolean; showDashboardValidationWarnings?: boolean; mysqlAnsiQuotes?: boolean; - accessControlOnCall?: boolean; nestedFolders?: boolean; alertingBacktesting?: boolean; editPanelCSVDragAndDrop?: boolean; - alertingNoNormalState?: boolean; logsContextDatasourceUi?: boolean; lokiShardSplitting?: boolean; lokiQuerySplitting?: boolean; @@ -106,15 +103,11 @@ export interface FeatureToggles { enableNativeHTTPHistogram?: boolean; disableClassicHTTPHistogram?: boolean; formatString?: boolean; - transformationsVariableSupport?: boolean; kubernetesPlaylists?: boolean; kubernetesSnapshots?: boolean; kubernetesDashboards?: boolean; - kubernetesCliDashboards?: boolean; kubernetesRestore?: boolean; - kubernetesFolders?: boolean; - kubernetesFoldersServiceV2?: boolean; - grafanaAPIServerTestingWithExperimentalAPIs?: boolean; + kubernetesClientDashboardsFolders?: boolean; datasourceQueryTypes?: boolean; queryService?: boolean; queryServiceRewrite?: boolean; @@ -122,10 +115,7 @@ export interface FeatureToggles { cloudWatchBatchQueries?: boolean; recoveryThreshold?: boolean; lokiStructuredMetadata?: boolean; - teamHttpHeaders?: boolean; cachingOptimizeSerializationMemoryUsage?: boolean; - managedPluginsInstall?: boolean; - prometheusPromQAIL?: boolean; prometheusCodeModeMetricNamesSearch?: boolean; addFieldFromCalculationStatFunctions?: boolean; alertmanagerRemoteSecondary?: boolean; @@ -155,8 +145,8 @@ export interface FeatureToggles { newFolderPicker?: boolean; jitterAlertRulesWithinGroups?: boolean; onPremToCloudMigrations?: boolean; - onPremToCloudMigrationsAuthApiMig?: boolean; alertingSaveStatePeriodic?: boolean; + alertingSaveStateCompressed?: boolean; scopeApi?: boolean; promQLScope?: boolean; logQLScope?: boolean; @@ -168,7 +158,6 @@ export interface FeatureToggles { kubernetesAggregator?: boolean; expressionParser?: boolean; groupByVariable?: boolean; - authAPIAccessTokenAuth?: boolean; scopeFilters?: boolean; ssoSettingsSAML?: boolean; oauthRequireSubClaim?: boolean; @@ -182,16 +171,13 @@ export interface FeatureToggles { newDashboardSharingComponent?: boolean; alertingListViewV2?: boolean; dashboardRestore?: boolean; - datasourceProxyDisableRBAC?: boolean; alertingDisableSendAlertsExternal?: boolean; preserveDashboardStateWhenNavigating?: boolean; alertingCentralAlertHistory?: boolean; pluginProxyPreserveTrailingSlash?: boolean; - sqlQuerybuilderFunctionParameters?: boolean; azureMonitorPrometheusExemplars?: boolean; pinNavItems?: boolean; authZGRPCServer?: boolean; - openSearchBackendFlowEnabled?: boolean; ssoSettingsLDAP?: boolean; failWrongDSUID?: boolean; zanzana?: boolean; @@ -219,6 +205,7 @@ export interface FeatureToggles { unifiedStorageSearch?: boolean; unifiedStorageSearchSprinkles?: boolean; unifiedStorageSearchPermissionFiltering?: boolean; + managedDualWriter?: boolean; pluginsSriChecks?: boolean; unifiedStorageBigObjectsSupport?: boolean; timeRangeProvider?: boolean; @@ -231,7 +218,6 @@ export interface FeatureToggles { exploreMetricsRelatedLogs?: boolean; prometheusSpecialCharsInLabelValues?: boolean; enableExtensionsAdminPage?: boolean; - zipkinBackendMigration?: boolean; enableSCIM?: boolean; crashDetection?: boolean; jaegerBackendMigration?: boolean; @@ -251,9 +237,21 @@ export interface FeatureToggles { improvedExternalSessionHandlingSAML?: boolean; teamHttpHeadersMimir?: boolean; ABTestFeatureToggleA?: boolean; + templateVariablesUsesCombobox?: boolean; ABTestFeatureToggleB?: boolean; - queryLibraryDashboards?: boolean; grafanaAdvisor?: boolean; elasticsearchImprovedParsing?: boolean; + exploreMetricsUseExternalAppPlugin?: boolean; datasourceConnectionsTab?: boolean; + fetchRulesUsingPost?: boolean; + alertingConversionAPI?: boolean; + alertingAlertmanagerExtraDedupStage?: boolean; + alertingAlertmanagerExtraDedupStageStopPipeline?: boolean; + newLogsPanel?: boolean; + grafanaconThemes?: boolean; + pluginsCDNSyncLoader?: boolean; + alertingJiraIntegration?: boolean; + alertingRuleVersionHistoryRestore?: boolean; + newShareReportDrawer?: boolean; + rendererDisableAppPluginsPreload?: boolean; } diff --git a/packages/grafana-data/src/types/fieldOverrides.ts b/packages/grafana-data/src/types/fieldOverrides.ts index d96f22190bd..f2283c5956e 100644 --- a/packages/grafana-data/src/types/fieldOverrides.ts +++ b/packages/grafana-data/src/types/fieldOverrides.ts @@ -144,6 +144,7 @@ export enum FieldConfigProperty { Thresholds = 'thresholds', Mappings = 'mappings', Links = 'links', + Actions = 'actions', Color = 'color', Filterable = 'filterable', } diff --git a/packages/grafana-data/src/types/icon.ts b/packages/grafana-data/src/types/icon.ts index 357cf07e46e..b1092680181 100644 --- a/packages/grafana-data/src/types/icon.ts +++ b/packages/grafana-data/src/types/icon.ts @@ -93,6 +93,7 @@ export const availableIconsIndex = { 'document-layout-left': true, 'download-alt': true, draggabledots: true, + drilldown: true, edit: true, 'ellipsis-v': true, enter: true, diff --git a/packages/grafana-data/src/types/logs.ts b/packages/grafana-data/src/types/logs.ts index 67a194202a7..bfd54b0ec6f 100644 --- a/packages/grafana-data/src/types/logs.ts +++ b/packages/grafana-data/src/types/logs.ts @@ -4,6 +4,7 @@ import { DataQuery, LogsSortOrder } from '@grafana/schema'; import { BusEventWithPayload } from '../events/types'; +import { ScopedVars } from './ScopedVars'; import { KeyValue, Labels } from './data'; import { DataFrame } from './dataFrame'; import { DataQueryRequest, DataQueryResponse, DataSourceApi, QueryFixAction, QueryFixType } from './datasource'; @@ -134,6 +135,7 @@ export enum LogsDedupDescription { export interface LogRowContextOptions { direction?: LogRowContextQueryDirection; limit?: number; + scopedVars?: ScopedVars; } export enum LogRowContextQueryDirection { @@ -172,7 +174,12 @@ export interface DataSourceWithLogsContextSupport void, origQuery?: TQuery): React.ReactNode; + getLogRowContextUi?( + row: LogRowModel, + runContextQuery?: () => void, + origQuery?: TQuery, + scopedVars?: ScopedVars + ): React.ReactNode; } export const hasLogsContextSupport = (datasource: unknown): datasource is DataSourceWithLogsContextSupport => { diff --git a/packages/grafana-data/src/types/navModel.ts b/packages/grafana-data/src/types/navModel.ts index 066d879df3d..f9ebb23fc07 100644 --- a/packages/grafana-data/src/types/navModel.ts +++ b/packages/grafana-data/src/types/navModel.ts @@ -14,6 +14,7 @@ export interface NavLinkDTO { sortWeight?: number; hideFromTabs?: boolean; roundIcon?: boolean; + isNew?: boolean; /** * This is true for some sections that have no children (but is still a section) **/ diff --git a/packages/grafana-data/src/types/options.ts b/packages/grafana-data/src/types/options.ts index d23ceb35c5e..84f45884a36 100644 --- a/packages/grafana-data/src/types/options.ts +++ b/packages/grafana-data/src/types/options.ts @@ -51,5 +51,5 @@ export interface OptionEditorConfig { /** * Function that enables configuration of when option editor should be shown based on current panel option properties. */ - showIf?: (currentOptions: TOptions, data?: DataFrame[]) => boolean | undefined; + showIf?: (currentOptions: TOptions, data?: DataFrame[], annotations?: DataFrame[]) => boolean | undefined; } diff --git a/packages/grafana-data/src/types/plugin.ts b/packages/grafana-data/src/types/plugin.ts index ebb3da684f6..8e1be814c96 100644 --- a/packages/grafana-data/src/types/plugin.ts +++ b/packages/grafana-data/src/types/plugin.ts @@ -130,6 +130,8 @@ export interface PluginExtensions { // The component extensions that the plugin registers addedComponents: ExtensionInfo[]; + addedFunctions: ExtensionInfo[]; + // The link extensions that the plugin registers addedLinks: ExtensionInfo[]; diff --git a/packages/grafana-data/src/types/pluginExtensions.ts b/packages/grafana-data/src/types/pluginExtensions.ts index fcbbecf5f41..331f6db43da 100644 --- a/packages/grafana-data/src/types/pluginExtensions.ts +++ b/packages/grafana-data/src/types/pluginExtensions.ts @@ -14,6 +14,7 @@ import { RawTimeRange, TimeZone } from './time'; export enum PluginExtensionTypes { link = 'link', component = 'component', + function = 'function', } type PluginExtensionBase = { @@ -31,12 +32,19 @@ export type PluginExtensionLink = PluginExtensionBase & { category?: string; }; +export type PluginExtensionComponentMeta = Omit; + export type PluginExtensionComponent = PluginExtensionBase & { type: PluginExtensionTypes.component; component: React.ComponentType; }; -export type PluginExtension = PluginExtensionLink | PluginExtensionComponent; +export type PluginExtensionFunction void> = PluginExtensionBase & { + type: PluginExtensionTypes.function; + fn: Signature; +}; + +export type PluginExtension = PluginExtensionLink | PluginExtensionComponent | PluginExtensionFunction; // Objects used for registering extensions (in app plugins) // -------------------------------------------------------- @@ -74,6 +82,17 @@ export type PluginExtensionAddedComponentConfig = PluginExtensionCon */ component: React.ComponentType; }; +export type PluginExtensionAddedFunctionConfig = PluginExtensionConfigBase & { + /** + * The target extension points where the component will be added + */ + targets: string | string[]; + + /** + * The function to be executed + */ + fn: Signature; +}; export type PluginAddedLinksConfigureFunc = (context: Readonly | undefined) => | Partial<{ @@ -176,7 +195,10 @@ export type PluginExtensionPanelContext = { data?: PanelData; }; -export type PluginExtensionDataSourceConfigContext = { +export type PluginExtensionDataSourceConfigContext< + JsonData extends DataSourceJsonData = DataSourceJsonData, + SecureJsonData = {}, +> = { // The current datasource settings dataSource: DataSourceSettings; @@ -192,6 +214,7 @@ export type PluginExtensionDataSourceConfigContext void; + setSecureJsonData: (secureJsonData: SecureJsonData) => void; }; export type PluginExtensionCommandPaletteContext = {}; diff --git a/packages/grafana-data/src/types/time.ts b/packages/grafana-data/src/types/time.ts index f3eeefac9ce..688c896ee6e 100644 --- a/packages/grafana-data/src/types/time.ts +++ b/packages/grafana-data/src/types/time.ts @@ -76,7 +76,7 @@ export function getDefaultTimeRange(): TimeRange { } /** - * Returns the default realtive time range. + * Returns the default relative time range. * * @public */ diff --git a/packages/grafana-data/src/utils/dataLinks.ts b/packages/grafana-data/src/utils/dataLinks.ts index 0ca5e4e64f1..51dac431148 100644 --- a/packages/grafana-data/src/utils/dataLinks.ts +++ b/packages/grafana-data/src/utils/dataLinks.ts @@ -38,7 +38,11 @@ export type LinkToExploreOptions = { export function mapInternalLinkToExplore(options: LinkToExploreOptions): LinkModel { const { onClickFn, replaceVariables, link, scopedVars, range, field, internalLink } = options; - const interpolatedQuery = interpolateObject(link.internal?.query, scopedVars, replaceVariables); + const query = + typeof link.internal?.query === 'function' + ? link.internal.query({ replaceVariables, scopedVars }) + : internalLink.query; + const interpolatedQuery = interpolateObject(query, scopedVars, replaceVariables); const interpolatedPanelsState = interpolateObject(link.internal?.panelsState, scopedVars, replaceVariables); const interpolatedCorrelationData = interpolateObject(link.meta?.correlationData, scopedVars, replaceVariables); const title = link.title ? link.title : internalLink.datasourceName; diff --git a/packages/grafana-data/src/utils/nodeGraph.ts b/packages/grafana-data/src/utils/nodeGraph.ts index e986804e89a..b3241370e5e 100644 --- a/packages/grafana-data/src/utils/nodeGraph.ts +++ b/packages/grafana-data/src/utils/nodeGraph.ts @@ -44,4 +44,7 @@ export enum NodeGraphDataFrameFieldNames { // Supplies a fixed Y position for the node to have in the finished graph. fixedY = 'fixedy', + + // Whether the node is instrumented or not + isInstrumented = 'isinstrumented', } diff --git a/packages/grafana-data/src/utils/unaryOperators.ts b/packages/grafana-data/src/utils/unaryOperators.ts index a7e9a85b782..e440382f31e 100644 --- a/packages/grafana-data/src/utils/unaryOperators.ts +++ b/packages/grafana-data/src/utils/unaryOperators.ts @@ -4,6 +4,7 @@ export enum UnaryOperationID { Abs = 'abs', Exp = 'exp', Ln = 'ln', + Round = 'round', Floor = 'floor', Ceil = 'ceil', } @@ -35,6 +36,12 @@ export const unaryOperators = new Registry(() => { operation: (value: number) => Math.log(value), unaryOperationID: UnaryOperationID.Ln, }, + { + id: UnaryOperationID.Round, + name: 'Round', + operation: (value: number) => Math.round(value), + unaryOperationID: UnaryOperationID.Round, + }, { id: UnaryOperationID.Floor, name: 'Floor', diff --git a/packages/grafana-data/tsconfig.json b/packages/grafana-data/tsconfig.json index d6da863dda3..26f29296c23 100644 --- a/packages/grafana-data/tsconfig.json +++ b/packages/grafana-data/tsconfig.json @@ -8,5 +8,11 @@ }, "exclude": ["dist/**/*"], "extends": "@grafana/tsconfig", - "include": ["src/**/*.ts*", "typings/jest", "../../public/app/types/jquery/*.ts", "../../public/app/types/*.d.ts"] + "include": [ + "src/**/*.ts*", + "typings/jest", + "../../public/app/types/jquery/*.ts", + "../../public/app/types/*.d.ts", + "../grafana-ui/src/types/react-table-config.d.ts" + ] } diff --git a/packages/grafana-e2e-selectors/package.json b/packages/grafana-e2e-selectors/package.json index 0b2c08dc592..739e36cfec8 100644 --- a/packages/grafana-e2e-selectors/package.json +++ b/packages/grafana-e2e-selectors/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/e2e-selectors", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "description": "Grafana End-to-End Test Selectors Library", "keywords": [ "cli", @@ -40,18 +40,18 @@ }, "devDependencies": { "@rollup/plugin-node-resolve": "16.0.0", - "@types/node": "22.10.7", + "@types/node": "22.12.0", "@types/semver": "7.5.8", - "esbuild": "0.24.2", + "esbuild": "0.25.0", "rimraf": "6.0.1", "rollup": "^4.22.4", "rollup-plugin-dts": "^6.1.1", - "rollup-plugin-esbuild": "6.1.1", + "rollup-plugin-esbuild": "6.2.0", "rollup-plugin-node-externals": "^8.0.0" }, "dependencies": { "@grafana/tsconfig": "^2.0.0", - "semver": "7.6.3", + "semver": "7.7.0", "tslib": "2.8.1", "typescript": "5.7.3" } diff --git a/packages/grafana-e2e-selectors/rollup.config.ts b/packages/grafana-e2e-selectors/rollup.config.ts index b70662c33a8..338dae23ac9 100644 --- a/packages/grafana-e2e-selectors/rollup.config.ts +++ b/packages/grafana-e2e-selectors/rollup.config.ts @@ -1,53 +1,15 @@ -import resolve from '@rollup/plugin-node-resolve'; import { createRequire } from 'node:module'; -import path from 'path'; -import dts from 'rollup-plugin-dts'; -import esbuild from 'rollup-plugin-esbuild'; -import { nodeExternals } from 'rollup-plugin-node-externals'; + +import { cjsOutput, entryPoint, esmOutput, plugins, tsDeclarationOutput } from '../rollup.config.parts'; const rq = createRequire(import.meta.url); const pkg = rq('./package.json'); -const legacyOutputDefaults = { - esModule: true, - interop: 'compat', -}; - export default [ { - input: 'src/index.ts', - plugins: [ - nodeExternals({ deps: true, packagePath: './package.json' }), - resolve(), - esbuild({ - target: 'es2018', - tsconfig: 'tsconfig.build.json', - }), - ], - output: [ - { - format: 'cjs', - sourcemap: true, - dir: path.dirname(pkg.publishConfig.main), - ...legacyOutputDefaults, - }, - { - format: 'esm', - sourcemap: true, - dir: path.dirname(pkg.publishConfig.module), - preserveModules: true, - // @ts-expect-error (TS cannot assure that `process.env.PROJECT_CWD` is a string) - preserveModulesRoot: path.join(process.env.PROJECT_CWD, `packages/grafana-e2e-selectors/src`), - ...legacyOutputDefaults, - }, - ], - }, - { - input: './compiled/index.d.ts', - plugins: [dts()], - output: { - file: pkg.publishConfig.types, - format: 'es', - }, + input: entryPoint, + plugins, + output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-e2e-selectors')], }, + tsDeclarationOutput(pkg), ]; diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts index df60344157d..0ad5ecb22e1 100644 --- a/packages/grafana-e2e-selectors/src/selectors/components.ts +++ b/packages/grafana-e2e-selectors/src/selectors/components.ts @@ -661,6 +661,12 @@ export const versionedComponents = { newEvaluationGroupCreate: { '11.1.0': 'data-testid alert-rule new-evaluation-group-create-button', }, + step: { + '11.5.0': (stepNo: string) => `data-testid alert-rule step-${stepNo}`, + }, + stepAdvancedModeSwitch: { + '11.5.0': (stepNo: string) => `data-testid advanced-mode-switch step-${stepNo}`, + }, }, Alert: { alertV2: { diff --git a/packages/grafana-eslint-rules/package.json b/packages/grafana-eslint-rules/package.json index 7a124984fe3..fab662fb0ac 100644 --- a/packages/grafana-eslint-rules/package.json +++ b/packages/grafana-eslint-rules/package.json @@ -1,7 +1,7 @@ { "name": "@grafana/eslint-plugin", "description": "ESLint rules for use within the Grafana repo. Not suitable (or supported) for external use.", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "main": "./index.cjs", "author": "Grafana Labs", "license": "Apache-2.0", @@ -18,7 +18,7 @@ }, "devDependencies": { "@typescript-eslint/types": "^8.9.0", - "eslint": "9.18.0", + "eslint": "9.19.0", "tslib": "2.8.1" }, "private": true diff --git a/packages/grafana-flamegraph/package.json b/packages/grafana-flamegraph/package.json index 9ff5b7b4f8b..3eb48f5eaef 100644 --- a/packages/grafana-flamegraph/package.json +++ b/packages/grafana-flamegraph/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/flamegraph", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "description": "Grafana flamegraph visualization component", "keywords": [ "grafana", @@ -44,8 +44,8 @@ ], "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "@leeoniya/ufuzzy": "1.0.18", "d3": "^7.8.5", "lodash": "4.17.21", @@ -56,29 +56,29 @@ "tslib": "2.8.1" }, "devDependencies": { - "@babel/core": "7.26.0", - "@babel/preset-env": "7.26.0", + "@babel/core": "7.26.7", + "@babel/preset-env": "7.26.7", "@babel/preset-react": "7.26.3", "@grafana/tsconfig": "^2.0.0", "@rollup/plugin-node-resolve": "16.0.0", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "^6.1.2", - "@testing-library/react": "16.1.0", - "@testing-library/user-event": "14.5.2", + "@testing-library/react": "16.2.0", + "@testing-library/user-event": "14.6.1", "@types/d3": "^7", "@types/jest": "^29.5.4", - "@types/lodash": "4.17.14", - "@types/node": "22.10.7", + "@types/lodash": "4.17.15", + "@types/node": "22.12.0", "@types/react": "18.3.18", "@types/react-virtualized-auto-sizer": "1.0.4", "@types/tinycolor2": "1.4.6", "babel-jest": "29.7.0", - "esbuild": "0.24.2", + "esbuild": "0.25.0", "jest": "^29.6.4", "jest-canvas-mock": "2.5.2", "rollup": "^4.22.4", "rollup-plugin-dts": "^6.1.1", - "rollup-plugin-esbuild": "6.1.1", + "rollup-plugin-esbuild": "6.2.0", "rollup-plugin-node-externals": "^8.0.0", "ts-jest": "29.2.5", "ts-node": "10.9.2", diff --git a/packages/grafana-flamegraph/rollup.config.ts b/packages/grafana-flamegraph/rollup.config.ts index 86d0ad86cfa..dbd64b4abdb 100644 --- a/packages/grafana-flamegraph/rollup.config.ts +++ b/packages/grafana-flamegraph/rollup.config.ts @@ -1,53 +1,15 @@ -import resolve from '@rollup/plugin-node-resolve'; import { createRequire } from 'node:module'; -import path from 'path'; -import dts from 'rollup-plugin-dts'; -import esbuild from 'rollup-plugin-esbuild'; -import { nodeExternals } from 'rollup-plugin-node-externals'; + +import { cjsOutput, entryPoint, esmOutput, plugins, tsDeclarationOutput } from '../rollup.config.parts'; const rq = createRequire(import.meta.url); const pkg = rq('./package.json'); -const legacyOutputDefaults = { - esModule: true, - interop: 'compat', -}; - export default [ { - input: 'src/index.ts', - plugins: [ - nodeExternals({ deps: true, packagePath: './package.json' }), - resolve(), - esbuild({ - target: 'es2018', - tsconfig: 'tsconfig.build.json', - }), - ], - output: [ - { - format: 'cjs', - sourcemap: true, - dir: path.dirname(pkg.publishConfig.main), - ...legacyOutputDefaults, - }, - { - format: 'esm', - sourcemap: true, - dir: path.dirname(pkg.publishConfig.module), - preserveModules: true, - // @ts-expect-error (TS cannot assure that `process.env.PROJECT_CWD` is a string) - preserveModulesRoot: path.join(process.env.PROJECT_CWD, `packages/grafana-ui/src`), - ...legacyOutputDefaults, - }, - ], - }, - { - input: './compiled/index.d.ts', - plugins: [dts()], - output: { - file: pkg.publishConfig.types, - format: 'es', - }, + input: entryPoint, + plugins, + output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-flamegraph')], }, + tsDeclarationOutput(pkg), ]; diff --git a/packages/grafana-icons/package.json b/packages/grafana-icons/package.json index d7b9d631c27..c08c70e14ed 100644 --- a/packages/grafana-icons/package.json +++ b/packages/grafana-icons/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/saga-icons", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "private": true, "description": "Icons for Grafana", "author": "Grafana Labs", @@ -16,6 +16,7 @@ "types": "src/index.ts", "publishConfig": { "main": "dist/index.js", + "module": "dist/index.js", "types": "dist/index.d.ts", "access": "public" }, @@ -34,7 +35,7 @@ "build": "yarn generate && rollup -c rollup.config.ts --configPlugin esbuild" }, "devDependencies": { - "@babel/core": "7.26.0", + "@babel/core": "7.26.7", "@grafana/tsconfig": "^2.0.0", "@rollup/plugin-node-resolve": "^16.0.0", "@rollup/plugin-typescript": "^12.1.0", @@ -45,17 +46,17 @@ "@svgr/plugin-prettier": "^8.1.0", "@svgr/plugin-svgo": "^8.1.0", "@types/babel__core": "^7", - "@types/node": "22.10.7", + "@types/node": "22.12.0", "@types/react": "18.3.18", "@types/react-dom": "18.3.5", - "esbuild": "0.24.2", + "esbuild": "0.25.0", "prettier": "3.4.2", "react": "18.3.1", "react-dom": "18.3.1", "rimraf": "6.0.1", "rollup": "^4.22.4", "rollup-plugin-dts": "^6.1.1", - "rollup-plugin-esbuild": "6.1.1", + "rollup-plugin-esbuild": "6.2.0", "rollup-plugin-node-externals": "8.0.0", "ts-node": "10.9.2", "typescript": "5.7.3" diff --git a/packages/grafana-icons/rollup.config.ts b/packages/grafana-icons/rollup.config.ts index 6bf3c3b536e..fde6ca115b0 100644 --- a/packages/grafana-icons/rollup.config.ts +++ b/packages/grafana-icons/rollup.config.ts @@ -1,45 +1,15 @@ -import resolve from '@rollup/plugin-node-resolve'; import { createRequire } from 'node:module'; -import path from 'path'; -import dts from 'rollup-plugin-dts'; -import esbuild from 'rollup-plugin-esbuild'; -import { nodeExternals } from 'rollup-plugin-node-externals'; + +import { entryPoint, esmOutput, plugins, tsDeclarationOutput } from '../rollup.config.parts'; const rq = createRequire(import.meta.url); const pkg = rq('./package.json'); -const legacyOutputDefaults = { - esModule: true, - interop: 'compat', -}; - export default [ { - input: 'src/index.ts', - plugins: [ - nodeExternals({ deps: true, packagePath: './package.json' }), - resolve(), - esbuild({ - target: 'es2018', - tsconfig: 'tsconfig.build.json', - }), - ], - output: [ - { - format: 'esm', - sourcemap: true, - dir: path.dirname(pkg.publishConfig.main), - preserveModules: true, - ...legacyOutputDefaults, - }, - ], - }, - { - input: 'src/index.ts', - plugins: [dts()], - output: { - file: pkg.publishConfig.types, - format: 'es', - }, + input: entryPoint, + plugins, + output: esmOutput(pkg, 'grafana-icons'), }, + tsDeclarationOutput(pkg, { input: 'src/index.ts' }), ]; diff --git a/packages/grafana-icons/svg/drilldown.svg b/packages/grafana-icons/svg/drilldown.svg new file mode 100644 index 00000000000..27f627ce19f --- /dev/null +++ b/packages/grafana-icons/svg/drilldown.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/grafana-o11y-ds-frontend/package.json b/packages/grafana-o11y-ds-frontend/package.json index 684b9aed7fc..9bf98a363fc 100644 --- a/packages/grafana-o11y-ds-frontend/package.json +++ b/packages/grafana-o11y-ds-frontend/package.json @@ -3,7 +3,7 @@ "license": "AGPL-3.0-only", "name": "@grafana/o11y-ds-frontend", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "description": "Library to manage traces in Grafana.", "sideEffects": false, "repository": { @@ -18,13 +18,13 @@ }, "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", - "@grafana/e2e-selectors": "11.5.0-pre", - "@grafana/experimental": "2.1.6", - "@grafana/runtime": "11.5.0-pre", - "@grafana/schema": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", - "react-select": "5.9.0", + "@grafana/data": "11.6.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", + "@grafana/plugin-ui": "0.10.1", + "@grafana/runtime": "11.6.0-pre", + "@grafana/schema": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", + "react-select": "5.10.0", "react-use": "17.6.0", "rxjs": "7.8.1", "tslib": "2.8.1" @@ -33,10 +33,10 @@ "@grafana/tsconfig": "^2.0.0", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "^6.1.2", - "@testing-library/react": "16.1.0", - "@testing-library/user-event": "14.5.2", + "@testing-library/react": "16.2.0", + "@testing-library/user-event": "14.6.1", "@types/jest": "^29.5.4", - "@types/node": "22.10.7", + "@types/node": "22.12.0", "@types/react": "18.3.18", "@types/systemjs": "6.15.1", "jest": "^29.6.4", diff --git a/packages/grafana-o11y-ds-frontend/src/NodeGraph/NodeGraphSettings.tsx b/packages/grafana-o11y-ds-frontend/src/NodeGraph/NodeGraphSettings.tsx index 8b6f3c5f4b6..fd9ba9cd6c9 100644 --- a/packages/grafana-o11y-ds-frontend/src/NodeGraph/NodeGraphSettings.tsx +++ b/packages/grafana-o11y-ds-frontend/src/NodeGraph/NodeGraphSettings.tsx @@ -7,7 +7,7 @@ import { GrafanaTheme2, updateDatasourcePluginJsonDataOption, } from '@grafana/data'; -import { ConfigDescriptionLink, ConfigSubSection } from '@grafana/experimental'; +import { ConfigDescriptionLink, ConfigSubSection } from '@grafana/plugin-ui'; import { InlineField, InlineFieldRow, InlineSwitch, useStyles2 } from '@grafana/ui'; export interface NodeGraphOptions { diff --git a/packages/grafana-o11y-ds-frontend/src/SpanBar/SpanBarSettings.tsx b/packages/grafana-o11y-ds-frontend/src/SpanBar/SpanBarSettings.tsx index a612dee380d..11710cef528 100644 --- a/packages/grafana-o11y-ds-frontend/src/SpanBar/SpanBarSettings.tsx +++ b/packages/grafana-o11y-ds-frontend/src/SpanBar/SpanBarSettings.tsx @@ -7,7 +7,7 @@ import { toOption, updateDatasourcePluginJsonDataOption, } from '@grafana/data'; -import { ConfigDescriptionLink, ConfigSubSection } from '@grafana/experimental'; +import { ConfigDescriptionLink, ConfigSubSection } from '@grafana/plugin-ui'; import { InlineField, InlineFieldRow, Input, Select, useStyles2 } from '@grafana/ui'; export interface SpanBarOptions { diff --git a/packages/grafana-o11y-ds-frontend/src/TraceToLogs/TraceToLogsSettings.tsx b/packages/grafana-o11y-ds-frontend/src/TraceToLogs/TraceToLogsSettings.tsx index d94e09b0411..b5bca15f317 100644 --- a/packages/grafana-o11y-ds-frontend/src/TraceToLogs/TraceToLogsSettings.tsx +++ b/packages/grafana-o11y-ds-frontend/src/TraceToLogs/TraceToLogsSettings.tsx @@ -3,7 +3,7 @@ import { useCallback, useMemo } from 'react'; import * as React from 'react'; import { DataSourceJsonData, DataSourceInstanceSettings, DataSourcePluginOptionsEditorProps } from '@grafana/data'; -import { ConfigDescriptionLink, ConfigSection } from '@grafana/experimental'; +import { ConfigDescriptionLink, ConfigSection } from '@grafana/plugin-ui'; import { DataSourcePicker } from '@grafana/runtime'; import { InlineField, InlineFieldRow, Input, InlineSwitch } from '@grafana/ui'; diff --git a/packages/grafana-o11y-ds-frontend/src/TraceToMetrics/TraceToMetricsSettings.tsx b/packages/grafana-o11y-ds-frontend/src/TraceToMetrics/TraceToMetricsSettings.tsx index 98cff44f304..966c1703b54 100644 --- a/packages/grafana-o11y-ds-frontend/src/TraceToMetrics/TraceToMetricsSettings.tsx +++ b/packages/grafana-o11y-ds-frontend/src/TraceToMetrics/TraceToMetricsSettings.tsx @@ -7,7 +7,7 @@ import { GrafanaTheme2, updateDatasourcePluginJsonDataOption, } from '@grafana/data'; -import { ConfigDescriptionLink, ConfigSection } from '@grafana/experimental'; +import { ConfigDescriptionLink, ConfigSection } from '@grafana/plugin-ui'; import { DataSourcePicker } from '@grafana/runtime'; import { Button, InlineField, InlineFieldRow, Input, useStyles2 } from '@grafana/ui'; diff --git a/packages/grafana-o11y-ds-frontend/src/TraceToProfiles/TraceToProfilesSettings.tsx b/packages/grafana-o11y-ds-frontend/src/TraceToProfiles/TraceToProfilesSettings.tsx index 9b2d5ca31cc..cb5af242966 100644 --- a/packages/grafana-o11y-ds-frontend/src/TraceToProfiles/TraceToProfilesSettings.tsx +++ b/packages/grafana-o11y-ds-frontend/src/TraceToProfiles/TraceToProfilesSettings.tsx @@ -9,7 +9,7 @@ import { DataSourcePluginOptionsEditorProps, updateDatasourcePluginJsonDataOption, } from '@grafana/data'; -import { ConfigDescriptionLink, ConfigSection } from '@grafana/experimental'; +import { ConfigDescriptionLink, ConfigSection } from '@grafana/plugin-ui'; import { DataSourcePicker, DataSourceWithBackend, getDataSourceSrv } from '@grafana/runtime'; import { InlineField, InlineFieldRow, Input, InlineSwitch } from '@grafana/ui'; diff --git a/packages/grafana-plugin-configs/package.json b/packages/grafana-plugin-configs/package.json index cb36a58bda0..384e4c2d42c 100644 --- a/packages/grafana-plugin-configs/package.json +++ b/packages/grafana-plugin-configs/package.json @@ -2,17 +2,17 @@ "name": "@grafana/plugin-configs", "description": "Shared dependencies and files for core plugins", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "tslib": "2.8.1" }, "devDependencies": { "@grafana/tsconfig": "^2.0.0", - "@swc/core": "1.10.9", + "@swc/core": "1.10.12", "@types/eslint": "9.6.1", "@types/webpack-bundle-analyzer": "^4.7.0", "copy-webpack-plugin": "12.0.2", - "eslint": "9.18.0", + "eslint": "9.19.0", "eslint-webpack-plugin": "4.2.0", "fork-ts-checker-webpack-plugin": "9.0.2", "glob": "11.0.1", diff --git a/packages/grafana-prometheus/package.json b/packages/grafana-prometheus/package.json index b2d54ad027b..4232a385393 100644 --- a/packages/grafana-prometheus/package.json +++ b/packages/grafana-prometheus/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "AGPL-3.0-only", "name": "@grafana/prometheus", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "description": "Grafana Prometheus Library", "keywords": [ "typescript" @@ -38,105 +38,63 @@ "dependencies": { "@emotion/css": "11.13.5", "@floating-ui/react": "0.27.3", - "@grafana/data": "11.5.0-pre", - "@grafana/experimental": "2.1.6", - "@grafana/faro-web-sdk": "1.12.3", - "@grafana/runtime": "11.5.0-pre", - "@grafana/schema": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", + "@grafana/plugin-ui": "0.10.1", + "@grafana/runtime": "11.6.0-pre", + "@grafana/schema": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "@hello-pangea/dnd": "17.0.0", "@leeoniya/ufuzzy": "1.0.18", "@lezer/common": "1.2.3", - "@lezer/highlight": "1.2.1", "@lezer/lr": "1.4.2", "@prometheus-io/lezer-promql": "0.301.0", - "@reduxjs/toolkit": "2.5.0", - "d3": "7.9.0", - "date-fns": "4.1.0", - "debounce-promise": "3.1.2", - "eventemitter3": "5.0.1", - "lodash": "4.17.21", - "lru-cache": "11.0.2", - "marked": "15.0.6", - "marked-mangle": "1.1.10", - "moment": "2.30.1", - "moment-timezone": "0.5.46", - "monaco-promql": "1.7.4", - "pluralize": "8.0.0", - "prismjs": "1.29.0", - "react-highlight-words": "0.20.0", - "react-select": "5.9.0", - "react-use": "17.6.0", - "react-window": "1.8.11", - "rxjs": "7.8.1", - "semver": "7.6.3", - "tslib": "2.8.1", - "uuid": "11.0.5", - "whatwg-fetch": "3.6.20" - }, - "devDependencies": { - "@emotion/eslint-plugin": "11.12.0", - "@grafana/e2e-selectors": "11.5.0-pre", - "@grafana/tsconfig": "^2.0.0", - "@rollup/plugin-image": "3.0.3", - "@rollup/plugin-node-resolve": "16.0.0", - "@swc/core": "1.10.9", - "@swc/helpers": "0.5.15", - "@testing-library/dom": "10.4.0", - "@testing-library/jest-dom": "6.6.3", - "@testing-library/react": "16.1.0", - "@testing-library/user-event": "14.5.2", - "@types/d3": "7.4.3", + "@reduxjs/toolkit": "2.5.1", "@types/debounce-promise": "3.1.9", - "@types/eslint": "9.6.1", - "@types/jest": "29.5.14", - "@types/jquery": "3.5.32", - "@types/lodash": "4.17.14", - "@types/node": "22.10.7", - "@types/pluralize": "^0.0.33", - "@types/prismjs": "1.26.5", + "@types/lodash": "4.17.15", "@types/react": "18.3.18", "@types/react-dom": "18.3.5", "@types/react-highlight-words": "0.20.0", "@types/react-window": "1.8.8", "@types/semver": "7.5.8", "@types/uuid": "10.0.0", - "@typescript-eslint/eslint-plugin": "8.18.1", - "@typescript-eslint/parser": "8.18.1", - "copy-webpack-plugin": "12.0.2", - "css-loader": "7.1.2", - "esbuild": "0.24.2", - "eslint": "9.18.0", - "eslint-config-prettier": "9.1.0", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-jest": "28.11.0", - "eslint-plugin-jsdoc": "50.6.2", - "eslint-plugin-jsx-a11y": "6.10.2", - "eslint-plugin-lodash": "8.0.0", - "eslint-plugin-react": "7.37.4", - "eslint-plugin-react-hooks": "5.1.0", - "eslint-webpack-plugin": "4.2.0", - "fork-ts-checker-webpack-plugin": "9.0.2", - "glob": "11.0.1", + "debounce-promise": "3.1.2", + "lodash": "4.17.21", + "moment": "2.30.1", + "moment-timezone": "0.5.47", + "monaco-promql": "1.7.4", + "pluralize": "8.0.0", + "prismjs": "1.29.0", + "react-highlight-words": "0.21.0", + "react-use": "17.6.0", + "react-window": "1.8.11", + "rxjs": "7.8.1", + "semver": "7.7.0", + "uuid": "11.0.5" + }, + "devDependencies": { + "@grafana/tsconfig": "^2.0.0", + "@rollup/plugin-image": "3.0.3", + "@rollup/plugin-node-resolve": "16.0.0", + "@testing-library/jest-dom": "6.6.3", + "@testing-library/react": "16.2.0", + "@testing-library/user-event": "14.6.1", + "@types/jest": "29.5.14", + "@types/node": "22.12.0", + "@types/pluralize": "^0.0.33", + "@types/prismjs": "1.26.5", + "esbuild": "0.25.0", "jest": "29.7.0", "jest-environment-jsdom": "29.7.0", - "jest-matcher-utils": "29.7.0", - "prettier": "3.4.2", "react": "18.3.1", "react-dom": "18.3.1", "react-select-event": "5.5.1", "rollup": "^4.22.4", "rollup-plugin-dts": "^6.1.1", - "rollup-plugin-esbuild": "6.1.1", + "rollup-plugin-esbuild": "6.2.0", "rollup-plugin-node-externals": "^8.0.0", - "sass": "1.83.4", - "sass-loader": "16.0.4", - "style-loader": "4.0.0", "testing-library-selector": "0.3.1", - "ts-node": "10.9.2", - "typescript": "5.7.3", - "webpack": "5.97.1", - "webpack-cli": "6.0.1" + "typescript": "5.7.3" }, "peerDependencies": { "react": "^18.0.0", diff --git a/packages/grafana-prometheus/rollup.config.ts b/packages/grafana-prometheus/rollup.config.ts index 7e630257c36..5c7d9dd3c04 100644 --- a/packages/grafana-prometheus/rollup.config.ts +++ b/packages/grafana-prometheus/rollup.config.ts @@ -1,55 +1,16 @@ import image from '@rollup/plugin-image'; -import resolve from '@rollup/plugin-node-resolve'; import { createRequire } from 'node:module'; -import path from 'path'; -import dts from 'rollup-plugin-dts'; -import esbuild from 'rollup-plugin-esbuild'; -import { nodeExternals } from 'rollup-plugin-node-externals'; + +import { cjsOutput, entryPoint, esmOutput, plugins, tsDeclarationOutput } from '../rollup.config.parts'; const rq = createRequire(import.meta.url); const pkg = rq('./package.json'); -const legacyOutputDefaults = { - esModule: true, - interop: 'compat', -}; - export default [ { - input: 'src/index.ts', - plugins: [ - nodeExternals({ deps: true, packagePath: './package.json' }), - resolve(), - esbuild({ - target: 'es2018', - tsconfig: 'tsconfig.build.json', - }), - image(), - ], - output: [ - { - format: 'cjs', - sourcemap: true, - dir: path.dirname(pkg.publishConfig.main), - ...legacyOutputDefaults, - }, - { - format: 'esm', - sourcemap: true, - dir: path.dirname(pkg.publishConfig.module), - preserveModules: true, - // @ts-expect-error (TS cannot assure that `process.env.PROJECT_CWD` is a string) - preserveModulesRoot: path.join(process.env.PROJECT_CWD, `packages/grafana-prometheus/src`), - ...legacyOutputDefaults, - }, - ], - }, - { - input: './compiled/index.d.ts', - plugins: [dts()], - output: { - file: pkg.publishConfig.types, - format: 'es', - }, + input: entryPoint, + plugins: [...plugins, image()], + output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-prometheus')], }, + tsDeclarationOutput(pkg), ]; diff --git a/packages/grafana-prometheus/src/components/AnnotationQueryEditor.tsx b/packages/grafana-prometheus/src/components/AnnotationQueryEditor.tsx index 04a50e72f10..93368508d0b 100644 --- a/packages/grafana-prometheus/src/components/AnnotationQueryEditor.tsx +++ b/packages/grafana-prometheus/src/components/AnnotationQueryEditor.tsx @@ -1,7 +1,7 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/components/AnnotationQueryEditor.tsx import { AnnotationQuery } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { EditorField, EditorRow, EditorRows, EditorSwitch } from '@grafana/experimental'; +import { EditorField, EditorRow, EditorRows, EditorSwitch } from '@grafana/plugin-ui'; import { AutoSizeInput, Input, Space } from '@grafana/ui'; import { PromQueryCodeEditor } from '../querybuilder/components/PromQueryCodeEditor'; diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx index 5115c6880d1..97e49df416b 100644 --- a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx +++ b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx @@ -1,7 +1,6 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/components/monaco-query-field/MonacoQueryField.tsx import { css } from '@emotion/css'; import { parser } from '@prometheus-io/lezer-promql'; -import { debounce } from 'lodash'; import { promLanguageDefinition } from 'monaco-promql'; import { useEffect, useRef } from 'react'; import { useLatest } from 'react-use'; @@ -88,6 +87,7 @@ const getStyles = (theme: GrafanaTheme2, placeholder: string) => { justifyContent: 'start', alignItems: 'center', height: '100%', + overflow: 'hidden', }), placeholder: css({ '::after': { @@ -105,13 +105,12 @@ const MonacoQueryField = (props: Props) => { // we need only one instance of `overrideServices` during the lifetime of the react component const overrideServicesRef = useRef(getOverrideServices()); const containerRef = useRef(null); - const { languageProvider, history, onBlur, onRunQuery, initialValue, placeholder, onChange, datasource } = props; + const { languageProvider, history, onBlur, onRunQuery, initialValue, placeholder, datasource } = props; const lpRef = useLatest(languageProvider); const historyRef = useLatest(history); const onRunQueryRef = useLatest(onRunQuery); const onBlurRef = useLatest(onBlur); - const onChangeRef = useLatest(onChange); const autocompleteDisposeFun = useRef<(() => void) | null>(null); @@ -133,6 +132,8 @@ const MonacoQueryField = (props: Props) => { ref={containerRef} > { editor.onDidContentSizeChange(updateElementHeight); updateElementHeight(); - // Whenever the editor changes, lets save the last value so the next query for this editor will be up-to-date. - // This change is being introduced to fix a bug where you can submit a query via shift+enter: - // If you clicked into another field and haven't un-blurred the active field, - // then the query that is run will be stale, as the reference is only updated - // with the value of the last blurred input. - // This can run quite slowly, so we're debouncing this which should accomplish two things - // 1. Should prevent this function from blocking the current call stack by pushing into the web API callback queue - // 2. Should prevent a bunch of duplicates of this function being called as the user is typing - const updateCurrentEditorValue = debounce(() => { - const editorValue = editor.getValue(); - onChangeRef.current(editorValue); - }, lpRef.current.datasource.getDebounceTimeInMilliseconds()); - - editor.getModel()?.onDidChangeContent(() => { - updateCurrentEditorValue(); - }); - // handle: shift + enter // FIXME: maybe move this functionality into CodeEditor? editor.addCommand( @@ -234,9 +218,8 @@ const MonacoQueryField = (props: Props) => { command: null, }); - /* Something in this configuration of monaco doesn't bubble up [mod]+K, which the - command palette uses. Pass the event out of monaco manually - */ + // Something in this configuration of monaco doesn't bubble up [mod]+K, + // which the command palette uses. Pass the event out of monaco manually editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK, function () { global.dispatchEvent(new KeyboardEvent('keydown', { key: 'k', metaKey: true })); }); diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldLazy.tsx b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldLazy.tsx index 53aad0b3047..53869e3cf58 100644 --- a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldLazy.tsx +++ b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldLazy.tsx @@ -4,8 +4,6 @@ import { Suspense } from 'react'; import MonacoQueryField from './MonacoQueryField'; import { Props } from './MonacoQueryFieldProps'; -// const Field = React.lazy(() => import('./MonacoQueryField')); - export const MonacoQueryFieldLazy = (props: Props) => { return ( diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldProps.ts b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldProps.ts index a0d21761f2c..4ca3fd894ed 100644 --- a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldProps.ts +++ b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldProps.ts @@ -16,7 +16,5 @@ export type Props = { placeholder: string; onRunQuery: (value: string) => void; onBlur: (value: string) => void; - // onChange will never initiate a query, it just denotes that a query value has been changed - onChange: (value: string) => void; datasource: PrometheusDatasource; }; diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldWrapper.tsx b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldWrapper.tsx index c95e8341b92..b46234a3b6b 100644 --- a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldWrapper.tsx +++ b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldWrapper.tsx @@ -23,13 +23,5 @@ export const MonacoQueryFieldWrapper = (props: Props) => { onChange(value); }; - /** - * Handles changes without running any queries - * @param value - */ - const handleChange = (value: string) => { - onChange(value); - }; - - return ; + return ; }; diff --git a/packages/grafana-prometheus/src/configuration/AlertingSettingsOverhaul.tsx b/packages/grafana-prometheus/src/configuration/AlertingSettingsOverhaul.tsx index bade01d047e..9613ce2b829 100644 --- a/packages/grafana-prometheus/src/configuration/AlertingSettingsOverhaul.tsx +++ b/packages/grafana-prometheus/src/configuration/AlertingSettingsOverhaul.tsx @@ -3,7 +3,7 @@ import { cx } from '@emotion/css'; import { DataSourceJsonData, DataSourcePluginOptionsEditorProps } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { ConfigSubSection } from '@grafana/experimental'; +import { ConfigSubSection } from '@grafana/plugin-ui'; import { config } from '@grafana/runtime'; import { InlineField, Switch, useTheme2 } from '@grafana/ui'; diff --git a/packages/grafana-prometheus/src/configuration/ConfigEditor.tsx b/packages/grafana-prometheus/src/configuration/ConfigEditor.tsx index 8c46b9a7244..e86a9ba8796 100644 --- a/packages/grafana-prometheus/src/configuration/ConfigEditor.tsx +++ b/packages/grafana-prometheus/src/configuration/ConfigEditor.tsx @@ -2,7 +2,7 @@ import { css } from '@emotion/css'; import { DataSourcePluginOptionsEditorProps, GrafanaTheme2 } from '@grafana/data'; -import { ConfigSection, DataSourceDescription, AdvancedHttpSettings } from '@grafana/experimental'; +import { ConfigSection, DataSourceDescription, AdvancedHttpSettings } from '@grafana/plugin-ui'; import { config } from '@grafana/runtime'; import { Alert, FieldValidationMessage, useTheme2 } from '@grafana/ui'; diff --git a/packages/grafana-prometheus/src/configuration/DataSourceHttpSettingsOverhaul.tsx b/packages/grafana-prometheus/src/configuration/DataSourceHttpSettingsOverhaul.tsx index 04c77dfe854..03c8199b87a 100644 --- a/packages/grafana-prometheus/src/configuration/DataSourceHttpSettingsOverhaul.tsx +++ b/packages/grafana-prometheus/src/configuration/DataSourceHttpSettingsOverhaul.tsx @@ -1,6 +1,6 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/configuration/DataSourceHttpSettingsOverhaul.tsx import { DataSourceSettings } from '@grafana/data'; -import { Auth, AuthMethod, ConnectionSettings, convertLegacyAuthProps } from '@grafana/experimental'; +import { Auth, AuthMethod, ConnectionSettings, convertLegacyAuthProps } from '@grafana/plugin-ui'; import { SecureSocksProxySettings, useTheme2 } from '@grafana/ui'; import { PromOptions } from '../types'; diff --git a/packages/grafana-prometheus/src/configuration/ExemplarsSettings.tsx b/packages/grafana-prometheus/src/configuration/ExemplarsSettings.tsx index dc82d80984f..eab5b67b18d 100644 --- a/packages/grafana-prometheus/src/configuration/ExemplarsSettings.tsx +++ b/packages/grafana-prometheus/src/configuration/ExemplarsSettings.tsx @@ -2,7 +2,7 @@ import { css } from '@emotion/css'; import { selectors } from '@grafana/e2e-selectors'; -import { ConfigSubSection } from '@grafana/experimental'; +import { ConfigSubSection } from '@grafana/plugin-ui'; import { Button, useTheme2 } from '@grafana/ui'; import { ExemplarTraceIdDestination } from '../types'; diff --git a/packages/grafana-prometheus/src/configuration/PromSettings.tsx b/packages/grafana-prometheus/src/configuration/PromSettings.tsx index 9291ec95040..7d82845a4f3 100644 --- a/packages/grafana-prometheus/src/configuration/PromSettings.tsx +++ b/packages/grafana-prometheus/src/configuration/PromSettings.tsx @@ -8,7 +8,7 @@ import { updateDatasourcePluginJsonDataOption, } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { ConfigSubSection } from '@grafana/experimental'; +import { ConfigSubSection } from '@grafana/plugin-ui'; import { config } from '@grafana/runtime'; import { InlineField, Input, Select, Switch, useTheme2 } from '@grafana/ui'; diff --git a/packages/grafana-prometheus/src/dashboards/grafana_stats.json b/packages/grafana-prometheus/src/dashboards/grafana_stats.json index 0131aa9bc40..292f93394f3 100644 --- a/packages/grafana-prometheus/src/dashboards/grafana_stats.json +++ b/packages/grafana-prometheus/src/dashboards/grafana_stats.json @@ -1178,8 +1178,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Grafana metrics", diff --git a/packages/grafana-prometheus/src/dashboards/prometheus_2_stats.json b/packages/grafana-prometheus/src/dashboards/prometheus_2_stats.json index 034663fe6f4..5a6fbdf8518 100644 --- a/packages/grafana-prometheus/src/dashboards/prometheus_2_stats.json +++ b/packages/grafana-prometheus/src/dashboards/prometheus_2_stats.json @@ -1394,8 +1394,7 @@ }, "timepicker": { "now": true, - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "browser", "title": "Prometheus 2.0 Stats", diff --git a/packages/grafana-prometheus/src/dashboards/prometheus_stats.json b/packages/grafana-prometheus/src/dashboards/prometheus_stats.json index 8a2764c5cb7..42ea6e7a4d5 100644 --- a/packages/grafana-prometheus/src/dashboards/prometheus_stats.json +++ b/packages/grafana-prometheus/src/dashboards/prometheus_stats.json @@ -825,8 +825,7 @@ }, "timepicker": { "now": true, - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "browser", "title": "Prometheus Stats", diff --git a/packages/grafana-prometheus/src/datasource.test.ts b/packages/grafana-prometheus/src/datasource.test.ts index 54cdf88f8c6..1e11c3b54d8 100644 --- a/packages/grafana-prometheus/src/datasource.test.ts +++ b/packages/grafana-prometheus/src/datasource.test.ts @@ -1029,6 +1029,23 @@ describe('PrometheusDatasource', () => { expect(interval).toEqual({ text: '15s', value: '15s' }); expect(intervalMs).toEqual({ text: 15000, value: 15000 }); }); + + it('should use the default time range when no range provided in options', () => { + const prometheusDatasource = new PrometheusDatasource( + { ...instanceSettings, jsonData: { ...instanceSettings.jsonData, cacheLevel: PrometheusCacheLevel.None } }, + templateSrvStub + ); + const query = 'query_result(topk(5,rate(http_request_duration_microseconds_count[$__interval])))'; + prometheusDatasource.metricFindQuery(query); + + // Last 6h + const range = replaceMock.mock.calls[1][1].__range; + const rangeMs = replaceMock.mock.calls[1][1].__range_ms; + const rangeS = replaceMock.mock.calls[1][1].__range_s; + expect(range).toEqual({ text: '21600s', value: '21600s' }); + expect(rangeMs).toEqual({ text: 21600000, value: 21600000 }); + expect(rangeS).toEqual({ text: 21600, value: 21600 }); + }); }); }); diff --git a/packages/grafana-prometheus/src/datasource.ts b/packages/grafana-prometheus/src/datasource.ts index a54475cebdd..fe1607693b7 100644 --- a/packages/grafana-prometheus/src/datasource.ts +++ b/packages/grafana-prometheus/src/datasource.ts @@ -1,5 +1,6 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/datasource.ts import { defaults } from 'lodash'; +import { tz } from 'moment-timezone'; import { lastValueFrom, Observable, throwError } from 'rxjs'; import { map, tap } from 'rxjs/operators'; import semver from 'semver/preload'; @@ -373,13 +374,39 @@ export class PrometheusDatasource } processTargetV2(target: PromQuery, request: DataQueryRequest) { + // The `utcOffsetSec` parameter is required by the backend to correctly align time ranges. + // This alignment ensures that relative time ranges (e.g., "Last N hours/days/years") are adjusted + // according to the user's selected time zone, rather than defaulting to UTC. + // + // Example: If the user selects "Last 5 days," each day should begin at 00:00 in the chosen time zone, + // rather than at 00:00 UTC, ensuring an accurate breakdown. + // + // This adjustment does not apply to absolute time ranges, where users explicitly set + // the start and end timestamps. + // + // Handling `utcOffsetSec`: + // - When using the browser's time zone, the UTC offset is derived from the request range object. + // - When the user selects a custom time zone, the UTC offset must be calculated accordingly. + // More details: + // - Issue that led to the introduction of utcOffsetSec: https://github.com/grafana/grafana/issues/17278 + // - Implementation PR: https://github.com/grafana/grafana/pull/17477 + let utcOffset = request.range.to.utcOffset(); + if (request.timezone === 'browser') { + // we need to check if the request is a relative or absolute range. + // if it is absolute time range then utcOffset must be 0. we don't care the offset + // because we are already sending the from and to values in utc. we don't need to adjust them again + // for relative ranges we need utcOffset to adjust query range. + utcOffset = this.isUsingRelativeTimeRange(request.range) ? utcOffset : 0; + } else { + utcOffset = tz(request.timezone).utcOffset(); + } + const processedTargets: PromQuery[] = []; const processedTarget = { ...target, exemplar: this.shouldRunExemplarQuery(target, request), requestId: request.panelId + target.refId, - // We need to pass utcOffsetSec to backend to calculate aligned range - utcOffsetSec: request.range.to.utcOffset() * 60, + utcOffsetSec: utcOffset * 60, }; if (config.featureToggles.promQLScope) { @@ -456,13 +483,15 @@ export class PrometheusDatasource return Promise.resolve([]); } + const timeRange = options?.range ?? this.languageProvider.timeRange ?? getDefaultTimeRange(); + const scopedVars = { ...this.getIntervalVars(), - ...this.getRangeScopedVars(options?.range ?? getDefaultTimeRange()), + ...this.getRangeScopedVars(timeRange), }; const interpolated = this.templateSrv.replace(query, scopedVars, this.interpolateQueryExpr); const metricFindQuery = new PrometheusMetricFindQuery(this, interpolated); - return metricFindQuery.process(options?.range ?? getDefaultTimeRange()); + return metricFindQuery.process(timeRange); } getIntervalVars() { @@ -973,6 +1002,14 @@ export class PrometheusDatasource }; } + isUsingRelativeTimeRange(range: TimeRange): boolean { + if (typeof range.raw.from !== 'string' || typeof range.raw.to !== 'string') { + return false; + } + + return range.raw.from.includes('now') || range.raw.to.includes('now'); + } + getDebounceTimeInMilliseconds(): number { switch (this.cacheLevel) { case PrometheusCacheLevel.Medium: diff --git a/packages/grafana-prometheus/src/index.ts b/packages/grafana-prometheus/src/index.ts index 4eb15995aae..80fa0e5130d 100644 --- a/packages/grafana-prometheus/src/index.ts +++ b/packages/grafana-prometheus/src/index.ts @@ -55,7 +55,6 @@ export { PromQueryEditorSelector } from './querybuilder/components/PromQueryEdit export { PromQueryLegendEditor } from './querybuilder/components/PromQueryLegendEditor'; export { QueryPreview } from './querybuilder/components/QueryPreview'; export { MetricsModal } from './querybuilder/components/metrics-modal/MetricsModal'; -export { PromQail } from './querybuilder/components/promQail/PromQail'; // SRC/ // Main export @@ -86,7 +85,7 @@ export { } from './types'; export { PrometheusVariableSupport } from './variables'; -// For explore metrics +// For Metrics Drilldown export { default as PromQlLanguageProvider } from './language_provider'; export { getPrometheusTime } from './language_utils'; export { isValidLegacyName, utf8Support, wrapUtf8Filters } from './utf8_support'; diff --git a/packages/grafana-prometheus/src/query_hints.test.ts b/packages/grafana-prometheus/src/query_hints.test.ts index a5bd75b0f81..af2e9eee01a 100644 --- a/packages/grafana-prometheus/src/query_hints.test.ts +++ b/packages/grafana-prometheus/src/query_hints.test.ts @@ -1,6 +1,6 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/query_hints.test.ts import { QueryHint } from '@grafana/data'; -import { QueryBuilderLabelFilter } from '@grafana/experimental'; +import { QueryBuilderLabelFilter } from '@grafana/plugin-ui'; import { PrometheusDatasource } from './datasource'; import { @@ -8,6 +8,7 @@ import { getQueryHints, getQueryLabelsForRuleName, getRecordingRuleIdentifierIdx, + isRuleInQuery, SUM_HINT_THRESHOLD_COUNT, } from './query_hints'; import { buildVisualQueryFromString } from './querybuilder/parsing'; @@ -365,6 +366,92 @@ describe('getExpandRulesHints', () => { }, ]); }); + + it('should return expand rule hint, when given query include a non-unique rule name', () => { + const extractedMapping: RuleQueryMapping = { + 'duration:p95': [ + { + query: 'expanded_duration_p95{}', + labels: {}, + }, + { + query: 'expanded_duration_p95_aggregated{}', + labels: { + span_name: '__aggregated__', + }, + }, + ], + 'duration:p95:upper_threshold': [ + { + query: 'expanded_duration_p95_upper_threshold{}', + labels: {}, + }, + ], + }; + const query = 'sum(rate(duration:p95:upper_threshold{label="foo"}[5m])) by(bar)'; + const hints = getExpandRulesHints(query, extractedMapping); + expect(hints).toEqual([ + { + type: 'EXPAND_RULES', + label: 'Query contains recording rules.', + fix: { + label: 'Expand rules', + action: { + type: 'EXPAND_RULES', + query, + options: { + 'duration:p95:upper_threshold': { + expandedQuery: 'expanded_duration_p95_upper_threshold{}', + }, + }, + }, + }, + }, + ]); + }); + + it('should return expand rule hint, when given query include a non-unique rule name - second case', () => { + const extractedMapping: RuleQueryMapping = { + 'duration:p95': [ + { + query: 'expanded_duration_p95{}', + labels: {}, + }, + { + query: 'expanded_duration_p95_aggregated{}', + labels: { + span_name: '__aggregated__', + }, + }, + ], + 'upper_threshold:duration:p95': [ + { + query: 'expanded_duration_p95_upper_threshold{}', + labels: {}, + }, + ], + }; + const query = 'sum(rate(upper_threshold:duration:p95{label="foo"}[5m])) by(bar)'; + const hints = getExpandRulesHints(query, extractedMapping); + expect(hints).toEqual([ + { + type: 'EXPAND_RULES', + label: 'Query contains recording rules.', + fix: { + label: 'Expand rules', + action: { + type: 'EXPAND_RULES', + query, + options: { + 'upper_threshold:duration:p95': { + expandedQuery: 'expanded_duration_p95_upper_threshold{}', + }, + }, + }, + }, + }, + ]); + }); }); describe('getRecordingRuleIdentifierIdx', () => { @@ -505,3 +592,73 @@ describe('getQueryLabelsForRuleName', () => { expect(result).toEqual(expected); }); }); + +describe('ruleInQuery', () => { + it('should return true when ruleName is present in the query', () => { + expect(isRuleInQuery('rate(http_requests_total{job="api"}[5m])', 'http_requests_total')).toBe(true); + }); + + it('should return false when ruleName is not present in the query', () => { + expect(isRuleInQuery('rate(cpu_usage{instance="localhost"}[5m])', 'http_requests_total')).toBe(false); + }); + + it('should return true for ruleName at the start of the query', () => { + expect(isRuleInQuery('http_requests_total{job="api"}', 'http_requests_total')).toBe(true); + }); + + it('should return true for ruleName at the end of the query', () => { + expect(isRuleInQuery('sum by (instance) (http_requests_total)', 'http_requests_total')).toBe(true); + expect(isRuleInQuery('sum(http_requests_total)', 'http_requests_total')).toBe(true); + expect(isRuleInQuery('http_requests_total', 'http_requests_total')).toBe(true); + }); + + it('should return true when ruleName is followed by spaces', () => { + expect(isRuleInQuery('http_requests_total { job="api" }', 'http_requests_total')).toBe(true); + }); + + it('should return false when ruleName is a substring of another metric', () => { + expect(isRuleInQuery('rate(http_requests_total_new{job="api"}[5m])', 'http_requests_total')).toBe(false); + }); + + it('should return false for escaped ruleName usage', () => { + expect(isRuleInQuery('rate(\"http_requests_total\"{job="api"}[5m])', 'http_requests_total')).toBe(false); + }); + + it('should return false when query is empty', () => { + expect(isRuleInQuery('', 'http_requests_total')).toBe(false); + }); + + it('should return false when ruleName is an empty string', () => { + expect(isRuleInQuery('rate(http_requests_total{job="api"}[5m])', '')).toBe(false); + }); + + it('should return false when both query and ruleName are empty', () => { + expect(isRuleInQuery('', '')).toBe(false); + }); + + it('should return true when used with binary operations', () => { + expect(isRuleInQuery('rate(http_requests_total{job="api"}[5m]) + my:rule', 'my:rule')).toBe(true); + expect(isRuleInQuery('rate(http_requests_total{job="api"}[5m])+my:rule', 'my:rule')).toBe(true); + }); + + it('should return true when ruleName is inside nested functions', () => { + expect(isRuleInQuery('sum(rate(http_requests_total[5m]))', 'http_requests_total')).toBe(true); + }); + + it('should return false when ruleName is part of a label value', () => { + expect(isRuleInQuery('http_requests_total_bytes{rule="http_requests_total"}', 'http_requests_total')).toBe(false); + }); + + it('should return true when ruleName contains special characters like colon', () => { + expect(isRuleInQuery('rate(my_namespace:http_requests_total[5m])', 'my_namespace:http_requests_total')).toBe(true); + }); + + it('should return false when ruleName appears within string literals', () => { + expect( + isRuleInQuery( + 'label_replace(http_requests_total, "label", "value", "instance", "http_requests_total")', + 'http_requests_total' + ) + ).toBe(false); + }); +}); diff --git a/packages/grafana-prometheus/src/query_hints.ts b/packages/grafana-prometheus/src/query_hints.ts index 3fcdd66e0d2..73c3e90960e 100644 --- a/packages/grafana-prometheus/src/query_hints.ts +++ b/packages/grafana-prometheus/src/query_hints.ts @@ -175,10 +175,19 @@ export function getInitHints(datasource: PrometheusDatasource): QueryHint[] { return hints; } +export function isRuleInQuery(query: string, ruleName: string) { + if (!query || !ruleName) { + return false; + } + + const getRuleRegex = new RegExp(`(? { - if (query.search(ruleName) === -1) { + if (!isRuleInQuery(query, ruleName)) { return acc; } diff --git a/packages/grafana-prometheus/src/querybuilder/components/LabelFilterItem.tsx b/packages/grafana-prometheus/src/querybuilder/components/LabelFilterItem.tsx index ca60e3d4bfd..57ed3202bd9 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/LabelFilterItem.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/LabelFilterItem.tsx @@ -4,7 +4,7 @@ import { useState } from 'react'; import { SelectableValue, toOption } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { AccessoryButton, InputGroup } from '@grafana/experimental'; +import { AccessoryButton, InputGroup } from '@grafana/plugin-ui'; import { AsyncSelect, Select } from '@grafana/ui'; import { truncateResult } from '../../language_utils'; diff --git a/packages/grafana-prometheus/src/querybuilder/components/LabelFilters.tsx b/packages/grafana-prometheus/src/querybuilder/components/LabelFilters.tsx index 45b0b9866eb..0372156b666 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/LabelFilters.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/LabelFilters.tsx @@ -4,7 +4,7 @@ import { isEqual } from 'lodash'; import { useEffect, useState } from 'react'; import { SelectableValue } from '@grafana/data'; -import { EditorField, EditorFieldGroup, EditorList } from '@grafana/experimental'; +import { EditorField, EditorFieldGroup, EditorList } from '@grafana/plugin-ui'; import { InlineFieldRow, InlineLabel } from '@grafana/ui'; import { QueryBuilderLabelFilter } from '../shared/types'; diff --git a/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.test.tsx b/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.test.tsx index c9ff1d55cd2..c832a547bdc 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.test.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.test.tsx @@ -35,9 +35,13 @@ describe('MetricCombobox', () => { } as unknown as DataSourceInstanceSettings; const mockDatasource = new PrometheusDatasource(instanceSettings); - const mockValues = [{ label: 'random_metric' }, { label: 'unique_metric' }, { label: 'more_unique_metric' }]; - // Mock metricFindQuery which will call backend API + // Options returned when user first opens the combobox - returned by onGetMetrics + const initialMockValues = [{ label: 'top_metric_one' }, { label: 'top_metric_two' }, { label: 'top_metric_three' }]; + const mockOnGetMetrics = jest.fn(() => Promise.resolve(initialMockValues.map((v) => ({ value: v.label })))); + + // Options returned when user searches for a metric + const mockValues = [{ label: 'random_metric' }, { label: 'unique_metric' }, { label: 'more_unique_metric' }]; mockDatasource.metricFindQuery = jest.fn((query: string) => { // return Promise.resolve([]); // Use the label values regex to get the values inside the label_values function call @@ -61,7 +65,6 @@ describe('MetricCombobox', () => { }); const mockOnChange = jest.fn(); - const mockOnGetMetrics = jest.fn(() => Promise.resolve(mockValues.map((v) => ({ value: v.label })))); const defaultProps: MetricComboboxProps = { metricLookupDisabled: false, @@ -92,10 +95,11 @@ describe('MetricCombobox', () => { const combobox = screen.getByPlaceholderText('Select metric'); await userEvent.click(combobox); - expect(mockOnGetMetrics).toHaveBeenCalledTimes(1); - - const item = await screen.findByRole('option', { name: 'random_metric' }); + const item = await screen.findByRole('option', { name: 'top_metric_one' }); expect(item).toBeInTheDocument(); + + // This should be asserted by the above check, but double check anyway + expect(mockOnGetMetrics).toHaveBeenCalledTimes(1); }); it('fetches metrics for the users query', async () => { @@ -108,8 +112,9 @@ describe('MetricCombobox', () => { const item = await screen.findByRole('option', { name: 'unique_metric' }); expect(item).toBeInTheDocument(); - const negativeItem = screen.queryByRole('option', { name: 'random_metric' }); - expect(negativeItem).not.toBeInTheDocument(); + // This should be asserted by the above check, but double check anyway + // This is the actual argument, created by formatKeyValueStringsForLabelValuesQuery() + expect(mockDatasource.metricFindQuery).toHaveBeenCalledWith('label_values({__name__=~".*unique.*"},__name__)'); }); it('calls onChange with the correct value when a metric is selected', async () => { @@ -118,10 +123,10 @@ describe('MetricCombobox', () => { const combobox = screen.getByPlaceholderText('Select metric'); await userEvent.click(combobox); - const item = await screen.findByRole('option', { name: 'random_metric' }); + const item = await screen.findByRole('option', { name: 'top_metric_two' }); await userEvent.click(item); - expect(mockOnChange).toHaveBeenCalledWith({ metric: 'random_metric', labels: [], operations: [] }); + expect(mockOnChange).toHaveBeenCalledWith({ metric: 'top_metric_two', labels: [], operations: [] }); }); it('shows the metrics explorer button by default', () => { diff --git a/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.tsx b/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.tsx index 62e75dee1eb..b939c6b8aa1 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.tsx @@ -1,7 +1,7 @@ import { useCallback, useState } from 'react'; import { SelectableValue } from '@grafana/data'; -import { EditorField, EditorFieldGroup, InputGroup } from '@grafana/experimental'; +import { EditorField, EditorFieldGroup, InputGroup } from '@grafana/plugin-ui'; import { Button, InlineField, InlineFieldRow, Combobox, ComboboxOption } from '@grafana/ui'; import { PrometheusDatasource } from '../../datasource'; diff --git a/packages/grafana-prometheus/src/querybuilder/components/MetricSelect.tsx b/packages/grafana-prometheus/src/querybuilder/components/MetricSelect.tsx index 0181641f45b..5e752bd89f8 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/MetricSelect.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/MetricSelect.tsx @@ -7,7 +7,7 @@ import Highlighter from 'react-highlight-words'; import { GrafanaTheme2, SelectableValue, toOption } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { EditorField, EditorFieldGroup } from '@grafana/experimental'; +import { EditorField, EditorFieldGroup } from '@grafana/plugin-ui'; import { AsyncSelect, Button, diff --git a/packages/grafana-prometheus/src/querybuilder/components/NestedQuery.tsx b/packages/grafana-prometheus/src/querybuilder/components/NestedQuery.tsx index 6c27f0fa1e3..0061cab7d5a 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/NestedQuery.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/NestedQuery.tsx @@ -3,7 +3,7 @@ import { css } from '@emotion/css'; import { memo } from 'react'; import { GrafanaTheme2, toOption } from '@grafana/data'; -import { EditorRows, FlexItem } from '@grafana/experimental'; +import { EditorRows, FlexItem } from '@grafana/plugin-ui'; import { AutoSizeInput, IconButton, Select, useStyles2 } from '@grafana/ui'; import { PrometheusDatasource } from '../../datasource'; diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx index db74ff98655..8e58f495963 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx @@ -11,7 +11,7 @@ import { QueryHint, TimeRange, } from '@grafana/data'; -import { config, TemplateSrv } from '@grafana/runtime'; +import { TemplateSrv } from '@grafana/runtime'; import { PrometheusDatasource } from '../../datasource'; import PromQlLanguageProvider from '../../language_provider'; @@ -108,28 +108,6 @@ describe('PromQueryBuilder', () => { await waitFor(() => expect(datasource.getVariables).toBeCalled()); }); - it('checks if the LLM plugin is enabled when the `prometheusPromQAIL` feature is enabled', async () => { - jest.replaceProperty(config, 'featureToggles', { - prometheusPromQAIL: true, - }); - const mockIsLLMPluginEnabled = jest.fn(); - mockIsLLMPluginEnabled.mockResolvedValue(true); - jest.spyOn(require('./promQail/state/helpers'), 'isLLMPluginEnabled').mockImplementation(mockIsLLMPluginEnabled); - setup(); - await waitFor(() => expect(mockIsLLMPluginEnabled).toHaveBeenCalledTimes(1)); - }); - - it('does not check if the LLM plugin is enabled when the `prometheusPromQAIL` feature is disabled', async () => { - jest.replaceProperty(config, 'featureToggles', { - prometheusPromQAIL: false, - }); - const mockIsLLMPluginEnabled = jest.fn(); - mockIsLLMPluginEnabled.mockResolvedValue(true); - jest.spyOn(require('./promQail/state/helpers'), 'isLLMPluginEnabled').mockImplementation(mockIsLLMPluginEnabled); - setup(); - await waitFor(() => expect(mockIsLLMPluginEnabled).toHaveBeenCalledTimes(0)); - }); - // it('tries to load labels when metric selected', async () => { const { languageProvider } = setup(); diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx index fd99b883f6d..11c79f5932c 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx @@ -1,12 +1,10 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.tsx import { css } from '@emotion/css'; -import { memo, useEffect, useState } from 'react'; +import { memo, useState } from 'react'; import { DataSourceApi, PanelData } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { EditorRow } from '@grafana/experimental'; -import { config } from '@grafana/runtime'; -import { Drawer } from '@grafana/ui'; +import { EditorRow } from '@grafana/plugin-ui'; import { PrometheusDatasource } from '../../datasource'; import promqlGrammar from '../../promql'; @@ -24,9 +22,6 @@ import { PromVisualQuery } from '../types'; import { MetricsLabelsSection } from './MetricsLabelsSection'; import { NestedQueryList } from './NestedQueryList'; import { EXPLAIN_LABEL_FILTER_CONTENT } from './PromQueryBuilderExplained'; -import { PromQail } from './promQail/PromQail'; -import { QueryAssistantButton } from './promQail/QueryAssistantButton'; -import { isLLMPluginEnabled } from './promQail/state/helpers'; export interface PromQueryBuilderProps { query: PromVisualQuery; @@ -40,37 +35,13 @@ export interface PromQueryBuilderProps { export const PromQueryBuilder = memo((props) => { const { datasource, query, onChange, onRunQuery, data, showExplain } = props; const [highlightedOp, setHighlightedOp] = useState(); - const [showDrawer, setShowDrawer] = useState(false); - const [llmAppEnabled, updateLlmAppEnabled] = useState(false); - const { prometheusPromQAIL } = config.featureToggles; // AI/ML + Prometheus const lang = { grammar: promqlGrammar, name: 'promql' }; const initHints = datasource.getInitHints(); - useEffect(() => { - async function checkLlms() { - const check = await isLLMPluginEnabled(); - updateLlmAppEnabled(check); - } - - if (prometheusPromQAIL) { - checkLlms(); - } - }, [prometheusPromQAIL]); - return ( <> - {prometheusPromQAIL && showDrawer && ( - setShowDrawer(false)}> - setShowDrawer(false)} - onChange={onChange} - datasource={datasource} - /> - - )} @@ -108,15 +79,6 @@ export const PromQueryBuilder = memo((props) => { onRunQuery={onRunQuery} highlightedOp={highlightedOp} /> - {prometheusPromQAIL && ( -

- -
- )}
datasource={datasource} diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilderOptions.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilderOptions.tsx index d79bb1716e0..7b2f9438efe 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilderOptions.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilderOptions.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { CoreApp, SelectableValue } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { EditorField, EditorRow, EditorSwitch } from '@grafana/experimental'; +import { EditorField, EditorRow, EditorSwitch } from '@grafana/plugin-ui'; import { AutoSizeInput, RadioButtonGroup, Select } from '@grafana/ui'; import { getQueryTypeChangeHandler, getQueryTypeOptions } from '../../components/PromExploreExtraField'; diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryCodeEditor.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryCodeEditor.tsx index 9ad1b409456..f6ea9df0e1c 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryCodeEditor.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryCodeEditor.tsx @@ -7,7 +7,6 @@ import { useStyles2 } from '@grafana/ui'; import { PromQueryField } from '../../components/PromQueryField'; import { PromQueryEditorProps } from '../../components/types'; -import { QueryEditorHints } from '../shared/QueryEditorHints'; import { PromQueryBuilderExplained } from './PromQueryBuilderExplained'; @@ -35,7 +34,6 @@ export function PromQueryCodeEditor(props: PromQueryCodeEditorProps) { app={app} /> {showExplain && } -
); } diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryEditorSelector.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryEditorSelector.tsx index d492d350df5..0d3a6b72b40 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryEditorSelector.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryEditorSelector.tsx @@ -4,7 +4,7 @@ import { memo, SyntheticEvent, useCallback, useEffect, useState } from 'react'; import { CoreApp, LoadingState, SelectableValue } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { EditorHeader, EditorRows, FlexItem } from '@grafana/experimental'; +import { EditorHeader, EditorRows, FlexItem } from '@grafana/plugin-ui'; import { reportInteraction } from '@grafana/runtime'; import { Button, ConfirmModal, Space } from '@grafana/ui'; diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryLegendEditor.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryLegendEditor.tsx index e06af3c98c2..6bafd1902f5 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryLegendEditor.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryLegendEditor.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { SelectableValue } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { EditorField } from '@grafana/experimental'; +import { EditorField } from '@grafana/plugin-ui'; import { AutoSizeInput, Select } from '@grafana/ui'; import { LegendFormatMode } from '../../types'; diff --git a/packages/grafana-prometheus/src/querybuilder/components/QueryPreview.tsx b/packages/grafana-prometheus/src/querybuilder/components/QueryPreview.tsx index 0f974b7df42..613a7111c7d 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/QueryPreview.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/QueryPreview.tsx @@ -1,5 +1,5 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/QueryPreview.tsx -import { EditorFieldGroup, EditorRow } from '@grafana/experimental'; +import { EditorFieldGroup, EditorRow } from '@grafana/plugin-ui'; import promqlGrammar from '../../promql'; import { RawQuery } from '../shared/RawQuery'; diff --git a/packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.test.tsx b/packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.test.tsx deleted file mode 100644 index 2f95975f5c4..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.test.tsx +++ /dev/null @@ -1,148 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/promQail/PromQail.test.tsx -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import { DataSourceInstanceSettings, DataSourcePluginMeta } from '@grafana/data'; - -import { PrometheusDatasource } from '../../../datasource'; -import PromQlLanguageProvider from '../../../language_provider'; -import { EmptyLanguageProviderMock } from '../../../language_provider.mock'; -import { PromOptions } from '../../../types'; -import { PromVisualQuery } from '../../types'; - -import { PromQail, queryAssistanttestIds } from './PromQail'; - -// don't care about interaction tracking in our unit tests -jest.mock('@grafana/runtime', () => ({ - ...jest.requireActual('@grafana/runtime'), - reportInteraction: jest.fn(), -})); - -window.HTMLElement.prototype.scrollIntoView = jest.fn(); - -describe('PromQail', () => { - it('renders the drawer', async () => { - setup(defaultQuery); - await waitFor(() => { - expect(screen.getByText('Query advisor')).toBeInTheDocument(); - }); - }); - - it('shows an option to not show security warning', async () => { - setup(defaultQuery); - await waitFor(() => { - expect(screen.getByText("Don't show this message again")).toBeInTheDocument(); - }); - }); - - it('shows selected metric and asks for a prompt', async () => { - setup(defaultQuery); - - await clickSecurityButton(); - - await waitFor(() => { - expect(screen.getByText('random_metric')).toBeInTheDocument(); - expect(screen.getByText('Do you know what you want to query?')).toBeInTheDocument(); - }); - }); - - it('displays a prompt when the user knows what they want to query', async () => { - setup(defaultQuery); - - await clickSecurityButton(); - - await waitFor(() => { - expect(screen.getByText('random_metric')).toBeInTheDocument(); - expect(screen.getByText('Do you know what you want to query?')).toBeInTheDocument(); - }); - - const aiPrompt = screen.getByTestId(queryAssistanttestIds.clickForAi); - - await userEvent.click(aiPrompt); - - await waitFor(() => { - expect(screen.getByText('What kind of data do you want to see with your metric?')).toBeInTheDocument(); - }); - }); - - it('does not display a prompt when choosing historical', async () => { - setup(defaultQuery); - - await clickSecurityButton(); - - await waitFor(() => { - expect(screen.getByText('random_metric')).toBeInTheDocument(); - expect(screen.getByText('Do you know what you want to query?')).toBeInTheDocument(); - }); - - const historicalPrompt = screen.getByTestId(queryAssistanttestIds.clickForHistorical); - - await userEvent.click(historicalPrompt); - - await waitFor(() => { - expect(screen.queryByText('What kind of data do you want to see with your metric?')).toBeNull(); - }); - }); -}); - -const defaultQuery: PromVisualQuery = { - metric: 'random_metric', - labels: [], - operations: [], -}; - -function createDatasource(withLabels?: boolean) { - const languageProvider = new EmptyLanguageProviderMock() as unknown as PromQlLanguageProvider; - - languageProvider.metricsMetadata = { - 'all-metrics': { - type: 'all-metrics-type', - help: 'all-metrics-help', - }, - a: { - type: 'counter', - help: 'a-metric-help', - }, - a_bucket: { - type: 'counter', - help: 'for functions', - }, - }; - - const datasource = new PrometheusDatasource( - { - url: '', - jsonData: {}, - meta: {} as DataSourcePluginMeta, - } as DataSourceInstanceSettings, - undefined, - languageProvider - ); - return datasource; -} - -function createProps(query: PromVisualQuery, datasource: PrometheusDatasource) { - return { - datasource, - onChange: jest.fn(), - closeDrawer: jest.fn(), - query: query, - }; -} - -function setup(query: PromVisualQuery) { - const withLabels: boolean = query.labels.length > 0; - const datasource = createDatasource(withLabels); - const props = createProps(query, datasource); - - // render the drawer only - const { container } = render(); - - return container; -} - -async function clickSecurityButton() { - const securityInfoButton = screen.getByTestId(queryAssistanttestIds.securityInfoButton); - - await userEvent.click(securityInfoButton); -} diff --git a/packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.tsx b/packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.tsx deleted file mode 100644 index 4993cb51127..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.tsx +++ /dev/null @@ -1,616 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/promQail/PromQail.tsx -import { css, cx } from '@emotion/css'; -import { PayloadAction, createSlice } from '@reduxjs/toolkit'; -import { useEffect, useReducer, useRef, useState } from 'react'; - -import { GrafanaTheme2, store } from '@grafana/data'; -import { reportInteraction } from '@grafana/runtime'; -import { Alert, Button, Checkbox, Input, Spinner, useTheme2 } from '@grafana/ui'; - -import { PrometheusDatasource } from '../../../datasource'; -import { PromVisualQuery } from '../../types'; - -import { QuerySuggestionContainer } from './QuerySuggestionContainer'; -// @ts-ignore until we can get these added for icons -import AI_Logo_color from './resources/AI_Logo_color.svg'; -import { promQailExplain, promQailSuggest } from './state/helpers'; -import { createInteraction, initialState } from './state/state'; -import { Interaction, SuggestionType } from './types'; - -export type PromQailProps = { - query: PromVisualQuery; - closeDrawer: () => void; - onChange: (query: PromVisualQuery) => void; - datasource: PrometheusDatasource; -}; - -const SKIP_STARTING_MESSAGE = 'SKIP_STARTING_MESSAGE'; - -export const PromQail = (props: PromQailProps) => { - const { query, closeDrawer, onChange, datasource } = props; - const skipStartingMessage = store.getBool(SKIP_STARTING_MESSAGE, false); - - const [state, dispatch] = useReducer(stateSlice.reducer, initialState(query, !skipStartingMessage)); - - const [labelNames, setLabelNames] = useState([]); - - const suggestions = state.interactions.reduce((acc, int) => acc + int.suggestions.length, 0); - - const responsesEndRef = useRef(null); - - const scrollToBottom = () => { - if (responsesEndRef) { - // @ts-ignore for React.MutableRefObject - responsesEndRef?.current?.scrollIntoView({ behavior: 'smooth' }); - } - }; - - useEffect(() => { - // only scroll when an interaction has been added or the suggestions have been updated - scrollToBottom(); - }, [state.interactions.length, suggestions]); - - useEffect(() => { - const fetchLabels = async () => { - let labelsIndex: Record = await datasource.languageProvider.fetchLabelsWithMatch(query.metric); - setLabelNames(Object.keys(labelsIndex)); - }; - fetchLabels(); - }, [query, datasource]); - - const theme = useTheme2(); - const styles = getStyles(theme); - - return ( -
- {/* Query Advisor */} - {/* header */} -
-

Query advisor

-
- {/* Starting message */} -
-
- AI logo color Assistant -
- {state.showStartingMessage ? ( - <> -
-
    -
  1. - Query Advisor suggests queries based on a metric and requests you type in. -
  2. -
  3. - Query Advisor sends Prometheus metrics, labels and metadata to the LLM provider you've configured. - Be sure to align its usage with your company's internal policies. -
  4. -
  5. - An AI-suggested query may not fully answer your question. Always take a moment to understand a query - before you use it. -
  6. -
-
- - Query Advisor is currently in Private Preview. Feedback is appreciated and can be provided on explanations - and suggestions. - - - {/* don't show this message again, store in localstorage */} -
- { - const val = store.getBool(SKIP_STARTING_MESSAGE, false); - store.set(SKIP_STARTING_MESSAGE, !val); - dispatch(indicateCheckbox(!val)); - }} - label="Don't show this message again" - /> -
-
-
- - -
-
- - ) : ( -
- {/* MAKE THIS TABLE RESPONSIVE */} - {/* FIT SUPER LONG METRICS AND LABELS IN HERE */} -
Here is the metric you have selected:
-
-
- - - - - - - - {state.query.labels.map((label, idx) => { - const text = idx === 0 ? 'labels' : ''; - return ( - - - - - - ); - })} - -
metric{state.query.metric} - -
{text}{`${label.label}${label.op}${label.value}`}
-
-
- - {/* Ask if you know what you want to query? */} - {!state.askForQueryHelp && state.interactions.length === 0 && ( - <> -
Do you know what you want to query?
-
-
- - -
-
- - )} - - {state.interactions.map((interaction: Interaction, idx: number) => { - return ( -
- {interaction.suggestionType === SuggestionType.AI ? ( - <> -
What kind of data do you want to see with your metric?
-
-
You do not need to enter in a metric or a label again in the prompt.
-
Example: I want to monitor request latency, not errors.
-
-
- 0} - onChange={(e) => { - const prompt = e.currentTarget.value; - - const payload = { - idx: idx, - interaction: { ...interaction, prompt }, - }; - - dispatch(updateInteraction(payload)); - }} - /> -
- {interaction.suggestions.length === 0 ? ( - interaction.isLoading ? ( - <> -
- Waiting for OpenAI -
- - ) : ( - <> -
-
- - - -
-
- - ) - ) : ( - // LIST OF SUGGESTED QUERIES FROM AI - { - const isLoading = false; - const suggestionType = SuggestionType.AI; - dispatch(addInteraction({ suggestionType, isLoading })); - }} - queryExplain={(suggIdx: number) => - interaction.suggestions[suggIdx].explanation === '' - ? promQailExplain(dispatch, idx, query, interaction, suggIdx, datasource) - : interaction.suggestions[suggIdx].explanation - } - onChange={onChange} - prompt={interaction.prompt ?? ''} - /> - )} - - ) : // HISTORICAL SUGGESTIONS - interaction.isLoading ? ( - <> -
- Waiting for OpenAI -
- - ) : ( - // LIST OF SUGGESTED QUERIES FROM HISTORICAL DATA - { - const isLoading = false; - const suggestionType = SuggestionType.AI; - dispatch(addInteraction({ suggestionType, isLoading })); - }} - queryExplain={(suggIdx: number) => - interaction.suggestions[suggIdx].explanation === '' - ? promQailExplain(dispatch, idx, query, interaction, suggIdx, datasource) - : interaction.suggestions[suggIdx].explanation - } - onChange={onChange} - prompt={interaction.prompt ?? ''} - /> - )} -
- ); - })} -
- )} -
-
-
- ); -}; - -export const getStyles = (theme: GrafanaTheme2) => { - return { - sectionPadding: css({ - padding: '20px', - }), - header: css({ - display: 'flex', - - button: { - marginLeft: 'auto', - }, - }), - iconSection: css({ - padding: '0 0 10px 0', - color: `${theme.colors.text.secondary}`, - - img: { - paddingRight: '4px', - }, - }), - rightButtonsWrapper: css({ - display: 'flex', - }), - rightButtons: css({ - marginLeft: 'auto', - }), - leftButton: css({ - marginRight: '10px', - }), - dataList: css({ - padding: '0px 28px 0px 28px', - }), - textPadding: css({ - paddingBottom: '12px', - }), - containerPadding: css({ - padding: '28px', - }), - infoContainer: css({ - border: `${theme.colors.border.strong}`, - padding: '16px', - backgroundColor: `${theme.colors.background.secondary}`, - borderRadius: `8px`, - borderBottomLeftRadius: 0, - }), - infoContainerWrapper: css({ - paddingBottom: '24px', - }), - metricTable: css({ - width: '100%', - }), - metricTableName: css({ - width: '15%', - }), - metricTableValue: css({ - fontFamily: `${theme.typography.fontFamilyMonospace}`, - fontSize: `${theme.typography.bodySmall.fontSize}`, - overflow: 'scroll', - textWrap: 'nowrap', - maxWidth: '150px', - width: '60%', - maskImage: `linear-gradient(to right, rgba(0, 0, 0, 1) 90%, rgba(0, 0, 0, 0))`, - }), - metricTableButton: css({ - float: 'right', - }), - queryQuestion: css({ - textAlign: 'end', - padding: '8px 0', - }), - secondaryText: css({ - color: `${theme.colors.text.secondary}`, - }), - loadingMessageContainer: css({ - border: `${theme.colors.border.strong}`, - padding: `16px`, - backgroundColor: `${theme.colors.background.secondary}`, - marginBottom: `20px`, - borderRadius: `8px`, - color: `${theme.colors.text.secondary}`, - fontStyle: 'italic', - }), - floatRight: css({ - float: 'right', - }), - codeText: css({ - fontFamily: `${theme.typography.fontFamilyMonospace}`, - fontSize: `${theme.typography.bodySmall.fontSize}`, - }), - bodySmall: css({ - fontSize: `${theme.typography.bodySmall.fontSize}`, - }), - explainPadding: css({ - paddingLeft: '26px', - }), - bottomMargin: css({ - marginBottom: '20px', - }), - topPadding: css({ - paddingTop: '22px', - }), - doc: css({ - textDecoration: 'underline', - }), - afterButtons: css({ - display: 'flex', - justifyContent: 'flex-end', - }), - feedbackStyle: css({ - margin: 0, - textAlign: 'right', - paddingTop: '22px', - paddingBottom: '22px', - }), - nextInteractionHeight: css({ - height: '88px', - }), - center: css({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }), - inputPadding: css({ - paddingBottom: '24px', - }), - querySuggestion: css({ - display: 'flex', - flexWrap: 'nowrap', - }), - longCode: css({ - width: '90%', - textWrap: 'nowrap', - overflow: 'scroll', - maskImage: `linear-gradient(to right, rgba(0, 0, 0, 1) 90%, rgba(0, 0, 0, 0))`, - - div: { - display: 'inline-block', - }, - }), - useButton: css({ - marginLeft: 'auto', - }), - suggestionFeedback: css({ - textAlign: 'left', - }), - feedbackQuestion: css({ - display: 'flex', - padding: '8px 0px', - h6: { marginBottom: 0 }, - i: { - marginTop: '1px', - }, - }), - explationTextInput: css({ - paddingLeft: '24px', - }), - submitFeedback: css({ - padding: '16px 0', - }), - noMargin: css({ - margin: 0, - }), - enableButtonTooltip: css({ - padding: 8, - }), - enableButtonTooltipText: css({ - color: `${theme.colors.text.secondary}`, - ul: { - marginLeft: 16, - }, - }), - link: css({ - color: `${theme.colors.text.link} !important`, - }), - }; -}; - -export const queryAssistanttestIds = { - promQail: 'prom-qail', - securityInfoButton: 'security-info-button', - clickForHistorical: 'click-for-historical', - clickForAi: 'click-for-ai', - submitPrompt: 'submit-prompt', - refinePrompt: 'refine-prompt', -}; - -const stateSlice = createSlice({ - name: 'metrics-modal-state', - initialState: initialState(), - reducers: { - showExplainer: (state, action: PayloadAction) => { - state.showExplainer = action.payload; - }, - showStartingMessage: (state, action: PayloadAction) => { - state.showStartingMessage = action.payload; - }, - indicateCheckbox: (state, action: PayloadAction) => { - state.indicateCheckbox = action.payload; - }, - askForQueryHelp: (state, action: PayloadAction) => { - state.askForQueryHelp = action.payload; - }, - /* - * start working on a collection of interactions - * { - * askForhelp y n - * prompt question - * queries querySuggestions - * } - * - */ - addInteraction: (state, action: PayloadAction<{ suggestionType: SuggestionType; isLoading: boolean }>) => { - // AI or Historical? - const interaction = createInteraction(action.payload.suggestionType, action.payload.isLoading); - const interactions = state.interactions; - state.interactions = interactions.concat([interaction]); - }, - updateInteraction: (state, action: PayloadAction<{ idx: number; interaction: Interaction }>) => { - // update the interaction by index - // will most likely be the last interaction but we might update previous by giving them cues of helpful or not - const index = action.payload.idx; - const updInteraction = action.payload.interaction; - - state.interactions = state.interactions.map((interaction: Interaction, idx: number) => { - if (idx === index) { - return updInteraction; - } - - return interaction; - }); - }, - }, -}); - -// actions to update the state -export const { showStartingMessage, indicateCheckbox, addInteraction, updateInteraction } = stateSlice.actions; diff --git a/packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.test.tsx b/packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.test.tsx deleted file mode 100644 index 8d1bf6971d4..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.test.tsx +++ /dev/null @@ -1,51 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/promQail/QueryAssistantButton.test.tsx -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import { QueryAssistantButton } from './QueryAssistantButton'; - -const setShowDrawer = jest.fn(() => {}); - -describe('QueryAssistantButton', () => { - it('renders the button', async () => { - const props = createProps(true, 'metric', setShowDrawer); - render(); - expect(screen.getByText('Get query suggestions')).toBeInTheDocument(); - }); - - it('shows the LLM app disabled message when LLM app is not set up with vector DB', async () => { - const props = createProps(false, 'metric', setShowDrawer); - render(); - const button = screen.getByText('Get query suggestions'); - await userEvent.hover(button); - await waitFor(() => { - expect(screen.getByText('Install and enable the LLM plugin')).toBeInTheDocument(); - }); - }); - - it('shows the message to select a metric when LLM is enabled and no metric is selected', async () => { - const props = createProps(true, '', setShowDrawer); - render(); - const button = screen.getByText('Get query suggestions'); - await userEvent.hover(button); - await waitFor(() => { - expect(screen.getByText('First, select a metric.')).toBeInTheDocument(); - }); - }); - - it('calls setShowDrawer when button is clicked', async () => { - const props = createProps(true, 'metric', setShowDrawer); - render(); - const button = screen.getByText('Get query suggestions'); - fireEvent.click(button); - expect(setShowDrawer).toHaveBeenCalled(); - }); -}); - -function createProps(llmAppEnabled: boolean, metric: string, setShowDrawer: () => void) { - return { - llmAppEnabled, - metric, - setShowDrawer, - }; -} diff --git a/packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.tsx b/packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.tsx deleted file mode 100644 index 2324f60e402..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.tsx +++ /dev/null @@ -1,86 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/promQail/QueryAssistantButton.tsx -import { selectors } from '@grafana/e2e-selectors'; -import { reportInteraction } from '@grafana/runtime'; -import { Button, Tooltip, useTheme2 } from '@grafana/ui'; - -import { getStyles } from './PromQail'; -import AI_Logo_color from './resources/AI_Logo_color.svg'; - -export type Props = { - llmAppEnabled: boolean; - metric: string; - setShowDrawer: (show: boolean) => void; -}; - -export function QueryAssistantButton(props: Props) { - const { llmAppEnabled, metric, setShowDrawer } = props; - - const llmAppDisabled = !llmAppEnabled; - const noMetricSelected = !metric; - - const theme = useTheme2(); - const styles = getStyles(theme); - - const button = () => { - return ( - - ); - }; - - const selectMetricMessage = ( - - {button()} - - ); - - const llmAppMessage = ( - -
Query Advisor is disabled
-
To enable Query Advisor you must:
-
- -
-
- } - > - {button()} - - ); - - if (llmAppDisabled) { - return llmAppMessage; - } else if (noMetricSelected) { - return selectMetricMessage; - } else { - return button(); - } -} diff --git a/packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionContainer.tsx b/packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionContainer.tsx deleted file mode 100644 index ac51393edac..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionContainer.tsx +++ /dev/null @@ -1,102 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/promQail/QuerySuggestionContainer.tsx -import { cx } from '@emotion/css'; -import { useState } from 'react'; - -import { Button, useTheme2 } from '@grafana/ui'; - -import { PromVisualQuery } from '../../types'; - -import { getStyles, queryAssistanttestIds } from './PromQail'; -import { QuerySuggestionItem } from './QuerySuggestionItem'; -import { QuerySuggestion, SuggestionType } from './types'; - -export type Props = { - querySuggestions: QuerySuggestion[]; - suggestionType: SuggestionType; - closeDrawer: () => void; - nextInteraction: () => void; - queryExplain: (idx: number) => void; - onChange: (query: PromVisualQuery) => void; - prompt: string; -}; - -export function QuerySuggestionContainer(props: Props) { - const { suggestionType, querySuggestions, closeDrawer, nextInteraction, queryExplain, onChange, prompt } = props; - - const [hasNextInteraction, updateHasNextInteraction] = useState(false); - - const theme = useTheme2(); - const styles = getStyles(theme); - - let text, secondaryText, refineText; - - if (suggestionType === SuggestionType.Historical) { - text = `Here are ${querySuggestions.length} query suggestions:`; - refineText = 'I want to write a prompt'; - } else if (suggestionType === SuggestionType.AI) { - text = text = 'Here is your query suggestion:'; - secondaryText = - 'This query is based off of natural language descriptions of the most commonly used PromQL queries.'; - refineText = 'Refine prompt'; - } - - return ( - <> - {suggestionType === SuggestionType.Historical ? ( -
{text}
- ) : ( - <> -
{text}
-
{secondaryText}
- - )} - -
-
- {querySuggestions.map((qs: QuerySuggestion, idx: number) => { - return ( - { - return acc + '$$' + qs.query; - }, '')} - prompt={prompt ?? ''} - /> - ); - })} -
-
- {!hasNextInteraction && ( -
-
- -
-
- -
-
- )} - - ); -} diff --git a/packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionItem.tsx b/packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionItem.tsx deleted file mode 100644 index a173c8c4efb..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionItem.tsx +++ /dev/null @@ -1,322 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/promQail/QuerySuggestionItem.tsx -import { cx } from '@emotion/css'; -import { FormEvent, useState } from 'react'; - -import { SelectableValue } from '@grafana/data'; -import { reportInteraction } from '@grafana/runtime'; -import { Button, RadioButtonList, Spinner, TextArea, Toggletip, useTheme2 } from '@grafana/ui'; - -import { buildVisualQueryFromString } from '../../parsing'; -import { PromVisualQuery } from '../../types'; - -import { getStyles } from './PromQail'; -import { QuerySuggestion } from './types'; - -export type Props = { - querySuggestion: QuerySuggestion; - order: number; - queryExplain: (idx: number) => void; - historical: boolean; - onChange: (query: PromVisualQuery) => void; - closeDrawer: () => void; - last: boolean; - prompt: string; - allSuggestions: string | undefined; -}; - -const suggestionOptions: SelectableValue[] = [ - { label: 'Yes', value: 'yes' }, - { label: 'No', value: 'no' }, -]; -const explationOptions: SelectableValue[] = [ - { label: 'Too vague', value: 'too vague' }, - { label: 'Too technical', value: 'too technical' }, - { label: 'Inaccurate', value: 'inaccurate' }, - { label: 'Other', value: 'other' }, -]; - -export function QuerySuggestionItem(props: Props) { - const { querySuggestion, order, queryExplain, historical, onChange, closeDrawer, last, allSuggestions, prompt } = - props; - const [showExp, updShowExp] = useState(false); - - const [gaveExplanationFeedback, updateGaveExplanationFeedback] = useState(false); - const [gaveSuggestionFeedback, updateGaveSuggestionFeedback] = useState(false); - - const [suggestionFeedback, setSuggestionFeedback] = useState({ - radioInput: '', - text: '', - }); - - const [explanationFeedback, setExplanationFeedback] = useState({ - radioInput: '', - text: '', - }); - - const theme = useTheme2(); - const styles = getStyles(theme); - - const { query, explanation } = querySuggestion; - - const feedbackToggleTip = (type: string) => { - const updateRadioFeedback = (value: string) => { - if (type === 'explanation') { - setExplanationFeedback({ - ...explanationFeedback, - radioInput: value, - }); - } else { - setSuggestionFeedback({ - ...suggestionFeedback, - radioInput: value, - }); - } - }; - - const updateTextFeedback = (e: FormEvent) => { - if (type === 'explanation') { - setExplanationFeedback({ - ...explanationFeedback, - text: e.currentTarget.value, - }); - } else { - setSuggestionFeedback({ - ...suggestionFeedback, - text: e.currentTarget.value, - }); - } - }; - - const disabledButton = () => - type === 'explanation' ? !explanationFeedback.radioInput : !suggestionFeedback.radioInput; - - const questionOne = - type === 'explanation' ? 'Why was the explanation not helpful?' : 'Were the query suggestions helpful?'; - - return ( -
-
-
-
{questionOne}
- (Required) -
- -
-
- {type !== 'explanation' && ( -
-
How can we improve the query suggestions?
-
- )} - - - ))} - {queryToAdd && ( - <> - - - - - - - - )} - - - - - - - - - - - - - ); -}; diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesList.tsx b/public/app/features/explore/QueryLibrary/QueryTemplatesList.tsx deleted file mode 100644 index a0860add66e..00000000000 --- a/public/app/features/explore/QueryLibrary/QueryTemplatesList.tsx +++ /dev/null @@ -1,245 +0,0 @@ -import { css } from '@emotion/css'; -import { compact, uniq, uniqBy } from 'lodash'; -import { useEffect, useMemo, useState } from 'react'; - -import { AppEvents, GrafanaTheme2, SelectableValue } from '@grafana/data'; -import { getAppEvents, getDataSourceSrv } from '@grafana/runtime'; -import { EmptyState, FilterInput, InlineLabel, MultiSelect, Spinner, useStyles2, Stack, Badge } from '@grafana/ui'; -import { t, Trans } from 'app/core/internationalization'; -import { createQueryText } from 'app/core/utils/richHistory'; -import { useListQueryTemplateQuery } from 'app/features/query-library'; -import { getUserInfo } from 'app/features/query-library/api/user'; -import { QueryTemplate } from 'app/features/query-library/types'; - -import { getDatasourceSrv } from '../../plugins/datasource_srv'; -import { convertDataQueryResponseToQueryTemplates } from '../../query-library/api/mappers'; -import { getK8sNamespace } from '../../query-library/api/query'; - -import { QueryLibraryProps } from './QueryLibrary'; -import { queryLibraryTrackFilterDatasource } from './QueryLibraryAnalyticsEvents'; -import { QueryLibraryExpmInfo } from './QueryLibraryExpmInfo'; -import QueryTemplatesTable from './QueryTemplatesTable'; -import { QueryTemplateRow } from './QueryTemplatesTable/types'; -import { searchQueryLibrary } from './utils/search'; - -interface QueryTemplatesListProps extends QueryLibraryProps {} - -export function QueryTemplatesList(props: QueryTemplatesListProps) { - const { - data: rawData, - isLoading, - error, - } = useListQueryTemplateQuery({ - namespace: getK8sNamespace(), - }); - const data = useMemo(() => (rawData ? convertDataQueryResponseToQueryTemplates(rawData) : undefined), [rawData]); - const [isModalOpen, setIsModalOpen] = useState(false); - const [searchQuery, setSearchQuery] = useState(''); - const [datasourceFilters, setDatasourceFilters] = useState>>( - props.activeDatasources?.map((ds) => ({ value: ds, label: ds })) || [] - ); - const [userData, setUserData] = useState([]); - const [userFilters, setUserFilters] = useState>>([]); - - const [allQueryTemplateRows, setAllQueryTemplateRows] = useState([]); - const [isRowsLoading, setIsRowsLoading] = useState(true); - const styles = useStyles2(getStyles); - - useEffect(() => { - let shouldCancel = true; - - const fetchRows = async () => { - if (!data) { - setIsRowsLoading(false); - return; - } - - let userDataList; - const userQtList = uniq(compact(data.map((qt) => qt.user?.uid))); - const usersParam = userQtList.map((userUid) => `key=${encodeURIComponent(userUid)}`).join('&'); - try { - userDataList = await getUserInfo(`?${usersParam}`); - } catch (error) { - getAppEvents().publish({ - type: AppEvents.alertError.name, - payload: [ - t('query-library.user-info-get-error', 'Error attempting to get user info from the library: {{error}}', { - error: JSON.stringify(error), - }), - ], - }); - setIsRowsLoading(false); - return; - } - - setUserData(userDataList.display.map((user) => user.displayName)); - - const rowsPromises = data.map(async (queryTemplate: QueryTemplate, index: number) => { - try { - const datasourceRef = queryTemplate.targets[0]?.datasource; - const datasourceApi = await getDataSourceSrv().get(datasourceRef); - const datasourceType = getDatasourceSrv().getInstanceSettings(datasourceRef)?.meta.name || ''; - const query = queryTemplate.targets[0]; - const queryText = createQueryText(query, datasourceApi); - const datasourceName = datasourceApi?.name || ''; - const extendedUserData = userDataList.display.find( - (user) => `${user?.identity.type}:${user?.identity.name}` === queryTemplate.user?.uid - ); - - return { - index: index.toString(), - uid: queryTemplate.uid, - datasourceName, - datasourceRef, - datasourceType, - createdAtTimestamp: queryTemplate?.createdAtTimestamp || 0, - query, - queryText, - description: queryTemplate.title, - user: { - uid: queryTemplate.user?.uid || '', - displayName: extendedUserData?.displayName || '', - avatarUrl: extendedUserData?.avatarUrl || '', - }, - }; - } catch (error) { - getAppEvents().publish({ - type: AppEvents.alertError.name, - payload: [ - t( - 'query-library.query-template-get-error', - 'Error attempting to get query template from the library: {{error}}', - { error: JSON.stringify(error) } - ), - ], - }); - return { index: index.toString(), error }; - } - }); - - const results = await Promise.allSettled(rowsPromises); - const rows = results.filter((result) => result.status === 'fulfilled').map((result) => result.value); - - if (shouldCancel) { - setAllQueryTemplateRows(rows); - setIsRowsLoading(false); - } - }; - - fetchRows(); - - return () => { - shouldCancel = false; - }; - }, [data]); - - const queryTemplateRows = useMemo( - () => - searchQueryLibrary( - allQueryTemplateRows, - searchQuery, - datasourceFilters.map((f) => f.value || ''), - userFilters.map((f) => f.value || '') - ), - [allQueryTemplateRows, searchQuery, datasourceFilters, userFilters] - ); - - const datasourceNames = useMemo(() => { - return uniqBy(allQueryTemplateRows, 'datasourceName').map((row) => row.datasourceName); - }, [allQueryTemplateRows]); - - if (error) { - return ( - - {error.message} - - ); - } - - if (isLoading || isRowsLoading) { - return ; - } - - if (!data || data.length === 0) { - return ( - -

- { - "You haven't saved any queries to your library yet. Start adding them from Explore or your Query History tab." - } -

-
- ); - } - - return ( - <> - setIsModalOpen(false)} /> - - setSearchQuery(query)} - escapeRegex={false} - /> - - Datasource name(s): - - { - setDatasourceFilters(items); - actionMeta.action === 'select-option' && queryLibraryTrackFilterDatasource(); - }} - value={datasourceFilters} - options={datasourceNames.map((r) => { - return { value: r, label: r }; - })} - placeholder={'Filter queries for data sources(s)'} - aria-label={'Filter queries for data sources(s)'} - /> - - User name(s): - - { - setUserFilters(items); - actionMeta.action === 'select-option' && queryLibraryTrackFilterDatasource(); - }} - value={userFilters} - options={userData.map((r) => { - return { value: r, label: r }; - })} - placeholder={'Filter queries for user name(s)'} - aria-label={'Filter queries for user name(s)'} - /> - setIsModalOpen(true)} - /> - - - - ); -} - -const getStyles = (theme: GrafanaTheme2) => ({ - searchInput: css({ - maxWidth: theme.spacing(55), - }), - multiSelect: css({ - maxWidth: theme.spacing(65), - }), - label: css({ - marginLeft: theme.spacing(1), - border: `1px solid ${theme.colors.secondary.border}`, - }), -}); diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/ActionsCell.tsx b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/ActionsCell.tsx deleted file mode 100644 index 7390f030338..00000000000 --- a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/ActionsCell.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { useState } from 'react'; - -import { getAppEvents } from '@grafana/runtime'; -import { IconButton, Modal } from '@grafana/ui'; -import { notifyApp } from 'app/core/actions'; -import { createSuccessNotification } from 'app/core/copy/appNotification'; -import { t } from 'app/core/internationalization'; -import { useDeleteQueryTemplateMutation } from 'app/features/query-library'; -import { dispatch } from 'app/store/store'; -import { ShowConfirmModalEvent } from 'app/types/events'; - -import { getK8sNamespace } from '../../../query-library/api/query'; -import ExploreRunQueryButton from '../../ExploreRunQueryButton'; -import { useQueriesDrawerContext } from '../../QueriesDrawer/QueriesDrawerContext'; -import { - queryLibaryTrackDeleteQuery, - queryLibraryTrackAddOrEditDescription, - queryLibraryTrackRunQuery, -} from '../QueryLibraryAnalyticsEvents'; -import { QueryTemplateForm } from '../QueryTemplateForm'; - -import { useQueryLibraryListStyles } from './styles'; -import { QueryTemplateRow } from './types'; - -interface ActionsCellProps { - queryUid?: string; - queryTemplate: QueryTemplateRow; - rootDatasourceUid?: string; -} - -function ActionsCell({ queryTemplate, rootDatasourceUid, queryUid }: ActionsCellProps) { - const [deleteQueryTemplate] = useDeleteQueryTemplateMutation(); - const [editFormOpen, setEditFormOpen] = useState(false); - const { setDrawerOpened } = useQueriesDrawerContext(); - const styles = useQueryLibraryListStyles(); - - const onDeleteQuery = (queryUid: string) => { - const performDelete = (queryUid: string) => { - deleteQueryTemplate({ - name: queryUid, - namespace: getK8sNamespace(), - ioK8SApimachineryPkgApisMetaV1DeleteOptions: {}, - }); - dispatch(notifyApp(createSuccessNotification(t('explore.query-library.query-deleted', 'Query deleted')))); - queryLibaryTrackDeleteQuery(); - }; - - getAppEvents().publish( - new ShowConfirmModalEvent({ - title: t('explore.query-library.delete-query-title', 'Delete query'), - text: t( - 'explore.query-library.delete-query-text', - "You're about to remove this query from the query library. This action cannot be undone. Do you want to continue?" - ), - yesText: t('query-library.delete-query-button', 'Delete query'), - icon: 'trash-alt', - onConfirm: () => performDelete(queryUid), - }) - ); - }; - - return ( -
- { - if (queryUid) { - onDeleteQuery(queryUid); - } - }} - /> - { - setEditFormOpen(true); - queryLibraryTrackAddOrEditDescription(); - }} - /> - { - setDrawerOpened(false); - queryLibraryTrackRunQuery(queryTemplate.datasourceType || ''); - }} - /> - setEditFormOpen(false)} - > - setEditFormOpen(false)} - templateData={queryTemplate} - onSave={() => { - setEditFormOpen(false); - }} - /> - -
- ); -} - -export default ActionsCell; diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/AddedByCell.tsx b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/AddedByCell.tsx deleted file mode 100644 index 35793ab7fbd..00000000000 --- a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/AddedByCell.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { Avatar } from '@grafana/ui'; -import { User } from 'app/features/query-library/types'; - -import { useQueryLibraryListStyles } from './styles'; - -type AddedByCellProps = { - user?: User; -}; -export function AddedByCell(props: AddedByCellProps) { - const styles = useQueryLibraryListStyles(); - - return ( -
- - - - {props.user?.displayName || 'Unknown'} -
- ); -} diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/DatasourceTypeCell.tsx b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/DatasourceTypeCell.tsx deleted file mode 100644 index bdc361b7532..00000000000 --- a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/DatasourceTypeCell.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { CellProps } from 'react-table'; - -import { useDatasource } from '../utils/useDatasource'; - -import { useQueryLibraryListStyles } from './styles'; -import { QueryTemplateRow } from './types'; - -export function DatasourceTypeCell(props: CellProps) { - const datasourceApi = useDatasource(props.row.original.datasourceRef); - const styles = useQueryLibraryListStyles(); - - return

{datasourceApi?.meta.name}

; -} diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/DateAddedCell.tsx b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/DateAddedCell.tsx deleted file mode 100644 index 29bf6a96b07..00000000000 --- a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/DateAddedCell.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { CellProps } from 'react-table'; - -import { dateTime } from '@grafana/data'; - -import { useQueryLibraryListStyles } from './styles'; -import { QueryTemplateRow } from './types'; - -export function DateAddedCell(props: CellProps) { - const styles = useQueryLibraryListStyles(); - const formattedTime = dateTime(props.row.original.createdAtTimestamp).format('YYYY-MM-DD HH:mm:ss'); - - return

{formattedTime}

; -} diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/QueryDescriptionCell.tsx b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/QueryDescriptionCell.tsx deleted file mode 100644 index f19e95f14c8..00000000000 --- a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/QueryDescriptionCell.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { css, cx } from '@emotion/css'; -import { CellProps } from 'react-table'; - -import { GrafanaTheme2 } from '@grafana/data'; -import { Spinner, Tooltip, useStyles2 } from '@grafana/ui'; - -import { useDatasource } from '../utils/useDatasource'; - -import { useQueryLibraryListStyles } from './styles'; -import { QueryTemplateRow } from './types'; - -export function QueryDescriptionCell(props: CellProps) { - const datasourceApi = useDatasource(props.row.original.datasourceRef); - const queryLibraryListStyles = useQueryLibraryListStyles(); - const styles = useStyles2(getStyles); - - if (!datasourceApi) { - return ; - } - - if (!props.row.original.query) { - return
No queries
; - } - const queryDisplayText = props.row.original.queryText; - const description = props.row.original.description; - const dsName = props.row.original.datasourceName; - - return ( -
-

- {datasourceApi?.meta.info.description} - {dsName} -

- -

- {queryDisplayText} -

-
-

{description}

-
- ); -} - -const getStyles = (theme: GrafanaTheme2) => ({ - container: css({ - maxWidth: theme.spacing(60), - }), - queryDisplayText: css({ - backgroundColor: theme.colors.background.canvas, - }), -}); diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/index.tsx b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/index.tsx deleted file mode 100644 index 884229702ec..00000000000 --- a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/index.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { css } from '@emotion/css'; -import { SortByFn } from 'react-table'; - -import { GrafanaTheme2 } from '@grafana/data'; -import { Column, InteractiveTable, useStyles2 } from '@grafana/ui'; - -import ActionsCell from './ActionsCell'; -import { AddedByCell } from './AddedByCell'; -import { DatasourceTypeCell } from './DatasourceTypeCell'; -import { DateAddedCell } from './DateAddedCell'; -import { QueryDescriptionCell } from './QueryDescriptionCell'; -import { QueryTemplateRow } from './types'; - -const timestampSort: SortByFn = (rowA, rowB, _, desc) => { - const timeA = rowA.original.createdAtTimestamp || 0; - const timeB = rowB.original.createdAtTimestamp || 0; - return desc ? timeA - timeB : timeB - timeA; -}; - -const columns: Array> = [ - { id: 'description', header: 'Data source and query', cell: QueryDescriptionCell }, - { id: 'addedBy', header: 'Added by', cell: ({ row: { original } }) => }, - { id: 'datasourceType', header: 'Datasource type', cell: DatasourceTypeCell, sortType: 'string' }, - { id: 'createdAtTimestamp', header: 'Date added', cell: DateAddedCell, sortType: timestampSort }, - { - id: 'actions', - header: '', - cell: ({ row: { original } }) => ( - - ), - }, -]; - -type Props = { - queryTemplateRows: QueryTemplateRow[]; -}; - -export default function QueryTemplatesTable({ queryTemplateRows }: Props) { - const styles = useStyles2(getStyles); - return ( - row.index} - pageSize={20} - className={styles.table} - /> - ); -} - -const getStyles = (theme: GrafanaTheme2) => ({ - table: css({ - 'tbody tr': { - position: 'relative', - backgroundColor: theme.colors.background.secondary, - borderCollapse: 'collapse', - borderBottom: 'unset', - overflow: 'hidden', // Ensure the row doesn't overflow and cause additonal scrollbars - }, - /* Adds the pseudo-element for the lines between table rows */ - 'tbody tr::after': { - content: '""', - position: 'absolute', - inset: 'auto 0 0 0', - height: theme.spacing(0.5), - backgroundColor: theme.colors.background.primary, - }, - }), -}); diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/styles.tsx b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/styles.tsx deleted file mode 100644 index 715cd5d235b..00000000000 --- a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/styles.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { css } from '@emotion/css'; - -import { GrafanaTheme2 } from '@grafana/data/'; -import { useStyles2 } from '@grafana/ui/'; - -export const useQueryLibraryListStyles = () => { - return useStyles2(getStyles); -}; - -const getStyles = (theme: GrafanaTheme2) => ({ - logo: css({ - marginRight: theme.spacing(2), - width: '16px', - }), - header: css({ - margin: 0, - fontSize: theme.typography.h5.fontSize, - color: theme.colors.text.secondary, - }), - mainText: css({ - margin: 0, - fontSize: theme.typography.body.fontSize, - textOverflow: 'ellipsis', - }), - otherText: css({ - margin: 0, - fontSize: theme.typography.body.fontSize, - color: theme.colors.text.secondary, - textOverflow: 'ellipsis', - }), - singleLine: css({ - display: '-webkit-box', - WebkitBoxOrient: 'vertical', - WebkitLineClamp: 1, - overflow: 'hidden', - }), - cell: css({ - display: 'flex', - alignItems: 'center', - '&:last-child': { - justifyContent: 'end', - }, - }), - actionButton: css({ - padding: theme.spacing(1), - }), -}); diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/types.ts b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/types.ts deleted file mode 100644 index 51f5e490953..00000000000 --- a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { DataQuery, DataSourceRef } from '@grafana/schema'; -import { User } from 'app/features/query-library/types'; - -export type QueryTemplateRow = { - index: string; - datasourceName?: string; - description?: string; - query?: DataQuery; - queryText?: string; - datasourceRef?: DataSourceRef | null; - datasourceType?: string; - createdAtTimestamp?: number; - user?: User; - uid?: string; -}; diff --git a/public/app/features/explore/QueryLibrary/mocks.tsx b/public/app/features/explore/QueryLibrary/mocks.tsx new file mode 100644 index 00000000000..c59911e7236 --- /dev/null +++ b/public/app/features/explore/QueryLibrary/mocks.tsx @@ -0,0 +1,25 @@ +import { PropsWithChildren } from 'react'; + +import { QueryLibraryContext } from './QueryLibraryContext'; + +type Props = { + queryLibraryAvailable?: boolean; +}; + +export function QueryLibraryContextProviderMock(props: PropsWithChildren) { + return ( + + {props.children} + + ); +} diff --git a/public/app/features/explore/QueryLibrary/types.ts b/public/app/features/explore/QueryLibrary/types.ts new file mode 100644 index 00000000000..50db96feeb0 --- /dev/null +++ b/public/app/features/explore/QueryLibrary/types.ts @@ -0,0 +1,3 @@ +import { DataQuery } from '@grafana/schema'; + +export type OnSelectQueryType = (query: DataQuery) => void; diff --git a/public/app/features/explore/QueryLibrary/utils/search.ts b/public/app/features/explore/QueryLibrary/utils/search.ts deleted file mode 100644 index 2c55be7e9d0..00000000000 --- a/public/app/features/explore/QueryLibrary/utils/search.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { QueryTemplateRow } from '../QueryTemplatesTable/types'; - -export const searchQueryLibrary = ( - queryLibrary: QueryTemplateRow[], - query: string, - dsFilters: string[], - userNameFilters: string[] -) => { - const result = queryLibrary.filter((item) => { - const matchesDsFilter = - dsFilters.length === 0 || dsFilters.some((f) => item.datasourceName?.toLowerCase().includes(f.toLowerCase())); - const matchesUserNameFilter = - userNameFilters.length === 0 || userNameFilters.includes(item.user?.displayName || ''); - return ( - (item.datasourceName?.toLowerCase().includes(query.toLowerCase()) || - item.datasourceType?.toLowerCase().includes(query.toLowerCase()) || - item.description?.toLowerCase().includes(query.toLowerCase()) || - item.queryText?.toLowerCase().includes(query.toLowerCase())) && - matchesDsFilter && - matchesUserNameFilter - ); - }); - return result; -}; diff --git a/public/app/features/explore/QueryLibrary/utils/useDatasource.tsx b/public/app/features/explore/QueryLibrary/utils/useDatasource.tsx deleted file mode 100644 index 1ea4fd29b76..00000000000 --- a/public/app/features/explore/QueryLibrary/utils/useDatasource.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { useAsync } from 'react-use'; - -import { getDataSourceSrv } from '@grafana/runtime'; -import { DataSourceRef } from '@grafana/schema'; - -export function useDatasource(dataSourceRef?: DataSourceRef | null) { - const { value } = useAsync(async () => await getDataSourceSrv().get(dataSourceRef), [dataSourceRef]); - return value; -} diff --git a/public/app/features/explore/RichHistory/RichHistory.tsx b/public/app/features/explore/RichHistory/RichHistory.tsx index 38a7cb66dcb..60bce0a6ec3 100644 --- a/public/app/features/explore/RichHistory/RichHistory.tsx +++ b/public/app/features/explore/RichHistory/RichHistory.tsx @@ -11,16 +11,12 @@ import { RichHistorySettings, createDatasourcesList, } from 'app/core/utils/richHistory'; -import { QUERY_LIBRARY_GET_LIMIT } from 'app/features/query-library/api/factory'; import { useSelector } from 'app/types'; import { RichHistoryQuery } from 'app/types/explore'; import { supportedFeatures } from '../../../core/history/richHistoryStorageProvider'; -import { useListQueryTemplateQuery } from '../../query-library'; -import { getK8sNamespace } from '../../query-library/api/query'; -import { Tabs, useQueriesDrawerContext } from '../QueriesDrawer/QueriesDrawerContext'; +import { Tabs } from '../QueriesDrawer/QueriesDrawerContext'; import { i18n } from '../QueriesDrawer/utils'; -import { QueryLibrary } from '../QueryLibrary/QueryLibrary'; import { selectExploreDSMaps } from '../state/selectors'; import { RichHistoryQueriesTab } from './RichHistoryQueriesTab'; @@ -56,8 +52,6 @@ export function RichHistory(props: RichHistoryProps) { const [loading, setLoading] = useState(false); - const { queryLibraryAvailable } = useQueriesDrawerContext(); - const updateSettings = (settingsToUpdate: Partial) => { props.updateHistorySettings({ ...props.richHistorySettings, ...settingsToUpdate }); }; @@ -99,18 +93,6 @@ export function RichHistory(props: RichHistoryProps) { .map((eDs) => listOfDatasources.find((ds) => ds.uid === eDs.datasource?.uid)?.name) .filter((name): name is string => !!name); - const { data } = useListQueryTemplateQuery({ - namespace: getK8sNamespace(), - }); - const queryTemplatesCount = data?.items?.length ?? 0; - - const QueryLibraryTab: TabConfig = { - label: `${i18n.queryLibrary} (${queryTemplatesCount}/${QUERY_LIBRARY_GET_LIMIT})`, - value: Tabs.QueryLibrary, - content: , - icon: 'book', - }; - const QueriesTab: TabConfig = { label: i18n.queryHistory, value: Tabs.RichHistory, @@ -167,7 +149,7 @@ export function RichHistory(props: RichHistoryProps) { icon: 'sliders-v-alt', }; - let tabs = (queryLibraryAvailable ? [QueryLibraryTab] : []).concat([QueriesTab, StarredTab, SettingsTab]); + let tabs = [QueriesTab, StarredTab, SettingsTab]; return ( { - const { refetch } = useListQueryTemplateQuery({ - namespace: getK8sNamespace(), - }); - const [isOpen, setIsOpen] = useState(false); const [hasBeenSaved, setHasBeenSaved] = useState(false); + const { openAddQueryModal, queryLibraryEnabled } = useQueryLibraryContext(); const buttonLabel = t('explore.rich-history-card.add-to-library', 'Add to library'); - return isQueryLibraryEnabled() && !hasBeenSaved ? ( + return queryLibraryEnabled && !hasBeenSaved ? ( <> - setIsOpen(false)} - > - setIsOpen(() => false)} - queryToAdd={query} - onSave={(isSuccess) => { - if (isSuccess) { - setIsOpen(false); - setHasBeenSaved(true); - refetch(); - queryLibraryTrackAddFromQueryHistory(query.datasource?.type || ''); - } - }} - /> - ) : undefined; }; diff --git a/public/app/features/explore/RichHistory/RichHistoryContainer.tsx b/public/app/features/explore/RichHistory/RichHistoryContainer.tsx index 7618301dad3..73d5103a3c7 100644 --- a/public/app/features/explore/RichHistory/RichHistoryContainer.tsx +++ b/public/app/features/explore/RichHistory/RichHistoryContainer.tsx @@ -89,7 +89,7 @@ export function RichHistoryContainer(props: Props) { } }, [tracked, selectedTab]); - if (!richHistorySettings || !selectedTab) { + if (!richHistorySettings) { return ( Loading... diff --git a/public/app/features/explore/SecondaryActions.test.tsx b/public/app/features/explore/SecondaryActions.test.tsx index 076d9883f10..64a1fd2a172 100644 --- a/public/app/features/explore/SecondaryActions.test.tsx +++ b/public/app/features/explore/SecondaryActions.test.tsx @@ -1,13 +1,34 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { noop } from 'lodash'; +import { render } from '../../../test/test-utils'; + import { QueriesDrawerContextProviderMock } from './QueriesDrawer/mocks'; +import { QueryLibraryContextProviderMock } from './QueryLibrary/mocks'; import { SecondaryActions } from './SecondaryActions'; +jest.mock('@grafana/runtime/src/services/dataSourceSrv', () => { + return { + getDataSourceSrv: () => ({ + get: () => Promise.resolve({}), + getList: () => [], + getInstanceSettings: () => {}, + }), + }; +}); + describe('SecondaryActions', () => { it('should render component with two buttons', () => { - render(); + render( + + + + ); expect(screen.getByRole('button', { name: /Add query/i })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /Query inspector/i })).toBeInTheDocument(); @@ -21,6 +42,7 @@ describe('SecondaryActions', () => { richHistoryRowButtonHidden={true} onClickAddQueryRowButton={noop} onClickQueryInspectorButton={noop} + onSelectQueryFromLibrary={noop} /> ); @@ -35,6 +57,7 @@ describe('SecondaryActions', () => { addQueryRowButtonDisabled={true} onClickAddQueryRowButton={noop} onClickQueryInspectorButton={noop} + onSelectQueryFromLibrary={noop} /> ); @@ -54,6 +77,7 @@ describe('SecondaryActions', () => { ); diff --git a/public/app/features/explore/SecondaryActions.tsx b/public/app/features/explore/SecondaryActions.tsx index dfa98f92b77..e50bb642d86 100644 --- a/public/app/features/explore/SecondaryActions.tsx +++ b/public/app/features/explore/SecondaryActions.tsx @@ -3,6 +3,14 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { ToolbarButton, useTheme2 } from '@grafana/ui'; import { t, Trans } from 'app/core/internationalization'; +import { useSelector } from 'app/types'; + +import { createDatasourcesList } from '../../core/utils/richHistory'; +import { MIXED_DATASOURCE_NAME } from '../../plugins/datasource/mixed/MixedDataSource'; + +import { useQueryLibraryContext } from './QueryLibrary/QueryLibraryContext'; +import { type OnSelectQueryType } from './QueryLibrary/types'; +import { selectExploreDSMaps } from './state/selectors'; type Props = { addQueryRowButtonDisabled?: boolean; @@ -12,6 +20,7 @@ type Props = { onClickAddQueryRowButton: () => void; onClickQueryInspectorButton: () => void; + onSelectQueryFromLibrary: OnSelectQueryType; }; const getStyles = (theme: GrafanaTheme2) => { @@ -25,27 +34,57 @@ const getStyles = (theme: GrafanaTheme2) => { }; }; -export function SecondaryActions(props: Props) { +export function SecondaryActions({ + addQueryRowButtonDisabled, + addQueryRowButtonHidden, + onClickAddQueryRowButton, + onClickQueryInspectorButton, + onSelectQueryFromLibrary, + queryInspectorButtonActive, +}: Props) { const theme = useTheme2(); const styles = getStyles(theme); + const exploreActiveDS = useSelector(selectExploreDSMaps); + // Prefill the query library filter with the dataSource. + // Get current dataSource that is open. As this is only used in Explore we get it from Explore state. + const listOfDatasources = createDatasourcesList(); + const activeDatasources = exploreActiveDS.dsToExplore + .map((eDs) => { + return listOfDatasources.find((ds) => ds.uid === eDs.datasource?.uid)?.name; + }) + .filter((name): name is string => !!name && name !== MIXED_DATASOURCE_NAME); + + const { queryLibraryEnabled, openDrawer: openQueryLibraryDrawer } = useQueryLibraryContext(); return (
- {!props.addQueryRowButtonHidden && ( - - Add query - + {!addQueryRowButtonHidden && ( + <> + + Add query + + {queryLibraryEnabled && ( + openQueryLibraryDrawer(activeDatasources, onSelectQueryFromLibrary)} + icon="plus" + > + Add query from library + + )} + )} Query inspector diff --git a/public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFiltersTags.tsx b/public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFiltersTags.tsx index 0aabff724a4..10fa8befd62 100644 --- a/public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFiltersTags.tsx +++ b/public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFiltersTags.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { useMount } from 'react-use'; import { GrafanaTheme2, SelectableValue, toOption } from '@grafana/data'; -import { AccessoryButton } from '@grafana/experimental'; +import { AccessoryButton } from '@grafana/plugin-ui'; import { Input, Select, Stack, useStyles2 } from '@grafana/ui'; import { randomId, SearchProps, Tag } from '../../../useSearch'; diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/jsonMarkup.js b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/jsonMarkup.js index 2dcbb1ee895..7db0889e1e6 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/jsonMarkup.js +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/jsonMarkup.js @@ -67,7 +67,7 @@ function escape(str) { return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } -module.exports = function (doc, styleFile) { +export default function jsonMarkup(doc, styleFile) { let indent = ''; const style = Stylize(styleFile); @@ -130,4 +130,4 @@ module.exports = function (doc, styleFile) { } return '
' + visit(doc) + '
'; -}; +} diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx index 1a15f6ace0d..c769f585b77 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { css } from '@emotion/css'; +import { css, cx } from '@emotion/css'; import { isEqual } from 'lodash'; import memoizeOne from 'memoize-one'; import * as React from 'react'; @@ -566,7 +566,7 @@ export class UnthemedVirtualizedTraceView extends React.Component +
, + title, + content: , onClick: link.onClick, field: link.origin, type: SpanLinkType.Traces, @@ -338,11 +340,12 @@ function legacyCreateSpanLinkFactory( if (span.subsidiarilyReferencedBy && createFocusSpanLink) { for (const reference of span.subsidiarilyReferencedBy) { const link = createFocusSpanLink(reference.traceID, reference.spanID); + const title = getReferenceTitle(reference); links!.push({ href: link.href, - title: reference.span ? reference.span.operationName : 'View linked span', - content: , + title, + content: , onClick: link.onClick, field: link.origin, type: SpanLinkType.Traces, @@ -366,6 +369,14 @@ function legacyCreateSpanLinkFactory( }; } +const getReferenceTitle = (reference: TraceSpanReference) => { + let title = reference.span ? reference.span.operationName : 'View linked span'; + if (reference.refType === 'EXTERNAL') { + title = 'View linked span'; + } + return title; +}; + function getQueryForLoki( span: TraceSpan, options: TraceToLogsOptionsV2, diff --git a/public/app/features/explore/spec/helper/assert.ts b/public/app/features/explore/spec/helper/assert.ts index 664109527db..9046747e7fa 100644 --- a/public/app/features/explore/spec/helper/assert.ts +++ b/public/app/features/explore/spec/helper/assert.ts @@ -1,6 +1,6 @@ import { waitFor } from '@testing-library/react'; -import { withinQueryHistory } from './setup'; +import { withinQueryHistory, withinQueryLibrary } from './setup'; export const assertQueryHistoryExists = async (query: string) => { const selector = withinQueryHistory(); @@ -23,10 +23,10 @@ export const assertQueryHistory = async (expectedQueryTexts: string[]) => { }; export const assertQueryLibraryTemplateExists = async (datasource: string, description: string) => { - const selector = withinQueryHistory(); + const selector = withinQueryLibrary(); await waitFor(() => { - const cell = selector.getByRole('cell', { - name: new RegExp(`query template for ${datasource.toLowerCase()}: ${description.toLowerCase()}`, 'i'), + const cell = selector.getByRole('radio', { + name: description, }); expect(cell).toBeInTheDocument(); diff --git a/public/app/features/explore/spec/helper/interactions.ts b/public/app/features/explore/spec/helper/interactions.ts index a74ffb783c1..dce38c66ef5 100644 --- a/public/app/features/explore/spec/helper/interactions.ts +++ b/public/app/features/explore/spec/helper/interactions.ts @@ -26,28 +26,29 @@ export const runQuery = async (exploreId = 'left') => { }; export const openQueryHistory = async () => { - const button = screen.getByRole('button', { name: 'Query history' }); - await userEvent.click(button); - expect(await screen.findByPlaceholderText('Search queries')).toBeInTheDocument(); + let button = screen.queryByRole('button', { name: 'Query history' }); + if (button) { + await userEvent.click(button); + expect(await screen.findByPlaceholderText('Search queries')).toBeInTheDocument(); + } else { + button = screen.getByRole('button', { name: 'Open query library or query history' }); + await userEvent.click(button); + button = await screen.findByRole('menuitem', { name: 'Query history' }); + await userEvent.click(button); + expect(await screen.findByPlaceholderText('Search queries')).toBeInTheDocument(); + } }; export const openQueryLibrary = async () => { - const button = screen.getByRole('button', { name: 'Query library' }); + const button = screen.getByRole('button', { name: 'Add query from library' }); await userEvent.click(button); await waitFor(async () => { - screen.getByRole('tab', { - name: /query library/i, + screen.getByRole('dialog', { + name: 'Drawer title Query library', }); }); }; -export const switchToQueryHistory = async () => { - const tab = screen.getByRole('tab', { - name: /query history/i, - }); - await userEvent.click(tab); -}; - export const addQueryHistoryToQueryLibrary = async () => { const button = withinQueryHistory().getByRole('button', { name: /add to library/i }); await userEvent.click(button); diff --git a/public/app/features/explore/spec/helper/mocks.ts b/public/app/features/explore/spec/helper/mocks.ts new file mode 100644 index 00000000000..fddd823240a --- /dev/null +++ b/public/app/features/explore/spec/helper/mocks.ts @@ -0,0 +1,20 @@ +import { getAPIBaseURL } from '../../../../api/utils'; + +import { getIdentityDisplayList } from './testdata/identityDisplayList'; +import { getTestQueryList } from './testdata/testQueryList'; + +// This is not ideal but not sure how to fix it right now as this is duplicated from the API definition which is in +// Enterprise and so we cannot import it here. +// We have some tests for testing QL inside Explore. The whole Explore setup is in OSS, and it needs these mocks but the +// test itself is in Enterprise. Ideally we would inject the mocks in the tests somehow. +export const BASE_URL = getAPIBaseURL('querylibrary.grafana.app', 'v0alpha1'); + +export const mockData = { + all: { + url: BASE_URL, + response: getTestQueryList(), + }, + identityDisplay: { + response: getIdentityDisplayList(), + }, +}; diff --git a/public/app/features/explore/spec/helper/setup.tsx b/public/app/features/explore/spec/helper/setup.tsx index 2ec9733a4d4..9b5870691e3 100644 --- a/public/app/features/explore/spec/helper/setup.tsx +++ b/public/app/features/explore/spec/helper/setup.tsx @@ -4,6 +4,7 @@ import { createMemoryHistory } from 'history'; import { KBarProvider } from 'kbar'; import { fromPairs } from 'lodash'; import { stringify } from 'querystring'; +import { ComponentType, ReactNode } from 'react'; import { Provider } from 'react-redux'; // eslint-disable-next-line no-restricted-imports import { Route, Router } from 'react-router-dom'; @@ -36,7 +37,6 @@ import { GrafanaContext } from 'app/core/context/GrafanaContext'; import { GrafanaRoute } from 'app/core/navigation/GrafanaRoute'; import { Echo } from 'app/core/services/echo/Echo'; import { setLastUsedDatasourceUID } from 'app/core/utils/explore'; -import { IdentityServiceMocks, QueryLibraryMocks } from 'app/features/query-library'; import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource'; import { configureStore } from 'app/store/configureStore'; @@ -48,6 +48,16 @@ import { initialUserState } from '../../../profile/state/reducers'; import ExplorePage from '../../ExplorePage'; import { QueriesDrawerContextProvider } from '../../QueriesDrawer/QueriesDrawerContext'; +import { mockData } from './mocks'; + +export const QueryLibraryMocks = { + data: mockData.all, +}; + +export const IdentityServiceMocks = { + data: mockData.identityDisplay, +}; + type DatasourceSetup = { settings: DataSourceInstanceSettings; api: DataSourceApi }; type SetupOptions = { @@ -59,6 +69,7 @@ type SetupOptions = { failAddToLibrary?: boolean; // Use AppChrome wrapper around ExplorePage - needed to test query library/history withAppChrome?: boolean; + provider?: ComponentType<{ children: ReactNode }>; }; type TearDownOptions = { @@ -178,28 +189,38 @@ export function setupExplore(options?: SetupOptions): { const contextMock = getGrafanaContextMock({ location }); + const FinalProvider = + options?.provider || + (({ children }) => { + return children; + }); + const { unmount, container } = render( - {options?.withAppChrome ? ( - - - } - /> - - - ) : ( - } - /> - )} + + {options?.withAppChrome ? ( + + + ( + + )} + /> + + + ) : ( + } + /> + )} + @@ -312,6 +333,11 @@ export const withinQueryHistory = () => { return within(container); }; +export const withinQueryLibrary = () => { + const container = screen.getByRole('dialog', { name: 'Drawer title Query library' }); + return within(container); +}; + const exploreTestsHelper: { setupExplore: typeof setupExplore; tearDownExplore?: (options?: TearDownOptions) => void } = { setupExplore, diff --git a/public/app/features/query-library/api/testdata/identityDisplayList.ts b/public/app/features/explore/spec/helper/testdata/identityDisplayList.ts similarity index 100% rename from public/app/features/query-library/api/testdata/identityDisplayList.ts rename to public/app/features/explore/spec/helper/testdata/identityDisplayList.ts diff --git a/public/app/features/query-library/api/testdata/testQueryList.ts b/public/app/features/explore/spec/helper/testdata/testQueryList.ts similarity index 95% rename from public/app/features/query-library/api/testdata/testQueryList.ts rename to public/app/features/explore/spec/helper/testdata/testQueryList.ts index c6efa9b93bd..7a730bb2c6c 100644 --- a/public/app/features/query-library/api/testdata/testQueryList.ts +++ b/public/app/features/explore/spec/helper/testdata/testQueryList.ts @@ -1,6 +1,6 @@ export const getTestQueryList = () => ({ kind: 'QueryTemplateList', - apiVersion: 'peakq.grafana.app/v0alpha1', + apiVersion: 'querylibrary.grafana.app/v0alpha1', metadata: { resourceVersion: '1783293408052252672', remainingItemCount: 0, @@ -8,7 +8,7 @@ export const getTestQueryList = () => ({ items: [ { kind: 'QueryTemplate', - apiVersion: 'peakq.grafana.app/v0alpha1', + apiVersion: 'querylibrary.grafana.app/v0alpha1', metadata: { name: 'AElastic2nkf9', generateName: 'AElastic', @@ -55,7 +55,7 @@ export const getTestQueryList = () => ({ }, { kind: 'QueryTemplate', - apiVersion: 'peakq.grafana.app/v0alpha1', + apiVersion: 'querylibrary.grafana.app/v0alpha1', metadata: { name: 'ALoki296tj', generateName: 'ALoki', diff --git a/public/app/features/explore/spec/queryHistory.test.tsx b/public/app/features/explore/spec/queryHistory.test.tsx index 90d889c4625..1e3d7b3a75d 100644 --- a/public/app/features/explore/spec/queryHistory.test.tsx +++ b/public/app/features/explore/spec/queryHistory.test.tsx @@ -7,6 +7,7 @@ import store from 'app/core/store'; import { silenceConsoleOutput } from '../../../../test/core/utils/silenceConsoleOutput'; import * as localStorage from '../../../core/history/RichHistoryLocalStorage'; +import { Tabs } from '../QueriesDrawer/QueriesDrawerContext'; import { assertDataSourceFilterVisibility, @@ -127,6 +128,7 @@ describe('Explore: Query History', () => { expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_opened', { queryHistoryEnabled: false, + selectedTab: Tabs.RichHistory, }); }); diff --git a/public/app/features/explore/spec/queryLibrary.test.tsx b/public/app/features/explore/spec/queryLibrary.test.tsx deleted file mode 100644 index dc942fa21a8..00000000000 --- a/public/app/features/explore/spec/queryLibrary.test.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { Props } from 'react-virtualized-auto-sizer'; - -import { EventBusSrv } from '@grafana/data'; -import { config } from '@grafana/runtime'; -import { DataQuery } from '@grafana/schema/dist/esm/veneer/common.types'; - -import { silenceConsoleOutput } from '../../../../test/core/utils/silenceConsoleOutput'; - -import { - assertAddToQueryLibraryButtonExists, - assertQueryHistory, - assertQueryLibraryTemplateExists, -} from './helper/assert'; -import { - addQueryHistoryToQueryLibrary, - openQueryHistory, - openQueryLibrary, - submitAddToQueryLibrary, - switchToQueryHistory, -} from './helper/interactions'; -import { setupExplore, waitForExplore } from './helper/setup'; - -const reportInteractionMock = jest.fn(); -const testEventBus = new EventBusSrv(); -testEventBus.publish = jest.fn(); - -interface MockQuery extends DataQuery { - expr: string; -} - -jest.mock('@grafana/runtime', () => ({ - ...jest.requireActual('@grafana/runtime'), - reportInteraction: (...args: object[]) => { - reportInteractionMock(...args); - }, - getAppEvents: () => testEventBus, - usePluginLinks: jest.fn().mockReturnValue({ links: [] }), -})); - -jest.mock('app/core/core', () => ({ - contextSrv: { - hasPermission: () => true, - isSignedIn: true, - getValidIntervals: (defaultIntervals: string[]) => defaultIntervals, - user: { - isSignedIn: true, - }, - }, -})); - -jest.mock('app/core/services/PreferencesService', () => ({ - PreferencesService: function () { - return { - patch: jest.fn(), - load: jest.fn().mockResolvedValue({ - queryHistory: { - homeTab: 'query', - }, - }), - }; - }, -})); - -jest.mock('../hooks/useExplorePageTitle', () => ({ - useExplorePageTitle: jest.fn(), -})); - -jest.mock('react-virtualized-auto-sizer', () => { - return { - __esModule: true, - default(props: Props) { - return
{props.children({ height: 1, scaledHeight: 1, scaledWidth: 1000, width: 1000 })}
; - }, - }; -}); - -function setupQueryLibrary() { - const mockQuery: MockQuery = { refId: 'TEST', expr: 'TEST' }; - setupExplore({ - queryHistory: { - queryHistory: [{ datasourceUid: 'loki', queries: [mockQuery] }], - totalCount: 1, - }, - withAppChrome: true, - }); -} - -let previousQueryLibraryEnabled: boolean | undefined; -let previousQueryHistoryEnabled: boolean; - -describe('QueryLibrary', () => { - silenceConsoleOutput(); - - beforeAll(() => { - previousQueryLibraryEnabled = config.featureToggles.queryLibrary; - previousQueryHistoryEnabled = config.queryHistoryEnabled; - - config.featureToggles.queryLibrary = true; - config.queryHistoryEnabled = true; - }); - - afterAll(() => { - config.featureToggles.queryLibrary = previousQueryLibraryEnabled; - config.queryHistoryEnabled = previousQueryHistoryEnabled; - jest.restoreAllMocks(); - }); - - it('Load query templates', async () => { - setupQueryLibrary(); - await waitForExplore(); - await openQueryLibrary(); - await assertQueryLibraryTemplateExists('loki', 'Loki Query Template'); - }); - - it('Shows add to query library button only when the toggle is enabled', async () => { - setupQueryLibrary(); - await waitForExplore(); - await openQueryLibrary(); - await switchToQueryHistory(); - await assertQueryHistory(['{"expr":"TEST"}']); - await assertAddToQueryLibraryButtonExists(true); - }); - - it('Does not show the query library button when the toggle is disabled', async () => { - config.featureToggles.queryLibrary = false; - setupQueryLibrary(); - await waitForExplore(); - await openQueryHistory(); - await assertQueryHistory(['{"expr":"TEST"}']); - await assertAddToQueryLibraryButtonExists(false); - config.featureToggles.queryLibrary = true; - }); - - it('Shows a notification when a template is added and hides the add button', async () => { - setupQueryLibrary(); - await waitForExplore(); - await openQueryLibrary(); - await switchToQueryHistory(); - await assertQueryHistory(['{"expr":"TEST"}']); - await addQueryHistoryToQueryLibrary(); - await submitAddToQueryLibrary({ description: 'Test' }); - expect(testEventBus.publish).toHaveBeenCalledWith( - expect.objectContaining({ - type: 'alert-success', - payload: ['Query successfully saved to the library'], - }) - ); - await assertAddToQueryLibraryButtonExists(false); - }); -}); diff --git a/public/app/features/explore/state/time.ts b/public/app/features/explore/state/time.ts index 38683f45bf4..f794042c113 100644 --- a/public/app/features/explore/state/time.ts +++ b/public/app/features/explore/state/time.ts @@ -10,8 +10,8 @@ import { } from '@grafana/data'; import { getTemplateSrv } from '@grafana/runtime'; import { RefreshPicker } from '@grafana/ui'; -import { t } from '@grafana/ui/src/utils/i18n'; import appEvents from 'app/core/app_events'; +import { t } from 'app/core/internationalization'; import { getTimeRange, refreshIntervalToSortOrder, stopQueryState } from 'app/core/utils/explore'; import { getCopiedTimeRange, getShiftedTimeRange, getZoomedTimeRange } from 'app/core/utils/timePicker'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; diff --git a/public/app/features/expressions/components/Condition.tsx b/public/app/features/expressions/components/Condition.tsx index fc6455fd540..778869c5a60 100644 --- a/public/app/features/expressions/components/Condition.tsx +++ b/public/app/features/expressions/components/Condition.tsx @@ -69,7 +69,10 @@ export const Condition = ({ condition, index, onChange, onRemoveCondition, refId }); const isRange = - condition.evaluator.type === EvalFunction.IsWithinRange || condition.evaluator.type === EvalFunction.IsOutsideRange; + condition.evaluator.type === EvalFunction.IsWithinRange || + condition.evaluator.type === EvalFunction.IsOutsideRange || + condition.evaluator.type === EvalFunction.IsOutsideRangeIncluded || + condition.evaluator.type === EvalFunction.IsWithinRangeIncluded; return ( diff --git a/public/app/features/expressions/components/SqlExpr.tsx b/public/app/features/expressions/components/SqlExpr.tsx index f05ed42c224..5b8d6c0a424 100644 --- a/public/app/features/expressions/components/SqlExpr.tsx +++ b/public/app/features/expressions/components/SqlExpr.tsx @@ -1,7 +1,7 @@ import { useMemo } from 'react'; import { SelectableValue } from '@grafana/data'; -import { SQLEditor } from '@grafana/experimental'; +import { SQLEditor } from '@grafana/plugin-ui'; import { ExpressionQuery } from '../types'; diff --git a/public/app/features/expressions/components/Threshold.tsx b/public/app/features/expressions/components/Threshold.tsx index fac93a84c0b..6d0d43eb7e9 100644 --- a/public/app/features/expressions/components/Threshold.tsx +++ b/public/app/features/expressions/components/Threshold.tsx @@ -5,9 +5,9 @@ import * as React from 'react'; import { FormEvent, useEffect, useReducer } from 'react'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; -import { Stack } from '@grafana/experimental'; -import { InlineField, InlineFieldRow, InlineSwitch, Input, Select, useStyles2 } from '@grafana/ui'; +import { InlineField, InlineFieldRow, InlineSwitch, Input, Select, Stack, useStyles2 } from '@grafana/ui'; import { config } from 'app/core/config'; +import { t } from 'app/core/internationalization'; import { EvalFunction } from 'app/features/alerting/state/alertDef'; import { ClassicCondition, ExpressionQuery, thresholdFunctions } from '../types'; @@ -82,7 +82,9 @@ export const Threshold = ({ labelWidth, onChange, refIds, query, onError, useHys const isRange = conditionInState.evaluator.type === EvalFunction.IsWithinRange || - conditionInState.evaluator.type === EvalFunction.IsOutsideRange; + conditionInState.evaluator.type === EvalFunction.IsOutsideRange || + conditionInState.evaluator.type === EvalFunction.IsOutsideRangeIncluded || + conditionInState.evaluator.type === EvalFunction.IsWithinRangeIncluded; const hysteresisEnabled = Boolean(config.featureToggles?.recoveryThreshold) && useHysteresis; @@ -156,7 +158,7 @@ export const Threshold = ({ labelWidth, onChange, refIds, query, onError, useHys
- - -
- - allowOnblur.current && onUnloadValueChange(event, 0)} - defaultValue={condition.unloadEvaluator?.params[0]} - /> - -
- -
- - allowOnblur.current && onUnloadValueChange(event, 1)} - defaultValue={condition.unloadEvaluator?.params[1]} - /> - -
-
-
- - ); - } else { - return ( - - - -
- - allowOnblur.current && onUnloadValueChange(event, 0)} - defaultValue={condition.unloadEvaluator?.params[0]} - /> - -
+ switch (condition.evaluator.type) { + case EvalFunction.IsWithinRange: + if (condition.evaluator.type === EvalFunction.IsWithinRange) { + return ( + + + +
+ + allowOnblur.current && onUnloadValueChange(event, 0)} + defaultValue={condition.unloadEvaluator?.params[0]} + /> + +
+ +
+ + allowOnblur.current && onUnloadValueChange(event, 1)} + defaultValue={condition.unloadEvaluator?.params[1]} + /> + +
+
+
+
+ ); + } + case EvalFunction.IsOutsideRange: + return ( + + + +
+ + allowOnblur.current && onUnloadValueChange(event, 0)} + defaultValue={condition.unloadEvaluator?.params[0]} + /> + +
- -
- - allowOnblur.current && onUnloadValueChange(event, 1)} - defaultValue={condition.unloadEvaluator?.params[1]} - /> - -
-
-
-
- ); + +
+ + allowOnblur.current && onUnloadValueChange(event, 1)} + defaultValue={condition.unloadEvaluator?.params[1]} + /> + +
+
+
+
+ ); + case EvalFunction.IsOutsideRangeIncluded: + return ( + + + +
+ + allowOnblur.current && onUnloadValueChange(event, 0)} + defaultValue={condition.unloadEvaluator?.params[0]} + /> + +
+ +
+ + allowOnblur.current && onUnloadValueChange(event, 1)} + defaultValue={condition.unloadEvaluator?.params[1]} + /> + +
+
+
+
+ ); + case EvalFunction.IsWithinRangeIncluded: + return ( + + + +
+ + allowOnblur.current && onUnloadValueChange(event, 0)} + defaultValue={condition.unloadEvaluator?.params[0]} + /> + +
+ +
+ + allowOnblur.current && onUnloadValueChange(event, 1)} + defaultValue={condition.unloadEvaluator?.params[1]} + /> + +
+
+
+
+ ); + default: + return null; } } function RecoveryForSingleValue({ allowOnblur }: RecoveryProps) { - if (condition.evaluator.type === EvalFunction.IsAbove) { - return ( - - - { - allowOnblur.current && onUnloadValueChange(event, 0); - }} - defaultValue={condition.unloadEvaluator?.params[0]} - /> - - - ); - } else { - return ( - - - { - allowOnblur.current && onUnloadValueChange(event, 0); - }} - defaultValue={condition.unloadEvaluator?.params[0]} - /> - - - ); + switch (condition.evaluator.type) { + case EvalFunction.IsAbove: + return ( + + + { + allowOnblur.current && onUnloadValueChange(event, 0); + }} + defaultValue={condition.unloadEvaluator?.params[0]} + /> + + + ); + case EvalFunction.IsBelow: + return ( + + + { + allowOnblur.current && onUnloadValueChange(event, 0); + }} + defaultValue={condition.unloadEvaluator?.params[0]} + /> + + + ); + case EvalFunction.IsEqual: + return ( + + + { + allowOnblur.current && onUnloadValueChange(event, 0); + }} + defaultValue={condition.unloadEvaluator?.params[0]} + /> + + + ); + case EvalFunction.IsNotEqual: + return ( + + + { + allowOnblur.current && onUnloadValueChange(event, 0); + }} + defaultValue={condition.unloadEvaluator?.params[0]} + /> + + + ); + case EvalFunction.IsGreaterThanEqual: + return ( + + + { + allowOnblur.current && onUnloadValueChange(event, 0); + }} + defaultValue={condition.unloadEvaluator?.params[0]} + /> + + + ); + case EvalFunction.IsLessThanEqual: + return ( + + + { + allowOnblur.current && onUnloadValueChange(event, 0); + }} + defaultValue={condition.unloadEvaluator?.params[0]} + /> + + + ); + default: + return null; } } } diff --git a/public/app/features/expressions/components/thresholdReducer.ts b/public/app/features/expressions/components/thresholdReducer.ts index 7fa1e428a98..503baa3e727 100644 --- a/public/app/features/expressions/components/thresholdReducer.ts +++ b/public/app/features/expressions/components/thresholdReducer.ts @@ -104,12 +104,30 @@ function getUnloadEvaluatorTypeFromEvaluatorType(type: EvalFunction) { if (type === EvalFunction.IsBelow) { return EvalFunction.IsAbove; } + if (type === EvalFunction.IsEqual) { + return EvalFunction.IsNotEqual; + } + if (type === EvalFunction.IsNotEqual) { + return EvalFunction.IsEqual; + } + if (type === EvalFunction.IsGreaterThanEqual) { + return EvalFunction.IsLessThanEqual; + } + if (type === EvalFunction.IsLessThanEqual) { + return EvalFunction.IsGreaterThanEqual; + } if (type === EvalFunction.IsWithinRange) { return EvalFunction.IsOutsideRange; } if (type === EvalFunction.IsOutsideRange) { return EvalFunction.IsWithinRange; } + if (type === EvalFunction.IsWithinRangeIncluded) { + return EvalFunction.IsOutsideRangeIncluded; + } + if (type === EvalFunction.IsOutsideRangeIncluded) { + return EvalFunction.IsWithinRangeIncluded; + } return EvalFunction.IsBelow; } @@ -126,7 +144,12 @@ export function isInvalid(condition: ClassicCondition) { const { type, params: loadParams } = evaluator; const { params: unloadParams } = unloadEvaluator; - if (type === EvalFunction.IsWithinRange || type === EvalFunction.IsOutsideRange) { + if ( + type === EvalFunction.IsWithinRange || + type === EvalFunction.IsOutsideRange || + type === EvalFunction.IsWithinRangeIncluded || + type === EvalFunction.IsOutsideRangeIncluded + ) { if (unloadParams[0] === undefined || Number.isNaN(unloadParams[0])) { return { errorMsgFrom: 'This value cannot be empty' }; } @@ -149,6 +172,26 @@ export function isInvalid(condition: ClassicCondition) { return { errorMsg: `Enter a number more than or equal to ${firstParamInEvaluator}` }; } break; + case EvalFunction.IsEqual: + if (firstParamInUnloadEvaluator === firstParamInEvaluator) { + return { errorMsg: `Enter a different number than ${firstParamInEvaluator}` }; + } + break; + case EvalFunction.IsNotEqual: + if (firstParamInUnloadEvaluator !== firstParamInEvaluator) { + return { errorMsg: `Enter the same number as ${firstParamInEvaluator}` }; + } + break; + case EvalFunction.IsGreaterThanEqual: + if (firstParamInUnloadEvaluator >= firstParamInEvaluator) { + return { errorMsg: `Enter a number less than ${firstParamInEvaluator}` }; + } + break; + case EvalFunction.IsLessThanEqual: + if (firstParamInUnloadEvaluator <= firstParamInEvaluator) { + return { errorMsg: `Enter a number more than ${firstParamInEvaluator}` }; + } + break; case EvalFunction.IsOutsideRange: if (firstParamInUnloadEvaluator < firstParamInEvaluator) { return { errorMsgFrom: `Enter a number more than or equal to ${firstParamInEvaluator}` }; @@ -165,6 +208,22 @@ export function isInvalid(condition: ClassicCondition) { return { errorMsgTo: `Enter a number be more than or equal to ${secondParamInEvaluator}` }; } break; + case EvalFunction.IsOutsideRangeIncluded: + if (firstParamInUnloadEvaluator <= firstParamInEvaluator) { + return { errorMsgFrom: `Enter a number more than ${firstParamInEvaluator}` }; + } + if (secondParamInUnloadEvaluator >= secondParamInEvaluator) { + return { errorMsgTo: `Enter a number less than ${secondParamInEvaluator}` }; + } + break; + case EvalFunction.IsWithinRangeIncluded: + if (firstParamInUnloadEvaluator >= firstParamInEvaluator) { + return { errorMsgFrom: `Enter a number less than ${firstParamInEvaluator}` }; + } + if (secondParamInUnloadEvaluator <= secondParamInEvaluator) { + return { errorMsgTo: `Enter a number be more than ${secondParamInEvaluator}` }; + } + break; default: throw new Error(`evaluator function type ${type} not supported.`); } diff --git a/public/app/features/expressions/types.ts b/public/app/features/expressions/types.ts index a1f88729c1e..a132670f00f 100644 --- a/public/app/features/expressions/types.ts +++ b/public/app/features/expressions/types.ts @@ -126,8 +126,14 @@ export const upsamplingTypes: Array> = [ export const thresholdFunctions: Array> = [ { value: EvalFunction.IsAbove, label: 'Is above' }, { value: EvalFunction.IsBelow, label: 'Is below' }, + { value: EvalFunction.IsEqual, label: 'Is equal to' }, + { value: EvalFunction.IsNotEqual, label: 'Is not equal to' }, + { value: EvalFunction.IsGreaterThanEqual, label: 'Is above or equal to' }, + { value: EvalFunction.IsLessThanEqual, label: 'Is below or equal to' }, { value: EvalFunction.IsWithinRange, label: 'Is within range' }, { value: EvalFunction.IsOutsideRange, label: 'Is outside range' }, + { value: EvalFunction.IsWithinRangeIncluded, label: 'Is within range included' }, + { value: EvalFunction.IsOutsideRangeIncluded, label: 'Is outside range included' }, ]; /** diff --git a/public/app/features/gops/configuration-tracker/incidents/hooks.ts b/public/app/features/gops/configuration-tracker/incidents/hooks.ts index aabbaf56a2b..748c21b4b11 100644 --- a/public/app/features/gops/configuration-tracker/incidents/hooks.ts +++ b/public/app/features/gops/configuration-tracker/incidents/hooks.ts @@ -1,6 +1,6 @@ import { incidentsApi } from 'app/features/alerting/unified/api/incidentsApi'; import { usePluginBridge } from 'app/features/alerting/unified/hooks/usePluginBridge'; -import { SupportedPlugin } from 'app/features/alerting/unified/types/pluginBridges'; +import { getIrmIfPresentOrIncidentPluginId } from 'app/features/alerting/unified/utils/config'; interface IncidentsPluginConfig { isInstalled: boolean; @@ -11,7 +11,7 @@ interface IncidentsPluginConfig { export function useGetIncidentPluginConfig(): IncidentsPluginConfig { const { installed: incidentPluginInstalled, loading: loadingPluginSettings } = usePluginBridge( - SupportedPlugin.Incident + getIrmIfPresentOrIncidentPluginId() ); const { data: incidentsConfig, isLoading: loadingPluginConfig } = incidentsApi.endpoints.getIncidentsPluginConfig.useQuery(); diff --git a/public/app/features/gops/configuration-tracker/irmHooks.ts b/public/app/features/gops/configuration-tracker/irmHooks.ts index 3510de2e5e5..082d96278ab 100644 --- a/public/app/features/gops/configuration-tracker/irmHooks.ts +++ b/public/app/features/gops/configuration-tracker/irmHooks.ts @@ -3,6 +3,11 @@ import { useMemo } from 'react'; import { locationService } from '@grafana/runtime'; import { useGrafanaContactPoints } from 'app/features/alerting/unified/components/contact-points/useContactPoints'; import { useNotificationPolicyRoute } from 'app/features/alerting/unified/components/notification-policies/useNotificationPolicyRoute'; +import { + getIrmIfPresentOrIncidentPluginId, + getIrmIfPresentOrOnCallPluginId, + getIsIrmPluginPresent, +} from 'app/features/alerting/unified/utils/config'; import { GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource'; import { RelativeUrl, createRelativeUrl } from 'app/features/alerting/unified/utils/url'; @@ -121,181 +126,249 @@ export function useGetEssentialsConfiguration(): EssentialsConfigurationData { locationService.push(urlToGoWithIntegration); } + function getGrafanaAlertingConfigSteps(): SectionDtoStep[] { + let steps: SectionDtoStep[] = [ + { + title: 'Update default contact point', + description: 'Update the default contact point to a method other than the example email address.', + button: { + type: 'openLink', + urlLink: { + url: `/alerting/notifications`, + queryParams: { search: defaultContactpoint, alertmanager: 'grafana' }, + }, + label: 'Edit', + labelOnDone: 'View', + urlLinkOnDone: { + url: `/alerting/notifications`, + }, + }, + done: isContactPointReady(defaultContactpoint, contactPoints), + }, + ]; + + if (!getIsIrmPluginPresent()) { + steps = [ + ...steps, + { + title: 'Connect alerting to OnCall', + description: 'Create an OnCall integration for an alerting contact point.', + button: { + type: 'openLink', + urlLink: { + url: '/alerting/notifications/receivers/new', + }, + label: 'Connect', + urlLinkOnDone: { + url: '/alerting/notifications', + }, + labelOnDone: 'View', + }, + done: isOnCallContactPointReady(contactPoints), + }, + ]; + } + + steps = [ + ...steps, + { + title: 'Create alert rule', + description: 'Create an alert rule to monitor your system.', + button: { + type: 'openLink', + urlLink: { + url: '/alerting/new', + }, + label: 'Create', + urlLinkOnDone: { + url: '/alerting/list', + }, + labelOnDone: 'View', + }, + done: isCreateAlertRuleDone, + }, + { + title: 'Create SLO', + description: 'Create SLOs to monitor your service.', + button: { + type: 'openLink', + urlLink: { + url: '/a/grafana-slo-app/wizard/new', + }, + label: 'Create', + urlLinkOnDone: { + url: '/a/grafana-slo-app/manage-slos', + }, + labelOnDone: 'View', + }, + done: hasSlo, + }, + { + title: 'Enable SLO alerting', + description: 'Configure SLO alerting to receive notifications when your SLOs are breached.', + button: { + type: 'openLink', + urlLink: { + queryParams: { alertsEnabled: 'disabled' }, + url: '/a/grafana-slo-app/manage-slos', + }, + label: 'Enable', + urlLinkOnDone: { + queryParams: { alertsEnabled: 'enabled' }, + url: '/a/grafana-slo-app/manage-slos', + }, + labelOnDone: 'View', + }, + done: hasSloWithAlert, + }, + ]; + + return steps; + } + const essentialContent: SectionsDto = { sections: [ { title: 'Detect', description: 'Configure Grafana Alerting', - steps: [ - { - title: 'Update default contact point', - description: 'Update the default contact point to a method other than the example email address.', - button: { - type: 'openLink', - urlLink: { - url: `/alerting/notifications`, - queryParams: { search: defaultContactpoint, alertmanager: 'grafana' }, - }, - label: 'Edit', - labelOnDone: 'View', - urlLinkOnDone: { - url: `/alerting/notifications`, - }, - }, - done: isContactPointReady(defaultContactpoint, contactPoints), - }, - { - title: 'Connect alerting to OnCall', - description: 'Create an OnCall integration for an alerting contact point.', - button: { - type: 'openLink', - urlLink: { - url: '/alerting/notifications/receivers/new', - }, - label: 'Connect', - urlLinkOnDone: { - url: '/alerting/notifications', - }, - labelOnDone: 'View', - }, - done: isOnCallContactPointReady(contactPoints), - }, - { - title: 'Create alert rule', - description: 'Create an alert rule to monitor your system.', - button: { - type: 'openLink', - urlLink: { - url: '/alerting/new', - }, - label: 'Create', - urlLinkOnDone: { - url: '/alerting/list', - }, - labelOnDone: 'View', - }, - done: isCreateAlertRuleDone, - }, - { - title: 'Create your first SLO', - description: 'Create SLOs to monitor your service.', - button: { - type: 'openLink', - urlLink: { - url: '/a/grafana-slo-app/wizard/new', - }, - label: 'Create', - urlLinkOnDone: { - url: '/a/grafana-slo-app/manage-slos', - }, - labelOnDone: 'View', - }, - done: hasSlo, - }, - { - title: 'Enable SLO alerting', - description: 'Configure SLO alerting to receive notifications when your SLOs are breached.', - button: { - type: 'openLink', - urlLink: { - queryParams: { alertsEnabled: 'disabled' }, - url: '/a/grafana-slo-app/manage-slos', - }, - label: 'Enable', - urlLinkOnDone: { - queryParams: { alertsEnabled: 'enabled' }, - url: '/a/grafana-slo-app/manage-slos', - }, - labelOnDone: 'View', - }, - done: hasSloWithAlert, - }, - ], + steps: getGrafanaAlertingConfigSteps(), }, { title: 'Respond', - description: 'Configure OnCall and Incident', - steps: [ - { - title: 'Initialize Incident plugin', - description: 'Initialize the Incident plugin to declare and manage incidents.', - button: { - type: 'openLink', - urlLink: { - url: '/a/grafana-incident-app/walkthrough/generate-key', + description: getIsIrmPluginPresent() ? 'Configure IRM' : 'Configure OnCall and Incident', + steps: getIsIrmPluginPresent() + ? [ + { + title: 'Connect alerting to IRM', + description: 'Create an IRM integration for an alerting contact point.', + button: { + type: 'openLink', + urlLink: { + url: '/alerting/notifications/receivers/new', + }, + label: 'Connect', + urlLinkOnDone: { + url: '/alerting/notifications', + }, + labelOnDone: 'View', + }, + done: isOnCallContactPointReady(contactPoints), }, - label: 'Initialize', - urlLinkOnDone: { - url: '/a/grafana-incident-app', + { + title: 'Connect IRM to your Slack workspace', + description: + 'Receive alerts and oncall notifications, or automatically create an incident channel and manage incidents directly within your chat environment.', + button: { + type: 'openLink', + urlLink: { + url: `/a/${getIrmIfPresentOrIncidentPluginId()}/integrations/apps/grate.irm.slack`, + }, + label: 'Connect', + urlLinkOnDone: { + url: `/a/${getIrmIfPresentOrIncidentPluginId()}/integrations`, + }, + }, + done: isChatOpsInstalled, }, - labelOnDone: 'View', - }, - done: isIncidentsInstalled, - }, - { - title: 'Connect your Messaging workspace to OnCall', - description: 'Receive alerts and oncall notifications within your chat environment.', - button: { - type: 'openLink', - urlLink: { - url: '/a/grafana-oncall-app/settings', - queryParams: { tab: 'ChatOps', chatOpsTab: 'Slack' }, + { + title: 'Add Slack notifications to IRM integrations', + description: 'Select ChatOps channels to route notifications', + button: { + type: 'openLink', + urlLink: { + url: `/a/${getIrmIfPresentOrOnCallPluginId()}/integrations/`, + }, + label: 'Add', + urlLinkOnDone: { + url: `/a/${getIrmIfPresentOrOnCallPluginId()}/integrations/`, + }, + labelOnDone: 'View', + }, + done: is_integration_chatops_connected, }, - label: 'Connect', - urlLinkOnDone: { - url: '/a/grafana-oncall-app/settings', - queryParams: { tab: 'ChatOps' }, + ] + : [ + { + title: 'Initialize Incident plugin', + description: 'Initialize the Incident plugin to declare and manage incidents.', + button: { + type: 'openLink', + urlLink: { + url: `/a/${getIrmIfPresentOrIncidentPluginId()}/walkthrough/generate-key`, + }, + label: 'Initialize', + urlLinkOnDone: { + url: `/a/${getIrmIfPresentOrIncidentPluginId()}`, + }, + labelOnDone: 'View', + }, + done: isIncidentsInstalled, }, - labelOnDone: 'View', - }, - done: is_chatops_connected, - }, - { - title: 'Connect your Messaging workspace to Incident', - description: - 'Automatically create an incident channel and manage incidents directly within your chat environment.', - button: { - type: 'openLink', - urlLink: { - url: '/a/grafana-incident-app/integrations/grate.slack', + { + title: 'Connect your Messaging workspace to OnCall', + description: 'Receive alerts and oncall notifications within your chat environment.', + button: { + type: 'openLink', + urlLink: { + url: `/a/${getIrmIfPresentOrOnCallPluginId()}/settings`, + queryParams: { tab: 'ChatOps', chatOpsTab: 'Slack' }, + }, + label: 'Connect', + urlLinkOnDone: { + url: `/a/${getIrmIfPresentOrOnCallPluginId()}/settings`, + queryParams: { tab: 'ChatOps' }, + }, + labelOnDone: 'View', + }, + done: is_chatops_connected, }, - label: 'Connect', - urlLinkOnDone: { - url: '/a/grafana-incident-app/integrations', + { + title: 'Connect your Messaging workspace to Incident', + description: + 'Automatically create an incident channel and manage incidents directly within your chat environment.', + button: { + type: 'openLink', + urlLink: { + url: `/a/${getIrmIfPresentOrIncidentPluginId()}/integrations/grate.slack`, + }, + label: 'Connect', + urlLinkOnDone: { + url: `/a/${getIrmIfPresentOrIncidentPluginId()}/integrations`, + }, + }, + done: isChatOpsInstalled, }, - }, - done: isChatOpsInstalled, - }, - { - title: 'Add Messaging workspace channel to OnCall Integration', - description: 'Select ChatOps channels to route notifications', - button: { - type: 'openLink', - urlLink: { - url: '/a/grafana-oncall-app/integrations/', + { + title: 'Add Messaging workspace channel to OnCall Integration', + description: 'Select ChatOps channels to route notifications', + button: { + type: 'openLink', + urlLink: { + url: `/a/${getIrmIfPresentOrOnCallPluginId()}/integrations/`, + }, + label: 'Add', + urlLinkOnDone: { + url: `/a/${getIrmIfPresentOrOnCallPluginId()}/integrations/`, + }, + labelOnDone: 'View', + }, + done: is_integration_chatops_connected, }, - label: 'Add', - urlLinkOnDone: { - url: '/a/grafana-oncall-app/integrations/', - }, - labelOnDone: 'View', - }, - done: is_integration_chatops_connected, - }, - ], + ], }, { title: 'Test your configuration', description: '', steps: [ { - title: 'Send OnCall demo alert via Alerting integration', + title: getIsIrmPluginPresent() ? 'Send test alert' : 'Send OnCall demo alert via Alerting integration', description: 'In the integration page, click Send demo alert, to review your notification', button: { type: 'dropDown', label: 'Select integration', options: onCallOptions, - onClickOption: (value) => onIntegrationClick(value, '/a/grafana-oncall-app/integrations/'), + onClickOption: (value) => + onIntegrationClick(value, `/a/${getIrmIfPresentOrOnCallPluginId()}/integrations/`), stepNotAvailableText: 'No integrations available', }, }, @@ -305,7 +378,7 @@ export function useGetEssentialsConfiguration(): EssentialsConfigurationData { button: { type: 'openLink', urlLink: { - url: '/a/grafana-incident-app', + url: `/a/${getIrmIfPresentOrIncidentPluginId()}`, queryParams: { declare: 'new', drill: '1' }, }, label: 'Start drill', diff --git a/public/app/features/gops/configuration-tracker/onCall/hooks.ts b/public/app/features/gops/configuration-tracker/onCall/hooks.ts index 7fb4862970d..fe7fb50c056 100644 --- a/public/app/features/gops/configuration-tracker/onCall/hooks.ts +++ b/public/app/features/gops/configuration-tracker/onCall/hooks.ts @@ -1,9 +1,9 @@ import { onCallApi } from 'app/features/alerting/unified/api/onCallApi'; import { usePluginBridge } from 'app/features/alerting/unified/hooks/usePluginBridge'; -import { SupportedPlugin } from 'app/features/alerting/unified/types/pluginBridges'; +import { getIrmIfPresentOrOnCallPluginId } from 'app/features/alerting/unified/utils/config'; export function useGetOnCallIntegrations() { - const { installed: onCallPluginInstalled } = usePluginBridge(SupportedPlugin.OnCall); + const { installed: onCallPluginInstalled } = usePluginBridge(getIrmIfPresentOrOnCallPluginId()); const { data: onCallIntegrations } = onCallApi.endpoints.grafanaOnCallIntegrations.useQuery(undefined, { skip: !onCallPluginInstalled, diff --git a/public/app/features/iam/api/api.ts b/public/app/features/iam/api/api.ts new file mode 100644 index 00000000000..07a735c561a --- /dev/null +++ b/public/app/features/iam/api/api.ts @@ -0,0 +1,12 @@ +import { createApi } from '@reduxjs/toolkit/query/react'; + +import { createBaseQuery } from '../../../api/createBaseQuery'; +import { getAPIBaseURL } from '../../../api/utils'; + +export const BASE_URL = getAPIBaseURL('iam.grafana.app', 'v0alpha1'); + +export const iamApi = createApi({ + baseQuery: createBaseQuery({ baseURL: BASE_URL }), + reducerPath: 'iamAPI', + endpoints: () => ({}), +}); diff --git a/public/app/features/iam/api/endpoints.gen.ts b/public/app/features/iam/api/endpoints.gen.ts new file mode 100644 index 00000000000..9f424e5ec09 --- /dev/null +++ b/public/app/features/iam/api/endpoints.gen.ts @@ -0,0 +1,64 @@ +import { iamApi as api } from './api'; +export const addTagTypes = ['Display'] as const; +const injectedRtkApi = api + .enhanceEndpoints({ + addTagTypes, + }) + .injectEndpoints({ + endpoints: (build) => ({ + getDisplayMapping: build.query({ + query: (queryArg) => ({ + url: `/display`, + params: { + key: queryArg.key, + }, + }), + providesTags: ['Display'], + }), + }), + overrideExisting: false, + }); +export { injectedRtkApi as generatedIamApi }; +export type GetDisplayMappingApiResponse = /** status 200 undefined */ DisplayList; +export type GetDisplayMappingApiArg = { + /** Display keys */ + key: string[]; +}; +export type IdentityRef = { + /** Name is the unique identifier for identity, guaranteed to be a unique value for the type within a namespace. */ + name: string; + /** Type of identity e.g. "user". For a full list see https://github.com/grafana/authlib/blob/d6737a7dc8f55e9d42834adb83b5da607ceed293/types/type.go#L15 */ + type: string; +}; +export type Display = { + /** AvatarURL is the url where we can get the avatar for identity */ + avatarURL?: string; + /** Display name for identity. */ + displayName: string; + identity: IdentityRef; + /** InternalID is the legacy numeric id for identity, Deprecated: use the identityRef where possible */ + internalId?: number; +}; +export type ListMeta = { + /** continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message. */ + continue?: string; + /** remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact. */ + remainingItemCount?: number; + /** String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency */ + resourceVersion?: string; + /** Deprecated: selfLink is a legacy read-only field that is no longer populated by the system. */ + selfLink?: string; +}; +export type DisplayList = { + /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */ + apiVersion?: string; + /** Matching items (the caller may need to remap from keys to results) */ + display: Display[]; + /** Input keys that were not useable */ + invalidKeys?: string[]; + /** Request keys used to lookup the display value */ + keys: string[]; + /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */ + kind?: string; + metadata?: ListMeta; +}; diff --git a/public/app/features/iam/index.ts b/public/app/features/iam/index.ts new file mode 100644 index 00000000000..5236538f8dd --- /dev/null +++ b/public/app/features/iam/index.ts @@ -0,0 +1,3 @@ +import { generatedIamApi } from './api/endpoints.gen'; + +export const { useGetDisplayMappingQuery } = generatedIamApi; diff --git a/public/app/features/inspector/InspectDataTab.test.tsx b/public/app/features/inspector/InspectDataTab.test.tsx index a19b4667437..3e7340d6733 100644 --- a/public/app/features/inspector/InspectDataTab.test.tsx +++ b/public/app/features/inspector/InspectDataTab.test.tsx @@ -4,6 +4,7 @@ import { ComponentProps } from 'react'; import { Props } from 'react-virtualized-auto-sizer'; import { DataFrame, FieldType } from '@grafana/data'; +import { config } from '@grafana/runtime'; import { InspectDataTab } from './InspectDataTab'; @@ -75,6 +76,8 @@ describe('InspectDataTab', () => { expect(screen.getByText(/Second data frame/i)).toBeInTheDocument(); }); it('should show download logs button if logs data', () => { + const oldConfig = config.exploreHideLogsDownload; + config.exploreHideLogsDownload = false; const dataWithLogs = [ { name: 'Data frame with logs', @@ -91,6 +94,28 @@ describe('InspectDataTab', () => { ] as unknown as DataFrame[]; render(); expect(screen.getByText(/Download logs/i)).toBeInTheDocument(); + config.exploreHideLogsDownload = oldConfig; + }); + it('should not show download logs button if logs data but config disabled', () => { + const oldConfig = config.exploreHideLogsDownload; + config.exploreHideLogsDownload = true; + const dataWithLogs = [ + { + name: 'Data frame with logs', + fields: [ + { name: 'time', type: FieldType.time, values: [100, 200, 300], config: {} }, + { name: 'name', type: FieldType.string, values: ['uniqueA', 'b', 'c'], config: {} }, + { name: 'value', type: FieldType.number, values: [1, 2, 3], config: {} }, + ], + length: 3, + meta: { + preferredVisualisationType: 'logs', + }, + }, + ] as unknown as DataFrame[]; + render(); + expect(screen.queryByText(/Download logs/i)).not.toBeInTheDocument(); + config.exploreHideLogsDownload = oldConfig; }); it('should not show download logs button if no logs data', () => { render(); diff --git a/public/app/features/inspector/InspectDataTab.tsx b/public/app/features/inspector/InspectDataTab.tsx index dcecf5386dc..49bae7ddbed 100644 --- a/public/app/features/inspector/InspectDataTab.tsx +++ b/public/app/features/inspector/InspectDataTab.tsx @@ -223,7 +223,7 @@ export class InspectDataTab extends PureComponent { - {hasLogs && ( + {hasLogs && !config.exploreHideLogsDownload && ( diff --git a/public/app/features/logs/components/InfiniteScroll.tsx b/public/app/features/logs/components/InfiniteScroll.tsx index 4fffcf4c62e..36d748d2db1 100644 --- a/public/app/features/logs/components/InfiniteScroll.tsx +++ b/public/app/features/logs/components/InfiniteScroll.tsx @@ -214,12 +214,12 @@ const outOfRangeMessage = (
); -enum ScrollDirection { +export enum ScrollDirection { Top = -1, Bottom = 1, NoScroll = 0, } -function shouldLoadMore( +export function shouldLoadMore( event: Event | WheelEvent, lastEvent: Event | WheelEvent | null, countRef: MutableRefObject, @@ -284,7 +284,7 @@ function shouldIgnoreChainOfEvents( return true; } -function getVisibleRange(rows: LogRowModel[]) { +export function getVisibleRange(rows: LogRowModel[]) { const firstTimeStamp = rows[0].timeEpochMs; const lastTimeStamp = rows[rows.length - 1].timeEpochMs; @@ -326,7 +326,7 @@ function canScrollTop( return canScroll ? getPrevRange(visibleRange, currentRange) : undefined; } -function canScrollBottom( +export function canScrollBottom( visibleRange: AbsoluteTimeRange, currentRange: TimeRange, timeZone: TimeZone, diff --git a/public/app/features/logs/components/LogRow.tsx b/public/app/features/logs/components/LogRow.tsx index 49a8f86c32a..30fc8c773ba 100644 --- a/public/app/features/logs/components/LogRow.tsx +++ b/public/app/features/logs/components/LogRow.tsx @@ -14,6 +14,7 @@ import { import { reportInteraction } from '@grafana/runtime'; import { DataQuery, TimeZone } from '@grafana/schema'; import { Icon, PopoverContent, Tooltip, useTheme2 } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; import { checkLogsError, checkLogsSampled, escapeUnescapedString } from '../utils'; @@ -244,7 +245,13 @@ export const LogRow = ({ className={enableLogDetails ? styles.logsRowToggleDetails : ''} > {enableLogDetails && ( - + )} {showTime && {timestamp}} diff --git a/public/app/features/logs/components/LogRowMessage.tsx b/public/app/features/logs/components/LogRowMessage.tsx index f771d9a2bb6..d3788b3c7cf 100644 --- a/public/app/features/logs/components/LogRowMessage.tsx +++ b/public/app/features/logs/components/LogRowMessage.tsx @@ -157,6 +157,7 @@ export const LogRowMessage = memo((props: Props) => { [raw, prettifyLogMessage, wrapLogMessage, expanded] ); const shouldShowMenu = mouseIsOver || pinned; + return ( <> { @@ -165,9 +166,9 @@ export const LogRowMessage = memo((props: Props) => { }
- +
diff --git a/public/app/features/logs/components/LogRows.tsx b/public/app/features/logs/components/LogRows.tsx index 206e3b88023..f0e037c5665 100644 --- a/public/app/features/logs/components/LogRows.tsx +++ b/public/app/features/logs/components/LogRows.tsx @@ -61,7 +61,6 @@ export interface Props { permalinkedRowId?: string; scrollIntoView?: (element: HTMLElement) => void; isFilterLabelActive?: (key: string, value: string, refId?: string) => Promise; - pinnedRowId?: string; pinnedLogs?: string[]; /** * If false or undefined, the `contain:strict` css property will be added to the wrapping `` for performance reasons. @@ -305,7 +304,7 @@ export const LogRows = memo( onPinLine={props.onPinLine} onUnpinLine={props.onUnpinLine} pinLineButtonTooltipTitle={props.pinLineButtonTooltipTitle} - pinned={props.pinnedRowId === row.uid || pinnedLogs?.some((logId) => logId === row.rowId)} + pinned={pinnedLogs?.some((logId) => logId === row.rowId || logId === row.uid)} isFilterLabelActive={props.isFilterLabelActive} handleTextSelection={handleSelection} enableLogDetails={enableLogDetails} diff --git a/public/app/features/logs/components/getLogRowStyles.ts b/public/app/features/logs/components/getLogRowStyles.ts index 9b965b65ea1..8605753487e 100644 --- a/public/app/features/logs/components/getLogRowStyles.ts +++ b/public/app/features/logs/components/getLogRowStyles.ts @@ -100,6 +100,11 @@ export const getLogRowStyles = memoizeOne((theme: GrafanaTheme2) => { cursor: 'pointer', verticalAlign: 'top', + '&:focus-within': { + outline: `2px solid ${theme.colors.primary.border}`, + outlineOffset: '-2px', + }, + '&:hover': { '.log-row-menu': { zIndex: 1, @@ -139,7 +144,6 @@ export const getLogRowStyles = memoizeOne((theme: GrafanaTheme2) => { logsRowToggleDetails: css({ label: 'logs-row-toggle-details__level', fontSize: '9px', - paddingTop: '5px', maxWidth: '15px', }), logsRowLocalTime: css({ @@ -221,6 +225,29 @@ export const getLogRowStyles = memoizeOne((theme: GrafanaTheme2) => { backgroundColor: hoverBgColor, }, }), + detailsToggle: css({ + appearance: 'none', + background: 'none', + border: 'none', + padding: 0, + // Don't increase the height of the row + maxHeight: '19px', + + // Don't show default button box-shadow on focus, we apply outline to the entire row instead + '&:focus-visible': { + boxShadow: 'none', + }, + + '&:focus': { + outline: 0, + }, + '&:after': { + content: '""', + inset: 0, + position: 'absolute', + }, + }), + // Log row topVerticalAlign: css({ label: 'topVerticalAlign', @@ -287,9 +314,6 @@ export const getLogRowStyles = memoizeOne((theme: GrafanaTheme2) => { }, }), logLine: css({ - backgroundColor: 'transparent', - border: 'none', - diplay: 'inline', fontFamily: theme.typography.fontFamilyMonospace, fontSize: theme.typography.bodySmall.fontSize, letterSpacing: theme.typography.bodySmall.letterSpacing, diff --git a/public/app/features/logs/components/log-context/LogRowContextModal.tsx b/public/app/features/logs/components/log-context/LogRowContextModal.tsx index 798e03ce065..60cb0d6a092 100644 --- a/public/app/features/logs/components/log-context/LogRowContextModal.tsx +++ b/public/app/features/logs/components/log-context/LogRowContextModal.tsx @@ -561,7 +561,7 @@ export const LogRowContextModal: React.FunctionComponent setSticky(false)} onPinLine={() => setSticky(true)} - pinnedRowId={sticky ? row.uid : undefined} + pinnedLogs={sticky ? [row.uid] : undefined} overflowingContent={true} scrollElement={null} /> diff --git a/public/app/features/logs/components/panel/InfiniteScroll.tsx b/public/app/features/logs/components/panel/InfiniteScroll.tsx new file mode 100644 index 00000000000..4f0e7a9584b --- /dev/null +++ b/public/app/features/logs/components/panel/InfiniteScroll.tsx @@ -0,0 +1,213 @@ +import { ReactNode, useCallback, useEffect, useRef, useState } from 'react'; +import { usePrevious } from 'react-use'; +import { ListChildComponentProps, ListOnItemsRenderedProps } from 'react-window'; + +import { AbsoluteTimeRange, LogsSortOrder, TimeRange } from '@grafana/data'; +import { config, reportInteraction } from '@grafana/runtime'; +import { Spinner } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; + +import { canScrollBottom, getVisibleRange, ScrollDirection, shouldLoadMore } from '../InfiniteScroll'; + +import { LogLine } from './LogLine'; +import { LogLineMessage } from './LogLineMessage'; +import { LogListModel } from './processing'; + +interface ChildrenProps { + itemCount: number; + getItemKey: (index: number) => string; + onItemsRendered: (props: ListOnItemsRenderedProps) => void; + Renderer: (props: ListChildComponentProps) => ReactNode; +} + +interface Props { + children: (props: ChildrenProps) => ReactNode; + handleOverflow: (index: number, id: string, height: number) => void; + loadMore?: (range: AbsoluteTimeRange) => void; + logs: LogListModel[]; + scrollElement: HTMLDivElement | null; + setInitialScrollPosition: () => void; + showTime: boolean; + sortOrder: LogsSortOrder; + timeRange: TimeRange; + timeZone: string; + wrapLogMessage: boolean; +} + +type InfiniteLoaderState = 'idle' | 'out-of-bounds' | 'pre-scroll' | 'loading'; + +export const InfiniteScroll = ({ + children, + handleOverflow, + loadMore, + logs, + scrollElement, + setInitialScrollPosition, + showTime, + sortOrder, + timeRange, + timeZone, + wrapLogMessage, +}: Props) => { + const [infiniteLoaderState, setInfiniteLoaderState] = useState('idle'); + const [autoScroll, setAutoScroll] = useState(false); + const prevLogs = usePrevious(logs); + const prevSortOrder = usePrevious(sortOrder); + const lastScroll = useRef(scrollElement?.scrollTop || 0); + const lastEvent = useRef(null); + const countRef = useRef(0); + const lastLogOfPage = useRef([]); + + useEffect(() => { + // Logs have not changed, ignore effect + if (!prevLogs || prevLogs === logs) { + return; + } + // New logs are from infinite scrolling + if (infiniteLoaderState === 'loading') { + // out-of-bounds if no new logs returned + setInfiniteLoaderState(logs.length === prevLogs.length ? 'out-of-bounds' : 'idle'); + } else { + lastLogOfPage.current = []; + setAutoScroll(true); + } + }, [infiniteLoaderState, logs, prevLogs]); + + useEffect(() => { + if (prevSortOrder && prevSortOrder !== sortOrder) { + setInfiniteLoaderState('idle'); + } + }, [prevSortOrder, sortOrder]); + + useEffect(() => { + if (autoScroll) { + setInitialScrollPosition(); + setAutoScroll(false); + } + }, [autoScroll, setInitialScrollPosition]); + + const onLoadMore = useCallback(() => { + const newRange = canScrollBottom(getVisibleRange(logs), timeRange, timeZone, sortOrder); + if (!newRange) { + setInfiniteLoaderState('out-of-bounds'); + return; + } + lastLogOfPage.current.push(logs[logs.length - 1].uid); + setInfiniteLoaderState('loading'); + loadMore?.(newRange); + + reportInteraction('grafana_logs_infinite_scrolling', { + direction: 'bottom', + sort_order: sortOrder, + }); + }, [loadMore, logs, sortOrder, timeRange, timeZone]); + + useEffect(() => { + if (!scrollElement || !loadMore || !config.featureToggles.logsInfiniteScrolling) { + return; + } + + function handleScroll(event: Event | WheelEvent) { + if (!scrollElement || !loadMore || !logs.length || infiniteLoaderState !== 'pre-scroll') { + return; + } + const scrollDirection = shouldLoadMore(event, lastEvent.current, countRef, scrollElement, lastScroll.current); + lastEvent.current = event; + lastScroll.current = scrollElement.scrollTop; + if (scrollDirection === ScrollDirection.Bottom) { + onLoadMore(); + } + } + + scrollElement.addEventListener('scroll', handleScroll); + scrollElement.addEventListener('wheel', handleScroll); + + return () => { + scrollElement.removeEventListener('scroll', handleScroll); + scrollElement.removeEventListener('wheel', handleScroll); + }; + }, [infiniteLoaderState, loadMore, logs.length, onLoadMore, scrollElement]); + + const Renderer = useCallback( + ({ index, style }: ListChildComponentProps) => { + if (!logs[index] && infiniteLoaderState !== 'idle') { + return ( + + {getMessageFromInfiniteLoaderState(infiniteLoaderState, sortOrder)} + + ); + } + return ( + + ); + }, + [handleOverflow, infiniteLoaderState, logs, onLoadMore, showTime, sortOrder, wrapLogMessage] + ); + + const onItemsRendered = useCallback( + (props: ListOnItemsRenderedProps) => { + if (!scrollElement || infiniteLoaderState === 'loading' || infiniteLoaderState === 'out-of-bounds') { + return; + } + if (scrollElement.scrollHeight <= scrollElement.clientHeight) { + return; + } + const lastLogIndex = logs.length - 1; + const preScrollIndex = logs.length - 2; + if (props.visibleStopIndex >= lastLogIndex) { + setInfiniteLoaderState('pre-scroll'); + } else if (props.visibleStartIndex < preScrollIndex) { + setInfiniteLoaderState('idle'); + } + }, + [infiniteLoaderState, logs.length, scrollElement] + ); + + const getItemKey = useCallback((index: number) => (logs[index] ? logs[index].uid : index.toString()), [logs]); + + const itemCount = logs.length && loadMore && infiniteLoaderState !== 'idle' ? logs.length + 1 : logs.length; + + return <>{children({ getItemKey, itemCount, onItemsRendered, Renderer })}; +}; + +function getMessageFromInfiniteLoaderState(state: InfiniteLoaderState, order: LogsSortOrder) { + switch (state) { + case 'out-of-bounds': + return t('logs.infinite-scroll.end-of-range', 'End of the selected time range.'); + case 'loading': + return ( + <> + {order === LogsSortOrder.Ascending + ? t('logs.infinite-scroll.load-newer', 'Loading newer logs...') + : t('logs.infinite-scroll.load-older', 'Loading older logs...')}{' '} + + + ); + case 'pre-scroll': + return t('logs.infinite-scroll.load-more', 'Scroll to load more'); + default: + return null; + } +} + +function getLogLineVariant(logs: LogListModel[], index: number, lastLogOfPage: string[]) { + if (!lastLogOfPage.length || !logs[index - 1]) { + return undefined; + } + const prevLog = logs[index - 1]; + for (const uid of lastLogOfPage) { + if (prevLog.uid === uid) { + // First log of an infinite scrolling page + return 'infinite-scroll'; + } + } + return undefined; +} diff --git a/public/app/features/logs/components/panel/LogLine.tsx b/public/app/features/logs/components/panel/LogLine.tsx new file mode 100644 index 00000000000..bba06ff1ab0 --- /dev/null +++ b/public/app/features/logs/components/panel/LogLine.tsx @@ -0,0 +1,140 @@ +import { css } from '@emotion/css'; +import { CSSProperties, useEffect, useRef } from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { useTheme2 } from '@grafana/ui'; + +import { LogListModel } from './processing'; +import { hasUnderOrOverflow } from './virtualization'; + +interface Props { + index: number; + log: LogListModel; + showTime: boolean; + style: CSSProperties; + onOverflow?: (index: number, id: string, height: number) => void; + variant?: 'infinite-scroll'; + wrapLogMessage: boolean; +} + +export const LogLine = ({ index, log, style, onOverflow, showTime, variant, wrapLogMessage }: Props) => { + const theme = useTheme2(); + const styles = getStyles(theme); + const logLineRef = useRef(null); + + useEffect(() => { + if (!onOverflow || !logLineRef.current) { + return; + } + const calculatedHeight = typeof style.height === 'number' ? style.height : undefined; + const actualHeight = hasUnderOrOverflow(logLineRef.current, calculatedHeight); + if (actualHeight) { + onOverflow(index, log.uid, actualHeight); + } + }, [index, log.uid, onOverflow, style.height]); + + return ( +
+
+ {showTime && {log.timestamp}} + {log.logLevel && {log.logLevel}} + {log.body} +
+
+ ); +}; + +export const getStyles = (theme: GrafanaTheme2) => { + const colors = { + critical: '#B877D9', + error: '#FF5286', + warning: '#FBAD37', + debug: '#6CCF8E', + trace: '#6ed0e0', + info: '#6E9FFF', + }; + + return { + logLine: css({ + color: theme.colors.text.primary, + fontFamily: theme.typography.fontFamilyMonospace, + fontSize: theme.typography.fontSize, + wordBreak: 'break-all', + '&:hover': { + opacity: 0.7, + }, + '&.infinite-scroll': { + '&::before': { + borderTop: `solid 1px ${theme.colors.border.strong}`, + content: '""', + height: 0, + left: 0, + position: 'absolute', + top: -3, + width: '100%', + }, + }, + }), + logLineMessage: css({ + fontFamily: theme.typography.fontFamily, + textAlign: 'center', + }), + timestamp: css({ + color: theme.colors.text.secondary, + display: 'inline-block', + marginRight: theme.spacing(1), + '&.level-critical': { + color: colors.critical, + }, + '&.level-error': { + color: colors.error, + }, + '&.level-info': { + color: colors.info, + }, + '&.level-warning': { + color: colors.warning, + }, + '&.level-debug': { + color: colors.debug, + }, + }), + level: css({ + color: theme.colors.text.secondary, + fontWeight: theme.typography.fontWeightBold, + display: 'inline-block', + marginRight: theme.spacing(1), + '&.level-critical': { + color: colors.critical, + }, + '&.level-error': { + color: colors.error, + }, + '&.level-warning': { + color: colors.warning, + }, + '&.level-info': { + color: colors.info, + }, + '&.level-debug': { + color: colors.debug, + }, + }), + loadMoreButton: css({ + background: 'transparent', + border: 'none', + display: 'inline', + }), + overflows: css({ + outline: 'solid 1px red', + }), + unwrappedLogLine: css({ + whiteSpace: 'pre', + paddingBottom: theme.spacing(0.75), + }), + wrappedLogLine: css({ + whiteSpace: 'pre-wrap', + paddingBottom: theme.spacing(0.75), + }), + }; +}; diff --git a/public/app/features/logs/components/panel/LogLineMessage.tsx b/public/app/features/logs/components/panel/LogLineMessage.tsx new file mode 100644 index 00000000000..2bdff1c03f5 --- /dev/null +++ b/public/app/features/logs/components/panel/LogLineMessage.tsx @@ -0,0 +1,27 @@ +import { CSSProperties, ReactNode } from 'react'; + +import { useTheme2 } from '@grafana/ui'; + +import { getStyles } from './LogLine'; + +interface Props { + children: ReactNode; + onClick?: () => void; + style: CSSProperties; +} + +export const LogLineMessage = ({ children, onClick, style }: Props) => { + const theme = useTheme2(); + const styles = getStyles(theme); + return ( +
+ {onClick ? ( + + ) : ( + children + )} +
+ ); +}; diff --git a/public/app/features/logs/components/panel/LogList.tsx b/public/app/features/logs/components/panel/LogList.tsx new file mode 100644 index 00000000000..9e42cb73214 --- /dev/null +++ b/public/app/features/logs/components/panel/LogList.tsx @@ -0,0 +1,150 @@ +import { debounce } from 'lodash'; +import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'; +import { VariableSizeList } from 'react-window'; + +import { AbsoluteTimeRange, CoreApp, EventBus, LogRowModel, LogsSortOrder, TimeRange } from '@grafana/data'; +import { useTheme2 } from '@grafana/ui'; + +import { InfiniteScroll } from './InfiniteScroll'; +import { preProcessLogs, LogListModel } from './processing'; +import { + getLogLineSize, + init as initVirtualization, + resetLogLineSizes, + ScrollToLogsEvent, + storeLogLineSize, +} from './virtualization'; + +interface Props { + app: CoreApp; + logs: LogRowModel[]; + containerElement: HTMLDivElement; + eventBus: EventBus; + forceEscape?: boolean; + initialScrollPosition?: 'top' | 'bottom'; + loadMore?: (range: AbsoluteTimeRange) => void; + showTime: boolean; + sortOrder: LogsSortOrder; + timeRange: TimeRange; + timeZone: string; + wrapLogMessage: boolean; +} + +export const LogList = ({ + app, + containerElement, + eventBus, + forceEscape = false, + initialScrollPosition = 'top', + loadMore, + logs, + showTime, + sortOrder, + timeRange, + timeZone, + wrapLogMessage, +}: Props) => { + const [processedLogs, setProcessedLogs] = useState([]); + const [listHeight, setListHeight] = useState( + app === CoreApp.Explore ? window.innerHeight * 0.75 : containerElement.clientHeight + ); + const theme = useTheme2(); + const listRef = useRef(null); + const widthRef = useRef(containerElement.clientWidth); + const scrollRef = useRef(null); + + useEffect(() => { + initVirtualization(theme); + }, [theme]); + + useEffect(() => { + const subscription = eventBus.subscribe(ScrollToLogsEvent, (e: ScrollToLogsEvent) => + handleScrollToEvent(e, logs.length, listRef.current) + ); + return () => subscription.unsubscribe(); + }, [eventBus, logs.length]); + + useEffect(() => { + setProcessedLogs(preProcessLogs(logs, { wrap: wrapLogMessage, escape: forceEscape, order: sortOrder, timeZone })); + listRef.current?.resetAfterIndex(0); + }, [forceEscape, logs, sortOrder, timeZone, wrapLogMessage]); + + useEffect(() => { + const handleResize = debounce(() => { + setListHeight(app === CoreApp.Explore ? window.innerHeight * 0.75 : containerElement.clientHeight); + }, 50); + window.addEventListener('resize', handleResize); + handleResize(); + return () => { + window.removeEventListener('resize', handleResize); + }; + }, [app, containerElement.clientHeight]); + + useLayoutEffect(() => { + if (widthRef.current === containerElement.clientWidth) { + return; + } + resetLogLineSizes(); + listRef.current?.resetAfterIndex(0); + widthRef.current = containerElement.clientWidth; + }); + + const handleOverflow = useCallback( + (index: number, id: string, height: number) => { + if (containerElement) { + storeLogLineSize(id, containerElement, height); + listRef.current?.resetAfterIndex(index); + } + }, + [containerElement] + ); + + const handleScrollPosition = useCallback(() => { + listRef.current?.scrollToItem(initialScrollPosition === 'top' ? 0 : logs.length - 1); + }, [initialScrollPosition, logs.length]); + + if (!containerElement || listHeight == null) { + // Wait for container to be rendered + return null; + } + + return ( + + {({ getItemKey, itemCount, onItemsRendered, Renderer }) => ( + + {Renderer} + + )} + + ); +}; + +function handleScrollToEvent(event: ScrollToLogsEvent, logsCount: number, list: VariableSizeList | null) { + if (event.payload.scrollTo === 'top') { + list?.scrollTo(0); + } else { + list?.scrollToItem(logsCount - 1); + } +} diff --git a/public/app/features/logs/components/panel/processing.ts b/public/app/features/logs/components/panel/processing.ts new file mode 100644 index 00000000000..77f525614b5 --- /dev/null +++ b/public/app/features/logs/components/panel/processing.ts @@ -0,0 +1,63 @@ +import { dateTimeFormat, LogRowModel, LogsSortOrder } from '@grafana/data'; + +import { escapeUnescapedString, sortLogRows } from '../../utils'; + +import { measureTextWidth } from './virtualization'; + +export interface LogListModel extends LogRowModel { + body: string; + timestamp: string; + dimensions: LogDimensions; +} + +export interface LogDimensions { + timestampWidth: number; + levelWidth: number; +} + +interface PreProcessOptions { + escape: boolean; + order: LogsSortOrder; + timeZone: string; + wrap: boolean; +} + +export const preProcessLogs = ( + logs: LogRowModel[], + { escape, order, timeZone, wrap }: PreProcessOptions +): LogListModel[] => { + const orderedLogs = sortLogRows(logs, order); + return orderedLogs.map((log) => preProcessLog(log, { wrap, escape, timeZone, expanded: false })); +}; + +interface PreProcessLogOptions { + escape: boolean; + expanded: boolean; // Not yet implemented + timeZone: string; + wrap: boolean; +} +const preProcessLog = (log: LogRowModel, { escape, expanded, timeZone, wrap }: PreProcessLogOptions): LogListModel => { + let body = log.entry; + const timestamp = dateTimeFormat(log.timeEpochMs, { + timeZone, + defaultWithMS: true, + }); + + if (escape && log.hasUnescapedContent) { + body = escapeUnescapedString(body); + } + // With wrapping disabled, we want to turn it into a single-line log entry unless the line is expanded + if (!wrap && !expanded) { + body = body.replace(/(\r\n|\n|\r)/g, ''); + } + + return { + ...log, + body, + timestamp, + dimensions: { + timestampWidth: measureTextWidth(timestamp), + levelWidth: measureTextWidth(log.logLevel), + }, + }; +}; diff --git a/public/app/features/logs/components/panel/virtualization.ts b/public/app/features/logs/components/panel/virtualization.ts new file mode 100644 index 00000000000..201cc4c82ed --- /dev/null +++ b/public/app/features/logs/components/panel/virtualization.ts @@ -0,0 +1,230 @@ +import { BusEventWithPayload, GrafanaTheme2 } from '@grafana/data'; + +import { LogListModel } from './processing'; + +let ctx: CanvasRenderingContext2D | null = null; +let gridSize = 8; +let paddingBottom = gridSize * 0.75; +let lineHeight = 22; +let measurementMode: 'canvas' | 'dom' = 'canvas'; + +export function init(theme: GrafanaTheme2) { + const font = `${theme.typography.fontSize}px ${theme.typography.fontFamilyMonospace}`; + const letterSpacing = theme.typography.body.letterSpacing; + + initDOMmeasurement(font, letterSpacing); + initCanvasMeasurement(font, letterSpacing); + + gridSize = theme.spacing.gridSize; + paddingBottom = gridSize * 0.75; + lineHeight = theme.typography.fontSize * theme.typography.body.lineHeight; + + widthMap = new Map(); + resetLogLineSizes(); + + determineMeasurementMode(); + + return true; +} + +function determineMeasurementMode() { + if (!ctx) { + measurementMode = 'dom'; + return; + } + const canvasCharWidth = ctx.measureText('e').width; + const domCharWidth = measureTextWidthWithDOM('e'); + const diff = domCharWidth - canvasCharWidth; + if (diff >= 0.1) { + console.warn('Virtualized log list: falling back to DOM for measurement'); + measurementMode = 'dom'; + } +} + +function initCanvasMeasurement(font: string, letterSpacing: string | undefined) { + const canvas = document.createElement('canvas'); + ctx = canvas.getContext('2d'); + if (!ctx) { + return; + } + ctx.font = font; + ctx.fontKerning = 'normal'; + ctx.fontStretch = 'normal'; + ctx.fontVariantCaps = 'normal'; + ctx.textRendering = 'optimizeLegibility'; + if (letterSpacing) { + ctx.letterSpacing = letterSpacing; + } +} + +const span = document.createElement('span'); +function initDOMmeasurement(font: string, letterSpacing: string | undefined) { + span.style.font = font; + span.style.visibility = 'hidden'; + span.style.position = 'absolute'; + span.style.wordBreak = 'break-all'; + if (letterSpacing) { + span.style.letterSpacing = letterSpacing; + } +} + +let widthMap = new Map(); +export function measureTextWidth(text: string): number { + if (!ctx) { + throw new Error(`Measuring context canvas is not initialized. Call init() before.`); + } + const key = text.length; + + const storedWidth = widthMap.get(key); + if (storedWidth) { + return storedWidth; + } + + const width = measurementMode === 'canvas' ? ctx.measureText(text).width : measureTextWidthWithDOM(text); + widthMap.set(key, width); + + return width; +} + +function measureTextWidthWithDOM(text: string) { + span.textContent = text; + + document.body.appendChild(span); + const width = span.getBoundingClientRect().width; + document.body.removeChild(span); + + return width; +} + +export function measureTextHeight(text: string, maxWidth: number, beforeWidth = 0) { + let logLines = 0; + const charWidth = measureTextWidth('e'); + let logLineCharsLength = Math.round(maxWidth / charWidth); + const firstLineCharsLength = Math.floor((maxWidth - beforeWidth) / charWidth) - 2 * charWidth; + const textLines = text.split('\n'); + + // Skip unnecessary measurements + if (textLines.length === 1 && text.length < firstLineCharsLength) { + return { + lines: 1, + height: lineHeight + paddingBottom, + }; + } + + for (const textLine of textLines) { + for (let start = 0; start < textLine.length; ) { + let testLogLine: string; + let width = 0; + let delta = 0; + let availableWidth = maxWidth - beforeWidth; + do { + testLogLine = textLine.substring(start, start + logLineCharsLength - delta); + width = measureTextWidth(testLogLine); + delta += 1; + } while (width >= availableWidth); + if (beforeWidth) { + beforeWidth = 0; + } + logLines += 1; + start += testLogLine.length; + } + } + + const height = logLines * lineHeight + paddingBottom; + + return { + lines: logLines, + height, + }; +} + +interface DisplayOptions { + wrap: boolean; + showTime: boolean; +} + +export function getLogLineSize( + logs: LogListModel[], + container: HTMLDivElement | null, + { wrap, showTime }: DisplayOptions, + index: number +) { + if (!container) { + return 0; + } + // !logs[index] means the line is not yet loaded by infinite scrolling + if (!wrap || !logs[index]) { + return lineHeight + paddingBottom; + } + const storedSize = retrieveLogLineSize(logs[index].uid, container); + if (storedSize) { + return storedSize; + } + const gap = gridSize; + let optionsWidth = 0; + if (showTime) { + optionsWidth += logs[index].dimensions.timestampWidth + gap; + } + if (logs[index].logLevel) { + optionsWidth += logs[index].dimensions.levelWidth + gap; + } + const { height } = measureTextHeight(logs[index].body, getLogContainerWidth(container), optionsWidth); + return height; +} + +export function hasUnderOrOverflow(element: HTMLDivElement, calculatedHeight?: number): number | null { + const height = calculatedHeight ?? element.clientHeight; + if (element.scrollHeight > height) { + return element.scrollHeight; + } + const child = element.firstChild; + if (child instanceof HTMLDivElement && child.clientHeight < height) { + return child.clientHeight; + } + return null; +} + +const scrollBarWidth = getScrollbarWidth(); + +export function getLogContainerWidth(container: HTMLDivElement) { + return container.clientWidth - scrollBarWidth; +} + +export function getScrollbarWidth() { + const hiddenDiv = document.createElement('div'); + + hiddenDiv.style.width = '100px'; + hiddenDiv.style.height = '100px'; + hiddenDiv.style.overflow = 'scroll'; + hiddenDiv.style.position = 'absolute'; + hiddenDiv.style.top = '-9999px'; + + document.body.appendChild(hiddenDiv); + const width = hiddenDiv.offsetWidth - hiddenDiv.clientWidth; + document.body.removeChild(hiddenDiv); + + return width; +} + +let logLineSizesMap = new Map(); +export function resetLogLineSizes() { + logLineSizesMap = new Map(); +} + +export function storeLogLineSize(id: string, container: HTMLDivElement, height: number) { + const key = `${id}_${getLogContainerWidth(container)}`; + logLineSizesMap.set(key, height); +} + +export function retrieveLogLineSize(id: string, container: HTMLDivElement) { + const key = `${id}_${getLogContainerWidth(container)}`; + return logLineSizesMap.get(key); +} + +export interface ScrollToLogsEventPayload { + scrollTo: 'top' | 'bottom'; +} + +export class ScrollToLogsEvent extends BusEventWithPayload { + static type = 'logs-panel-scroll-to'; +} diff --git a/public/app/features/logs/logsModel.ts b/public/app/features/logs/logsModel.ts index a298b7f0bb9..60aef4364bd 100644 --- a/public/app/features/logs/logsModel.ts +++ b/public/app/features/logs/logsModel.ts @@ -92,13 +92,11 @@ export function dedupLogRows(rows: LogRowModel[], strategy?: LogsDedupStrategy): } return rows.reduce((result: LogRowModel[], row: LogRowModel, index) => { - const rowCopy = { ...row }; const previous = result[result.length - 1]; if (index > 0 && isDuplicateRow(row, previous, strategy)) { previous.duplicates!++; } else { - rowCopy.duplicates = 0; - result.push(rowCopy); + result.push({ ...row, duplicates: 0 }); } return result; }, []); diff --git a/public/app/features/manage-dashboards/components/ImportDashboardForm.tsx b/public/app/features/manage-dashboards/components/ImportDashboardForm.tsx index 35a241dad27..b4f8d041b82 100644 --- a/public/app/features/manage-dashboards/components/ImportDashboardForm.tsx +++ b/public/app/features/manage-dashboards/components/ImportDashboardForm.tsx @@ -4,7 +4,7 @@ import { Controller, FieldErrors, UseFormReturn } from 'react-hook-form'; import { selectors } from '@grafana/e2e-selectors'; import { ExpressionDatasourceRef } from '@grafana/runtime/src/utils/DataSourceWithBackend'; import { Button, Field, FormFieldErrors, FormsOnSubmit, Stack, Input, Legend } from '@grafana/ui'; -import { OldFolderPicker } from 'app/core/components/Select/OldFolderPicker'; +import { FolderPicker } from 'app/core/components/Select/FolderPicker'; import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; import { @@ -21,7 +21,6 @@ import { ImportDashboardLibraryPanelsList } from './ImportDashboardLibraryPanels interface Props extends Pick, 'register' | 'control' | 'getValues' | 'watch'> { uidReset: boolean; inputs: DashboardInputs; - initialFolderUid: string; errors: FieldErrors; onCancel: () => void; onUidReset: () => void; @@ -35,7 +34,6 @@ export const ImportDashboardForm = ({ getValues, uidReset, inputs, - initialFolderUid, onUidReset, onCancel, onSubmit, @@ -72,8 +70,8 @@ export const ImportDashboardForm = ({ ( - + render={({ field: { ref, value, onChange, ...field } }) => ( + onChange({ uid, title })} value={value.uid} /> )} name="folder" control={control} diff --git a/public/app/features/manage-dashboards/components/ImportDashboardOverview.tsx b/public/app/features/manage-dashboards/components/ImportDashboardOverview.tsx index 96caec35f96..f00e1c4ce55 100644 --- a/public/app/features/manage-dashboards/components/ImportDashboardOverview.tsx +++ b/public/app/features/manage-dashboards/components/ImportDashboardOverview.tsx @@ -112,7 +112,6 @@ class ImportDashboardOverviewUnConnected extends PureComponent { onUidReset={this.onUidReset} onSubmit={this.onSubmit} watch={watch} - initialFolderUid={folder.uid} /> )} diff --git a/public/app/features/manage-dashboards/components/SnapshotListTable.tsx b/public/app/features/manage-dashboards/components/SnapshotListTable.tsx index c8c36dd5e86..10554a440f0 100644 --- a/public/app/features/manage-dashboards/components/SnapshotListTable.tsx +++ b/public/app/features/manage-dashboards/components/SnapshotListTable.tsx @@ -2,7 +2,7 @@ import { useState, useCallback } from 'react'; import useAsync from 'react-use/lib/useAsync'; import { config } from '@grafana/runtime'; -import { ConfirmModal, EmptyState, TextLink } from '@grafana/ui'; +import { ConfirmModal, EmptyState, ScrollContainer, TextLink } from '@grafana/ui'; import { Trans, t } from 'app/core/internationalization'; import { getDashboardSnapshotSrv, Snapshot } from 'app/features/dashboard/services/SnapshotSrv'; @@ -62,7 +62,7 @@ export const SnapshotListTable = () => { } return ( -
+
@@ -112,6 +112,6 @@ export const SnapshotListTable = () => { setRemoveSnapshot(undefined); }} /> - + ); }; diff --git a/public/app/features/manage-dashboards/state/actions.ts b/public/app/features/manage-dashboards/state/actions.ts index 60fb84442bb..adab7a4746c 100644 --- a/public/app/features/manage-dashboards/state/actions.ts +++ b/public/app/features/manage-dashboards/state/actions.ts @@ -267,7 +267,7 @@ export function createFolder(payload: any) { export const SLICE_FOLDER_RESULTS_TO = 1000; -export function searchFolders( +export async function searchFolders( query: any, permission?: PermissionLevelString, type: SearchQueryType = SearchQueryType.Folder diff --git a/public/app/features/manage-dashboards/utils/validation.ts b/public/app/features/manage-dashboards/utils/validation.ts index b6d78b9b952..d70e22ed975 100644 --- a/public/app/features/manage-dashboards/utils/validation.ts +++ b/public/app/features/manage-dashboards/utils/validation.ts @@ -1,5 +1,4 @@ -import { t } from 'i18next'; - +import { t } from 'app/core/internationalization'; import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api'; import { validationSrv } from '../services/ValidationSrv'; @@ -54,6 +53,12 @@ export const validateUid = (value: string) => { }) .catch((error) => { error.isHandled = true; + + // when Editor user tries to import admin only dashboard (with same uid) he gets an unhelpful 403 error + // therefore handling this use case to return some indication of whats wrong + if (error.status === 403) { + return 'Dashboard with the same UID already exists'; + } return true; }); }; diff --git a/public/app/features/migrate-to-cloud/MigrateToCloud.tsx b/public/app/features/migrate-to-cloud/MigrateToCloud.tsx index 325574de336..7f967789c50 100644 --- a/public/app/features/migrate-to-cloud/MigrateToCloud.tsx +++ b/public/app/features/migrate-to-cloud/MigrateToCloud.tsx @@ -24,28 +24,24 @@ export default function MigrateToCloud() { } > - No SLAs are available yet.{' '} Visit our docs {' '} to learn more about this feature! + {config.cloudMigrationIsTarget && ( + <> +   + + Your self-managed instance of Grafana requires version 11.5+, or 11.2+ with the onPremToCloudMigrations + feature flag enabled. + + + )} - - - - Only Community and Commercial signed plugins are eligible for migration. Their latest version will be - installed in the cloud instance, please upgrade your plugins before starting the migration process. - - - {config.cloudMigrationIsTarget ? : } ); diff --git a/public/app/features/migrate-to-cloud/api/baseAPI.ts b/public/app/features/migrate-to-cloud/api/baseAPI.ts index 6a140906631..60c18dfd3e3 100644 --- a/public/app/features/migrate-to-cloud/api/baseAPI.ts +++ b/public/app/features/migrate-to-cloud/api/baseAPI.ts @@ -1,38 +1,9 @@ -import { BaseQueryFn, createApi } from '@reduxjs/toolkit/query/react'; -import { lastValueFrom } from 'rxjs'; +import { createApi } from '@reduxjs/toolkit/query/react'; -import { BackendSrvRequest, getBackendSrv } from '@grafana/runtime'; - -interface RequestOptions extends BackendSrvRequest { - manageError?: (err: unknown) => { error: unknown }; - showErrorAlert?: boolean; - - // rtk codegen sets this - body?: BackendSrvRequest['data']; -} - -function createBackendSrvBaseQuery({ baseURL }: { baseURL: string }): BaseQueryFn { - async function backendSrvBaseQuery(requestOptions: RequestOptions) { - try { - const { data: responseData, ...meta } = await lastValueFrom( - getBackendSrv().fetch({ - ...requestOptions, - url: baseURL + requestOptions.url, - showErrorAlert: false, - data: requestOptions.body, - }) - ); - return { data: responseData, meta }; - } catch (error) { - return requestOptions.manageError ? requestOptions.manageError(error) : { error }; - } - } - - return backendSrvBaseQuery; -} +import { createBaseQuery } from 'app/api/createBaseQuery'; export const baseAPI = createApi({ reducerPath: 'migrateToCloudGeneratedAPI', - baseQuery: createBackendSrvBaseQuery({ baseURL: '/api' }), + baseQuery: createBaseQuery({ baseURL: '/api' }), endpoints: () => ({}), }); diff --git a/public/app/features/migrate-to-cloud/api/endpoints.gen.ts b/public/app/features/migrate-to-cloud/api/endpoints.gen.ts index 06eecb33db9..98e962b61e0 100644 --- a/public/app/features/migrate-to-cloud/api/endpoints.gen.ts +++ b/public/app/features/migrate-to-cloud/api/endpoints.gen.ts @@ -194,6 +194,7 @@ export type MigrateDataResponseItemDto = { | 'FOLDER' | 'LIBRARY_ELEMENT' | 'ALERT_RULE' + | 'ALERT_RULE_GROUP' | 'CONTACT_POINT' | 'NOTIFICATION_POLICY' | 'NOTIFICATION_TEMPLATE' @@ -302,7 +303,7 @@ export type DashboardFullWithMeta = { dashboard?: Json; meta?: DashboardMeta; }; -export type LibraryElementDtoMetaUserDefinesModelForLibraryElementDtoMetaUser = { +export type LibraryElementDtoMetaUser = { avatarUrl?: string; id?: number; name?: string; @@ -310,11 +311,11 @@ export type LibraryElementDtoMetaUserDefinesModelForLibraryElementDtoMetaUser = export type LibraryElementDtoMetaIsTheMetaInformationForLibraryElementDto = { connectedDashboards?: number; created?: string; - createdBy?: LibraryElementDtoMetaUserDefinesModelForLibraryElementDtoMetaUser; + createdBy?: LibraryElementDtoMetaUser; folderName?: string; folderUid?: string; updated?: string; - updatedBy?: LibraryElementDtoMetaUserDefinesModelForLibraryElementDtoMetaUser; + updatedBy?: LibraryElementDtoMetaUser; }; export type LibraryElementDtoIsTheFrontendDtoForEntities = { description?: string; diff --git a/public/app/features/migrate-to-cloud/api/index.ts b/public/app/features/migrate-to-cloud/api/index.ts index 60a2d7a7b87..0e5c1d8b453 100644 --- a/public/app/features/migrate-to-cloud/api/index.ts +++ b/public/app/features/migrate-to-cloud/api/index.ts @@ -1,11 +1,14 @@ -export * from './endpoints.gen'; import { BaseQueryFn, EndpointDefinition } from '@reduxjs/toolkit/query'; import { getLocalPlugins } from 'app/features/plugins/admin/api'; import { LocalPlugin } from 'app/features/plugins/admin/types'; +import { handleRequestError } from '../../../api/createBaseQuery'; + import { generatedAPI } from './endpoints.gen'; +export * from './endpoints.gen'; + export const cloudMigrationAPI = generatedAPI .injectEndpoints({ endpoints: (build) => ({ @@ -16,7 +19,7 @@ export const cloudMigrationAPI = generatedAPI const list = await getLocalPlugins(); return { data: list }; } catch (error) { - return { error: error }; + return handleRequestError(error); } }, }), diff --git a/public/app/features/migrate-to-cloud/onprem/NameCell.tsx b/public/app/features/migrate-to-cloud/onprem/NameCell.tsx index c6b8f78f04b..3080a26fdd9 100644 --- a/public/app/features/migrate-to-cloud/onprem/NameCell.tsx +++ b/public/app/features/migrate-to-cloud/onprem/NameCell.tsx @@ -231,6 +231,8 @@ function ResourceIcon({ resource }: { resource: ResourceTableItem }) { return ; case 'ALERT_RULE': return ; + case 'ALERT_RULE_GROUP': + return ; case 'PLUGIN': if (pluginLogo) { return ; diff --git a/public/app/features/migrate-to-cloud/onprem/TypeCell.tsx b/public/app/features/migrate-to-cloud/onprem/TypeCell.tsx index 399945cdaba..00d576db9c5 100644 --- a/public/app/features/migrate-to-cloud/onprem/TypeCell.tsx +++ b/public/app/features/migrate-to-cloud/onprem/TypeCell.tsx @@ -23,6 +23,8 @@ export function prettyTypeName(type: ResourceTableItem['type']) { return t('migrate-to-cloud.resource-type.notification_policy', 'Notification Policy'); case 'ALERT_RULE': return t('migrate-to-cloud.resource-type.alert_rule', 'Alert Rule'); + case 'ALERT_RULE_GROUP': + return t('migrate-to-cloud.resource-type.alert_rule_group', 'Alert Rule Group'); case 'PLUGIN': return t('migrate-to-cloud.resource-type.plugin', 'Plugin'); default: diff --git a/public/app/features/migrate-to-cloud/onprem/useNotifyOnSuccess.tsx b/public/app/features/migrate-to-cloud/onprem/useNotifyOnSuccess.tsx index a5baf03f93e..e98a8221bb6 100644 --- a/public/app/features/migrate-to-cloud/onprem/useNotifyOnSuccess.tsx +++ b/public/app/features/migrate-to-cloud/onprem/useNotifyOnSuccess.tsx @@ -1,7 +1,7 @@ -import { t } from 'i18next'; import { useRef, useEffect } from 'react'; import { useAppNotification } from 'app/core/copy/appNotification'; +import { t } from 'app/core/internationalization'; import { GetSnapshotResponseDto, SnapshotDto } from '../api'; @@ -62,6 +62,8 @@ function getTranslatedMessage(snapshot: GetSnapshotResponseDto) { types.push(t('migrate-to-cloud.migrated-counts.notification_policies', 'notification policies')); } else if (type === 'ALERT_RULE') { types.push(t('migrate-to-cloud.migrated-counts.alert_rules', 'alert rules')); + } else if (type === 'ALERT_RULE_GROUP') { + types.push(t('migrate-to-cloud.migrated-counts.alert_rule_groups', 'alert rule groups')); } else if (type === 'PLUGIN') { types.push(t('migrate-to-cloud.migrated-counts.plugins', 'plugins')); } diff --git a/public/app/features/plugins/admin/api.ts b/public/app/features/plugins/admin/api.ts index 0479721f708..f79968d47ae 100644 --- a/public/app/features/plugins/admin/api.ts +++ b/public/app/features/plugins/admin/api.ts @@ -37,6 +37,10 @@ export async function getPluginDetails(id: string): Promise ({ version: v.version, createdAt: v.createdAt, + updatedAt: v.updatedAt, isCompatible: v.isCompatible, grafanaDependency: v.grafanaDependency, angularDetected: v.angularDetected, diff --git a/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.test.tsx b/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.test.tsx index 941fbb1eee7..a1b80773d8a 100644 --- a/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.test.tsx +++ b/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.test.tsx @@ -37,10 +37,8 @@ const plugin: CatalogPlugin = { }; describe('GetStartedWithDataSource', () => { - const oldFeatureTogglesManagedPluginsInstall = config.featureToggles.managedPluginsInstall; const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled; - config.featureToggles.managedPluginsInstall = true; config.pluginAdminExternalManageEnabled = true; const contextSrv = new ContextSrv(); @@ -51,11 +49,10 @@ describe('GetStartedWithDataSource', () => { setContextSrv(contextSrv); afterAll(() => { - config.featureToggles.managedPluginsInstall = oldFeatureTogglesManagedPluginsInstall; config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled; }); - it('should disable button when managedPluginsInstall and pluginAdminExternalManaged are enabled, but plugin.isFullyInstalled is false', () => { + it('should disable button when pluginAdminExternalManaged is enabled, but plugin.isFullyInstalled is false', () => { render( @@ -67,7 +64,7 @@ describe('GetStartedWithDataSource', () => { expect(el).toBeDisabled(); }); - it('should disable button when managedPluginsInstall and pluginAdminExternalManaged are enabled, but plugin.isFullyInstalled is true', () => { + it('should disable button when pluginAdminExternalManaged enabled, but plugin.isFullyInstalled is true', () => { render( diff --git a/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx b/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx index 22ba118489b..640560d62f5 100644 --- a/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx +++ b/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx @@ -30,8 +30,7 @@ export function GetStartedWithDataSource({ plugin }: Props): React.ReactElement return null; } - const disabledButton = - config.featureToggles.managedPluginsInstall && config.pluginAdminExternalManageEnabled && !plugin.isFullyInstalled; + const disabledButton = config.pluginAdminExternalManageEnabled && !plugin.isFullyInstalled; return ( + + + + )} + + {reportAbuseModalOpen && ( + Report a plugin concern} + isOpen + onDismiss={() => setReportAbuseModalOpen(false)} + > + + + + This feature is for reporting malicious or harmful behaviour within plugins. For plugin concerns, email + us at:{' '} + + integrations@grafana.com + + + + Note: For general plugin issues like bugs or feature requests, please contact the plugin author using + the provided links.{' '} + - - Contact Grafana Labs - - + + + + + )} - + ); } diff --git a/public/app/features/plugins/admin/components/UpdateAllButton.test.tsx b/public/app/features/plugins/admin/components/UpdateAllButton.test.tsx new file mode 100644 index 00000000000..8a0cb1e4945 --- /dev/null +++ b/public/app/features/plugins/admin/components/UpdateAllButton.test.tsx @@ -0,0 +1,29 @@ +import { render, screen, fireEvent } from '@testing-library/react'; + +import UpdateAllButton from './UpdateAllButton'; + +describe('UpdateAllButton', () => { + const onUpdateAllMock = jest.fn(); + + beforeEach(() => { + onUpdateAllMock.mockClear(); + }); + + it('should display "No updates available" when disabled', () => { + render(); + + expect(screen.getByText('No updates available')).toBeInTheDocument(); + expect(screen.getByRole('button')).toBeDisabled(); + }); + + it('should display update count and be clickable when enabled', () => { + render(); + + const button = screen.getByRole('button'); + expect(button).toHaveTextContent('Update all (3)'); + expect(button).toBeEnabled(); + + fireEvent.click(button); + expect(onUpdateAllMock).toHaveBeenCalledTimes(1); + }); +}); diff --git a/public/app/features/plugins/admin/components/UpdateAllButton.tsx b/public/app/features/plugins/admin/components/UpdateAllButton.tsx new file mode 100644 index 00000000000..7ba9c7ad3aa --- /dev/null +++ b/public/app/features/plugins/admin/components/UpdateAllButton.tsx @@ -0,0 +1,24 @@ +import { Button } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; + +interface UpdateAllButtonProps { + disabled: boolean; + onUpdateAll: () => void; + updatablePluginsLength: number; +} + +const UpdateAllButton = ({ disabled, onUpdateAll, updatablePluginsLength }: UpdateAllButtonProps) => { + return ( + + ); +}; + +export default UpdateAllButton; diff --git a/public/app/features/plugins/admin/components/UpdateAllModal.tsx b/public/app/features/plugins/admin/components/UpdateAllModal.tsx index 5006d8096b8..f12113499b9 100644 --- a/public/app/features/plugins/admin/components/UpdateAllModal.tsx +++ b/public/app/features/plugins/admin/components/UpdateAllModal.tsx @@ -97,7 +97,7 @@ export const UpdateAllModal = ({ isOpen, onDismiss, isLoading, plugins }: Props) setInProgress(true); // in cloud the requests need to be sync - if (config.pluginAdminExternalManageEnabled && config.featureToggles.managedPluginsInstall) { + if (config.pluginAdminExternalManageEnabled) { for (let plugin of plugins) { if (selectedPlugins?.has(plugin.id)) { await install(plugin.id, plugin.latestVersion, true); diff --git a/public/app/features/plugins/admin/components/UpdateAllModalBody.tsx b/public/app/features/plugins/admin/components/UpdateAllModalBody.tsx index 20214900d3a..8d793ae10ad 100644 --- a/public/app/features/plugins/admin/components/UpdateAllModalBody.tsx +++ b/public/app/features/plugins/admin/components/UpdateAllModalBody.tsx @@ -194,7 +194,7 @@ export const UpdateModalBody = ({ {`${errorMap.size} ${t('plugins.catalog.update-all.error-status-text', 'failed - see error messages')}`} )} - {config.pluginAdminExternalManageEnabled && config.featureToggles.managedPluginsInstall && ( + {config.pluginAdminExternalManageEnabled && (
* It may take a few minutes for the plugins to be available for usage. diff --git a/public/app/features/plugins/admin/components/VersionList.tsx b/public/app/features/plugins/admin/components/VersionList.tsx index aaf98fd183b..2e79b0d43c1 100644 --- a/public/app/features/plugins/admin/components/VersionList.tsx +++ b/public/app/features/plugins/admin/components/VersionList.tsx @@ -96,7 +96,7 @@ export const VersionList = ({ pluginId, versions = [], installedVersion, disable {/* Last updated */}
{/* Dependency */} diff --git a/public/app/features/plugins/admin/helpers.test.ts b/public/app/features/plugins/admin/helpers.test.ts index b47c5bf774b..4976659623a 100644 --- a/public/app/features/plugins/admin/helpers.test.ts +++ b/public/app/features/plugins/admin/helpers.test.ts @@ -92,10 +92,8 @@ describe('Plugins/Helpers', () => { test('core plugins should be fullyInstalled in cloud', () => { const corePluginId = 'plugin-core'; - const oldFeatureTogglesManagedPluginsInstall = config.featureToggles.managedPluginsInstall; const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled; - config.featureToggles.managedPluginsInstall = true; config.pluginAdminExternalManageEnabled = true; const merged = mergeLocalsAndRemotes({ @@ -109,17 +107,14 @@ describe('Plugins/Helpers', () => { expect(findMerged(corePluginId)?.isCore).toBe(true); expect(findMerged(corePluginId)?.isFullyInstalled).toBe(true); - config.featureToggles.managedPluginsInstall = oldFeatureTogglesManagedPluginsInstall; config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled; }); test('plugins should be fully installed if they are installed and it is provisioned', () => { const pluginId = 'plugin-1'; - const oldFeatureTogglesManagedPluginsInstall = config.featureToggles.managedPluginsInstall; const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled; - config.featureToggles.managedPluginsInstall = true; config.pluginAdminExternalManageEnabled = true; const merged = mergeLocalsAndRemotes({ @@ -133,15 +128,12 @@ describe('Plugins/Helpers', () => { expect(findMerged(pluginId)).not.toBeUndefined(); expect(findMerged(pluginId)?.isFullyInstalled).toBe(true); - config.featureToggles.managedPluginsInstall = oldFeatureTogglesManagedPluginsInstall; config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled; }); test('plugins should have update when instance version is different from remote version', () => { - const oldFeatureTogglesManagedPluginsInstall = config.featureToggles.managedPluginsInstall; const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled; - config.featureToggles.managedPluginsInstall = true; config.pluginAdminExternalManageEnabled = true; const pluginId = 'plugin-1'; @@ -162,7 +154,6 @@ describe('Plugins/Helpers', () => { expect(findMerged(pluginId)).not.toBeUndefined(); expect(findMerged(pluginId)?.hasUpdate).toBe(true); - config.featureToggles.managedPluginsInstall = oldFeatureTogglesManagedPluginsInstall; config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled; }); }); @@ -217,6 +208,7 @@ describe('Plugins/Helpers', () => { updatedAt: '2021-05-18T14:53:01.000Z', isFullyInstalled: false, angularDetected: false, + url: 'https://github.com/alexanderzobnin/grafana-zabbix', }); }); @@ -354,6 +346,7 @@ describe('Plugins/Helpers', () => { installedVersion: '4.2.2', isFullyInstalled: true, angularDetected: false, + url: 'https://github.com/alexanderzobnin/grafana-zabbix', }); }); diff --git a/public/app/features/plugins/admin/helpers.ts b/public/app/features/plugins/admin/helpers.ts index 172a32e088f..28386a73cbd 100644 --- a/public/app/features/plugins/admin/helpers.ts +++ b/public/app/features/plugins/admin/helpers.ts @@ -2,7 +2,7 @@ import uFuzzy from '@leeoniya/ufuzzy'; import { PluginSignatureStatus, dateTimeParse, PluginError, PluginType, PluginErrorCode } from '@grafana/data'; import { config, featureEnabled } from '@grafana/runtime'; -import configCore, { Settings } from 'app/core/config'; +import { Settings } from 'app/core/config'; import { contextSrv } from 'app/core/core'; import { getBackendSrv } from 'app/core/services/backend_srv'; import { AccessControlAction } from 'app/types'; @@ -63,7 +63,7 @@ export function mergeLocalsAndRemotes({ const catalogPlugin = mergeLocalAndRemote(localCounterpart, remotePlugin, error); // for managed instances, check if plugin is installed, but not yet present in the current instance - if (configCore.featureToggles.managedPluginsInstall && config.pluginAdminExternalManageEnabled) { + if (config.pluginAdminExternalManageEnabled) { catalogPlugin.isFullyInstalled = catalogPlugin.isCore ? true : (instancesMap.has(remotePlugin.slug) || provisionedSet.has(remotePlugin.slug)) && catalogPlugin.isInstalled; @@ -121,6 +121,8 @@ export function mapRemoteToCatalog(plugin: RemotePlugin, error?: PluginError): C signatureType, versionSignatureType, versionSignedByOrgName, + url, + raiseAnIssueUrl, } = plugin; const isDisabled = !!error || isDisabledSecretsPlugin(typeCode); @@ -158,6 +160,8 @@ export function mapRemoteToCatalog(plugin: RemotePlugin, error?: PluginError): C angularDetected, isFullyInstalled: isDisabled, latestVersion: plugin.version, + url, + raiseAnIssueUrl, }; } @@ -174,6 +178,7 @@ export function mapLocalToCatalog(plugin: LocalPlugin, error?: PluginError): Cat hasUpdate, accessControl, angularDetected, + raiseAnIssueUrl, } = plugin; const isDisabled = !!error || isDisabledSecretsPlugin(type); @@ -208,6 +213,7 @@ export function mapLocalToCatalog(plugin: LocalPlugin, error?: PluginError): Cat isFullyInstalled: true, iam: plugin.iam, latestVersion: plugin.latestVersion, + raiseAnIssueUrl, }; } @@ -271,6 +277,8 @@ export function mapToCatalogPlugin(local?: LocalPlugin, remote?: RemotePlugin, e isFullyInstalled: Boolean(local) || isDisabled, iam: local?.iam, latestVersion: local?.latestVersion || remote?.version || '', + url: remote?.url || '', + raiseAnIssueUrl: remote?.raiseAnIssueUrl || local?.raiseAnIssueUrl, }; } diff --git a/public/app/features/plugins/admin/hooks/usePluginConfig.tsx b/public/app/features/plugins/admin/hooks/usePluginConfig.tsx index 79feffc09e9..62cb89aaf33 100644 --- a/public/app/features/plugins/admin/hooks/usePluginConfig.tsx +++ b/public/app/features/plugins/admin/hooks/usePluginConfig.tsx @@ -11,10 +11,8 @@ export const usePluginConfig = (plugin?: CatalogPlugin) => { return null; } - const isPluginInstalled = - config.pluginAdminExternalManageEnabled && config.featureToggles.managedPluginsInstall - ? plugin.isFullyInstalled - : plugin.isInstalled; + const isPluginInstalled = config.pluginAdminExternalManageEnabled ? plugin.isFullyInstalled : plugin.isInstalled; + if (isPluginInstalled && !plugin.isDisabled) { return loadPlugin(plugin.id); } diff --git a/public/app/features/plugins/admin/hooks/usePluginDetailsTabs.tsx b/public/app/features/plugins/admin/hooks/usePluginDetailsTabs.tsx index e9c50269d15..60074bb053a 100644 --- a/public/app/features/plugins/admin/hooks/usePluginDetailsTabs.tsx +++ b/public/app/features/plugins/admin/hooks/usePluginDetailsTabs.tsx @@ -42,7 +42,8 @@ export const usePluginDetailsTabs = ( const navModelChildren = useMemo(() => { const canConfigurePlugins = plugin && contextSrv.hasPermissionInMetadata(AccessControlAction.PluginsWrite, plugin); const navModelChildren: NavModelItem[] = []; - if (isPublished) { + // currently the versions available of core plugins are not consistent + if (isPublished && !plugin?.isCore) { navModelChildren.push({ text: PluginTabLabels.VERSIONS, id: PluginTabIds.VERSIONS, @@ -51,7 +52,8 @@ export const usePluginDetailsTabs = ( active: PluginTabIds.VERSIONS === currentPageId, }); } - if (isPublished && plugin?.details?.changelog) { + // currently there is not changelog available for core plugins + if (isPublished && plugin?.details?.changelog && !plugin.isCore) { navModelChildren.push({ text: PluginTabLabels.CHANGELOG, id: PluginTabIds.CHANGELOG, @@ -153,6 +155,7 @@ export const usePluginDetailsTabs = ( const navModel: NavModelItem = { text: plugin?.name ?? '', img: plugin?.info.logos.small, + url: pathname, children: [ { text: PluginTabLabels.OVERVIEW, diff --git a/public/app/features/plugins/admin/hooks/usePluginInfo.tsx b/public/app/features/plugins/admin/hooks/usePluginInfo.tsx index 1bb1334e1ef..2c9124f1860 100644 --- a/public/app/features/plugins/admin/hooks/usePluginInfo.tsx +++ b/public/app/features/plugins/admin/hooks/usePluginInfo.tsx @@ -53,7 +53,10 @@ export const usePluginInfo = (plugin?: CatalogPlugin): PageInfoItem[] => { latestVersionValue = latestVersion; } - addInfo('latestVersion', latestVersionValue); + // latest versions of core plugins are not consistent + if (!plugin.isCore) { + addInfo('latestVersion', latestVersionValue); + } } if (Boolean(plugin.orgName)) { diff --git a/public/app/features/plugins/admin/pages/Browse.tsx b/public/app/features/plugins/admin/pages/Browse.tsx index 58857662c54..70615d4f743 100644 --- a/public/app/features/plugins/admin/pages/Browse.tsx +++ b/public/app/features/plugins/admin/pages/Browse.tsx @@ -4,9 +4,8 @@ import { useLocation } from 'react-router-dom-v5-compat'; import { SelectableValue, GrafanaTheme2, PluginType } from '@grafana/data'; import { locationSearchToObject } from '@grafana/runtime'; -import { Select, RadioButtonGroup, useStyles2, Tooltip, Field, Button } from '@grafana/ui'; +import { Select, RadioButtonGroup, useStyles2, Tooltip, Field } from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; -import { Trans } from 'app/core/internationalization'; import { getNavModel } from 'app/core/selectors/navModel'; import { ROUTES as CONNECTIONS_ROUTES } from 'app/features/connections/constants'; import { useSelector } from 'app/types'; @@ -15,6 +14,7 @@ import { HorizontalGroup } from '../components/HorizontalGroup'; import { PluginList } from '../components/PluginList'; import { RoadmapLinks } from '../components/RoadmapLinks'; import { SearchField } from '../components/SearchField'; +import UpdateAllButton from '../components/UpdateAllButton'; import { UpdateAllModal } from '../components/UpdateAllModal'; import { Sorters } from '../helpers'; import { useHistory } from '../hooks/useHistory'; @@ -86,15 +86,17 @@ export default function Browse() { . ); - const updateAll = ( - + + const updateAllButton = ( + ); return ( - + diff --git a/public/app/features/plugins/admin/pages/PluginDetails.test.tsx b/public/app/features/plugins/admin/pages/PluginDetails.test.tsx index 0ff39437740..fe4f257fd6e 100644 --- a/public/app/features/plugins/admin/pages/PluginDetails.test.tsx +++ b/public/app/features/plugins/admin/pages/PluginDetails.test.tsx @@ -391,31 +391,6 @@ describe('Plugin details page', () => { expect(await queryByRole('button', { name: /(un)?install/i })).not.toBeInTheDocument(); }); - it('should display install link with `config.pluginAdminExternalManageEnabled` set to true', async () => { - config.pluginAdminExternalManageEnabled = true; - - const { queryByRole } = renderPluginDetails({ id, isInstalled: false }); - - expect(await queryByRole('link', { name: /install via grafana.com/i })).toBeInTheDocument(); - }); - - it('should display uninstall link for an installed plugin with `config.pluginAdminExternalManageEnabled` set to true', async () => { - config.pluginAdminExternalManageEnabled = true; - - const { queryByRole } = renderPluginDetails({ id, isInstalled: true }); - - expect(await queryByRole('link', { name: /uninstall via grafana.com/i })).toBeInTheDocument(); - }); - - it('should display update and uninstall links for a plugin with an available update and `config.pluginAdminExternalManageEnabled` set to true', async () => { - config.pluginAdminExternalManageEnabled = true; - - const { queryByRole } = renderPluginDetails({ id, isInstalled: true, hasUpdate: true }); - - expect(await queryByRole('link', { name: /update via grafana.com/i })).toBeInTheDocument(); - expect(queryByRole('link', { name: /uninstall via grafana.com/i })).toBeInTheDocument(); - }); - it('should display alert with information about why the plugin is disabled', async () => { const { queryByTestId } = renderPluginDetails({ id, diff --git a/public/app/features/plugins/admin/state/actions.ts b/public/app/features/plugins/admin/state/actions.ts index b5e765d1864..eab93de18fa 100644 --- a/public/app/features/plugins/admin/state/actions.ts +++ b/public/app/features/plugins/admin/state/actions.ts @@ -3,7 +3,6 @@ import { from, forkJoin, timeout, lastValueFrom, catchError, of } from 'rxjs'; import { PanelPlugin, PluginError } from '@grafana/data'; import { config, getBackendSrv, isFetchError } from '@grafana/runtime'; -import configCore from 'app/core/config'; import { importPanelPlugin } from 'app/features/plugins/importPanelPlugin'; import { StoreState, ThunkResult } from 'app/types'; @@ -28,14 +27,8 @@ export const fetchAll = createAsyncThunk(`${STATE_PREFIX}/fetchAll`, async (_, t thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchLocal/pending` }); thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchRemote/pending` }); - const instance$ = - config.pluginAdminExternalManageEnabled && configCore.featureToggles.managedPluginsInstall - ? from(getInstancePlugins()) - : of(undefined); - const provisioned$ = - config.pluginAdminExternalManageEnabled && configCore.featureToggles.managedPluginsInstall - ? from(getProvisionedPlugins()) - : of(undefined); + const instance$ = config.pluginAdminExternalManageEnabled ? from(getInstancePlugins()) : of(undefined); + const provisioned$ = config.pluginAdminExternalManageEnabled ? from(getProvisionedPlugins()) : of(undefined); const TIMEOUT = 500; const pluginErrors$ = from(getPluginErrors()); const local$ = from(getLocalPlugins()); diff --git a/public/app/features/plugins/admin/types.ts b/public/app/features/plugins/admin/types.ts index 082d3cdf941..f3d5783ae3c 100644 --- a/public/app/features/plugins/admin/types.ts +++ b/public/app/features/plugins/admin/types.ts @@ -64,6 +64,8 @@ export interface CatalogPlugin extends WithAccessControlMetadata { isUpdatingFromInstance?: boolean; iam?: IdentityAccessManagement; isProvisioned?: boolean; + url?: string; + raiseAnIssueUrl?: string; } export interface CatalogPluginDetails { @@ -79,6 +81,10 @@ export interface CatalogPluginDetails { iam?: IdentityAccessManagement; changelog?: string; lastCommitDate?: string; + licenseUrl?: string; + documentationUrl?: string; + signatureType?: PluginSignatureType; + signature?: PluginSignatureStatus; } export interface CatalogPluginInfo { @@ -141,6 +147,9 @@ export type RemotePlugin = { versionStatus: string; angularDetected?: boolean; lastCommitDate?: string; + licenseUrl?: string; + documentationUrl?: string; + raiseAnIssueUrl?: string; }; // The available status codes on GCOM are available here: @@ -188,6 +197,7 @@ export type LocalPlugin = WithAccessControlMetadata & { dependencies: PluginDependencies; angularDetected: boolean; iam?: IdentityAccessManagement; + raiseAnIssueUrl?: string; }; interface IdentityAccessManagement { @@ -214,6 +224,7 @@ export interface Build { export interface Version { version: string; createdAt: string; + updatedAt?: string; isCompatible: boolean; grafanaDependency: string | null; angularDetected?: boolean; diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index 1036baa2e89..9f8ac9cda32 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -45,6 +45,8 @@ const histogramPanel = async () => await import(/* webpackChunkName: "histogramPanel" */ 'app/plugins/panel/histogram/module'); const livePanel = async () => await import(/* webpackChunkName: "livePanel" */ 'app/plugins/panel/live/module'); const logsPanel = async () => await import(/* webpackChunkName: "logsPanel" */ 'app/plugins/panel/logs/module'); +const newLogsPanel = async () => + await import(/* webpackChunkName: "newLogsPanel" */ 'app/plugins/panel/logs-new/module'); const newsPanel = async () => await import(/* webpackChunkName: "newsPanel" */ 'app/plugins/panel/news/module'); const pieChartPanel = async () => await import(/* webpackChunkName: "pieChartPanel" */ 'app/plugins/panel/piechart/module'); @@ -116,6 +118,7 @@ const builtInPlugins: Record Promise ({ jest.mock('@grafana/runtime', () => ({ ...jest.requireActual('@grafana/runtime'), config: { - featureToggles: { - accessControlOnCall: true, - }, + featureToggles: {}, + apps: {}, theme2: { breakpoints: { values: { @@ -92,6 +92,7 @@ function renderUnderRouter(page = '') { addedComponentsRegistry: new AddedComponentsRegistry(), exposedComponentsRegistry: new ExposedComponentsRegistry(), addedLinksRegistry: new AddedLinksRegistry(), + addedFunctionsRegistry: new AddedFunctionsRegistry(), }; const pagePath = page ? `/${page}` : ''; const route = { diff --git a/public/app/features/plugins/components/AppRootPage.tsx b/public/app/features/plugins/components/AppRootPage.tsx index f0d6bd5329e..22e324e7478 100644 --- a/public/app/features/plugins/components/AppRootPage.tsx +++ b/public/app/features/plugins/components/AppRootPage.tsx @@ -29,6 +29,7 @@ import { useAddedLinksRegistry, useAddedComponentsRegistry, useExposedComponentsRegistry, + useAddedFunctionsRegistry, } from '../extensions/ExtensionRegistriesContext'; import { getPluginSettings } from '../pluginSettings'; import { importAppPlugin } from '../plugin_loader'; @@ -60,6 +61,7 @@ export function AppRootPage({ pluginId, pluginNavSection }: Props) { const addedLinksRegistry = useAddedLinksRegistry(); const addedComponentsRegistry = useAddedComponentsRegistry(); const exposedComponentsRegistry = useExposedComponentsRegistry(); + const addedFunctionsRegistry = useAddedFunctionsRegistry(); const location = useLocation(); const [state, dispatch] = useReducer(stateSlice.reducer, initialState); const currentUrl = config.appSubUrl + location.pathname + location.search; @@ -104,6 +106,7 @@ export function AppRootPage({ pluginId, pluginNavSection }: Props) { addedLinksRegistry: addedLinksRegistry.readOnly(), addedComponentsRegistry: addedComponentsRegistry.readOnly(), exposedComponentsRegistry: exposedComponentsRegistry.readOnly(), + addedFunctionsRegistry: addedFunctionsRegistry.readOnly(), }} > (undefined); export const AddedComponentsRegistryContext = createContext(undefined); +export const AddedFunctionsRegistryContext = createContext(undefined); export const ExposedComponentsRegistryContext = createContext(undefined); export function useAddedLinksRegistry(): AddedLinksRegistry { @@ -31,6 +33,14 @@ export function useAddedComponentsRegistry(): AddedComponentsRegistry { return context; } +export function useAddedFunctionsRegistry(): AddedFunctionsRegistry { + const context = useContext(AddedFunctionsRegistryContext); + if (!context) { + throw new Error('No `AddedFunctionsRegistry` found.'); + } + return context; +} + export function useExposedComponentsRegistry(): ExposedComponentsRegistry { const context = useContext(ExposedComponentsRegistryContext); if (!context) { @@ -46,9 +56,11 @@ export const ExtensionRegistriesProvider = ({ return ( - - {children} - + + + {children} + + ); diff --git a/public/app/features/plugins/extensions/errors.ts b/public/app/features/plugins/extensions/errors.ts index 39c65df0f8e..cb9d22e7b46 100644 --- a/public/app/features/plugins/extensions/errors.ts +++ b/public/app/features/plugins/extensions/errors.ts @@ -8,6 +8,8 @@ export const TITLE_MISSING = 'Title is missing.'; export const DESCRIPTION_MISSING = 'Description is missing.'; +export const INVALID_EXTENSION_FUNCTION = 'The "fn" argument is invalid, it should be a function.'; + export const INVALID_CONFIGURE_FUNCTION = 'The "configure" function is invalid. It should be a function.'; export const INVALID_PATH_OR_ON_CLICK = 'Either "path" or "onClick" is required.'; @@ -33,6 +35,9 @@ export const TITLE_NOT_MATCHING_META_INFO = 'The "title" doesn\'t match the titl export const ADDED_LINK_META_INFO_MISSING = 'The extension was not recorded in the plugin.json. Added link extensions must be listed in the section "extensions.addedLinks[]". Currently, this is only required in development but will be enforced also in production builds in the future.'; +export const ADDED_FUNCTION_META_INFO_MISSING = + 'The extension was not recorded in the plugin.json. Added function extensions must be listed in the section "extensions.addedFunction[]". Currently, this is only required in development but will be enforced also in production builds in the future.'; + export const DESCRIPTION_NOT_MATCHING_META_INFO = 'The "description" doesn\'t match the description recorded in plugin.json.'; diff --git a/public/app/features/plugins/extensions/getPluginExtensions.ts b/public/app/features/plugins/extensions/getPluginExtensions.ts index a47292c8497..30f5865b259 100644 --- a/public/app/features/plugins/extensions/getPluginExtensions.ts +++ b/public/app/features/plugins/extensions/getPluginExtensions.ts @@ -1,8 +1,8 @@ import { isString } from 'lodash'; import { - type PluginExtension, PluginExtensionTypes, + type PluginExtension, type PluginExtensionLink, type PluginExtensionComponent, } from '@grafana/data'; diff --git a/public/app/features/plugins/extensions/registry/AddedComponentsRegistry.test.ts b/public/app/features/plugins/extensions/registry/AddedComponentsRegistry.test.ts index e82c48dcee0..5a18e222431 100644 --- a/public/app/features/plugins/extensions/registry/AddedComponentsRegistry.test.ts +++ b/public/app/features/plugins/extensions/registry/AddedComponentsRegistry.test.ts @@ -52,6 +52,7 @@ describe('AddedComponentsRegistry', () => { extensions: { addedLinks: [], addedComponents: [], + addedFunctions: [], exposedComponents: [], extensionPoints: [], }, diff --git a/public/app/features/plugins/extensions/registry/AddedFunctionsRegistry.test.ts b/public/app/features/plugins/extensions/registry/AddedFunctionsRegistry.test.ts new file mode 100644 index 00000000000..ae0addc52c7 --- /dev/null +++ b/public/app/features/plugins/extensions/registry/AddedFunctionsRegistry.test.ts @@ -0,0 +1,677 @@ +import { firstValueFrom } from 'rxjs'; + +import { PluginLoadingStrategy } from '@grafana/data'; +import { config } from '@grafana/runtime'; + +import { log } from '../logs/log'; +import { resetLogMock } from '../logs/testUtils'; +import { isGrafanaDevMode } from '../utils'; + +import { AddedFunctionsRegistry } from './AddedFunctionsRegistry'; +import { MSG_CANNOT_REGISTER_READ_ONLY } from './Registry'; + +jest.mock('../utils', () => ({ + ...jest.requireActual('../utils'), + + // Manually set the dev mode to false + // (to make sure that by default we are testing a production scneario) + isGrafanaDevMode: jest.fn().mockReturnValue(false), +})); + +jest.mock('../logs/log', () => { + const { createLogMock } = jest.requireActual('../logs/testUtils'); + const original = jest.requireActual('../logs/log'); + + return { + ...original, + log: createLogMock(), + }; +}); + +describe('addedFunctionsRegistry', () => { + const originalApps = config.apps; + const pluginId = 'grafana-basic-app'; + const appPluginConfig = { + id: pluginId, + path: '', + version: '', + preload: false, + angular: { + detected: false, + hideDeprecation: false, + }, + loadingStrategy: PluginLoadingStrategy.fetch, + dependencies: { + grafanaVersion: '8.0.0', + plugins: [], + extensions: { + exposedComponents: [], + }, + }, + extensions: { + addedFunctions: [], + addedLinks: [], + addedComponents: [], + exposedComponents: [], + extensionPoints: [], + }, + }; + + beforeEach(() => { + resetLogMock(log); + jest.mocked(isGrafanaDevMode).mockReturnValue(false); + config.apps = { + [pluginId]: appPluginConfig, + }; + }); + + afterEach(() => { + config.apps = originalApps; + }); + + it('should return empty registry when no extensions registered', async () => { + const addedFunctionsRegistry = new AddedFunctionsRegistry(); + const observable = addedFunctionsRegistry.asObservable(); + const registry = await firstValueFrom(observable); + expect(registry).toEqual({}); + }); + + it('should be possible to register function extensions in the registry', async () => { + const addedFunctionsRegistry = new AddedFunctionsRegistry(); + + addedFunctionsRegistry.register({ + pluginId, + configs: [ + { + title: 'Function 1', + description: 'Function 1 description', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn(), + }, + { + title: 'Function 2', + description: 'Function 2 description', + targets: 'plugins/myorg-basic-app/start', + fn: jest.fn(), + }, + ], + }); + + const registry = await addedFunctionsRegistry.getState(); + + expect(registry).toEqual({ + 'grafana/dashboard/panel/menu': [ + { + pluginId: pluginId, + title: 'Function 1', + description: 'Function 1 description', + extensionPointId: 'grafana/dashboard/panel/menu', + fn: expect.any(Function), + }, + ], + 'plugins/myorg-basic-app/start': [ + { + pluginId: pluginId, + title: 'Function 2', + description: 'Function 2 description', + extensionPointId: 'plugins/myorg-basic-app/start', + fn: expect.any(Function), + }, + ], + }); + }); + it('should be possible to asynchronously register function extensions for the same placement (different plugins)', async () => { + const pluginId1 = 'grafana-basic-app'; + const pluginId2 = 'grafana-basic-app2'; + const reactiveRegistry = new AddedFunctionsRegistry(); + + // Register extensions for the first plugin + reactiveRegistry.register({ + pluginId: pluginId1, + configs: [ + { + title: 'Function 1', + description: 'Function 1 description', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + + const registry1 = await reactiveRegistry.getState(); + + expect(registry1).toEqual({ + 'grafana/dashboard/panel/menu': [ + { + pluginId: pluginId1, + title: 'Function 1', + description: 'Function 1 description', + extensionPointId: 'grafana/dashboard/panel/menu', + fn: expect.any(Function), + }, + ], + }); + + // Register extensions for the second plugin to a different placement + reactiveRegistry.register({ + pluginId: pluginId2, + configs: [ + { + title: 'Function 2', + description: 'Function 2 description', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + + const registry2 = await reactiveRegistry.getState(); + + expect(registry2).toEqual({ + 'grafana/dashboard/panel/menu': [ + { + pluginId: pluginId1, + title: 'Function 1', + description: 'Function 1 description', + extensionPointId: 'grafana/dashboard/panel/menu', + fn: expect.any(Function), + }, + { + pluginId: pluginId2, + title: 'Function 2', + description: 'Function 2 description', + extensionPointId: 'grafana/dashboard/panel/menu', + fn: expect.any(Function), + }, + ], + }); + }); + + it('should be possible to asynchronously register function extensions for a different placement (different plugin)', async () => { + const pluginId1 = 'grafana-basic-app'; + const pluginId2 = 'grafana-basic-app2'; + const reactiveRegistry = new AddedFunctionsRegistry(); + + // Register extensions for the first plugin + reactiveRegistry.register({ + pluginId: pluginId1, + configs: [ + { + title: 'Function 1', + description: 'Function 1 description', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + + const registry1 = await reactiveRegistry.getState(); + + expect(registry1).toEqual({ + 'grafana/dashboard/panel/menu': [ + { + pluginId: pluginId1, + + title: 'Function 1', + description: 'Function 1 description', + extensionPointId: 'grafana/dashboard/panel/menu', + fn: expect.any(Function), + }, + ], + }); + + // Register extensions for the second plugin to a different placement + reactiveRegistry.register({ + pluginId: pluginId2, + configs: [ + { + title: 'Function 2', + description: 'Function 2 description', + targets: 'plugins/myorg-basic-app/start', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + + const registry2 = await reactiveRegistry.getState(); + + expect(registry2).toEqual({ + 'grafana/dashboard/panel/menu': [ + { + pluginId: pluginId1, + title: 'Function 1', + description: 'Function 1 description', + extensionPointId: 'grafana/dashboard/panel/menu', + fn: expect.any(Function), + }, + ], + 'plugins/myorg-basic-app/start': [ + { + pluginId: pluginId2, + title: 'Function 2', + description: 'Function 2 description', + extensionPointId: 'plugins/myorg-basic-app/start', + fn: expect.any(Function), + }, + ], + }); + }); + + it('should be possible to asynchronously register function extensions for the same placement (same plugin)', async () => { + const pluginId = 'grafana-basic-app'; + const reactiveRegistry = new AddedFunctionsRegistry(); + + // Register extensions for the first extension point + reactiveRegistry.register({ + pluginId: pluginId, + configs: [ + { + title: 'Function 1', + description: 'Function 1 description', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + + // Register extensions to a different extension point + reactiveRegistry.register({ + pluginId: pluginId, + configs: [ + { + title: 'Function 2', + description: 'Function 2 description', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + + const registry2 = await reactiveRegistry.getState(); + + expect(registry2).toEqual({ + 'grafana/dashboard/panel/menu': [ + { + pluginId: pluginId, + + title: 'Function 1', + description: 'Function 1 description', + extensionPointId: 'grafana/dashboard/panel/menu', + fn: expect.any(Function), + }, + { + pluginId: pluginId, + + title: 'Function 2', + description: 'Function 2 description', + extensionPointId: 'grafana/dashboard/panel/menu', + fn: expect.any(Function), + }, + ], + }); + }); + + it('should be possible to asynchronously register function extensions for a different placement (same plugin)', async () => { + const pluginId = 'grafana-basic-app'; + const reactiveRegistry = new AddedFunctionsRegistry(); + + // Register extensions for the first extension point + reactiveRegistry.register({ + pluginId: pluginId, + configs: [ + { + title: 'Function 1', + description: 'Function 1 description', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + + // Register extensions to a different extension point + reactiveRegistry.register({ + pluginId: pluginId, + configs: [ + { + title: 'Function 2', + description: 'Function 2 description', + targets: 'plugins/myorg-basic-app/start', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + + const registry2 = await reactiveRegistry.getState(); + + expect(registry2).toEqual({ + 'grafana/dashboard/panel/menu': [ + { + pluginId: pluginId, + + title: 'Function 1', + description: 'Function 1 description', + extensionPointId: 'grafana/dashboard/panel/menu', + fn: expect.any(Function), + }, + ], + 'plugins/myorg-basic-app/start': [ + { + pluginId: pluginId, + + title: 'Function 2', + description: 'Function 2 description', + extensionPointId: 'plugins/myorg-basic-app/start', + fn: expect.any(Function), + }, + ], + }); + }); + + it('should notify subscribers when the registry changes', async () => { + const pluginId = 'grafana-basic-app'; + const reactiveRegistry = new AddedFunctionsRegistry(); + const observable = reactiveRegistry.asObservable(); + const subscribeCallback = jest.fn(); + + observable.subscribe(subscribeCallback); + + // Register extensions for the first plugin + reactiveRegistry.register({ + pluginId: pluginId, + configs: [ + { + title: 'Function 1', + description: 'Function 1 description', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + + expect(subscribeCallback).toHaveBeenCalledTimes(2); + + // Register extensions for the first plugin + reactiveRegistry.register({ + pluginId: 'another-plugin', + configs: [ + { + title: 'Function 1', + description: 'Function 1 description', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + + expect(subscribeCallback).toHaveBeenCalledTimes(3); + + const registry = subscribeCallback.mock.calls[2][0]; + + expect(registry).toEqual({ + 'grafana/dashboard/panel/menu': [ + { + pluginId: pluginId, + title: 'Function 1', + description: 'Function 1 description', + extensionPointId: 'grafana/dashboard/panel/menu', + fn: expect.any(Function), + }, + { + pluginId: 'another-plugin', + title: 'Function 1', + description: 'Function 1 description', + extensionPointId: 'grafana/dashboard/panel/menu', + fn: expect.any(Function), + }, + ], + }); + }); + + it('should give the last version of the registry for new subscribers', async () => { + const pluginId = 'grafana-basic-app'; + const reactiveRegistry = new AddedFunctionsRegistry(); + const observable = reactiveRegistry.asObservable(); + const subscribeCallback = jest.fn(); + + reactiveRegistry.register({ + pluginId: pluginId, + configs: [ + { + title: 'Function 1', + description: 'Function 1 description', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + + observable.subscribe(subscribeCallback); + expect(subscribeCallback).toHaveBeenCalledTimes(1); + + const registry = subscribeCallback.mock.calls[0][0]; + + expect(registry).toEqual({ + 'grafana/dashboard/panel/menu': [ + { + pluginId: pluginId, + title: 'Function 1', + description: 'Function 1 description', + extensionPointId: 'grafana/dashboard/panel/menu', + fn: expect.any(Function), + }, + ], + }); + }); + + it('should not register a function extension if it has an invalid fn function', () => { + const pluginId = 'grafana-basic-app'; + const reactiveRegistry = new AddedFunctionsRegistry(); + const observable = reactiveRegistry.asObservable(); + const subscribeCallback = jest.fn(); + + reactiveRegistry.register({ + pluginId: pluginId, + configs: [ + { + title: 'Function 1', + description: 'Function 1 description', + targets: 'grafana/dashboard/panel/menu', + //@ts-ignore + fn: '...', + }, + ], + }); + + expect(log.error).toHaveBeenCalled(); + + observable.subscribe(subscribeCallback); + expect(subscribeCallback).toHaveBeenCalledTimes(1); + + const registry = subscribeCallback.mock.calls[0][0]; + expect(registry).toEqual({}); + }); + + it('should not register a function extension if it has invalid properties (empty title)', () => { + const pluginId = 'grafana-basic-app'; + const reactiveRegistry = new AddedFunctionsRegistry(); + const observable = reactiveRegistry.asObservable(); + const subscribeCallback = jest.fn(); + + reactiveRegistry.register({ + pluginId: pluginId, + configs: [ + { + title: '', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + + expect(log.error).toHaveBeenCalled(); + + observable.subscribe(subscribeCallback); + expect(subscribeCallback).toHaveBeenCalledTimes(1); + + const registry = subscribeCallback.mock.calls[0][0]; + expect(registry).toEqual({}); + }); + + it('should not be possible to register a function on a read-only registry', async () => { + const pluginId = 'grafana-basic-app'; + const registry = new AddedFunctionsRegistry(); + const readOnlyRegistry = registry.readOnly(); + + expect(() => { + readOnlyRegistry.register({ + pluginId, + configs: [ + { + title: 'Function 2', + description: 'Function 2 description', + targets: 'plugins/myorg-basic-app/start', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + }).toThrow(MSG_CANNOT_REGISTER_READ_ONLY); + + const currentState = await readOnlyRegistry.getState(); + expect(Object.keys(currentState)).toHaveLength(0); + }); + + it('should pass down fresh registrations to the read-only version of the registry', async () => { + const pluginId = 'grafana-basic-app'; + const registry = new AddedFunctionsRegistry(); + const readOnlyRegistry = registry.readOnly(); + const subscribeCallback = jest.fn(); + let readOnlyState; + + // Should have no extensions registered in the beginning + readOnlyState = await readOnlyRegistry.getState(); + expect(Object.keys(readOnlyState)).toHaveLength(0); + + readOnlyRegistry.asObservable().subscribe(subscribeCallback); + + // Register an extension to the original (writable) registry + registry.register({ + pluginId, + configs: [ + { + title: 'Function 2', + description: 'Function 2 description', + targets: 'plugins/myorg-basic-app/start', + fn: jest.fn().mockReturnValue({}), + }, + ], + }); + + // The read-only registry should have received the new extension + readOnlyState = await readOnlyRegistry.getState(); + expect(Object.keys(readOnlyState)).toHaveLength(1); + + expect(subscribeCallback).toHaveBeenCalledTimes(2); + expect(Object.keys(subscribeCallback.mock.calls[1][0])).toEqual(['plugins/myorg-basic-app/start']); + }); + + it('should not register a function added by a plugin in dev-mode if the meta-info is missing from the plugin.json', async () => { + // Enabling dev mode + jest.mocked(isGrafanaDevMode).mockReturnValue(true); + + const registry = new AddedFunctionsRegistry(); + const fnConfig = { + title: 'Function 1', + description: 'Function 1 description', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn().mockReturnValue({}), + }; + + // Make sure that the meta-info is empty + config.apps[pluginId].extensions.addedFunctions = []; + + registry.register({ + pluginId, + configs: [fnConfig], + }); + + const currentState = await registry.getState(); + + expect(Object.keys(currentState)).toHaveLength(0); + expect(log.error).toHaveBeenCalled(); + }); + + it('should register a function added by core Grafana in dev-mode even if the meta-info is missing', async () => { + // Enabling dev mode + jest.mocked(isGrafanaDevMode).mockReturnValue(true); + + const registry = new AddedFunctionsRegistry(); + const fnConfig = { + title: 'Function 1', + description: 'Function 1 description', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn().mockReturnValue({}), + }; + + registry.register({ + pluginId: 'grafana', + configs: [fnConfig], + }); + + const currentState = await registry.getState(); + + expect(Object.keys(currentState)).toHaveLength(1); + expect(log.error).not.toHaveBeenCalled(); + }); + + it('should register a function added by a plugin in production mode even if the meta-info is missing', async () => { + // Production mode + jest.mocked(isGrafanaDevMode).mockReturnValue(false); + + const registry = new AddedFunctionsRegistry(); + const fnConfig = { + title: 'Function 1', + description: 'Function 1 description', + targets: 'grafana/dashboard/panel/menu', + fn: jest.fn().mockReturnValue({}), + }; + + // Make sure that the meta-info is empty + config.apps[pluginId].extensions.addedFunctions = []; + + registry.register({ + pluginId, + configs: [fnConfig], + }); + + const currentState = await registry.getState(); + + expect(Object.keys(currentState)).toHaveLength(1); + expect(log.error).not.toHaveBeenCalled(); + }); + + it('should register a function added by a plugin in dev-mode if the meta-info is present', async () => { + // Enabling dev mode + jest.mocked(isGrafanaDevMode).mockReturnValue(true); + + const registry = new AddedFunctionsRegistry(); + const fnConfig = { + title: 'Function 1', + description: 'Function 1 description', + targets: ['grafana/dashboard/panel/menu'], + fn: jest.fn().mockReturnValue({}), + }; + + // Make sure that the meta-info is empty + config.apps[pluginId].extensions.addedFunctions = [fnConfig]; + + registry.register({ + pluginId, + configs: [fnConfig], + }); + + const currentState = await registry.getState(); + + expect(Object.keys(currentState)).toHaveLength(1); + expect(log.error).not.toHaveBeenCalled(); + }); +}); diff --git a/public/app/features/plugins/extensions/registry/AddedFunctionsRegistry.ts b/public/app/features/plugins/extensions/registry/AddedFunctionsRegistry.ts new file mode 100644 index 00000000000..d23fe5e78b2 --- /dev/null +++ b/public/app/features/plugins/extensions/registry/AddedFunctionsRegistry.ts @@ -0,0 +1,87 @@ +import { isFunction } from 'lodash'; +import { ReplaySubject } from 'rxjs'; + +import { PluginExtensionAddedFunctionConfig } from '@grafana/data'; + +import * as errors from '../errors'; +import { isGrafanaDevMode } from '../utils'; +import { isAddedFunctionMetaInfoMissing } from '../validators'; + +import { PluginExtensionConfigs, Registry, RegistryType } from './Registry'; + +const logPrefix = 'Could not register function extension. Reason:'; + +export type AddedFunctionsRegistryItem = { + pluginId: string; + title: string; + fn: unknown; + description?: string; +}; + +export class AddedFunctionsRegistry extends Registry { + constructor( + options: { + registrySubject?: ReplaySubject>; + initialState?: RegistryType; + } = {} + ) { + super(options); + } + + mapToRegistry( + registry: RegistryType, + item: PluginExtensionConfigs + ): RegistryType { + const { pluginId, configs } = item; + for (const config of configs) { + const configLog = this.logger.child({ + title: config.title, + pluginId, + }); + + if (!config.title) { + configLog.error(`${logPrefix} ${errors.TITLE_MISSING}`); + continue; + } + + if (!isFunction(config.fn)) { + configLog.error(`${logPrefix} ${errors.INVALID_EXTENSION_FUNCTION}`); + continue; + } + + if (pluginId !== 'grafana' && isGrafanaDevMode() && isAddedFunctionMetaInfoMissing(pluginId, config, configLog)) { + continue; + } + + const extensionPointIds = Array.isArray(config.targets) ? config.targets : [config.targets]; + for (const extensionPointId of extensionPointIds) { + const pointIdLog = configLog.child({ extensionPointId }); + + const result = { + pluginId, + fn: config.fn, + description: config.description, + title: config.title, + extensionPointId, + }; + + pointIdLog.debug('Added function extension successfully registered'); + + if (!(extensionPointId in registry)) { + registry[extensionPointId] = [result]; + } else { + registry[extensionPointId].push(result); + } + } + } + + return registry; + } + + // Returns a read-only version of the registry. + readOnly() { + return new AddedFunctionsRegistry({ + registrySubject: this.registrySubject, + }); + } +} diff --git a/public/app/features/plugins/extensions/registry/AddedLinksRegistry.test.ts b/public/app/features/plugins/extensions/registry/AddedLinksRegistry.test.ts index 4d5ab5c084f..d3586240276 100644 --- a/public/app/features/plugins/extensions/registry/AddedLinksRegistry.test.ts +++ b/public/app/features/plugins/extensions/registry/AddedLinksRegistry.test.ts @@ -51,6 +51,7 @@ describe('AddedLinksRegistry', () => { extensions: { addedLinks: [], addedComponents: [], + addedFunctions: [], exposedComponents: [], extensionPoints: [], }, diff --git a/public/app/features/plugins/extensions/registry/ExposedComponentsRegistry.test.ts b/public/app/features/plugins/extensions/registry/ExposedComponentsRegistry.test.ts index 0a7036894d1..863c89f7b40 100644 --- a/public/app/features/plugins/extensions/registry/ExposedComponentsRegistry.test.ts +++ b/public/app/features/plugins/extensions/registry/ExposedComponentsRegistry.test.ts @@ -52,6 +52,7 @@ describe('ExposedComponentsRegistry', () => { extensions: { addedLinks: [], addedComponents: [], + addedFunctions: [], exposedComponents: [], extensionPoints: [], }, diff --git a/public/app/features/plugins/extensions/registry/setup.ts b/public/app/features/plugins/extensions/registry/setup.ts index 6c2fd1e6a5f..91b7badc4eb 100644 --- a/public/app/features/plugins/extensions/registry/setup.ts +++ b/public/app/features/plugins/extensions/registry/setup.ts @@ -1,6 +1,7 @@ import { getCoreExtensionConfigurations } from '../getCoreExtensionConfigurations'; import { AddedComponentsRegistry } from './AddedComponentsRegistry'; +import { AddedFunctionsRegistry } from './AddedFunctionsRegistry'; import { AddedLinksRegistry } from './AddedLinksRegistry'; import { ExposedComponentsRegistry } from './ExposedComponentsRegistry'; import { PluginExtensionRegistries } from './types'; @@ -8,10 +9,12 @@ import { PluginExtensionRegistries } from './types'; export const addedComponentsRegistry = new AddedComponentsRegistry(); export const exposedComponentsRegistry = new ExposedComponentsRegistry(); export const addedLinksRegistry = new AddedLinksRegistry(); +export const addedFunctionsRegistry = new AddedFunctionsRegistry(); export const pluginExtensionRegistries: PluginExtensionRegistries = { addedComponentsRegistry, exposedComponentsRegistry, addedLinksRegistry, + addedFunctionsRegistry, }; // Registering core extensions diff --git a/public/app/features/plugins/extensions/registry/types.ts b/public/app/features/plugins/extensions/registry/types.ts index 115e859b7d9..1927bf31b75 100644 --- a/public/app/features/plugins/extensions/registry/types.ts +++ b/public/app/features/plugins/extensions/registry/types.ts @@ -1,9 +1,11 @@ import { AddedComponentsRegistry } from './AddedComponentsRegistry'; +import { AddedFunctionsRegistry } from './AddedFunctionsRegistry'; import { AddedLinksRegistry } from './AddedLinksRegistry'; import { ExposedComponentsRegistry } from './ExposedComponentsRegistry'; export type PluginExtensionRegistries = { addedComponentsRegistry: AddedComponentsRegistry; exposedComponentsRegistry: ExposedComponentsRegistry; + addedFunctionsRegistry: AddedFunctionsRegistry; addedLinksRegistry: AddedLinksRegistry; }; diff --git a/public/app/features/plugins/extensions/usePluginComponent.test.tsx b/public/app/features/plugins/extensions/usePluginComponent.test.tsx index 6385c022027..b2a3d3d3435 100644 --- a/public/app/features/plugins/extensions/usePluginComponent.test.tsx +++ b/public/app/features/plugins/extensions/usePluginComponent.test.tsx @@ -7,6 +7,7 @@ import { ExtensionRegistriesProvider } from './ExtensionRegistriesContext'; import { log } from './logs/log'; import { resetLogMock } from './logs/testUtils'; import { AddedComponentsRegistry } from './registry/AddedComponentsRegistry'; +import { AddedFunctionsRegistry } from './registry/AddedFunctionsRegistry'; import { AddedLinksRegistry } from './registry/AddedLinksRegistry'; import { ExposedComponentsRegistry } from './registry/ExposedComponentsRegistry'; import { PluginExtensionRegistries } from './registry/types'; @@ -78,6 +79,7 @@ describe('usePluginComponent()', () => { extensions: { addedLinks: [], addedComponents: [], + addedFunctions: [], // This is necessary, so we can register exposed components to the registry during the tests // (Otherwise the registry would reject it in the imitated production mode) exposedComponents: [exposedComponentConfig], @@ -90,6 +92,7 @@ describe('usePluginComponent()', () => { addedComponentsRegistry: new AddedComponentsRegistry(), exposedComponentsRegistry: new ExposedComponentsRegistry(), addedLinksRegistry: new AddedLinksRegistry(), + addedFunctionsRegistry: new AddedFunctionsRegistry(), }; jest.mocked(useLoadAppPlugins).mockReturnValue({ isLoading: false }); jest.mocked(isGrafanaDevMode).mockReturnValue(false); @@ -122,6 +125,7 @@ describe('usePluginComponent()', () => { addedComponents: [], exposedComponents: [], extensionPoints: [], + addedFunctions: [], }, dependencies: { grafanaVersion: '8.0.0', diff --git a/public/app/features/plugins/extensions/usePluginComponents.test.tsx b/public/app/features/plugins/extensions/usePluginComponents.test.tsx index 75adda33f75..03aad063d53 100644 --- a/public/app/features/plugins/extensions/usePluginComponents.test.tsx +++ b/public/app/features/plugins/extensions/usePluginComponents.test.tsx @@ -6,6 +6,7 @@ import { ExtensionRegistriesProvider } from './ExtensionRegistriesContext'; import { log } from './logs/log'; import { resetLogMock } from './logs/testUtils'; import { AddedComponentsRegistry } from './registry/AddedComponentsRegistry'; +import { AddedFunctionsRegistry } from './registry/AddedFunctionsRegistry'; import { AddedLinksRegistry } from './registry/AddedLinksRegistry'; import { ExposedComponentsRegistry } from './registry/ExposedComponentsRegistry'; import { PluginExtensionRegistries } from './registry/types'; @@ -60,6 +61,7 @@ describe('usePluginComponents()', () => { addedComponentsRegistry: new AddedComponentsRegistry(), exposedComponentsRegistry: new ExposedComponentsRegistry(), addedLinksRegistry: new AddedLinksRegistry(), + addedFunctionsRegistry: new AddedFunctionsRegistry(), }; jest.mocked(wrapWithPluginContext).mockClear(); @@ -89,6 +91,7 @@ describe('usePluginComponents()', () => { addedComponents: [], exposedComponents: [], extensionPoints: [], + addedFunctions: [], }, dependencies: { grafanaVersion: '8.0.0', @@ -156,6 +159,50 @@ describe('usePluginComponents()', () => { expect(screen.queryByText('Hello World3')).toBeNull(); }); + it('should return component with meta information attached to it', async () => { + registries.addedComponentsRegistry.register({ + pluginId, + configs: [ + { + targets: extensionPointId, + title: '1', + description: '1', + component: () =>
Hello World1
, + }, + { + targets: extensionPointId, + title: '2', + description: '2', + component: () =>
Hello World2
, + }, + { + targets: 'plugins/another-extension/v1', + title: '3', + description: '3', + component: () =>
Hello World3
, + }, + ], + }); + + const { result } = renderHook(() => usePluginComponents({ extensionPointId }), { wrapper }); + + expect(result.current.components.length).toBe(2); + expect(result.current.components[0].meta).toEqual({ + pluginId, + title: '1', + description: '1', + id: '-1921123020', + type: 'component', + }); + expect(result.current.components[1].meta).toEqual({ + pluginId, + title: '2', + description: '2', + id: '-1921123019', + type: 'component', + }); + }); + it('should dynamically update the extensions registered for a certain extension point', () => { let { result, rerender } = renderHook(() => usePluginComponents({ extensionPointId }), { wrapper }); diff --git a/public/app/features/plugins/extensions/usePluginComponents.tsx b/public/app/features/plugins/extensions/usePluginComponents.tsx index e583a69141d..c3e406d5d23 100644 --- a/public/app/features/plugins/extensions/usePluginComponents.tsx +++ b/public/app/features/plugins/extensions/usePluginComponents.tsx @@ -1,24 +1,22 @@ import { useMemo } from 'react'; import { useObservable } from 'react-use'; -import { usePluginContext } from '@grafana/data'; -import { - UsePluginComponentOptions, - UsePluginComponentsResult, -} from '@grafana/runtime/src/services/pluginExtensions/getPluginExtensions'; +import { PluginExtensionComponentMeta, PluginExtensionTypes, usePluginContext } from '@grafana/data'; +import { UsePluginComponentsOptions, UsePluginComponentsResult } from '@grafana/runtime'; import { useAddedComponentsRegistry } from './ExtensionRegistriesContext'; import * as errors from './errors'; import { log } from './logs/log'; +import { AddedComponentRegistryItem } from './registry/AddedComponentsRegistry'; import { useLoadAppPlugins } from './useLoadAppPlugins'; -import { getExtensionPointPluginDependencies, isGrafanaDevMode } from './utils'; +import { generateExtensionId, getExtensionPointPluginDependencies, isGrafanaDevMode } from './utils'; import { isExtensionPointIdValid, isExtensionPointMetaInfoMissing } from './validators'; // Returns an array of component extensions for the given extension point export function usePluginComponents({ limitPerPlugin, extensionPointId, -}: UsePluginComponentOptions): UsePluginComponentsResult { +}: UsePluginComponentsOptions): UsePluginComponentsResult { const registry = useAddedComponentsRegistry(); const registryState = useObservable(registry.asObservable()); const pluginContext = usePluginContext(); @@ -27,7 +25,7 @@ export function usePluginComponents({ return useMemo(() => { // For backwards compatibility we don't enable restrictions in production or when the hook is used in core Grafana. const enableRestrictions = isGrafanaDevMode() && pluginContext; - const components: Array> = []; + const components: Array & { meta: PluginExtensionComponentMeta }> = []; const extensionsByPlugin: Record = {}; const pluginId = pluginContext?.meta.id ?? ''; const pointLog = log.child({ @@ -66,7 +64,12 @@ export function usePluginComponents({ extensionsByPlugin[pluginId] = 0; } - components.push(registryItem.component as React.ComponentType); + const component = createComponentWithMeta( + registryItem as AddedComponentRegistryItem, + extensionPointId + ); + + components.push(component); extensionsByPlugin[pluginId] += 1; } @@ -76,3 +79,28 @@ export function usePluginComponents({ }; }, [extensionPointId, limitPerPlugin, pluginContext, registryState, isLoadingAppPlugins]); } + +// exported so it can be used in tests +export function createComponentWithMeta( + registryItem: AddedComponentRegistryItem, + extensionPointId: string +): React.ComponentType & { meta: PluginExtensionComponentMeta } { + const { component: Component, ...config } = registryItem; + function ComponentWithMeta(props: Props) { + return ; + } + + ComponentWithMeta.displayName = Component.displayName; + ComponentWithMeta.defaultProps = Component.defaultProps; + ComponentWithMeta.propTypes = Component.propTypes; + ComponentWithMeta.contextTypes = Component.contextTypes; + ComponentWithMeta.meta = { + pluginId: config.pluginId, + title: config.title ?? '', + description: config.description ?? '', + id: generateExtensionId(config.pluginId, extensionPointId, config.title), + type: PluginExtensionTypes.component, + } satisfies PluginExtensionComponentMeta; + + return ComponentWithMeta; +} diff --git a/public/app/features/plugins/extensions/usePluginExtensions.test.tsx b/public/app/features/plugins/extensions/usePluginExtensions.test.tsx index 739906dd815..479e5cd4c6c 100644 --- a/public/app/features/plugins/extensions/usePluginExtensions.test.tsx +++ b/public/app/features/plugins/extensions/usePluginExtensions.test.tsx @@ -1,6 +1,7 @@ import { act, renderHook } from '@testing-library/react'; import { AddedComponentsRegistry } from './registry/AddedComponentsRegistry'; +import { AddedFunctionsRegistry } from './registry/AddedFunctionsRegistry'; import { AddedLinksRegistry } from './registry/AddedLinksRegistry'; import { ExposedComponentsRegistry } from './registry/ExposedComponentsRegistry'; import { PluginExtensionRegistries } from './registry/types'; @@ -19,6 +20,7 @@ describe('usePluginExtensions()', () => { addedComponentsRegistry: new AddedComponentsRegistry(), addedLinksRegistry: new AddedLinksRegistry(), exposedComponentsRegistry: new ExposedComponentsRegistry(), + addedFunctionsRegistry: new AddedFunctionsRegistry(), }; jest.mocked(useLoadAppPlugins).mockReturnValue({ isLoading: false }); }); diff --git a/public/app/features/plugins/extensions/usePluginFunctions.tsx b/public/app/features/plugins/extensions/usePluginFunctions.tsx new file mode 100644 index 00000000000..68acee76221 --- /dev/null +++ b/public/app/features/plugins/extensions/usePluginFunctions.tsx @@ -0,0 +1,82 @@ +import { useMemo } from 'react'; +import { useObservable } from 'react-use'; + +import { usePluginContext, PluginExtensionFunction, PluginExtensionTypes } from '@grafana/data'; +import { UsePluginFunctionsOptions, UsePluginFunctionsResult } from '@grafana/runtime'; + +import { useAddedFunctionsRegistry } from './ExtensionRegistriesContext'; +import * as errors from './errors'; +import { log } from './logs/log'; +import { useLoadAppPlugins } from './useLoadAppPlugins'; +import { generateExtensionId, getExtensionPointPluginDependencies, isGrafanaDevMode } from './utils'; +import { isExtensionPointIdValid, isExtensionPointMetaInfoMissing } from './validators'; + +// Returns an array of component extensions for the given extension point +export function usePluginFunctions({ + limitPerPlugin, + extensionPointId, +}: UsePluginFunctionsOptions): UsePluginFunctionsResult { + const registry = useAddedFunctionsRegistry(); + const registryState = useObservable(registry.asObservable()); + const pluginContext = usePluginContext(); + const deps = getExtensionPointPluginDependencies(extensionPointId); + const { isLoading: isLoadingAppPlugins } = useLoadAppPlugins(deps); + + return useMemo(() => { + // For backwards compatibility we don't enable restrictions in production or when the hook is used in core Grafana. + const enableRestrictions = isGrafanaDevMode() && pluginContext; + const results: Array> = []; + const extensionsByPlugin: Record = {}; + const pluginId = pluginContext?.meta.id ?? ''; + const pointLog = log.child({ + pluginId, + extensionPointId, + }); + if (enableRestrictions && !isExtensionPointIdValid({ extensionPointId, pluginId })) { + pointLog.error(errors.INVALID_EXTENSION_POINT_ID); + } + + if (enableRestrictions && isExtensionPointMetaInfoMissing(extensionPointId, pluginContext)) { + pointLog.error(errors.EXTENSION_POINT_META_INFO_MISSING); + return { + isLoading: false, + functions: [], + }; + } + + if (isLoadingAppPlugins) { + return { + isLoading: true, + functions: [], + }; + } + + for (const registryItem of registryState?.[extensionPointId] ?? []) { + const { pluginId } = registryItem; + + // Only limit if the `limitPerPlugin` is set + if (limitPerPlugin && extensionsByPlugin[pluginId] >= limitPerPlugin) { + continue; + } + + if (extensionsByPlugin[pluginId] === undefined) { + extensionsByPlugin[pluginId] = 0; + } + + results.push({ + id: generateExtensionId(pluginId, extensionPointId, registryItem.title), + type: PluginExtensionTypes.function, + title: registryItem.title, + description: registryItem.description ?? '', + pluginId: pluginId, + fn: registryItem.fn as Signature, + }); + extensionsByPlugin[pluginId] += 1; + } + + return { + isLoading: false, + functions: results, + }; + }, [extensionPointId, limitPerPlugin, pluginContext, registryState, isLoadingAppPlugins]); +} diff --git a/public/app/features/plugins/extensions/usePluginLinks.test.tsx b/public/app/features/plugins/extensions/usePluginLinks.test.tsx index 9186ca0ddf6..f9fb623f42a 100644 --- a/public/app/features/plugins/extensions/usePluginLinks.test.tsx +++ b/public/app/features/plugins/extensions/usePluginLinks.test.tsx @@ -6,6 +6,7 @@ import { ExtensionRegistriesProvider } from './ExtensionRegistriesContext'; import { log } from './logs/log'; import { resetLogMock } from './logs/testUtils'; import { AddedComponentsRegistry } from './registry/AddedComponentsRegistry'; +import { AddedFunctionsRegistry } from './registry/AddedFunctionsRegistry'; import { AddedLinksRegistry } from './registry/AddedLinksRegistry'; import { ExposedComponentsRegistry } from './registry/ExposedComponentsRegistry'; import { PluginExtensionRegistries } from './registry/types'; @@ -57,6 +58,7 @@ describe('usePluginLinks()', () => { addedComponentsRegistry: new AddedComponentsRegistry(), exposedComponentsRegistry: new ExposedComponentsRegistry(), addedLinksRegistry: new AddedLinksRegistry(), + addedFunctionsRegistry: new AddedFunctionsRegistry(), }; resetLogMock(log); @@ -85,6 +87,7 @@ describe('usePluginLinks()', () => { addedComponents: [], exposedComponents: [], extensionPoints: [], + addedFunctions: [], }, dependencies: { grafanaVersion: '8.0.0', diff --git a/public/app/features/plugins/extensions/usePluginLinks.tsx b/public/app/features/plugins/extensions/usePluginLinks.tsx index 58efcc4e3e6..6ecaf6b71b9 100644 --- a/public/app/features/plugins/extensions/usePluginLinks.tsx +++ b/public/app/features/plugins/extensions/usePluginLinks.tsx @@ -3,10 +3,7 @@ import { useMemo } from 'react'; import { useObservable } from 'react-use'; import { PluginExtensionLink, PluginExtensionTypes, usePluginContext } from '@grafana/data'; -import { - UsePluginLinksOptions, - UsePluginLinksResult, -} from '@grafana/runtime/src/services/pluginExtensions/getPluginExtensions'; +import { UsePluginLinksOptions, UsePluginLinksResult } from '@grafana/runtime'; import { useAddedLinksRegistry } from './ExtensionRegistriesContext'; import * as errors from './errors'; diff --git a/public/app/features/plugins/extensions/utils.test.tsx b/public/app/features/plugins/extensions/utils.test.tsx index 32ad6215875..0e2d98f44b3 100644 --- a/public/app/features/plugins/extensions/utils.test.tsx +++ b/public/app/features/plugins/extensions/utils.test.tsx @@ -475,6 +475,7 @@ describe('Plugin Extensions / Utils', () => { extensions: { addedLinks: [], addedComponents: [], + addedFunctions: [], exposedComponents: [], extensionPoints: [], }, @@ -553,6 +554,7 @@ describe('Plugin Extensions / Utils', () => { extensions: { addedLinks: [], addedComponents: [], + addedFunctions: [], exposedComponents: [], extensionPoints: [], }, @@ -584,6 +586,7 @@ describe('Plugin Extensions / Utils', () => { addedComponents: [], exposedComponents: [], extensionPoints: [], + addedFunctions: [], }, }, 'myorg-third-app': { @@ -623,6 +626,7 @@ describe('Plugin Extensions / Utils', () => { ], exposedComponents: [], extensionPoints: [], + addedFunctions: [], }, }, }; @@ -679,6 +683,7 @@ describe('Plugin Extensions / Utils', () => { ], exposedComponents: [], extensionPoints: [], + addedFunctions: [], }, dependencies: { ...genereicAppPluginConfig.dependencies, @@ -705,6 +710,7 @@ describe('Plugin Extensions / Utils', () => { }, ], extensionPoints: [], + addedFunctions: [], }, dependencies: { ...genereicAppPluginConfig.dependencies, @@ -726,6 +732,7 @@ describe('Plugin Extensions / Utils', () => { }, ], extensionPoints: [], + addedFunctions: [], }, }, 'myorg-sixth-app': { @@ -763,6 +770,7 @@ describe('Plugin Extensions / Utils', () => { addedComponents: [], exposedComponents: [], extensionPoints: [], + addedFunctions: [], }, }; @@ -791,6 +799,7 @@ describe('Plugin Extensions / Utils', () => { }, ], extensionPoints: [], + addedFunctions: [], }, }, 'myorg-third-app': { @@ -825,6 +834,7 @@ describe('Plugin Extensions / Utils', () => { }, ], extensionPoints: [], + addedFunctions: [], }, dependencies: { ...genereicAppPluginConfig.dependencies, @@ -850,6 +860,7 @@ describe('Plugin Extensions / Utils', () => { }, ], extensionPoints: [], + addedFunctions: [], }, dependencies: { ...genereicAppPluginConfig.dependencies, @@ -871,6 +882,7 @@ describe('Plugin Extensions / Utils', () => { }, ], extensionPoints: [], + addedFunctions: [], }, }, }; @@ -902,6 +914,7 @@ describe('Plugin Extensions / Utils', () => { extensions: { addedLinks: [], addedComponents: [], + addedFunctions: [], exposedComponents: [], extensionPoints: [], }, diff --git a/public/app/features/plugins/extensions/validators.test.tsx b/public/app/features/plugins/extensions/validators.test.tsx index 12b146bbf5c..f083ff738ea 100644 --- a/public/app/features/plugins/extensions/validators.test.tsx +++ b/public/app/features/plugins/extensions/validators.test.tsx @@ -271,6 +271,7 @@ describe('Plugin Extension Validators', () => { addedComponents: [], exposedComponents: [], extensionPoints: [], + addedFunctions: [], }, }; const extensionConfig = { @@ -387,6 +388,7 @@ describe('Plugin Extension Validators', () => { addedComponents: [], exposedComponents: [], extensionPoints: [], + addedFunctions: [], }, }; const extensionConfig = { @@ -503,6 +505,7 @@ describe('Plugin Extension Validators', () => { addedComponents: [], exposedComponents: [], extensionPoints: [], + addedFunctions: [], }, }; const exposedComponentConfig = { @@ -688,6 +691,7 @@ describe('Plugin Extension Validators', () => { addedComponents: [], exposedComponents: [], extensionPoints: [], + addedFunctions: [], }, dependencies: { grafanaVersion: '8.0.0', diff --git a/public/app/features/plugins/extensions/validators.ts b/public/app/features/plugins/extensions/validators.ts index b1dbd5e9af8..cbdaf81e935 100644 --- a/public/app/features/plugins/extensions/validators.ts +++ b/public/app/features/plugins/extensions/validators.ts @@ -5,6 +5,7 @@ import type { PluginContextType, PluginExtensionAddedComponentConfig, PluginExtensionExposedComponentConfig, + PluginExtensionAddedFunctionConfig, } from '@grafana/data'; import { PluginAddedLinksConfigureFunc, PluginExtensionPoints } from '@grafana/data/src/types/pluginExtensions'; import { config, isPluginExtensionLink } from '@grafana/runtime'; @@ -160,6 +161,38 @@ export const isAddedLinkMetaInfoMissing = ( return false; }; +export const isAddedFunctionMetaInfoMissing = ( + pluginId: string, + metaInfo: PluginExtensionAddedFunctionConfig, + log: ExtensionsLog +) => { + const logPrefix = 'Could not register function extension. Reason:'; + const app = config.apps[pluginId]; + const pluginJsonMetaInfo = app ? app.extensions.addedFunctions.find(({ title }) => title === metaInfo.title) : null; + + if (!app) { + log.error(`${logPrefix} ${errors.APP_NOT_FOUND(pluginId)}`); + return true; + } + + if (!pluginJsonMetaInfo) { + log.error(`${logPrefix} ${errors.ADDED_FUNCTION_META_INFO_MISSING}`); + return true; + } + + const targets = Array.isArray(metaInfo.targets) ? metaInfo.targets : [metaInfo.targets]; + if (!targets.every((target) => pluginJsonMetaInfo.targets.includes(target))) { + log.error(`${logPrefix} ${errors.TARGET_NOT_MATCHING_META_INFO}`); + return true; + } + + if (pluginJsonMetaInfo.description !== metaInfo.description) { + log.warning(errors.DESCRIPTION_NOT_MATCHING_META_INFO); + } + + return false; +}; + export const isAddedComponentMetaInfoMissing = ( pluginId: string, metaInfo: PluginExtensionAddedComponentConfig, diff --git a/public/app/features/plugins/importPanelPlugin.ts b/public/app/features/plugins/importPanelPlugin.ts index 45732de7fb1..f6d9c1ac2a6 100644 --- a/public/app/features/plugins/importPanelPlugin.ts +++ b/public/app/features/plugins/importPanelPlugin.ts @@ -82,7 +82,6 @@ function getPanelPlugin(meta: PanelPluginMeta): Promise { if (!plugin.panel && plugin.angularPanelCtrl) { plugin.panel = getAngularPanelReactWrapper(plugin); } - return plugin; }) .catch((err) => { diff --git a/public/app/features/plugins/loader/sharedDependencies.ts b/public/app/features/plugins/loader/sharedDependencies.ts index dbb8ed8692f..7059274c717 100644 --- a/public/app/features/plugins/loader/sharedDependencies.ts +++ b/public/app/features/plugins/loader/sharedDependencies.ts @@ -18,6 +18,7 @@ import { appEvents, contextSrv } from 'app/core/core'; import { BackendSrv, getBackendSrv } from 'app/core/services/backend_srv'; import impressionSrv from 'app/core/services/impression_srv'; import TimeSeries from 'app/core/time_series2'; +import { arrayMove } from 'app/core/utils/arrayMove'; import * as flatten from 'app/core/utils/flatten'; import kbn from 'app/core/utils/kbn'; import * as ticks from 'app/core/utils/ticks'; @@ -90,7 +91,8 @@ export const sharedDependenciesMap = { __useDefault: true, }, ...jQueryFlotDeps, - lodash: () => import('lodash').then((module) => ({ ...module, __useDefault: true })), + // add move to lodash for backward compatabilty with plugins + lodash: () => import('lodash').then((module) => ({ ...module, move: arrayMove, __useDefault: true })), moment: () => import('moment').then((module) => ({ ...module, __useDefault: true })), prismjs: () => import('prismjs'), react: () => import('react'), diff --git a/public/app/features/plugins/plugin_loader.ts b/public/app/features/plugins/plugin_loader.ts index aa20ae5881f..98915c40f6d 100644 --- a/public/app/features/plugins/plugin_loader.ts +++ b/public/app/features/plugins/plugin_loader.ts @@ -13,7 +13,12 @@ import { DataQuery } from '@grafana/schema'; import { GenericDataSourcePlugin } from '../datasources/types'; import builtInPlugins from './built_in_plugins'; -import { addedComponentsRegistry, addedLinksRegistry, exposedComponentsRegistry } from './extensions/registry/setup'; +import { + addedComponentsRegistry, + addedFunctionsRegistry, + addedLinksRegistry, + exposedComponentsRegistry, +} from './extensions/registry/setup'; import { getPluginFromCache, registerPluginInCache } from './loader/cache'; // SystemJS has to be imported before the sharedDependenciesMap import { SystemJS } from './loader/systemjs'; @@ -153,7 +158,6 @@ export function importDataSourcePlugin(meta: DataSourcePluginMeta): Promise, @@ -205,6 +209,10 @@ export async function importAppPlugin(meta: PluginMeta): Promise { pluginId, configs: plugin.addedLinkConfigs || [], }); + addedFunctionsRegistry.register({ + pluginId, + configs: plugin.addedFunctionConfigs || [], + }); importedAppPlugins[pluginId] = plugin; diff --git a/public/app/features/plugins/routes.tsx b/public/app/features/plugins/routes.tsx index 46cc470549d..1504f6e2e48 100644 --- a/public/app/features/plugins/routes.tsx +++ b/public/app/features/plugins/routes.tsx @@ -4,26 +4,28 @@ import { getRootSectionForNode } from 'app/core/selectors/navModel'; import AppRootPage from 'app/features/plugins/components/AppRootPage'; import { getState } from 'app/store/store'; +const isPluginNavModelItem = (model: NavModelItem): model is PluginNavModelItem => 'pluginId' in model && 'id' in model; +const isStandalonePluginPage = (id: string) => id.startsWith('standalone-plugin-page-/'); + +function getPathForNavItem(navItem: PluginNavModelItem) { + const pluginNavSection = getRootSectionForNode(navItem); + const appPluginUrl = `/a/${navItem.pluginId}`; + const path = isStandalonePluginPage(navItem.id) ? navItem.url || appPluginUrl : appPluginUrl; // Only standalone pages can use core URLs, otherwise we fall back to "/a/:pluginId" + const isSensitive = isStandalonePluginPage(navItem.id) && !navItem.url?.startsWith('/a/'); // Have case-sensitive URLs only for standalone pages that have custom URLs + + return { + path: `${path}/*`, + sensitive: isSensitive, + component: () => , + }; +} + export function getAppPluginRoutes(): RouteDescriptor[] { const state = getState(); const { navIndex } = state; - const isStandalonePluginPage = (id: string) => id.startsWith('standalone-plugin-page-/'); - const isPluginNavModelItem = (model: NavModelItem): model is PluginNavModelItem => - 'pluginId' in model && 'id' in model; const explicitAppPluginRoutes = Object.values(navIndex) .filter(isPluginNavModelItem) - .map((navItem) => { - const pluginNavSection = getRootSectionForNode(navItem); - const appPluginUrl = `/a/${navItem.pluginId}`; - const path = isStandalonePluginPage(navItem.id) ? navItem.url || appPluginUrl : appPluginUrl; // Only standalone pages can use core URLs, otherwise we fall back to "/a/:pluginId" - const isSensitive = isStandalonePluginPage(navItem.id) && !navItem.url?.startsWith('/a/'); // Have case-sensitive URLs only for standalone pages that have custom URLs - - return { - path: `${path}/*`, - sensitive: isSensitive, - component: () => , - }; - }); + .map(getPathForNavItem); return [ ...explicitAppPluginRoutes, @@ -36,6 +38,23 @@ export function getAppPluginRoutes(): RouteDescriptor[] { ]; } +export function getRouteForAppPlugin(pluginId: string): RouteDescriptor { + const state = getState(); + const { navIndex } = state; + const navItem = Object.values(navIndex) + .filter(isPluginNavModelItem) + .find((navItem) => navItem.pluginId === pluginId); + + if (!navItem) { + return { + path: '/a/:pluginId/*', + component: () => , + }; + } + + return getPathForNavItem(navItem); +} + interface PluginNavModelItem extends Omit { pluginId: string; id: string; diff --git a/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.test.ts b/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.test.ts index a0c0121ee0f..8138baf6b6f 100644 --- a/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.test.ts +++ b/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.test.ts @@ -1,6 +1,8 @@ -import { PluginMeta, PluginSignatureType } from '@grafana/data'; +import { PluginMeta, PluginSignatureStatus, PluginSignatureType } from '@grafana/data'; import { config } from '@grafana/runtime'; +import { getPluginDetails } from '../admin/api'; +import { CatalogPluginDetails } from '../admin/types'; import { getPluginSettings } from '../pluginSettings'; import { @@ -22,18 +24,31 @@ jest.mock('../pluginSettings', () => ({ getPluginSettings: jest.fn(), })); -const getPluginSettingsMock = getPluginSettings as jest.MockedFunction; +jest.mock('../admin/api', () => ({ + getPluginDetails: jest.fn(), +})); -const fakePlugin: PluginMeta = { +const getPluginSettingsMock = jest.mocked(getPluginSettings); +const getPluginDetailsMock = jest.mocked(getPluginDetails); + +const fakePluginSettings: PluginMeta = { id: 'test-plugin', name: 'Test Plugin', } as PluginMeta; +const fakePluginDetails: CatalogPluginDetails = {} as CatalogPluginDetails; + describe('Sandbox eligibility checks', () => { const originalNodeEnv = process.env.NODE_ENV; beforeEach(() => { jest.clearAllMocks(); + getPluginDetailsMock.mockReset(); + getPluginSettingsMock.mockReset(); + + // restore default check + setSandboxEnabledCheck(isPluginFrontendSandboxEnabled); + config.enableFrontendSandboxForPlugins = []; config.featureToggles.pluginsFrontendSandbox = true; process.env.NODE_ENV = 'development'; @@ -54,26 +69,8 @@ describe('Sandbox eligibility checks', () => { expect(result).toBe(false); }); - test('shouldLoadPluginInFrontendSandbox returns false for Grafana-signed plugins', async () => { - getPluginSettingsMock.mockResolvedValue({ ...fakePlugin, signatureType: PluginSignatureType.grafana }); - const result = await shouldLoadPluginInFrontendSandbox({ pluginId: 'test-plugin' }); - expect(result).toBe(false); - }); - - test('shouldLoadPluginInFrontendSandbox returns true for eligible plugins in the list', async () => { - getPluginSettingsMock.mockResolvedValue({ ...fakePlugin, signatureType: PluginSignatureType.community }); - config.enableFrontendSandboxForPlugins = ['test-plugin']; - const result = await shouldLoadPluginInFrontendSandbox({ pluginId: 'test-plugin' }); - expect(result).toBe(true); - }); - - test('isPluginFrontendSandboxEnabled returns false when plugin is not in the enabled list', async () => { - config.enableFrontendSandboxForPlugins = ['other-plugin']; - const result = await isPluginFrontendSandboxEnabled({ pluginId: 'test-plugin' }); - expect(result).toBe(false); - }); - test('setSandboxEnabledCheck sets custom check function', async () => { + getPluginDetailsMock.mockResolvedValue(fakePluginDetails); const customCheck = jest.fn().mockResolvedValue(true); setSandboxEnabledCheck(customCheck); const result = await shouldLoadPluginInFrontendSandbox({ pluginId: 'test-plugin' }); @@ -82,6 +79,7 @@ describe('Sandbox eligibility checks', () => { }); test('setSandboxEnabledCheck has precedence over default', async () => { + getPluginDetailsMock.mockResolvedValue(fakePluginDetails); const customCheck = jest.fn().mockResolvedValue(false); setSandboxEnabledCheck(customCheck); // this should be ignored by the custom check @@ -91,10 +89,123 @@ describe('Sandbox eligibility checks', () => { expect(result).toBe(false); }); - test('isPluginFrontendSandboxEligible returns false for plugins with internal signature', async () => { - //@ts-expect-error We don't publicly export the internal signature - getPluginSettingsMock.mockResolvedValue({ ...fakePlugin, signature: 'internal' }); - const result = await isPluginFrontendSandboxEligible({ pluginId: 'test-plugin' }); - expect(result).toBe(false); + describe('with getPluginDetails', () => { + test('shouldLoadPluginInFrontendSandbox returns false for Grafana-signed plugins', async () => { + getPluginSettingsMock.mockRejectedValueOnce(new Error('not found')); + + getPluginDetailsMock.mockResolvedValue({ ...fakePluginDetails, signatureType: PluginSignatureType.grafana }); + + const result = await shouldLoadPluginInFrontendSandbox({ pluginId: 'test-plugin' }); + expect(result).toBe(false); + }); + + test('shouldLoadPluginInFrontendSandbox returns true for community plugins', async () => { + getPluginSettingsMock.mockRejectedValueOnce(new Error('not found')); + + getPluginDetailsMock.mockResolvedValue({ ...fakePluginDetails, signatureType: PluginSignatureType.community }); + + config.enableFrontendSandboxForPlugins = ['test-plugin']; + const result = await shouldLoadPluginInFrontendSandbox({ pluginId: 'test-plugin' }); + expect(result).toBe(true); + }); + + test('isPluginFrontendSandboxEnabled returns false when plugin is not in the enabled list', async () => { + getPluginSettingsMock.mockRejectedValueOnce(new Error('not found')); + + getPluginDetailsMock.mockResolvedValue({ ...fakePluginDetails, signatureType: PluginSignatureType.community }); + config.enableFrontendSandboxForPlugins = ['other-plugin']; + const result = await isPluginFrontendSandboxEnabled({ pluginId: 'test-plugin' }); + expect(result).toBe(false); + }); + + test('shouldLoadPluginInFrontendSandbox returns true for commercial plugins in the enabled list', async () => { + getPluginSettingsMock.mockRejectedValueOnce(new Error('not found')); + + getPluginDetailsMock.mockResolvedValue({ ...fakePluginDetails, signatureType: PluginSignatureType.commercial }); + config.enableFrontendSandboxForPlugins = ['test-plugin']; + const result = await shouldLoadPluginInFrontendSandbox({ pluginId: 'test-plugin' }); + expect(result).toBe(true); + }); + + test('shouldLoadPluginInFrontendSandbox returns true for private plugins in the enabled list', async () => { + getPluginSettingsMock.mockRejectedValueOnce(new Error('not found')); + + getPluginDetailsMock.mockResolvedValue({ ...fakePluginDetails, signatureType: PluginSignatureType.private }); + config.enableFrontendSandboxForPlugins = ['test-plugin']; + const result = await shouldLoadPluginInFrontendSandbox({ pluginId: 'test-plugin' }); + expect(result).toBe(true); + }); + + test('isPluginFrontendSandboxEligible returns false for plugins with internal signature', async () => { + getPluginSettingsMock.mockRejectedValueOnce(new Error('not found')); + + getPluginDetailsMock.mockResolvedValue({ + ...fakePluginDetails, + signatureType: PluginSignatureType.community, + signature: PluginSignatureStatus.internal, + }); + + const result = await isPluginFrontendSandboxEligible({ pluginId: 'test-plugin' }); + expect(result).toBe(false); + }); + }); + + describe('with getPluginSettings', () => { + test('shouldLoadPluginInFrontendSandbox returns false for Grafana-signed plugins', async () => { + // if getPluginDetails fails it fallsback to getPluginSettings + getPluginDetailsMock.mockRejectedValueOnce(new Error('not found')); + + getPluginSettingsMock.mockResolvedValue({ ...fakePluginSettings, signatureType: PluginSignatureType.grafana }); + const result = await shouldLoadPluginInFrontendSandbox({ pluginId: 'test-plugin' }); + expect(result).toBe(false); + }); + + test('shouldLoadPluginInFrontendSandbox returns true for eligible plugins in the list', async () => { + // if getPluginDetails fails it fallsback to getPluginSettings + getPluginDetailsMock.mockRejectedValueOnce(new Error('not found')); + + getPluginSettingsMock.mockResolvedValue({ ...fakePluginSettings, signatureType: PluginSignatureType.community }); + config.enableFrontendSandboxForPlugins = ['test-plugin']; + const result = await shouldLoadPluginInFrontendSandbox({ pluginId: 'test-plugin' }); + expect(result).toBe(true); + }); + + test('isPluginFrontendSandboxEnabled returns false when plugin is not in the enabled list', async () => { + // if getPluginDetails fails it fallsback to getPluginSettings + getPluginDetailsMock.mockRejectedValueOnce(new Error('not found')); + + config.enableFrontendSandboxForPlugins = ['other-plugin']; + const result = await isPluginFrontendSandboxEnabled({ pluginId: 'test-plugin' }); + expect(result).toBe(false); + }); + + test('shouldLoadPluginInFrontendSandbox returns true for commercial plugins in the enabled list', async () => { + // if getPluginDetails fails it fallsback to getPluginSettings + getPluginDetailsMock.mockRejectedValueOnce(new Error('not found')); + + getPluginSettingsMock.mockResolvedValue({ ...fakePluginSettings, signatureType: PluginSignatureType.commercial }); + config.enableFrontendSandboxForPlugins = ['test-plugin']; + const result = await shouldLoadPluginInFrontendSandbox({ pluginId: 'test-plugin' }); + expect(result).toBe(true); + }); + + test('shouldLoadPluginInFrontendSandbox returns true for private plugins in the enabled list', async () => { + // if getPluginDetails fails it fallsback to getPluginSettings + getPluginDetailsMock.mockRejectedValueOnce(new Error('not found')); + + getPluginSettingsMock.mockResolvedValue({ ...fakePluginSettings, signatureType: PluginSignatureType.private }); + config.enableFrontendSandboxForPlugins = ['test-plugin']; + const result = await shouldLoadPluginInFrontendSandbox({ pluginId: 'test-plugin' }); + expect(result).toBe(true); + }); + + test('isPluginFrontendSandboxEligible returns false for plugins with internal signature', async () => { + // if getPluginDetails fails it fallsback to getPluginSettings + getPluginDetailsMock.mockRejectedValueOnce(new Error('not found')); + + getPluginSettingsMock.mockResolvedValue({ ...fakePluginSettings, signature: PluginSignatureStatus.internal }); + const result = await isPluginFrontendSandboxEligible({ pluginId: 'test-plugin' }); + expect(result).toBe(false); + }); }); }); diff --git a/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.ts b/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.ts index 9d94a3556e0..2dbef3be1e1 100644 --- a/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.ts +++ b/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.ts @@ -1,6 +1,7 @@ import { PluginSignatureType } from '@grafana/data'; import { config } from '@grafana/runtime'; +import { getPluginDetails } from '../admin/api'; import { getPluginSettings } from '../pluginSettings'; type SandboxEligibilityCheckParams = { @@ -61,18 +62,24 @@ export async function isPluginFrontendSandboxEligible({ return false; } + // grafana signature and internal plugins are not allowed in the sandbox + return isPluginSignatureEligibleForSandbox({ pluginId }); +} + +async function isPluginSignatureEligibleForSandbox({ pluginId }: SandboxEligibilityCheckParams): Promise { try { - // don't run grafana-signed plugins in sandbox + // this can fail if we are trying to fetch settings of a non-installed plugin const pluginMeta = await getPluginSettings(pluginId, { showErrorAlert: false }); - if (pluginMeta.signatureType === PluginSignatureType.grafana || pluginMeta.signature === 'internal') { + return pluginMeta.signatureType !== PluginSignatureType.grafana && pluginMeta.signature !== 'internal'; + } catch (e) { + try { + //this can fail if gcom is not accesible + const details = await getPluginDetails(pluginId); + return details.signatureType !== PluginSignatureType.grafana && details.signature !== 'internal'; + } catch (e) { return false; } - } catch (e) { - // this can fail if we are trying to fetch settings of a non-installed plugin - return false; } - - return true; } /** diff --git a/public/app/features/preferences/api/user/baseAPI.ts b/public/app/features/preferences/api/user/baseAPI.ts index b1b75e7b036..33316419f6e 100644 --- a/public/app/features/preferences/api/user/baseAPI.ts +++ b/public/app/features/preferences/api/user/baseAPI.ts @@ -1,36 +1,9 @@ -import { BaseQueryFn, createApi } from '@reduxjs/toolkit/query/react'; -import { lastValueFrom } from 'rxjs'; +import { createApi } from '@reduxjs/toolkit/query/react'; -import { BackendSrvRequest, getBackendSrv } from '@grafana/runtime'; - -interface RequestOptions extends BackendSrvRequest { - manageError?: (err: unknown) => { error: unknown }; - showErrorAlert?: boolean; - body?: BackendSrvRequest['data']; -} - -function createBackendSrvBaseQuery({ baseURL }: { baseURL: string }): BaseQueryFn { - async function backendSrvBaseQuery(requestOptions: RequestOptions) { - try { - const { data: responseData, ...meta } = await lastValueFrom( - getBackendSrv().fetch({ - ...requestOptions, - url: baseURL + requestOptions.url, - showErrorAlert: requestOptions.showErrorAlert, - data: requestOptions.body, - }) - ); - return { data: responseData, meta }; - } catch (error) { - return requestOptions.manageError ? requestOptions.manageError(error) : { error }; - } - } - - return backendSrvBaseQuery; -} +import { createBaseQuery } from 'app/api/createBaseQuery'; export const baseAPI = createApi({ reducerPath: 'userPreferencesAPI', - baseQuery: createBackendSrvBaseQuery({ baseURL: '/api' }), + baseQuery: createBaseQuery({ baseURL: '/api' }), endpoints: () => ({}), }); diff --git a/public/app/features/preferences/api/user/endpoints.gen.ts b/public/app/features/preferences/api/user/endpoints.gen.ts index 4d61e519280..3dbe2470217 100644 --- a/public/app/features/preferences/api/user/endpoints.gen.ts +++ b/public/app/features/preferences/api/user/endpoints.gen.ts @@ -26,38 +26,32 @@ export type UpdateUserPreferencesApiResponse = export type UpdateUserPreferencesApiArg = { updatePrefsCmd: UpdatePrefsCmd; }; -export type CookiePreferencesDefinesModelForCookiePreferences = { - analytics?: { - [key: string]: any; - }; - functional?: { - [key: string]: any; - }; - performance?: { - [key: string]: any; - }; +export type CookiePreferences = { + analytics?: any; + functional?: any; + performance?: any; }; -export type NavbarPreferenceDefinesModelForNavbarPreference = { +export type NavbarPreference = { bookmarkUrls?: string[]; }; -export type QueryHistoryPreferenceDefinesModelForQueryHistoryPreference = { - /** HomeTab one of: '' | 'query' | 'starred'; */ +export type QueryHistoryPreference = { + /** one of: '' | 'query' | 'starred'; */ homeTab?: string; }; export type Preferences = { - cookiePreferences?: CookiePreferencesDefinesModelForCookiePreferences; + cookiePreferences?: CookiePreferences; /** UID for the home dashboard */ homeDashboardUID?: string; /** Selected language (beta) */ language?: string; - navbar?: NavbarPreferenceDefinesModelForNavbarPreference; - queryHistory?: QueryHistoryPreferenceDefinesModelForQueryHistoryPreference; - /** Theme light, dark, empty is default */ + navbar?: NavbarPreference; + queryHistory?: QueryHistoryPreference; + /** light, dark, empty is default */ theme?: string; /** The timezone selection TODO: this should use the timezone defined in common */ timezone?: string; - /** WeekStart day of the week (sunday, monday, etc) */ + /** day of the week (sunday, monday, etc) */ weekStart?: string; }; export type ErrorResponseBody = { @@ -80,8 +74,8 @@ export type PatchPrefsCmd = { homeDashboardId?: number; homeDashboardUID?: string; language?: string; - navbar?: NavbarPreferenceDefinesModelForNavbarPreference; - queryHistory?: QueryHistoryPreferenceDefinesModelForQueryHistoryPreference; + navbar?: NavbarPreference; + queryHistory?: QueryHistoryPreference; theme?: 'light' | 'dark'; timezone?: 'utc' | 'browser'; weekStart?: string; @@ -92,8 +86,8 @@ export type UpdatePrefsCmd = { homeDashboardId?: number; homeDashboardUID?: string; language?: string; - navbar?: NavbarPreferenceDefinesModelForNavbarPreference; - queryHistory?: QueryHistoryPreferenceDefinesModelForQueryHistoryPreference; + navbar?: NavbarPreference; + queryHistory?: QueryHistoryPreference; theme?: 'light' | 'dark' | 'system'; timezone?: 'utc' | 'browser'; weekStart?: string; diff --git a/public/app/features/profile/UserOrganizations.tsx b/public/app/features/profile/UserOrganizations.tsx index 8e0455e618e..c378e49b680 100644 --- a/public/app/features/profile/UserOrganizations.tsx +++ b/public/app/features/profile/UserOrganizations.tsx @@ -1,7 +1,7 @@ import { PureComponent } from 'react'; import { selectors } from '@grafana/e2e-selectors'; -import { Button, LoadingPlaceholder } from '@grafana/ui'; +import { Button, LoadingPlaceholder, ScrollContainer } from '@grafana/ui'; import { Trans } from 'app/core/internationalization'; import { UserDTO, UserOrg } from 'app/types'; @@ -30,46 +30,48 @@ export class UserOrganizations extends PureComponent { Organizations -
- {dateTimeFormatTimeAgo(version.createdAt)} + {dateTimeFormatTimeAgo(version.updatedAt || version.createdAt)} {version.grafanaDependency || 'N/A'}
- - - - - - - - {orgs.map((org: UserOrg, index) => { - return ( - - - - - - ); - })} - -
- Name - - Role - -
{org.name}{org.role} - {org.orgId === user?.orgId ? ( - - ) : ( - - )} -
+ + + + + + + + + + {orgs.map((org: UserOrg, index) => { + return ( + + + + + + ); + })} + +
+ Name + + Role + +
{org.name}{org.role} + {org.orgId === user?.orgId ? ( + + ) : ( + + )} +
+
); } diff --git a/public/app/features/profile/UserSessions.tsx b/public/app/features/profile/UserSessions.tsx index a6ed2d98543..64138ebe1d6 100644 --- a/public/app/features/profile/UserSessions.tsx +++ b/public/app/features/profile/UserSessions.tsx @@ -1,11 +1,10 @@ import { css } from '@emotion/css'; -import { t } from 'i18next'; import { PureComponent } from 'react'; import { selectors } from '@grafana/e2e-selectors'; -import { Button, Icon, LoadingPlaceholder } from '@grafana/ui'; +import { Button, Icon, LoadingPlaceholder, ScrollContainer } from '@grafana/ui'; import { TagBadge } from 'app/core/components/TagFilter/TagBadge'; -import { Trans } from 'app/core/internationalization'; +import { t, Trans } from 'app/core/internationalization'; import { formatDate } from 'app/core/internationalization/dates'; import { UserSession } from 'app/types'; @@ -29,55 +28,57 @@ class UserSessions extends PureComponent { {sessions.length > 0 && ( <>

Sessions

- - - - - - - - - - - - - - {sessions.map((session: UserSession, index) => ( - - {session.isActive ? : } - - - - - + +
- Last seen - - Logged on - - IP address - - Browser & OS - - Identity Provider -
Now{session.seenAt}{formatDate(session.createdAt, { dateStyle: 'long' })}{session.clientIp} - {session.browser} on {session.os} {session.osVersion} - - {session.authModule && } - - -
+ + + + + + + + - ))} - -
+ Last seen + + Logged on + + IP address + + Browser & OS + + Identity Provider +
+ + + + {sessions.map((session: UserSession, index) => ( + + {session.isActive ? Now : {session.seenAt}} + {formatDate(session.createdAt, { dateStyle: 'long' })} + {session.clientIp} + + {session.browser} on {session.os} {session.osVersion} + + + {session.authModule && } + + + + + + ))} + + + )}
diff --git a/public/app/features/profile/state/reducers.ts b/public/app/features/profile/state/reducers.ts index 5098fa3670d..f4de9c34f0b 100644 --- a/public/app/features/profile/state/reducers.ts +++ b/public/app/features/profile/state/reducers.ts @@ -2,6 +2,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { isEmpty, isString, set } from 'lodash'; import { dateTimeFormatTimeAgo, setWeekStart, TimeZone } from '@grafana/data'; +import { getWeekStart, WeekStart } from '@grafana/ui'; import config from 'app/core/config'; import { contextSrv } from 'app/core/core'; import { Team, ThunkResult, UserDTO, UserOrg, UserSession } from 'app/types'; @@ -116,10 +117,10 @@ export const updateTimeZoneForSession = (timeZone: TimeZone): ThunkResult }; }; -export const updateWeekStartForSession = (weekStart: string): ThunkResult => { +export const updateWeekStartForSession = (weekStart?: WeekStart): ThunkResult => { return async (dispatch) => { - if (!isString(weekStart) || isEmpty(weekStart)) { - weekStart = config?.bootData?.user?.weekStart; + if (!weekStart) { + weekStart = getWeekStart(); } set(contextSrv, 'user.weekStart', weekStart); diff --git a/public/app/features/query-library/api/endpoints.gen.ts b/public/app/features/query-library/api/endpoints.gen.ts deleted file mode 100644 index 9d7a680ed24..00000000000 --- a/public/app/features/query-library/api/endpoints.gen.ts +++ /dev/null @@ -1,486 +0,0 @@ -import { queryLibraryApi as api } from './factory'; -export const addTagTypes = ['QueryTemplate'] as const; -const injectedRtkApi = api - .enhanceEndpoints({ - addTagTypes, - }) - .injectEndpoints({ - endpoints: (build) => ({ - listQueryTemplate: build.query({ - query: (queryArg) => ({ - url: `/apis/peakq.grafana.app/v0alpha1/namespaces/${queryArg['namespace']}/querytemplates`, - params: { - pretty: queryArg.pretty, - allowWatchBookmarks: queryArg.allowWatchBookmarks, - continue: queryArg['continue'], - fieldSelector: queryArg.fieldSelector, - labelSelector: queryArg.labelSelector, - limit: queryArg.limit, - resourceVersion: queryArg.resourceVersion, - resourceVersionMatch: queryArg.resourceVersionMatch, - sendInitialEvents: queryArg.sendInitialEvents, - timeoutSeconds: queryArg.timeoutSeconds, - watch: queryArg.watch, - }, - }), - providesTags: ['QueryTemplate'], - }), - createQueryTemplate: build.mutation({ - query: (queryArg) => ({ - url: `/apis/peakq.grafana.app/v0alpha1/namespaces/${queryArg['namespace']}/querytemplates`, - method: 'POST', - body: queryArg.comGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplate, - params: { - pretty: queryArg.pretty, - dryRun: queryArg.dryRun, - fieldManager: queryArg.fieldManager, - fieldValidation: queryArg.fieldValidation, - }, - }), - invalidatesTags: ['QueryTemplate'], - }), - deleteQueryTemplate: build.mutation({ - query: (queryArg) => ({ - url: `/apis/peakq.grafana.app/v0alpha1/namespaces/${queryArg['namespace']}/querytemplates/${queryArg.name}`, - method: 'DELETE', - body: queryArg.ioK8SApimachineryPkgApisMetaV1DeleteOptions, - params: { - pretty: queryArg.pretty, - dryRun: queryArg.dryRun, - gracePeriodSeconds: queryArg.gracePeriodSeconds, - ignoreStoreReadErrorWithClusterBreakingPotential: queryArg.ignoreStoreReadErrorWithClusterBreakingPotential, - orphanDependents: queryArg.orphanDependents, - propagationPolicy: queryArg.propagationPolicy, - }, - }), - invalidatesTags: ['QueryTemplate'], - }), - updateQueryTemplate: build.mutation({ - query: (queryArg) => ({ - url: `/apis/peakq.grafana.app/v0alpha1/namespaces/${queryArg['namespace']}/querytemplates/${queryArg.name}`, - method: 'PATCH', - body: queryArg.ioK8SApimachineryPkgApisMetaV1Patch, - params: { - pretty: queryArg.pretty, - dryRun: queryArg.dryRun, - fieldManager: queryArg.fieldManager, - fieldValidation: queryArg.fieldValidation, - force: queryArg.force, - }, - }), - invalidatesTags: ['QueryTemplate'], - }), - }), - overrideExisting: false, - }); -export { injectedRtkApi as generatedQueryLibraryApi }; -export type ListQueryTemplateApiResponse = - /** status 200 OK */ ComGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplateList; -export type ListQueryTemplateApiArg = { - /** object name and auth scope, such as for teams and projects */ - namespace: string; - /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ - pretty?: string; - /** allowWatchBookmarks requests watch events with type "BOOKMARK". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. */ - allowWatchBookmarks?: boolean; - /** The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the "next key". - - This field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications. */ - continue?: string; - /** A selector to restrict the list of returned objects by their fields. Defaults to everything. */ - fieldSelector?: string; - /** A selector to restrict the list of returned objects by their labels. Defaults to everything. */ - labelSelector?: string; - /** limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true. - - The server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned. */ - limit?: number; - /** resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details. - - Defaults to unset */ - resourceVersion?: string; - /** resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details. - - Defaults to unset */ - resourceVersionMatch?: string; - /** `sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic "Bookmark" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `"k8s.io/initial-events-end": "true"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched. - - When `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan - is interpreted as "data at least as new as the provided `resourceVersion`" - and the bookmark event is send when the state is synced - to a `resourceVersion` at least as fresh as the one provided by the ListOptions. - If `resourceVersion` is unset, this is interpreted as "consistent read" and the - bookmark event is send when the state is synced at least to the moment - when request started being processed. - - `resourceVersionMatch` set to any other value or unset - Invalid error is returned. - - Defaults to true if `resourceVersion=""` or `resourceVersion="0"` (for backward compatibility reasons) and to false otherwise. */ - sendInitialEvents?: boolean; - /** Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity. */ - timeoutSeconds?: number; - /** Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion. */ - watch?: boolean; -}; -export type CreateQueryTemplateApiResponse = /** status 200 OK */ - | ComGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplate - | /** status 201 Created */ ComGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplate - | /** status 202 Accepted */ ComGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplate; -export type CreateQueryTemplateApiArg = { - /** object name and auth scope, such as for teams and projects */ - namespace: string; - /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ - pretty?: string; - /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */ - dryRun?: string; - /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */ - fieldManager?: string; - /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */ - fieldValidation?: string; - comGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplate: ComGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplate; -}; -export type DeleteQueryTemplateApiResponse = /** status 200 OK */ - | IoK8SApimachineryPkgApisMetaV1Status - | /** status 202 Accepted */ IoK8SApimachineryPkgApisMetaV1Status; -export type DeleteQueryTemplateApiArg = { - /** name of the QueryTemplate */ - name: string; - /** object name and auth scope, such as for teams and projects */ - namespace: string; - /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ - pretty?: string; - /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */ - dryRun?: string; - /** The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately. */ - gracePeriodSeconds?: number; - /** if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it */ - ignoreStoreReadErrorWithClusterBreakingPotential?: boolean; - /** Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both. */ - orphanDependents?: boolean; - /** Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground. */ - propagationPolicy?: string; - ioK8SApimachineryPkgApisMetaV1DeleteOptions: IoK8SApimachineryPkgApisMetaV1DeleteOptions; -}; -export type UpdateQueryTemplateApiResponse = /** status 200 OK */ - | ComGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplate - | /** status 201 Created */ ComGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplate; -export type UpdateQueryTemplateApiArg = { - /** name of the QueryTemplate */ - name: string; - /** object name and auth scope, such as for teams and projects */ - namespace: string; - /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */ - pretty?: string; - /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */ - dryRun?: string; - /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch). */ - fieldManager?: string; - /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */ - fieldValidation?: string; - /** Force is going to "force" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests. */ - force?: boolean; - ioK8SApimachineryPkgApisMetaV1Patch: IoK8SApimachineryPkgApisMetaV1Patch; -}; -export type IoK8SApimachineryPkgApisMetaV1Time = string; -export type IoK8SApimachineryPkgApisMetaV1FieldsV1 = object; -export type IoK8SApimachineryPkgApisMetaV1ManagedFieldsEntry = { - /** APIVersion defines the version of this resource that this field set applies to. The format is "group/version" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted. */ - apiVersion?: string; - /** FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: "FieldsV1" */ - fieldsType?: string; - /** FieldsV1 holds the first JSON version format as described in the "FieldsV1" type. */ - fieldsV1?: IoK8SApimachineryPkgApisMetaV1FieldsV1; - /** Manager is an identifier of the workflow managing these fields. */ - manager?: string; - /** Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'. */ - operation?: string; - /** Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource. */ - subresource?: string; - /** Time is the timestamp of when the ManagedFields entry was added. The timestamp will also be updated if a field is added, the manager changes any of the owned fields value or removes a field. The timestamp does not update when a field is removed from the entry because another manager took it over. */ - time?: IoK8SApimachineryPkgApisMetaV1Time; -}; -export type IoK8SApimachineryPkgApisMetaV1OwnerReference = { - /** API version of the referent. */ - apiVersion: string; - /** If true, AND if the owner has the "foregroundDeletion" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs "delete" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned. */ - blockOwnerDeletion?: boolean; - /** If true, this reference points to the managing controller. */ - controller?: boolean; - /** Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */ - kind: string; - /** Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names */ - name: string; - /** UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids */ - uid: string; -}; -export type IoK8SApimachineryPkgApisMetaV1ObjectMeta = { - /** Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations */ - annotations?: { - [key: string]: string; - }; - /** CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. - - Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata */ - creationTimestamp?: IoK8SApimachineryPkgApisMetaV1Time; - /** Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only. */ - deletionGracePeriodSeconds?: number; - /** DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested. - - Populated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata */ - deletionTimestamp?: IoK8SApimachineryPkgApisMetaV1Time; - /** Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list. */ - finalizers?: string[]; - /** GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server. - - If this field is specified and the generated name exists, the server will return a 409. - - Applied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency */ - generateName?: string; - /** A sequence number representing a specific generation of the desired state. Populated by the system. Read-only. */ - generation?: number; - /** Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels */ - labels?: { - [key: string]: string; - }; - /** ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like "ci-cd". The set of fields is always in the version that the workflow used when modifying the object. */ - managedFields?: IoK8SApimachineryPkgApisMetaV1ManagedFieldsEntry[]; - /** Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names */ - name?: string; - /** Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty. - - Must be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces */ - namespace?: string; - /** List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller. */ - ownerReferences?: IoK8SApimachineryPkgApisMetaV1OwnerReference[]; - /** An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources. - - Populated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency */ - resourceVersion?: string; - /** Deprecated: selfLink is a legacy read-only field that is no longer populated by the system. */ - selfLink?: string; - /** UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations. - - Populated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids */ - uid?: string; -}; -export type ComGithubGrafanaGrafanaPluginSdkGoExperimentalApisDataV0Alpha1DataQuery = { - /** The datasource */ - datasource?: { - /** The apiserver version */ - apiVersion?: string; - /** The datasource plugin type */ - type: string; - /** Datasource UID (NOTE: name in k8s) */ - uid?: string; - }; - /** true if query is disabled (ie should not be returned to the dashboard) - NOTE: this does not always imply that the query should not be executed since - the results from a hidden query may be used as the input to other queries (SSE etc) */ - hide?: boolean; - /** Interval is the suggested duration between time points in a time series query. - NOTE: the values for intervalMs is not saved in the query model. It is typically calculated - from the interval required to fill a pixels in the visualization */ - intervalMs?: number; - /** MaxDataPoints is the maximum number of data points that should be returned from a time series query. - NOTE: the values for maxDataPoints is not saved in the query model. It is typically calculated - from the number of pixels visible in a visualization */ - maxDataPoints?: number; - /** QueryType is an optional identifier for the type of query. - It can be used to distinguish different types of queries. */ - queryType?: string; - /** RefID is the unique identifier of the query, set by the frontend call. */ - refId?: string; - /** Optionally define expected query result behavior */ - resultAssertions?: { - /** Maximum frame count */ - maxFrames?: number; - /** Type asserts that the frame matches a known type structure. - - - Possible enum values: - - `""` - - `"timeseries-wide"` - - `"timeseries-long"` - - `"timeseries-many"` - - `"timeseries-multi"` - - `"directory-listing"` - - `"table"` - - `"numeric-wide"` - - `"numeric-multi"` - - `"numeric-long"` - - `"log-lines"` */ - type?: - | '' - | 'timeseries-wide' - | 'timeseries-long' - | 'timeseries-many' - | 'timeseries-multi' - | 'directory-listing' - | 'table' - | 'numeric-wide' - | 'numeric-multi' - | 'numeric-long' - | 'log-lines'; - /** TypeVersion is the version of the Type property. Versions greater than 0.0 correspond to the dataplane - contract documentation https://grafana.github.io/dataplane/contract/. */ - typeVersion: number[]; - }; - /** TimeRange represents the query range - NOTE: unlike generic /ds/query, we can now send explicit time values in each query - NOTE: the values for timeRange are not saved in a dashboard, they are constructed on the fly */ - timeRange?: { - /** From is the start time of the query. */ - from: string; - /** To is the end time of the query. */ - to: string; - }; - [key: string]: any; -}; -export type ComGithubGrafanaGrafanaPkgApisQueryV0Alpha1TemplatePosition = { - /** End is the byte offset of the end of the variable. */ - end: number; - /** Start is the byte offset within TargetKey's property of the variable. It is the start location for replacements). */ - start: number; -}; -export type ComGithubGrafanaGrafanaPkgApisQueryV0Alpha1TemplateVariableReplacement = { - /** How values should be interpolated - - Possible enum values: - - `"csv"` Formats variables with multiple values as a comma-separated string. - - `"doublequote"` Formats single- and multi-valued variables into a comma-separated string - - `"json"` Formats variables with multiple values as a comma-separated string. - - `"pipe"` Formats variables with multiple values into a pipe-separated string. - - `"raw"` Formats variables with multiple values into comma-separated string. This is the default behavior when no format is specified - - `"singlequote"` Formats single- and multi-valued variables into a comma-separated string */ - format?: 'csv' | 'doublequote' | 'json' | 'pipe' | 'raw' | 'singlequote'; - /** Path is the location of the property within a target. The format for this is not figured out yet (Maybe JSONPath?). Idea: ["string", int, "string"] where int indicates array offset */ - path: string; - /** Positions is a list of where to perform the interpolation within targets during render. The first string is the Idx of the target as a string, since openAPI does not support ints as map keys */ - position?: ComGithubGrafanaGrafanaPkgApisQueryV0Alpha1TemplatePosition; -}; -export type ComGithubGrafanaGrafanaPkgApisQueryV0Alpha1TemplateTarget = { - /** DataType is the returned Dataplane type from the query. */ - dataType?: string; - /** Query target */ - properties: ComGithubGrafanaGrafanaPluginSdkGoExperimentalApisDataV0Alpha1DataQuery; - /** Variables that will be replaced in the query */ - variables: { - [key: string]: ComGithubGrafanaGrafanaPkgApisQueryV0Alpha1TemplateVariableReplacement[]; - }; -}; -export type ComGithubGrafanaGrafanaPkgApimachineryApisCommonV0Alpha1Unstructured = { - [key: string]: any; -}; -export type ComGithubGrafanaGrafanaPkgApisQueryV0Alpha1TemplateTemplateVariable = { - /** DefaultValue is the value to be used when there is no selected value during render. */ - defaultValues?: string[]; - /** Key is the name of the variable. */ - key: string; - /** ValueListDefinition is the object definition used by the FE to get a list of possible values to select for render. */ - valueListDefinition?: ComGithubGrafanaGrafanaPkgApimachineryApisCommonV0Alpha1Unstructured; -}; -export type ComGithubGrafanaGrafanaPkgApisQueryV0Alpha1TemplateQueryTemplate = { - /** Longer description for why it is interesting */ - description?: string; - /** Output variables */ - targets: ComGithubGrafanaGrafanaPkgApisQueryV0Alpha1TemplateTarget[]; - /** A display name */ - title?: string; - /** The variables that can be used to render */ - vars?: ComGithubGrafanaGrafanaPkgApisQueryV0Alpha1TemplateTemplateVariable[]; -}; -export type ComGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplate = { - /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */ - apiVersion?: string; - /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */ - kind?: string; - metadata?: IoK8SApimachineryPkgApisMetaV1ObjectMeta; - spec?: ComGithubGrafanaGrafanaPkgApisQueryV0Alpha1TemplateQueryTemplate; -}; -export type IoK8SApimachineryPkgApisMetaV1ListMeta = { - /** continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message. */ - continue?: string; - /** remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact. */ - remainingItemCount?: number; - /** String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency */ - resourceVersion?: string; - /** Deprecated: selfLink is a legacy read-only field that is no longer populated by the system. */ - selfLink?: string; -}; -export type ComGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplateList = { - /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */ - apiVersion?: string; - items?: ComGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplate[]; - /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */ - kind?: string; - metadata?: IoK8SApimachineryPkgApisMetaV1ListMeta; -}; -export type IoK8SApimachineryPkgApisMetaV1StatusCause = { - /** The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional. - - Examples: - "name" - the field "name" on the current resource - "items[0].name" - the field "name" on the first array entry in "items" */ - field?: string; - /** A human-readable description of the cause of the error. This field may be presented as-is to a reader. */ - message?: string; - /** A machine-readable description of the cause of the error. If this value is empty there is no information available. */ - reason?: string; -}; -export type IoK8SApimachineryPkgApisMetaV1StatusDetails = { - /** The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes. */ - causes?: IoK8SApimachineryPkgApisMetaV1StatusCause[]; - /** The group attribute of the resource associated with the status StatusReason. */ - group?: string; - /** The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */ - kind?: string; - /** The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described). */ - name?: string; - /** If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action. */ - retryAfterSeconds?: number; - /** UID of the resource. (when there is a single resource which can be described). More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids */ - uid?: string; -}; -export type IoK8SApimachineryPkgApisMetaV1Status = { - /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */ - apiVersion?: string; - /** Suggested HTTP return code for this status, 0 if not set. */ - code?: number; - /** Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type. */ - details?: IoK8SApimachineryPkgApisMetaV1StatusDetails; - /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */ - kind?: string; - /** A human-readable description of the status of this operation. */ - message?: string; - /** Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */ - metadata?: IoK8SApimachineryPkgApisMetaV1ListMeta; - /** A machine-readable description of why this operation is in the "Failure" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it. */ - reason?: string; - /** Status of the operation. One of: "Success" or "Failure". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status */ - status?: string; -}; -export type IoK8SApimachineryPkgApisMetaV1Preconditions = { - /** Specifies the target ResourceVersion */ - resourceVersion?: string; - /** Specifies the target UID. */ - uid?: string; -}; -export type IoK8SApimachineryPkgApisMetaV1DeleteOptions = { - /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */ - apiVersion?: string; - /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */ - dryRun?: string[]; - /** The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately. */ - gracePeriodSeconds?: number; - /** if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it */ - ignoreStoreReadErrorWithClusterBreakingPotential?: boolean; - /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */ - kind?: string; - /** Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both. */ - orphanDependents?: boolean; - /** Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned. */ - preconditions?: IoK8SApimachineryPkgApisMetaV1Preconditions; - /** Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground. */ - propagationPolicy?: string; -}; -export type IoK8SApimachineryPkgApisMetaV1Patch = object; diff --git a/public/app/features/query-library/api/factory.ts b/public/app/features/query-library/api/factory.ts deleted file mode 100644 index 00e6115db7d..00000000000 --- a/public/app/features/query-library/api/factory.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { createApi } from '@reduxjs/toolkit/query/react'; - -import { baseQuery } from './query'; - -// Currently, we are loading all query templates -// Organizations can have maximum of 1000 query templates -export const QUERY_LIBRARY_GET_LIMIT = 1000; - -export const queryLibraryApi = createApi({ - baseQuery, - endpoints: () => ({}), -}); diff --git a/public/app/features/query-library/api/mappers.ts b/public/app/features/query-library/api/mappers.ts deleted file mode 100644 index 01b3f870e2e..00000000000 --- a/public/app/features/query-library/api/mappers.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; - -import { AddQueryTemplateCommand, QueryTemplate } from '../types'; - -import { - ComGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplate, - ListQueryTemplateApiResponse, -} from './endpoints.gen'; -import { API_VERSION, QueryTemplateKinds } from './query'; -import { CREATED_BY_KEY } from './types'; - -export const convertDataQueryResponseToQueryTemplates = (result: ListQueryTemplateApiResponse): QueryTemplate[] => { - if (!result.items) { - return []; - } - return result.items.map((spec) => { - return { - uid: spec.metadata?.name ?? '', - title: spec.spec?.title ?? '', - targets: - spec.spec?.targets.map((target) => ({ - ...target.properties, - refId: target.properties.refId ?? '', - })) ?? [], - createdAtTimestamp: new Date(spec.metadata?.creationTimestamp ?? '').getTime(), - user: { - uid: spec.metadata?.annotations?.[CREATED_BY_KEY] ?? '', - }, - }; - }); -}; - -export const convertAddQueryTemplateCommandToDataQuerySpec = ( - addQueryTemplateCommand: AddQueryTemplateCommand -): ComGithubGrafanaGrafanaPkgApisPeakqV0Alpha1QueryTemplate => { - const { title, targets } = addQueryTemplateCommand; - return { - apiVersion: API_VERSION, - kind: QueryTemplateKinds.QueryTemplate, - metadata: { - /** - * Server will append to whatever is passed here, but just to be safe we generate a uuid - * More info https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#idempotency - */ - generateName: uuidv4(), - }, - spec: { - title: title, - vars: [], // TODO: Detect variables in #86838 - targets: targets.map((dataQuery) => ({ - variables: {}, - properties: { - ...dataQuery, - datasource: { - ...dataQuery.datasource, - type: dataQuery.datasource?.type ?? '', - }, - }, - })), - }, - }; -}; diff --git a/public/app/features/query-library/api/mocks.ts b/public/app/features/query-library/api/mocks.ts deleted file mode 100644 index 0fb6d5fe8d5..00000000000 --- a/public/app/features/query-library/api/mocks.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { BASE_URL } from './query'; -import { getIdentityDisplayList } from './testdata/identityDisplayList'; -import { getTestQueryList } from './testdata/testQueryList'; - -export const mockData = { - all: { - url: BASE_URL, - response: getTestQueryList(), - }, - identityDisplay: { - response: getIdentityDisplayList(), - }, -}; diff --git a/public/app/features/query-library/api/query.ts b/public/app/features/query-library/api/query.ts deleted file mode 100644 index 1de6d303408..00000000000 --- a/public/app/features/query-library/api/query.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { BaseQueryFn } from '@reduxjs/toolkit/query/react'; -import { lastValueFrom } from 'rxjs'; - -import { config } from '@grafana/runtime'; -import { BackendSrvRequest, getBackendSrv, isFetchError } from '@grafana/runtime/src/services/backendSrv'; - -/** - * @alpha - */ -export const API_VERSION = 'peakq.grafana.app/v0alpha1'; - -/** - * @alpha - */ -export enum QueryTemplateKinds { - QueryTemplate = 'QueryTemplate', -} - -export const getK8sNamespace = () => config.namespace; - -/** - * Query Library is an experimental feature. API (including the URL path) will likely change. - * - * @alpha - */ -export const BASE_URL = `/apis/${API_VERSION}/namespaces/${getK8sNamespace()}/querytemplates`; - -interface QueryLibraryBackendRequest extends BackendSrvRequest { - body?: BackendSrvRequest['data']; -} - -export const baseQuery: BaseQueryFn = async (requestOptions) => { - try { - const responseObservable = getBackendSrv().fetch({ - url: `${requestOptions.url ?? ''}`, - showErrorAlert: true, - method: requestOptions.method || 'GET', - data: requestOptions.body, - params: requestOptions.params, - headers: { ...requestOptions.headers }, - }); - return await lastValueFrom(responseObservable); - } catch (error) { - if (isFetchError(error)) { - return { error: new Error(error.data.message) }; - } else if (error instanceof Error) { - return { error }; - } else { - return { error: new Error('Unknown error') }; - } - } -}; diff --git a/public/app/features/query-library/api/scripts/generate-rtk-apis.ts b/public/app/features/query-library/api/scripts/generate-rtk-apis.ts deleted file mode 100644 index 8b1c90b60c6..00000000000 --- a/public/app/features/query-library/api/scripts/generate-rtk-apis.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * To generate query library k8s APIs, run: - * `npx rtk-query-codegen-openapi ./public/app/features/query-library/api/scripts/generate-rtk-apis.ts` from the root of the repo - */ - -import { ConfigFile } from '@rtk-query/codegen-openapi'; -import { accessSync } from 'fs'; - -const schemaFile = '../../../../../../data/query-library/openapi.json'; - -try { - // Check we have the OpenAPI before generating query library RTK APIs, - // as this is currently a manual process - accessSync(schemaFile); -} catch (e) { - console.error('\nCould not find OpenAPI definition.\n'); - console.error( - 'Please visit /openapi/v3/apis/peakq.grafana.app/v0alpha1 and save the OpenAPI definition to data/query-library/openapi.json\n' - ); - throw e; -} - -const config: ConfigFile = { - schemaFile, - apiFile: '', - tag: true, - outputFiles: { - '../endpoints.gen.ts': { - apiFile: '../factory.ts', - apiImport: 'queryLibraryApi', - filterEndpoints: ['listQueryTemplate', 'createQueryTemplate', 'deleteQueryTemplate', 'updateQueryTemplate'], - exportName: 'generatedQueryLibraryApi', - flattenArg: false, - }, - }, -}; - -export default config; diff --git a/public/app/features/query-library/api/types.ts b/public/app/features/query-library/api/types.ts deleted file mode 100644 index e8485f22536..00000000000 --- a/public/app/features/query-library/api/types.ts +++ /dev/null @@ -1,26 +0,0 @@ -// pkg/apis/iam/v0alpha1/types_display.go -export type UserDataQueryResponse = { - apiVersion: string; - kind: string; - metadata: { - selfLink: string; - resourceVersion: string; - continue: string; - remainingItemCount: number; - }; - display: UserSpecResponse[]; - keys: string[]; -}; - -// pkg/apis/iam/v0alpha1/types_display.go -export type UserSpecResponse = { - avatarUrl: string; - displayName: string; - identity: { - name: string; - type: string; - }; - internalId: number; -}; - -export const CREATED_BY_KEY = 'grafana.app/createdBy'; diff --git a/public/app/features/query-library/api/user.ts b/public/app/features/query-library/api/user.ts deleted file mode 100644 index 640b04aa1dd..00000000000 --- a/public/app/features/query-library/api/user.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { getBackendSrv } from '@grafana/runtime'; - -import { getK8sNamespace } from './query'; -import { UserDataQueryResponse } from './types'; - -/** - * @alpha - */ -export const API_VERSION = 'iam.grafana.app/v0alpha1'; - -/** - * @alpha - */ -const BASE_URL = `apis/${API_VERSION}/namespaces/${getK8sNamespace()}/display`; - -export async function getUserInfo(url?: string): Promise { - const userInfo = await getBackendSrv().get(`${BASE_URL}${url}`); - return userInfo; -} diff --git a/public/app/features/query-library/index.ts b/public/app/features/query-library/index.ts deleted file mode 100644 index f81a701e632..00000000000 --- a/public/app/features/query-library/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** - * This is a temporary place for Query Library API and data types. - * To be exposed via grafana-runtime/data in the future. - * - * Query Library is an experimental feature, the API and components are subject to change - * - * @alpha - */ - -import { config } from '@grafana/runtime'; - -import { generatedQueryLibraryApi } from './api/endpoints.gen'; -import { QUERY_LIBRARY_GET_LIMIT } from './api/factory'; -import { mockData } from './api/mocks'; - -export const { - useCreateQueryTemplateMutation, - useDeleteQueryTemplateMutation, - useListQueryTemplateQuery, - useUpdateQueryTemplateMutation, -} = generatedQueryLibraryApi.enhanceEndpoints({ - endpoints: { - // Need to mutate the generated query to force query limit - listQueryTemplate: (endpointDefinition) => { - const originalQuery = endpointDefinition.query; - if (originalQuery) { - endpointDefinition.query = (requestOptions) => - originalQuery({ - ...requestOptions, - limit: QUERY_LIBRARY_GET_LIMIT, - }); - } - }, - // Need to mutate the generated query to set the Content-Type header correctly - updateQueryTemplate: (endpointDefinition) => { - const originalQuery = endpointDefinition.query; - if (originalQuery) { - endpointDefinition.query = (requestOptions) => ({ - ...originalQuery(requestOptions), - headers: { - 'Content-Type': 'application/merge-patch+json', - }, - }); - } - }, - }, -}); - -export function isQueryLibraryEnabled() { - return config.featureToggles.queryLibrary; -} - -export const QueryLibraryMocks = { - data: mockData.all, -}; - -export const IdentityServiceMocks = { - data: mockData.identityDisplay, -}; diff --git a/public/app/features/query-library/types.ts b/public/app/features/query-library/types.ts deleted file mode 100644 index 30a6410c520..00000000000 --- a/public/app/features/query-library/types.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { DataQuery } from '@grafana/schema'; - -export type DataQueryTarget = { - variables: object; // TODO: Detect variables in #86838 - properties: DataQuery; -}; - -export type DataQuerySpec = { - title: string; - vars: object[]; // TODO: Detect variables in #86838 - targets: DataQueryTarget[]; -}; - -export type DataQueryPartialSpec = Partial; - -export type QueryTemplate = { - uid: string; - title: string; - targets: DataQuery[]; - createdAtTimestamp: number; - user?: User; -}; - -export type AddQueryTemplateCommand = { - title: string; - targets: DataQuery[]; -}; - -export type EditQueryTemplateCommand = { - uid: string; - partialSpec: DataQueryPartialSpec; -}; - -export type DeleteQueryTemplateCommand = { - uid: string; -}; - -export type User = { - uid: string; - displayName?: string; - avatarUrl?: string; -}; diff --git a/public/app/features/query/components/QueryEditorRow.test.tsx b/public/app/features/query/components/QueryEditorRow.test.tsx index ae1bf9acc33..350d98bf11d 100644 --- a/public/app/features/query/components/QueryEditorRow.test.tsx +++ b/public/app/features/query/components/QueryEditorRow.test.tsx @@ -354,6 +354,7 @@ describe('QueryEditorRow', () => { onChange: jest.fn(), onRemoveQuery: jest.fn(), index: 0, + range: { from: dateTime(), to: dateTime(), raw: { from: 'now-1d', to: 'now' } }, }); it('should display error message in corresponding panel', async () => { const data = { diff --git a/public/app/features/query/components/QueryEditorRow.tsx b/public/app/features/query/components/QueryEditorRow.tsx index 7094901511f..46093ff35ba 100644 --- a/public/app/features/query/components/QueryEditorRow.tsx +++ b/public/app/features/query/components/QueryEditorRow.tsx @@ -8,7 +8,6 @@ import { PureComponent, ReactNode } from 'react'; // Utils & Services import { CoreApp, - DataQuery, DataSourceApi, DataSourceInstanceSettings, DataSourcePluginContextProvider, @@ -25,6 +24,7 @@ import { } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { AngularComponent, getAngularLoader, getDataSourceSrv, reportInteraction } from '@grafana/runtime'; +import { DataQuery } from '@grafana/schema'; import { Badge, ErrorBoundaryAlert } from '@grafana/ui'; import { OperationRowHelp } from 'app/core/components/QueryOperationRow/OperationRowHelp'; import { @@ -40,6 +40,8 @@ import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { PanelModel } from 'app/features/dashboard/state/PanelModel'; +import { useQueryLibraryContext } from '../../explore/QueryLibrary/QueryLibraryContext'; + import { QueryActionComponent, RowActionComponents } from './QueryActionComponent'; import { QueryEditorRowHeader } from './QueryEditorRowHeader'; import { QueryErrorAlert } from './QueryErrorAlert'; @@ -61,9 +63,9 @@ export interface Props { visualization?: ReactNode; hideHideQueryButton?: boolean; app?: CoreApp; + range: TimeRange; history?: Array>; eventBus?: EventBusExtended; - alerting?: boolean; hideActionButtons?: boolean; onQueryCopied?: () => void; onQueryRemoved?: () => void; @@ -271,7 +273,7 @@ export class QueryEditorRow extends PureComponent { - const { query, onChange, queries, onRunQuery, onAddQuery, app = CoreApp.PanelEditor, history } = this.props; + const { query, onChange, queries, onRunQuery, onAddQuery, range, app = CoreApp.PanelEditor, history } = this.props; const { datasource, data } = this.state; if (this.isWaitingForDatasourceToLoad()) { @@ -296,7 +298,7 @@ export class QueryEditorRow extends PureComponent extends PureComponent )} {this.renderExtraActions()} + extends PureComponent { - const { alerting, query, dataSource, onChangeDataSource, onChange, queries, renderHeaderExtras, hideRefId } = - this.props; + const { app, query, dataSource, onChangeDataSource, onChange, queries, renderHeaderExtras, hideRefId } = this.props; return ( extends PureComponent ); @@ -656,3 +658,9 @@ export function filterPanelDataToQuery(data: PanelData, refId: string): PanelDat timeRange, }; } + +// Will render anything only if query library is enabled +function MaybeQueryLibrarySaveButton(props: { query: DataQuery }) { + const { renderSaveQueryButton } = useQueryLibraryContext(); + return renderSaveQueryButton(props.query); +} diff --git a/public/app/features/query/components/QueryEditorRows.tsx b/public/app/features/query/components/QueryEditorRows.tsx index 187ea5df7a5..3d87a646ab6 100644 --- a/public/app/features/query/components/QueryEditorRows.tsx +++ b/public/app/features/query/components/QueryEditorRows.tsx @@ -11,6 +11,7 @@ import { getDataSourceRef, } from '@grafana/data'; import { getDataSourceSrv, reportInteraction } from '@grafana/runtime'; +import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { QueryEditorRow } from './QueryEditorRow'; @@ -175,6 +176,7 @@ export class QueryEditorRows extends PureComponent { onQueryToggled={onQueryToggled} queries={queries} app={app} + range={getTimeSrv().timeRange()} history={history} eventBus={eventBus} /> diff --git a/public/app/features/scopes/internal/api.ts b/public/app/features/scopes/internal/api.ts index b1f1ceaf869..1b0dae4cce9 100644 --- a/public/app/features/scopes/internal/api.ts +++ b/public/app/features/scopes/internal/api.ts @@ -1,13 +1,15 @@ import { Scope, ScopeDashboardBinding, ScopeNode, ScopeSpec } from '@grafana/data'; -import { config, getBackendSrv } from '@grafana/runtime'; +import { getBackendSrv } from '@grafana/runtime'; import { ScopedResourceClient } from 'app/features/apiserver/client'; +import { getAPINamespace } from '../../../api/utils'; + import { NodeReason, NodesMap, SelectedScope, TreeScope } from './types'; import { getBasicScope, mergeScopes } from './utils'; const group = 'scope.grafana.app'; const version = 'v0alpha1'; -const namespace = config.namespace ?? 'default'; +const namespace = getAPINamespace(); const nodesEndpoint = `/apis/${group}/${version}/namespaces/${namespace}/find/scope_node_children`; const dashboardsEndpoint = `/apis/${group}/${version}/namespaces/${namespace}/find/scope_dashboard_bindings`; diff --git a/public/app/features/search/page/components/columns.tsx b/public/app/features/search/page/components/columns.tsx index 05c4b2e6792..3f982658dd0 100644 --- a/public/app/features/search/page/components/columns.tsx +++ b/public/app/features/search/page/components/columns.tsx @@ -194,17 +194,34 @@ export const generateColumns = ( if (!info && p === 'general') { info = { kind: 'folder', url: '/dashboards', name: 'Dashboards' }; } - return info ? ( - - - - {info.name} - - - ) : ( - {p} - ); + if (info) { + const content = ( + <> + + + + {info.name} + + + ); + + if (info.url) { + return ( + + {content} + + ); + } + + return ( +
+ {content} +
+ ); + } + + return {p}; })}
)} diff --git a/public/app/features/search/service/unified.test.ts b/public/app/features/search/service/unified.test.ts new file mode 100644 index 00000000000..f013ca91ca7 --- /dev/null +++ b/public/app/features/search/service/unified.test.ts @@ -0,0 +1,200 @@ +import { BackendSrv } from '@grafana/runtime'; + +import { GrafanaSearcher, SearchQuery } from './types'; +import { toDashboardResults, SearchHit, SearchAPIResponse, UnifiedSearcher } from './unified'; + +beforeEach(() => { + jest.clearAllMocks(); +}); + +const mockResults: SearchAPIResponse = { + hits: [], + totalHits: 0, +}; + +const mockFolders: SearchAPIResponse = { + hits: [], + totalHits: 0, +}; + +const mockFallbackSearcher = { + search: jest.fn(), +} as unknown as GrafanaSearcher; + +const getResponse = (uri: string) => { + if (uri.endsWith('?type=folders')) { + return Promise.resolve(mockFolders); + } + return Promise.resolve(mockResults); +}; + +const mockSearcher = { + search: (uri: string) => getResponse(uri), +}; + +jest.mock('@grafana/runtime', () => { + const originalRuntime = jest.requireActual('@grafana/runtime'); + return { + ...originalRuntime, + getBackendSrv: () => + ({ + get: (uri: string) => mockSearcher.search(uri), + }) as unknown as BackendSrv, + }; +}); + +describe('Unified Storage Searcher', () => { + it('should perform search with basic query', async () => { + mockFolders.hits = [ + { + name: 'folder1', + title: 'Folder 1', + resource: 'folders', + } as SearchHit, + ]; + mockResults.hits = [ + { + name: 'dashboard1', + title: 'Dashboard 1', + resource: 'dashboards', + folder: 'folder1', + } as SearchHit, + ]; + + const query: SearchQuery = { + query: 'test', + limit: 50, + }; + + const searcher = new UnifiedSearcher(mockFallbackSearcher); + + const response = await searcher.search(query); + + expect(response.view.length).toBe(1); + expect(response.view.get(0).title).toBe('Dashboard 1'); + + const df = response.view.dataFrame; + const locationInfo = df.meta?.custom?.locationInfo; + expect(locationInfo).toBeDefined(); + expect(locationInfo?.folder1.name).toBe('Folder 1'); + }); + + it('should perform search and sync folders with missing folder', async () => { + const mockFolders = { + hits: [ + { + name: 'folder2', + title: 'Folder 2', + resource: 'folders', + } as SearchHit, + ], + totalHits: 1, + }; + + const mockResults = { + hits: [ + { + name: 'db1', + title: 'DB 1', + resource: 'dashboards', + folder: 'folder1', + } as SearchHit, + { + name: 'db2', + title: 'DB 2', + resource: 'dashboards', + folder: 'folder2', + } as SearchHit, + ], + totalHits: 2, + }; + + jest + .spyOn(mockSearcher, 'search') + .mockResolvedValueOnce(mockFolders) + .mockResolvedValueOnce(mockResults) + .mockResolvedValueOnce(mockFolders); + + const query: SearchQuery = { + query: 'test', + limit: 50, + }; + + const searcher = new UnifiedSearcher(mockFallbackSearcher); + + const response = await searcher.search(query); + + expect(response.view.length).toBe(2); + expect(response.view.get(0).title).toBe('DB 1'); + expect(response.view.get(0).folder).toBe('sharedwithme'); + expect(response.view.get(1).title).toBe('DB 2'); + + const df = response.view.dataFrame; + const locationInfo = df.meta?.custom?.locationInfo; + expect(locationInfo).toBeDefined(); + expect(locationInfo?.folder2.name).toBe('Folder 2'); + expect(mockSearcher.search).toHaveBeenCalledTimes(3); + }); + + it('can create dashboard search results and set meta sortBy so column is added for sprinkles sort field', () => { + const mockHits: SearchHit[] = [ + { + resource: 'dashboard', + name: 'Main Dashboard', + title: 'Main Dashboard Title', + location: '/dashboards/1', + folder: 'General', + tags: ['monitoring', 'performance'], + field: { errors_today: 1 }, + url: '/dashboards/1/main-dashboard-title', + }, + { + resource: 'dashboard', + name: 'Main Dashboard', + title: 'Main Dashboard Title', + location: '/dashboards/1', + folder: 'General', + tags: ['monitoring', 'performance'], + field: { errors_today: 2 }, + url: '/dashboards/1/main-dashboard-title', + }, + ]; + + const mockResponse: SearchAPIResponse = { + totalHits: 2, + hits: mockHits, + facets: {}, + }; + const results = toDashboardResults(mockResponse, 'errors_today'); + + expect(results.length).toBe(2); + const sprinklesField = results.fields[10]; + expect(sprinklesField.name).toBe('errors_today'); + expect(sprinklesField.values).toEqual([1, 2]); // this also tests the hits original order is preserved + expect(results.meta?.custom?.sortBy).toBe('errors_today'); + }); + + it('will trim "-" from the sort field name', () => { + const mockHits: SearchHit[] = [ + { + resource: 'dashboard', + name: 'Main Dashboard', + title: 'Main Dashboard Title', + location: '/dashboards/1', + folder: 'General', + tags: ['monitoring', 'performance'], + field: { errors_today: 1 }, + url: '/dashboards/1/main-dashboard-title', + }, + ]; + + const mockResponse: SearchAPIResponse = { + totalHits: 0, + hits: mockHits, + facets: {}, + }; + const results = toDashboardResults(mockResponse, '-errors_today'); + + expect(results.meta?.custom?.sortBy).toBe('errors_today'); + }); +}); diff --git a/public/app/features/search/service/unified.ts b/public/app/features/search/service/unified.ts index 9e379873cc3..2746ca81748 100644 --- a/public/app/features/search/service/unified.ts +++ b/public/app/features/search/service/unified.ts @@ -3,6 +3,9 @@ import { isEmpty } from 'lodash'; import { DataFrame, DataFrameView, getDisplayProcessor, SelectableValue, toDataFrame } from '@grafana/data'; import { config, getBackendSrv } from '@grafana/runtime'; import { TermCount } from 'app/core/components/TagFilter/TagFilter'; +import kbn from 'app/core/utils/kbn'; + +import { getAPINamespace } from '../../../api/utils'; import { DashboardQueryResult, @@ -18,9 +21,9 @@ import { replaceCurrentFolderQuery } from './utils'; // and that it can not serve any search requests. We are temporarily using the old SQL Search API as a fallback when that happens. const loadingFrameName = 'Loading'; -const searchURI = `apis/dashboard.grafana.app/v0alpha1/namespaces/${config.namespace}/search`; +const searchURI = `apis/dashboard.grafana.app/v0alpha1/namespaces/${getAPINamespace()}/search`; -type SearchHit = { +export type SearchHit = { resource: string; // dashboards | folders name: string; title: string; @@ -28,11 +31,13 @@ type SearchHit = { folder: string; tags: string[]; + field: Record; // extra fields from the backend - sort fields included here as well + // calculated in the frontend url: string; }; -type SearchAPIResponse = { +export type SearchAPIResponse = { totalHits: number; hits: SearchHit[]; facets?: { @@ -97,10 +102,6 @@ export class UnifiedSearcher implements GrafanaSearcher { opts.push({ value: `-${sf.name}`, label: `${sf.display} (most)` }); opts.push({ value: `${sf.name}`, label: `${sf.display} (least)` }); } - for (const sf of sortTimeFields) { - opts.push({ value: `-${sf.name}`, label: `${sf.display} (recent)` }); - opts.push({ value: `${sf.name}`, label: `${sf.display} (oldest)` }); - } } return Promise.resolve(opts); @@ -108,20 +109,14 @@ export class UnifiedSearcher implements GrafanaSearcher { async doSearchQuery(query: SearchQuery): Promise { const uri = await this.newRequest(query); - const rsp = await getBackendSrv().get(uri); + const rsp = await this.fetchResponse(uri); - const first = toDashboardResults(rsp); + const first = toDashboardResults(rsp, query.sort ?? ''); if (first.name === loadingFrameName) { return this.fallbackSearcher.search(query); } const meta = first.meta?.custom || ({} as SearchResultMeta); - const locationInfo = await this.locationInfo; - const hasMissing = rsp.hits.some((hit) => !locationInfo[hit.folder]); - if (hasMissing) { - // sync the location info ( folders ) - this.locationInfo = loadLocationInfo(); - } meta.locationInfo = await this.locationInfo; // Set the field name to a better display name @@ -137,15 +132,14 @@ export class UnifiedSearcher implements GrafanaSearcher { let loadMax = 0; let pending: Promise | undefined = undefined; const getNextPage = async () => { - // TODO: implement this correctly while (loadMax > view.dataFrame.length) { const offset = view.dataFrame.length; if (offset >= meta.count) { return; } const nextPageUrl = `${uri}&offset=${offset}`; - const resp = await getBackendSrv().get(nextPageUrl); - const frame = toDashboardResults(resp); + const resp = await this.fetchResponse(nextPageUrl); + const frame = toDashboardResults(resp, query.sort ?? ''); if (!frame) { console.log('no results', frame); return; @@ -194,6 +188,44 @@ export class UnifiedSearcher implements GrafanaSearcher { }; } + async fetchResponse(uri: string) { + const rsp = await getBackendSrv().get(uri); + const isFolderCacheStale = await this.isFolderCacheStale(rsp.hits); + if (!isFolderCacheStale) { + return rsp; + } + // sync the location info ( folders ) + this.locationInfo = loadLocationInfo(); + // recheck for missing folders + const hasMissing = await this.isFolderCacheStale(rsp.hits); + if (!hasMissing) { + return rsp; + } + + const locationInfo = await this.locationInfo; + const hits = rsp.hits.map((hit) => { + if (hit.folder === undefined) { + return { ...hit, location: 'general', folder: 'general' }; + } + + // this means user has permission to see this dashboard, but not the folder contents + if (locationInfo[hit.folder] === undefined) { + return { ...hit, location: 'sharedwithme', folder: 'sharedwithme' }; + } + + return hit; + }); + const totalHits = rsp.totalHits - (rsp.hits.length - hits.length); + return { ...rsp, hits, totalHits }; + } + + async isFolderCacheStale(hits: SearchHit[]): Promise { + const locationInfo = await this.locationInfo; + return hits.some((hit) => { + return hit.folder !== undefined && locationInfo[hit.folder] === undefined; + }); + } + private async newRequest(query: SearchQuery): Promise { query = await replaceCurrentFolderQuery(query); @@ -217,6 +249,9 @@ export class UnifiedSearcher implements GrafanaSearcher { if (query.sort) { const sort = query.sort.replace('_sort', '').replace('name', 'title'); uri += `&sort=${sort}`; + const sortField = sort.startsWith('-') ? sort.substring(1) : sort; + + uri += `&field=${sortField}`; // we want to the sort field to be included in the response } if (query.name?.length) { @@ -245,12 +280,6 @@ const sortFields = [ { name: 'errors_last_30_days', display: 'Errors 30 days' }, ]; -// Enterprise only time sort field values for dashboards -const sortTimeFields = [ - { name: 'created_at', display: 'Created time' }, - { name: 'updated_at', display: 'Updated time' }, -]; - function noDataResponse(): QueryResponse | PromiseLike { return { view: new DataFrameView({ length: 0, fields: [] }), @@ -271,15 +300,10 @@ function getSortFieldDisplayName(name: string) { return sf.display; } } - for (const sf of sortTimeFields) { - if (sf.name === name) { - return sf.display; - } - } return name; } -function toDashboardResults(rsp: SearchAPIResponse): DataFrame { +export function toDashboardResults(rsp: SearchAPIResponse, sort: string): DataFrame { const hits = rsp.hits; if (hits.length < 1) { return { fields: [], length: 0 }; @@ -290,15 +314,21 @@ function toDashboardResults(rsp: SearchAPIResponse): DataFrame { location = 'general'; } + // display null field values as "-" + const field = Object.fromEntries( + Object.entries(hit.field ?? {}).map(([key, value]) => [key, value == null ? '-' : value]) + ); + return { ...hit, uid: hit.name, - url: toURL(hit.resource, hit.name), + url: toURL(hit.resource, hit.name, hit.title), tags: hit.tags || [], folder: hit.folder || 'general', location, name: hit.title, // 🤯 FIXME hit.name is k8s name, eg grafana dashboards UID kind: hit.resource.substring(0, hit.resource.length - 1), // dashboard "kind" is not plural + ...field, }; }); const frame = toDataFrame(dashboardHits); @@ -308,6 +338,11 @@ function toDashboardResults(rsp: SearchAPIResponse): DataFrame { max_score: 1, }, }; + if (sort && frame.meta.custom) { + // trim the "-" from sort if it exists + frame.meta.custom.sortBy = sort.startsWith('-') ? sort.substring(1) : sort; + } + for (const field of frame.fields) { field.display = getDisplayProcessor({ field, theme: config.theme2 }); } @@ -325,12 +360,17 @@ async function loadLocationInfo(): Promise> { name: 'Dashboards', url: '/dashboards', }, // share location info with everyone + sharedwithme: { + kind: 'sharedwithme', + name: 'Shared with me', + url: '', + }, }; for (const hit of rsp.hits) { locationInfo[hit.name] = { name: hit.title, kind: 'folder', - url: toURL('folders', hit.name), + url: toURL('folders', hit.name, hit.title), }; } return locationInfo; @@ -338,9 +378,10 @@ async function loadLocationInfo(): Promise> { return rsp; } -function toURL(resource: string, name: string): string { +function toURL(resource: string, name: string, title: string): string { if (resource === 'folders') { return `/dashboards/f/${name}`; } - return `/d/${name}`; + const slug = kbn.slugifyForUrl(title); + return `/d/${name}/${slug}`; } diff --git a/public/app/features/search/service/utils.ts b/public/app/features/search/service/utils.ts index 6aeeaefba92..85c0168e480 100644 --- a/public/app/features/search/service/utils.ts +++ b/public/app/features/search/service/utils.ts @@ -49,6 +49,10 @@ export function getIconForKind(kind: string, isOpen?: boolean): IconName { return isOpen ? 'folder-open' : 'folder'; } + if (kind === 'sharedwithme') { + return 'users-alt'; + } + return 'question-circle'; } diff --git a/public/app/features/serviceaccounts/ServiceAccountCreatePage.tsx b/public/app/features/serviceaccounts/ServiceAccountCreatePage.tsx index fa24596b328..c9816d0fb1a 100644 --- a/public/app/features/serviceaccounts/ServiceAccountCreatePage.tsx +++ b/public/app/features/serviceaccounts/ServiceAccountCreatePage.tsx @@ -3,13 +3,13 @@ import { FormProvider, useForm } from 'react-hook-form'; import { config, getBackendSrv, locationService } from '@grafana/runtime'; import { Button, Input, Field, FieldSet } from '@grafana/ui'; -import { t, Trans } from '@grafana/ui/src/utils/i18n'; import { Form } from 'app/core/components/Form/Form'; import { Page } from 'app/core/components/Page/Page'; import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker'; import { fetchRoleOptions, updateUserRoles } from 'app/core/components/RolePicker/api'; import { RolePickerSelect } from 'app/core/components/RolePickerDrawer/RolePickerSelect'; import { contextSrv } from 'app/core/core'; +import { t, Trans } from 'app/core/internationalization'; import { AccessControlAction, OrgRole, Role, ServiceAccountCreateApiResponse, ServiceAccountDTO } from 'app/types'; import { OrgRolePicker } from '../admin/OrgRolePicker'; diff --git a/public/app/features/serviceaccounts/ServiceAccountsListPage.tsx b/public/app/features/serviceaccounts/ServiceAccountsListPage.tsx index 3c4bab9d3ac..5b8ede18148 100644 --- a/public/app/features/serviceaccounts/ServiceAccountsListPage.tsx +++ b/public/app/features/serviceaccounts/ServiceAccountsListPage.tsx @@ -1,9 +1,20 @@ +import { css } from '@emotion/css'; import pluralize from 'pluralize'; import { useEffect, useState } from 'react'; import { connect, ConnectedProps } from 'react-redux'; -import { OrgRole } from '@grafana/data'; -import { ConfirmModal, FilterInput, LinkButton, RadioButtonGroup, InlineField, EmptyState, Box } from '@grafana/ui'; +import { GrafanaTheme2, OrgRole } from '@grafana/data'; +import { + ConfirmModal, + FilterInput, + LinkButton, + RadioButtonGroup, + InlineField, + EmptyState, + Box, + Stack, + useStyles2, +} from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import config from 'app/core/config'; import { contextSrv } from 'app/core/core'; @@ -78,6 +89,7 @@ export const ServiceAccountsListPageUnconnected = ({ const [isDisableModalOpen, setIsDisableModalOpen] = useState(false); const [newToken, setNewToken] = useState(''); const [currentServiceAccount, setCurrentServiceAccount] = useState(null); + const styles = useStyles2(getStyles); useEffect(() => { fetchServiceAccounts({ withLoadingIndicator: true }); @@ -192,13 +204,13 @@ export const ServiceAccountsListPageUnconnected = ({ } > -
+ @@ -208,7 +220,7 @@ export const ServiceAccountsListPageUnconnected = ({ value={serviceAccountStateFilter} /> -
+ {!isLoading && !noServiceAccountsCreated && serviceAccounts.length === 0 && ( ({ + filterInput: css({ + maxWidth: theme.spacing(50), + }), +}); + const ServiceAccountsListPage = connector(ServiceAccountsListPageUnconnected); export default ServiceAccountsListPage; diff --git a/public/app/features/templating/template_srv.test.ts b/public/app/features/templating/template_srv.test.ts index 640143a289e..58da8d87c04 100644 --- a/public/app/features/templating/template_srv.test.ts +++ b/public/app/features/templating/template_srv.test.ts @@ -1,5 +1,5 @@ import { dateTime, QueryVariableModel, TimeRange, TypedVariableModel } from '@grafana/data'; -import { setDataSourceSrv, VariableInterpolation } from '@grafana/runtime'; +import { VariableInterpolation } from '@grafana/runtime'; import { ConstantVariable, CustomVariable, @@ -13,10 +13,11 @@ import { TestVariable, } from '@grafana/scenes'; import { VariableFormatID } from '@grafana/schema'; +import { setupDataSources } from 'app/features/alerting/unified/testSetup/datasources'; import { silenceConsoleOutput } from '../../../test/core/utils/silenceConsoleOutput'; import { initTemplateSrv } from '../../../test/helpers/initTemplateSrv'; -import { mockDataSource, MockDataSourceSrv } from '../alerting/unified/mocks'; +import { mockDataSource } from '../alerting/unified/mocks'; import { VariableAdapter, variableAdapters } from '../variables/adapters'; import { createAdHocVariableAdapter } from '../variables/adhoc/adapter'; import { createQueryVariableAdapter } from '../variables/query/adapter'; @@ -218,16 +219,14 @@ describe('templateSrv', () => { { type: 'adhoc', name: 'test', datasource: { uid: 'oogle' }, filters: [1] }, { type: 'adhoc', name: 'test2', datasource: { uid: '$ds' }, filters: [2] }, ]); - setDataSourceSrv( - new MockDataSourceSrv({ - oogle: mockDataSource({ - name: 'oogle', - uid: 'oogle', - }), - logstash: mockDataSource({ - name: 'logstash', - uid: 'logstash-id', - }), + setupDataSources( + mockDataSource({ + name: 'oogle', + uid: 'oogle', + }), + mockDataSource({ + name: 'logstash', + uid: 'logstash-id', }) ); }); diff --git a/public/app/features/trails/Breakdown/LabelBreakdownScene.tsx b/public/app/features/trails/Breakdown/LabelBreakdownScene.tsx index 8b9e014aa57..adc86af8f1c 100644 --- a/public/app/features/trails/Breakdown/LabelBreakdownScene.tsx +++ b/public/app/features/trails/Breakdown/LabelBreakdownScene.tsx @@ -32,8 +32,8 @@ import { Trans } from 'app/core/internationalization'; import { BreakdownLabelSelector } from '../BreakdownLabelSelector'; import { DataTrail } from '../DataTrail'; +import { PanelMenu } from '../Menu/PanelMenu'; import { MetricScene } from '../MetricScene'; -import { AddToExplorationButton } from '../MetricSelect/AddToExplorationsButton'; import { StatusWrapper } from '../StatusWrapper'; import { getAutoQueriesForMetric } from '../autoQuery/getAutoQueriesForMetric'; import { AutoQueryDef } from '../autoQuery/types'; @@ -478,10 +478,9 @@ export function buildAllLayout( ], }) ) - .setHeaderActions([ - new SelectLabelAction({ labelName: String(option.value) }), - new AddToExplorationButton({ labelName: String(option.value) }), - ]) + .setHeaderActions([new SelectLabelAction({ labelName: String(option.value) })]) + .setShowMenuAlways(true) + .setMenu(new PanelMenu({ labelName: String(option.value) })) .setUnit(unit) .setBehaviors([fixLegendForUnspecifiedLabelValueBehavior]) .build(); @@ -532,10 +531,9 @@ function buildNormalLayout( .setTitle(getLabelValue(frame)) .setData(new SceneDataNode({ data: { ...data, series: [frame] } })) .setColor({ mode: 'fixed', fixedColor: getColorByIndex(frameIndex) }) - .setHeaderActions([ - new AddToFiltersGraphAction({ frame }), - new AddToExplorationButton({ labelName: getLabelValue(frame) }), - ]) + .setHeaderActions([new AddToFiltersGraphAction({ frame })]) + .setShowMenuAlways(true) + .setMenu(new PanelMenu({ labelName: getLabelValue(frame) })) .setUnit(unit) .build(); diff --git a/public/app/features/trails/DataTrail.test.tsx b/public/app/features/trails/DataTrail.test.tsx index 4d0d0af86c4..ec3ebdb88bc 100644 --- a/public/app/features/trails/DataTrail.test.tsx +++ b/public/app/features/trails/DataTrail.test.tsx @@ -1,9 +1,10 @@ import { VariableHide } from '@grafana/data'; -import { locationService, setDataSourceSrv } from '@grafana/runtime'; +import { locationService } from '@grafana/runtime'; import { AdHocFiltersVariable, ConstantVariable, sceneGraph } from '@grafana/scenes'; +import { setupDataSources } from 'app/features/alerting/unified/testSetup/datasources'; import { DataSourceType } from 'app/features/alerting/unified/utils/datasource'; -import { MockDataSourceSrv, mockDataSource } from '../alerting/unified/mocks'; +import { mockDataSource } from '../alerting/unified/mocks'; import { activateFullSceneTree } from '../dashboard-scene/utils/test-utils'; import { DataTrail } from './DataTrail'; @@ -26,12 +27,11 @@ jest.mock('./otel/api', () => ({ describe('DataTrail', () => { beforeAll(() => { jest.spyOn(DataTrail.prototype, 'checkDataSourceForOTelResources').mockImplementation(() => Promise.resolve()); - setDataSourceSrv( - new MockDataSourceSrv({ - prom: mockDataSource({ - name: 'Prometheus', - type: DataSourceType.Prometheus, - }), + + setupDataSources( + mockDataSource({ + name: 'Prometheus', + type: DataSourceType.Prometheus, }) ); }); @@ -44,14 +44,6 @@ describe('DataTrail', () => { let trail: DataTrail; const preTrailUrl = '/'; - function getFilterVar() { - const variable = sceneGraph.lookupVariable(VAR_FILTERS, trail); - if (variable instanceof AdHocFiltersVariable) { - return variable; - } - throw new Error('getFilterVar failed'); - } - function getStepFilterVar(step: number) { const variable = trail.state.history.state.steps[step].trailState.$variables?.getByName(VAR_FILTERS); if (variable instanceof AdHocFiltersVariable) { @@ -226,12 +218,12 @@ describe('DataTrail', () => { }); it('Should have default empty filter', () => { - expect(getFilterVar().state.filters.length).toBe(0); + expect(getFilterVar(trail).state.filters.length).toBe(0); }); describe('And when changing the filter to zone=a', () => { beforeEach(() => { - getFilterVar().setState({ filters: [{ key: 'zone', operator: '=', value: 'a' }] }); + getFilterVar(trail).setState({ filters: [{ key: 'zone', operator: '=', value: 'a' }] }); }); it('should add history step', () => { @@ -247,8 +239,8 @@ describe('DataTrail', () => { }); it('Should have filter be updated to "zone=a"', () => { - expect(getFilterVar().state.filters[0].key).toBe('zone'); - expect(getFilterVar().state.filters[0].value).toBe('a'); + expect(getFilterVar(trail).state.filters[0].key).toBe('zone'); + expect(getFilterVar(trail).state.filters[0].value).toBe('a'); }); it('Previous history step should have empty filter', () => { @@ -274,12 +266,12 @@ describe('DataTrail', () => { }); it('Should have filters set back to empty', () => { - expect(getFilterVar().state.filters.length).toBe(0); + expect(getFilterVar(trail).state.filters.length).toBe(0); }); describe('And when changing the filter to zone=b', () => { beforeEach(() => { - getFilterVar().setState({ filters: [{ key: 'zone', operator: '=', value: 'b' }] }); + getFilterVar(trail).setState({ filters: [{ key: 'zone', operator: '=', value: 'b' }] }); }); it('should add history step', () => { @@ -295,8 +287,8 @@ describe('DataTrail', () => { }); it('Should have filter be updated to "zone=b"', () => { - expect(getFilterVar().state.filters[0].key).toBe('zone'); - expect(getFilterVar().state.filters[0].value).toBe('b'); + expect(getFilterVar(trail).state.filters[0].key).toBe('zone'); + expect(getFilterVar(trail).state.filters[0].value).toBe('b'); }); it('Parent history step 1 should still have empty filter', () => { @@ -327,7 +319,7 @@ describe('DataTrail', () => { }); it('Should have filters set back to empty', () => { - expect(getFilterVar().state.filters.length).toBe(0); + expect(getFilterVar(trail).state.filters.length).toBe(0); }); it('History step 1 should still have empty filter', () => { @@ -417,12 +409,12 @@ describe('DataTrail', () => { describe('And filter is added zone=a', () => { beforeEach(() => { - getFilterVar().setState({ filters: [{ key: 'zone', operator: '=', value: 'a' }] }); + getFilterVar(trail).setState({ filters: [{ key: 'zone', operator: '=', value: 'a' }] }); }); it('Filter of trail should be zone=a', () => { - expect(getFilterVar().state.filters[0].key).toBe('zone'); - expect(getFilterVar().state.filters[0].value).toBe('a'); + expect(getFilterVar(trail).state.filters[0].key).toBe('zone'); + expect(getFilterVar(trail).state.filters[0].value).toBe('a'); }); it('Filter of step 2 should be zone=a', () => { @@ -440,7 +432,7 @@ describe('DataTrail', () => { }); it('Filter of trail should be empty', () => { - expect(getFilterVar().state.filters.length).toBe(0); + expect(getFilterVar(trail).state.filters.length).toBe(0); }); }); }); @@ -518,14 +510,6 @@ describe('DataTrail', () => { throw new Error('getOtelGroupLeftVar failed'); } - function getFilterVar() { - const variable = sceneGraph.lookupVariable(VAR_FILTERS, trail); - if (variable instanceof AdHocFiltersVariable) { - return variable; - } - throw new Error('getFilterVar failed'); - } - beforeEach(() => { trail = new DataTrail({ nonPromotedOtelResources, @@ -540,7 +524,7 @@ describe('DataTrail', () => { it('clicking start button should start with OTel off and showing var filters', () => { trail.setState({ startButtonClicked: true }); const otelResourcesHide = getOtelResourcesVar(trail).state.hide; - const varFiltersHide = getFilterVar().state.hide; + const varFiltersHide = getFilterVar(trail).state.hide; expect(otelResourcesHide).toBe(VariableHide.hideVariable); expect(varFiltersHide).toBe(VariableHide.hideLabel); }); @@ -557,7 +541,7 @@ describe('DataTrail', () => { describe('resetting the OTel experience', () => { it('should display with hideLabel var filters and hide VAR_OTEL_AND_METRIC_FILTERS when resetting otel experience', () => { trail.resetOtelExperience(); - expect(getFilterVar().state.hide).toBe(VariableHide.hideLabel); + expect(getFilterVar(trail).state.hide).toBe(VariableHide.hideLabel); expect(getOtelAndMetricsVar(trail).state.hide).toBe(VariableHide.hideVariable); }); @@ -589,7 +573,7 @@ describe('DataTrail', () => { it('should automatically update the var filters when a promoted resource has been selected from VAR_OTEL_AND_METRICS', () => { getOtelAndMetricsVar(trail).setState({ filters: [{ key: 'promoted', operator: '=', value: 'resource' }] }); - const varFilters = getFilterVar().state.filters[0]; + const varFilters = getFilterVar(trail).state.filters[0]; expect(varFilters.key).toBe('promoted'); expect(varFilters.value).toBe('resource'); }); @@ -600,4 +584,26 @@ describe('DataTrail', () => { }); }); }); + + describe('Label filters', () => { + let trail: DataTrail; + + beforeEach(() => { + trail = new DataTrail({}); + }); + + it('should not escape regex metacharacters in label values', () => { + const filterVar = getFilterVar(trail); + filterVar.setState({ filters: [{ key: 'app', operator: '=~', value: '.*end' }] }); // matches app=frontend, app=backend, etc. + expect(filterVar.getValue()).toBe('app=~".*end"'); + }); + }); }); + +function getFilterVar(trail: DataTrail) { + const variable = sceneGraph.lookupVariable(VAR_FILTERS, trail); + if (variable instanceof AdHocFiltersVariable) { + return variable; + } + throw new Error('getFilterVar failed'); +} diff --git a/public/app/features/trails/DataTrail.tsx b/public/app/features/trails/DataTrail.tsx index 46a854ffdb1..aef53bad053 100644 --- a/public/app/features/trails/DataTrail.tsx +++ b/public/app/features/trails/DataTrail.tsx @@ -650,9 +650,10 @@ function getVariableSet( addFilterButtonText: 'Select resource attributes', datasource: trailDS, hide: VariableHide.hideVariable, - layout: 'vertical', + layout: 'combobox', defaultKeys: [], applyMode: 'manual', + allowCustomValue: true, }), new AdHocFiltersVariable({ name: VAR_FILTERS, @@ -660,12 +661,16 @@ function getVariableSet( datasource: trailDS, // default to use var filters and have otel off hide: VariableHide.hideLabel, - layout: 'vertical', + layout: 'combobox', filters: initialFilters ?? [], baseFilters: getBaseFiltersForMetric(metric), applyMode: 'manual', - // since we only support prometheus datasources, this is always true - supportsMultiValueOperators: true, + allowCustomValue: true, + expressionBuilder: (filters: AdHocVariableFilter[]) => { + return [...getBaseFiltersForMetric(metric), ...filters] + .map((filter) => `${filter.key}${filter.operator}"${filter.value}"`) + .join(','); + }, }), ...getVariablesWithOtelJoinQueryConstant(otelJoinQuery ?? ''), new ConstantVariable({ @@ -683,12 +688,11 @@ function getVariableSet( addFilterButtonText: 'Filter', datasource: trailDS, hide: VariableHide.hideVariable, - layout: 'vertical', + layout: 'combobox', filters: initialFilters ?? [], baseFilters: getBaseFiltersForMetric(metric), applyMode: 'manual', - // since we only support prometheus datasources, this is always true - supportsMultiValueOperators: true, + allowCustomValue: true, // skipUrlSync: true }), // Legacy variable needed for bookmarking which is necessary because diff --git a/public/app/features/trails/DataTrailSettings.tsx b/public/app/features/trails/DataTrailSettings.tsx index 2584b7bb930..7007b511ad0 100644 --- a/public/app/features/trails/DataTrailSettings.tsx +++ b/public/app/features/trails/DataTrailSettings.tsx @@ -3,7 +3,7 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { SceneComponentProps, SceneObjectBase, SceneObjectState } from '@grafana/scenes'; import { Dropdown, Switch, ToolbarButton, useStyles2 } from '@grafana/ui'; -import { Trans } from '@grafana/ui/src/utils/i18n'; +import { Trans } from 'app/core/internationalization'; import { MetricScene } from './MetricScene'; import { MetricSelectScene } from './MetricSelect/MetricSelectScene'; diff --git a/public/app/features/trails/Integrations/dashboardIntegration.ts b/public/app/features/trails/Integrations/dashboardIntegration.ts index 4ff039f377b..dd52e73fe44 100644 --- a/public/app/features/trails/Integrations/dashboardIntegration.ts +++ b/public/app/features/trails/Integrations/dashboardIntegration.ts @@ -61,7 +61,7 @@ export async function addDataTrailPanelAction(dashboard: DashboardScene, panel: if (subMenu.length > 0) { items.push({ - text: 'Explore metrics', + text: 'Metrics drilldown', iconClassName: 'code-branch', subMenu: getUnique(subMenu), }); @@ -109,7 +109,7 @@ function createCommonEmbeddedTrailStateProps(item: QueryMetric, dashboard: Dashb const commonProps = { scene: embeddedTrail, - title: 'Explore metrics', + title: 'Metrics drilldown', }; return commonProps; diff --git a/public/app/features/trails/Menu/PanelMenu.tsx b/public/app/features/trails/Menu/PanelMenu.tsx new file mode 100644 index 00000000000..2d100a8b36b --- /dev/null +++ b/public/app/features/trails/Menu/PanelMenu.tsx @@ -0,0 +1,177 @@ +import { DataFrame, PanelMenuItem } from '@grafana/data'; +import { getPluginLinkExtensions } from '@grafana/runtime'; +import { + SceneComponentProps, + sceneGraph, + SceneObject, + SceneObjectBase, + SceneObjectState, + VizPanel, + VizPanelMenu, +} from '@grafana/scenes'; +import { getExploreUrl } from 'app/core/utils/explore'; +import { getQueryRunnerFor } from 'app/features/dashboard-scene/utils/utils'; + +import { AddToExplorationButton, extensionPointId } from '../MetricSelect/AddToExplorationsButton'; +import { getDataSource, getTrailFor } from '../utils'; + +const ADD_TO_INVESTIGATION_MENU_TEXT = 'Add to investigation'; +const ADD_TO_INVESTIGATION_MENU_DIVIDER_TEXT = 'investigations_divider'; // Text won't be visible +const ADD_TO_INVESTIGATION_MENU_GROUP_TEXT = 'Investigations'; + +interface PanelMenuState extends SceneObjectState { + body?: VizPanelMenu; + frame?: DataFrame; + labelName?: string; + fieldName?: string; + addExplorationsLink?: boolean; + explorationsButton?: AddToExplorationButton; +} + +/** + * @todo the VizPanelMenu interface is overly restrictive, doesn't allow any member functions on this class, so everything is currently inlined + */ +export class PanelMenu extends SceneObjectBase implements VizPanelMenu, SceneObject { + constructor(state: Partial) { + super({ ...state, addExplorationsLink: state.addExplorationsLink ?? true }); + this.addActivationHandler(() => { + let exploreUrl: Promise | undefined; + try { + const viz = sceneGraph.getAncestor(this, VizPanel); + const queryRunner = getQueryRunnerFor(viz); + const queries = queryRunner?.state.queries ?? []; + queries.forEach((query) => { + // removing legendFormat to get verbose legend in Explore + delete query.legendFormat; + }); + const trail = getTrailFor(this); + const dsValue = getDataSource(trail); + const timeRange = sceneGraph.getTimeRange(this); + exploreUrl = getExploreUrl({ + queries, + dsRef: { uid: dsValue }, + timeRange: timeRange.state.value, + scopedVars: { __sceneObject: { value: viz } }, + }); + } catch (e) {} + + // Navigation options (all panels) + const items: PanelMenuItem[] = [ + { + text: 'Navigation', + type: 'group', + }, + { + text: 'Explore', + iconClassName: 'compass', + onClick: () => exploreUrl?.then((url) => url && window.open(url, '_blank')), + shortcut: 'p x', + }, + ]; + + this.setState({ + body: new VizPanelMenu({ + items, + }), + }); + + const addToExplorationsButton = new AddToExplorationButton({ + labelName: this.state.labelName, + fieldName: this.state.fieldName, + frame: this.state.frame, + }); + this._subs.add( + addToExplorationsButton?.subscribeToState(() => { + subscribeToAddToExploration(this); + }) + ); + this.setState({ + explorationsButton: addToExplorationsButton, + }); + + if (this.state.addExplorationsLink) { + this.state.explorationsButton?.activate(); + } + }); + } + + addItem(item: PanelMenuItem): void { + if (this.state.body) { + this.state.body.addItem(item); + } + } + + setItems(items: PanelMenuItem[]): void { + if (this.state.body) { + this.state.body.setItems(items); + } + } + + public static Component = ({ model }: SceneComponentProps) => { + const { body } = model.useState(); + + if (body) { + return ; + } + + return <>; + }; +} + +const getInvestigationLink = (addToExplorations: AddToExplorationButton) => { + const links = getPluginLinkExtensions({ + extensionPointId: extensionPointId, + context: addToExplorations.state.context, + }); + + return links.extensions[0]; +}; + +const onAddToInvestigationClick = (event: React.MouseEvent, addToExplorations: AddToExplorationButton) => { + const link = getInvestigationLink(addToExplorations); + if (link && link.onClick) { + link.onClick(event); + } +}; + +function subscribeToAddToExploration(menu: PanelMenu) { + const addToExplorationButton = menu.state.explorationsButton; + if (addToExplorationButton) { + const link = getInvestigationLink(addToExplorationButton); + + const existingMenuItems = menu.state.body?.state.items ?? []; + + const existingAddToExplorationLink = existingMenuItems.find((item) => item.text === ADD_TO_INVESTIGATION_MENU_TEXT); + + if (link) { + if (!existingAddToExplorationLink) { + menu.state.body?.addItem({ + text: ADD_TO_INVESTIGATION_MENU_DIVIDER_TEXT, + type: 'divider', + }); + menu.state.body?.addItem({ + text: ADD_TO_INVESTIGATION_MENU_GROUP_TEXT, + type: 'group', + }); + menu.state.body?.addItem({ + text: ADD_TO_INVESTIGATION_MENU_TEXT, + iconClassName: 'plus-square', + onClick: (e) => onAddToInvestigationClick(e, addToExplorationButton), + }); + } else { + if (existingAddToExplorationLink) { + menu.state.body?.setItems( + existingMenuItems.filter( + (item) => + [ + ADD_TO_INVESTIGATION_MENU_DIVIDER_TEXT, + ADD_TO_INVESTIGATION_MENU_GROUP_TEXT, + ADD_TO_INVESTIGATION_MENU_TEXT, + ].includes(item.text) === false + ) + ); + } + } + } + } +} diff --git a/public/app/features/trails/MetricSelect/AddToExplorationsButton.tsx b/public/app/features/trails/MetricSelect/AddToExplorationsButton.tsx index da46b7635d0..bbdd7110530 100644 --- a/public/app/features/trails/MetricSelect/AddToExplorationsButton.tsx +++ b/public/app/features/trails/MetricSelect/AddToExplorationsButton.tsx @@ -80,7 +80,7 @@ export class AddToExplorationButton extends SceneObjectBase i } // refactor this into the query generator in future const isNative = trail.isNativeHistogram(metric.name); - const panel = getPreviewPanelFor(metric.name, index, currentFilterCount, description, isNative); + const panel = getPreviewPanelFor(metric.name, index, currentFilterCount, description, isNative, true); metric.itemRef = panel.getRef(); metric.isPanel = true; @@ -579,7 +578,7 @@ export class MetricSelectScene extends SceneObjectBase i }, ...Array.from(rootGroup?.groups.keys() ?? []).map((g) => ({ label: `${g}_`, value: g })), ]} - className="explore-metrics-metric-prefix-select" + className="metrics-drilldown-metric-prefix-select" /> {!metric && hasOtelResources && ( @@ -655,10 +654,7 @@ function getCardPanelFor(metric: string, description?: string) { return PanelBuilders.text() .setTitle(metric) .setDescription(description) - .setHeaderActions([ - new SelectMetricAction({ metric, title: 'Select' }), - new AddToExplorationButton({ labelName: metric }), - ]) + .setHeaderActions([new SelectMetricAction({ metric, title: 'Select' })]) .setOption('content', '') .build(); } diff --git a/public/app/features/trails/MetricSelect/NativeHistogramBadge.tsx b/public/app/features/trails/MetricSelect/NativeHistogramBadge.tsx index f2c551f3d70..21fed5c0ce7 100644 --- a/public/app/features/trails/MetricSelect/NativeHistogramBadge.tsx +++ b/public/app/features/trails/MetricSelect/NativeHistogramBadge.tsx @@ -3,7 +3,7 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { SceneObjectBase } from '@grafana/scenes'; import { Badge, useStyles2 } from '@grafana/ui'; -import { Trans } from '@grafana/ui/src/utils/i18n'; +import { Trans } from 'app/core/internationalization'; export class NativeHistogramBadge extends SceneObjectBase { public static Component = () => { diff --git a/public/app/features/trails/MetricSelect/api.ts b/public/app/features/trails/MetricSelect/api.ts index bb807ee31e9..c36cbfe3e2c 100644 --- a/public/app/features/trails/MetricSelect/api.ts +++ b/public/app/features/trails/MetricSelect/api.ts @@ -57,7 +57,7 @@ export async function getMetricNamesWithoutScopes( ...(limit ? { limit } : {}), }; - const response = await getBackendSrv().get(url, params, 'explore-metrics-names'); + const response = await getBackendSrv().get(url, params, 'metrics-drilldown-names'); if (limit && response.warnings?.includes(LIMIT_REACHED)) { return { ...response, limitReached: true, missingOtelTargets }; @@ -82,7 +82,7 @@ export async function getMetricNamesWithScopes( filters, '__name__', limit, - 'explore-metrics-names' + 'metrics-drilldown-names' ); if (jobs.length > 0 && instances.length > 0) { diff --git a/public/app/features/trails/MetricSelect/previewPanel.ts b/public/app/features/trails/MetricSelect/previewPanel.ts index 7206d3bb8e5..e21c346b090 100644 --- a/public/app/features/trails/MetricSelect/previewPanel.ts +++ b/public/app/features/trails/MetricSelect/previewPanel.ts @@ -1,11 +1,11 @@ import { PromQuery } from '@grafana/prometheus'; import { SceneCSSGridItem, SceneQueryRunner, SceneVariableSet } from '@grafana/scenes'; +import { PanelMenu } from '../Menu/PanelMenu'; import { getAutoQueriesForMetric } from '../autoQuery/getAutoQueriesForMetric'; import { getVariablesWithMetricConstant, MDP_METRIC_PREVIEW, trailDS } from '../shared'; import { getColorByIndex } from '../utils'; -import { AddToExplorationButton } from './AddToExplorationsButton'; import { NativeHistogramBadge } from './NativeHistogramBadge'; import { SelectMetricAction } from './SelectMetricAction'; import { hideEmptyPreviews } from './hideEmptyPreviews'; @@ -15,24 +15,29 @@ export function getPreviewPanelFor( index: number, currentFilterCount: number, description?: string, - nativeHistogram?: boolean + nativeHistogram?: boolean, + hideMenu?: boolean ) { const autoQuery = getAutoQueriesForMetric(metric, nativeHistogram); - let actions: Array = [ - new SelectMetricAction({ metric, title: 'Select' }), - new AddToExplorationButton({ labelName: metric }), - ]; + let actions: Array = [new SelectMetricAction({ metric, title: 'Select' })]; if (nativeHistogram) { actions.unshift(new NativeHistogramBadge({})); } - const vizPanel = autoQuery.preview + let vizPanelBuilder = autoQuery.preview .vizBuilder() .setColor({ mode: 'fixed', fixedColor: getColorByIndex(index) }) .setDescription(description) .setHeaderActions(actions) - .build(); + .setShowMenuAlways(true) + .setMenu(new PanelMenu({ labelName: metric })); + + if (!hideMenu) { + vizPanelBuilder = vizPanelBuilder.setShowMenuAlways(true).setMenu(new PanelMenu({ labelName: metric })); + } + + const vizPanel = vizPanelBuilder.build(); const queries = autoQuery.preview.queries.map((query) => convertPreviewQueriesToIgnoreUsage(query, currentFilterCount) diff --git a/public/app/features/trails/RedirectToDrilldownApp.tsx b/public/app/features/trails/RedirectToDrilldownApp.tsx new file mode 100644 index 00000000000..0a4ff04e8d7 --- /dev/null +++ b/public/app/features/trails/RedirectToDrilldownApp.tsx @@ -0,0 +1,17 @@ +import { Navigate, useLocation, useParams } from 'react-router-dom-v5-compat'; + +import { getRouteForAppPlugin } from 'app/features/plugins/routes'; + +/** + * Navigate to the drilldown app with the remaining path parameters and search params + */ +const RedirectToDrilldownApp = () => { + const { '*': remainingPath } = useParams(); + const location = useLocation(); + const appPath = getRouteForAppPlugin('grafana-metricsdrilldown-app').path.replaceAll('*', ''); + const newPath = `${appPath}${remainingPath}${location.search}`; + + return ; +}; + +export default RedirectToDrilldownApp; diff --git a/public/app/features/trails/TrailStore/TrailStore.test.ts b/public/app/features/trails/TrailStore/TrailStore.test.ts index b92abceca20..ee6b79c9049 100644 --- a/public/app/features/trails/TrailStore/TrailStore.test.ts +++ b/public/app/features/trails/TrailStore/TrailStore.test.ts @@ -1,8 +1,9 @@ -import { locationService, setDataSourceSrv } from '@grafana/runtime'; +import { locationService } from '@grafana/runtime'; import { AdHocFiltersVariable, sceneGraph, sceneUtils } from '@grafana/scenes'; +import { setupDataSources } from 'app/features/alerting/unified/testSetup/datasources'; import { DataSourceType } from 'app/features/alerting/unified/utils/datasource'; -import { MockDataSourceSrv, mockDataSource } from '../../alerting/unified/mocks'; +import { mockDataSource } from '../../alerting/unified/mocks'; import { DataTrail } from '../DataTrail'; import { TRAIL_BOOKMARKS_KEY, RECENT_TRAILS_KEY, VAR_FILTERS } from '../shared'; @@ -31,13 +32,10 @@ describe('TrailStore', () => { jest.useFakeTimers(); // Having the mock service set up is required for activating the loaded trails - setDataSourceSrv( - new MockDataSourceSrv({ - prom: mockDataSource({ - name: 'Prometheus', - type: DataSourceType.Prometheus, - uid: 'ds', - }), + setupDataSources( + mockDataSource({ + name: 'Prometheus', + type: DataSourceType.Prometheus, }) ); }); diff --git a/public/app/features/trails/autoQuery/components/AutoVizPanel.tsx b/public/app/features/trails/autoQuery/components/AutoVizPanel.tsx index 85096ecbb0e..a2996330fb9 100644 --- a/public/app/features/trails/autoQuery/components/AutoVizPanel.tsx +++ b/public/app/features/trails/autoQuery/components/AutoVizPanel.tsx @@ -1,6 +1,6 @@ -import { SceneObjectState, SceneObjectBase, SceneComponentProps, VizPanel, SceneQueryRunner } from '@grafana/scenes'; +import { SceneComponentProps, SceneObjectBase, SceneObjectState, SceneQueryRunner, VizPanel } from '@grafana/scenes'; -import { AddToExplorationButton } from '../../MetricSelect/AddToExplorationsButton'; +import { PanelMenu } from '../../Menu/PanelMenu'; import { getMetricDescription } from '../../helpers/MetricDatasourceHelper'; import { MDP_METRIC_OVERVIEW, trailDS } from '../../shared'; import { getMetricSceneFor, getTrailFor } from '../../utils'; @@ -57,10 +57,9 @@ export class AutoVizPanel extends SceneObjectBase { }) ) .setDescription(description) - .setHeaderActions([ - new AutoVizPanelQuerySelector({ queryDef: def, onChangeQuery: this.onChangeQuery }), - new AddToExplorationButton({ labelName: metric ?? this.state.metric }), - ]) + .setHeaderActions([new AutoVizPanelQuerySelector({ queryDef: def, onChangeQuery: this.onChangeQuery })]) + .setShowMenuAlways(true) + .setMenu(new PanelMenu({ labelName: metric ?? this.state.metric })) .build(); } diff --git a/public/app/features/trails/banners/NativeHistogramBanner.test.tsx b/public/app/features/trails/banners/NativeHistogramBanner.test.tsx index d5191037dae..23d4d4f21d1 100644 --- a/public/app/features/trails/banners/NativeHistogramBanner.test.tsx +++ b/public/app/features/trails/banners/NativeHistogramBanner.test.tsx @@ -51,4 +51,11 @@ describe('NativeHistogramBanner', () => { fireEvent.click(histogramButton); expect(mockTrail.publishEvent).toHaveBeenCalledWith(new MetricSelectedEvent('histogram1'), true); }); + + test('Set that the banner has been shown in local storage when a user closes the banner', () => { + render(); + // click the button with aria label "Close alert" + fireEvent.click(screen.getByLabelText('Close alert')); + expect(localStorage.getItem('nativeHistogramBanner')).toBe('true'); + }); }); diff --git a/public/app/features/trails/banners/NativeHistogramBanner.tsx b/public/app/features/trails/banners/NativeHistogramBanner.tsx index 5813c06d30f..d32375e054e 100644 --- a/public/app/features/trails/banners/NativeHistogramBanner.tsx +++ b/public/app/features/trails/banners/NativeHistogramBanner.tsx @@ -3,7 +3,7 @@ import { useState, type Dispatch, type SetStateAction } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2, useTheme2, Alert, Button } from '@grafana/ui'; -import { t, Trans } from '@grafana/ui/src/utils/i18n'; +import { t, Trans } from 'app/core/internationalization'; import { DataTrail } from '../DataTrail'; import { reportExploreMetrics } from '../interactions'; @@ -21,7 +21,7 @@ export function NativeHistogramBanner(props: NativeHistogramInfoProps) { const [showHistogramExamples, setShowHistogramExamples] = useState(false); const styles = useStyles2(getStyles, 0); - if (!histogramsLoaded || nativeHistograms.length === 0 || !histogramMessage) { + if (bannerHasBeenShown() || !histogramsLoaded || nativeHistograms.length === 0 || !histogramMessage) { return null; } @@ -32,6 +32,8 @@ export function NativeHistogramBanner(props: NativeHistogramInfoProps) { title={'Native Histogram Support'} severity={'info'} onRemove={() => { + // when a user explicitly closes the banner, save that it has been closed in local storage to not show again + setBannerHasBeenShown(); setHistogramMessage(false); }} className={styles.banner} @@ -275,3 +277,11 @@ function getStyles(theme: GrafanaTheme2, _chromeHeaderHeight: number) { }), }; } + +export function setBannerHasBeenShown() { + localStorage.setItem('nativeHistogramBanner', 'true'); +} + +export function bannerHasBeenShown() { + return localStorage.getItem('nativeHistogramBanner') ?? false; +} diff --git a/public/app/features/trails/helpers/MetricDatasourceHelper.ts b/public/app/features/trails/helpers/MetricDatasourceHelper.ts index 5a64feb9584..ff7d1fa62f2 100644 --- a/public/app/features/trails/helpers/MetricDatasourceHelper.ts +++ b/public/app/features/trails/helpers/MetricDatasourceHelper.ts @@ -154,7 +154,7 @@ export class MetricDatasourceHelper { } /** - * Used for additional filtering for adhoc vars labels in Explore metrics. + * Used for additional filtering for adhoc vars labels in Metrics Drilldown. * @param options * @returns */ @@ -170,7 +170,7 @@ export class MetricDatasourceHelper { } /** - * Used for additional filtering for adhoc vars label values in Explore metrics. + * Used for additional filtering for adhoc vars label values in Metrics Drilldown. * @param options * @returns */ diff --git a/public/app/features/trails/migrations/otelDeploymentEnvironment.ts b/public/app/features/trails/migrations/otelDeploymentEnvironment.ts index 969c95bf662..517ce88ce2c 100644 --- a/public/app/features/trails/migrations/otelDeploymentEnvironment.ts +++ b/public/app/features/trails/migrations/otelDeploymentEnvironment.ts @@ -32,8 +32,15 @@ export function migrateOtelDeploymentEnvironment(trail: DataTrail, urlParams: Ur ) { return; } - // if there is no dep env, does not need to be migrated - if (!deploymentEnv) { + + // check that there is a deployment environment variable value to migrate + // in some cases the deployment environment may not present + // but due to this change it is now always present and the value is undefined + // https://github.com/grafana/scenes/pull/1033 + if ( + !deploymentEnv || + (Array.isArray(deploymentEnv) && deploymentEnv.length > 0 && deploymentEnv[0] === 'undefined') + ) { return; } diff --git a/public/app/features/trails/otel/api.test.ts b/public/app/features/trails/otel/api.test.ts index 97e5f4a01ae..9ed900fa8be 100644 --- a/public/app/features/trails/otel/api.test.ts +++ b/public/app/features/trails/otel/api.test.ts @@ -30,10 +30,10 @@ jest.mock('@grafana/runtime', () => ({ requestId?: string, options?: Partial ) => { - // explore-metrics-otel-resources + // metrics-drilldown-otel-resources if ( - requestId === 'explore-metrics-otel-check-total-count(target_info{}) by (job, instance)' || - requestId === 'explore-metrics-otel-check-total-count(metric) by (job, instance)' + requestId === 'metrics-drilldown-otel-check-total-count(target_info{}) by (job, instance)' || + requestId === 'metrics-drilldown-otel-check-total-count(metric) by (job, instance)' ) { return Promise.resolve({ data: { @@ -43,17 +43,17 @@ jest.mock('@grafana/runtime', () => ({ ], }, }); - } else if (requestId === 'explore-metrics-otel-resources-deployment-env') { + } else if (requestId === 'metrics-drilldown-otel-resources-deployment-env') { return Promise.resolve({ data: ['env1', 'env2'] }); } else if ( requestId === - 'explore-metrics-otel-resources-metric-job-instance-metric{job=~"job1|job2",instance=~"instance1|instance2"}' + 'metrics-drilldown-otel-resources-metric-job-instance-metric{job=~"job1|job2",instance=~"instance1|instance2"}' ) { // part of getFilteredResourceAttributes to get metric labels. We prioritize metric labels over resource attributes so we use these to filter return Promise.resolve({ data: ['promotedResourceAttribute'] }); } else if ( requestId === - 'explore-metrics-otel-resources-metric-job-instance-target_info{job=~"job1|job2",instance=~"instance1|instance2"}' + 'metrics-drilldown-otel-resources-metric-job-instance-target_info{job=~"job1|job2",instance=~"instance1|instance2"}' ) { // part of getFilteredResourceAttributes to get instance labels return Promise.resolve({ data: ['promotedResourceAttribute', 'resourceAttribute'] }); diff --git a/public/app/features/trails/otel/api.ts b/public/app/features/trails/otel/api.ts index 542e9b2cd70..3e1bf5bb293 100644 --- a/public/app/features/trails/otel/api.ts +++ b/public/app/features/trails/otel/api.ts @@ -56,7 +56,7 @@ export async function totalOtelResources( const responseTotal = await getBackendSrv().get( url, paramsTotalTargets, - `explore-metrics-otel-check-total-${query}` + `metrics-drilldown-otel-check-total-${query}` ); let jobs: string[] = []; @@ -127,7 +127,7 @@ export async function getDeploymentEnvironmentsWithoutScopes( const response = await getBackendSrv().get( url, params, - 'explore-metrics-otel-resources-deployment-env' + 'metrics-drilldown-otel-resources-deployment-env' ); // exclude __name__ or previously chosen filters @@ -162,7 +162,7 @@ export async function getDeploymentEnvironmentsWithScopes( ], 'deployment_environment', undefined, - 'explore-metrics-otel-resources-deployment-env' + 'metrics-drilldown-otel-resources-deployment-env' ); // exclude __name__ or previously chosen filters return response.data.data; @@ -230,7 +230,7 @@ export async function getFilteredResourceAttributes( const metricResponse = await getBackendSrv().get( url, metricParams, - `explore-metrics-otel-resources-metric-job-instance-${metricMatchParam}` + `metrics-drilldown-otel-resources-metric-job-instance-${metricMatchParam}` ); // the metric labels here const metricLabels = metricResponse.data ?? []; @@ -249,7 +249,7 @@ export async function getFilteredResourceAttributes( const targetInfoResponse = await getBackendSrv().get( url, targetInfoParams, - `explore-metrics-otel-resources-metric-job-instance-${targetInfoMatchParam}` + `metrics-drilldown-otel-resources-metric-job-instance-${targetInfoMatchParam}` ); const targetInfoAttributes = targetInfoResponse.data ?? []; @@ -294,7 +294,7 @@ export async function getNonPromotedOtelResources(datasourceUid: string, timeRan const targetInfoResponse = getBackendSrv().get( url, targetInfoParams, - `explore-metrics-all-otel-resources-on-target_info` + `metrics-drilldown-all-otel-resources-on-target_info` ); // all labels in all metrics @@ -311,7 +311,7 @@ export async function getNonPromotedOtelResources(datasourceUid: string, timeRan const metricResponse = await getBackendSrv().get( url, metricParams, - `explore-metrics-all-metric-labels-not-otel-resource-attributes` + `metrics-drilldown-all-metric-labels-not-otel-resource-attributes` ); const promResponses = await Promise.all([targetInfoResponse, metricResponse]); // otel resource attributes diff --git a/public/app/features/trails/otel/util.ts b/public/app/features/trails/otel/util.ts index cd96351c28b..8b1d4e29ca5 100644 --- a/public/app/features/trails/otel/util.ts +++ b/public/app/features/trails/otel/util.ts @@ -522,6 +522,7 @@ export async function updateOtelData( resettingOtel: false, afterFirstOtelCheck: true, isUpdatingOtel: false, + nonPromotedOtelResources, }); } } @@ -547,7 +548,7 @@ function checkLabelPromotion(filters: AdHocVariableFilter[], nonPromotedOtelReso * Once we know this, we can add the selected filter to either the * VAR_OTEL_RESOURCES or VAR_FILTERS variable. * - * When the correct variable is updated, the rest of the explore metrics behavior will remain the same. + * When the correct variable is updated, the rest of the Metrics Drilldown behavior will remain the same. * * @param newStateFilters * @param prevStateFilters diff --git a/public/app/features/trails/otel/utils.test.ts b/public/app/features/trails/otel/utils.test.ts index 572643831d7..aa81281c92a 100644 --- a/public/app/features/trails/otel/utils.test.ts +++ b/public/app/features/trails/otel/utils.test.ts @@ -1,7 +1,8 @@ import { AdHocVariableFilter, MetricFindValue } from '@grafana/data'; -import { locationService, setDataSourceSrv } from '@grafana/runtime'; +import { locationService } from '@grafana/runtime'; import { AdHocFiltersVariable, ConstantVariable, sceneGraph } from '@grafana/scenes'; -import { mockDataSource, MockDataSourceSrv } from 'app/features/alerting/unified/mocks'; +import { mockDataSource } from 'app/features/alerting/unified/mocks'; +import { setupDataSources } from 'app/features/alerting/unified/testSetup/datasources'; import { DataSourceType } from 'app/features/alerting/unified/utils/datasource'; import { activateFullSceneTree } from 'app/features/dashboard-scene/utils/test-utils'; @@ -205,12 +206,10 @@ describe('updateOtelJoinWithGroupLeft', () => { beforeEach(() => { jest.spyOn(DataTrail.prototype, 'checkDataSourceForOTelResources').mockImplementation(() => Promise.resolve()); - setDataSourceSrv( - new MockDataSourceSrv({ - prom: mockDataSource({ - name: 'Prometheus', - type: DataSourceType.Prometheus, - }), + setupDataSources( + mockDataSource({ + name: 'Prometheus', + type: DataSourceType.Prometheus, }) ); trail = new DataTrail({ @@ -274,12 +273,11 @@ describe('getProdOrDefaultEnv', () => { describe('util functions that rely on trail and variable setup', () => { beforeAll(() => { jest.spyOn(DataTrail.prototype, 'checkDataSourceForOTelResources').mockImplementation(() => Promise.resolve()); - setDataSourceSrv( - new MockDataSourceSrv({ - prom: mockDataSource({ - name: 'Prometheus', - type: DataSourceType.Prometheus, - }), + + setupDataSources( + mockDataSource({ + name: 'Prometheus', + type: DataSourceType.Prometheus, }) ); }); @@ -332,6 +330,12 @@ describe('util functions that rely on trail and variable setup', () => { useOtelExperience: true, nonPromotedOtelResources, }); + setupDataSources( + mockDataSource({ + name: 'Prometheus', + type: DataSourceType.Prometheus, + }) + ); locationService.push(preTrailUrl); activateFullSceneTree(trail); getOtelGroupLeftVar(trail).setState({ value: 'attribute1,attribute2' }); diff --git a/public/app/features/transformers/FilterByValueTransformer/ValueMatchers/BasicMatcherEditor.tsx b/public/app/features/transformers/FilterByValueTransformer/ValueMatchers/BasicMatcherEditor.tsx index 3c8fb0aaa3f..6cb7328c59b 100644 --- a/public/app/features/transformers/FilterByValueTransformer/ValueMatchers/BasicMatcherEditor.tsx +++ b/public/app/features/transformers/FilterByValueTransformer/ValueMatchers/BasicMatcherEditor.tsx @@ -1,52 +1,21 @@ import { useCallback, useState } from 'react'; import * as React from 'react'; -import { ValueMatcherID, BasicValueMatcherOptions, VariableOrigin } from '@grafana/data'; -import { getTemplateSrv, config as cfg } from '@grafana/runtime'; -import { Input } from '@grafana/ui'; +import { ValueMatcherID, BasicValueMatcherOptions } from '@grafana/data'; import { SuggestionsInput } from '../../suggestionsInput/SuggestionsInput'; -import { numberOrVariableValidator } from '../../utils'; +import { getVariableSuggestions, numberOrVariableValidator } from '../../utils'; import { ValueMatcherEditorConfig, ValueMatcherUIProps, ValueMatcherUIRegistryItem } from './types'; -import { convertToType } from './utils'; export function basicMatcherEditor( config: ValueMatcherEditorConfig ): React.FC> { - return function Render({ options, onChange, field }) { - const { validator, converter = convertToType } = config; + return function Render({ options, onChange }) { + const { validator } = config; const { value } = options; const [isInvalid, setInvalid] = useState(!validator(value)); - const templateSrv = getTemplateSrv(); - const variables = templateSrv.getVariables().map((v) => { - return { value: v.name, label: v.label || v.name, origin: VariableOrigin.Template }; - }); - - const onChangeValue = useCallback( - (event: React.FormEvent) => { - setInvalid(!validator(event.currentTarget.value)); - }, - [setInvalid, validator] - ); - - const onChangeOptions = useCallback( - (event: React.FocusEvent) => { - if (isInvalid) { - return; - } - - const { value } = event.currentTarget; - - onChange({ - ...options, - value: converter(value, field), - }); - }, - [options, onChange, isInvalid, field, converter] - ); - const onChangeVariableValue = useCallback( (value: string) => { setInvalid(!validator(value)); @@ -58,29 +27,16 @@ export function basicMatcherEditor( [setInvalid, validator, onChange, options] ); - if (cfg.featureToggles.transformationsVariableSupport) { - return ( - - ); - } else { - return ( - - ); - } + return ( + + ); }; } diff --git a/public/app/features/transformers/FilterByValueTransformer/ValueMatchers/RangeMatcherEditor.tsx b/public/app/features/transformers/FilterByValueTransformer/ValueMatchers/RangeMatcherEditor.tsx index 3202d5e37eb..0dd2c09ff92 100644 --- a/public/app/features/transformers/FilterByValueTransformer/ValueMatchers/RangeMatcherEditor.tsx +++ b/public/app/features/transformers/FilterByValueTransformer/ValueMatchers/RangeMatcherEditor.tsx @@ -1,59 +1,26 @@ import { useCallback, useState } from 'react'; import * as React from 'react'; -import { ValueMatcherID, RangeValueMatcherOptions, VariableOrigin } from '@grafana/data'; -import { getTemplateSrv, config as cfg } from '@grafana/runtime'; -import { InlineLabel, Input } from '@grafana/ui'; +import { ValueMatcherID, RangeValueMatcherOptions } from '@grafana/data'; +import { InlineLabel } from '@grafana/ui'; import { SuggestionsInput } from '../../suggestionsInput/SuggestionsInput'; -import { numberOrVariableValidator } from '../../utils'; +import { getVariableSuggestions, numberOrVariableValidator } from '../../utils'; import { ValueMatcherEditorConfig, ValueMatcherUIProps, ValueMatcherUIRegistryItem } from './types'; -import { convertToType } from './utils'; type PropNames = 'from' | 'to'; export function rangeMatcherEditor( config: ValueMatcherEditorConfig ): React.FC>> { - return function RangeMatcherEditor({ options, onChange, field }) { + return function RangeMatcherEditor({ options, onChange }) { const { validator } = config; const [isInvalid, setInvalid] = useState({ from: !validator(options.from), to: !validator(options.to), }); - const templateSrv = getTemplateSrv(); - const variables = templateSrv.getVariables().map((v) => { - return { value: v.name, label: v.label || v.name, origin: VariableOrigin.Template }; - }); - - const onChangeValue = useCallback( - (event: React.FormEvent, prop: PropNames) => { - setInvalid({ - ...isInvalid, - [prop]: !validator(event.currentTarget.value), - }); - }, - [setInvalid, validator, isInvalid] - ); - - const onChangeOptions = useCallback( - (event: React.FocusEvent, prop: PropNames) => { - if (isInvalid[prop]) { - return; - } - - const { value } = event.currentTarget; - - onChange({ - ...options, - [prop]: convertToType(value, field), - }); - }, - [options, onChange, isInvalid, field] - ); - const onChangeOptionsSuggestions = useCallback( (value: string, prop: PropNames) => { const invalid = !validator(value); @@ -74,45 +41,27 @@ export function rangeMatcherEditor( }, [options, onChange, isInvalid, setInvalid, validator] ); - if (cfg.featureToggles.transformationsVariableSupport) { - return ( - <> - onChangeOptionsSuggestions(val, 'from')} - suggestions={variables} - /> - and - onChangeOptionsSuggestions(val, 'to')} - /> - - ); - } + + const suggestions = getVariableSuggestions(); + return ( <> - onChangeValue(event, 'from')} - onBlur={(event) => onChangeOptions(event, 'from')} + onChange={(val) => onChangeOptionsSuggestions(val, 'from')} + suggestions={suggestions} /> and - onChangeValue(event, 'to')} - onBlur={(event) => onChangeOptions(event, 'to')} + suggestions={suggestions} + onChange={(val) => onChangeOptionsSuggestions(val, 'to')} /> ); diff --git a/public/app/features/transformers/FilterByValueTransformer/ValueMatchers/RegexMatcherEditor.tsx b/public/app/features/transformers/FilterByValueTransformer/ValueMatchers/RegexMatcherEditor.tsx index b99f246b358..451bcf65999 100644 --- a/public/app/features/transformers/FilterByValueTransformer/ValueMatchers/RegexMatcherEditor.tsx +++ b/public/app/features/transformers/FilterByValueTransformer/ValueMatchers/RegexMatcherEditor.tsx @@ -1,35 +1,21 @@ import { useCallback, useState } from 'react'; import * as React from 'react'; -import { ValueMatcherID, BasicValueMatcherOptions, VariableOrigin } from '@grafana/data'; -import { config as cfg, getTemplateSrv } from '@grafana/runtime'; -import { Input } from '@grafana/ui'; +import { ValueMatcherID, BasicValueMatcherOptions } from '@grafana/data'; import { SuggestionsInput } from '../../suggestionsInput/SuggestionsInput'; +import { getVariableSuggestions } from '../../utils'; import { ValueMatcherEditorConfig, ValueMatcherUIProps, ValueMatcherUIRegistryItem } from './types'; -import { convertToType } from './utils'; export function regexMatcherEditor( config: ValueMatcherEditorConfig ): React.FC>> { - return function Render({ options, onChange, field }) { - const { validator, converter = convertToType } = config; + return function Render({ options, onChange }) { + const { validator } = config; const { value } = options; const [isInvalid, setInvalid] = useState(!validator(value)); - const templateSrv = getTemplateSrv(); - const variables = templateSrv.getVariables().map((v) => { - return { value: v.name, label: v.label || v.name, origin: VariableOrigin.Template }; - }); - - const onChangeValue = useCallback( - (event: React.FormEvent) => { - setInvalid(!validator(event.currentTarget.value)); - }, - [setInvalid, validator] - ); - const onChangeVariableValue = useCallback( (value: string) => { setInvalid(!validator(value)); @@ -41,42 +27,13 @@ export function regexMatcherEditor( [setInvalid, validator, onChange, options] ); - const onChangeOptions = useCallback( - (event: React.FocusEvent) => { - if (isInvalid) { - return; - } - - const { value } = event.currentTarget; - - onChange({ - ...options, - value: converter(value, field), - }); - }, - [options, onChange, isInvalid, field, converter] - ); - - if (cfg.featureToggles.transformationsVariableSupport) { - return ( - - ); - } - return ( - ); }; @@ -89,7 +46,6 @@ export const getRegexValueMatchersUI = (): Array true, - converter: (value) => String(value), }), }, ]; diff --git a/public/app/features/transformers/calculateHeatmap/heatmap.ts b/public/app/features/transformers/calculateHeatmap/heatmap.ts index 0b086eb0907..6a7a29ce69c 100644 --- a/public/app/features/transformers/calculateHeatmap/heatmap.ts +++ b/public/app/features/transformers/calculateHeatmap/heatmap.ts @@ -16,7 +16,6 @@ import { TimeRange, } from '@grafana/data'; import { isLikelyAscendingVector } from '@grafana/data/src/transformations/transformers/joinDataFrames'; -import { config } from '@grafana/runtime'; import { ScaleDistribution, HeatmapCellLayout, @@ -52,29 +51,7 @@ export const heatmapTransformer: SynchronousDataTransformerInfo (source) => - source.pipe( - map((data) => { - if (config.featureToggles.transformationsVariableSupport) { - const optionsCopy = { - ...options, - xBuckets: { ...options.xBuckets }, - yBuckets: { ...options.yBuckets }, - }; - - if (optionsCopy.xBuckets?.value) { - optionsCopy.xBuckets.value = ctx.interpolate(optionsCopy.xBuckets.value); - } - - if (optionsCopy.yBuckets?.value) { - optionsCopy.yBuckets.value = ctx.interpolate(optionsCopy.yBuckets.value); - } - - return heatmapTransformer.transformer(optionsCopy, ctx)(data); - } else { - return heatmapTransformer.transformer(options, ctx)(data); - } - }) - ), + source.pipe(map((data) => heatmapTransformer.transformer(options, ctx)(data))), transformer: (options: HeatmapTransformerOptions) => { return (data: DataFrame[]) => { diff --git a/public/app/features/transformers/editors/CalculateFieldTransformerEditor/CalculateFieldTransformerEditor.tsx b/public/app/features/transformers/editors/CalculateFieldTransformerEditor/CalculateFieldTransformerEditor.tsx index 9fb3537abd8..bc39a4e2c62 100644 --- a/public/app/features/transformers/editors/CalculateFieldTransformerEditor/CalculateFieldTransformerEditor.tsx +++ b/public/app/features/transformers/editors/CalculateFieldTransformerEditor/CalculateFieldTransformerEditor.tsx @@ -1,6 +1,6 @@ import { ChangeEvent, useEffect, useState } from 'react'; import * as React from 'react'; -import { identity, of, OperatorFunction } from 'rxjs'; +import { of, OperatorFunction } from 'rxjs'; import { map } from 'rxjs/operators'; import { @@ -81,10 +81,8 @@ export const CalculateFieldTransformerEditor = (props: CalculateFieldTransformer }, [input, configuredOptions]); const getVariableNames = (): OperatorFunction => { - if (!cfg.featureToggles.transformationsVariableSupport) { - return identity; - } const templateSrv = getTemplateSrv(); + return (source) => source.pipe( map((input) => { diff --git a/public/app/features/transformers/editors/HistogramTransformerEditor.tsx b/public/app/features/transformers/editors/HistogramTransformerEditor.tsx index 696f3458d59..a257f02b0af 100644 --- a/public/app/features/transformers/editors/HistogramTransformerEditor.tsx +++ b/public/app/features/transformers/editors/HistogramTransformerEditor.tsx @@ -6,19 +6,16 @@ import { TransformerRegistryItem, TransformerUIProps, TransformerCategory, - VariableOrigin, } from '@grafana/data'; import { histogramFieldInfo, HistogramTransformerInputs, } from '@grafana/data/src/transformations/transformers/histogram'; -import { getTemplateSrv, config as cfg } from '@grafana/runtime'; import { InlineField, InlineFieldRow, InlineSwitch } from '@grafana/ui'; -import { NumberInput } from 'app/core/components/OptionsUI/NumberInput'; import { getTransformationContent } from '../docs/getTransformationContent'; import { SuggestionsInput } from '../suggestionsInput/SuggestionsInput'; -import { numberOrVariableValidator } from '../utils'; +import { getVariableSuggestions, numberOrVariableValidator } from '../utils'; export const HistogramTransformerEditor = ({ input, @@ -33,36 +30,6 @@ export const HistogramTransformerEditor = ({ bucketOffset: !numberOrVariableValidator(options.bucketOffset || ''), }); - const onBucketCountChanged = useCallback( - (val?: number) => { - onChange({ - ...options, - bucketCount: val, - }); - }, - [onChange, options] - ); - - const onBucketSizeChanged = useCallback( - (val?: number) => { - onChange({ - ...options, - bucketSize: val, - }); - }, - [onChange, options] - ); - - const onBucketOffsetChanged = useCallback( - (val?: number) => { - onChange({ - ...options, - bucketOffset: val, - }); - }, - [onChange, options] - ); - const onVariableBucketCountChanged = useCallback( (value: string) => { setInvalid({ ...isInvalid, bucketCount: !numberOrVariableValidator(value) }); @@ -106,72 +73,7 @@ export const HistogramTransformerEditor = ({ }); }, [onChange, options]); - const templateSrv = getTemplateSrv(); - const variables = templateSrv.getVariables().map((v) => { - return { value: v.name, label: v.label || v.name, origin: VariableOrigin.Template }; - }); - - if (!cfg.featureToggles.transformationsVariableSupport) { - let bucketSize; - if (typeof options.bucketSize === 'string') { - bucketSize = parseInt(options.bucketSize, 10); - } else { - bucketSize = options.bucketSize; - } - - let bucketOffset; - if (typeof options.bucketOffset === 'string') { - bucketOffset = parseInt(options.bucketOffset, 10); - } else { - bucketOffset = options.bucketOffset; - } - - return ( -
- - - - - - - - - - - - - - - - - - - - -
- ); - } + const suggestions = getVariableSuggestions(); return (
@@ -184,7 +86,7 @@ export const HistogramTransformerEditor = ({ tooltip={histogramFieldInfo.bucketCount.description} > ) => { const [isInvalid, setInvalid] = useState(false); - const onSetLimit = useCallback( - (value: FormEvent) => { - onChange({ - ...options, - limitField: Number(value.currentTarget.value), - }); - }, - [onChange, options] - ); - const onSetVariableLimit = useCallback( (value: string) => { setInvalid(!numberOrVariableValidator(value)); @@ -40,28 +28,6 @@ export const LimitTransformerEditor = ({ options, onChange }: TransformerUIProps [onChange, options] ); - const templateSrv = getTemplateSrv(); - const variables = templateSrv.getVariables().map((v) => { - return { value: v.name, label: v.label || v.name, origin: VariableOrigin.Template }; - }); - - if (!cfg.featureToggles.transformationsVariableSupport) { - return ( - <> - - - - - - - ); - } return ( <> @@ -71,7 +37,7 @@ export const LimitTransformerEditor = ({ options, onChange }: TransformerUIProps value={String(options.limitField)} onChange={onSetVariableLimit} placeholder="Value or variable" - suggestions={variables} + suggestions={getVariableSuggestions()} > diff --git a/public/app/features/transformers/editors/SortByTransformerEditor.tsx b/public/app/features/transformers/editors/SortByTransformerEditor.tsx index f0362a65ddb..3faa19ebf8f 100644 --- a/public/app/features/transformers/editors/SortByTransformerEditor.tsx +++ b/public/app/features/transformers/editors/SortByTransformerEditor.tsx @@ -8,7 +8,7 @@ import { TransformerCategory, } from '@grafana/data'; import { SortByField, SortByTransformerOptions } from '@grafana/data/src/transformations/transformers/sortBy'; -import { getTemplateSrv, config as cfg } from '@grafana/runtime'; +import { getTemplateSrv } from '@grafana/runtime'; import { InlineField, InlineSwitch, InlineFieldRow, Select } from '@grafana/ui'; import { getTransformationContent } from '../docs/getTransformationContent'; @@ -36,7 +36,7 @@ export const SortByTransformerEditor = ({ input, options, onChange }: Transforme { /> )} + {requireCustomNamespace && ( + + + dispatch(changeBucketAggregationSetting({ bucketAgg, settingName: 'size', newValue: e.target.value })) + } + defaultValue={size} /> diff --git a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/utils.ts b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/utils.ts index e873a07cda4..a00e750ca67 100644 --- a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/utils.ts +++ b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/utils.ts @@ -64,14 +64,3 @@ export const orderOptions: Array> = [ { label: 'Top', value: 'desc' }, { label: 'Bottom', value: 'asc' }, ]; - -export const sizeOptions = [ - { label: 'No limit', value: '0' }, - { label: '1', value: '1' }, - { label: '2', value: '2' }, - { label: '3', value: '3' }, - { label: '5', value: '5' }, - { label: '10', value: '10' }, - { label: '15', value: '15' }, - { label: '20', value: '20' }, -]; diff --git a/public/app/plugins/datasource/elasticsearch/datasource.test.ts b/public/app/plugins/datasource/elasticsearch/datasource.test.ts index 6b8d2606184..f80d969bfb0 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.test.ts +++ b/public/app/plugins/datasource/elasticsearch/datasource.test.ts @@ -121,6 +121,48 @@ describe('ElasticDatasource', () => { expect(gte).toBe('1663740610000'); // 2022-09-21T06:10:10Z expect(lte).toBe('1663999821000'); // 2022-09-24T06:10:21Z }); + + it('should return fields properly', async () => { + const ds = createElasticDatasource({ jsonData: { timeField: '@timestamp' } }); + const getTagValuesData = { + responses: [ + { + aggregations: { + '1': { + buckets: [ + { + doc_count: 10, + key: 'foo', + }, + { + doc_count: 20, + key: 6, + key_as_string: 'six', + }, + { + doc_count: 30, + key: 7, + }, + ], + }, + }, + }, + ], + }; + const postResource = jest.spyOn(ds, 'postResourceRequest').mockResolvedValue(getTagValuesData); + const values = await ds.getTagValues({ key: 'test', timeRange: timeRangeMock, filters: [] }); + expect(postResource).toHaveBeenCalledTimes(1); + + expect(values.length).toBe(3); + expect(values[0].text).toBe('foo'); + expect(values[0].value).toBe('foo'); + + expect(values[1].text).toBe('six'); + expect(values[1].value).toBe('6'); + + expect(values[2].text).toBe('7'); + expect(values[2].value).toBe('7'); + }); }); describe('query', () => { @@ -770,6 +812,13 @@ describe('ElasticDatasource', () => { const query = ds.addAdHocFilters('', filters); expect(query).toBe('field\\:name:/field value\\//'); }); + + it('should replace level with the log level field', () => { + const ds = createElasticDatasource({ jsonData: { logLevelField: 'level_field' } }); + const filters = [{ key: 'level', operator: '=', value: 'foo', condition: '' }]; + const query = ds.addAdHocFilters('', filters); + expect(query).toBe('level_field:"foo"'); + }); }); }); @@ -1078,6 +1127,10 @@ describe('ElasticDatasource', () => { key: 'test2', key_as_string: 'test2_as_string', }, + { + doc_count: 2, + key: 5, + }, ], }, }, @@ -1102,13 +1155,14 @@ describe('ElasticDatasource', () => { it('should get results', async () => { const { results } = await runScenario(); - expect(results.length).toEqual(2); + expect(results.length).toEqual(3); }); - it('should use key or key_as_string', async () => { + it('should use key, key_as_string, or cast key to string', async () => { const { results } = await runScenario(); expect(results[0].text).toEqual('test'); expect(results[1].text).toEqual('test2_as_string'); + expect(results[2].text).toEqual('5'); }); it('should not set search type to count', async () => { diff --git a/public/app/plugins/datasource/elasticsearch/datasource.ts b/public/app/plugins/datasource/elasticsearch/datasource.ts index 60623c04cd1..bccb678babf 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.ts +++ b/public/app/plugins/datasource/elasticsearch/datasource.ts @@ -1,4 +1,4 @@ -import { cloneDeep, first as _first, isNumber, isString, map as _map, find, isObject } from 'lodash'; +import { cloneDeep, first as _first, isNumber, isString, map as _map, isObject } from 'lodash'; import { from, generate, lastValueFrom, Observable, of } from 'rxjs'; import { catchError, first, map, mergeMap, skipWhile, throwIfEmpty, tap } from 'rxjs/operators'; import { SemVer } from 'semver'; @@ -83,7 +83,7 @@ import { isElasticsearchResponseWithHits, ElasticsearchHits, } from './types'; -import { getScriptValue, isSupportedVersion, isTimeSeriesQuery, unsupportedVersionMessage } from './utils'; +import { getScriptValue, isTimeSeriesQuery } from './utils'; export const REF_ID_STARTER_LOG_VOLUME = 'log-volume-'; export const REF_ID_STARTER_LOG_SAMPLE = 'log-sample-'; @@ -441,37 +441,6 @@ export class ElasticDatasource return queries.map((q) => this.applyTemplateVariables(q, scopedVars, filters)); } - /** - * @todo Remove as we have health checks in the backend - */ - async testDatasource() { - // we explicitly ask for uncached, "fresh" data here - const dbVersion = await this.getDatabaseVersion(false); - // if we are not able to determine the elastic-version, we assume it is a good version. - const isSupported = dbVersion != null ? isSupportedVersion(dbVersion) : true; - const versionMessage = isSupported ? '' : `WARNING: ${unsupportedVersionMessage} `; - // validate that the index exist and has date field - return lastValueFrom( - this.getFields(['date']).pipe( - mergeMap((dateFields) => { - const timeField = find(dateFields, { text: this.timeField }); - if (!timeField) { - return of({ - status: 'error', - message: 'No date field named ' + this.timeField + ' found', - }); - } - return of({ status: 'success', message: `${versionMessage}Data source successfully connected.` }); - }), - catchError((err) => { - const infoInParentheses = err.message ? ` (${err.message})` : ''; - const message = `Unable to connect with Elasticsearch${infoInParentheses}. Please check the server logs for more details.`; - return of({ status: 'error', message }); - }) - ) - ); - } - // Private method used in `getTerms` to get the header for the Elasticsearch query private getQueryHeader(searchType: string, timeFrom?: DateTime, timeTo?: DateTime): string { const queryHeader = { @@ -897,7 +866,11 @@ export class ElasticDatasource * Get values for a given field. * Used for example in getTagValues. */ - getTerms(queryDef: TermsQuery, range = getDefaultTimeRange()): Observable { + getTerms( + queryDef: TermsQuery, + range = getDefaultTimeRange(), + isTagValueQuery = false + ): Observable { const searchType = 'query_then_fetch'; const header = this.getQueryHeader(searchType, range.from, range.to); let esQuery = JSON.stringify(this.queryBuilder.getTermsQuery(queryDef)); @@ -919,9 +892,10 @@ export class ElasticDatasource const buckets = res.responses[0].aggregations['1'].buckets; return _map(buckets, (bucket) => { + const keyString = String(bucket.key); return { - text: bucket.key_as_string || bucket.key, - value: bucket.key, + text: bucket.key_as_string || keyString, + value: isTagValueQuery ? keyString : bucket.key, }; }); }) @@ -979,7 +953,7 @@ export class ElasticDatasource * @returns A Promise that resolves to an array of label values represented as MetricFindValue objects */ getTagValues(options: DataSourceGetTagValuesOptions) { - return lastValueFrom(this.getTerms({ field: options.key }, options.timeRange)); + return lastValueFrom(this.getTerms({ field: options.key }, options.timeRange, true)); } /** @@ -1134,7 +1108,7 @@ export class ElasticDatasource } let finalQuery = query; adhocFilters.forEach((filter) => { - finalQuery = addAddHocFilter(finalQuery, filter); + finalQuery = addAddHocFilter(finalQuery, filter, this.logLevelField); }); return finalQuery; diff --git a/public/app/plugins/datasource/elasticsearch/modifyQuery.ts b/public/app/plugins/datasource/elasticsearch/modifyQuery.ts index 3971e3b1396..467d5bea494 100644 --- a/public/app/plugins/datasource/elasticsearch/modifyQuery.ts +++ b/public/app/plugins/datasource/elasticsearch/modifyQuery.ts @@ -83,7 +83,7 @@ function concatenate(query: string, filter: string, condition = 'AND'): string { /** * Adds a label:"value" expression to the query. */ -export function addAddHocFilter(query: string, filter: AdHocVariableFilter): string { +export function addAddHocFilter(query: string, filter: AdHocVariableFilter, logLevelField?: string): string { if (!filter.key || !filter.value) { return query; } @@ -94,15 +94,20 @@ export function addAddHocFilter(query: string, filter: AdHocVariableFilter): str value: filter.value.toString(), }; + let key = filter.key; + if (logLevelField && key === 'level') { + key = logLevelField; + } + const equalityFilters = ['=', '!=']; if (equalityFilters.includes(filter.operator)) { - return addFilterToQuery(query, filter.key, filter.value, filter.operator === '=' ? '' : '-'); + return addFilterToQuery(query, key, filter.value, filter.operator === '=' ? '' : '-'); } /** * Keys and values in ad hoc filters may contain characters such as * colons, which needs to be escaped. */ - const key = escapeFilter(filter.key); + key = escapeFilter(key); const value = escapeFilterValue(filter.value); const regexValue = escapeFilterValue(filter.value, false); let addHocFilter = ''; diff --git a/public/app/plugins/datasource/grafana-postgresql-datasource/datasource.ts b/public/app/plugins/datasource/grafana-postgresql-datasource/datasource.ts index 24dd9e1c15d..3a79e700fb7 100644 --- a/public/app/plugins/datasource/grafana-postgresql-datasource/datasource.ts +++ b/public/app/plugins/datasource/grafana-postgresql-datasource/datasource.ts @@ -2,7 +2,7 @@ import { v4 as uuidv4 } from 'uuid'; import { DataSourceInstanceSettings, ScopedVars } from '@grafana/data'; import { LanguageDefinition } from '@grafana/plugin-ui'; -import { TemplateSrv, config } from '@grafana/runtime'; +import { TemplateSrv } from '@grafana/runtime'; import { COMMON_FNS, DB, @@ -94,17 +94,13 @@ export class PostgresDatasource extends SqlDatasource { } getFunctions = (): ReturnType => { - if (config.featureToggles.sqlQuerybuilderFunctionParameters) { - const columnParam: FuncParameter = { - name: 'Column', - required: true, - options: (query) => this.fetchFields(query), - }; + const columnParam: FuncParameter = { + name: 'Column', + required: true, + options: (query) => this.fetchFields(query), + }; - return [...MACRO_FUNCTIONS(columnParam), ...COMMON_FNS.map((fn) => ({ ...fn, parameters: [columnParam] }))]; - } else { - return COMMON_FNS; - } + return [...MACRO_FUNCTIONS(columnParam), ...COMMON_FNS.map((fn) => ({ ...fn, parameters: [columnParam] }))]; }; getDB(): DB { diff --git a/public/app/plugins/datasource/grafana-postgresql-datasource/package.json b/public/app/plugins/datasource/grafana-postgresql-datasource/package.json index 8cea9b0a65b..6cf8a965548 100644 --- a/public/app/plugins/datasource/grafana-postgresql-datasource/package.json +++ b/public/app/plugins/datasource/grafana-postgresql-datasource/package.json @@ -2,28 +2,28 @@ "name": "@grafana-plugins/grafana-postgresql-datasource", "description": "PostgreSQL data source plugin", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", - "@grafana/plugin-ui": "^0.9.6", - "@grafana/runtime": "11.5.0-pre", - "@grafana/sql": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", + "@grafana/plugin-ui": "0.10.1", + "@grafana/runtime": "11.6.0-pre", + "@grafana/sql": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "lodash": "4.17.21", "react": "18.3.1", "rxjs": "7.8.1", "tslib": "2.8.1" }, "devDependencies": { - "@grafana/e2e-selectors": "11.5.0-pre", - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", + "@grafana/plugin-configs": "11.6.0-pre", "@testing-library/dom": "10.4.0", - "@testing-library/react": "16.1.0", - "@testing-library/user-event": "14.5.2", + "@testing-library/react": "16.2.0", + "@testing-library/user-event": "14.6.1", "@types/jest": "29.5.14", - "@types/lodash": "4.17.14", - "@types/node": "22.10.7", + "@types/lodash": "4.17.15", + "@types/node": "22.12.0", "@types/react": "18.3.18", "ts-node": "10.9.2", "typescript": "5.7.3", diff --git a/public/app/plugins/datasource/grafana-postgresql-datasource/plugin.json b/public/app/plugins/datasource/grafana-postgresql-datasource/plugin.json index fc5ed6d136c..c6a68531161 100644 --- a/public/app/plugins/datasource/grafana-postgresql-datasource/plugin.json +++ b/public/app/plugins/datasource/grafana-postgresql-datasource/plugin.json @@ -14,7 +14,8 @@ "logos": { "small": "img/postgresql_logo.svg", "large": "img/postgresql_logo.svg" - } + }, + "version": "%VERSION%" }, "alerting": true, diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/ConfigEditor.tsx b/public/app/plugins/datasource/grafana-pyroscope-datasource/ConfigEditor.tsx index af6467d7896..3b2e3cbb5d0 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/ConfigEditor.tsx +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/ConfigEditor.tsx @@ -9,7 +9,7 @@ import { ConnectionSettings, DataSourceDescription, convertLegacyAuthProps, -} from '@grafana/experimental'; +} from '@grafana/plugin-ui'; import { config } from '@grafana/runtime'; import { Divider, diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json index 9d271b159df..d75bed9c7ca 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json @@ -2,13 +2,13 @@ "name": "@grafana-plugins/grafana-pyroscope-datasource", "description": "Continuous profiling for analysis of CPU and memory usage, down to the line number and throughout time. Saving infrastructure cost, improving performance, and increasing reliability.", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", - "@grafana/runtime": "11.5.0-pre", - "@grafana/schema": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", + "@grafana/runtime": "11.6.0-pre", + "@grafana/schema": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "fast-deep-equal": "^3.1.3", "lodash": "4.17.21", "monaco-editor": "0.34.1", @@ -20,14 +20,14 @@ "tslib": "2.8.1" }, "devDependencies": { - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/plugin-configs": "11.6.0-pre", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.6.3", - "@testing-library/react": "16.1.0", - "@testing-library/user-event": "14.5.2", + "@testing-library/react": "16.2.0", + "@testing-library/user-event": "14.6.1", "@types/jest": "29.5.14", - "@types/lodash": "4.17.14", - "@types/node": "22.10.7", + "@types/lodash": "4.17.15", + "@types/node": "22.12.0", "@types/prismjs": "1.26.5", "@types/react": "18.3.18", "@types/react-dom": "18.3.5", diff --git a/public/app/plugins/datasource/grafana-testdata-datasource/dashboards/streaming.json b/public/app/plugins/datasource/grafana-testdata-datasource/dashboards/streaming.json index 8498d79f2f1..b5bd877bcaa 100644 --- a/public/app/plugins/datasource/grafana-testdata-datasource/dashboards/streaming.json +++ b/public/app/plugins/datasource/grafana-testdata-datasource/dashboards/streaming.json @@ -199,8 +199,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Simple Streaming Example", diff --git a/public/app/plugins/datasource/grafana-testdata-datasource/package.json b/public/app/plugins/datasource/grafana-testdata-datasource/package.json index 23dc2a0f2e4..1c283aa6baa 100644 --- a/public/app/plugins/datasource/grafana-testdata-datasource/package.json +++ b/public/app/plugins/datasource/grafana-testdata-datasource/package.json @@ -2,35 +2,34 @@ "name": "@grafana-plugins/grafana-testdata-datasource", "description": "Generates test data in different forms", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", - "@grafana/experimental": "2.1.6", - "@grafana/runtime": "11.5.0-pre", - "@grafana/schema": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", + "@grafana/runtime": "11.6.0-pre", + "@grafana/schema": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "d3-random": "^3.0.1", "lodash": "4.17.21", "micro-memoize": "^4.1.2", "react": "18.3.1", "react-dom": "18.3.1", - "react-select": "5.9.0", + "react-select": "5.10.0", "react-use": "17.6.0", "rxjs": "7.8.1", "tslib": "2.8.1", "uuid": "11.0.5" }, "devDependencies": { - "@grafana/e2e-selectors": "11.5.0-pre", - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", + "@grafana/plugin-configs": "11.6.0-pre", "@testing-library/dom": "10.4.0", - "@testing-library/react": "16.1.0", - "@testing-library/user-event": "14.5.2", + "@testing-library/react": "16.2.0", + "@testing-library/user-event": "14.6.1", "@types/d3-random": "^3.0.2", "@types/jest": "29.5.14", - "@types/lodash": "4.17.14", - "@types/node": "22.10.7", + "@types/lodash": "4.17.15", + "@types/node": "22.12.0", "@types/react": "18.3.18", "@types/react-dom": "18.3.5", "@types/uuid": "10.0.0", diff --git a/public/app/plugins/datasource/grafana/components/TimeRegionEditor.tsx b/public/app/plugins/datasource/grafana/components/TimeRegionEditor.tsx index 9ba25879675..79b52e5a571 100644 --- a/public/app/plugins/datasource/grafana/components/TimeRegionEditor.tsx +++ b/public/app/plugins/datasource/grafana/components/TimeRegionEditor.tsx @@ -1,12 +1,24 @@ import { css } from '@emotion/css'; import moment, { Moment } from 'moment/moment'; -import { useState } from 'react'; +import { ChangeEvent, useState } from 'react'; import { dateTimeAsMoment, getTimeZoneInfo, GrafanaTheme2, isDateTime, SelectableValue } from '@grafana/data'; -import { Button, Field, FieldSet, Select, Stack, TimeOfDayPicker, TimeZonePicker, useStyles2 } from '@grafana/ui'; +import { + Button, + Field, + FieldSet, + Input, + Select, + Stack, + Switch, + TimeOfDayPicker, + TimeZonePicker, + useStyles2, +} from '@grafana/ui'; import { TimeZoneOffset } from '@grafana/ui/src/components/DateTimePickers/TimeZonePicker/TimeZoneOffset'; import { TimeZoneTitle } from '@grafana/ui/src/components/DateTimePickers/TimeZonePicker/TimeZoneTitle'; -import { TimeRegionConfig } from 'app/core/utils/timeRegions'; +import { t } from 'app/core/internationalization'; +import { TimeRegionConfig, TimeRegionMode } from 'app/core/utils/timeRegions'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; interface Props { @@ -20,6 +32,7 @@ const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' value: idx + 1, }; }); + export const TimeRegionEditor = ({ value, onChange }: Props) => { const styles = useStyles2(getStyles); const timestamp = Date.now(); @@ -85,6 +98,18 @@ export const TimeRegionEditor = ({ value, onChange }: Props) => { onChange({ ...value, timezone: v }); }; + const onModeChange = (v: TimeRegionMode) => { + onChange({ ...value, mode: v }); + }; + + const onCronExprChange = (v: string) => { + onChange({ ...value, cronExpr: v }); + }; + + const onDurationChange = (v: string) => { + onChange({ ...value, duration: v }); + }; + const onFromDayOfWeekChange = (v: SelectableValue) => { const fromDayOfWeek = v ? v.value : undefined; const toDayOfWeek = v ? value.toDayOfWeek : undefined; // clear if everyday @@ -125,46 +150,92 @@ export const TimeRegionEditor = ({ value, onChange }: Props) => { return (
- - - onToDayOfWeekChange(v)} - width={20} + + {value.mode == null && ( + <> + + + onToDayOfWeekChange(v)} + width={20} + /> + )} + onTimeChange(v ? dateTimeAsMoment(v) : v, 'to')} + allowEmpty={true} + placeholder="HH:mm" + size="sm" + /> + + + + )} + {value.mode === 'cron' && ( + <> + + ) => onCronExprChange(e.target.value)} + value={value.cronExpr} + placeholder="0 9 * * 1-5" + width={40} /> - )} - onTimeChange(v ? dateTimeAsMoment(v) : v, 'to')} - allowEmpty={true} - placeholder="HH:mm" - size="sm" - /> - - + + + ) => onDurationChange(e.target.value)} + value={value.duration} + placeholder="8h" + width={40} + /> + + + )} {renderTimezone()}
); diff --git a/public/app/plugins/datasource/grafana/datasource.ts b/public/app/plugins/datasource/grafana/datasource.ts index 51dc7afb94e..02216e02f73 100644 --- a/public/app/plugins/datasource/grafana/datasource.ts +++ b/public/app/plugins/datasource/grafana/datasource.ts @@ -108,7 +108,7 @@ export class GrafanaDatasource extends DataSourceWithBackend { continue; } if (target.queryType === GrafanaQueryType.TimeRegions) { - const frame = doTimeRegionQuery('', target.timeRegion!, request.range, request.timezone); + const frame = doTimeRegionQuery('', target.timeRegion!, request.range); results.push( of({ data: frame ? [frame] : [], @@ -195,12 +195,7 @@ export class GrafanaDatasource extends DataSourceWithBackend { async getAnnotations(options: AnnotationQueryRequest): Promise { const query = options.annotation.target as GrafanaQuery; if (query?.queryType === GrafanaQueryType.TimeRegions) { - const frame = doTimeRegionQuery( - options.annotation.name, - query.timeRegion!, - options.range, - getDashboardSrv().getCurrent()?.timezone // Annotation queries don't include the timezone - ); + const frame = doTimeRegionQuery(options.annotation.name, query.timeRegion!, options.range); return Promise.resolve({ data: frame ? [frame] : [] }); } diff --git a/public/app/plugins/datasource/grafana/timeRegions.test.ts b/public/app/plugins/datasource/grafana/timeRegions.test.ts index f5502fec661..76c052bc195 100644 --- a/public/app/plugins/datasource/grafana/timeRegions.test.ts +++ b/public/app/plugins/datasource/grafana/timeRegions.test.ts @@ -6,7 +6,7 @@ describe('grafana data source', () => { it('supports time region query', () => { const frame = doTimeRegionQuery( 'test', - { fromDayOfWeek: 1, toDayOfWeek: 2 }, + { fromDayOfWeek: 1, toDayOfWeek: 2, timezone: 'utc' }, { from: dateTime('2023-03-01'), to: dateTime('2023-03-31'), @@ -14,8 +14,7 @@ describe('grafana data source', () => { to: '', from: '', }, - }, - 'utc' + } ); expect(toDataFrameDTO(frame!)).toMatchInlineSnapshot(` @@ -39,10 +38,10 @@ describe('grafana data source', () => { "name": "timeEnd", "type": "time", "values": [ - 1678233599000, - 1678838399000, - 1679443199000, - 1680047999000, + 1678233600000, + 1678838400000, + 1679443200000, + 1680048000000, ], }, { @@ -80,8 +79,7 @@ describe('grafana data source', () => { to: '', from: '', }, - }, - 'utc' + } ); expect(toDataFrameDTO(frame!).fields).toMatchInlineSnapshot(` @@ -101,7 +99,7 @@ describe('grafana data source', () => { "name": "timeEnd", "type": "time", "values": [ - 1678147199000, + 1678147200000, ], }, { @@ -133,8 +131,7 @@ describe('grafana data source', () => { to: '', from: '', }, - }, - 'utc' + } ); expect(toDataFrameDTO(frame!).fields).toMatchInlineSnapshot(` @@ -154,7 +151,7 @@ describe('grafana data source', () => { "name": "timeEnd", "type": "time", "values": [ - 1678165199000, + 1678165200000, ], }, { @@ -186,8 +183,7 @@ describe('grafana data source', () => { to: '', from: '', }, - }, - 'utc' + } ); expect(toDataFrameDTO(frame!).fields).toMatchInlineSnapshot(` @@ -207,7 +203,7 @@ describe('grafana data source', () => { "name": "timeEnd", "type": "time", "values": [ - 1678168799000, + 1678168800000, ], }, { @@ -239,8 +235,7 @@ describe('grafana data source', () => { to: '', from: '', }, - }, - 'utc' + } ); expect(toDataFrameDTO(frame!).fields).toMatchInlineSnapshot(` @@ -260,7 +255,7 @@ describe('grafana data source', () => { "name": "timeEnd", "type": "time", "values": [ - 1678143599000, + 1678143600000, ], }, { @@ -292,8 +287,7 @@ describe('grafana data source', () => { to: '', from: '', }, - }, - 'utc' + } ); expect(toDataFrameDTO(frame!).fields).toMatchInlineSnapshot(` @@ -313,7 +307,7 @@ describe('grafana data source', () => { "name": "timeEnd", "type": "time", "values": [ - 1678121999000, + 1678122000000, ], }, { @@ -345,8 +339,7 @@ describe('grafana data source', () => { to: '', from: '', }, - }, - 'Asia/Dubai' + } ); expect(toDataFrameDTO(frame!).fields).toMatchInlineSnapshot(` @@ -366,7 +359,7 @@ describe('grafana data source', () => { "name": "timeEnd", "type": "time", "values": [ - 1678147199000, + 1678147200000, ], }, { @@ -398,8 +391,7 @@ describe('grafana data source', () => { to: '', from: '', }, - }, - 'America/Chicago' + } ); expect(toDataFrameDTO(frame!).fields).toMatchInlineSnapshot(` @@ -451,8 +443,7 @@ describe('grafana data source', () => { to: '', from: '', }, - }, - 'America/Chicago' + } ); expect(toDataFrameDTO(frame!).fields).toMatchInlineSnapshot(` diff --git a/public/app/plugins/datasource/grafana/timeRegions.ts b/public/app/plugins/datasource/grafana/timeRegions.ts index 8c7f000ba2a..6e3fb3b0da6 100644 --- a/public/app/plugins/datasource/grafana/timeRegions.ts +++ b/public/app/plugins/datasource/grafana/timeRegions.ts @@ -1,48 +1,28 @@ -import { TimeRange, DataFrame, FieldType, getTimeZoneInfo } from '@grafana/data'; +import { TimeRange, DataFrame, FieldType } from '@grafana/data'; import { TimeRegionConfig, calculateTimesWithin } from 'app/core/utils/timeRegions'; -export function doTimeRegionQuery( - name: string, - config: TimeRegionConfig, - range: TimeRange, - tz: string -): DataFrame | undefined { - if (!config) { - return undefined; - } - const regions = calculateTimesWithin(config, range); // UTC - if (!regions.length) { - return undefined; - } +export function doTimeRegionQuery(name: string, config: TimeRegionConfig, range: TimeRange): DataFrame | undefined { + const { mode, duration, cronExpr, from, fromDayOfWeek } = config; - const times: number[] = []; - const timesEnd: number[] = []; - const texts: string[] = []; + const isValidSimple = mode == null && (fromDayOfWeek != null || from != null); + const isValidAdvanced = mode === 'cron' && cronExpr != null && duration != null; - const regionTimezone = config.timezone ?? tz; + if (isValidSimple || isValidAdvanced) { + const ranges = calculateTimesWithin(config, range); - for (const region of regions) { - let from = region.from; - let to = region.to; + if (ranges.length > 0) { + const frame: DataFrame = { + fields: [ + { name: 'time', type: FieldType.time, values: ranges.map((r) => r.from), config: {} }, + { name: 'timeEnd', type: FieldType.time, values: ranges.map((r) => r.to), config: {} }, + { name: 'text', type: FieldType.string, values: Array(ranges.length).fill(name), config: {} }, + ], + length: ranges.length, + }; - const info = getTimeZoneInfo(regionTimezone, from); - if (info) { - const offset = info.offsetInMins * 60 * 1000; - from += offset; - to += offset; + return frame; } - - times.push(from); - timesEnd.push(to); - texts.push(name); } - return { - fields: [ - { name: 'time', type: FieldType.time, values: times, config: {} }, - { name: 'timeEnd', type: FieldType.time, values: timesEnd, config: {} }, - { name: 'text', type: FieldType.string, values: texts, config: {} }, - ], - length: times.length, - }; + return undefined; } diff --git a/public/app/plugins/datasource/graphite/dashboards/carbon_metrics.json b/public/app/plugins/datasource/graphite/dashboards/carbon_metrics.json index 94e8e685d45..418ba46835f 100644 --- a/public/app/plugins/datasource/graphite/dashboards/carbon_metrics.json +++ b/public/app/plugins/datasource/graphite/dashboards/carbon_metrics.json @@ -154,7 +154,6 @@ "now": true, "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], "status": "Stable", - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"], "type": "timepicker" }, "templating": { diff --git a/public/app/plugins/datasource/graphite/dashboards/metrictank.json b/public/app/plugins/datasource/graphite/dashboards/metrictank.json index 18b2e3939ff..b70ad4d58ec 100644 --- a/public/app/plugins/datasource/graphite/dashboards/metrictank.json +++ b/public/app/plugins/datasource/graphite/dashboards/metrictank.json @@ -4771,7 +4771,6 @@ "now": true, "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], "status": "Stable", - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"], "type": "timepicker" }, "timezone": "utc", diff --git a/public/app/plugins/datasource/graphite/datasource.test.ts b/public/app/plugins/datasource/graphite/datasource.test.ts index 849271524eb..de993ff6102 100644 --- a/public/app/plugins/datasource/graphite/datasource.test.ts +++ b/public/app/plugins/datasource/graphite/datasource.test.ts @@ -55,6 +55,10 @@ describe('graphiteDatasource', () => { describe('convertResponseToDataFrames', () => { it('should transform regular result', () => { + const refIDMap = { + refIDA: 'A', + refIDB: 'B', + }; const result = ctx.ds.convertResponseToDataFrames( createFetchResponse({ meta: { @@ -65,7 +69,7 @@ describe('graphiteDatasource', () => { }, series: [ { - target: 'seriesA', + target: 'seriesA refIDA', datapoints: [ [100, 200], [101, 201], @@ -85,7 +89,7 @@ describe('graphiteDatasource', () => { ], }, { - target: 'seriesB', + target: 'seriesB refIDB', meta: [ { 'aggnum-norm': 1, @@ -105,7 +109,8 @@ describe('graphiteDatasource', () => { ], }, ], - }) + }), + refIDMap ); expect(result.data.length).toBe(2); @@ -113,24 +118,68 @@ describe('graphiteDatasource', () => { expect(getFrameDisplayName(result.data[1])).toBe('seriesB'); expect(result.data[0].length).toBe(2); expect(result.data[0].meta.notices.length).toBe(1); + expect(result.data[0].refId).toBe('A'); expect(result.data[0].meta.notices[0].text).toBe('Data is rolled up, aggregated over 2h using Average function'); expect(result.data[1].meta.notices).toBeUndefined(); + expect(result.data[1].refId).toBe('B'); + }); + it('handles series with spaces in the name', () => { + const refIDMap = { + refIDA: 'A', + refIDB: 'B', + }; + const result = ctx.ds.convertResponseToDataFrames( + createFetchResponse({ + meta: { + stats: { + 'executeplan.cache-hit-partial.count': 5, + 'executeplan.cache-hit.count': 10, + }, + }, + series: [ + { + target: 'series A with spaces refIDA', + datapoints: [ + [100, 200], + [101, 201], + ], + }, + { + target: 'series B with spaces refIDB', + datapoints: [ + [200, 300], + [201, 301], + ], + }, + ], + }), + refIDMap + ); + + expect(result.data.length).toBe(2); + expect(getFrameDisplayName(result.data[0])).toBe('series A with spaces'); + expect(getFrameDisplayName(result.data[1])).toBe('series B with spaces'); + expect(result.data[0].length).toBe(2); + expect(result.data[0].refId).toBe('A'); + expect(result.data[1].refId).toBe('B'); }); }); describe('When querying graphite with one target using query editor target spec', () => { - const query = { - panelId: 3, - dashboardId: 5, - range: { from: dateTime('2022-04-01T00:00:00'), to: dateTime('2022-07-01T00:00:00') }, - targets: [{ target: 'prod1.count' }, { target: 'prod2.count' }], - maxDataPoints: 500, - }; - let response: unknown; let requestOptions: BackendSrvRequest; beforeEach(() => { + const query = { + panelId: 3, + dashboardId: 5, + range: { from: dateTime('2022-04-01T00:00:00'), to: dateTime('2022-07-01T00:00:00') }, + targets: [ + { target: 'prod1.count', refId: 'A' }, + { target: 'prod2.count', refId: 'B' }, + ], + maxDataPoints: 500, + }; fetchMock.mockImplementation((options) => { requestOptions = options; return of( @@ -164,8 +213,8 @@ describe('graphiteDatasource', () => { it('should query correctly', () => { const params = requestOptions.data.split('&'); - expect(params).toContain('target=prod1.count'); - expect(params).toContain('target=prod2.count'); + expect(params).toContain(`target=${encodeURIComponent(`aliasSub(prod1.count, "(^.*$)", "\\1 A")`)}`); + expect(params).toContain(`target=${encodeURIComponent(`aliasSub(prod2.count, "(^.*$)", "\\1 B")`)}`); expect(params).toContain('from=1648789200'); expect(params).toContain('until=1656655200'); }); diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index e3ab7e93dc7..9998fe8980e 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -42,6 +42,7 @@ import { GraphiteQueryType, GraphiteType, MetricTankRequestMeta, + MetricTankSeriesMeta, } from './types'; import { reduceError } from './utils'; import { DEFAULT_GRAPHITE_VERSION } from './versions'; @@ -209,6 +210,17 @@ export class GraphiteDatasource return merge(...streams); } + // Use this object to map the original refID of the query to our sanitised one + const refIds: { [key: string]: string } = {}; + for (const target of options.targets) { + // Sanitise the refID otherwise the Graphite query will fail + const formattedRefId = target.refId.replaceAll(' ', '_'); + refIds[formattedRefId] = target.refId; + // Use aliasSub to include the refID in the response series name. This allows us to set the refID on the frame. + const updatedTarget = `aliasSub(${target.target}, "(^.*$)", "\\1 ${formattedRefId}")`; + target.target = updatedTarget; + } + // handle the queries here const graphOptions = { from: this.translateTime(options.range.from, false, options.timezone), @@ -243,7 +255,7 @@ export class GraphiteDatasource httpOptions.requestId = this.name + '.panelId.' + options.panelId; } - return this.doGraphiteRequest(httpOptions).pipe(map(this.convertResponseToDataFrames)); + return this.doGraphiteRequest(httpOptions).pipe(map((result) => this.convertResponseToDataFrames(result, refIds))); } addTracingHeaders( @@ -267,14 +279,20 @@ export class GraphiteDatasource } } - convertResponseToDataFrames = (result: FetchResponse): DataQueryResponse => { + convertResponseToDataFrames = (result: FetchResponse, refIdMap: { [key: string]: string }): DataQueryResponse => { const data: DataFrame[] = []; if (!result || !result.data) { return { data }; } // Series are either at the root or under a node called 'series' - const series = result.data.series || result.data; + const series: Array<{ + target: string; + title: string; + tags: Record; + datapoints: Array<[number, number]>; + meta: MetricTankSeriesMeta[]; + }> = result.data.series || result.data; if (!isArray(series)) { throw { message: 'Missing series in result', data: result }; @@ -283,6 +301,14 @@ export class GraphiteDatasource for (let i = 0; i < series.length; i++) { const s = series[i]; + let refId = ''; + // Retrieve the original refID of the query + const splitTarget = s.target.split(' '); + if (splitTarget.length > 1) { + // refID should always be the last element + refId = splitTarget.pop() || ''; + s.target = splitTarget.join(' '); + } // Disables Grafana own series naming s.title = s.target; @@ -291,6 +317,8 @@ export class GraphiteDatasource } const frame = toDataFrame(s); + // Set the refID value on the frame + frame.refId = refIdMap[refId]; // Metrictank metadata if (s.meta) { diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/FromSection.tsx b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/FromSection.tsx index 458f2eb9176..153ec2aa039 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/FromSection.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/FromSection.tsx @@ -1,4 +1,4 @@ -import { AccessoryButton } from '@grafana/experimental'; +import { AccessoryButton } from '@grafana/plugin-ui'; import { DEFAULT_POLICY } from '../../../../../types'; import { toSelectableValue } from '../utils/toSelectableValue'; diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/PartListSection.tsx b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/PartListSection.tsx index 3bb380ee126..14f188bd683 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/PartListSection.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/PartListSection.tsx @@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css'; import { Fragment, useMemo } from 'react'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; -import { AccessoryButton } from '@grafana/experimental'; +import { AccessoryButton } from '@grafana/plugin-ui'; import { useTheme2 } from '@grafana/ui'; import { toSelectableValue } from '../utils/toSelectableValue'; diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/TagsSection.tsx b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/TagsSection.tsx index 9079bc056c7..3edc8320144 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/TagsSection.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/TagsSection.tsx @@ -1,5 +1,5 @@ import { SelectableValue } from '@grafana/data'; -import { AccessoryButton } from '@grafana/experimental'; +import { AccessoryButton } from '@grafana/plugin-ui'; import { InfluxQueryTag } from '../../../../../types'; import { adjustOperatorIfNeeded, getCondition, getOperator } from '../utils/tagUtils'; diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/VisualInfluxQLEditor.tags.test.tsx b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/VisualInfluxQLEditor.tags.test.tsx index 7d6b19d637f..910bb67fca5 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/VisualInfluxQLEditor.tags.test.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/VisualInfluxQLEditor.tags.test.tsx @@ -38,13 +38,12 @@ jest.mock('../../../../../influxql_metadata_query', () => { }; }); -jest.mock('@grafana/runtime', () => { - return { - getTemplateSrv: jest.fn().mockReturnValueOnce({ - getVariables: jest.fn().mockReturnValueOnce([]), - }), - }; -}); +jest.mock('@grafana/runtime', () => ({ + ...jest.requireActual('@grafana/runtime'), + getTemplateSrv: jest.fn().mockReturnValueOnce({ + getVariables: jest.fn().mockReturnValueOnce([]), + }), +})); beforeEach(() => { (mockedMeta.getTagKeys as jest.Mock).mockClear(); diff --git a/public/app/plugins/datasource/influxdb/datasource.test.ts b/public/app/plugins/datasource/influxdb/datasource.test.ts index b2cdd4522e3..284717ac701 100644 --- a/public/app/plugins/datasource/influxdb/datasource.test.ts +++ b/public/app/plugins/datasource/influxdb/datasource.test.ts @@ -363,6 +363,18 @@ describe('interpolateQueryExpr', () => { expect(result).toBe(expectation); }); + it('should **not** return the escaped value if the value **is not** wrapped in regex and the query is more complex (e.g. text is contained between two / but not a regex', () => { + const value = 'testmatch'; + const variableMock = queryBuilder().withId('tempVar').withName('tempVar').withMulti(false).build(); + const result = ds.interpolateQueryExpr( + value, + variableMock, + `select value where ("tag"::tag =~ /value/) AND where other = $tempVar $timeFilter GROUP BY time($__interval) tz('Europe/London')` + ); + const expectation = `testmatch`; + expect(result).toBe(expectation); + }); + it('should return floating point number as it is', () => { const variableMock = queryBuilder() .withId('tempVar') diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index acfa33d6ced..b6f55d1a3ef 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -351,17 +351,30 @@ export default class InfluxDatasource extends DataSourceWithBackend escapeRegex(v)).join('|')})`; + return typeof value === 'string' ? escapeRegex(value) : `(${value.map((v) => escapeRegex(v)).join('|')})`; } return value; diff --git a/public/app/plugins/datasource/influxdb/fsql/datasource.flightsql.ts b/public/app/plugins/datasource/influxdb/fsql/datasource.flightsql.ts index ce40ff3c53a..4d47cc0766d 100644 --- a/public/app/plugins/datasource/influxdb/fsql/datasource.flightsql.ts +++ b/public/app/plugins/datasource/influxdb/fsql/datasource.flightsql.ts @@ -1,8 +1,8 @@ import { v4 as uuidv4 } from 'uuid'; import { DataSourceInstanceSettings, TimeRange } from '@grafana/data'; -import { CompletionItemKind, LanguageDefinition, TableIdentifier } from '@grafana/experimental'; -import { TemplateSrv, config, getTemplateSrv } from '@grafana/runtime'; +import { CompletionItemKind, LanguageDefinition, TableIdentifier } from '@grafana/plugin-ui'; +import { TemplateSrv, getTemplateSrv } from '@grafana/runtime'; import { COMMON_FNS, DB, FuncParameter, SQLQuery, SqlDatasource, formatSQL } from '@grafana/sql'; import { mapFieldsToTypes } from './fields'; @@ -106,36 +106,32 @@ export class FlightSQLDatasource extends SqlDatasource { getFunctions = (): ReturnType => { const fns = [...COMMON_FNS, { name: 'VARIANCE' }, { name: 'STDDEV' }]; - if (config.featureToggles.sqlQuerybuilderFunctionParameters) { - const columnParam: FuncParameter = { - name: 'Column', - required: true, - options: (query) => this.fetchFields(query), - }; - const intervalParam: FuncParameter = { - name: 'Interval', - required: true, - options: () => { - return Promise.resolve([{ label: '$__interval', value: '$__interval' }]); - }, - }; + const columnParam: FuncParameter = { + name: 'Column', + required: true, + options: (query) => this.fetchFields(query), + }; + const intervalParam: FuncParameter = { + name: 'Interval', + required: true, + options: () => { + return Promise.resolve([{ label: '$__interval', value: '$__interval' }]); + }, + }; - return [ - ...fns.map((fn) => ({ ...fn, parameters: [columnParam] })), - { - name: '$__timeGroup', - description: 'Time grouping function', - parameters: [columnParam, intervalParam], - }, - { - name: '$__timeGroupAlias', - description: 'Time grouping function with time as alias', - parameters: [columnParam, intervalParam], - }, - ]; - } else { - return fns; - } + return [ + ...fns.map((fn) => ({ ...fn, parameters: [columnParam] })), + { + name: '$__timeGroup', + description: 'Time grouping function', + parameters: [columnParam, intervalParam], + }, + { + name: '$__timeGroupAlias', + description: 'Time grouping function with time as alias', + parameters: [columnParam, intervalParam], + }, + ]; }; getDB(): DB { diff --git a/public/app/plugins/datasource/influxdb/fsql/sqlCompletionProvider.ts b/public/app/plugins/datasource/influxdb/fsql/sqlCompletionProvider.ts index b44b2475027..19fca7d6f56 100644 --- a/public/app/plugins/datasource/influxdb/fsql/sqlCompletionProvider.ts +++ b/public/app/plugins/datasource/influxdb/fsql/sqlCompletionProvider.ts @@ -11,7 +11,7 @@ import { TableDefinition, TableIdentifier, TokenType, -} from '@grafana/experimental'; +} from '@grafana/plugin-ui'; interface CompletionProviderGetterArgs { getMeta: (t?: TableIdentifier) => Promise; diff --git a/public/app/plugins/datasource/jaeger/components/SearchForm.test.tsx b/public/app/plugins/datasource/jaeger/components/SearchForm.test.tsx index 533f5b900eb..719b2ca49d9 100644 --- a/public/app/plugins/datasource/jaeger/components/SearchForm.test.tsx +++ b/public/app/plugins/datasource/jaeger/components/SearchForm.test.tsx @@ -34,7 +34,7 @@ describe('SearchForm', () => { }; const ds = { async metadataRequest(url) { - if (url === '/api/services') { + if (url === 'services') { return Promise.resolve(['jaeger-query', 'service2', 'service3']); } return undefined; diff --git a/public/app/plugins/datasource/jaeger/components/SearchForm.tsx b/public/app/plugins/datasource/jaeger/components/SearchForm.tsx index f0eee728f38..c5840a60bab 100644 --- a/public/app/plugins/datasource/jaeger/components/SearchForm.tsx +++ b/public/app/plugins/datasource/jaeger/components/SearchForm.tsx @@ -68,7 +68,7 @@ export function SearchForm({ datasource, query, onChange }: Props) { useEffect(() => { const getServices = async () => { - const services = await loadOptions('/api/services', 'services'); + const services = await loadOptions('services', 'services'); if (query.service && getTemplateSrv().containsTemplate(query.service)) { services.push(toOption(query.service)); } @@ -80,7 +80,7 @@ export function SearchForm({ datasource, query, onChange }: Props) { useEffect(() => { const getOperations = async () => { const operations = await loadOptions( - `/api/services/${encodeURIComponent(getTemplateSrv().replace(query.service!))}/operations`, + `services/${encodeURIComponent(getTemplateSrv().replace(query.service!))}/operations`, 'operations' ); if (query.operation && getTemplateSrv().containsTemplate(query.operation)) { @@ -101,7 +101,7 @@ export function SearchForm({ datasource, query, onChange }: Props) { + updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'tagLimit', v.currentTarget.value) + } + value={options.jsonData.tagLimit || ''} + width={40} + /> + +
+
+ ); +} + +export const TagLimitSection = ({ options, onOptionsChange }: DataSourcePluginOptionsEditorProps) => { + return ( + + } + > + + + ); +}; + +const getStyles = (theme: GrafanaTheme2) => ({ + infoText: css({ + label: 'infoText', + paddingBottom: theme.spacing(2), + color: theme.colors.text.secondary, + }), + row: css({ + label: 'row', + alignItems: 'baseline', + }), +}); diff --git a/public/app/plugins/datasource/tempo/dataquery.cue b/public/app/plugins/datasource/tempo/dataquery.cue index 345a5d11409..ef5a4526dbb 100644 --- a/public/app/plugins/datasource/tempo/dataquery.cue +++ b/public/app/plugins/datasource/tempo/dataquery.cue @@ -55,10 +55,14 @@ composableKinds: DataQuery: { step?: string // For metric queries, how many exemplars to request, 0 means no exemplars exemplars?: int64 + // For metric queries, whether to run instant or range queries + metricsQueryType?: #MetricsQueryType } @cuetsy(kind="interface") @grafana(TSVeneer="type") #TempoQueryType: "traceql" | "traceqlSearch" | "serviceMap" | "upload" | "nativeSearch" | "traceId" | "clear" @cuetsy(kind="type") + #MetricsQueryType: "range" | "instant" @cuetsy(kind="enum") + // The state of the TraceQL streaming search query #SearchStreamingState: "pending" | "streaming" | "done" | "error" @cuetsy(kind="enum") diff --git a/public/app/plugins/datasource/tempo/dataquery.gen.ts b/public/app/plugins/datasource/tempo/dataquery.gen.ts index 31a2a9f3fbe..757960330c8 100644 --- a/public/app/plugins/datasource/tempo/dataquery.gen.ts +++ b/public/app/plugins/datasource/tempo/dataquery.gen.ts @@ -28,6 +28,10 @@ export interface TempoQuery extends common.DataQuery { * @deprecated Define the maximum duration to select traces. Use duration format, for example: 1.2s, 100ms */ maxDuration?: string; + /** + * For metric queries, whether to run instant or range queries + */ + metricsQueryType?: MetricsQueryType; /** * @deprecated Define the minimum duration to select traces. Use duration format, for example: 1.2s, 100ms */ @@ -77,6 +81,11 @@ export const defaultTempoQuery: Partial = { export type TempoQueryType = ('traceql' | 'traceqlSearch' | 'serviceMap' | 'upload' | 'nativeSearch' | 'traceId' | 'clear'); +export enum MetricsQueryType { + Instant = 'instant', + Range = 'range', +} + /** * The state of the TraceQL streaming search query */ diff --git a/public/app/plugins/datasource/tempo/datasource.test.ts b/public/app/plugins/datasource/tempo/datasource.test.ts index 4289a20e1cc..c51e27fdec8 100644 --- a/public/app/plugins/datasource/tempo/datasource.test.ts +++ b/public/app/plugins/datasource/tempo/datasource.test.ts @@ -15,6 +15,8 @@ import { DataQueryRequest, getTimeZone, PluginMetaInfo, + DataLink, + NodeGraphDataFrameFieldNames, } from '@grafana/data'; import { BackendDataSourceResponse, @@ -544,6 +546,32 @@ describe('Tempo service graph view', () => { expect(response.data[1].fields[0]?.config?.links?.length).toBeGreaterThan(0); expect(response.data[1].fields[0]?.config?.links).toEqual(serviceGraphLinks); + const viewServicesLink = response.data[1].fields[0]?.config?.links.find( + (link: DataLink) => link.title === 'View traces' + ); + expect(viewServicesLink).toBeDefined(); + expect(viewServicesLink.internal.query({ replaceVariables: replaceVariablesInstrumented })).toEqual({ + refId: 'A', + queryType: 'traceqlSearch', + filters: [ + { + id: 'service-name', + operator: '=', + scope: 'resource', + tag: 'service.name', + value: 'my-service', + valueType: 'string', + }, + ], + }); + expect(viewServicesLink.internal.query({ replaceVariables: replaceVariablesUninstrumented })).toEqual({ + refId: 'A', + queryType: 'traceql', + filters: [], + query: + '{span.db.name="my-service" || span.db.system="my-service" || span.peer.service="my-service" || span.messaging.system="my-service" || span.net.peer.name="my-service"}', + }); + expect(response.data[2].name).toBe('Edges'); expect(response.data[2].fields[0].values.length).toBe(2); }); @@ -584,14 +612,26 @@ describe('Tempo service graph view', () => { 'sum by (client, server) (rate(traces_service_graph_request_server_seconds_sum{ foo="bar" }[$__range]))' ); expect(nthQuery(0).targets[1].expr).toBe( - 'sum by (client, server) (rate(traces_service_graph_request_total{ foo="bar" }[$__range]))' + 'group by (client, connection_type, server) (traces_service_graph_request_server_seconds_sum{ foo="bar" })' ); expect(nthQuery(0).targets[2].expr).toBe( - 'sum by (client, server) (rate(traces_service_graph_request_failed_total{ foo="bar" }[$__range]))' + 'sum by (client, server) (rate(traces_service_graph_request_total{ foo="bar" }[$__range]))' ); expect(nthQuery(0).targets[3].expr).toBe( + 'group by (client, connection_type, server) (traces_service_graph_request_total{ foo="bar" })' + ); + expect(nthQuery(0).targets[4].expr).toBe( + 'sum by (client, server) (rate(traces_service_graph_request_failed_total{ foo="bar" }[$__range]))' + ); + expect(nthQuery(0).targets[5].expr).toBe( + 'group by (client, connection_type, server) (traces_service_graph_request_failed_total{ foo="bar" })' + ); + expect(nthQuery(0).targets[6].expr).toBe( 'sum by (client, server) (rate(traces_service_graph_request_server_seconds_bucket{ foo="bar" }[$__range]))' ); + expect(nthQuery(0).targets[7].expr).toBe( + 'group by (client, connection_type, server) (traces_service_graph_request_server_seconds_bucket{ foo="bar" })' + ); }); it('runs correct queries with multiple serviceMapQuery defined', async () => { @@ -632,14 +672,26 @@ describe('Tempo service graph view', () => { 'sum by (client, server) (rate(traces_service_graph_request_server_seconds_sum{ foo="bar" }[$__range])) OR sum by (client, server) (rate(traces_service_graph_request_server_seconds_sum{baz="bad"}[$__range]))' ); expect(nthQuery(0).targets[1].expr).toBe( - 'sum by (client, server) (rate(traces_service_graph_request_total{ foo="bar" }[$__range])) OR sum by (client, server) (rate(traces_service_graph_request_total{baz="bad"}[$__range]))' + 'group by (client, connection_type, server) (traces_service_graph_request_server_seconds_sum{ foo="bar" }) OR group by (client, connection_type, server) (traces_service_graph_request_server_seconds_sum{baz="bad"})' ); expect(nthQuery(0).targets[2].expr).toBe( - 'sum by (client, server) (rate(traces_service_graph_request_failed_total{ foo="bar" }[$__range])) OR sum by (client, server) (rate(traces_service_graph_request_failed_total{baz="bad"}[$__range]))' + 'sum by (client, server) (rate(traces_service_graph_request_total{ foo="bar" }[$__range])) OR sum by (client, server) (rate(traces_service_graph_request_total{baz="bad"}[$__range]))' ); expect(nthQuery(0).targets[3].expr).toBe( + 'group by (client, connection_type, server) (traces_service_graph_request_total{ foo="bar" }) OR group by (client, connection_type, server) (traces_service_graph_request_total{baz="bad"})' + ); + expect(nthQuery(0).targets[4].expr).toBe( + 'sum by (client, server) (rate(traces_service_graph_request_failed_total{ foo="bar" }[$__range])) OR sum by (client, server) (rate(traces_service_graph_request_failed_total{baz="bad"}[$__range]))' + ); + expect(nthQuery(0).targets[5].expr).toBe( + 'group by (client, connection_type, server) (traces_service_graph_request_failed_total{ foo="bar" }) OR group by (client, connection_type, server) (traces_service_graph_request_failed_total{baz="bad"})' + ); + expect(nthQuery(0).targets[6].expr).toBe( 'sum by (client, server) (rate(traces_service_graph_request_server_seconds_bucket{ foo="bar" }[$__range])) OR sum by (client, server) (rate(traces_service_graph_request_server_seconds_bucket{baz="bad"}[$__range]))' ); + expect(nthQuery(0).targets[7].expr).toBe( + 'group by (client, connection_type, server) (traces_service_graph_request_server_seconds_bucket{ foo="bar" }) OR group by (client, connection_type, server) (traces_service_graph_request_server_seconds_bucket{baz="bad"})' + ); }); it('should build expr correctly', () => { @@ -803,27 +855,33 @@ describe('Tempo service graph view', () => { url: '', title: 'View traces', internal: { - query: { - refId: 'A', - queryType: 'traceqlSearch', - filters: [ - { - id: 'service-name', - operator: '=', - scope: 'resource', - tag: 'service.name', - value: '${__data.fields.target}', - valueType: 'string', - }, - ], - }, - datasourceUid: 'EbPO1fYnz', datasourceName: '', + datasourceUid: 'EbPO1fYnz', + query: expect.any(Function), }, }, ], }; expect(fieldConfig).toStrictEqual(resultObj); + + const viewServicesLink: DataLink | undefined = fieldConfig.links.find( + (link: DataLink) => link.title === 'View traces' + ); + expect(viewServicesLink).toBeDefined(); + expect(viewServicesLink!.internal!.query({ replaceVariables: replaceVariablesInstrumented })).toEqual({ + refId: 'A', + queryType: 'traceqlSearch', + filters: [ + { + id: 'service-name', + operator: '=', + scope: 'resource', + tag: 'service.name', + value: 'my-service', + valueType: 'string', + }, + ], + }); }); it('should get field config correctly when namespaces are present', () => { @@ -894,35 +952,41 @@ describe('Tempo service graph view', () => { url: '', title: 'View traces', internal: { - query: { - queryType: 'traceqlSearch', - refId: 'A', - filters: [ - { - id: 'service-namespace', - operator: '=', - scope: 'resource', - tag: 'service.namespace', - value: '${__data.fields.targetNamespace}', - valueType: 'string', - }, - { - id: 'service-name', - operator: '=', - scope: 'resource', - tag: 'service.name', - value: '${__data.fields.targetName}', - valueType: 'string', - }, - ], - }, - datasourceUid: 'EbPO1fYnz', datasourceName: '', + datasourceUid: 'EbPO1fYnz', + query: expect.any(Function), }, }, ], }; expect(fieldConfig).toStrictEqual(resultObj); + + const viewServicesLink: DataLink | undefined = fieldConfig.links.find( + (link: DataLink) => link.title === 'View traces' + ); + expect(viewServicesLink).toBeDefined(); + expect(viewServicesLink!.internal!.query({ replaceVariables: replaceVariablesInstrumented })).toEqual({ + refId: 'A', + queryType: 'traceqlSearch', + filters: [ + { + id: 'service-namespace', + operator: '=', + scope: 'resource', + tag: 'service.namespace', + value: 'my-namespace', + valueType: 'string', + }, + { + id: 'service-name', + operator: '=', + scope: 'resource', + tag: 'service.name', + value: 'my-service', + valueType: 'string', + }, + ], + }); }); it('should get rate aligned values correctly', () => { @@ -1435,26 +1499,31 @@ const serviceGraphLinks = [ url: '', title: 'View traces', internal: { - query: { - refId: 'A', - queryType: 'traceqlSearch', - filters: [ - { - id: 'service-name', - operator: '=', - scope: 'resource', - tag: 'service.name', - value: '${__data.fields.id}', - valueType: 'string', - }, - ], - } as TempoQuery, + query: expect.any(Function), datasourceUid: 'gdev-tempo', datasourceName: 'Tempo', }, }, ]; +const replaceVariablesInstrumented = (variable: string): string => { + const variables: Record = { + [`\${__data.fields.${NodeGraphDataFrameFieldNames.title}}`]: 'my-service', + [`\${__data.fields.${NodeGraphDataFrameFieldNames.subTitle}}`]: 'my-namespace', + [`\${__data.fields.${NodeGraphDataFrameFieldNames.isInstrumented}}`]: 'true', + }; + return variables[variable]; +}; + +const replaceVariablesUninstrumented = (variable: string): string => { + const variables: Record = { + [`\${__data.fields.${NodeGraphDataFrameFieldNames.title}}`]: 'my-service', + [`\${__data.fields.${NodeGraphDataFrameFieldNames.subTitle}}`]: 'my-namespace', + [`\${__data.fields.${NodeGraphDataFrameFieldNames.isInstrumented}}`]: 'false', + }; + return variables[variable]; +}; + interface PromQuery extends DataQuery { expr: string; } diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index 8d8b0a3e5ee..b06a9d7bcc3 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -7,6 +7,7 @@ import { CoreApp, DataFrame, DataFrameDTO, + DataLink, DataQueryRequest, DataQueryResponse, DataQueryResponseData, @@ -15,6 +16,7 @@ import { dateTime, FieldType, LoadingState, + NodeGraphDataFrameFieldNames, rangeUtil, ScopedVars, SelectableValue, @@ -37,6 +39,7 @@ import { BarGaugeDisplayMode, TableCellDisplayMode, VariableFormatID } from '@gr import { generateQueryFromAdHocFilters, getTagWithoutScope, interpolateFilters } from './SearchTraceQLEditor/utils'; import { TempoVariableQuery, TempoVariableQueryType } from './VariableQueryEditor'; import { PrometheusDatasource, PromQuery } from './_importedDependencies/datasources/prometheus/types'; +import { TagLimitOptions } from './configuration/TagLimitSettings'; import { SearchTableType, TraceqlFilter, TraceqlSearchScope } from './dataquery.gen'; import { defaultTableFilter, @@ -57,7 +60,7 @@ import { transformFromOTLP as transformFromOTEL, transformTrace, } from './resultTransformer'; -import { doTempoChannelStream } from './streaming'; +import { doTempoMetricsStreaming, doTempoSearchStreaming } from './streaming'; import { TempoJsonData, TempoQuery } from './types'; import { getErrorMessage, migrateFromSearchToTraceQLSearch } from './utils'; import { TempoVariableSupport } from './variables'; @@ -66,7 +69,8 @@ export const DEFAULT_LIMIT = 20; export const DEFAULT_SPSS = 3; // spans per span set export enum FeatureName { - streaming = 'streaming', + searchStreaming = 'searchStreaming', + metricsStreaming = 'metricsStreaming', } /* Map, for each feature (e.g., streaming), the minimum Tempo version required to have that @@ -74,7 +78,8 @@ export enum FeatureName { ** target version, the feature is disabled in Grafana (frontend). */ export const featuresToTempoVersion = { - [FeatureName.streaming]: '2.2.0', + [FeatureName.searchStreaming]: '2.2.0', + [FeatureName.metricsStreaming]: '2.7.0', }; // The version that we use as default in case we cannot retrieve it from the backend. @@ -109,17 +114,19 @@ export class TempoDatasource extends DataSourceWithBackend, + public instanceSettings: DataSourceInstanceSettings, private readonly templateSrv: TemplateSrv = getTemplateSrv() ) { super(instanceSettings); @@ -289,6 +296,18 @@ export class TempoDatasource extends DataSourceWithBackend - doTempoChannelStream( + doTempoSearchStreaming( { ...target, query }, this, // the datasource options, @@ -697,6 +721,28 @@ export class TempoDatasource extends DataSourceWithBackend, + targets: TempoQuery[], + query: string + ): Observable { + if (query === '') { + return EMPTY; + } + + return merge( + ...targets.map((target) => + doTempoMetricsStreaming( + { ...target, query }, + this, // the datasource + options + ) + ) + ); + } + makeTraceIdRequest(options: DataQueryRequest, targets: TempoQuery[]): DataQueryRequest { const request = { ...options, @@ -1078,13 +1124,7 @@ export function getFieldConfig( datasourceUid, false ), - makeTempoLink( - 'View traces', - namespaceFields !== undefined ? `\${${namespaceFields.targetNamespace}}` : '', - `\${${targetField}}`, - '', - tempoDatasourceUid - ), + makeTempoLinkServiceMap('View traces', tempoDatasourceUid, !!namespaceFields?.targetNamespace), ], }; } @@ -1139,25 +1179,99 @@ export function makeTempoLink( }; } +function makeTempoLinkServiceMap( + title: string, + datasourceUid: string, + includeNamespace: boolean +): DataLink { + return { + url: '', + title, + internal: { + datasourceUid, + datasourceName: getDataSourceSrv().getInstanceSettings(datasourceUid)?.name ?? '', + query: ({ replaceVariables, scopedVars }) => { + const serviceName = replaceVariables?.(`\${__data.fields.${NodeGraphDataFrameFieldNames.title}}`, scopedVars); + const serviceNamespace = replaceVariables?.( + `\${__data.fields.${NodeGraphDataFrameFieldNames.subTitle}}`, + scopedVars + ); + const isInstrumented = + replaceVariables?.(`\${__data.fields.${NodeGraphDataFrameFieldNames.isInstrumented}}`, scopedVars) !== + 'false'; + const query: TempoQuery = { refId: 'A', queryType: 'traceqlSearch', filters: [] }; + + // Only do the peer query if service is actively set as not instrumented + if (isInstrumented === false) { + const filters = ['db.name', 'db.system', 'peer.service', 'messaging.system', 'net.peer.name'] + .map((peerAttribute) => `span.${peerAttribute}="${serviceName}"`) + .join(' || '); + query.queryType = 'traceql'; + query.query = `{${filters}}`; + } else { + if (includeNamespace && serviceNamespace) { + query.filters.push({ + id: 'service-namespace', + scope: TraceqlSearchScope.Resource, + tag: 'service.namespace', + value: serviceNamespace, + operator: '=', + valueType: 'string', + }); + } + if (serviceName) { + query.filters.push({ + id: 'service-name', + scope: TraceqlSearchScope.Resource, + tag: 'service.name', + value: serviceName, + operator: '=', + valueType: 'string', + }); + } + } + + return query; + }, + }, + }; +} + function makePromServiceMapRequest(options: DataQueryRequest): DataQueryRequest { return { ...options, - targets: serviceMapMetrics.map((metric) => { - const { serviceMapQuery, serviceMapIncludeNamespace: serviceMapIncludeNamespace } = options.targets[0]; - const extraSumByFields = serviceMapIncludeNamespace ? ', client_service_namespace, server_service_namespace' : ''; - const queries = Array.isArray(serviceMapQuery) ? serviceMapQuery : [serviceMapQuery]; - const subExprs = queries.map( - (query) => `sum by (client, server${extraSumByFields}) (rate(${metric}${query || ''}[$__range]))` - ); - return { - format: 'table', - refId: metric, - // options.targets[0] is not correct here, but not sure what should happen if you have multiple queries for - // service map at the same time anyway - expr: subExprs.join(' OR '), - instant: true, - }; - }), + targets: serviceMapMetrics + .map((metric) => { + const { serviceMapQuery, serviceMapIncludeNamespace: serviceMapIncludeNamespace } = options.targets[0]; + const extraSumByFields = serviceMapIncludeNamespace + ? ', client_service_namespace, server_service_namespace' + : ''; + const queries = Array.isArray(serviceMapQuery) ? serviceMapQuery : [serviceMapQuery]; + const sumSubExprs = queries.map( + (query) => `sum by (client, server${extraSumByFields}) (rate(${metric}${query || ''}[$__range]))` + ); + const groupSubExprs = queries.map( + (query) => `group by (client, connection_type, server${extraSumByFields}) (${metric}${query || ''})` + ); + + return [ + { + format: 'table', + refId: metric, + // options.targets[0] is not correct here, but not sure what should happen if you have multiple queries for + // service map at the same time anyway + expr: sumSubExprs.join(' OR '), + instant: true, + }, + { + format: 'table', + refId: `${metric}_labels`, + expr: groupSubExprs.join(' OR '), + instant: true, + }, + ]; + }) + .flat(), }; } diff --git a/public/app/plugins/datasource/tempo/graphTransform.test.ts b/public/app/plugins/datasource/tempo/graphTransform.test.ts index 2726a1b4141..bcd723c1310 100644 --- a/public/app/plugins/datasource/tempo/graphTransform.test.ts +++ b/public/app/plugins/datasource/tempo/graphTransform.test.ts @@ -20,6 +20,7 @@ it('assigns correct field type even if values are numbers', async () => { { name: 'secondarystat', values: [10, 20], type: FieldType.number }, { name: 'arc__success', values: [1, 1], type: FieldType.number }, { name: 'arc__failed', values: [0, 0], type: FieldType.number }, + { name: 'isinstrumented', values: [true, true], type: FieldType.boolean }, ]); }); @@ -41,6 +42,7 @@ it('do not fail on response with empty list', async () => { { name: 'secondarystat', values: [], type: FieldType.number }, { name: 'arc__success', values: [], type: FieldType.number }, { name: 'arc__failed', values: [], type: FieldType.number }, + { name: 'isinstrumented', values: [], type: FieldType.boolean }, ]); }); @@ -66,6 +68,7 @@ describe('mapPromMetricsToServiceMap', () => { { name: 'secondarystat', values: [10, 20, NaN] }, { name: 'arc__success', values: [0.8, 0.25, 1] }, { name: 'arc__failed', values: [0.2, 0.75, 0] }, + { name: 'isinstrumented', values: [true, true, true] }, ]); expect(edges.fields).toMatchObject([ { name: 'id', values: ['app_db', 'lb_app'] }, @@ -101,6 +104,7 @@ describe('mapPromMetricsToServiceMap', () => { { name: 'secondarystat', values: [10, 20, NaN] }, { name: 'arc__success', values: [0.8, 0.25, 1] }, { name: 'arc__failed', values: [0.2, 0.75, 0] }, + { name: 'isinstrumented', values: [true, true, true] }, ]); expect(edges.fields).toMatchObject([ { name: 'id', values: ['ns1/app_ns3/db', 'ns2/lb_ns1/app'] }, @@ -138,6 +142,41 @@ describe('mapPromMetricsToServiceMap', () => { { name: 'secondarystat', values: [10, 20, NaN] }, { name: 'arc__success', values: [0, 0, 1] }, { name: 'arc__failed', values: [1, 1, 0] }, + { name: 'isinstrumented', values: [true, true, true] }, + ]); + }); + + it('handles setting isInstrumented based on the connection_type', () => { + const range = { + from: dateTime('2000-01-01T00:00:00'), + to: dateTime('2000-01-01T00:01:00'), + }; + const { nodes } = mapPromMetricsToServiceMap( + [ + { + data: [ + totalsPromMetric(true), + secondsPromMetric(true), + secondsLabelsPromMetric(true), + failedPromMetric(true), + ], + }, + ], + { + ...range, + raw: range, + } + ); + + expect(nodes.fields).toMatchObject([ + { name: 'id', values: ['ns3/db', 'ns1/app', 'ns2/lb'] }, + { name: 'title', values: ['db', 'app', 'lb'] }, + { name: 'subtitle', values: ['ns3', 'ns1', 'ns2'] }, + { name: 'mainstat', values: [1000, 2000, NaN] }, + { name: 'secondarystat', values: [10, 20, NaN] }, + { name: 'arc__success', values: [0.8, 0.25, 1] }, + { name: 'arc__failed', values: [0.2, 0.75, 0] }, + { name: 'isinstrumented', values: [true, false, true] }, ]); }); }); @@ -182,6 +221,27 @@ const secondsPromMetric = (namespace?: boolean) => ], }); +const secondsLabelsPromMetric = (namespace?: boolean) => + createDataFrame({ + refId: 'traces_service_graph_request_server_seconds_sum_labels', + fields: [ + { name: 'Time', values: [1628169788000, 1628169788000] }, + { name: 'client', values: ['app', 'lb'] }, + { name: 'instance', values: ['127.0.0.1:12345', '127.0.0.1:12345'] }, + { name: 'job', values: ['local_scrape', 'local_scrape'] }, + { name: 'server', values: ['db', 'app'] }, + { name: 'tempo_config', values: ['default', 'default'] }, + { name: 'Value #traces_service_graph_request_server_seconds_sum_label', values: [1, 1] }, + { name: 'connection_type', values: ['messaging_system', 'virtual_node'] }, + ...(namespace + ? [ + { name: 'client_service_namespace', values: ['ns1', 'ns2'] }, + { name: 'server_service_namespace', values: ['ns3', 'ns1'] }, + ] + : []), + ], + }); + const failedPromMetric = (namespace?: boolean) => createDataFrame({ refId: 'traces_service_graph_request_failed_total', diff --git a/public/app/plugins/datasource/tempo/graphTransform.ts b/public/app/plugins/datasource/tempo/graphTransform.ts index 9efc7eea29c..caf5f49ead7 100644 --- a/public/app/plugins/datasource/tempo/graphTransform.ts +++ b/public/app/plugins/datasource/tempo/graphTransform.ts @@ -61,6 +61,10 @@ export function mapPromMetricsToServiceMap( collectMetricData(frames[secondsMetric], 'seconds', secondsMetric, nodesMap, edgesMap); collectMetricData(frames[failedMetric], 'failed', failedMetric, nodesMap, edgesMap); + collectIsInstrumented(frames[`${totalsMetric}_labels`], nodesMap); + collectIsInstrumented(frames[`${secondsMetric}_labels`], nodesMap); + collectIsInstrumented(frames[`${failedMetric}_labels`], nodesMap); + return convertToDataFrames(nodesMap, edgesMap, range); } @@ -97,6 +101,11 @@ function createServiceMapDataFrames() { config: { displayName: 'Failed', color: { fixedColor: 'red', mode: FieldColorModeId.Fixed } }, values: [], }, + { + name: Fields.isInstrumented, + type: FieldType.boolean, + values: [], + }, ]); const edges = createDF('Edges', [ { name: Fields.id, type: FieldType.string, values: [] }, @@ -145,6 +154,7 @@ type ServiceMapStatistics = { type NodeObject = ServiceMapStatistics & { name: string; namespace?: string; + isInstrumented?: boolean; }; type EdgeObject = ServiceMapStatistics & { @@ -240,6 +250,21 @@ function collectMetricData( } } +function collectIsInstrumented(frame: DataFrameView | undefined, nodesMap: Record) { + if (!frame) { + return; + } + + for (let i = 0; i < frame.length; i++) { + const row = frame.get(i); + const serverId = row.server_service_namespace ? `${row.server_service_namespace}/${row.server}` : row.server; + + if (nodesMap[serverId] && nodesMap[serverId].isInstrumented !== true) { + nodesMap[serverId].isInstrumented = row.connection_type === '' || row.connection_type === 'messaging_system'; + } + } +} + function convertToDataFrames( nodesMap: Record, edgesMap: Record, @@ -258,6 +283,7 @@ function convertToDataFrames( [Fields.secondaryStat]: node.total ? Math.round(node.total * 100) / 100 : Number.NaN, // Request per second (to 2 decimals) [Fields.arc + 'success']: node.total ? (node.total - Math.min(node.failed || 0, node.total)) / node.total : 1, [Fields.arc + 'failed']: node.total ? Math.min(node.failed || 0, node.total) / node.total : 0, + [Fields.isInstrumented]: node.isInstrumented ?? true, }); } for (const edgeId of Object.keys(edgesMap)) { diff --git a/public/app/plugins/datasource/tempo/language_provider.ts b/public/app/plugins/datasource/tempo/language_provider.ts index 0691d9b5a83..63639645ef1 100644 --- a/public/app/plugins/datasource/tempo/language_provider.ts +++ b/public/app/plugins/datasource/tempo/language_provider.ts @@ -14,6 +14,12 @@ import { TempoDatasource } from './datasource'; import { intrinsicsV1 } from './traceql/traceql'; import { Scope } from './types'; +// Limit maximum tags retrieved from the backend +export const TAGS_LIMIT = 5000; + +// Limit maximum options in select dropdowns +export const OPTIONS_LIMIT = 1000; + export default class TempoLanguageProvider extends LanguageProvider { datasource: TempoDatasource; tagsV1?: string[]; @@ -40,10 +46,14 @@ export default class TempoLanguageProvider extends LanguageProvider { return this.startTask; }; + getTagsLimit = () => { + return this.datasource.instanceSettings.jsonData?.tagLimit || TAGS_LIMIT; + }; + async fetchTags() { let v1Resp, v2Resp; try { - v2Resp = await this.request('/api/v2/search/tags', []); + v2Resp = await this.request('/api/v2/search/tags', { limit: this.getTagsLimit() }); } catch (error) { v1Resp = await this.request('/api/search/tags', []); } @@ -150,7 +160,9 @@ export default class TempoLanguageProvider extends LanguageProvider { const encodedTag = this.encodeTag(tag); const response = await this.request( `/api/v2/search/tag/${encodedTag}/values`, - query ? { q: getTemplateSrv().replace(query, {}, VariableFormatID.Pipe) } : {} + query + ? { q: getTemplateSrv().replace(query, {}, VariableFormatID.Pipe), limit: this.getTagsLimit() } + : { limit: this.getTagsLimit() } ); let options: Array> = []; if (response && response.tagValues) { diff --git a/public/app/plugins/datasource/tempo/package.json b/public/app/plugins/datasource/tempo/package.json index f81c885cc34..1cf120ff959 100644 --- a/public/app/plugins/datasource/tempo/package.json +++ b/public/app/plugins/datasource/tempo/package.json @@ -2,16 +2,16 @@ "name": "@grafana-plugins/tempo", "description": "Grafana plugin for the Tempo data source.", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", "@grafana/data": "workspace:*", "@grafana/e2e-selectors": "workspace:*", - "@grafana/experimental": "2.1.6", "@grafana/lezer-logql": "0.2.6", "@grafana/lezer-traceql": "0.0.20", "@grafana/monaco-logql": "^0.0.8", "@grafana/o11y-ds-frontend": "workspace:*", + "@grafana/plugin-ui": "0.10.1", "@grafana/runtime": "workspace:*", "@grafana/schema": "workspace:*", "@grafana/ui": "workspace:*", @@ -29,24 +29,24 @@ "prismjs": "1.29.0", "react": "18.3.1", "react-dom": "18.3.1", - "react-select": "5.9.0", + "react-select": "5.10.0", "react-use": "17.6.0", "rxjs": "7.8.1", - "semver": "7.6.3", + "semver": "7.7.0", "stream-browserify": "3.0.0", "string_decoder": "1.3.0", "tslib": "2.8.1", "uuid": "11.0.5" }, "devDependencies": { - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/plugin-configs": "11.6.0-pre", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.6.3", - "@testing-library/react": "16.1.0", - "@testing-library/user-event": "14.5.2", + "@testing-library/react": "16.2.0", + "@testing-library/user-event": "14.6.1", "@types/jest": "29.5.14", - "@types/lodash": "4.17.14", - "@types/node": "22.10.7", + "@types/lodash": "4.17.15", + "@types/node": "22.12.0", "@types/prismjs": "1.26.5", "@types/react": "18.3.18", "@types/react-dom": "18.3.5", diff --git a/public/app/plugins/datasource/tempo/streaming.ts b/public/app/plugins/datasource/tempo/streaming.ts index 333faca7388..a1dd6e7ea68 100644 --- a/public/app/plugins/datasource/tempo/streaming.ts +++ b/public/app/plugins/datasource/tempo/streaming.ts @@ -1,31 +1,36 @@ import { capitalize } from 'lodash'; -import { map, Observable, takeWhile } from 'rxjs'; +import { map, Observable, scan, takeWhile } from 'rxjs'; import { v4 as uuidv4 } from 'uuid'; import { DataFrame, + dataFrameFromJSON, DataQueryRequest, DataQueryResponse, DataSourceInstanceSettings, + FieldCache, FieldType, LiveChannelScope, LoadingState, MutableDataFrame, + sortDataFrame, ThresholdsConfig, ThresholdsMode, } from '@grafana/data'; +import { cloneQueryResponse, combineResponses } from '@grafana/o11y-ds-frontend'; import { getGrafanaLiveSrv } from '@grafana/runtime'; import { SearchStreamingState } from './dataquery.gen'; import { DEFAULT_SPSS, TempoDatasource } from './datasource'; import { formatTraceQLResponse } from './resultTransformer'; import { SearchMetrics, TempoJsonData, TempoQuery } from './types'; +import { stepToNanos } from './utils'; function getLiveStreamKey(): string { return uuidv4(); } -export function doTempoChannelStream( +export function doTempoSearchStreaming( query: TempoQuery, ds: TempoDatasource, options: DataQueryRequest, @@ -67,11 +72,14 @@ export function doTempoChannelStream( if ('message' in evt && evt?.message) { const currentTime = performance.now(); const elapsedTime = currentTime - requestTime; - // Schema should be [traces, metrics, state, error] - const traces = evt.message.data.values[0][0]; - const metrics = evt.message.data.values[1][0]; - const frameState: SearchStreamingState = evt.message.data.values[2][0]; - const error = evt.message.data.values[3][0]; + + const messageFrame = dataFrameFromJSON(evt.message); + const fieldCache = new FieldCache(messageFrame); + + const traces = fieldCache.getFieldByName('result')?.values[0]; + const metrics = fieldCache.getFieldByName('metrics')?.values[0]; + const frameState = fieldCache.getFieldByName('state')?.values[0]; + const error = fieldCache.getFieldByName('error')?.values[0]; switch (frameState) { case SearchStreamingState.Done: @@ -100,6 +108,127 @@ export function doTempoChannelStream( ); } +export function doTempoMetricsStreaming( + query: TempoQuery, + ds: TempoDatasource, + options: DataQueryRequest +): Observable { + const range = options.range; + const key = getLiveStreamKey(); + + let state: LoadingState = LoadingState.NotStarted; + const step = stepToNanos(query.step); + + return getGrafanaLiveSrv() + .getStream({ + scope: LiveChannelScope.DataSource, + namespace: ds.uid, + path: `metrics/${key}`, + data: { + ...query, + step, + timeRange: { + from: range.from.toISOString(), + to: range.to.toISOString(), + }, + }, + }) + .pipe( + takeWhile((evt) => { + if ('message' in evt && evt?.message) { + const frameState: SearchStreamingState = evt.message.data.values[2][0]; + if (frameState === SearchStreamingState.Done || frameState === SearchStreamingState.Error) { + return false; + } + } + return true; + }, true), + map((evt) => { + let newResult: DataQueryResponse = { data: [], state: LoadingState.NotStarted }; + if ('message' in evt && evt?.message) { + const messageFrame = dataFrameFromJSON(evt.message); + const fieldCache = new FieldCache(messageFrame); + + const data = fieldCache.getFieldByName('result')?.values[0]; + const frameState = fieldCache.getFieldByName('state')?.values[0]; + const error = fieldCache.getFieldByName('error')?.values[0]; + + switch (frameState) { + case SearchStreamingState.Done: + state = LoadingState.Done; + break; + case SearchStreamingState.Streaming: + state = LoadingState.Streaming; + break; + case SearchStreamingState.Error: + throw new Error(error); + } + + newResult = { + data: data?.map(dataFrameFromJSON) ?? [], + state, + }; + } + + return newResult; + }), + // Merge results on acc + scan((acc, curr) => { + if (!curr) { + return acc; + } + if (!acc) { + return cloneQueryResponse(curr); + } + return mergeFrames(acc, curr); + }) + ); +} + +function mergeFrames(acc: DataQueryResponse, newResult: DataQueryResponse): DataQueryResponse { + const result = combineResponses(cloneQueryResponse(acc), newResult); + + // Remove duplicate time field values for all frames + result.data = result.data.map((frame: DataFrame) => { + let newFrame = frame; + const timeFieldIndex = frame.fields.findIndex((f) => f.type === FieldType.time); + if (timeFieldIndex >= 0) { + removeDuplicateTimeFieldValues(frame, timeFieldIndex); + newFrame = sortDataFrame(frame, timeFieldIndex); + } + return newFrame; + }); + + result.state = newResult.state; + return result; +} + +/** + * Remove duplicate time field values from the DataFrame. This is necessary because Tempo sends partial results to Grafana + * that we append to an existing DataFrame. This can result in duplicate values for the same timestamp so this function removes + * older values and keeps the latest value. + * @param accFrame + * @param timeFieldIndex + */ +function removeDuplicateTimeFieldValues(accFrame: DataFrame, timeFieldIndex: number) { + const duplicatesMap = accFrame.fields[timeFieldIndex].values.reduce((acc: Record, value, index) => { + if (acc[value]) { + acc[value].push(index); + } else { + acc[value] = [index]; + } + return acc; + }, {}); + + const indexesToRemove = Object.values(duplicatesMap) + .filter((indexes) => indexes.length > 1) + .map((indexes) => indexes.slice(1)) + .flat(); + accFrame.fields.forEach((field) => { + field.values = field.values.filter((_, index) => !indexesToRemove.includes(index)); + }); +} + function metricsDataFrame(metrics: SearchMetrics, state: SearchStreamingState, elapsedTime: number) { const progressThresholds: ThresholdsConfig = { steps: [ diff --git a/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx b/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx index b64aa5865e9..c4c90c78041 100644 --- a/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx +++ b/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx @@ -71,7 +71,8 @@ export function QueryEditor(props: Props) { diff --git a/public/app/plugins/datasource/tempo/traceql/TempoQueryBuilderOptions.tsx b/public/app/plugins/datasource/tempo/traceql/TempoQueryBuilderOptions.tsx index 25a13cc349a..7552565acc1 100644 --- a/public/app/plugins/datasource/tempo/traceql/TempoQueryBuilderOptions.tsx +++ b/public/app/plugins/datasource/tempo/traceql/TempoQueryBuilderOptions.tsx @@ -1,17 +1,21 @@ +import { css } from '@emotion/css'; import * as React from 'react'; +import { useToggle } from 'react-use'; -import { EditorField, EditorRow } from '@grafana/experimental'; -import { AutoSizeInput, RadioButtonGroup } from '@grafana/ui'; +import { GrafanaTheme2 } from '@grafana/data'; +import { EditorField, EditorRow } from '@grafana/plugin-ui'; +import { AutoSizeInput, RadioButtonGroup, useStyles2 } from '@grafana/ui'; import { QueryOptionGroup } from '../_importedDependencies/datasources/prometheus/QueryOptionGroup'; -import { SearchTableType } from '../dataquery.gen'; +import { SearchTableType, MetricsQueryType } from '../dataquery.gen'; import { DEFAULT_LIMIT, DEFAULT_SPSS } from '../datasource'; import { TempoQuery } from '../types'; interface Props { onChange: (value: TempoQuery) => void; query: Partial & TempoQuery; - isStreaming: boolean; + searchStreaming: boolean; + metricsStreaming: boolean; } /** @@ -27,7 +31,10 @@ const parseIntWithFallback = (val: string, fallback: number) => { return isNaN(parsed) ? fallback : parsed; }; -export const TempoQueryBuilderOptions = React.memo(({ onChange, query, isStreaming }) => { +export const TempoQueryBuilderOptions = React.memo(({ onChange, query, searchStreaming, metricsStreaming }) => { + const styles = useStyles2(getStyles); + const [isOpen, toggleOpen] = useToggle(false); + if (!query.hasOwnProperty('limit')) { query.limit = DEFAULT_LIMIT; } @@ -36,6 +43,10 @@ export const TempoQueryBuilderOptions = React.memo(({ onChange, query, is query.tableType = SearchTableType.Traces; } + if (!query.hasOwnProperty('metricsQueryType')) { + query.metricsQueryType = MetricsQueryType.Range; + } + const onLimitChange = (e: React.FormEvent) => { onChange({ ...query, limit: parseIntWithFallback(e.currentTarget.value, DEFAULT_LIMIT) }); }; @@ -45,6 +56,9 @@ export const TempoQueryBuilderOptions = React.memo(({ onChange, query, is const onTableTypeChange = (val: SearchTableType) => { onChange({ ...query, tableType: val }); }; + const onMetricsQueryTypeChange = (val: MetricsQueryType) => { + onChange({ ...query, metricsQueryType: val }); + }; const onStepChange = (e: React.FormEvent) => { onChange({ ...query, step: e.currentTarget.value }); }; @@ -60,21 +74,31 @@ export const TempoQueryBuilderOptions = React.memo(({ onChange, query, is // } // }; - const collapsedInfoList = [ + const collapsedSearchOptions = [ `Limit: ${query.limit || DEFAULT_LIMIT}`, `Spans Limit: ${query.spss || DEFAULT_SPSS}`, `Table Format: ${query.tableType === SearchTableType.Traces ? 'Traces' : 'Spans'}`, '|', + `Streaming: ${searchStreaming ? 'Enabled' : 'Disabled'}`, + ]; + + const collapsedMetricsOptions = [ `Step: ${query.step || 'auto'}`, - // `Exemplars: ${query.exemplars !== undefined ? query.exemplars : 'auto'}`, + `Type: ${query.metricsQueryType === MetricsQueryType.Range ? 'Range' : 'Instant'}`, '|', - `Streaming: ${isStreaming ? 'Enabled' : 'Disabled'}`, + `Streaming: ${metricsStreaming ? 'Enabled' : 'Disabled'}`, + // `Exemplars: ${query.exemplars !== undefined ? query.exemplars : 'auto'}`, ]; return ( - <> - - + +
+ (({ onChange, query, is onChange={onTableTypeChange} /> + } tooltipInteractive> +
{searchStreaming ? 'Enabled' : 'Disabled'}
+
+
+ + (({ onChange, query, is value={query.step} /> + + + + + } tooltipInteractive> +
{metricsStreaming ? 'Enabled' : 'Disabled'}
+
{/*(({ onChange, query, is {/* value={query.exemplars}*/} {/* />*/} {/**/} - } tooltipInteractive> -
{isStreaming ? 'Enabled' : 'Disabled'}
-
- - +
+
); }); @@ -163,3 +209,17 @@ const StreamingTooltip = () => { }; TempoQueryBuilderOptions.displayName = 'TempoQueryBuilderOptions'; + +const getStyles = (theme: GrafanaTheme2) => { + return { + options: css({ + display: 'flex', + width: '-webkit-fill-available', + gap: theme.spacing(1), + + '> div': { + width: 'auto', + }, + }), + }; +}; diff --git a/public/app/plugins/datasource/tempo/types.ts b/public/app/plugins/datasource/tempo/types.ts index c671a1bf15a..de6c050fb6c 100644 --- a/public/app/plugins/datasource/tempo/types.ts +++ b/public/app/plugins/datasource/tempo/types.ts @@ -16,6 +16,7 @@ export interface TempoJsonData extends DataSourceJsonData { spanBar?: { tag: string; }; + tagLimit?: number; traceQuery?: { timeShiftEnabled?: boolean; spanStartTimeShift?: string; diff --git a/public/app/plugins/datasource/tempo/utils.ts b/public/app/plugins/datasource/tempo/utils.ts index f23b77f3e57..8bc5c2addd5 100644 --- a/public/app/plugins/datasource/tempo/utils.ts +++ b/public/app/plugins/datasource/tempo/utils.ts @@ -1,4 +1,4 @@ -import { DataSourceApi } from '@grafana/data'; +import { DataSourceApi, parseDuration } from '@grafana/data'; import { getDataSourceSrv } from '@grafana/runtime'; import { generateId } from './SearchTraceQLEditor/TagsInput'; @@ -99,3 +99,34 @@ export const migrateFromSearchToTraceQLSearch = (query: TempoQuery) => { }; return migratedQuery; }; + +export const stepToNanos = (step?: string) => { + if (!step) { + return 0; + } + + const match = step.match(/(\d+)(.+)/); + + const rawLength = match?.[1]; + const unit = match?.[2]; + + if (rawLength) { + if (unit === 'ns') { + return parseInt(rawLength, 10); + } + if (unit === 'µs') { + return parseInt(rawLength, 10) * 1000; + } + if (unit === 'ms') { + return parseInt(rawLength, 10) * 1000000; + } + const duration = parseDuration(step); + return ( + (duration.seconds || 0) * 1000000000 + + (duration.minutes || 0) * 60000000000 + + (duration.hours || 0) * 3600000000000 + ); + } + + return 0; +}; diff --git a/public/app/plugins/datasource/zipkin/datasource.test.ts b/public/app/plugins/datasource/zipkin/datasource.test.ts index 4123de7960d..5b1ab000004 100644 --- a/public/app/plugins/datasource/zipkin/datasource.test.ts +++ b/public/app/plugins/datasource/zipkin/datasource.test.ts @@ -1,5 +1,4 @@ import { lastValueFrom, of } from 'rxjs'; -import { createFetchResponse } from 'test/helpers/createFetchResponse'; import { DataFrameView, @@ -8,52 +7,49 @@ import { DataSourcePluginMeta, FieldType, } from '@grafana/data'; -import { BackendSrv, TemplateSrv } from '@grafana/runtime'; +import { BackendSrv, getBackendSrv, setBackendSrv, TemplateSrv } from '@grafana/runtime'; import { addNodeGraphFramesToResponse, ZipkinDatasource } from './datasource'; import mockJson from './mocks/mockJsonResponse.json'; import { mockTraceDataFrame } from './mocks/mockTraceDataFrame'; -import { ZipkinQuery, ZipkinSpan } from './types'; -import { traceFrameFields, zipkinResponse } from './utils/testData'; +import { ZipkinQuery } from './types'; -export const backendSrv = { fetch: jest.fn() } as unknown as BackendSrv; - -jest.mock('@grafana/runtime', () => ({ - ...jest.requireActual('@grafana/runtime'), - getBackendSrv: () => backendSrv, -})); +const templateSrv: TemplateSrv = { + replace: jest.fn().mockImplementation((value) => value), + getVariables: jest.fn(), + containsTemplate: jest.fn(), + updateTimeRange: jest.fn(), +}; describe('ZipkinDatasource', () => { + let origBackendSrv: BackendSrv; + let ds: ZipkinDatasource; + beforeEach(() => { + origBackendSrv = getBackendSrv(); + setBackendSrv({ ...origBackendSrv, fetch: jest.fn().mockReturnValue(of({ data: {} })) }); + ds = new ZipkinDatasource(defaultSettings, templateSrv); + }); + + afterEach(() => { + setBackendSrv(origBackendSrv); + jest.clearAllMocks(); + }); describe('query', () => { - const templateSrv: TemplateSrv = { - replace: jest.fn(), - getVariables: jest.fn(), - containsTemplate: jest.fn(), - updateTimeRange: jest.fn(), - }; - it('runs query', async () => { - setupBackendSrv(zipkinResponse); - const ds = new ZipkinDatasource(defaultSettings, templateSrv); - await expect(ds.query({ targets: [{ query: '12345' }] } as DataQueryRequest)).toEmitValuesWith( - (val) => { - expect(val[0].data[0].fields).toMatchObject(traceFrameFields); - } - ); - }); + const origBackendSrv = getBackendSrv(); + setBackendSrv({ + ...origBackendSrv, + fetch: jest.fn().mockReturnValue(of({ data: [] })), + }); - it('runs query with traceId that includes special characters', async () => { - setupBackendSrv(zipkinResponse); - const ds = new ZipkinDatasource(defaultSettings, templateSrv); - await expect(ds.query({ targets: [{ query: 'a/b' }] } as DataQueryRequest)).toEmitValuesWith( - (val) => { - expect(val[0].data[0].fields).toMatchObject(traceFrameFields); - } - ); + const response = await lastValueFrom(ds.query({ targets: [{ query: 'test' }] } as DataQueryRequest)); + expect(response).toEqual({ + state: 'Done', + data: [], + }); }); it('should handle json file upload', async () => { - const ds = new ZipkinDatasource(defaultSettings); ds.uploadedJson = JSON.stringify(mockJson); const response = await lastValueFrom( ds.query({ @@ -67,7 +63,6 @@ describe('ZipkinDatasource', () => { }); it('should fail on invalid json file upload', async () => { - const ds = new ZipkinDatasource(defaultSettings); ds.uploadedJson = JSON.stringify({ key: 'value', arr: [] }); const response = await lastValueFrom( ds.query({ @@ -81,7 +76,10 @@ describe('ZipkinDatasource', () => { describe('metadataRequest', () => { it('runs query', async () => { - setupBackendSrv(['service 1', 'service 2'] as unknown as ZipkinSpan[]); + setBackendSrv({ + ...origBackendSrv, + fetch: jest.fn().mockReturnValue(of({ data: ['service 1', 'service 2'] })), + }); const ds = new ZipkinDatasource(defaultSettings); const response = await ds.metadataRequest('services'); expect(response).toEqual(['service 1', 'service 2']); @@ -118,13 +116,6 @@ describe('addNodeGraphFramesToResponse', () => { }); }); -function setupBackendSrv(response: ZipkinSpan[]) { - const defaultMock = () => of(createFetchResponse(response)); - - const fetchMock = jest.spyOn(backendSrv, 'fetch'); - fetchMock.mockImplementation(defaultMock); -} - const defaultSettings: DataSourceInstanceSettings = { id: 1, uid: '1', diff --git a/public/app/plugins/datasource/zipkin/datasource.ts b/public/app/plugins/datasource/zipkin/datasource.ts index 54ff15dafb1..7b49e7a0c1c 100644 --- a/public/app/plugins/datasource/zipkin/datasource.ts +++ b/public/app/plugins/datasource/zipkin/datasource.ts @@ -1,4 +1,4 @@ -import { lastValueFrom, Observable, of } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { map } from 'rxjs/operators'; import { @@ -9,26 +9,15 @@ import { FieldType, createDataFrame, ScopedVars, - urlUtil, toDataFrame, } from '@grafana/data'; import { createNodeGraphFrames, NodeGraphOptions, SpanBarOptions } from '@grafana/o11y-ds-frontend'; -import { - BackendSrvRequest, - config, - DataSourceWithBackend, - FetchResponse, - getBackendSrv, - getTemplateSrv, - TemplateSrv, -} from '@grafana/runtime'; +import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime'; import { ZipkinQuery, ZipkinSpan } from './types'; import { createGraphFrames } from './utils/graphTransform'; import { transformResponse } from './utils/transforms'; -const apiPrefix = '/api/v2'; - export interface ZipkinJsonData extends DataSourceJsonData { nodeGraph?: NodeGraphOptions; } @@ -38,7 +27,7 @@ export class ZipkinDatasource extends DataSourceWithBackend, + instanceSettings: DataSourceInstanceSettings, private readonly templateSrv: TemplateSrv = getTemplateSrv() ) { super(instanceSettings); @@ -61,40 +50,24 @@ export class ZipkinDatasource extends DataSourceWithBackend { - if (this.nodeGraph?.enabled) { - return addNodeGraphFramesToResponse(response); - } - return response; - }) - ); - } - const query = this.applyTemplateVariables(target, options.scopedVars); - return this.request(`${apiPrefix}/trace/${encodeURIComponent(query.query)}`).pipe( - map((res) => responseToDataQueryResponse(res, this.nodeGraph?.enabled)) + return super.query(options).pipe( + map((response) => { + if (this.nodeGraph?.enabled) { + return addNodeGraphFramesToResponse(response); + } + return response; + }) ); } return of(emptyDataQueryResponse); } async metadataRequest(url: string, params?: Record) { - if (config.featureToggles.zipkinBackendMigration) { - return await this.getResource(url, params); - } - const urlWithPrefix = `${apiPrefix}/${url}`; - const res = await lastValueFrom(this.request(urlWithPrefix, params, { hideFromInspector: true })); - return res.data; + return await this.getResource(url, params); } async testDatasource(): Promise<{ status: string; message: string }> { - if (config.featureToggles.zipkinBackendMigration) { - return await super.testDatasource(); - } - - await this.metadataRequest('services'); - return { status: 'success', message: 'Data source is working' }; + return await super.testDatasource(); } getQueryDisplayText(query: ZipkinQuery): string { @@ -123,21 +96,6 @@ export class ZipkinDatasource extends DataSourceWithBackend( - apiUrl: string, - data?: unknown, - options?: Partial - ): Observable> { - const params = data ? urlUtil.serializeParams(data) : ''; - const url = `${this.instanceSettings.url}${apiUrl}${params.length ? `?${params}` : ''}`; - const req = { - ...options, - url, - }; - - return getBackendSrv().fetch(req); - } } function responseToDataQueryResponse(response: { data: ZipkinSpan[] }, nodeGraph = false): DataQueryResponse { diff --git a/public/app/plugins/datasource/zipkin/package.json b/public/app/plugins/datasource/zipkin/package.json index 3a9f96042ae..b40fb1cf8ec 100644 --- a/public/app/plugins/datasource/zipkin/package.json +++ b/public/app/plugins/datasource/zipkin/package.json @@ -2,19 +2,19 @@ "name": "@grafana-plugins/zipkin", "description": "Zipkin plugin for Grafana", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", "@grafana/data": "workspace:*", "@grafana/e2e-selectors": "workspace:*", "@grafana/o11y-ds-frontend": "workspace:*", - "@grafana/plugin-ui": "^0.9.6", + "@grafana/plugin-ui": "0.10.1", "@grafana/runtime": "workspace:*", "@grafana/ui": "workspace:*", "lodash": "4.17.21", "react": "18.3.1", "react-dom": "18.3.1", - "react-select": "5.9.0", + "react-select": "5.10.0", "react-use": "17.6.0", "rxjs": "7.8.1", "tslib": "2.8.1" @@ -23,10 +23,10 @@ "@grafana/plugin-configs": "workspace:*", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.6.3", - "@testing-library/react": "16.1.0", + "@testing-library/react": "16.2.0", "@types/jest": "29.5.14", - "@types/lodash": "4.17.14", - "@types/node": "22.10.7", + "@types/lodash": "4.17.15", + "@types/node": "22.12.0", "@types/react": "18.3.18", "@types/react-dom": "18.3.5", "ts-node": "10.9.2", diff --git a/public/app/plugins/panel/alertlist/types.ts b/public/app/plugins/panel/alertlist/types.ts index fd9e9cf3540..9e497041d45 100644 --- a/public/app/plugins/panel/alertlist/types.ts +++ b/public/app/plugins/panel/alertlist/types.ts @@ -6,11 +6,6 @@ export enum SortOrder { TimeDesc, } -export enum ShowOption { - Current = 'current', - RecentChanges = 'changes', -} - export enum GroupMode { Default = 'default', Custom = 'custom', @@ -21,26 +16,6 @@ export enum ViewMode { Stat = 'stat', } -export interface AlertListOptions { - showOptions: ShowOption; - maxItems: number; - sortOrder: SortOrder; - dashboardAlerts: boolean; - alertName: string; - dashboardTitle: string; - tags: string[]; - stateFilter: { - ok: boolean; - paused: boolean; - no_data: boolean; - execution_error: boolean; - alerting: boolean; - pending: boolean; - }; - folderId: number; - showInactiveAlerts: boolean; -} - export interface StateFilter { firing: boolean; pending: boolean; diff --git a/public/app/plugins/panel/barchart/BarChartPanel.tsx b/public/app/plugins/panel/barchart/BarChartPanel.tsx index dba432c57d0..424bbd16de0 100644 --- a/public/app/plugins/panel/barchart/BarChartPanel.tsx +++ b/public/app/plugins/panel/barchart/BarChartPanel.tsx @@ -157,8 +157,8 @@ export const BarChartPanel = (props: PanelProps) => { hoverMode={ options.tooltip.mode === TooltipDisplayMode.Single ? TooltipHoverMode.xOne : TooltipHoverMode.xAll } - getDataLinks={(seriesIdx: number, dataIdx: number) => - vizSeries[0].fields[seriesIdx]!.getLinks?.({ valueRowIndex: dataIdx }) ?? [] + getDataLinks={(seriesIdx, dataIdx) => + vizSeries[0].fields[seriesIdx].getLinks?.({ valueRowIndex: dataIdx }) ?? [] } render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2, viaSync, dataLinks) => { return ( diff --git a/public/app/plugins/panel/barchart/module.tsx b/public/app/plugins/panel/barchart/module.tsx index 35d1f095828..c31c17f5a81 100644 --- a/public/app/plugins/panel/barchart/module.tsx +++ b/public/app/plugins/panel/barchart/module.tsx @@ -32,6 +32,14 @@ export const plugin = new PanelPlugin(BarChartPanel) mode: FieldColorModeId.PaletteClassic, }, }, + [FieldConfigProperty.Links]: { + settings: { + showOneClick: true, + }, + }, + [FieldConfigProperty.Actions]: { + hideFromDefaults: false, + }, }, useCustomConfig: (builder) => { const cfg = defaultFieldConfig; @@ -103,7 +111,7 @@ export const plugin = new PanelPlugin(BarChartPanel) shouldApply: () => true, }); - commonOptionsBuilder.addAxisConfig(builder, cfg, false); + commonOptionsBuilder.addAxisConfig(builder, cfg); commonOptionsBuilder.addHideFrom(builder); }, }) diff --git a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx index 4c2fba704d9..a872b6bd301 100644 --- a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx +++ b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx @@ -29,6 +29,9 @@ export class BarGaugePanel extends PureComponent { const { value, alignmentFactors, orientation, width, height, count } = valueProps; const { field, display, view, colIndex } = value; const { openMenu, targetClassName } = menuProps; + const spacing = this.getItemSpacing(); + // check if the total height is bigger than the visualization height, if so, there will be scrollbars for overflow + const isOverflow = (height + spacing) * count - spacing > this.props.height; let processor: DisplayProcessor | undefined = undefined; if (view && isNumber(colIndex)) { @@ -45,7 +48,7 @@ export class BarGaugePanel extends PureComponent { text={options.text} display={processor} theme={config.theme2} - itemSpacing={this.getItemSpacing()} + itemSpacing={spacing} displayMode={options.displayMode} onClick={openMenu} className={targetClassName} @@ -53,6 +56,7 @@ export class BarGaugePanel extends PureComponent { showUnfilled={options.showUnfilled} valueDisplayMode={options.valueMode} namePlacement={options.namePlacement} + isOverflow={isOverflow} /> ); }; diff --git a/public/app/plugins/panel/candlestick/CandlestickPanel.tsx b/public/app/plugins/panel/candlestick/CandlestickPanel.tsx index 4ed97d59456..16eeeae3ca6 100644 --- a/public/app/plugins/panel/candlestick/CandlestickPanel.tsx +++ b/public/app/plugins/panel/candlestick/CandlestickPanel.tsx @@ -282,8 +282,8 @@ export const CandlestickPanel = ({ clientZoom={true} syncMode={cursorSync} syncScope={eventsScope} - getDataLinks={(seriesIdx: number, dataIdx: number) => - alignedFrame.fields[seriesIdx]!.getLinks?.({ valueRowIndex: dataIdx }) ?? [] + getDataLinks={(seriesIdx, dataIdx) => + alignedFrame.fields[seriesIdx].getLinks?.({ valueRowIndex: dataIdx }) ?? [] } render={(u, dataIdxs, seriesIdx, isPinned = false, dismiss, timeRange2, viaSync, dataLinks) => { if (enableAnnotationCreation && timeRange2 != null) { diff --git a/public/app/plugins/panel/canvas/CanvasPanel.tsx b/public/app/plugins/panel/canvas/CanvasPanel.tsx index cd48d2ed8dd..4db38108706 100644 --- a/public/app/plugins/panel/canvas/CanvasPanel.tsx +++ b/public/app/plugins/panel/canvas/CanvasPanel.tsx @@ -85,6 +85,7 @@ export class CanvasPanel extends Component { this.scene.setBackgroundCallback = this.openSetBackground; this.scene.tooltipCallback = this.tooltipCallback; this.scene.moveableActionCallback = this.moveableActionCallback; + this.scene.actionConfirmationCallback = this.actionConfirmationCallback; this.subs.add( this.props.eventBus.subscribe(PanelEditEnteredEvent, (evt: PanelEditEnteredEvent) => { @@ -302,6 +303,10 @@ export class CanvasPanel extends Component { this.forceUpdate(); }; + actionConfirmationCallback = () => { + this.forceUpdate(); + }; + closeInlineEdit = () => { this.setState({ openInlineEdit: false }); isInlineEditOpen = false; diff --git a/public/app/plugins/panel/canvas/components/CanvasTooltip.tsx b/public/app/plugins/panel/canvas/components/CanvasTooltip.tsx index 39390afefdf..f2a5959edc6 100644 --- a/public/app/plugins/panel/canvas/components/CanvasTooltip.tsx +++ b/public/app/plugins/panel/canvas/components/CanvasTooltip.tsx @@ -88,7 +88,6 @@ export const CanvasTooltip = ({ scene }: Props) => { } }); } - // --------- if (scene.data?.series) { getElementFields(scene.data?.series, element.options).forEach((field) => { diff --git a/public/app/plugins/panel/canvas/editor/element/ActionsEditor.tsx b/public/app/plugins/panel/canvas/editor/element/ActionsEditor.tsx index 06613ffe843..4928c7c3368 100644 --- a/public/app/plugins/panel/canvas/editor/element/ActionsEditor.tsx +++ b/public/app/plugins/panel/canvas/editor/element/ActionsEditor.tsx @@ -1,20 +1,26 @@ -import { StandardEditorProps, OneClickMode, Action, VariableSuggestionsScope } from '@grafana/data'; +import { StandardEditorProps, Action, VariableSuggestionsScope } from '@grafana/data'; +import { ActionsInlineEditor } from 'app/features/actions/ActionsInlineEditor'; import { CanvasElementOptions } from 'app/features/canvas/element'; -import { ActionsInlineEditor } from '../../../../../features/actions/ActionsInlineEditor'; - type Props = StandardEditorProps; export function ActionsEditor({ value, onChange, item, context }: Props) { - const oneClickMode = item.settings?.oneClickMode; + const dataLinks = item.settings?.links || []; return ( { + if (actions.some(({ oneClick }) => oneClick === true)) { + dataLinks.forEach((link) => { + link.oneClick = false; + }); + } + onChange(actions); + }} getSuggestions={() => (context.getSuggestions ? context.getSuggestions(VariableSuggestionsScope.Values) : [])} data={[]} - showOneClick={oneClickMode === OneClickMode.Action} + showOneClick={true} /> ); } diff --git a/public/app/plugins/panel/canvas/editor/element/DataLinksEditor.tsx b/public/app/plugins/panel/canvas/editor/element/DataLinksEditor.tsx index 95779a4b7a1..16af0cd074b 100644 --- a/public/app/plugins/panel/canvas/editor/element/DataLinksEditor.tsx +++ b/public/app/plugins/panel/canvas/editor/element/DataLinksEditor.tsx @@ -1,19 +1,26 @@ -import { StandardEditorProps, DataLink, VariableSuggestionsScope, OneClickMode } from '@grafana/data'; +import { StandardEditorProps, DataLink, VariableSuggestionsScope } from '@grafana/data'; import { DataLinksInlineEditor } from '@grafana/ui'; import { CanvasElementOptions } from 'app/features/canvas/element'; type Props = StandardEditorProps; export function DataLinksEditor({ value, onChange, item, context }: Props) { - const oneClickMode = item.settings?.oneClickMode; + const actions = item.settings?.actions || []; return ( { + if (links.some(({ oneClick }) => oneClick === true)) { + actions.forEach((action) => { + action.oneClick = false; + }); + } + onChange(links); + }} getSuggestions={() => (context.getSuggestions ? context.getSuggestions(VariableSuggestionsScope.Values) : [])} data={[]} - showOneClick={oneClickMode === OneClickMode.Link} + showOneClick={true} /> ); } diff --git a/public/app/plugins/panel/canvas/editor/element/elementEditor.tsx b/public/app/plugins/panel/canvas/editor/element/elementEditor.tsx index 5e3a545b87b..bdad1130a50 100644 --- a/public/app/plugins/panel/canvas/editor/element/elementEditor.tsx +++ b/public/app/plugins/panel/canvas/editor/element/elementEditor.tsx @@ -1,8 +1,6 @@ -import { capitalize, get as lodashGet } from 'lodash'; +import { get as lodashGet } from 'lodash'; -import { OneClickMode } from '@grafana/data'; import { NestedPanelOptions, NestedValueAccess } from '@grafana/data/src/utils/OptionsUIBuilders'; -import { config } from '@grafana/runtime'; import { CanvasElementOptions } from 'app/features/canvas/element'; import { canvasElementRegistry, @@ -68,8 +66,6 @@ export function getElementEditor(opts: CanvasEditorOptions): NestedPanelOptions< const current = options?.type ? options.type : DEFAULT_CANVAS_ELEMENT_CONFIG.type; const layerTypes = getElementTypes(opts.scene.shouldShowAdvancedTypes, current).options; - const actionsEnabled = config.featureToggles.vizActions; - const isUnsupported = !opts.scene.shouldShowAdvancedTypes && !defaultElementItems.filter((item) => item.id === options?.type).length; @@ -123,34 +119,6 @@ export function getElementEditor(opts: CanvasEditorOptions): NestedPanelOptions< optionBuilder.addBorder(builder, ctx); } - const oneClickModeOptions: Array<{ - value: OneClickMode; - label: string; - }> = [ - { value: OneClickMode.Off, label: capitalize(OneClickMode.Off) }, - { value: OneClickMode.Link, label: capitalize(OneClickMode.Link) }, - ]; - - let oneClickCategory = 'Data links'; - let oneClickDescription = 'When enabled, a single click opens the first link'; - - if (actionsEnabled) { - oneClickModeOptions.push({ value: OneClickMode.Action, label: capitalize(OneClickMode.Action) }); - oneClickCategory += ' and actions'; - oneClickDescription += ' or action'; - } - - builder.addRadio({ - category: [oneClickCategory], - path: 'oneClickMode', - name: 'One-click', - description: oneClickDescription, - settings: { - options: oneClickModeOptions, - }, - defaultValue: OneClickMode.Off, - }); - optionBuilder.addDataLinks(builder, ctx); optionBuilder.addActions(builder, ctx); }, diff --git a/public/app/plugins/panel/canvas/editor/options.ts b/public/app/plugins/panel/canvas/editor/options.ts index 954478b88dd..221ab5f7979 100644 --- a/public/app/plugins/panel/canvas/editor/options.ts +++ b/public/app/plugins/panel/canvas/editor/options.ts @@ -2,7 +2,6 @@ import { capitalize } from 'lodash'; import { FieldType } from '@grafana/data'; import { PanelOptionsSupplier } from '@grafana/data/src/panel/PanelPlugin'; -import { config } from '@grafana/runtime'; import { ConnectionDirection } from 'app/features/canvas/element'; import { SVGElements } from 'app/features/canvas/runtime/element'; import { ColorDimensionEditor, ResourceDimensionEditor, ScaleDimensionEditor } from 'app/features/dimensions/editors'; @@ -213,7 +212,7 @@ export const optionBuilder: OptionSuppliers = { addDataLinks: (builder, context) => { builder.addCustomEditor({ - category: config.featureToggles.vizActions ? ['Data links and actions'] : ['Data links'], + category: ['Data links and actions'], id: 'dataLinks', path: 'links', name: 'Links', @@ -230,7 +229,6 @@ export const optionBuilder: OptionSuppliers = { name: 'Actions', editor: ActionsEditor, settings: context.options, - showIf: () => config.featureToggles.vizActions, }); }, }; diff --git a/public/app/plugins/panel/canvas/migrations.test.ts b/public/app/plugins/panel/canvas/migrations.test.ts index 0a1354b2ae0..2c66529bd1c 100644 --- a/public/app/plugins/panel/canvas/migrations.test.ts +++ b/public/app/plugins/panel/canvas/migrations.test.ts @@ -1,4 +1,4 @@ -import { OneClickMode, PanelModel } from '@grafana/data'; +import { PanelModel } from '@grafana/data'; import { canvasMigrationHandler } from './migrations'; @@ -20,6 +20,16 @@ describe('Canvas migration', () => { }, }, ], + links: [ + { + title: 'Link1', + url: 'www.link1.com', + }, + { + title: 'Link2', + url: 'www.link2.com', + }, + ], }, ], }, @@ -29,7 +39,7 @@ describe('Canvas migration', () => { panel.options = canvasMigrationHandler(panel); - expect(panel.options.root.elements[0].oneClickMode).toBe(OneClickMode.Link); + expect(panel.options.root.elements[0].links[0].oneClick).toBe(true); expect(panel.options.root.elements[0].actions[0].fetch.url).toBe('http://test.com'); }); }); diff --git a/public/app/plugins/panel/canvas/migrations.ts b/public/app/plugins/panel/canvas/migrations.ts index f287556a2f7..22923598029 100644 --- a/public/app/plugins/panel/canvas/migrations.ts +++ b/public/app/plugins/panel/canvas/migrations.ts @@ -46,12 +46,6 @@ export const canvasMigrationHandler = (panel: PanelModel): Partial => { const root = panel.options?.root; if (root?.elements) { for (const element of root.elements) { - // migrate oneClickLinks to oneClickMode - if (element.oneClickLinks) { - element.oneClickMode = OneClickMode.Link; - delete element.oneClickLinks; - } - // migrate action options to new format (fetch) if (element.actions) { for (const action of element.actions) { @@ -65,5 +59,22 @@ export const canvasMigrationHandler = (panel: PanelModel): Partial => { } } + // migrate oneClickMode to first link/action oneClick + if (parseFloat(pluginVersion) <= 11.6) { + const root = panel.options?.root; + if (root?.elements) { + for (const element of root.elements) { + if ((element.oneClickMode === OneClickMode.Link || element.oneClickLinks) && element.links?.length) { + element.links[0].oneClick = true; + } else if (element.oneClickMode === OneClickMode.Action && element.actions?.length) { + element.actions[0].oneClick = true; + } + + delete element.oneClickMode; + delete element.oneClickLinks; + } + } + } + return panel.options; }; diff --git a/public/app/plugins/panel/canvas/module.tsx b/public/app/plugins/panel/canvas/module.tsx index 19b9ce04ded..011c9e78682 100644 --- a/public/app/plugins/panel/canvas/module.tsx +++ b/public/app/plugins/panel/canvas/module.tsx @@ -59,7 +59,14 @@ export const plugin = new PanelPlugin(CanvasPanel) }, }, [FieldConfigProperty.Links]: { - hideFromDefaults: true, + settings: { + showOneClick: false, + }, + }, + [FieldConfigProperty.Actions]: { + settings: { + showOneClick: false, + }, }, }, }) diff --git a/public/app/plugins/panel/dashlist/DashList.tsx b/public/app/plugins/panel/dashlist/DashList.tsx index 0b66df03ae9..e3044da47d2 100644 --- a/public/app/plugins/panel/dashlist/DashList.tsx +++ b/public/app/plugins/panel/dashlist/DashList.tsx @@ -1,6 +1,6 @@ import { take } from 'lodash'; -import { useEffect, useMemo, useState } from 'react'; -import * as React from 'react'; +import { SyntheticEvent, useEffect, useMemo, useState } from 'react'; +import { useThrottle } from 'react-use'; import { DataLinkBuiltInVars, @@ -107,13 +107,15 @@ export function DashList(props: PanelProps) { const dispatch = useDispatch(); const navIndex = useSelector((state) => state.navIndex); + const throttledRenderCount = useThrottle(props.renderCounter, 5000); + useEffect(() => { fetchDashboards(props.options, props.replaceVariables).then((dashes) => { setDashboards(dashes); }); - }, [props.options, props.replaceVariables, props.renderCounter]); + }, [props.options, props.replaceVariables, throttledRenderCount]); - const toggleDashboardStar = async (e: React.SyntheticEvent, dash: Dashboard) => { + const toggleDashboardStar = async (e: SyntheticEvent, dash: Dashboard) => { const { uid, title, url } = dash; e.preventDefault(); e.stopPropagation(); diff --git a/public/app/plugins/panel/dashlist/module.tsx b/public/app/plugins/panel/dashlist/module.tsx index 8cb3c286e16..a4033ebc7dc 100644 --- a/public/app/plugins/panel/dashlist/module.tsx +++ b/public/app/plugins/panel/dashlist/module.tsx @@ -1,7 +1,6 @@ import { PanelPlugin } from '@grafana/data'; import { TagsInput } from '@grafana/ui'; import { FolderPicker } from 'app/core/components/Select/FolderPicker'; -import { PermissionLevelString } from 'app/types'; import { DashList } from './DashList'; import { dashlistMigrationHandler } from './migrations'; @@ -62,12 +61,7 @@ export const plugin = new PanelPlugin(DashList) defaultValue: undefined, editor: function RenderFolderPicker({ value, onChange }) { return ( - onChange(folderUID)} - /> + onChange(folderUID)} /> ); }, }) diff --git a/public/app/plugins/panel/graph/specs/time_region_manager.test.ts b/public/app/plugins/panel/graph/specs/time_region_manager.test.ts deleted file mode 100644 index 960baaa4287..00000000000 --- a/public/app/plugins/panel/graph/specs/time_region_manager.test.ts +++ /dev/null @@ -1,372 +0,0 @@ -import { dateTime } from '@grafana/data'; - -import { TimeRegionManager, colorModes } from '../time_region_manager'; - -describe('TimeRegionManager', () => { - function plotOptionsScenario(desc: string, func: any) { - describe(desc, () => { - const ctx: any = { - panel: { - timeRegions: [], - }, - options: { - grid: { markings: [] }, - }, - panelCtrl: { - range: {}, - dashboard: {}, - }, - }; - - ctx.setup = (regions: any, from: any, to: any) => { - ctx.panel.timeRegions = regions; - ctx.panelCtrl.range.from = from; - ctx.panelCtrl.range.to = to; - const manager = new TimeRegionManager(ctx.panelCtrl); - manager.addFlotOptions(ctx.options, ctx.panel); - }; - - ctx.printScenario = () => { - console.log( - `Time range: from=${ctx.panelCtrl.range.from.format()}, to=${ctx.panelCtrl.range.to.format()}`, - ctx.panelCtrl.range.from._isUTC - ); - ctx.options.grid.markings.forEach((m: any, i: number) => { - console.log( - `Marking (${i}): from=${dateTime(m.xaxis.from).format()}, to=${dateTime(m.xaxis.to).format()}, color=${ - m.color - }` - ); - }); - }; - - func(ctx); - }); - } - - describe('When colors missing in config', () => { - plotOptionsScenario('should not throw an error when fillColor is undefined', (ctx: any) => { - const regions = [ - { fromDayOfWeek: 1, toDayOfWeek: 1, fill: true, line: true, lineColor: '#ffffff', colorMode: 'custom' }, - ]; - const from = dateTime('2018-01-01T00:00:00+01:00'); - const to = dateTime('2018-01-01T23:59:00+01:00'); - expect(() => ctx.setup(regions, from, to)).not.toThrow(); - }); - plotOptionsScenario('should not throw an error when lineColor is undefined', (ctx: any) => { - const regions = [ - { fromDayOfWeek: 1, toDayOfWeek: 1, fill: true, fillColor: '#ffffff', line: true, colorMode: 'custom' }, - ]; - const from = dateTime('2018-01-01T00:00:00+01:00'); - const to = dateTime('2018-01-01T23:59:00+01:00'); - expect(() => ctx.setup(regions, from, to)).not.toThrow(); - }); - }); - - describe('When creating plot markings using local time', () => { - plotOptionsScenario('for day of week region', (ctx: any) => { - const regions = [{ fromDayOfWeek: 1, toDayOfWeek: 1, fill: true, line: true, colorMode: 'red' }]; - const from = dateTime('2018-01-01T00:00:00+01:00'); - const to = dateTime('2018-01-01T23:59:00+01:00'); - ctx.setup(regions, from, to); - - it('should add 3 markings', () => { - expect(ctx.options.grid.markings.length).toBe(3); - }); - - it('should add fill', () => { - const markings = ctx.options.grid.markings; - expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-01-01T01:00:00+01:00').format()); - expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-01-02T00:59:59+01:00').format()); - expect(markings[0].color).toBe(colorModes.red.color.fill); - }); - - it('should add line before', () => { - const markings = ctx.options.grid.markings; - expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-01-01T01:00:00+01:00').format()); - expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-01-01T01:00:00+01:00').format()); - expect(markings[1].color).toBe(colorModes.red.color.line); - }); - - it('should add line after', () => { - const markings = ctx.options.grid.markings; - expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-01-02T00:59:59+01:00').format()); - expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-01-02T00:59:59+01:00').format()); - expect(markings[2].color).toBe(colorModes.red.color.line); - }); - }); - - plotOptionsScenario('for time from region', (ctx: any) => { - const regions = [{ from: '05:00', fill: true, colorMode: 'red' }]; - const from = dateTime('2018-01-01T00:00+01:00'); - const to = dateTime('2018-01-03T23:59+01:00'); - ctx.setup(regions, from, to); - - it('should add 3 markings', () => { - expect(ctx.options.grid.markings.length).toBe(3); - }); - - it('should add one fill at 05:00 each day', () => { - const markings = ctx.options.grid.markings; - - expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-01-01T06:00:00+01:00').format()); - expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-01-01T06:00:00+01:00').format()); - expect(markings[0].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-01-02T06:00:00+01:00').format()); - expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-01-02T06:00:00+01:00').format()); - expect(markings[1].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-01-03T06:00:00+01:00').format()); - expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-01-03T06:00:00+01:00').format()); - expect(markings[2].color).toBe(colorModes.red.color.fill); - }); - }); - - plotOptionsScenario('for time to region', (ctx: any) => { - const regions = [{ to: '05:00', fill: true, colorMode: 'red' }]; - const from = dateTime('2018-02-01T00:00+01:00'); - const to = dateTime('2018-02-03T23:59+01:00'); - ctx.setup(regions, from, to); - - it('should add 3 markings', () => { - expect(ctx.options.grid.markings.length).toBe(3); - }); - - it('should add one fill at 05:00 each day', () => { - const markings = ctx.options.grid.markings; - - expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-02-01T06:00:00+01:00').format()); - expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-02-01T06:00:00+01:00').format()); - expect(markings[0].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-02-02T06:00:00+01:00').format()); - expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-02-02T06:00:00+01:00').format()); - expect(markings[1].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-02-03T06:00:00+01:00').format()); - expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-02-03T06:00:00+01:00').format()); - expect(markings[2].color).toBe(colorModes.red.color.fill); - }); - }); - - plotOptionsScenario('for time from/to region', (ctx: any) => { - const regions = [{ from: '00:00', to: '05:00', fill: true, colorMode: 'red' }]; - const from = dateTime('2018-12-01T00:00+01:00'); - const to = dateTime('2018-12-03T23:59+01:00'); - ctx.setup(regions, from, to); - - it('should add 3 markings', () => { - expect(ctx.options.grid.markings.length).toBe(3); - }); - - it('should add one fill between 00:00 and 05:00 each day', () => { - const markings = ctx.options.grid.markings; - - expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-12-01T01:00:00+01:00').format()); - expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-12-01T06:00:00+01:00').format()); - expect(markings[0].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-12-02T01:00:00+01:00').format()); - expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-12-02T06:00:00+01:00').format()); - expect(markings[1].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-12-03T01:00:00+01:00').format()); - expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-12-03T06:00:00+01:00').format()); - expect(markings[2].color).toBe(colorModes.red.color.fill); - }); - }); - - plotOptionsScenario('for time from/to region crossing midnight', (ctx: any) => { - const regions = [{ from: '22:00', to: '00:30', fill: true, colorMode: 'red' }]; - const from = dateTime('2018-12-01T12:00+01:00'); - const to = dateTime('2018-12-04T08:00+01:00'); - ctx.setup(regions, from, to); - - it('should add 3 markings', () => { - expect(ctx.options.grid.markings.length).toBe(3); - }); - - it('should add one fill between 22:00 and 00:30 each day', () => { - const markings = ctx.options.grid.markings; - - expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-12-01T23:00:00+01:00').format()); - expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-12-02T01:30:00+01:00').format()); - expect(markings[0].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-12-02T23:00:00+01:00').format()); - expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-12-03T01:30:00+01:00').format()); - expect(markings[1].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-12-03T23:00:00+01:00').format()); - expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-12-04T01:30:00+01:00').format()); - expect(markings[2].color).toBe(colorModes.red.color.fill); - }); - }); - - plotOptionsScenario('for day of week from/to region', (ctx: any) => { - const regions = [{ fromDayOfWeek: 7, toDayOfWeek: 7, fill: true, colorMode: 'red' }]; - const from = dateTime('2018-01-01T18:45:05+01:00'); - const to = dateTime('2018-01-22T08:27:00+01:00'); - ctx.setup(regions, from, to); - - it('should add 3 markings', () => { - expect(ctx.options.grid.markings.length).toBe(3); - }); - - it('should add one fill at each sunday', () => { - const markings = ctx.options.grid.markings; - - expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-01-07T01:00:00+01:00').format()); - expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-01-08T00:59:59+01:00').format()); - expect(markings[0].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-01-14T01:00:00+01:00').format()); - expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-01-15T00:59:59+01:00').format()); - expect(markings[1].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-01-21T01:00:00+01:00').format()); - expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-01-22T00:59:59+01:00').format()); - expect(markings[2].color).toBe(colorModes.red.color.fill); - }); - }); - - plotOptionsScenario('for day of week from region', (ctx: any) => { - const regions = [{ fromDayOfWeek: 7, fill: true, colorMode: 'red' }]; - const from = dateTime('2018-01-01T18:45:05+01:00'); - const to = dateTime('2018-01-22T08:27:00+01:00'); - ctx.setup(regions, from, to); - - it('should add 3 markings', () => { - expect(ctx.options.grid.markings.length).toBe(3); - }); - - it('should add one fill at each sunday', () => { - const markings = ctx.options.grid.markings; - - expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-01-07T01:00:00+01:00').format()); - expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-01-08T00:59:59+01:00').format()); - expect(markings[0].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-01-14T01:00:00+01:00').format()); - expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-01-15T00:59:59+01:00').format()); - expect(markings[1].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-01-21T01:00:00+01:00').format()); - expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-01-22T00:59:59+01:00').format()); - expect(markings[2].color).toBe(colorModes.red.color.fill); - }); - }); - - plotOptionsScenario('for day of week to region', (ctx: any) => { - const regions = [{ toDayOfWeek: 7, fill: true, colorMode: 'red' }]; - const from = dateTime('2018-01-01T18:45:05+01:00'); - const to = dateTime('2018-01-22T08:27:00+01:00'); - ctx.setup(regions, from, to); - - it('should add 3 markings', () => { - expect(ctx.options.grid.markings.length).toBe(3); - }); - - it('should add one fill at each sunday', () => { - const markings = ctx.options.grid.markings; - - expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-01-07T01:00:00+01:00').format()); - expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-01-08T00:59:59+01:00').format()); - expect(markings[0].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-01-14T01:00:00+01:00').format()); - expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-01-15T00:59:59+01:00').format()); - expect(markings[1].color).toBe(colorModes.red.color.fill); - - expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-01-21T01:00:00+01:00').format()); - expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-01-22T00:59:59+01:00').format()); - expect(markings[2].color).toBe(colorModes.red.color.fill); - }); - }); - - plotOptionsScenario('for day of week from/to time region', (ctx: any) => { - const regions = [{ fromDayOfWeek: 7, from: '23:00', toDayOfWeek: 1, to: '01:40', fill: true, colorMode: 'red' }]; - const from = dateTime('2018-12-07T12:51:19+01:00'); - const to = dateTime('2018-12-10T13:51:29+01:00'); - ctx.setup(regions, from, to); - - it('should add 1 marking', () => { - expect(ctx.options.grid.markings.length).toBe(1); - }); - - it('should add one fill between sunday 23:00 and monday 01:40', () => { - const markings = ctx.options.grid.markings; - - expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-12-10T00:00:00+01:00').format()); - expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-12-10T02:40:00+01:00').format()); - }); - }); - - plotOptionsScenario('for day of week from/to time region', (ctx: any) => { - const regions = [{ fromDayOfWeek: 6, from: '03:00', toDayOfWeek: 7, to: '02:00', fill: true, colorMode: 'red' }]; - const from = dateTime('2018-12-07T12:51:19+01:00'); - const to = dateTime('2018-12-10T13:51:29+01:00'); - ctx.setup(regions, from, to); - - it('should add 1 marking', () => { - expect(ctx.options.grid.markings.length).toBe(1); - }); - - it('should add one fill between saturday 03:00 and sunday 02:00', () => { - const markings = ctx.options.grid.markings; - - expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-12-08T04:00:00+01:00').format()); - expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-12-09T03:00:00+01:00').format()); - }); - }); - - plotOptionsScenario('for day of week from/to time region with daylight saving time', (ctx: any) => { - const regions = [{ fromDayOfWeek: 7, from: '20:00', toDayOfWeek: 7, to: '23:00', fill: true, colorMode: 'red' }]; - const from = dateTime('2018-03-17T06:00:00+01:00'); - const to = dateTime('2018-04-03T06:00:00+02:00'); - ctx.setup(regions, from, to); - - it('should add 3 markings', () => { - expect(ctx.options.grid.markings.length).toBe(3); - }); - - it('should add one fill at each sunday between 20:00 and 23:00', () => { - const markings = ctx.options.grid.markings; - - expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-03-18T21:00:00+01:00').format()); - expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-03-19T00:00:00+01:00').format()); - - expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-03-25T22:00:00+02:00').format()); - expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-03-26T01:00:00+02:00').format()); - - expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-04-01T22:00:00+02:00').format()); - expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-04-02T01:00:00+02:00').format()); - }); - }); - - plotOptionsScenario('for each day of week with winter time', (ctx: any) => { - const regions = [{ fromDayOfWeek: 7, toDayOfWeek: 7, fill: true, colorMode: 'red' }]; - const from = dateTime('2018-10-20T14:50:11+02:00'); - const to = dateTime('2018-11-07T12:56:23+01:00'); - ctx.setup(regions, from, to); - - it('should add 3 markings', () => { - expect(ctx.options.grid.markings.length).toBe(3); - }); - - it('should add one fill at each sunday', () => { - const markings = ctx.options.grid.markings; - - expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-10-21T02:00:00+02:00').format()); - expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-10-22T01:59:59+02:00').format()); - - expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-10-28T02:00:00+02:00').format()); - expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-10-29T00:59:59+01:00').format()); - - expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-11-04T01:00:00+01:00').format()); - expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-11-05T00:59:59+01:00').format()); - }); - }); - }); -}); diff --git a/public/app/plugins/panel/heatmap/module.tsx b/public/app/plugins/panel/heatmap/module.tsx index f710858e0ec..6ec45e27d9a 100644 --- a/public/app/plugins/panel/heatmap/module.tsx +++ b/public/app/plugins/panel/heatmap/module.tsx @@ -23,6 +23,13 @@ import { Options, defaultOptions, HeatmapColorMode, HeatmapColorScale } from './ export const plugin = new PanelPlugin(HeatmapPanel) .useFieldConfig({ disableStandardOptions: Object.values(FieldConfigProperty).filter((v) => v !== FieldConfigProperty.Links), + standardOptions: { + [FieldConfigProperty.Links]: { + settings: { + showOneClick: true, + }, + }, + }, useCustomConfig: (builder) => { builder.addCustomEditor({ id: 'scaleDistribution', diff --git a/public/app/plugins/panel/histogram/HistogramPanel.tsx b/public/app/plugins/panel/histogram/HistogramPanel.tsx index 2c2805069a8..517f41187fc 100644 --- a/public/app/plugins/panel/histogram/HistogramPanel.tsx +++ b/public/app/plugins/panel/histogram/HistogramPanel.tsx @@ -1,7 +1,7 @@ import { useMemo } from 'react'; -import { PanelProps, buildHistogram, cacheFieldDisplayNames, getHistogramFields } from '@grafana/data'; -import { histogramFieldsToFrame } from '@grafana/data/src/transformations/transformers/histogram'; +import { DataFrameType, PanelProps, buildHistogram, cacheFieldDisplayNames, getHistogramFields } from '@grafana/data'; +import { histogramFieldsToFrame, joinHistograms } from '@grafana/data/src/transformations/transformers/histogram'; import { TooltipDisplayMode, TooltipPlugin2, useTheme2 } from '@grafana/ui'; import { TooltipHoverMode } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin2'; @@ -34,13 +34,19 @@ export const HistogramPanel = ({ data, options, width, height }: Props) => { cacheFieldDisplayNames(data.series); - if (data.series.length === 1) { - const info = getHistogramFields(data.series[0]); - if (info) { - return histogramFieldsToFrame(info); + if ( + data.series.length === 1 || + data.series.every( + (frame) => frame.meta?.type === DataFrameType.HeatmapCells || frame.meta?.type === DataFrameType.HeatmapRows + ) + ) { + const histograms = data.series.map((frame) => getHistogramFields(frame)).filter((hist) => hist != null); + + if (histograms.length) { + return histogramFieldsToFrame(joinHistograms(histograms), theme); } } - const hist = buildHistogram(data.series, options); + const hist = buildHistogram(data.series, options, theme); if (!hist) { return undefined; } diff --git a/public/app/plugins/panel/histogram/module.tsx b/public/app/plugins/panel/histogram/module.tsx index c73870cf739..3be418ceb87 100644 --- a/public/app/plugins/panel/histogram/module.tsx +++ b/public/app/plugins/panel/histogram/module.tsx @@ -81,6 +81,11 @@ export const plugin = new PanelPlugin(HistogramPanel) mode: FieldColorModeId.PaletteClassic, }, }, + [FieldConfigProperty.Links]: { + settings: { + showOneClick: true, + }, + }, }, useCustomConfig: (builder) => { const cfg = defaultFieldConfig; diff --git a/public/app/plugins/panel/logs-new/LogsPanel.tsx b/public/app/plugins/panel/logs-new/LogsPanel.tsx new file mode 100644 index 00000000000..44dea0e5f54 --- /dev/null +++ b/public/app/plugins/panel/logs-new/LogsPanel.tsx @@ -0,0 +1,129 @@ +import { css } from '@emotion/css'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; + +import { + AbsoluteTimeRange, + CoreApp, + DataFrame, + GrafanaTheme2, + LoadingState, + LogsSortOrder, + PanelProps, +} from '@grafana/data'; +import { config } from '@grafana/runtime'; +import { usePanelContext, useStyles2 } from '@grafana/ui'; +import { LogList } from 'app/features/logs/components/panel/LogList'; +import { PanelDataErrorView } from 'app/features/panel/components/PanelDataErrorView'; + +import { dataFrameToLogsModel, dedupLogRows } from '../../../features/logs/logsModel'; +import { requestMoreLogs } from '../logs/LogsPanel'; +import { isOnNewLogsReceivedType } from '../logs/types'; +import { useDatasourcesFromTargets } from '../logs/useDatasourcesFromTargets'; + +import { Options } from './panelcfg.gen'; + +interface LogsPanelProps extends PanelProps {} + +export const LogsPanel = ({ + data, + timeZone, + fieldConfig, + options: { dedupStrategy, enableInfiniteScrolling, onNewLogsReceived, showTime, sortOrder, wrapLogMessage }, + id, +}: LogsPanelProps) => { + const style = useStyles2(getStyles); + const [logsContainer, setLogsContainer] = useState(null); + const [panelData, setPanelData] = useState(data); + const dataSourcesMap = useDatasourcesFromTargets(data.request?.targets); + // Prevents the scroll position to change when new data from infinite scrolling is received + const keepScrollPositionRef = useRef(false); + // Loading ref to prevent firing multiple requests + const loadingRef = useRef(false); + const { eventBus } = usePanelContext(); + + const logs = useMemo(() => { + const logsModel = panelData + ? dataFrameToLogsModel(panelData.series, panelData.request?.intervalMs, undefined, panelData.request?.targets) + : null; + return logsModel ? dedupLogRows(logsModel.rows, dedupStrategy) : []; + }, [dedupStrategy, panelData]); + + useEffect(() => { + if (data.state !== LoadingState.Loading) { + setPanelData(data); + } + }, [data]); + + const loadMoreLogs = useCallback( + async (scrollRange: AbsoluteTimeRange) => { + if (!data.request || !config.featureToggles.logsInfiniteScrolling || loadingRef.current) { + return; + } + loadingRef.current = true; + + const onNewLogsReceivedCallback = isOnNewLogsReceivedType(onNewLogsReceived) ? onNewLogsReceived : undefined; + + let newSeries: DataFrame[] = []; + try { + newSeries = await requestMoreLogs(dataSourcesMap, panelData, scrollRange, timeZone, onNewLogsReceivedCallback); + } catch (e) { + console.error(e); + } finally { + loadingRef.current = false; + } + + keepScrollPositionRef.current = true; + setPanelData({ + ...panelData, + series: newSeries, + }); + }, + [data.request, dataSourcesMap, onNewLogsReceived, panelData, timeZone] + ); + + const initialScrollPosition = useMemo(() => { + /** + * In dashboards, users with newest logs at the bottom have the expectation of keeping the scroll at the bottom + * when new data is received. See https://github.com/grafana/grafana/pull/37634 + */ + if (data.request?.app === CoreApp.Dashboard || data.request?.app === CoreApp.PanelEditor) { + return sortOrder === LogsSortOrder.Ascending ? 'bottom' : 'top'; + } + return 'top'; + }, [data.request?.app, sortOrder]); + + if (!logs.length) { + return ; + } + + return ( +
setLogsContainer(element)}> + {logs.length > 0 && logsContainer && ( + + )} +
+ ); +}; + +const getStyles = (theme: GrafanaTheme2) => ({ + container: css({ + marginBottom: theme.spacing(1.5), + minHeight: '100%', + maxHeight: '100%', + display: 'flex', + flex: 1, + flexDirection: 'column', + }), +}); diff --git a/public/app/plugins/panel/logs-new/img/icn-logs-panel.svg b/public/app/plugins/panel/logs-new/img/icn-logs-panel.svg new file mode 100644 index 00000000000..046b59454ad --- /dev/null +++ b/public/app/plugins/panel/logs-new/img/icn-logs-panel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/app/plugins/panel/logs-new/module.tsx b/public/app/plugins/panel/logs-new/module.tsx new file mode 100644 index 00000000000..1e024cfcbd3 --- /dev/null +++ b/public/app/plugins/panel/logs-new/module.tsx @@ -0,0 +1,73 @@ +import { PanelPlugin, LogsSortOrder, LogsDedupStrategy, LogsDedupDescription } from '@grafana/data'; + +import { LogsPanel } from './LogsPanel'; +import { Options } from './panelcfg.gen'; +import { LogsPanelSuggestionsSupplier } from './suggestions'; + +export const plugin = new PanelPlugin(LogsPanel) + .setPanelOptions((builder) => { + builder + .addBooleanSwitch({ + path: 'showTime', + name: 'Time', + description: '', + defaultValue: false, + }) + .addBooleanSwitch({ + path: 'wrapLogMessage', + name: 'Wrap lines', + description: '', + defaultValue: false, + }) + .addBooleanSwitch({ + path: 'enableLogDetails', + name: 'Enable log details', + description: '', + defaultValue: true, + }) + .addBooleanSwitch({ + path: 'enableInfiniteScrolling', + name: 'Enable infinite scrolling', + description: 'Experimental. Request more results by scrolling to the bottom of the logs list.', + defaultValue: false, + }) + .addRadio({ + path: 'dedupStrategy', + name: 'Deduplication', + description: '', + settings: { + options: [ + { value: LogsDedupStrategy.none, label: 'None', description: LogsDedupDescription[LogsDedupStrategy.none] }, + { + value: LogsDedupStrategy.exact, + label: 'Exact', + description: LogsDedupDescription[LogsDedupStrategy.exact], + }, + { + value: LogsDedupStrategy.numbers, + label: 'Numbers', + description: LogsDedupDescription[LogsDedupStrategy.numbers], + }, + { + value: LogsDedupStrategy.signature, + label: 'Signature', + description: LogsDedupDescription[LogsDedupStrategy.signature], + }, + ], + }, + defaultValue: LogsDedupStrategy.none, + }) + .addRadio({ + path: 'sortOrder', + name: 'Order', + description: '', + settings: { + options: [ + { value: LogsSortOrder.Descending, label: 'Newest first' }, + { value: LogsSortOrder.Ascending, label: 'Oldest first' }, + ], + }, + defaultValue: LogsSortOrder.Descending, + }); + }) + .setSuggestionsSupplier(new LogsPanelSuggestionsSupplier()); diff --git a/public/app/plugins/panel/logs-new/panelcfg.cue b/public/app/plugins/panel/logs-new/panelcfg.cue new file mode 100644 index 00000000000..e7b28449105 --- /dev/null +++ b/public/app/plugins/panel/logs-new/panelcfg.cue @@ -0,0 +1,41 @@ +// Copyright 2023 Grafana Labs +// +// Licensed under the Apache License, Version 2.0 (the "License") +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package grafanaplugin + +import ( + "github.com/grafana/grafana/packages/grafana-schema/src/common" +) + +composableKinds: PanelCfg: { + maturity: "experimental" + + lineage: { + schemas: [{ + version: [0, 0] + schema: { + Options: { + showTime: bool + wrapLogMessage: bool + enableLogDetails: bool + sortOrder: common.LogsSortOrder + dedupStrategy: common.LogsDedupStrategy + enableInfiniteScrolling?: bool + onNewLogsReceived?: _ + } @cuetsy(kind="interface") + } + }] + lenses: [] + } +} diff --git a/public/app/plugins/panel/logs-new/panelcfg.gen.ts b/public/app/plugins/panel/logs-new/panelcfg.gen.ts new file mode 100644 index 00000000000..62523679a72 --- /dev/null +++ b/public/app/plugins/panel/logs-new/panelcfg.gen.ts @@ -0,0 +1,21 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. +// +// Generated by: +// public/app/plugins/gen.go +// Using jennies: +// TSTypesJenny +// PluginTsTypesJenny +// +// Run 'make gen-cue' from repository root to regenerate. + +import * as common from '@grafana/schema'; + +export interface Options { + dedupStrategy: common.LogsDedupStrategy; + enableInfiniteScrolling?: boolean; + enableLogDetails: boolean; + onNewLogsReceived?: unknown; + showTime: boolean; + sortOrder: common.LogsSortOrder; + wrapLogMessage: boolean; +} diff --git a/public/app/plugins/panel/logs-new/plugin.json b/public/app/plugins/panel/logs-new/plugin.json new file mode 100644 index 00000000000..4d02ef9582d --- /dev/null +++ b/public/app/plugins/panel/logs-new/plugin.json @@ -0,0 +1,17 @@ +{ + "type": "panel", + "name": "Logs new", + "id": "logs-new", + "state": "alpha", + + "info": { + "author": { + "name": "Grafana Labs", + "url": "https://grafana.com" + }, + "logos": { + "small": "img/icn-logs-panel.svg", + "large": "img/icn-logs-panel.svg" + } + } +} diff --git a/public/app/plugins/panel/logs-new/suggestions.ts b/public/app/plugins/panel/logs-new/suggestions.ts new file mode 100644 index 00000000000..5229b4fb7f0 --- /dev/null +++ b/public/app/plugins/panel/logs-new/suggestions.ts @@ -0,0 +1,33 @@ +import { VisualizationSuggestionsBuilder, VisualizationSuggestionScore } from '@grafana/data'; +import { SuggestionName } from 'app/types/suggestions'; + +import { Options } from './panelcfg.gen'; + +export class LogsPanelSuggestionsSupplier { + getSuggestionsForData(builder: VisualizationSuggestionsBuilder) { + const list = builder.getListAppender({ + name: '', + pluginId: 'logs-new', + options: {}, + fieldConfig: { + defaults: { + custom: {}, + }, + overrides: [], + }, + }); + + const { dataSummary: ds } = builder; + + // Require a string & time field + if (!ds.hasData || !ds.hasTimeField || !ds.hasStringField) { + return; + } + + if (ds.preferredVisualisationType === 'logs') { + list.append({ name: SuggestionName.Logs, score: VisualizationSuggestionScore.Best }); + } else { + list.append({ name: SuggestionName.Logs }); + } + } +} diff --git a/public/app/plugins/panel/logs/LogsPanel.test.tsx b/public/app/plugins/panel/logs/LogsPanel.test.tsx index 5f83f431ba3..7def6ca6cb5 100644 --- a/public/app/plugins/panel/logs/LogsPanel.test.tsx +++ b/public/app/plugins/panel/logs/LogsPanel.test.tsx @@ -414,7 +414,7 @@ describe('LogsPanel', () => { await userEvent.click(screen.getByLabelText(/show context/i)); const getRowContextCb = logRowContextModalMock.mock.calls[0][0].getRowContext; - getRowContextCb(); + getRowContextCb({}, {}); expect(showContextDs.getLogRowContext).toBeCalled(); }); }); @@ -518,6 +518,22 @@ describe('LogsPanel', () => { expect(await screen.findByRole('row')).toBeInTheDocument(); expect(jest.mocked(styles.getLogRowStyles).mock.calls.length).toBeGreaterThan(3); }); + + it('does not re-render when data is loading', async () => { + const { rerender, props } = setup({ + data: { + ...defaultProps.data, + series, + }, + }); + + expect(await screen.findByRole('row')).toBeInTheDocument(); + + rerender(); + + expect(await screen.findByRole('row')).toBeInTheDocument(); + expect(styles.getLogRowStyles).toHaveBeenCalledTimes(3); + }); }); describe('Filters', () => { diff --git a/public/app/plugins/panel/logs/LogsPanel.tsx b/public/app/plugins/panel/logs/LogsPanel.tsx index 73d835edbbf..a335e830aca 100644 --- a/public/app/plugins/panel/logs/LogsPanel.tsx +++ b/public/app/plugins/panel/logs/LogsPanel.tsx @@ -28,6 +28,7 @@ import { toUtc, urlUtil, LogSortOrderChangeEvent, + LoadingState, } from '@grafana/data'; import { convertRawToRange } from '@grafana/data/src/datetime/rangeutil'; import { config, getAppEvents } from '@grafana/runtime'; @@ -133,7 +134,6 @@ export const LogsPanel = ({ const style = useStyles2(getStyles); const logsContainerRef = useRef(null); const [contextRow, setContextRow] = useState(null); - const dataSourcesMap = useDatasourcesFromTargets(data.request?.targets); const [scrollElement, setScrollElement] = useState(null); const [displayedFields, setDisplayedFields] = useState(options.displayedFields ?? []); // Loading state to be passed as a prop to the component @@ -141,6 +141,7 @@ export const LogsPanel = ({ // Loading ref to prevent firing multiple requests const loadingRef = useRef(false); const [panelData, setPanelData] = useState(data); + const dataSourcesMap = useDatasourcesFromTargets(panelData.request?.targets); // Prevents the scroll position to change when new data from infinite scrolling is received const keepScrollPositionRef = useRef(false); let closeCallback = useRef<() => void>(); @@ -194,9 +195,9 @@ export const LogsPanel = ({ !row.dataFrame.refId || !dataSourcesMap || (!showLogContextToggle && - data.request?.app !== CoreApp.Dashboard && - data.request?.app !== CoreApp.PanelEditor && - data.request?.app !== CoreApp.PanelViewer) + panelData.request?.app !== CoreApp.Dashboard && + panelData.request?.app !== CoreApp.PanelEditor && + panelData.request?.app !== CoreApp.PanelViewer) ) { return false; } @@ -204,16 +205,16 @@ export const LogsPanel = ({ const dataSource = dataSourcesMap.get(row.dataFrame.refId); return hasLogsContextSupport(dataSource); }, - [dataSourcesMap, showLogContextToggle, data.request?.app] + [dataSourcesMap, showLogContextToggle, panelData.request?.app] ); const showPermaLink = useCallback(() => { return !( - data.request?.app !== CoreApp.Dashboard && - data.request?.app !== CoreApp.PanelEditor && - data.request?.app !== CoreApp.PanelViewer + panelData.request?.app !== CoreApp.Dashboard && + panelData.request?.app !== CoreApp.PanelEditor && + panelData.request?.app !== CoreApp.PanelViewer ); - }, [data.request?.app]); + }, [panelData.request?.app]); const getLogRowContext = useCallback( async (row: LogRowModel, origRow: LogRowModel, options: LogRowContextOptions): Promise => { @@ -221,7 +222,7 @@ export const LogsPanel = ({ return Promise.resolve({ data: [] }); } - const query = data.request?.targets[0]; + const query = panelData.request?.targets[0]; if (!query) { return Promise.resolve({ data: [] }); } @@ -231,9 +232,11 @@ export const LogsPanel = ({ return Promise.resolve({ data: [] }); } + options.scopedVars = panelData.request?.scopedVars; + return dataSource.getLogRowContext(row, options, query); }, - [data.request?.targets, dataSourcesMap] + [panelData.request?.targets, panelData.request?.scopedVars, dataSourcesMap] ); const getLogRowContextUi = useCallback( @@ -242,7 +245,7 @@ export const LogsPanel = ({ return <>; } - const query = data.request?.targets[0]; + const query = panelData.request?.targets[0]; if (!query) { return <>; } @@ -256,31 +259,33 @@ export const LogsPanel = ({ return <>; } - return dataSource.getLogRowContextUi(origRow, runContextQuery, query); + return dataSource.getLogRowContextUi(origRow, runContextQuery, query, panelData.request?.scopedVars); }, - [data.request?.targets, dataSourcesMap] + [panelData.request?.targets, panelData.request?.scopedVars, dataSourcesMap] ); // Important to memoize stuff here, as panel rerenders a lot for example when resizing. const [logRows, deduplicatedRows, commonLabels] = useMemo(() => { const logs = panelData - ? dataFrameToLogsModel(panelData.series, data.request?.intervalMs, undefined, data.request?.targets) + ? dataFrameToLogsModel(panelData.series, panelData.request?.intervalMs, undefined, panelData.request?.targets) : null; const logRows = logs?.rows || []; const commonLabels = logs?.meta?.find((m) => m.label === COMMON_LABELS); const deduplicatedRows = dedupLogRows(logRows, dedupStrategy); return [logRows, deduplicatedRows, commonLabels]; - }, [data.request?.intervalMs, data.request?.targets, dedupStrategy, panelData]); + }, [dedupStrategy, panelData]); const onPermalinkClick = useCallback( async (row: LogRowModel) => { - return await copyDashboardUrl(row, logRows, data.timeRange); + return await copyDashboardUrl(row, logRows, panelData.timeRange); }, - [data.timeRange, logRows] + [panelData.timeRange, logRows] ); useEffect(() => { - setPanelData(data); + if (data.state !== LoadingState.Loading) { + setPanelData(data); + } }, [data]); useLayoutEffect(() => { @@ -292,16 +297,16 @@ export const LogsPanel = ({ * In dashboards, users with newest logs at the bottom have the expectation of keeping the scroll at the bottom * when new data is received. See https://github.com/grafana/grafana/pull/37634 */ - if (data.request?.app === CoreApp.Dashboard || data.request?.app === CoreApp.PanelEditor) { + if (panelData.request?.app === CoreApp.Dashboard || panelData.request?.app === CoreApp.PanelEditor) { scrollElement.scrollTo(0, isAscending ? logsContainerRef.current.scrollHeight : 0); } - }, [data.request?.app, isAscending, scrollElement, logRows]); + }, [panelData.request?.app, isAscending, scrollElement, logRows]); const getFieldLinks = useCallback( (field: Field, rowIndex: number) => { - return getFieldLinksForExplore({ field, rowIndex, range: data.timeRange }); + return getFieldLinksForExplore({ field, rowIndex, range: panelData.timeRange }); }, - [data] + [panelData] ); /** @@ -558,7 +563,7 @@ async function copyDashboardUrl(row: LogRowModel, rows: LogRowModel[], timeRange return Promise.resolve(); } -async function requestMoreLogs( +export async function requestMoreLogs( dataSourcesMap: Map, panelData: PanelData, timeRange: AbsoluteTimeRange, diff --git a/public/app/plugins/panel/logs/module.tsx b/public/app/plugins/panel/logs/module.tsx index f6d3aa142fa..45952d81f89 100644 --- a/public/app/plugins/panel/logs/module.tsx +++ b/public/app/plugins/panel/logs/module.tsx @@ -1,8 +1,8 @@ import { PanelPlugin, LogsSortOrder, LogsDedupStrategy, LogsDedupDescription } from '@grafana/data'; import { LogsPanel } from './LogsPanel'; +import { Options } from './panelcfg.gen'; import { LogsPanelSuggestionsSupplier } from './suggestions'; -import { Options } from './types'; export const plugin = new PanelPlugin(LogsPanel) .setPanelOptions((builder) => { diff --git a/public/app/plugins/panel/nodeGraph/Edge.tsx b/public/app/plugins/panel/nodeGraph/Edge.tsx index 405d07f1a9e..e4802236cef 100644 --- a/public/app/plugins/panel/nodeGraph/Edge.tsx +++ b/public/app/plugins/panel/nodeGraph/Edge.tsx @@ -15,10 +15,11 @@ interface Props { onClick: (event: MouseEvent, link: EdgeDatumLayout) => void; onMouseEnter: (id: string) => void; onMouseLeave: (id: string) => void; + processedNodesLength: number; } export const Edge = memo(function Edge(props: Props) { - const { edge, onClick, onMouseEnter, onMouseLeave, hovering, svgIdNamespace } = props; + const { edge, onClick, onMouseEnter, onMouseLeave, hovering, svgIdNamespace, processedNodesLength } = props; // Not great typing but after we do layout these properties are full objects not just references const { source, target, sourceNodeRadius, targetNodeRadius } = edge as { @@ -56,6 +57,7 @@ export const Edge = memo(function Edge(props: Props) { onClick(event, edge)} style={{ cursor: 'pointer' }} aria-label={`Edge from: ${source.id} to: ${target.id}`} diff --git a/public/app/plugins/panel/nodeGraph/NodeGraph.tsx b/public/app/plugins/panel/nodeGraph/NodeGraph.tsx index 02a7f5018d6..ffd741ca52c 100644 --- a/public/app/plugins/panel/nodeGraph/NodeGraph.tsx +++ b/public/app/plugins/panel/nodeGraph/NodeGraph.tsx @@ -227,6 +227,7 @@ export function NodeGraph({ getLinks, dataFrames, nodeLimit, panelId, zoomMode } onMouseEnter={setEdgeHover} onMouseLeave={clearEdgeHover} svgIdNamespace={svgIdNamespace} + processedNodesLength={processed.nodes.length} /> )} , link: EdgeDatumLayout) => void; onMouseEnter: (id: string) => void; onMouseLeave: (id: string) => void; + processedNodesLength: number; } const Edges = memo(function Edges(props: EdgesProps) { return ( <> {props.edges.map((e) => ( ))} diff --git a/public/app/plugins/panel/nodeGraph/types.ts b/public/app/plugins/panel/nodeGraph/types.ts index 377c5df5526..55081318b65 100644 --- a/public/app/plugins/panel/nodeGraph/types.ts +++ b/public/app/plugins/panel/nodeGraph/types.ts @@ -17,6 +17,7 @@ export type NodeDatum = SimulationNodeDatum & { icon?: IconName; nodeRadius?: Field; highlighted: boolean; + isInstrumented?: boolean; }; export type NodeDatumFromEdge = NodeDatum & { mainStatNumeric?: number; secondaryStatNumeric?: number }; diff --git a/public/app/plugins/panel/nodeGraph/utils.test.ts b/public/app/plugins/panel/nodeGraph/utils.test.ts index 3c563cbddd6..5ad0ce82d4e 100644 --- a/public/app/plugins/panel/nodeGraph/utils.test.ts +++ b/public/app/plugins/panel/nodeGraph/utils.test.ts @@ -19,7 +19,7 @@ describe('processNodes', () => { it('returns proper nodes and edges', async () => { const { nodes, edges, legend } = processNodes( - makeNodesDataFrame(3), + makeNodesDataFrame(3, [{ isinstrumented: false }]), makeEdgesDataFrame([ { source: '0', target: '1' }, { source: '0', target: '2' }, @@ -28,7 +28,7 @@ describe('processNodes', () => { ); expect(nodes).toEqual([ - makeNodeDatum(), + makeNodeDatum({ isInstrumented: false }), makeNodeDatum({ dataFrameRowIndex: 1, id: '1', incoming: 1, title: 'service:1' }), makeNodeDatum({ dataFrameRowIndex: 2, id: '2', incoming: 2, title: 'service:2' }), ]); @@ -366,6 +366,7 @@ function makeNodeDatum(options: Partial = {}) { type: 'number', values: [40, 40, 40], }, + isInstrumented: true, ...options, }; } diff --git a/public/app/plugins/panel/nodeGraph/utils.ts b/public/app/plugins/panel/nodeGraph/utils.ts index bfeb54bd7cc..4a26ae38685 100644 --- a/public/app/plugins/panel/nodeGraph/utils.ts +++ b/public/app/plugins/panel/nodeGraph/utils.ts @@ -58,6 +58,7 @@ export type NodeFields = { icon?: Field; nodeRadius?: Field; highlighted?: Field; + isInstrumented?: Field; }; export function getNodeFields(nodes: DataFrame): NodeFields { @@ -80,6 +81,7 @@ export function getNodeFields(nodes: DataFrame): NodeFields { highlighted: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.highlighted.toLowerCase()), fixedX: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.fixedX.toLowerCase()), fixedY: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.fixedY.toLowerCase()), + isInstrumented: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.isInstrumented.toLowerCase()), }; } @@ -364,6 +366,7 @@ function makeNodeDatum(id: string, nodeFields: NodeFields, index: number): NodeD highlighted: nodeFields.highlighted?.values[index] || false, x: nodeFields.fixedX?.values[index] ?? undefined, y: nodeFields.fixedY?.values[index] ?? undefined, + isInstrumented: nodeFields.isInstrumented?.values[index] ?? true, }; } @@ -384,16 +387,19 @@ export function statToString(config: FieldConfig, value: number | string): strin * Utilities mainly for testing */ -export function makeNodesDataFrame(count: number) { +export function makeNodesDataFrame( + count: number, + partialNodes: Array>> = [] +) { const frame = nodesFrame(); for (let i = 0; i < count; i++) { - frame.add(makeNode(i)); + frame.add(makeNode(i, partialNodes[i])); } return frame; } -function makeNode(index: number) { +function makeNode(index: number, partialNode: Partial> = {}) { return { id: index.toString(), title: `service:${index}`, @@ -405,6 +411,8 @@ function makeNode(index: number) { color: 0.5, icon: 'database', noderadius: 40, + isinstrumented: true, + ...partialNode, }; } @@ -453,6 +461,10 @@ function nodesFrame() { values: [], type: FieldType.number, }, + [NodeGraphDataFrameFieldNames.isInstrumented]: { + values: [], + type: FieldType.boolean, + }, }; return new MutableDataFrame({ diff --git a/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx b/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx index 81e272a6b98..7a23e4a6898 100644 --- a/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx +++ b/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx @@ -1,11 +1,9 @@ -import { css } from '@emotion/css'; import { useMemo, useState } from 'react'; -import { useMeasure } from 'react-use'; -import { DashboardCursorSync, DataFrame, PanelProps } from '@grafana/data'; +import { DashboardCursorSync, PanelProps } from '@grafana/data'; import { + AxisPlacement, EventBusPlugin, - Pagination, TooltipDisplayMode, TooltipPlugin2, usePanelContext, @@ -14,7 +12,6 @@ import { import { TimeRange2, TooltipHoverMode } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin2'; import { TimelineChart } from 'app/core/components/TimelineChart/TimelineChart'; import { - makeFramePerSeries, prepareTimelineFields, prepareTimelineLegendItems, TimelineMode, @@ -25,73 +22,11 @@ import { OutsideRangePlugin } from '../timeseries/plugins/OutsideRangePlugin'; import { getTimezones } from '../timeseries/utils'; import { StateTimelineTooltip2 } from './StateTimelineTooltip2'; -import { Options, defaultOptions } from './panelcfg.gen'; +import { Options } from './panelcfg.gen'; +import { containerStyles, usePagination } from './utils'; interface TimelinePanelProps extends PanelProps {} -const styles = { - container: css({ - display: 'flex', - flexDirection: 'column', - }), - paginationContainer: css({ - display: 'flex', - justifyContent: 'center', - width: '100%', - }), - paginationElement: css({ - marginTop: '8px', - }), -}; - -function usePagination(frames?: DataFrame[], perPage?: number) { - const [currentPage, setCurrentPage] = useState(1); - - const [paginationWrapperRef, { height: paginationHeight, width: paginationWidth }] = useMeasure(); - - const pagedFrames = useMemo( - () => (!perPage || frames == null ? frames : makeFramePerSeries(frames)), - [frames, perPage] - ); - - if (!perPage || pagedFrames == null) { - return { - paginatedFrames: pagedFrames, - paginationRev: 'disabled', - paginationElement: undefined, - paginationHeight: 0, - }; - } - - perPage ||= defaultOptions.perPage!; - - const numberOfPages = Math.ceil(pagedFrames.length / perPage); - // `perPage` changing might lead to temporarily too large values of `currentPage`. - const currentPageCapped = Math.min(currentPage, numberOfPages); - const pageOffset = (currentPageCapped - 1) * perPage; - const currentPageFrames = pagedFrames.slice(pageOffset, pageOffset + perPage); - - // `paginationRev` needs to change value whenever any of the pagination settings changes. - // It's used in to trigger a reconfiguration of the underlying graphs (which is cached, - // hence an explicit nudge is required). - const paginationRev = `${currentPageCapped}/${perPage}`; - - const showSmallVersion = paginationWidth < 550; - const paginationElement = ( -
- -
- ); - - return { paginatedFrames: currentPageFrames, paginationRev, paginationElement, paginationHeight }; -} - /** * @alpha */ @@ -140,7 +75,7 @@ export const StateTimelinePanel = ({ const enableAnnotationCreation = Boolean(canAddAnnotations && canAddAnnotations()); return ( -
+
- alignedFrame.fields[seriesIdx]!.getLinks?.({ valueRowIndex: dataIdx }) ?? [] + getDataLinks={(seriesIdx, dataIdx) => + alignedFrame.fields[seriesIdx].getLinks?.({ valueRowIndex: dataIdx }) ?? [] } render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2, viaSync, dataLinks) => { if (enableAnnotationCreation && timeRange2 != null) { @@ -209,15 +144,16 @@ export const StateTimelinePanel = ({ maxWidth={options.tooltip.maxWidth} /> )} - {/* Renders annotations */} - + {alignedFrame.fields[0].config.custom?.axisPlacement !== AxisPlacement.Hidden && ( + + )} ); diff --git a/public/app/plugins/panel/state-timeline/StateTimelineTooltip2.tsx b/public/app/plugins/panel/state-timeline/StateTimelineTooltip2.tsx index a2f2665a11d..b400b9e6e73 100644 --- a/public/app/plugins/panel/state-timeline/StateTimelineTooltip2.tsx +++ b/public/app/plugins/panel/state-timeline/StateTimelineTooltip2.tsx @@ -68,12 +68,16 @@ export const StateTimelineTooltip2 = ({ let footer: ReactNode; - if (isPinned && seriesIdx != null) { + if (seriesIdx != null) { const field = series.fields[seriesIdx]; - const dataIdx = dataIdxs[seriesIdx]!; - const actions = getFieldActions(series, field, replaceVariables!, dataIdx); + const hasOneClickLink = dataLinks.some((dataLink) => dataLink.oneClick === true); - footer = ; + if (isPinned || hasOneClickLink) { + const dataIdx = dataIdxs[seriesIdx]!; + const actions = getFieldActions(series, field, replaceVariables!, dataIdx); + + footer = ; + } } const headerItem: VizTooltipItem = { diff --git a/public/app/plugins/panel/state-timeline/module.tsx b/public/app/plugins/panel/state-timeline/module.tsx index 972194fc52d..7d84934e74a 100644 --- a/public/app/plugins/panel/state-timeline/module.tsx +++ b/public/app/plugins/panel/state-timeline/module.tsx @@ -5,7 +5,7 @@ import { identityOverrideProcessor, PanelPlugin, } from '@grafana/data'; -import { VisibilityMode } from '@grafana/schema'; +import { AxisPlacement, VisibilityMode } from '@grafana/schema'; import { commonOptionsBuilder } from '@grafana/ui'; import { InsertNullsEditor } from '../timeseries/InsertNullsEditor'; @@ -29,6 +29,14 @@ export const plugin = new PanelPlugin(StateTimelinePanel) mode: FieldColorModeId.ContinuousGrYlRd, }, }, + [FieldConfigProperty.Links]: { + settings: { + showOneClick: true, + }, + }, + [FieldConfigProperty.Actions]: { + hideFromDefaults: false, + }, }, useCustomConfig: (builder) => { builder @@ -76,6 +84,11 @@ export const plugin = new PanelPlugin(StateTimelinePanel) }); commonOptionsBuilder.addHideFrom(builder); + commonOptionsBuilder.addAxisPlacement( + builder, + (placement) => placement === AxisPlacement.Auto || placement === AxisPlacement.Hidden + ); + commonOptionsBuilder.addAxisWidth(builder); }, }) .setPanelOptions((builder) => { diff --git a/public/app/plugins/panel/state-timeline/panelcfg.cue b/public/app/plugins/panel/state-timeline/panelcfg.cue index a186f5e12e8..5a9c34e2a28 100644 --- a/public/app/plugins/panel/state-timeline/panelcfg.cue +++ b/public/app/plugins/panel/state-timeline/panelcfg.cue @@ -41,6 +41,7 @@ composableKinds: PanelCfg: { perPage?: number & >=1 | *20 } @cuetsy(kind="interface") FieldConfig: { + ui.AxisConfig ui.HideableFieldConfig lineWidth?: uint32 & <=10 | *0 fillOpacity?: uint32 & <=100 | *70 diff --git a/public/app/plugins/panel/state-timeline/panelcfg.gen.ts b/public/app/plugins/panel/state-timeline/panelcfg.gen.ts index 0fc25fe65b8..5ea1b58af54 100644 --- a/public/app/plugins/panel/state-timeline/panelcfg.gen.ts +++ b/public/app/plugins/panel/state-timeline/panelcfg.gen.ts @@ -41,7 +41,7 @@ export const defaultOptions: Partial = { showValue: ui.VisibilityMode.Auto, }; -export interface FieldConfig extends ui.HideableFieldConfig { +export interface FieldConfig extends ui.AxisConfig, ui.HideableFieldConfig { fillOpacity?: number; lineWidth?: number; } diff --git a/public/app/plugins/panel/state-timeline/utils.tsx b/public/app/plugins/panel/state-timeline/utils.tsx new file mode 100644 index 00000000000..188625534c7 --- /dev/null +++ b/public/app/plugins/panel/state-timeline/utils.tsx @@ -0,0 +1,75 @@ +import { css } from '@emotion/css'; +import { useMemo, useState } from 'react'; +import { useMeasure } from 'react-use'; + +import { DataFrame } from '@grafana/data'; +import { Pagination } from '@grafana/ui'; +import { makeFramePerSeries } from 'app/core/components/TimelineChart/utils'; + +import { defaultOptions } from './panelcfg.gen'; + +export const containerStyles = { + container: css({ + display: 'flex', + flexDirection: 'column', + }), +}; + +const styles = { + paginationContainer: css({ + display: 'flex', + justifyContent: 'center', + width: '100%', + }), + paginationElement: css({ + marginTop: '8px', + }), +}; + +export function usePagination(frames?: DataFrame[], perPage?: number) { + const [currentPage, setCurrentPage] = useState(1); + + const [paginationWrapperRef, { height: paginationHeight, width: paginationWidth }] = useMeasure(); + + const pagedFrames = useMemo( + () => (!perPage || frames == null ? frames : makeFramePerSeries(frames)), + [frames, perPage] + ); + + if (!perPage || pagedFrames == null) { + return { + paginatedFrames: pagedFrames, + paginationRev: 'disabled', + paginationElement: undefined, + paginationHeight: 0, + }; + } + + perPage ||= defaultOptions.perPage!; + + const numberOfPages = Math.ceil(pagedFrames.length / perPage); + // `perPage` changing might lead to temporarily too large values of `currentPage`. + const currentPageCapped = Math.min(currentPage, numberOfPages); + const pageOffset = (currentPageCapped - 1) * perPage; + const currentPageFrames = pagedFrames.slice(pageOffset, pageOffset + perPage); + + // `paginationRev` needs to change value whenever any of the pagination settings changes. + // It's used in to trigger a reconfiguration of the underlying graphs (which is cached, + // hence an explicit nudge is required). + const paginationRev = `${currentPageCapped}/${perPage}`; + + const showSmallVersion = paginationWidth < 550; + const paginationElement = ( +
+ +
+ ); + + return { paginatedFrames: currentPageFrames, paginationRev, paginationElement, paginationHeight }; +} diff --git a/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx b/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx index fd1164c85f3..1f410d1578c 100644 --- a/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx +++ b/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx @@ -1,7 +1,14 @@ import { useMemo, useState } from 'react'; import { DashboardCursorSync, PanelProps } from '@grafana/data'; -import { EventBusPlugin, TooltipDisplayMode, TooltipPlugin2, usePanelContext, useTheme2 } from '@grafana/ui'; +import { + AxisPlacement, + EventBusPlugin, + TooltipDisplayMode, + TooltipPlugin2, + usePanelContext, + useTheme2, +} from '@grafana/ui'; import { TimeRange2, TooltipHoverMode } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin2'; import { TimelineChart } from 'app/core/components/TimelineChart/TimelineChart'; import { @@ -11,6 +18,7 @@ import { } from 'app/core/components/TimelineChart/utils'; import { StateTimelineTooltip2 } from '../state-timeline/StateTimelineTooltip2'; +import { containerStyles, usePagination } from '../state-timeline/utils'; import { AnnotationsPlugin2 } from '../timeseries/plugins/AnnotationsPlugin2'; import { OutsideRangePlugin } from '../timeseries/plugins/OutsideRangePlugin'; import { getTimezones } from '../timeseries/utils'; @@ -46,14 +54,19 @@ export const StatusHistoryPanel = ({ [data.series, timeRange, theme] ); + const { paginatedFrames, paginationRev, paginationElement, paginationHeight } = usePagination( + frames, + options.perPage + ); + const legendItems = useMemo( - () => prepareTimelineLegendItems(frames, options.legend, theme), - [frames, options.legend, theme] + () => prepareTimelineLegendItems(paginatedFrames, options.legend, theme), + [paginatedFrames, options.legend, theme] ); const timezones = useMemo(() => getTimezones(options.timezone, timeZone), [options.timezone, timeZone]); - if (!frames || warn) { + if (!paginatedFrames || warn) { return (

{warn ?? 'No data found in response'}

@@ -62,97 +75,103 @@ export const StatusHistoryPanel = ({ } // Status grid requires some space between values - if (frames[0].length > width / 2) { + if (paginatedFrames[0].length > width / 2) { return (

Too many points to visualize properly.
- Update the query to return fewer points.
({frames[0].length} points received) + Update the query to return fewer points.
({paginatedFrames[0].length} points received)

); } return ( - - {(builder, alignedFrame) => { - return ( - <> - {cursorSync !== DashboardCursorSync.Off && ( - - )} - {options.tooltip.mode !== TooltipDisplayMode.None && ( - - alignedFrame.fields[seriesIdx]!.getLinks?.({ valueRowIndex: dataIdx }) ?? [] - } - render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2, viaSync, dataLinks) => { - if (enableAnnotationCreation && timeRange2 != null) { - setNewAnnotationRange(timeRange2); - dismiss(); - return; +
+ + {(builder, alignedFrame) => { + return ( + <> + {cursorSync !== DashboardCursorSync.Off && ( + + )} + {options.tooltip.mode !== TooltipDisplayMode.None && ( + + alignedFrame.fields[seriesIdx].getLinks?.({ valueRowIndex: dataIdx }) ?? [] + } + render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2, viaSync, dataLinks) => { + if (enableAnnotationCreation && timeRange2 != null) { + setNewAnnotationRange(timeRange2); + dismiss(); + return; + } - const annotate = () => { - let xVal = u.posToVal(u.cursor.left!, 'x'); + const annotate = () => { + let xVal = u.posToVal(u.cursor.left!, 'x'); - setNewAnnotationRange({ from: xVal, to: xVal }); - dismiss(); - }; + setNewAnnotationRange({ from: xVal, to: xVal }); + dismiss(); + }; - return ( - - ); - }} - maxWidth={options.tooltip.maxWidth} - /> - )} - - - - ); - }} - + return ( + + ); + }} + maxWidth={options.tooltip.maxWidth} + /> + )} + {alignedFrame.fields[0].config.custom?.axisPlacement !== AxisPlacement.Hidden && ( + + )} + + + ); + }} + + {paginationElement} +
); }; diff --git a/public/app/plugins/panel/status-history/module.tsx b/public/app/plugins/panel/status-history/module.tsx index 8d42b1c4d1b..d556c52ccc0 100644 --- a/public/app/plugins/panel/status-history/module.tsx +++ b/public/app/plugins/panel/status-history/module.tsx @@ -1,5 +1,5 @@ import { FieldColorModeId, FieldConfigProperty, PanelPlugin } from '@grafana/data'; -import { VisibilityMode } from '@grafana/schema'; +import { AxisPlacement, VisibilityMode } from '@grafana/schema'; import { commonOptionsBuilder } from '@grafana/ui'; import { StatusHistoryPanel } from './StatusHistoryPanel'; @@ -17,6 +17,14 @@ export const plugin = new PanelPlugin(StatusHistoryPanel) mode: FieldColorModeId.Thresholds, }, }, + [FieldConfigProperty.Links]: { + settings: { + showOneClick: true, + }, + }, + [FieldConfigProperty.Actions]: { + hideFromDefaults: false, + }, }, useCustomConfig: (builder) => { builder @@ -42,6 +50,11 @@ export const plugin = new PanelPlugin(StatusHistoryPanel) }); commonOptionsBuilder.addHideFrom(builder); + commonOptionsBuilder.addAxisPlacement( + builder, + (placement) => placement === AxisPlacement.Auto || placement === AxisPlacement.Hidden + ); + commonOptionsBuilder.addAxisWidth(builder); }, }) .setPanelOptions((builder) => { @@ -77,6 +90,15 @@ export const plugin = new PanelPlugin(StatusHistoryPanel) max: 1, step: 0.01, }, + }) + .addNumberInput({ + path: 'perPage', + name: 'Page size (enable pagination)', + settings: { + min: 1, + step: 1, + integer: true, + }, }); commonOptionsBuilder.addLegendOptions(builder, false); diff --git a/public/app/plugins/panel/status-history/panelcfg.cue b/public/app/plugins/panel/status-history/panelcfg.cue index ec5ab6c4fc7..f8bfc020da0 100644 --- a/public/app/plugins/panel/status-history/panelcfg.cue +++ b/public/app/plugins/panel/status-history/panelcfg.cue @@ -35,8 +35,11 @@ composableKinds: PanelCfg: { showValue: ui.VisibilityMode & (*"auto" | _) //Controls the column width colWidth?: float & <=1 | *0.9 + //Enables pagination when > 0 + perPage?: number & >=1 | *20 } @cuetsy(kind="interface") FieldConfig: { + ui.AxisConfig ui.HideableFieldConfig lineWidth?: uint32 & <=10 | *1 fillOpacity?: uint32 & <=100 | *70 diff --git a/public/app/plugins/panel/status-history/panelcfg.gen.ts b/public/app/plugins/panel/status-history/panelcfg.gen.ts index 8bc46e363d0..366f69f05fd 100644 --- a/public/app/plugins/panel/status-history/panelcfg.gen.ts +++ b/public/app/plugins/panel/status-history/panelcfg.gen.ts @@ -15,6 +15,10 @@ export interface Options extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui * Controls the column width */ colWidth?: number; + /** + * Enables pagination when > 0 + */ + perPage?: number; /** * Set the height of the rows */ @@ -27,11 +31,12 @@ export interface Options extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui export const defaultOptions: Partial = { colWidth: 0.9, + perPage: 20, rowHeight: 0.9, showValue: ui.VisibilityMode.Auto, }; -export interface FieldConfig extends ui.HideableFieldConfig { +export interface FieldConfig extends ui.AxisConfig, ui.HideableFieldConfig { fillOpacity?: number; lineWidth?: number; } diff --git a/public/app/plugins/panel/status-history/utils.ts b/public/app/plugins/panel/status-history/utils.ts index 9be0a4fefc5..cd683b7129c 100644 --- a/public/app/plugins/panel/status-history/utils.ts +++ b/public/app/plugins/panel/status-history/utils.ts @@ -1,6 +1,5 @@ import { ActionModel, Field, InterpolateFunction, LinkModel } from '@grafana/data'; import { DataFrame } from '@grafana/data/'; -import { config } from '@grafana/runtime'; import { getActions } from 'app/features/actions/utils'; export const getDataLinks = (field: Field, rowIdx: number) => { @@ -24,16 +23,14 @@ export const getDataLinks = (field: Field, rowIdx: number) => { return links; }; +export const getAllFrameActions = (dataFrame: DataFrame) => {}; + export const getFieldActions = ( dataFrame: DataFrame, field: Field, replaceVars: InterpolateFunction, rowIndex: number ) => { - if (!config.featureToggles?.vizActions) { - return []; - } - const actions: Array> = []; const actionLookup = new Set(); diff --git a/public/app/plugins/panel/table/TableCellOptionEditor.tsx b/public/app/plugins/panel/table/TableCellOptionEditor.tsx index cff753a5344..39b2059d760 100644 --- a/public/app/plugins/panel/table/TableCellOptionEditor.tsx +++ b/public/app/plugins/panel/table/TableCellOptionEditor.tsx @@ -3,7 +3,6 @@ import { merge } from 'lodash'; import { useState } from 'react'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; -import { config } from '@grafana/runtime'; import { TableCellOptions } from '@grafana/schema'; import { Field, Select, TableCellDisplayMode, useStyles2 } from '@grafana/ui'; @@ -91,15 +90,9 @@ let cellDisplayModeOptions: Array> = [ { value: { type: TableCellDisplayMode.DataLinks }, label: 'Data links' }, { value: { type: TableCellDisplayMode.JSONView }, label: 'JSON View' }, { value: { type: TableCellDisplayMode.Image }, label: 'Image' }, + { value: { type: TableCellDisplayMode.Actions }, label: 'Actions' }, ]; -if (config.featureToggles.vizActions) { - cellDisplayModeOptions = [ - ...cellDisplayModeOptions, - { value: { type: TableCellDisplayMode.Actions }, label: 'Actions' }, - ]; -} - const getStyles = (theme: GrafanaTheme2) => ({ fixBottomMargin: css({ marginBottom: theme.spacing(-2), diff --git a/public/app/plugins/panel/table/TablePanel.tsx b/public/app/plugins/panel/table/TablePanel.tsx index 3945829809b..527389c9e90 100644 --- a/public/app/plugins/panel/table/TablePanel.tsx +++ b/public/app/plugins/panel/table/TablePanel.tsx @@ -152,10 +152,6 @@ const getCellActions = ( rowIndex: number, replaceVariables: InterpolateFunction | undefined ) => { - if (!config.featureToggles?.vizActions) { - return []; - } - const actions: Array> = []; const actionLookup = new Set(); diff --git a/public/app/plugins/panel/table/module.tsx b/public/app/plugins/panel/table/module.tsx index f930e45de19..0d6466426da 100644 --- a/public/app/plugins/panel/table/module.tsx +++ b/public/app/plugins/panel/table/module.tsx @@ -6,6 +6,7 @@ import { ReducerID, standardEditorsRegistry, identityOverrideProcessor, + FieldConfigProperty, } from '@grafana/data'; import { TableCellOptions, TableCellDisplayMode, defaultTableFieldOptions, TableCellHeight } from '@grafana/schema'; @@ -23,6 +24,11 @@ export const plugin = new PanelPlugin(TablePanel) .setPanelChangeHandler(tablePanelChangedHandler) .setMigrationHandler(tableMigrationHandler) .useFieldConfig({ + standardOptions: { + [FieldConfigProperty.Actions]: { + hideFromDefaults: false, + }, + }, useCustomConfig: (builder) => { builder .addNumberInput({ diff --git a/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx b/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx index c83af4b2703..f8866b705f3 100644 --- a/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx +++ b/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx @@ -106,8 +106,8 @@ export const TimeSeriesPanel = ({ clientZoom={true} syncMode={cursorSync} syncScope={eventsScope} - getDataLinks={(seriesIdx: number, dataIdx: number) => - alignedFrame.fields[seriesIdx]!.getLinks?.({ valueRowIndex: dataIdx }) ?? [] + getDataLinks={(seriesIdx, dataIdx) => + alignedFrame.fields[seriesIdx].getLinks?.({ valueRowIndex: dataIdx }) ?? [] } render={(u, dataIdxs, seriesIdx, isPinned = false, dismiss, timeRange2, viaSync, dataLinks) => { if (enableAnnotationCreation && timeRange2 != null) { diff --git a/public/app/plugins/panel/timeseries/TimeSeriesTooltip.tsx b/public/app/plugins/panel/timeseries/TimeSeriesTooltip.tsx index e96f391a200..4471ed2e84c 100644 --- a/public/app/plugins/panel/timeseries/TimeSeriesTooltip.tsx +++ b/public/app/plugins/panel/timeseries/TimeSeriesTooltip.tsx @@ -51,7 +51,7 @@ export const TimeSeriesTooltip = ({ isPinned, annotate, maxHeight, - replaceVariables, + replaceVariables = (str) => str, dataLinks, hideZeros, }: TimeSeriesTooltipProps) => { @@ -80,12 +80,16 @@ export const TimeSeriesTooltip = ({ let footer: ReactNode; - if (isPinned && seriesIdx != null) { + if (seriesIdx != null) { const field = series.fields[seriesIdx]; - const dataIdx = dataIdxs[seriesIdx]!; - const actions = getFieldActions(series, field, replaceVariables!, dataIdx); + const hasOneClickLink = dataLinks.some((dataLink) => dataLink.oneClick === true); - footer = ; + if (isPinned || hasOneClickLink) { + const dataIdx = dataIdxs[seriesIdx]!; + const actions = getFieldActions(series, field, replaceVariables, dataIdx); + + footer = ; + } } const headerItem: VizTooltipItem | null = xField.config.custom?.hideFrom?.tooltip diff --git a/public/app/plugins/panel/timeseries/config.ts b/public/app/plugins/panel/timeseries/config.ts index f749b1f6e6c..39e6a283d01 100644 --- a/public/app/plugins/panel/timeseries/config.ts +++ b/public/app/plugins/panel/timeseries/config.ts @@ -59,6 +59,14 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig, isTime = true): SetFi mode: FieldColorModeId.PaletteClassic, }, }, + [FieldConfigProperty.Links]: { + settings: { + showOneClick: true, + }, + }, + [FieldConfigProperty.Actions]: { + hideFromDefaults: false, + }, }, useCustomConfig: (builder) => { builder diff --git a/public/app/plugins/panel/timeseries/plugins/AnnotationsPlugin2.tsx b/public/app/plugins/panel/timeseries/plugins/AnnotationsPlugin2.tsx index 6e915bd3671..f01152e2f88 100644 --- a/public/app/plugins/panel/timeseries/plugins/AnnotationsPlugin2.tsx +++ b/public/app/plugins/panel/timeseries/plugins/AnnotationsPlugin2.tsx @@ -176,19 +176,16 @@ export const AnnotationsPlugin2 = ({ let color = getColorByName(vals.color?.[i] || DEFAULT_ANNOTATION_COLOR_HEX8); let x0 = u.valToPos(vals.time[i], 'x', true); + renderLine(ctx, y0, y1, x0, color); - if (!vals.isRegion?.[i]) { - renderLine(ctx, y0, y1, x0, color); - // renderUpTriangle(ctx, x0, y1, 8 * uPlot.pxRatio, 5 * uPlot.pxRatio, color); - } else if (canvasRegionRendering) { - renderLine(ctx, y0, y1, x0, color); - + if (vals.isRegion?.[i]) { let x1 = u.valToPos(vals.timeEnd[i], 'x', true); - renderLine(ctx, y0, y1, x1, color); - ctx.fillStyle = colorManipulator.alpha(color, 0.1); - ctx.fillRect(x0, y0, x1 - x0, u.bbox.height); + if (canvasRegionRendering) { + ctx.fillStyle = colorManipulator.alpha(color, 0.1); + ctx.fillRect(x0, y0, x1 - x0, u.bbox.height); + } } } } diff --git a/public/app/plugins/panel/timeseries/plugins/ExemplarMarker.tsx b/public/app/plugins/panel/timeseries/plugins/ExemplarMarker.tsx index fec87618709..54e79e06743 100644 --- a/public/app/plugins/panel/timeseries/plugins/ExemplarMarker.tsx +++ b/public/app/plugins/panel/timeseries/plugins/ExemplarMarker.tsx @@ -285,6 +285,7 @@ const getExemplarMarkerStyles = (theme: GrafanaTheme2) => { padding: 0, overflowY: 'auto', maxHeight: '95vh', + boxShadow: theme.shadows.z2, }), header: css({ background: headerBg, diff --git a/public/app/plugins/panel/timeseries/plugins/annotations2/AnnotationEditor2.tsx b/public/app/plugins/panel/timeseries/plugins/annotations2/AnnotationEditor2.tsx index dde832c1fe1..b5b9b960e27 100644 --- a/public/app/plugins/panel/timeseries/plugins/annotations2/AnnotationEditor2.tsx +++ b/public/app/plugins/panel/timeseries/plugins/annotations2/AnnotationEditor2.tsx @@ -129,8 +129,7 @@ export const AnnotationEditor2 = ({ annoVals, annoIdx, dismiss, timeZone, ...oth const getStyles = (theme: GrafanaTheme2) => { return { editor: css({ - // zIndex: theme.zIndex.tooltip, - background: theme.colors.background.primary, + background: theme.colors.background.elevated, border: `1px solid ${theme.colors.border.weak}`, borderRadius: theme.shape.radius.default, boxShadow: theme.shadows.z3, diff --git a/public/app/plugins/panel/timeseries/plugins/annotations2/AnnotationTooltip2.tsx b/public/app/plugins/panel/timeseries/plugins/annotations2/AnnotationTooltip2.tsx index 7e14fddb50b..a504e098f4b 100644 --- a/public/app/plugins/panel/timeseries/plugins/annotations2/AnnotationTooltip2.tsx +++ b/public/app/plugins/panel/timeseries/plugins/annotations2/AnnotationTooltip2.tsx @@ -106,9 +106,9 @@ const getStyles = (theme: GrafanaTheme2) => ({ zIndex: theme.zIndex.tooltip, whiteSpace: 'initial', borderRadius: theme.shape.radius.default, - background: theme.colors.background.primary, + background: theme.colors.background.elevated, border: `1px solid ${theme.colors.border.weak}`, - boxShadow: theme.shadows.z2, + boxShadow: theme.shadows.z3, userSelect: 'text', }), header: css({ diff --git a/public/app/plugins/panel/trend/TrendPanel.tsx b/public/app/plugins/panel/trend/TrendPanel.tsx index 67dcf81bb4e..1fe5ced2a49 100644 --- a/public/app/plugins/panel/trend/TrendPanel.tsx +++ b/public/app/plugins/panel/trend/TrendPanel.tsx @@ -119,8 +119,8 @@ export const TrendPanel = ({ hoverMode={ options.tooltip.mode === TooltipDisplayMode.Single ? TooltipHoverMode.xOne : TooltipHoverMode.xAll } - getDataLinks={(seriesIdx: number, dataIdx: number) => - alignedDataFrame.fields[seriesIdx]!.getLinks?.({ valueRowIndex: dataIdx }) ?? [] + getDataLinks={(seriesIdx, dataIdx) => + alignedDataFrame.fields[seriesIdx].getLinks?.({ valueRowIndex: dataIdx }) ?? [] } render={(u, dataIdxs, seriesIdx, isPinned = false, dismiss, timeRange, viaSync, dataLinks) => { return ( diff --git a/public/app/plugins/panel/xychart/XYChartPanel.tsx b/public/app/plugins/panel/xychart/XYChartPanel.tsx index f22fd8248e1..25ef452dc6b 100644 --- a/public/app/plugins/panel/xychart/XYChartPanel.tsx +++ b/public/app/plugins/panel/xychart/XYChartPanel.tsx @@ -17,6 +17,8 @@ import { import { TooltipHoverMode } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin2'; import { getDisplayValuesForCalcs } from '@grafana/ui/src/components/uPlot/utils'; +import { getDataLinks } from '../status-history/utils'; + import { XYChartTooltip } from './XYChartTooltip'; import { Options } from './panelcfg.gen'; import { prepConfig } from './scatter'; @@ -113,7 +115,11 @@ export const XYChartPanel2 = (props: Props2) => { { + getDataLinks={(seriesIdx, dataIdx) => { + const xySeries = series[seriesIdx - 1]; + return getDataLinks(xySeries.y.field, dataIdx); + }} + render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2, viaSync, dataLinks) => { return ( { isPinned={isPinned} seriesIdx={seriesIdx!} replaceVariables={props.replaceVariables} + dataLinks={dataLinks} /> ); }} diff --git a/public/app/plugins/panel/xychart/XYChartTooltip.tsx b/public/app/plugins/panel/xychart/XYChartTooltip.tsx index abd3cb32bc2..08bd3903bad 100644 --- a/public/app/plugins/panel/xychart/XYChartTooltip.tsx +++ b/public/app/plugins/panel/xychart/XYChartTooltip.tsx @@ -1,6 +1,6 @@ import { ReactNode } from 'react'; -import { DataFrame, InterpolateFunction } from '@grafana/data'; +import { DataFrame, InterpolateFunction, LinkModel } from '@grafana/data'; import { alpha } from '@grafana/data/src/themes/colorManipulator'; import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent'; import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter'; @@ -8,7 +8,7 @@ import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizToolt import { VizTooltipWrapper } from '@grafana/ui/src/components/VizTooltip/VizTooltipWrapper'; import { ColorIndicator, VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types'; -import { getDataLinks, getFieldActions } from '../status-history/utils'; +import { getFieldActions } from '../status-history/utils'; import { XYSeries } from './types2'; import { fmt } from './utils'; @@ -21,6 +21,7 @@ export interface Props { data: DataFrame[]; xySeries: XYSeries[]; replaceVariables: InterpolateFunction; + dataLinks: LinkModel[]; } function stripSeriesName(fieldName: string, seriesName: string) { @@ -31,7 +32,16 @@ function stripSeriesName(fieldName: string, seriesName: string) { return fieldName; } -export const XYChartTooltip = ({ dataIdxs, seriesIdx, data, xySeries, dismiss, isPinned, replaceVariables }: Props) => { +export const XYChartTooltip = ({ + dataIdxs, + seriesIdx, + data, + xySeries, + dismiss, + isPinned, + replaceVariables, + dataLinks, +}: Props) => { const rowIndex = dataIdxs.find((idx) => idx !== null)!; const series = xySeries[seriesIdx! - 1]; @@ -93,12 +103,15 @@ export const XYChartTooltip = ({ dataIdxs, seriesIdx, data, xySeries, dismiss, i let footer: ReactNode; - if (isPinned && seriesIdx != null) { - const links = getDataLinks(yField, rowIndex); - const yFieldFrame = data.find((frame) => frame.fields.includes(yField))!; - const actions = getFieldActions(yFieldFrame, yField, replaceVariables, rowIndex); + if (seriesIdx != null) { + const hasOneClickLink = dataLinks?.some((dataLink) => dataLink.oneClick === true); - footer = ; + if (isPinned || hasOneClickLink) { + const yFieldFrame = data.find((frame) => frame.fields.includes(yField))!; + const actions = getFieldActions(yFieldFrame, yField, replaceVariables, rowIndex); + + footer = ; + } } return ( diff --git a/public/app/plugins/panel/xychart/config.ts b/public/app/plugins/panel/xychart/config.ts index c3e958c8c12..bff3ffb8774 100644 --- a/public/app/plugins/panel/xychart/config.ts +++ b/public/app/plugins/panel/xychart/config.ts @@ -35,7 +35,6 @@ export function getScatterFieldConfig(cfg: FieldConfig): SetFieldConfigOptionsAr [FieldConfigProperty.DisplayName]: { hideFromDefaults: true, }, - // TODO: this still leaves Color series by: [ Last | Min | Max ] // because item.settings?.bySeriesSupport && colorMode.isByValue [FieldConfigProperty.Color]: { @@ -48,6 +47,14 @@ export function getScatterFieldConfig(cfg: FieldConfig): SetFieldConfigOptionsAr mode: FieldColorModeId.PaletteClassic, }, }, + [FieldConfigProperty.Links]: { + settings: { + showOneClick: true, + }, + }, + [FieldConfigProperty.Actions]: { + hideFromDefaults: false, + }, }, useCustomConfig: (builder) => { diff --git a/public/app/routes/RoutesWrapper.tsx b/public/app/routes/RoutesWrapper.tsx index 1dc751a2ade..8a897553c7c 100644 --- a/public/app/routes/RoutesWrapper.tsx +++ b/public/app/routes/RoutesWrapper.tsx @@ -1,5 +1,5 @@ import { css } from '@emotion/css'; -import { ComponentType } from 'react'; +import { ComponentType, ReactNode } from 'react'; // eslint-disable-next-line no-restricted-imports import { Router } from 'react-router-dom'; import { CompatRouter } from 'react-router-dom-v5-compat'; @@ -19,10 +19,17 @@ import { AppNotificationList } from '../core/components/AppNotifications/AppNoti import { ModalsContextProvider } from '../core/context/ModalsContextProvider'; import { QueriesDrawerContextProvider } from '../features/explore/QueriesDrawer/QueriesDrawerContext'; +function ExtraProviders(props: { children: ReactNode; providers: Array> }) { + return props.providers.reduce((tree, Provider): ReactNode => { + return {tree}; + }, props.children); +} + type RouterWrapperProps = { routes?: JSX.Element | false; bodyRenderHooks: ComponentType[]; pageBanners: ComponentType[]; + providers: Array>; }; export function RouterWrapper(props: RouterWrapperProps) { return ( @@ -30,22 +37,24 @@ export function RouterWrapper(props: RouterWrapperProps) { - - - - - - {props.pageBanners.map((Banner, index) => ( - + + + + + + + {props.pageBanners.map((Banner, index) => ( + + ))} + {props.routes} + + {props.bodyRenderHooks.map((Hook, index) => ( + ))} - {props.routes} - - {props.bodyRenderHooks.map((Hook, index) => ( - - ))} - - - + + + + diff --git a/public/app/routes/routes.tsx b/public/app/routes/routes.tsx index 8a59242f820..35ecd371283 100644 --- a/public/app/routes/routes.tsx +++ b/public/app/routes/routes.tsx @@ -96,8 +96,10 @@ export function getAppRoutes(): RouteDescriptor[] { path: '/dashboard-solo/:type/:slug', routeName: DashboardRoutes.Normal, chromeless: true, - component: SafeDynamicImport( - () => import(/* webpackChunkName: "SoloPanelPage" */ '../features/dashboard/containers/SoloPanelPage') + component: SafeDynamicImport(() => + config.featureToggles.dashboardSceneSolo + ? import(/* webpackChunkName: "SoloPanelPage" */ '../features/dashboard-scene/solo/SoloPanelPage') + : import(/* webpackChunkName: "SoloPanelPageOld" */ '../features/dashboard/containers/SoloPanelPage') ), }, { @@ -160,6 +162,10 @@ export function getAppRoutes(): RouteDescriptor[] { : import(/* webpackChunkName: "explore-feature-toggle-page" */ 'app/features/explore/FeatureTogglePage') ), }, + { + path: '/drilldown', + component: () => , + }, { path: '/apps', component: () => , @@ -515,11 +521,20 @@ export function getAppRoutes(): RouteDescriptor[] { }, config.featureToggles.exploreMetrics && { path: '/explore/metrics/*', - chromeless: false, roles: () => contextSrv.evaluatePermission([AccessControlAction.DataSourcesExplore]), - component: SafeDynamicImport( - () => import(/* webpackChunkName: "DataTrailsPage"*/ 'app/features/trails/DataTrailsPage') - ), + ...(config.featureToggles.exploreMetricsUseExternalAppPlugin + ? { + component: SafeDynamicImport( + () => + import(/* webpackChunkName: "MetricsDrilldownRedirect"*/ 'app/features/trails/RedirectToDrilldownApp') + ), + } + : { + chromeless: false, + component: SafeDynamicImport( + () => import(/* webpackChunkName: "DataTrailsPage"*/ 'app/features/trails/DataTrailsPage') + ), + }), }, { path: '/bookmarks', diff --git a/public/app/store/configureStore.ts b/public/app/store/configureStore.ts index 6697ec42208..5757615ad61 100644 --- a/public/app/store/configureStore.ts +++ b/public/app/store/configureStore.ts @@ -11,7 +11,7 @@ import { StoreState } from 'app/types/store'; import { buildInitialState } from '../core/reducers/navModel'; import { addReducer, createRootReducer } from '../core/reducers/root'; import { alertingApi } from '../features/alerting/unified/api/alertingApi'; -import { queryLibraryApi } from '../features/query-library/api/factory'; +import { iamApi } from '../features/iam/api/api'; import { setStore } from './store'; @@ -39,8 +39,8 @@ export function configureStore(initialState?: Partial) { publicDashboardApi.middleware, browseDashboardsAPI.middleware, cloudMigrationAPI.middleware, - queryLibraryApi.middleware, userPreferencesAPI.middleware, + iamApi.middleware, ...extraMiddleware ), devTools: process.env.NODE_ENV !== 'production', diff --git a/public/app/types/acl.ts b/public/app/types/acl.ts index b939d67c726..684a3a158b1 100644 --- a/public/app/types/acl.ts +++ b/public/app/types/acl.ts @@ -7,6 +7,9 @@ export enum TeamPermissionLevel { export { OrgRole as OrgRole }; +export type PermissionLevel = 'view' | 'edit' | 'admin'; + +/** @deprecated Use PermissionLevel instead */ export enum PermissionLevelString { View = 'View', Edit = 'Edit', diff --git a/public/app/types/alerting.ts b/public/app/types/alerting.ts index ac598691607..3b32017319e 100644 --- a/public/app/types/alerting.ts +++ b/public/app/types/alerting.ts @@ -74,7 +74,8 @@ export type CloudNotifierType = | 'telegram' | 'sns' | 'discord' - | 'msteams'; + | 'msteams' + | 'jira'; export type NotifierType = GrafanaNotifierType | CloudNotifierType; export interface NotifierDTO { diff --git a/public/app/types/dashboard.ts b/public/app/types/dashboard.ts index d79e7a5a382..51478ac3829 100644 --- a/public/app/types/dashboard.ts +++ b/public/app/types/dashboard.ts @@ -3,8 +3,11 @@ import { Dashboard, DataSourceRef } from '@grafana/schema'; import { ObjectMeta } from 'app/features/apiserver/types'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; +export interface HomeDashboardRedirectDTO { + redirectUri: string; +} + export interface DashboardDTO { - redirectUri?: string; dashboard: DashboardDataDTO; meta: DashboardMeta; } @@ -66,7 +69,6 @@ export interface DashboardMeta { hasUnsavedFolderChange?: boolean; annotationsPermissions?: AnnotationsPermissions; publicDashboardEnabled?: boolean; - dashboardNotFound?: boolean; isEmbedded?: boolean; isNew?: boolean; version?: number; @@ -141,3 +143,7 @@ export interface DashboardState { } export const DASHBOARD_FROM_LS_KEY = 'DASHBOARD_FROM_LS_KEY'; + +export function isRedirectResponse(dto: DashboardDTO | HomeDashboardRedirectDTO): dto is HomeDashboardRedirectDTO { + return 'redirectUri' in dto; +} diff --git a/public/app/types/unified-alerting-dto.ts b/public/app/types/unified-alerting-dto.ts index 1cc55fe038e..85f06bf9722 100644 --- a/public/app/types/unified-alerting-dto.ts +++ b/public/app/types/unified-alerting-dto.ts @@ -242,6 +242,11 @@ export interface GrafanaEditorSettings { simplified_query_and_expressions_section: boolean; simplified_notifications_section: boolean; } + +export interface UpdatedBy { + uid: string; + name: string; +} export interface PostableGrafanaRuleDefinition { uid?: string; title: string; @@ -258,6 +263,7 @@ export interface PostableGrafanaRuleDefinition { metric: string; from: string; }; + intervalSeconds?: number; } export interface GrafanaRuleDefinition extends PostableGrafanaRuleDefinition { id?: string; @@ -265,6 +271,11 @@ export interface GrafanaRuleDefinition extends PostableGrafanaRuleDefinition { namespace_uid: string; rule_group: string; provenance?: string; + // TODO: For updated_by, updated, and version, fix types so these aren't optional, and + // are not conflated with test fixtures + updated?: string; + updated_by?: UpdatedBy | null; + version?: number; } export interface RulerGrafanaRuleDTO { @@ -274,6 +285,9 @@ export interface RulerGrafanaRuleDTO { labels: Labels; } +export type TopLevelGrafanaRuleDTOField = keyof Omit; +export type GrafanaAlertRuleDTOField = keyof GrafanaRuleDefinition; + export type PostableRuleGrafanaRuleDTO = RulerGrafanaRuleDTO; export type RulerCloudRuleDTO = RulerAlertingRuleDTO | RulerRecordingRuleDTO; diff --git a/public/app/types/unified-alerting.ts b/public/app/types/unified-alerting.ts index 0e956d2cacf..32ce7621f8f 100644 --- a/public/app/types/unified-alerting.ts +++ b/public/app/types/unified-alerting.ts @@ -209,7 +209,6 @@ export interface DataSourceNamespaceIdentifier { } export interface GrafanaRuleGroupIdentifier { - rulesSource: GrafanaRulesSourceIdentifier; groupName: string; namespace: GrafanaNamespaceIdentifier; groupOrigin: 'grafana'; @@ -310,6 +309,7 @@ export interface StateHistoryItem { export interface RulerDataSourceConfig { dataSourceName: string; + dataSourceUid: string; apiVersion: 'legacy' | 'config'; } diff --git a/public/dashboards/default.json b/public/dashboards/default.json index c59f98ec1dd..2b2ef5d1c9b 100644 --- a/public/dashboards/default.json +++ b/public/dashboards/default.json @@ -131,7 +131,6 @@ "collapse": false, "enable": true, "status": "Stable", - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"], "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], "now": true } diff --git a/public/dashboards/home.json b/public/dashboards/home.json index 718b6b52079..8d5cfd00e52 100644 --- a/public/dashboards/home.json +++ b/public/dashboards/home.json @@ -68,7 +68,6 @@ "timepicker": { "hidden": true, "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"], "type": "timepicker" }, "timezone": "browser", diff --git a/public/dashboards/scripted.js b/public/dashboards/scripted.js index daa4c4efb70..03de55bff44 100644 --- a/public/dashboards/scripted.js +++ b/public/dashboards/scripted.js @@ -13,16 +13,11 @@ 'use strict'; -// accessible variables in this scope -let window, document, $, jQuery, moment, kbn; +// accessible variables in this scope: window, document, $, jQuery, moment, kbn; // Setup some variables let dashboard; -// All url parameters are available via the ARGS object -// eslint-disable-next-line no-redeclare -let ARGS; - // Initialize a skeleton with nothing but a rows array and service object dashboard = { rows: [], @@ -56,6 +51,7 @@ for (let i = 0; i < rows; i++) { height: '300px', panels: [ { + id: 1, title: 'Events', type: 'graph', span: 12, @@ -63,23 +59,17 @@ for (let i = 0; i < rows; i++) { linewidth: 2, targets: [ { - target: "randomWalk('" + seriesName + "')", + scenarioId: 'random_walk', + refId: 'A', + seriesCount: 1, + alias: seriesName, }, { - target: "randomWalk('random walk2')", + scenarioId: 'random_walk', + refId: 'B', + seriesCount: 1, }, ], - seriesOverrides: [ - { - alias: '/random/', - yaxis: 2, - fill: 0, - linewidth: 5, - }, - ], - tooltip: { - shared: true, - }, }, ], }); diff --git a/public/dashboards/scripted_async.js b/public/dashboards/scripted_async.js index 98fb144d243..d13a80eb034 100644 --- a/public/dashboards/scripted_async.js +++ b/public/dashboards/scripted_async.js @@ -17,7 +17,7 @@ 'use strict'; // accessible variables in this scope -let window, document, ARGS, $, jQuery, moment, kbn; +// let window, document, ARGS, $, jQuery, moment, kbn; return function (callback) { // Setup some variables @@ -60,6 +60,7 @@ return function (callback) { height: '300px', panels: [ { + id: 1, title: 'Async dashboard test', type: 'text', span: 12, diff --git a/public/dashboards/scripted_templated.js b/public/dashboards/scripted_templated.js index df8647ff86e..0b09fa50986 100644 --- a/public/dashboards/scripted_templated.js +++ b/public/dashboards/scripted_templated.js @@ -14,14 +14,14 @@ 'use strict'; // accessible variables in this scope -let window, document, $, jQuery, moment, kbn; +// let window, document, $, jQuery, moment, kbn; // Setup some variables let dashboard; // All url parameters are available via the ARGS object // eslint-disable-next-line no-redeclare -let ARGS; +// let ARGS; // Initialize a skeleton with nothing but a rows array and service object dashboard = { @@ -44,19 +44,22 @@ dashboard.templating = { list: [ { name: 'test', - query: 'apps.backend.*', - refresh: 1, - type: 'query', - datasource: null, hide: 2, + includeAll: false, + multi: false, + query: 'a,b,c\n', + skipUrlSync: false, + type: 'custom', }, { - name: 'test2', - query: '*', - refresh: 1, - type: 'query', - datasource: null, - hide: 2, + name: 'seriesName', + label: 'Series name', + hide: 0, + includeAll: false, + multi: false, + query: 'series1,series2,series3\n', + skipUrlSync: false, + type: 'custom', }, ], }; @@ -78,6 +81,7 @@ for (let i = 0; i < rows; i++) { height: '300px', panels: [ { + id: 1, title: 'Events', type: 'graph', span: 12, @@ -85,10 +89,16 @@ for (let i = 0; i < rows; i++) { linewidth: 2, targets: [ { - target: "randomWalk('" + seriesName + "')", + scenarioId: 'random_walk', + refId: 'A', + seriesCount: 1, + alias: seriesName, }, { - target: "randomWalk('[[test2]]')", + scenarioId: 'random_walk', + refId: 'B', + seriesCount: 1, + alias: '${seriesName}', }, ], }, diff --git a/public/dashboards/template_vars.json b/public/dashboards/template_vars.json index 33478bc8081..04cb1c95d79 100644 --- a/public/dashboards/template_vars.json +++ b/public/dashboards/template_vars.json @@ -169,7 +169,6 @@ "notice": false, "enable": true, "status": "Stable", - "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"], "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], "now": true } diff --git a/public/img/alerting/irm_logo.svg b/public/img/alerting/irm_logo.svg new file mode 100644 index 00000000000..aa300c81f17 --- /dev/null +++ b/public/img/alerting/irm_logo.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/img/icons/unicons/drilldown.svg b/public/img/icons/unicons/drilldown.svg new file mode 100644 index 00000000000..27f627ce19f --- /dev/null +++ b/public/img/icons/unicons/drilldown.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json index 46fe1314938..1222cec2022 100644 --- a/public/locales/de-DE/grafana.json +++ b/public/locales/de-DE/grafana.json @@ -34,12 +34,6 @@ "save-button": "" } }, - "actions-editor": { - "inline": { - "add-button": "", - "one-click-link": "" - } - }, "admin": { "anon-users": { "not-found": "" @@ -174,6 +168,24 @@ } }, "alerting": { + "alert": { + "alert-state": "", + "annotations": "", + "evaluation": "", + "evaluation-paused": "", + "evaluation-paused-description": "", + "last-evaluated": "", + "last-evaluation-duration": "", + "last-updated-at": "", + "last-updated-by": "", + "no-annotations": "", + "pending-period": "", + "rule": "", + "rule-identifier": "", + "rule-type": "", + "state-error-timeout": "", + "state-no-data": "" + }, "alert-recording-rule-form": { "evaluation-behaviour": { "description": { @@ -193,6 +205,41 @@ "recording": "" } }, + "alertVersionHistory": { + "alerting": "", + "alerting-change-description": "", + "annotations": "", + "compareVersions": "", + "comparing-versions": "", + "condition": "", + "contactPointRouting": "", + "description": "", + "errorloading": "", + "execErrorState": "", + "intervalSeconds": "", + "labels": "", + "latest": "", + "name": "", + "namespace_uid": "", + "noDataState": "", + "noVersionsFound": "", + "paused": "", + "pendingPeriod": "", + "provisioning": "", + "provisioning-change-description": "", + "queryAndAlertCondition": "", + "reset": "", + "restore": "", + "restore-modal": { + "body": "", + "summary": "", + "title": "" + }, + "rule_group": "", + "unknown": "", + "unknown-change-description": "", + "user-id": "" + }, "annotations": { "description": "", "title": "" @@ -271,6 +318,9 @@ "no-delivery-attempts": "", "no-integrations": "", "only-firing": "", + "receiver-summary": { + "jira": "" + }, "telegram": { "parse-mode-warning-body": "", "parse-mode-warning-title": "" @@ -283,6 +333,11 @@ "contactPointFilter": { "label": "" }, + "copy-to-clipboard": "", + "dag": { + "missing-reference": "", + "self-reference": "" + }, "export": { "subtitle": { "formats": "", @@ -299,6 +354,13 @@ } } }, + "group-actions": { + "actions-trigger": "", + "delete": "", + "edit": "", + "export": "", + "reorder": "" + }, "list-view": { "empty": { "new-alert-rule": "", @@ -466,6 +528,19 @@ }, "pause": { "label": "" + }, + "threshold": { + "recovery": { + "stop-alerting-above": "", + "stop-alerting-bellow": "", + "stop-alerting-equal": "", + "stop-alerting-inside-range": "", + "stop-alerting-less": "", + "stop-alerting-more": "", + "stop-alerting-not-equal": "", + "stop-alerting-outside-range": "", + "title": "" + } } }, "rule-groups": { @@ -484,11 +559,23 @@ }, "rule-list": { "configure-datasource": "", + "ds-error-boundary": { + "description": "", + "title": "" + }, "filter-view": { "no-more-results": "", "no-rules-found": "" }, - "new-alert-rule": "" + "new-alert-rule": "", + "pagination": { + "next-page": "", + "previous-page": "" + }, + "return-button": { + "title": "" + }, + "rulerrule-loading-error": "" }, "rule-state": { "creating": "", @@ -496,6 +583,14 @@ "paused": "", "recording-rule": "" }, + "rule-view": { + "query": { + "datasources-na": { + "description": "", + "title": "" + } + } + }, "rule-viewer": { "prometheus-consistency-check": { "alert-message": "", @@ -690,7 +785,10 @@ "title": "" }, "custom-value": { - "label": "" + "description": "" + }, + "group": { + "undefined": "" }, "options": { "no-found": "" @@ -725,6 +823,7 @@ "collapse": "", "edit": "", "help": "", + "loading": "", "locale": { "default": "Standard" }, @@ -748,6 +847,25 @@ "placeholder": "Alle durchsuchen" } }, + "core": { + "versionHistory": { + "comparison": { + "header": { + "hide-json-diff": "", + "show-json-diff": "", + "text": "" + }, + "select": "" + }, + "no-properties-changed": "", + "table": { + "updated": "", + "updatedBy": "", + "version": "" + }, + "view-json-diff": "" + } + }, "correlations": { "add-new": "Neu", "alert": { @@ -863,6 +981,87 @@ "redirect-link": "Liste in Grafana Alerting", "subtitle": "Benachrichtigungsregeln im Zusammenhang mit diesem Dashboard" }, + "default-layout": { + "description": "", + "item-options": { + "repeat": { + "direction": { + "horizontal": "", + "title": "", + "vertical": "" + }, + "max": "", + "title": "", + "variable": { + "description": "", + "title": "" + } + } + }, + "name": "", + "row-actions": { + "delete": "", + "modal": { + "alt-action": "", + "text": "", + "title": "" + } + }, + "row-options": { + "button": { + "label": "" + }, + "form": { + "cancel": "", + "repeat-for": { + "label": "", + "learn-more": "", + "warning": { + "text": "" + } + }, + "title": "", + "update": "" + }, + "modal": { + "title": "" + } + } + }, + "edit-pane": { + "objects": { + "multi-select": { + "selection-number": "" + } + }, + "open": "", + "panels": { + "multi-select": { + "selection-number": "" + } + }, + "row": { + "header": { + "hide": "", + "title": "" + }, + "multi-select": { + "options-header": "", + "selection-number": "" + } + }, + "tab": { + "multi-select": { + "options-header": "", + "selection-number": "" + } + } + }, + "editpane": { + "add": "", + "configure": "", + "outline": "" + }, "empty": { "add-library-panel-body": "Visualisierungen hinzufügen, die mit anderen Dashboards geteilt werden.", "add-library-panel-button": "Bibliotheksfenster hinzufügen", @@ -874,6 +1073,9 @@ "import-a-dashboard-header": "Dashboard importieren", "import-dashboard-button": "Dashboard importieren" }, + "errors": { + "failed-to-load": "" + }, "inspect": { "data-tab": "Daten", "error-tab": "Fehler", @@ -926,19 +1128,134 @@ "rows": "Gesamtanzahl an Zeilen", "table-title": "Statistiken" }, + "options": { + "description": "", + "title": "", + "title-option": "" + }, + "panel-edit": { + "alerting-tab": { + "dashboard-not-saved": "", + "no-rules": "" + } + }, + "responsive-layout": { + "description": "", + "item-options": { + "hide-no-data": "", + "title": "" + }, + "name": "", + "options": { + "columns": "", + "fixed": "", + "min": "", + "one-column": "", + "rows": "", + "three-columns": "", + "two-columns": "" + } + }, + "rows-layout": { + "description": "", + "name": "", + "row": { + "collapse": "", + "expand": "", + "new": "", + "repeat": { + "learn-more": "", + "warning": "" + } + }, + "row-options": { + "height": { + "expand": "", + "hide-row-header": "", + "min": "", + "title": "" + }, + "repeat": { + "title": "", + "variable": { + "title": "" + } + }, + "title": "", + "title-option": "" + } + }, + "tabs-layout": { + "description": "", + "name": "", + "tab": { + "new": "" + }, + "tab-options": { + "title": "", + "title-option": "" + } + }, "toolbar": { "add": "Hinzufügen", + "add-panel": "", + "add-panel-description": "", + "add-panel-lib": "", + "add-row": "", + "add-tab": "", "alert-rules": "Warnregeln", + "back-to-dashboard": "", + "dashboard-settings": { + "label": "", + "tooltip": "" + }, + "discard-library-panel-changes": "", + "discard-panel": "", + "discard-panel-new": "", + "edit": { + "label": "", + "tooltip": "" + }, + "edit-dashboard-v2-schema": "", + "enter-edit-mode": { + "label": "", + "tooltip": "" + }, + "exit-edit-mode": { + "label": "", + "tooltip": "" + }, + "libray-panel-description": "", "mark-favorite": "Als Favorit markieren", + "more-save-options": "", "open-original": "Original-Dashboard öffnen", "playlist-next": "Zum nächsten Dashboard", "playlist-previous": "Zum vorherigen Dashboard", "playlist-stop": "Wiedergabeliste stoppen", + "public-dashboard": "", "refresh": "Dashboard aktualisieren", + "row-description": "", "save": "Dashboard speichern", + "save-dashboard": { + "label": "", + "tooltip": "" + }, + "save-dashboard-copy": { + "label": "", + "tooltip": "" + }, + "save-dashboard-short": "", + "save-library-panel": "", "settings": "Dashboard-Einstellungen", - "share": "Dashboard oder Panel teilen", + "share": { + "label": "", + "tooltip": "" + }, "share-button": "Teilen", + "show-hidden-elements": "", + "switch-old-dashboard": "", + "tabs-description": "", + "unlink-library-panel": "", "unmark-favorite": "Markierung als Favorit entfernen" }, "validation": { @@ -946,6 +1263,14 @@ "invalid-json": "JSON ungültig", "tags-expected-array": "Array der jeweiligen Tags", "tags-expected-strings": "String-Array der jeweiligen Tags" + }, + "viz-panel": { + "options": { + "description": "", + "title": "", + "title-option": "", + "transparent-background": "" + } } }, "dashboard-import": { @@ -1035,6 +1360,12 @@ "time-zone-label": "Zeitzone", "week-start-label": "Wochenbeginn" }, + "time-regions": { + "advanced-description-cron": "", + "advanced-description-rest": "", + "advanced-description-use": "", + "advanced-label": "" + }, "variables": { "title": "Variable" }, @@ -1048,6 +1379,9 @@ "angular-deprecation-description": "", "angular-deprecation-heading": "" }, + "panel-queries": { + "add-query-from-library": "" + }, "settings": { "variables": { "dependencies": { @@ -1114,6 +1448,11 @@ }, "explore": { "add-to-dashboard": "Zum Dashboard hinzufügen", + "drilldownInfo": { + "action": "", + "description": "", + "title": "" + }, "logs": { "logs-volume": { "add-filters": "", @@ -1125,36 +1464,6 @@ "scan-for-older-logs": "", "stop-scan": "" }, - "query-library": { - "add-edit-description": "", - "cancel": "", - "default-description": "", - "delete-query": "", - "delete-query-text": "", - "delete-query-title": "", - "private": "", - "public": "", - "query-deleted": "", - "query-template-add-error": "", - "query-template-added": "", - "query-template-edit-error": "", - "query-template-edited": "", - "save": "" - }, - "query-template-modal": { - "add-info": "", - "add-title": "", - "auto-star": "", - "data-source-name": "", - "description": "", - "edit-info": "", - "edit-title": "", - "query": "", - "visibility": "" - }, - "query-template-modall": { - "data-source-type": "" - }, "rich-history": { "close-tooltip": "Abfrageverlauf schließen", "datasource-a-z": "Datenquelle A-Z", @@ -1262,6 +1571,7 @@ "switch-datasource-button": "" }, "secondary-actions": { + "add-from-query-library": "", "query-add-button": "Abfrage hinzufügen", "query-add-button-aria-label": "Abfrage hinzufügen", "query-history-button": "Abfrageverlauf", @@ -1305,10 +1615,9 @@ "sortBy": "" }, "related-logs": { - "docsLink": "", + "LrrDocsLink": "", "openExploreLogs": "", - "relatedLogsUnavailableAfterDocsLink": "", - "relatedLogsUnavailableBeforeDocsLink": "", + "relatedLogsUnavailable": "", "warnExperimentalFeature": "" }, "viewBy": "" @@ -1356,7 +1665,29 @@ "incomplete-request-error": "", "send-custom-feedback": "" }, + "get-enterprise": { + "requires-license": "", + "title": "" + }, "grafana-ui": { + "action-editor": { + "button": { + "confirm": "", + "confirm-action": "" + }, + "inline": { + "add-action": "", + "edit-action": "" + }, + "modal": { + "action-body": "", + "action-method": "", + "action-query-params": "", + "action-title": "", + "action-title-placeholder": "", + "one-click-description": "" + } + }, "auto-save-field": { "saved": "", "saving": "" @@ -1373,11 +1704,21 @@ }, "data-link-editor-modal": { "cancel": "", + "one-click-description": "", "save": "" }, + "data-link-inline-editor": { + "one-click": "" + }, "data-links-inline-editor": { "add-link": "", - "one-click-link": "" + "edit-link": "", + "one-click": "", + "one-click-enabled": "", + "title-not-provided": "", + "tooltip-edit": "", + "tooltip-remove": "", + "url-not-provided": "" }, "data-source-http-settings": { "access-help": "", @@ -1409,6 +1750,12 @@ "drawer": { "close": "Schließen" }, + "feature-badge": { + "experimental": "", + "new": "", + "preview": "", + "private-preview": "" + }, "field-link-list": { "external-links-heading": "" }, @@ -1458,7 +1805,12 @@ "right-axis-indicator": "" }, "viz-tooltip": { - "footer-add-annotation": "" + "actions-confirmation-input-placeholder": "", + "actions-confirmation-label": "", + "actions-confirmation-message": "", + "footer-add-annotation": "", + "footer-click-to-action": "", + "footer-click-to-navigate": "" } }, "graph": { @@ -1726,6 +2078,7 @@ "render-image-error-description": "" } }, + "lock-icon": "", "login": { "divider": { "connecting-text": "" @@ -1770,6 +2123,10 @@ }, "logs": { "infinite-scroll": { + "end-of-range": "", + "load-more": "", + "load-newer": "", + "load-older": "", "older-logs": "" }, "log-details": { @@ -1780,7 +2137,8 @@ }, "log-row-message": { "ellipsis": "", - "more": "" + "more": "", + "see-details": "" }, "log-rows": { "disable-popover": { @@ -1792,6 +2150,13 @@ "shortcut": "" } }, + "logs-navigation": { + "newer-logs": "", + "older-logs": "", + "scroll-bottom": "", + "scroll-top": "", + "start-of-range": "" + }, "popover-menu": { "copy": "", "disable-menu": "", @@ -1881,6 +2246,7 @@ "title": "" }, "migrated-counts": { + "alert_rule_groups": "", "alert_rules": "", "contact_points": "", "dashboards": "", @@ -1941,9 +2307,8 @@ "public-preview": { "button-text": "", "message": "", - "message-plugins": "", - "title": "", - "title-plugins": "" + "message-cloud": "", + "title": "" }, "resource-details": { "dismiss-button": "", @@ -1986,6 +2351,7 @@ }, "resource-type": { "alert_rule": "", + "alert_rule_group": "", "contact_point": "", "dashboard": "Dashboard", "datasource": "Datenquelle", @@ -2031,6 +2397,15 @@ "title": "Warum mit Grafana hosten?" } }, + "multicombobox": { + "all": { + "title": "", + "title-filtered": "" + }, + "clear": { + "title": "" + } + }, "nav": { "add-new-connections": { "title": "Neue Verbindung hinzufügen" @@ -2169,6 +2544,9 @@ "detect": { "title": "Erkennen" }, + "drilldown": { + "title": "" + }, "explore": { "title": "Entdecken" }, @@ -2206,6 +2584,8 @@ "history-wrapper": { "collapse": "", "expand": "", + "icon-selected": "", + "icon-unselected": "", "show-more": "", "today": "", "yesterday": "" @@ -2384,7 +2764,8 @@ "list-label": "Navigation", "open": "", "undock": "Menü abdocken" - } + }, + "rss-button": "" }, "news": { "drawer": { @@ -2491,6 +2872,7 @@ }, "plugins": { "catalog": { + "no-updates-available": "", "update-all": { "all-plugins-updated": "", "available-header": "", @@ -2516,24 +2898,46 @@ } }, "details": { + "connections-tab": { + "description": "" + }, "labels": { "contactGrafanaLabs": "", + "customLinks": "", + "customLinksTooltip": "", "dependencies": "", + "documentation": "", "downloads": "", "from": "", "installedVersion": "", "lastCommitDate": "", "latestVersion": "", - "links": "", + "license": "", + "raiseAnIssue": "", "reportAbuse": "", + "reportAbuseTooltip": "", + "repository": "", "signature": "", "status": "", "updatedAt": "" + }, + "modal": { + "cancel": "", + "copyEmail": "", + "description": "", + "node": "", + "title": "" } }, "empty-state": { "message": "" }, + "filter": { + "disabled": "", + "sort": "", + "sort-list": "", + "state": "" + }, "plugin-help": { "error": "", "not-found": "" @@ -2743,14 +3147,6 @@ "role-label": "Rolle" } }, - "query-library": { - "datasource-names": "", - "delete-query-button": "", - "query-template-get-error": "", - "search": "", - "user-info-get-error": "", - "user-names": "" - }, "query-operation": { "header": { "collapse-row": "Abfragezeile einklappen", @@ -2760,7 +3156,6 @@ "expand-row": "Suchzeile erweitern", "hide-response": "", "remove-query": "Abfrage entfernen", - "save-to-query-library": "", "show-response": "", "toggle-edit-mode": "Textbearbeitungsmodus umschalten" }, @@ -2862,6 +3257,10 @@ "title": "" }, "save-dashboards": { + "message-length": { + "info": "", + "title": "" + }, "name-exists": { "message-info": "", "message-suggestion": "", @@ -3145,6 +3544,7 @@ "home-dashboard-placeholder": "Standard-Dashboard", "locale-label": "Sprache", "locale-placeholder": "Sprache wählen", + "theme-description": "", "theme-label": "UI-Design", "week-start-label": "Wochenbeginn" }, @@ -3216,6 +3616,13 @@ "url-column-header": "Snapshot url", "view-button": "Anzeigen" }, + "table": { + "container": { + "content": "", + "show-all-series": "", + "show-only-series": "" + } + }, "tag-filter": { "clear-button": "", "loading": "Wird geladen ...", @@ -3309,20 +3716,26 @@ "start-your-metrics-exploration": "", "subtitle": "" }, - "metric-overview": { - "description-label": "", - "labels": "", - "metric-attributes": "", - "no-description": "", - "type-label": "", - "unit-label": "", - "unknown-type": "" - }, "metric-select": { "filter-by": "", + "native-histogram": "", "new-badge": "", "otel-switch": "" }, + "native-histogram-banner": { + "ch-heatmap": "", + "ch-histogram": "", + "click-histogram": "", + "hide-examples": "", + "learn-more": "", + "metric-examples": "", + "nh-heatmap": "", + "nh-histogram": "", + "now": "", + "previously": "", + "see-examples": "", + "sentence": "" + }, "recent-metrics": { "or-view-a-recent-exploration": "" }, diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 7f6844d6779..2a6465e6357 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -34,12 +34,6 @@ "save-button": "Save" } }, - "actions-editor": { - "inline": { - "add-button": "Add action", - "one-click-link": "One-click link" - } - }, "admin": { "anon-users": { "not-found": "No anonymous users found." @@ -174,6 +168,24 @@ } }, "alerting": { + "alert": { + "alert-state": "Alert state", + "annotations": "Annotations", + "evaluation": "Evaluation", + "evaluation-paused": "Alert evaluation currently paused", + "evaluation-paused-description": "Notifications for this rule will not fire and no alert instances will be created until the rule is un-paused.", + "last-evaluated": "Last evaluated", + "last-evaluation-duration": "Last evaluation duration", + "last-updated-at": "Last updated at", + "last-updated-by": "Last updated by", + "no-annotations": "No annotations", + "pending-period": "Pending period", + "rule": "Rule", + "rule-identifier": "Rule identifier", + "rule-type": "Rule type", + "state-error-timeout": "Alert state if execution error or timeout", + "state-no-data": "Alert state if no data or all values are null" + }, "alert-recording-rule-form": { "evaluation-behaviour": { "description": { @@ -193,6 +205,41 @@ "recording": "Add labels to your rule." } }, + "alertVersionHistory": { + "alerting": "Alerting", + "alerting-change-description": "This update was made by the alerting system due to other changes. For example, when renaming a contact point that is used for simplified routing, this will update affected rules", + "annotations": "Annotations", + "compareVersions": "Compare versions", + "comparing-versions": "Comparing versions", + "condition": "Alert condition", + "contactPointRouting": "Contact point routing", + "description": "Each time you edit the alert rule, a new version is created. Select two versions below and compare their differences.", + "errorloading": "Failed to load alert rule versions", + "execErrorState": "Alert state when execution error", + "intervalSeconds": "Evaluation interval", + "labels": "Labels", + "latest": "Latest", + "name": "Name", + "namespace_uid": "Folder UID", + "noDataState": "Alert state when no data", + "noVersionsFound": "No versions found for this rule", + "paused": "Paused state", + "pendingPeriod": "Pending period", + "provisioning": "Provisioning", + "provisioning-change-description": "Version update was made via provisioning", + "queryAndAlertCondition": "Query and alert condition", + "reset": "Reset to version {{version}}", + "restore": "Restore", + "restore-modal": { + "body": "Are you sure you want to restore the alert rule definition to this version? All unsaved changes will be lost.", + "summary": "Summary of changes to be applied:", + "title": "Restore Version" + }, + "rule_group": "Rule group", + "unknown": "Unknown", + "unknown-change-description": "This update was made prior to the implementation of alert rule version history. The user who made the change is not tracked, but future changes will include the user", + "user-id": "User ID {{uid}}" + }, "annotations": { "description": "Add more context to your alert notifications.", "title": "Configure notification message" @@ -248,6 +295,9 @@ "export-all": "Export all", "loading": "Loading...", "search-by-matchers": "Search by matchers", + "titles": { + "notification-templates": "Notification Templates" + }, "view": "View" }, "contact-points": { @@ -271,6 +321,9 @@ "no-delivery-attempts": "No delivery attempts", "no-integrations": "No integrations configured", "only-firing": "Delivering <1>only firing notifications", + "receiver-summary": { + "jira": "Creates a \"{{issueType}}\" issue in the \"{{project}}\" project" + }, "telegram": { "parse-mode-warning-body": "If you use a <1>parse_mode option other than <3>None, truncation may result in an invalid message, causing the notification to fail. For longer messages, we recommend using an alternative contact method.", "parse-mode-warning-title": "Telegram messages are limited to 4096 UTF-8 characters." @@ -283,6 +336,11 @@ "contactPointFilter": { "label": "Contact point" }, + "copy-to-clipboard": "Copy \"{{label}}\" to clipboard", + "dag": { + "missing-reference": "Expression \"{{source}}\" failed to run because \"{{target}}\" is missing or also failed.", + "self-reference": "You can't link an expression to itself" + }, "export": { "subtitle": { "formats": "Select the format and download the file or copy the contents to clipboard", @@ -349,6 +407,20 @@ "title": "Alert instance routing preview", "uninitialized": "When you have your folder selected and your query and labels are configured, click \"Preview routing\" to see the results here." }, + "notification-templates": { + "duplicate": { + "subTitle": "Duplicate a group of notification templates", + "title": "Duplicate notification template group" + }, + "edit": { + "subTitle": "Edit a group of notification templates", + "title": "Edit notification template group" + }, + "new": { + "subTitle": "Create a new group of notification templates", + "title": "New notification template group" + } + }, "policies": { "default-policy": { "description": "All alert instances will be handled by the default policy if no other matching policies are found.", @@ -473,6 +545,19 @@ }, "pause": { "label": "Pause evaluation" + }, + "threshold": { + "recovery": { + "stop-alerting-above": "Stop alerting when above", + "stop-alerting-bellow": "Stop alerting when below", + "stop-alerting-equal": "Stop alerting when equal to", + "stop-alerting-inside-range": "Stop alerting when inside range", + "stop-alerting-less": "Stop alerting when less than", + "stop-alerting-more": "Stop alerting when more than", + "stop-alerting-not-equal": "Stop alerting when not equal to", + "stop-alerting-outside-range": "Stop alerting when outside range", + "title": "Custom recovery threshold" + } } }, "rule-groups": { @@ -506,7 +591,8 @@ }, "return-button": { "title": "Alert rules" - } + }, + "rulerrule-loading-error": "Failed to load the rule" }, "rule-state": { "creating": "Creating", @@ -514,7 +600,16 @@ "paused": "Paused", "recording-rule": "Recording rule" }, + "rule-view": { + "query": { + "datasources-na": { + "description": "Cannot display the query preview. Some of the data sources used in the queries are not available.", + "title": "Query not available" + } + } + }, "rule-viewer": { + "error-loading": "Something went wrong loading the rule", "prometheus-consistency-check": { "alert-message": "Alert rule has been updated. Changes may take up to a minute to appear on the Alert rules list view.", "alert-title": "Update in progress" @@ -634,15 +729,15 @@ }, "counts": { "alertRule_one": "{{count}} alert rule", - "alertRule_other": "{{count}} alert rules", + "alertRule_other": "{{count}} alert rule", "dashboard_one": "{{count}} dashboard", - "dashboard_other": "{{count}} dashboards", + "dashboard_other": "{{count}} dashboard", "folder_one": "{{count}} folder", - "folder_other": "{{count}} folders", + "folder_other": "{{count}} folder", "libraryPanel_one": "{{count}} library panel", - "libraryPanel_other": "{{count}} library panels", + "libraryPanel_other": "{{count}} library panel", "total_one": "{{count}} item", - "total_other": "{{count}} items" + "total_other": "{{count}} item" }, "dashboards-tree": { "collapse-folder-button": "Collapse folder {{title}}", @@ -708,7 +803,10 @@ "title": "Clear value" }, "custom-value": { - "label": "Custom value: " + "description": "Use custom value" + }, + "group": { + "undefined": "No group" }, "options": { "no-found": "No options found." @@ -743,6 +841,7 @@ "collapse": "Collapse", "edit": "Edit", "help": "Help", + "loading": "Loading...", "locale": { "default": "Default" }, @@ -766,6 +865,25 @@ "placeholder": "Search all" } }, + "core": { + "versionHistory": { + "comparison": { + "header": { + "hide-json-diff": "Hide JSON diff ", + "show-json-diff": "Show JSON diff ", + "text": "Version {{version}} updated by {{createdBy}} ({{ageString}}) {{message}}" + }, + "select": "Select two versions to start comparing" + }, + "no-properties-changed": "No relevant properties changed", + "table": { + "updated": "Date", + "updatedBy": "Updated By", + "version": "Version" + }, + "view-json-diff": "View JSON diff to see all changes" + } + }, "correlations": { "add-new": "Add new", "alert": { @@ -881,6 +999,87 @@ "redirect-link": "List in Grafana Alerting", "subtitle": "Alert rules related to this dashboard" }, + "default-layout": { + "description": "The default grid layout", + "item-options": { + "repeat": { + "direction": { + "horizontal": "Horizontal", + "title": "Repeat direction", + "vertical": "Vertical" + }, + "max": "Max per row", + "title": "Repeat options", + "variable": { + "description": "Repeat this panel for each value in the selected variable. This is not visible while in edit mode. You need to go back to dashboard and then update the variable or reload the dashboard.", + "title": "Repeat by variable" + } + } + }, + "name": "Default grid", + "row-actions": { + "delete": "Delete row", + "modal": { + "alt-action": "Delete row only", + "text": "Are you sure you want to remove this row and all its panels?", + "title": "Delete row" + } + }, + "row-options": { + "button": { + "label": "Row options" + }, + "form": { + "cancel": "Cancel", + "repeat-for": { + "label": "Repeat for", + "learn-more": "Learn more", + "warning": { + "text": "Panels in this row use the {{SHARED_DASHBOARD_QUERY}} data source. These panels will reference the panel in the original row, not the ones in the repeated rows." + } + }, + "title": "Title", + "update": "Update" + }, + "modal": { + "title": "Row options" + } + } + }, + "edit-pane": { + "objects": { + "multi-select": { + "selection-number": "No. of objects selected: {{length}}" + } + }, + "open": "Open options pane", + "panels": { + "multi-select": { + "selection-number": "No. of panels selected: {{length}}" + } + }, + "row": { + "header": { + "hide": "Hide", + "title": "Row header" + }, + "multi-select": { + "options-header": "Multi-selected Row options", + "selection-number": "No. of rows selected: {{length}}" + } + }, + "tab": { + "multi-select": { + "options-header": "Multi-selected Tab options", + "selection-number": "No. of tabs selected: " + } + } + }, + "editpane": { + "add": "Add", + "configure": "Configure", + "outline": "Outline" + }, "empty": { "add-library-panel-body": "Add visualizations that are shared with other dashboards.", "add-library-panel-button": "Add library panel", @@ -892,6 +1091,9 @@ "import-a-dashboard-header": "Import a dashboard", "import-dashboard-button": "Import dashboard" }, + "errors": { + "failed-to-load": "Failed to load dashboard" + }, "inspect": { "data-tab": "Data", "error-tab": "Error", @@ -944,20 +1146,134 @@ "rows": "Total number rows", "table-title": "Stats" }, + "options": { + "description": "Description", + "title": "Dashboard options", + "title-option": "Title" + }, + "panel-edit": { + "alerting-tab": { + "dashboard-not-saved": "Dashboard must be saved before alerts can be added.", + "no-rules": "There are no alert rules linked to this panel." + } + }, + "responsive-layout": { + "description": "CSS layout that adjusts to the available space", + "item-options": { + "hide-no-data": "Hide when no data", + "title": "Layout options" + }, + "name": "Responsive grid", + "options": { + "columns": "Columns", + "fixed": "Fixed: {{size}}px", + "min": "Min: {{size}}px", + "one-column": "1 column", + "rows": "Rows", + "three-columns": "3 columns", + "two-columns": "2 columns" + } + }, + "rows-layout": { + "description": "Rows layout", + "name": "Rows", + "row": { + "collapse": "Collapse row", + "expand": "Expand row", + "new": "New row", + "repeat": { + "learn-more": "Learn more", + "warning": "Panels in this row use the {{SHARED_DASHBOARD_QUERY}} data source. These panels will reference the panel in the original row, not the ones in the repeated rows." + } + }, + "row-options": { + "height": { + "expand": "Expand", + "hide-row-header": "Hide row header", + "min": "Min", + "title": "Height" + }, + "repeat": { + "title": "Repeat options", + "variable": { + "title": "Variable" + } + }, + "title": "Row options", + "title-option": "Title" + } + }, + "tabs-layout": { + "description": "Tabs layout", + "name": "Tabs", + "tab": { + "new": "New tab" + }, + "tab-options": { + "title": "Tab options", + "title-option": "Title" + } + }, "toolbar": { "add": "Add", + "add-panel": "Panel", + "add-panel-description": "A container for visualizations and other content", + "add-panel-lib": "Import library panel", + "add-row": "Row", + "add-tab": "Tab", "alert-rules": "Alert rules", + "back-to-dashboard": "Back to dashboard", + "dashboard-settings": { + "label": "Settings", + "tooltip": "Dashboard settings" + }, + "discard-library-panel-changes": "Discard library panel changes", + "discard-panel": "Discard panel changes", + "discard-panel-new": "Discard panel", + "edit": { + "label": "Edit", + "tooltip": "Enter edit mode" + }, + "edit-dashboard-v2-schema": "Edit dashboard v2 schema", + "enter-edit-mode": { + "label": "Make editable", + "tooltip": "This dashboard was marked as read only" + }, + "exit-edit-mode": { + "label": "Exit edit", + "tooltip": "Exits edit mode and discards unsaved changes" + }, + "libray-panel-description": "Libray panels allow you share and reuse panels between dashboards", "mark-favorite": "Mark as favorite", + "more-save-options": "More save options", "open-original": "Open original dashboard", "playlist-next": "Go to next dashboard", "playlist-previous": "Go to previous dashboard", "playlist-stop": "Stop playlist", + "public-dashboard": "Public", "refresh": "Refresh dashboard", + "row-description": "A group of panels with an optional header", "save": "Save dashboard", + "save-dashboard": { + "label": "Save dashboard", + "tooltip": "Save changes" + }, + "save-dashboard-copy": { + "label": "Save as copy", + "tooltip": "Save as copy" + }, + "save-dashboard-short": "Save", + "save-library-panel": "Save library panel", "settings": "Dashboard settings", - "share": "Share dashboard", + "share": { + "label": "Share", + "tooltip": "Share dashboard" + }, "share-button": "Share", "show-hidden-elements": "Show hidden", + "switch-old-dashboard": "Switch to old dashboard page", + "tabs-description": "Break up your dashboard into different horizontal tabs", + "unlink-library-panel": "Unlink library panel", "unmark-favorite": "Unmark as favorite" }, "validation": { @@ -965,6 +1281,14 @@ "invalid-json": "Not valid JSON", "tags-expected-array": "tags expected array", "tags-expected-strings": "tags expected array of strings" + }, + "viz-panel": { + "options": { + "description": "Description", + "title": "Panel options", + "title-option": "Title", + "transparent-background": "Transparent background" + } } }, "dashboard-import": { @@ -1054,6 +1378,12 @@ "time-zone-label": "Time zone", "week-start-label": "Week start" }, + "time-regions": { + "advanced-description-cron": "Cron syntax", + "advanced-description-rest": " to define a recurrence schedule and duration", + "advanced-description-use": "Use ", + "advanced-label": "Advanced" + }, "variables": { "title": "Variables" }, @@ -1067,6 +1397,9 @@ "angular-deprecation-description": "Angular panels options can only be edited using the JSON editor.", "angular-deprecation-heading": "Panel options" }, + "panel-queries": { + "add-query-from-library": "Add query from library" + }, "settings": { "variables": { "dependencies": { @@ -1133,6 +1466,11 @@ }, "explore": { "add-to-dashboard": "Add to dashboard", + "drilldownInfo": { + "action": "Go to Grafana Drilldown", + "description": "Looking for the Grafana Explore apps? They are now called the Grafana Drilldown apps and can be found under <1>Menu > Drilldown", + "title": "Explore Metrics, Logs, Traces and Profiles have moved!" + }, "logs": { "logs-volume": { "add-filters": "Add more labels to your query to narrow down your search.", @@ -1144,36 +1482,6 @@ "scan-for-older-logs": "Scan for older logs", "stop-scan": "Stop scan" }, - "query-library": { - "add-edit-description": "Add/edit description", - "cancel": "Cancel", - "default-description": "Public", - "delete-query": "Delete query", - "delete-query-text": "You're about to remove this query from the query library. This action cannot be undone. Do you want to continue?", - "delete-query-title": "Delete query", - "private": "Private", - "public": "Public", - "query-deleted": "Query deleted", - "query-template-add-error": "Error attempting to save this query to the library", - "query-template-added": "Query successfully saved to the library", - "query-template-edit-error": "Error attempting to edit this query", - "query-template-edited": "Query template successfully edited", - "save": "Save" - }, - "query-template-modal": { - "add-info": "You're about to save this query. Once saved, you can easily access it in the Query Library tab for future use and reference.", - "add-title": "Add query to Query Library", - "auto-star": "Auto-star this query to add it to your starred list in the Query Library.", - "data-source-name": "Data source name", - "description": "Description", - "edit-info": "You're about to edit this query. Once saved, you can easily access it in the Query Library tab for future use and reference.", - "edit-title": "Edit query", - "query": "Query", - "visibility": "Visibility" - }, - "query-template-modall": { - "data-source-type": "Data source type" - }, "rich-history": { "close-tooltip": "Close query history", "datasource-a-z": "Data source A-Z", @@ -1281,6 +1589,7 @@ "switch-datasource-button": "Switch data source and run query" }, "secondary-actions": { + "add-from-query-library": "Add query from library", "query-add-button": "Add query", "query-add-button-aria-label": "Add query", "query-history-button": "Query history", @@ -1374,11 +1683,42 @@ "incomplete-request-error": "Sorry, I was unable to complete your request. Please try again.", "send-custom-feedback": "Send" }, + "get-enterprise": { + "requires-license": "Requires a Grafana Enterprise license", + "title": "Enterprise" + }, "grafana-ui": { + "action-editor": { + "button": { + "confirm": "Confirm", + "confirm-action": "Confirm action" + }, + "inline": { + "add-action": "Add action", + "edit-action": "Edit action" + }, + "modal": { + "action-body": "Body", + "action-method": "Method", + "action-query-params": "Query parameters", + "action-title": "Title", + "action-title-placeholder": "Action title", + "one-click-description": "Only one link or action can have one click enabled at a time" + } + }, + "alert": { + "close-button": "Close alert" + }, "auto-save-field": { "saved": "Saved!", "saving": "Saving <1>" }, + "card": { + "option": "option" + }, + "cascader": { + "clear-button": "Clear selection" + }, "color-picker-popover": { "palette-tab": "Colors", "spectrum-tab": "Custom" @@ -1386,47 +1726,115 @@ "confirm-button": { "cancel": "Cancel" }, + "confirm-content": { + "placeholder": "Type \"{{confirmPromptText}}\" to confirm" + }, "data-link-editor": { - "info": "With data links you can reference data variables like series name, labels and values. Type CMD+Space, CTRL+Space, or $ to open variable suggestions." + "info": "With data links you can reference data variables like series name, labels and values. Type CMD+Space, CTRL+Space, or $ to open variable suggestions.", + "new-tab-label": "Open in new tab", + "title-label": "Title", + "title-placeholder": "Show details", + "url-label": "URL" }, "data-link-editor-modal": { "cancel": "Cancel", + "one-click-description": "Only one link can have one click enabled at a time", "save": "Save" }, + "data-link-inline-editor": { + "one-click": "One click" + }, "data-links-inline-editor": { "add-link": "Add link", - "one-click-link": "One-click link" + "edit-link": "Edit link", + "one-click": "One click", + "one-click-enabled": "One click enabled", + "title-not-provided": "Title not provided", + "tooltip-edit": "Edit", + "tooltip-remove": "Remove", + "url-not-provided": "Data link url not provided" + }, + "data-source-basic-auth-settings": { + "user-label": "User", + "user-placeholder": "user" + }, + "data-source-http-proxy-settings": { + "oauth-identity-label": "Forward OAuth Identity", + "oauth-identity-tooltip": "Forward the user's upstream OAuth identity to the data source (Their access token gets passed along).", + "skip-tls-verify-label": "Skip TLS Verify", + "ts-client-auth-label": "TLS Client Auth", + "with-ca-cert-label": "With CA Cert", + "with-ca-cert-tooltip": "Needed for verifying self-signed TLS Certs" }, "data-source-http-settings": { "access-help": "Help <1>", "access-help-details": "Access mode controls how requests to the data source will be handled.<1> <1>Server should be the preferred way if nothing else is stated.", + "access-label": "Access", + "access-options-browser": "Browser", + "access-options-proxy": "Server (default)", "allowed-cookies": "Allowed cookies", + "allowed-cookies-tooltip": "Grafana proxy deletes forwarded cookies by default. Specify cookies by name that should be forwarded to the data source.", "auth": "Auth", + "azure-auth-label": "Azure Authentication", + "azure-auth-tooltip": "Use Azure authentication for Azure endpoint.", "basic-auth": "Basic Auth Details", + "basic-auth-label": "Basic auth", "browser-mode-description": "All requests will be made from the browser directly to the data source and may be subject to Cross-Origin Resource Sharing (CORS) requirements. The URL needs to be accessible from the browser if you select this access mode.", "browser-mode-title": "<0>Browser access mode:", + "default-url-access-select": "Access", "default-url-tooltip": "Specify a complete HTTP URL (for example http://your_server:8080)", "direct-url-tooltip": "Your access method is <1>Browser, this means the URL needs to be accessible from the browser.", "heading": "HTTP", "proxy-url-tooltip": "Your access method is <1>Server, this means the URL needs to be accessible from the grafana backend/server.", "server-mode-description": "All requests will be made from the browser to Grafana backend/server which in turn will forward the requests to the data source and by that circumvent possible Cross-Origin Resource Sharing (CORS) requirements. The URL needs to be accessible from the grafana backend/server if you select this access mode.", - "server-mode-title": "<0>Server access mode (Default):" + "server-mode-title": "<0>Server access mode (Default):", + "timeout-form-label": "Timeout", + "timeout-label": "Timeout in seconds", + "timeout-tooltip": "HTTP request timeout in seconds", + "url-label": "URL", + "with-credential-label": "With Credentials", + "with-credential-tooltip": "Whether credentials such as cookies or auth headers should be sent with cross-site requests." }, "data-source-settings": { "alerting-settings-heading": "Alerting", + "alerting-settings-label": "Manage alert rules in Alerting UI", + "alerting-settings-tooltip": "Manage alert rules for this data source. To manage other alerting resources, add an Alertmanager data source.", "cert-key-reset": "Reset", "custom-headers-add": "Add header", + "custom-headers-header": "Header", + "custom-headers-header-placeholder": "Header Value", + "custom-headers-header-remove": "Remove header", + "custom-headers-header-value": "Value", "custom-headers-title": "Custom HTTP Headers", "secure-socks-heading": "Secure Socks Proxy", - "tls-heading": "TLS/SSL Auth Details" + "secure-socks-label": "Enabled", + "secure-socks-tooltip": "Connect to this datasource via the secure socks proxy.", + "tls-certification-label": "CA Cert", + "tls-certification-placeholder": "Begins with {{certificateBeginsWith}}", + "tls-client-certification-label": "Client Cert", + "tls-client-key-label": "Client Key", + "tls-client-key-placeholder": "Begins with {{privateKeyBeginsWith}}", + "tls-heading": "TLS/SSL Auth Details", + "tls-server-name-label": "ServerName", + "tls-tooltip": "TLS/SSL Certs are encrypted and stored in the Grafana database." }, "date-time-picker": { "apply": "Apply", - "cancel": "Cancel" + "calendar-icon-label": "Time picker", + "cancel": "Cancel", + "next-label": "Next month", + "previous-label": "Previous month", + "select-placeholder": "Select date/time" }, "drawer": { "close": "Close" }, + "feature-badge": { + "experimental": "Experimental", + "new": "New!", + "preview": "Preview", + "private-preview": "Private preview" + }, "field-link-list": { "external-links-heading": "External links" }, @@ -1440,6 +1848,10 @@ "modal": { "close-tooltip": "Close" }, + "named-colors-palette": { + "text-color-swatch": "Text color", + "transparent-swatch": "Transparent" + }, "secret-form-field": { "reset": "Reset" }, @@ -1453,6 +1865,9 @@ "no-options-label": "No options found", "placeholder": "Choose" }, + "series-color-picker-popover": { + "y-axis-usage": "Use right y-axis" + }, "spinner": { "aria-label": "Loading" }, @@ -1472,11 +1887,19 @@ "user-icon": { "active-text": "Active last 15m" }, + "value-pill": { + "remove-button": "Remove {{children}}" + }, "viz-legend": { "right-axis-indicator": "(right y-axis)" }, "viz-tooltip": { - "footer-add-annotation": "Add annotation" + "actions-confirmation-input-placeholder": "Are you sure you want to {{ actionTitle }}?", + "actions-confirmation-label": "Confirmation message", + "actions-confirmation-message": "Provide a descriptive prompt to confirm or cancel the action.", + "footer-add-annotation": "Add annotation", + "footer-click-to-action": "Click to {{actionTitle}}", + "footer-click-to-navigate": "Click to open {{linkTitle}}" } }, "graph": { @@ -1744,6 +2167,7 @@ "render-image-error-description": "An error occurred when generating the image" } }, + "lock-icon": "lock icon", "login": { "divider": { "connecting-text": "or" @@ -1788,6 +2212,10 @@ }, "logs": { "infinite-scroll": { + "end-of-range": "End of the selected time range.", + "load-more": "Scroll to load more", + "load-newer": "Loading newer logs...", + "load-older": "Loading older logs...", "older-logs": "Older logs" }, "log-details": { @@ -1798,7 +2226,8 @@ }, "log-row-message": { "ellipsis": "… ", - "more": "more" + "more": "more", + "see-details": "See log details" }, "log-rows": { "disable-popover": { @@ -1810,6 +2239,13 @@ "shortcut": "alt+select to enable again" } }, + "logs-navigation": { + "newer-logs": "Newer logs", + "older-logs": "Older logs", + "scroll-bottom": "Scroll to bottom", + "scroll-top": "Scroll to top", + "start-of-range": "Start of range" + }, "popover-menu": { "copy": "Copy selection", "disable-menu": "Disable menu", @@ -1899,6 +2335,7 @@ "title": "Let us help you migrate to this stack" }, "migrated-counts": { + "alert_rule_groups": "alert rule groups", "alert_rules": "alert rules", "contact_points": "contact points", "dashboards": "dashboards", @@ -1958,10 +2395,9 @@ }, "public-preview": { "button-text": "Give feedback", - "message": "No SLAs are available yet. <2>Visit our docs to learn more about this feature!", - "message-plugins": "Only Community and Commercial signed plugins are eligible for migration. Their latest version will be installed in the cloud instance, please upgrade your plugins before starting the migration process.", - "title": "Migrate to Grafana Cloud is in public preview", - "title-plugins": "Migration of plugins" + "message": "<0>Visit our docs to learn more about this feature!", + "message-cloud": "Your self-managed instance of Grafana requires version 11.5+, or 11.2+ with the onPremToCloudMigrations feature flag enabled.", + "title": "Migrate to Grafana Cloud is in public preview" }, "resource-details": { "dismiss-button": "OK", @@ -2004,6 +2440,7 @@ }, "resource-type": { "alert_rule": "Alert Rule", + "alert_rule_group": "Alert Rule Group", "contact_point": "Contact Point", "dashboard": "Dashboard", "datasource": "Data source", @@ -2053,6 +2490,9 @@ "all": { "title": "All", "title-filtered": "All (filtered)" + }, + "clear": { + "title": "Clear all" } }, "nav": { @@ -2193,6 +2633,9 @@ "detect": { "title": "Detect" }, + "drilldown": { + "title": "Drilldown" + }, "explore": { "title": "Explore" }, @@ -2230,6 +2673,8 @@ "history-wrapper": { "collapse": "Collapse", "expand": "Expand", + "icon-selected": "Selected Entry", + "icon-unselected": "Normal Entry", "show-more": "Show more", "today": "Today", "yesterday": "Yesterday" @@ -2408,7 +2853,8 @@ "list-label": "Navigation", "open": "Open menu", "undock": "Undock menu" - } + }, + "rss-button": "Latest from the blog" }, "news": { "drawer": { @@ -2515,10 +2961,11 @@ }, "plugins": { "catalog": { + "no-updates-available": "No updates available", "update-all": { "all-plugins-updated": "All plugins updated!", "available-header": "Available", - "button": "Update all", + "button": "Update all ({{length}})", "cloud-update-message": "* It may take a few minutes for the plugins to be available for usage.", "error": "Error updating plugin:", "error-status-text": "failed - see error messages", @@ -2545,22 +2992,41 @@ }, "labels": { "contactGrafanaLabs": "Contact Grafana Labs", + "customLinks": "Custom links ", + "customLinksTooltip": "These links are provided by the plugin developer to offer additional, developer-specific resources and information", "dependencies": "Dependencies", + "documentation": "Documentation", "downloads": "Downloads", "from": "From", "installedVersion": "Installed Version", "lastCommitDate": "Last commit date:", "latestVersion": "Latest Version", - "links": "Links ", + "license": "License", + "raiseAnIssue": "Raise an issue", "reportAbuse": "Report a concern ", + "reportAbuseTooltip": "Report issues related to malicious or harmful plugins directly to Grafana Labs.", + "repository": "Repository", "signature": "Signature", "status": "Status", "updatedAt": "Last updated:" + }, + "modal": { + "cancel": "Cancel", + "copyEmail": "Copy email address", + "description": "This feature is for reporting malicious or harmful behaviour within plugins. For plugin concerns, email us at: ", + "node": "Note: For general plugin issues like bugs or feature requests, please contact the plugin author using the provided links. ", + "title": "Report a plugin concern" } }, "empty-state": { "message": "No plugins found" }, + "filter": { + "disabled": "This filter has been disabled because the Grafana server cannot access grafana.com", + "sort": "Sort", + "sort-list": "Sort Plugins List", + "state": "State" + }, "plugin-help": { "error": "An error occurred when loading help.", "not-found": "No query help could be found." @@ -2758,7 +3224,7 @@ "shared-dashboard-modal-title": "Shared dashboards" }, "table-body": { - "dashboard-count_one": "{{count}} dashboard", + "dashboard-count_one": "{{count}} dashboards", "dashboard-count_other": "{{count}} dashboards" }, "table-header": { @@ -2770,14 +3236,6 @@ "role-label": "Role" } }, - "query-library": { - "datasource-names": "Datasource name(s):", - "delete-query-button": "Delete query", - "query-template-get-error": "Error attempting to get query template from the library: {{error}}", - "search": "Search by data source, query content or description", - "user-info-get-error": "Error attempting to get user info from the library: {{error}}", - "user-names": "User name(s):" - }, "query-operation": { "header": { "collapse-row": "Collapse query row", @@ -2787,7 +3245,6 @@ "expand-row": "Expand query row", "hide-response": "Hide response", "remove-query": "Remove query", - "save-to-query-library": "Save to query library", "show-response": "Show response", "toggle-edit-mode": "Toggle text edit mode" }, @@ -3176,6 +3633,7 @@ "home-dashboard-placeholder": "Default dashboard", "locale-label": "Language", "locale-placeholder": "Choose language", + "theme-description": "Enjoying the limited edition themes? Tell us what you'd like to see <2>here.", "theme-label": "Interface theme", "week-start-label": "Week start" }, @@ -3268,6 +3726,16 @@ "title": "You haven't created any teams yet" } }, + "theme-preview": { + "breadcrumbs": { + "dashboards": "Dashboards", + "home": "Home" + }, + "panel": { + "form-label": "Form label", + "title": "Panel" + } + }, "time-picker": { "absolute": { "recent-title": "Recently used absolute ranges", @@ -3324,10 +3792,13 @@ "example": "Example: to select a time range from 10 minutes ago to now", "example-details": "From: now-10m To: now", "example-title": "Example time ranges", + "from-label": "From", "from-to": "{{timeOptionFrom}} to {{timeOptionTo}}", "more-info": "For more information see <2>docs <1>.", "specify": "Specify time range <1>", - "supported-formats": "Supported formats: <1>now-[digit]s/m/h/d/w" + "submit-button-label": "TimePicker submit button", + "supported-formats": "Supported formats: <1>now-[digit]s/m/h/d/w", + "to-label": "To" }, "zone": { "select-aria-label": "Time zone picker", diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json index f324380f834..e6d9efb5f9b 100644 --- a/public/locales/es-ES/grafana.json +++ b/public/locales/es-ES/grafana.json @@ -34,12 +34,6 @@ "save-button": "" } }, - "actions-editor": { - "inline": { - "add-button": "", - "one-click-link": "" - } - }, "admin": { "anon-users": { "not-found": "" @@ -174,6 +168,24 @@ } }, "alerting": { + "alert": { + "alert-state": "", + "annotations": "", + "evaluation": "", + "evaluation-paused": "", + "evaluation-paused-description": "", + "last-evaluated": "", + "last-evaluation-duration": "", + "last-updated-at": "", + "last-updated-by": "", + "no-annotations": "", + "pending-period": "", + "rule": "", + "rule-identifier": "", + "rule-type": "", + "state-error-timeout": "", + "state-no-data": "" + }, "alert-recording-rule-form": { "evaluation-behaviour": { "description": { @@ -193,6 +205,41 @@ "recording": "" } }, + "alertVersionHistory": { + "alerting": "", + "alerting-change-description": "", + "annotations": "", + "compareVersions": "", + "comparing-versions": "", + "condition": "", + "contactPointRouting": "", + "description": "", + "errorloading": "", + "execErrorState": "", + "intervalSeconds": "", + "labels": "", + "latest": "", + "name": "", + "namespace_uid": "", + "noDataState": "", + "noVersionsFound": "", + "paused": "", + "pendingPeriod": "", + "provisioning": "", + "provisioning-change-description": "", + "queryAndAlertCondition": "", + "reset": "", + "restore": "", + "restore-modal": { + "body": "", + "summary": "", + "title": "" + }, + "rule_group": "", + "unknown": "", + "unknown-change-description": "", + "user-id": "" + }, "annotations": { "description": "", "title": "" @@ -271,6 +318,9 @@ "no-delivery-attempts": "", "no-integrations": "", "only-firing": "", + "receiver-summary": { + "jira": "" + }, "telegram": { "parse-mode-warning-body": "", "parse-mode-warning-title": "" @@ -283,6 +333,11 @@ "contactPointFilter": { "label": "" }, + "copy-to-clipboard": "", + "dag": { + "missing-reference": "", + "self-reference": "" + }, "export": { "subtitle": { "formats": "", @@ -299,6 +354,13 @@ } } }, + "group-actions": { + "actions-trigger": "", + "delete": "", + "edit": "", + "export": "", + "reorder": "" + }, "list-view": { "empty": { "new-alert-rule": "", @@ -466,6 +528,19 @@ }, "pause": { "label": "" + }, + "threshold": { + "recovery": { + "stop-alerting-above": "", + "stop-alerting-bellow": "", + "stop-alerting-equal": "", + "stop-alerting-inside-range": "", + "stop-alerting-less": "", + "stop-alerting-more": "", + "stop-alerting-not-equal": "", + "stop-alerting-outside-range": "", + "title": "" + } } }, "rule-groups": { @@ -484,11 +559,23 @@ }, "rule-list": { "configure-datasource": "", + "ds-error-boundary": { + "description": "", + "title": "" + }, "filter-view": { "no-more-results": "", "no-rules-found": "" }, - "new-alert-rule": "" + "new-alert-rule": "", + "pagination": { + "next-page": "", + "previous-page": "" + }, + "return-button": { + "title": "" + }, + "rulerrule-loading-error": "" }, "rule-state": { "creating": "", @@ -496,6 +583,14 @@ "paused": "", "recording-rule": "" }, + "rule-view": { + "query": { + "datasources-na": { + "description": "", + "title": "" + } + } + }, "rule-viewer": { "prometheus-consistency-check": { "alert-message": "", @@ -690,7 +785,10 @@ "title": "" }, "custom-value": { - "label": "" + "description": "" + }, + "group": { + "undefined": "" }, "options": { "no-found": "" @@ -725,6 +823,7 @@ "collapse": "", "edit": "", "help": "", + "loading": "", "locale": { "default": "Por defecto" }, @@ -748,6 +847,25 @@ "placeholder": "Buscar todo" } }, + "core": { + "versionHistory": { + "comparison": { + "header": { + "hide-json-diff": "", + "show-json-diff": "", + "text": "" + }, + "select": "" + }, + "no-properties-changed": "", + "table": { + "updated": "", + "updatedBy": "", + "version": "" + }, + "view-json-diff": "" + } + }, "correlations": { "add-new": "Añadir nuevo", "alert": { @@ -863,6 +981,87 @@ "redirect-link": "Lista en Alertas de Grafana", "subtitle": "Reglas de alerta relacionadas con este tablero" }, + "default-layout": { + "description": "", + "item-options": { + "repeat": { + "direction": { + "horizontal": "", + "title": "", + "vertical": "" + }, + "max": "", + "title": "", + "variable": { + "description": "", + "title": "" + } + } + }, + "name": "", + "row-actions": { + "delete": "", + "modal": { + "alt-action": "", + "text": "", + "title": "" + } + }, + "row-options": { + "button": { + "label": "" + }, + "form": { + "cancel": "", + "repeat-for": { + "label": "", + "learn-more": "", + "warning": { + "text": "" + } + }, + "title": "", + "update": "" + }, + "modal": { + "title": "" + } + } + }, + "edit-pane": { + "objects": { + "multi-select": { + "selection-number": "" + } + }, + "open": "", + "panels": { + "multi-select": { + "selection-number": "" + } + }, + "row": { + "header": { + "hide": "", + "title": "" + }, + "multi-select": { + "options-header": "", + "selection-number": "" + } + }, + "tab": { + "multi-select": { + "options-header": "", + "selection-number": "" + } + } + }, + "editpane": { + "add": "", + "configure": "", + "outline": "" + }, "empty": { "add-library-panel-body": "Añadir las visualizaciones que se comparten con otros tableros.", "add-library-panel-button": "Añadir panel de biblioteca", @@ -874,6 +1073,9 @@ "import-a-dashboard-header": "Importar un tablero", "import-dashboard-button": "Importar panel de control" }, + "errors": { + "failed-to-load": "" + }, "inspect": { "data-tab": "Datos", "error-tab": "Error", @@ -926,19 +1128,134 @@ "rows": "Número total de filas", "table-title": "Estadísticas" }, + "options": { + "description": "", + "title": "", + "title-option": "" + }, + "panel-edit": { + "alerting-tab": { + "dashboard-not-saved": "", + "no-rules": "" + } + }, + "responsive-layout": { + "description": "", + "item-options": { + "hide-no-data": "", + "title": "" + }, + "name": "", + "options": { + "columns": "", + "fixed": "", + "min": "", + "one-column": "", + "rows": "", + "three-columns": "", + "two-columns": "" + } + }, + "rows-layout": { + "description": "", + "name": "", + "row": { + "collapse": "", + "expand": "", + "new": "", + "repeat": { + "learn-more": "", + "warning": "" + } + }, + "row-options": { + "height": { + "expand": "", + "hide-row-header": "", + "min": "", + "title": "" + }, + "repeat": { + "title": "", + "variable": { + "title": "" + } + }, + "title": "", + "title-option": "" + } + }, + "tabs-layout": { + "description": "", + "name": "", + "tab": { + "new": "" + }, + "tab-options": { + "title": "", + "title-option": "" + } + }, "toolbar": { "add": "Añadir", + "add-panel": "", + "add-panel-description": "", + "add-panel-lib": "", + "add-row": "", + "add-tab": "", "alert-rules": "Reglas de alerta", + "back-to-dashboard": "", + "dashboard-settings": { + "label": "", + "tooltip": "" + }, + "discard-library-panel-changes": "", + "discard-panel": "", + "discard-panel-new": "", + "edit": { + "label": "", + "tooltip": "" + }, + "edit-dashboard-v2-schema": "", + "enter-edit-mode": { + "label": "", + "tooltip": "" + }, + "exit-edit-mode": { + "label": "", + "tooltip": "" + }, + "libray-panel-description": "", "mark-favorite": "Marcar como favorito", + "more-save-options": "", "open-original": "Abrir el panel de control original", "playlist-next": "Ir al siguiente panel de control", "playlist-previous": "Ir al panel de control anterior", "playlist-stop": "Detener la lista de reproducción", + "public-dashboard": "", "refresh": "Actualizar panel de control", + "row-description": "", "save": "Guardar panel de control", + "save-dashboard": { + "label": "", + "tooltip": "" + }, + "save-dashboard-copy": { + "label": "", + "tooltip": "" + }, + "save-dashboard-short": "", + "save-library-panel": "", "settings": "Ajustes del panel de control", - "share": "Compartir panel o panel de control", + "share": { + "label": "", + "tooltip": "" + }, "share-button": "Compartir", + "show-hidden-elements": "", + "switch-old-dashboard": "", + "tabs-description": "", + "unlink-library-panel": "", "unmark-favorite": "Deshacer marca como favorito" }, "validation": { @@ -946,6 +1263,14 @@ "invalid-json": "JSON no válido", "tags-expected-array": "etiquetas: matriz prevista", "tags-expected-strings": "etiquetas: matriz de cadenas prevista" + }, + "viz-panel": { + "options": { + "description": "", + "title": "", + "title-option": "", + "transparent-background": "" + } } }, "dashboard-import": { @@ -1035,6 +1360,12 @@ "time-zone-label": "Huso horario", "week-start-label": "Inicio de la semana" }, + "time-regions": { + "advanced-description-cron": "", + "advanced-description-rest": "", + "advanced-description-use": "", + "advanced-label": "" + }, "variables": { "title": "Variables" }, @@ -1048,6 +1379,9 @@ "angular-deprecation-description": "", "angular-deprecation-heading": "" }, + "panel-queries": { + "add-query-from-library": "" + }, "settings": { "variables": { "dependencies": { @@ -1114,6 +1448,11 @@ }, "explore": { "add-to-dashboard": "Añadir al tablero", + "drilldownInfo": { + "action": "", + "description": "", + "title": "" + }, "logs": { "logs-volume": { "add-filters": "", @@ -1125,36 +1464,6 @@ "scan-for-older-logs": "", "stop-scan": "" }, - "query-library": { - "add-edit-description": "", - "cancel": "", - "default-description": "", - "delete-query": "", - "delete-query-text": "", - "delete-query-title": "", - "private": "", - "public": "", - "query-deleted": "", - "query-template-add-error": "", - "query-template-added": "", - "query-template-edit-error": "", - "query-template-edited": "", - "save": "" - }, - "query-template-modal": { - "add-info": "", - "add-title": "", - "auto-star": "", - "data-source-name": "", - "description": "", - "edit-info": "", - "edit-title": "", - "query": "", - "visibility": "" - }, - "query-template-modall": { - "data-source-type": "" - }, "rich-history": { "close-tooltip": "Cerrar el historial de consultas", "datasource-a-z": "Fuente de datos A-Z", @@ -1262,6 +1571,7 @@ "switch-datasource-button": "" }, "secondary-actions": { + "add-from-query-library": "", "query-add-button": "Añadir consulta", "query-add-button-aria-label": "Añadir consulta", "query-history-button": "Historial de consultas", @@ -1305,10 +1615,9 @@ "sortBy": "" }, "related-logs": { - "docsLink": "", + "LrrDocsLink": "", "openExploreLogs": "", - "relatedLogsUnavailableAfterDocsLink": "", - "relatedLogsUnavailableBeforeDocsLink": "", + "relatedLogsUnavailable": "", "warnExperimentalFeature": "" }, "viewBy": "" @@ -1356,7 +1665,29 @@ "incomplete-request-error": "", "send-custom-feedback": "" }, + "get-enterprise": { + "requires-license": "", + "title": "" + }, "grafana-ui": { + "action-editor": { + "button": { + "confirm": "", + "confirm-action": "" + }, + "inline": { + "add-action": "", + "edit-action": "" + }, + "modal": { + "action-body": "", + "action-method": "", + "action-query-params": "", + "action-title": "", + "action-title-placeholder": "", + "one-click-description": "" + } + }, "auto-save-field": { "saved": "", "saving": "" @@ -1373,11 +1704,21 @@ }, "data-link-editor-modal": { "cancel": "", + "one-click-description": "", "save": "" }, + "data-link-inline-editor": { + "one-click": "" + }, "data-links-inline-editor": { "add-link": "", - "one-click-link": "" + "edit-link": "", + "one-click": "", + "one-click-enabled": "", + "title-not-provided": "", + "tooltip-edit": "", + "tooltip-remove": "", + "url-not-provided": "" }, "data-source-http-settings": { "access-help": "", @@ -1409,6 +1750,12 @@ "drawer": { "close": "Cerrar" }, + "feature-badge": { + "experimental": "", + "new": "", + "preview": "", + "private-preview": "" + }, "field-link-list": { "external-links-heading": "" }, @@ -1458,7 +1805,12 @@ "right-axis-indicator": "" }, "viz-tooltip": { - "footer-add-annotation": "" + "actions-confirmation-input-placeholder": "", + "actions-confirmation-label": "", + "actions-confirmation-message": "", + "footer-add-annotation": "", + "footer-click-to-action": "", + "footer-click-to-navigate": "" } }, "graph": { @@ -1726,6 +2078,7 @@ "render-image-error-description": "" } }, + "lock-icon": "", "login": { "divider": { "connecting-text": "" @@ -1770,6 +2123,10 @@ }, "logs": { "infinite-scroll": { + "end-of-range": "", + "load-more": "", + "load-newer": "", + "load-older": "", "older-logs": "" }, "log-details": { @@ -1780,7 +2137,8 @@ }, "log-row-message": { "ellipsis": "", - "more": "" + "more": "", + "see-details": "" }, "log-rows": { "disable-popover": { @@ -1792,6 +2150,13 @@ "shortcut": "" } }, + "logs-navigation": { + "newer-logs": "", + "older-logs": "", + "scroll-bottom": "", + "scroll-top": "", + "start-of-range": "" + }, "popover-menu": { "copy": "", "disable-menu": "", @@ -1881,6 +2246,7 @@ "title": "" }, "migrated-counts": { + "alert_rule_groups": "", "alert_rules": "", "contact_points": "", "dashboards": "", @@ -1941,9 +2307,8 @@ "public-preview": { "button-text": "", "message": "", - "message-plugins": "", - "title": "", - "title-plugins": "" + "message-cloud": "", + "title": "" }, "resource-details": { "dismiss-button": "", @@ -1986,6 +2351,7 @@ }, "resource-type": { "alert_rule": "", + "alert_rule_group": "", "contact_point": "", "dashboard": "Panel de control", "datasource": "Fuente de datos", @@ -2031,6 +2397,15 @@ "title": "¿Por qué alojar con Grafana?" } }, + "multicombobox": { + "all": { + "title": "", + "title-filtered": "" + }, + "clear": { + "title": "" + } + }, "nav": { "add-new-connections": { "title": "Añadir nueva conexión" @@ -2169,6 +2544,9 @@ "detect": { "title": "Detectar" }, + "drilldown": { + "title": "" + }, "explore": { "title": "Explorar" }, @@ -2206,6 +2584,8 @@ "history-wrapper": { "collapse": "", "expand": "", + "icon-selected": "", + "icon-unselected": "", "show-more": "", "today": "", "yesterday": "" @@ -2384,7 +2764,8 @@ "list-label": "Navegación", "open": "", "undock": "Desanclar el menú" - } + }, + "rss-button": "" }, "news": { "drawer": { @@ -2491,6 +2872,7 @@ }, "plugins": { "catalog": { + "no-updates-available": "", "update-all": { "all-plugins-updated": "", "available-header": "", @@ -2516,24 +2898,46 @@ } }, "details": { + "connections-tab": { + "description": "" + }, "labels": { "contactGrafanaLabs": "", + "customLinks": "", + "customLinksTooltip": "", "dependencies": "", + "documentation": "", "downloads": "", "from": "", "installedVersion": "", "lastCommitDate": "", "latestVersion": "", - "links": "", + "license": "", + "raiseAnIssue": "", "reportAbuse": "", + "reportAbuseTooltip": "", + "repository": "", "signature": "", "status": "", "updatedAt": "" + }, + "modal": { + "cancel": "", + "copyEmail": "", + "description": "", + "node": "", + "title": "" } }, "empty-state": { "message": "" }, + "filter": { + "disabled": "", + "sort": "", + "sort-list": "", + "state": "" + }, "plugin-help": { "error": "", "not-found": "" @@ -2743,14 +3147,6 @@ "role-label": "Rol" } }, - "query-library": { - "datasource-names": "", - "delete-query-button": "", - "query-template-get-error": "", - "search": "", - "user-info-get-error": "", - "user-names": "" - }, "query-operation": { "header": { "collapse-row": "Contraer la fila de la consulta", @@ -2760,7 +3156,6 @@ "expand-row": "Expandir la fila de la consulta", "hide-response": "", "remove-query": "Eliminar consulta", - "save-to-query-library": "", "show-response": "", "toggle-edit-mode": "Alternar el modo de edición de texto" }, @@ -2862,6 +3257,10 @@ "title": "" }, "save-dashboards": { + "message-length": { + "info": "", + "title": "" + }, "name-exists": { "message-info": "", "message-suggestion": "", @@ -3145,6 +3544,7 @@ "home-dashboard-placeholder": "Panel de control por defecto", "locale-label": "Idioma", "locale-placeholder": "Cambiar idioma", + "theme-description": "", "theme-label": "Tema de interfaz de usuario", "week-start-label": "Inicio de la semana" }, @@ -3216,6 +3616,13 @@ "url-column-header": "URL de la instantánea", "view-button": "Vista" }, + "table": { + "container": { + "content": "", + "show-all-series": "", + "show-only-series": "" + } + }, "tag-filter": { "clear-button": "", "loading": "Cargando...", @@ -3309,20 +3716,26 @@ "start-your-metrics-exploration": "", "subtitle": "" }, - "metric-overview": { - "description-label": "", - "labels": "", - "metric-attributes": "", - "no-description": "", - "type-label": "", - "unit-label": "", - "unknown-type": "" - }, "metric-select": { "filter-by": "", + "native-histogram": "", "new-badge": "", "otel-switch": "" }, + "native-histogram-banner": { + "ch-heatmap": "", + "ch-histogram": "", + "click-histogram": "", + "hide-examples": "", + "learn-more": "", + "metric-examples": "", + "nh-heatmap": "", + "nh-histogram": "", + "now": "", + "previously": "", + "see-examples": "", + "sentence": "" + }, "recent-metrics": { "or-view-a-recent-exploration": "" }, diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json index 2ea9883ffe1..f6ab1a8be74 100644 --- a/public/locales/fr-FR/grafana.json +++ b/public/locales/fr-FR/grafana.json @@ -34,12 +34,6 @@ "save-button": "" } }, - "actions-editor": { - "inline": { - "add-button": "", - "one-click-link": "" - } - }, "admin": { "anon-users": { "not-found": "" @@ -174,6 +168,24 @@ } }, "alerting": { + "alert": { + "alert-state": "", + "annotations": "", + "evaluation": "", + "evaluation-paused": "", + "evaluation-paused-description": "", + "last-evaluated": "", + "last-evaluation-duration": "", + "last-updated-at": "", + "last-updated-by": "", + "no-annotations": "", + "pending-period": "", + "rule": "", + "rule-identifier": "", + "rule-type": "", + "state-error-timeout": "", + "state-no-data": "" + }, "alert-recording-rule-form": { "evaluation-behaviour": { "description": { @@ -193,6 +205,41 @@ "recording": "" } }, + "alertVersionHistory": { + "alerting": "", + "alerting-change-description": "", + "annotations": "", + "compareVersions": "", + "comparing-versions": "", + "condition": "", + "contactPointRouting": "", + "description": "", + "errorloading": "", + "execErrorState": "", + "intervalSeconds": "", + "labels": "", + "latest": "", + "name": "", + "namespace_uid": "", + "noDataState": "", + "noVersionsFound": "", + "paused": "", + "pendingPeriod": "", + "provisioning": "", + "provisioning-change-description": "", + "queryAndAlertCondition": "", + "reset": "", + "restore": "", + "restore-modal": { + "body": "", + "summary": "", + "title": "" + }, + "rule_group": "", + "unknown": "", + "unknown-change-description": "", + "user-id": "" + }, "annotations": { "description": "", "title": "" @@ -271,6 +318,9 @@ "no-delivery-attempts": "", "no-integrations": "", "only-firing": "", + "receiver-summary": { + "jira": "" + }, "telegram": { "parse-mode-warning-body": "", "parse-mode-warning-title": "" @@ -283,6 +333,11 @@ "contactPointFilter": { "label": "" }, + "copy-to-clipboard": "", + "dag": { + "missing-reference": "", + "self-reference": "" + }, "export": { "subtitle": { "formats": "", @@ -299,6 +354,13 @@ } } }, + "group-actions": { + "actions-trigger": "", + "delete": "", + "edit": "", + "export": "", + "reorder": "" + }, "list-view": { "empty": { "new-alert-rule": "", @@ -466,6 +528,19 @@ }, "pause": { "label": "" + }, + "threshold": { + "recovery": { + "stop-alerting-above": "", + "stop-alerting-bellow": "", + "stop-alerting-equal": "", + "stop-alerting-inside-range": "", + "stop-alerting-less": "", + "stop-alerting-more": "", + "stop-alerting-not-equal": "", + "stop-alerting-outside-range": "", + "title": "" + } } }, "rule-groups": { @@ -484,11 +559,23 @@ }, "rule-list": { "configure-datasource": "", + "ds-error-boundary": { + "description": "", + "title": "" + }, "filter-view": { "no-more-results": "", "no-rules-found": "" }, - "new-alert-rule": "" + "new-alert-rule": "", + "pagination": { + "next-page": "", + "previous-page": "" + }, + "return-button": { + "title": "" + }, + "rulerrule-loading-error": "" }, "rule-state": { "creating": "", @@ -496,6 +583,14 @@ "paused": "", "recording-rule": "" }, + "rule-view": { + "query": { + "datasources-na": { + "description": "", + "title": "" + } + } + }, "rule-viewer": { "prometheus-consistency-check": { "alert-message": "", @@ -690,7 +785,10 @@ "title": "" }, "custom-value": { - "label": "" + "description": "" + }, + "group": { + "undefined": "" }, "options": { "no-found": "" @@ -725,6 +823,7 @@ "collapse": "", "edit": "", "help": "", + "loading": "", "locale": { "default": "Par défaut" }, @@ -748,6 +847,25 @@ "placeholder": "Rechercher tout" } }, + "core": { + "versionHistory": { + "comparison": { + "header": { + "hide-json-diff": "", + "show-json-diff": "", + "text": "" + }, + "select": "" + }, + "no-properties-changed": "", + "table": { + "updated": "", + "updatedBy": "", + "version": "" + }, + "view-json-diff": "" + } + }, "correlations": { "add-new": "Ajouter un nouveau", "alert": { @@ -863,6 +981,87 @@ "redirect-link": "Liste dans Alertes Grafana", "subtitle": "Règles d'alerte liées à ce tableau de bord" }, + "default-layout": { + "description": "", + "item-options": { + "repeat": { + "direction": { + "horizontal": "", + "title": "", + "vertical": "" + }, + "max": "", + "title": "", + "variable": { + "description": "", + "title": "" + } + } + }, + "name": "", + "row-actions": { + "delete": "", + "modal": { + "alt-action": "", + "text": "", + "title": "" + } + }, + "row-options": { + "button": { + "label": "" + }, + "form": { + "cancel": "", + "repeat-for": { + "label": "", + "learn-more": "", + "warning": { + "text": "" + } + }, + "title": "", + "update": "" + }, + "modal": { + "title": "" + } + } + }, + "edit-pane": { + "objects": { + "multi-select": { + "selection-number": "" + } + }, + "open": "", + "panels": { + "multi-select": { + "selection-number": "" + } + }, + "row": { + "header": { + "hide": "", + "title": "" + }, + "multi-select": { + "options-header": "", + "selection-number": "" + } + }, + "tab": { + "multi-select": { + "options-header": "", + "selection-number": "" + } + } + }, + "editpane": { + "add": "", + "configure": "", + "outline": "" + }, "empty": { "add-library-panel-body": "Ajoutez des visualisations partagées avec d'autres tableaux de bord.", "add-library-panel-button": "Ajouter un panneau Bibliothèque", @@ -874,6 +1073,9 @@ "import-a-dashboard-header": "Importer un tableau de bord", "import-dashboard-button": "Importer un tableau de bord" }, + "errors": { + "failed-to-load": "" + }, "inspect": { "data-tab": "Données", "error-tab": "Erreur", @@ -926,19 +1128,134 @@ "rows": "Nombre total de lignes", "table-title": "Statistiques" }, + "options": { + "description": "", + "title": "", + "title-option": "" + }, + "panel-edit": { + "alerting-tab": { + "dashboard-not-saved": "", + "no-rules": "" + } + }, + "responsive-layout": { + "description": "", + "item-options": { + "hide-no-data": "", + "title": "" + }, + "name": "", + "options": { + "columns": "", + "fixed": "", + "min": "", + "one-column": "", + "rows": "", + "three-columns": "", + "two-columns": "" + } + }, + "rows-layout": { + "description": "", + "name": "", + "row": { + "collapse": "", + "expand": "", + "new": "", + "repeat": { + "learn-more": "", + "warning": "" + } + }, + "row-options": { + "height": { + "expand": "", + "hide-row-header": "", + "min": "", + "title": "" + }, + "repeat": { + "title": "", + "variable": { + "title": "" + } + }, + "title": "", + "title-option": "" + } + }, + "tabs-layout": { + "description": "", + "name": "", + "tab": { + "new": "" + }, + "tab-options": { + "title": "", + "title-option": "" + } + }, "toolbar": { "add": "Ajouter", + "add-panel": "", + "add-panel-description": "", + "add-panel-lib": "", + "add-row": "", + "add-tab": "", "alert-rules": "Règles d'alerte", + "back-to-dashboard": "", + "dashboard-settings": { + "label": "", + "tooltip": "" + }, + "discard-library-panel-changes": "", + "discard-panel": "", + "discard-panel-new": "", + "edit": { + "label": "", + "tooltip": "" + }, + "edit-dashboard-v2-schema": "", + "enter-edit-mode": { + "label": "", + "tooltip": "" + }, + "exit-edit-mode": { + "label": "", + "tooltip": "" + }, + "libray-panel-description": "", "mark-favorite": "Marquer comme favori", + "more-save-options": "", "open-original": "Ouvrir le tableau de bord d'origine", "playlist-next": "Accéder au tableau de bord suivant", "playlist-previous": "Accéder au tableau de bord précédent", "playlist-stop": "Arrêter la liste de lecture", + "public-dashboard": "", "refresh": "Actualiser le tableau de bord", + "row-description": "", "save": "Enregistrer le tableau de bord", + "save-dashboard": { + "label": "", + "tooltip": "" + }, + "save-dashboard-copy": { + "label": "", + "tooltip": "" + }, + "save-dashboard-short": "", + "save-library-panel": "", "settings": "Paramètres du tableau de bord", - "share": "Partager le tableau de bord ou le panneau", + "share": { + "label": "", + "tooltip": "" + }, "share-button": "Partager", + "show-hidden-elements": "", + "switch-old-dashboard": "", + "tabs-description": "", + "unlink-library-panel": "", "unmark-favorite": "Supprimer des favoris" }, "validation": { @@ -946,6 +1263,14 @@ "invalid-json": "JSON non valide", "tags-expected-array": "étiquettes attendues tableau", "tags-expected-strings": "étiquettes attendues tableau de chaînes" + }, + "viz-panel": { + "options": { + "description": "", + "title": "", + "title-option": "", + "transparent-background": "" + } } }, "dashboard-import": { @@ -1035,6 +1360,12 @@ "time-zone-label": "Fuseau horaire", "week-start-label": "Début de la semaine" }, + "time-regions": { + "advanced-description-cron": "", + "advanced-description-rest": "", + "advanced-description-use": "", + "advanced-label": "" + }, "variables": { "title": "Variables" }, @@ -1048,6 +1379,9 @@ "angular-deprecation-description": "", "angular-deprecation-heading": "" }, + "panel-queries": { + "add-query-from-library": "" + }, "settings": { "variables": { "dependencies": { @@ -1114,6 +1448,11 @@ }, "explore": { "add-to-dashboard": "Ajouter au tableau de bord", + "drilldownInfo": { + "action": "", + "description": "", + "title": "" + }, "logs": { "logs-volume": { "add-filters": "", @@ -1125,36 +1464,6 @@ "scan-for-older-logs": "", "stop-scan": "" }, - "query-library": { - "add-edit-description": "", - "cancel": "", - "default-description": "", - "delete-query": "", - "delete-query-text": "", - "delete-query-title": "", - "private": "", - "public": "", - "query-deleted": "", - "query-template-add-error": "", - "query-template-added": "", - "query-template-edit-error": "", - "query-template-edited": "", - "save": "" - }, - "query-template-modal": { - "add-info": "", - "add-title": "", - "auto-star": "", - "data-source-name": "", - "description": "", - "edit-info": "", - "edit-title": "", - "query": "", - "visibility": "" - }, - "query-template-modall": { - "data-source-type": "" - }, "rich-history": { "close-tooltip": "Fermer l'historique des requêtes", "datasource-a-z": "Source de données A-Z", @@ -1262,6 +1571,7 @@ "switch-datasource-button": "" }, "secondary-actions": { + "add-from-query-library": "", "query-add-button": "Ajouter une requête", "query-add-button-aria-label": "Ajouter une requête", "query-history-button": "Historique des requêtes", @@ -1305,10 +1615,9 @@ "sortBy": "" }, "related-logs": { - "docsLink": "", + "LrrDocsLink": "", "openExploreLogs": "", - "relatedLogsUnavailableAfterDocsLink": "", - "relatedLogsUnavailableBeforeDocsLink": "", + "relatedLogsUnavailable": "", "warnExperimentalFeature": "" }, "viewBy": "" @@ -1356,7 +1665,29 @@ "incomplete-request-error": "", "send-custom-feedback": "" }, + "get-enterprise": { + "requires-license": "", + "title": "" + }, "grafana-ui": { + "action-editor": { + "button": { + "confirm": "", + "confirm-action": "" + }, + "inline": { + "add-action": "", + "edit-action": "" + }, + "modal": { + "action-body": "", + "action-method": "", + "action-query-params": "", + "action-title": "", + "action-title-placeholder": "", + "one-click-description": "" + } + }, "auto-save-field": { "saved": "", "saving": "" @@ -1373,11 +1704,21 @@ }, "data-link-editor-modal": { "cancel": "", + "one-click-description": "", "save": "" }, + "data-link-inline-editor": { + "one-click": "" + }, "data-links-inline-editor": { "add-link": "", - "one-click-link": "" + "edit-link": "", + "one-click": "", + "one-click-enabled": "", + "title-not-provided": "", + "tooltip-edit": "", + "tooltip-remove": "", + "url-not-provided": "" }, "data-source-http-settings": { "access-help": "", @@ -1409,6 +1750,12 @@ "drawer": { "close": "Fermer" }, + "feature-badge": { + "experimental": "", + "new": "", + "preview": "", + "private-preview": "" + }, "field-link-list": { "external-links-heading": "" }, @@ -1458,7 +1805,12 @@ "right-axis-indicator": "" }, "viz-tooltip": { - "footer-add-annotation": "" + "actions-confirmation-input-placeholder": "", + "actions-confirmation-label": "", + "actions-confirmation-message": "", + "footer-add-annotation": "", + "footer-click-to-action": "", + "footer-click-to-navigate": "" } }, "graph": { @@ -1726,6 +2078,7 @@ "render-image-error-description": "" } }, + "lock-icon": "", "login": { "divider": { "connecting-text": "" @@ -1770,6 +2123,10 @@ }, "logs": { "infinite-scroll": { + "end-of-range": "", + "load-more": "", + "load-newer": "", + "load-older": "", "older-logs": "" }, "log-details": { @@ -1780,7 +2137,8 @@ }, "log-row-message": { "ellipsis": "", - "more": "" + "more": "", + "see-details": "" }, "log-rows": { "disable-popover": { @@ -1792,6 +2150,13 @@ "shortcut": "" } }, + "logs-navigation": { + "newer-logs": "", + "older-logs": "", + "scroll-bottom": "", + "scroll-top": "", + "start-of-range": "" + }, "popover-menu": { "copy": "", "disable-menu": "", @@ -1881,6 +2246,7 @@ "title": "" }, "migrated-counts": { + "alert_rule_groups": "", "alert_rules": "", "contact_points": "", "dashboards": "", @@ -1941,9 +2307,8 @@ "public-preview": { "button-text": "", "message": "", - "message-plugins": "", - "title": "", - "title-plugins": "" + "message-cloud": "", + "title": "" }, "resource-details": { "dismiss-button": "", @@ -1986,6 +2351,7 @@ }, "resource-type": { "alert_rule": "", + "alert_rule_group": "", "contact_point": "", "dashboard": "Tableau de bord", "datasource": "Source de données", @@ -2031,6 +2397,15 @@ "title": "Pourquoi héberger avec Grafana ?" } }, + "multicombobox": { + "all": { + "title": "", + "title-filtered": "" + }, + "clear": { + "title": "" + } + }, "nav": { "add-new-connections": { "title": "Ajouter une nouvelle connexion" @@ -2169,6 +2544,9 @@ "detect": { "title": "Détecter" }, + "drilldown": { + "title": "" + }, "explore": { "title": "Explorer" }, @@ -2206,6 +2584,8 @@ "history-wrapper": { "collapse": "", "expand": "", + "icon-selected": "", + "icon-unselected": "", "show-more": "", "today": "", "yesterday": "" @@ -2384,7 +2764,8 @@ "list-label": "Navigation", "open": "", "undock": "Ancrer le menu" - } + }, + "rss-button": "" }, "news": { "drawer": { @@ -2491,6 +2872,7 @@ }, "plugins": { "catalog": { + "no-updates-available": "", "update-all": { "all-plugins-updated": "", "available-header": "", @@ -2516,24 +2898,46 @@ } }, "details": { + "connections-tab": { + "description": "" + }, "labels": { "contactGrafanaLabs": "", + "customLinks": "", + "customLinksTooltip": "", "dependencies": "", + "documentation": "", "downloads": "", "from": "", "installedVersion": "", "lastCommitDate": "", "latestVersion": "", - "links": "", + "license": "", + "raiseAnIssue": "", "reportAbuse": "", + "reportAbuseTooltip": "", + "repository": "", "signature": "", "status": "", "updatedAt": "" + }, + "modal": { + "cancel": "", + "copyEmail": "", + "description": "", + "node": "", + "title": "" } }, "empty-state": { "message": "" }, + "filter": { + "disabled": "", + "sort": "", + "sort-list": "", + "state": "" + }, "plugin-help": { "error": "", "not-found": "" @@ -2743,14 +3147,6 @@ "role-label": "Rôle" } }, - "query-library": { - "datasource-names": "", - "delete-query-button": "", - "query-template-get-error": "", - "search": "", - "user-info-get-error": "", - "user-names": "" - }, "query-operation": { "header": { "collapse-row": "Réduire la ligne de requête", @@ -2760,7 +3156,6 @@ "expand-row": "Développer la ligne de requête", "hide-response": "", "remove-query": "Supprimer la requête", - "save-to-query-library": "", "show-response": "", "toggle-edit-mode": "Activer/désactiver le mode édition de texte" }, @@ -2862,6 +3257,10 @@ "title": "" }, "save-dashboards": { + "message-length": { + "info": "", + "title": "" + }, "name-exists": { "message-info": "", "message-suggestion": "", @@ -3145,6 +3544,7 @@ "home-dashboard-placeholder": "Tableau de bord par défaut", "locale-label": "Langue", "locale-placeholder": "Choisir une langue", + "theme-description": "", "theme-label": "Thème de l'interface utilisateur", "week-start-label": "Début de la semaine" }, @@ -3216,6 +3616,13 @@ "url-column-header": "URL de l'instantané", "view-button": "Afficher" }, + "table": { + "container": { + "content": "", + "show-all-series": "", + "show-only-series": "" + } + }, "tag-filter": { "clear-button": "", "loading": "Chargement en cours...", @@ -3309,20 +3716,26 @@ "start-your-metrics-exploration": "", "subtitle": "" }, - "metric-overview": { - "description-label": "", - "labels": "", - "metric-attributes": "", - "no-description": "", - "type-label": "", - "unit-label": "", - "unknown-type": "" - }, "metric-select": { "filter-by": "", + "native-histogram": "", "new-badge": "", "otel-switch": "" }, + "native-histogram-banner": { + "ch-heatmap": "", + "ch-histogram": "", + "click-histogram": "", + "hide-examples": "", + "learn-more": "", + "metric-examples": "", + "nh-heatmap": "", + "nh-histogram": "", + "now": "", + "previously": "", + "see-examples": "", + "sentence": "" + }, "recent-metrics": { "or-view-a-recent-exploration": "" }, diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index c87e26d6a1e..3eea1ed364a 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -34,12 +34,6 @@ "save-button": "Ŝävę" } }, - "actions-editor": { - "inline": { - "add-button": "Åđđ äčŧįőʼn", - "one-click-link": "Øʼnę-čľįčĸ ľįʼnĸ" - } - }, "admin": { "anon-users": { "not-found": "Ńő äʼnőʼnymőūş ūşęřş ƒőūʼnđ." @@ -174,6 +168,24 @@ } }, "alerting": { + "alert": { + "alert-state": "Åľęřŧ şŧäŧę", + "annotations": "Åʼnʼnőŧäŧįőʼnş", + "evaluation": "Ēväľūäŧįőʼn", + "evaluation-paused": "Åľęřŧ ęväľūäŧįőʼn čūřřęʼnŧľy päūşęđ", + "evaluation-paused-description": "Ńőŧįƒįčäŧįőʼnş ƒőř ŧĥįş řūľę ŵįľľ ʼnőŧ ƒįřę äʼnđ ʼnő äľęřŧ įʼnşŧäʼnčęş ŵįľľ þę čřęäŧęđ ūʼnŧįľ ŧĥę řūľę įş ūʼn-päūşęđ.", + "last-evaluated": "Ŀäşŧ ęväľūäŧęđ", + "last-evaluation-duration": "Ŀäşŧ ęväľūäŧįőʼn đūřäŧįőʼn", + "last-updated-at": "Ŀäşŧ ūpđäŧęđ äŧ", + "last-updated-by": "Ŀäşŧ ūpđäŧęđ þy", + "no-annotations": "Ńő äʼnʼnőŧäŧįőʼnş", + "pending-period": "Pęʼnđįʼnģ pęřįőđ", + "rule": "Ŗūľę", + "rule-identifier": "Ŗūľę įđęʼnŧįƒįęř", + "rule-type": "Ŗūľę ŧypę", + "state-error-timeout": "Åľęřŧ şŧäŧę įƒ ęχęčūŧįőʼn ęřřőř őř ŧįmęőūŧ", + "state-no-data": "Åľęřŧ şŧäŧę įƒ ʼnő đäŧä őř äľľ väľūęş äřę ʼnūľľ" + }, "alert-recording-rule-form": { "evaluation-behaviour": { "description": { @@ -193,6 +205,41 @@ "recording": "Åđđ ľäþęľş ŧő yőūř řūľę." } }, + "alertVersionHistory": { + "alerting": "Åľęřŧįʼnģ", + "alerting-change-description": "Ŧĥįş ūpđäŧę ŵäş mäđę þy ŧĥę äľęřŧįʼnģ şyşŧęm đūę ŧő őŧĥęř čĥäʼnģęş. Főř ęχämpľę, ŵĥęʼn řęʼnämįʼnģ ä čőʼnŧäčŧ pőįʼnŧ ŧĥäŧ įş ūşęđ ƒőř şįmpľįƒįęđ řőūŧįʼnģ, ŧĥįş ŵįľľ ūpđäŧę 䃃ęčŧęđ řūľęş", + "annotations": "Åʼnʼnőŧäŧįőʼnş", + "compareVersions": "Cőmpäřę vęřşįőʼnş", + "comparing-versions": "Cőmpäřįʼnģ vęřşįőʼnş", + "condition": "Åľęřŧ čőʼnđįŧįőʼn", + "contactPointRouting": "Cőʼnŧäčŧ pőįʼnŧ řőūŧįʼnģ", + "description": "Ēäčĥ ŧįmę yőū ęđįŧ ŧĥę äľęřŧ řūľę, ä ʼnęŵ vęřşįőʼn įş čřęäŧęđ. Ŝęľęčŧ ŧŵő vęřşįőʼnş þęľőŵ äʼnđ čőmpäřę ŧĥęįř đįƒƒęřęʼnčęş.", + "errorloading": "Fäįľęđ ŧő ľőäđ äľęřŧ řūľę vęřşįőʼnş", + "execErrorState": "Åľęřŧ şŧäŧę ŵĥęʼn ęχęčūŧįőʼn ęřřőř", + "intervalSeconds": "Ēväľūäŧįőʼn įʼnŧęřväľ", + "labels": "Ŀäþęľş", + "latest": "Ŀäŧęşŧ", + "name": "Ńämę", + "namespace_uid": "Főľđęř ŮĨĐ", + "noDataState": "Åľęřŧ şŧäŧę ŵĥęʼn ʼnő đäŧä", + "noVersionsFound": "Ńő vęřşįőʼnş ƒőūʼnđ ƒőř ŧĥįş řūľę", + "paused": "Päūşęđ şŧäŧę", + "pendingPeriod": "Pęʼnđįʼnģ pęřįőđ", + "provisioning": "Přővįşįőʼnįʼnģ", + "provisioning-change-description": "Vęřşįőʼn ūpđäŧę ŵäş mäđę vįä přővįşįőʼnįʼnģ", + "queryAndAlertCondition": "Qūęřy äʼnđ äľęřŧ čőʼnđįŧįőʼn", + "reset": "Ŗęşęŧ ŧő vęřşįőʼn {{version}}", + "restore": "Ŗęşŧőřę", + "restore-modal": { + "body": "Åřę yőū şūřę yőū ŵäʼnŧ ŧő řęşŧőřę ŧĥę äľęřŧ řūľę đęƒįʼnįŧįőʼn ŧő ŧĥįş vęřşįőʼn? Åľľ ūʼnşävęđ čĥäʼnģęş ŵįľľ þę ľőşŧ.", + "summary": "Ŝūmmäřy őƒ čĥäʼnģęş ŧő þę äppľįęđ:", + "title": "Ŗęşŧőřę Vęřşįőʼn" + }, + "rule_group": "Ŗūľę ģřőūp", + "unknown": "Ůʼnĸʼnőŵʼn", + "unknown-change-description": "Ŧĥįş ūpđäŧę ŵäş mäđę přįőř ŧő ŧĥę įmpľęmęʼnŧäŧįőʼn őƒ äľęřŧ řūľę vęřşįőʼn ĥįşŧőřy. Ŧĥę ūşęř ŵĥő mäđę ŧĥę čĥäʼnģę įş ʼnőŧ ŧřäčĸęđ, þūŧ ƒūŧūřę čĥäʼnģęş ŵįľľ įʼnčľūđę ŧĥę ūşęř", + "user-id": "Ůşęř ĨĐ {{uid}}" + }, "annotations": { "description": "Åđđ mőřę čőʼnŧęχŧ ŧő yőūř äľęřŧ ʼnőŧįƒįčäŧįőʼnş.", "title": "Cőʼnƒįģūřę ʼnőŧįƒįčäŧįőʼn męşşäģę" @@ -248,6 +295,9 @@ "export-all": "Ēχpőřŧ äľľ", "loading": "Ŀőäđįʼnģ...", "search-by-matchers": "Ŝęäřčĥ þy mäŧčĥęřş", + "titles": { + "notification-templates": "Ńőŧįƒįčäŧįőʼn Ŧęmpľäŧęş" + }, "view": "Vįęŵ" }, "contact-points": { @@ -271,6 +321,9 @@ "no-delivery-attempts": "Ńő đęľįvęřy äŧŧęmpŧş", "no-integrations": "Ńő įʼnŧęģřäŧįőʼnş čőʼnƒįģūřęđ", "only-firing": "Đęľįvęřįʼnģ <1>őʼnľy ƒįřįʼnģ ʼnőŧįƒįčäŧįőʼnş", + "receiver-summary": { + "jira": "Cřęäŧęş ä \"{{issueType}}\" įşşūę įʼn ŧĥę \"{{project}}\" přőĵęčŧ" + }, "telegram": { "parse-mode-warning-body": "Ĩƒ yőū ūşę ä <1>päřşę_mőđę őpŧįőʼn őŧĥęř ŧĥäʼn <3>Ńőʼnę, ŧřūʼnčäŧįőʼn mäy řęşūľŧ įʼn äʼn įʼnväľįđ męşşäģę, čäūşįʼnģ ŧĥę ʼnőŧįƒįčäŧįőʼn ŧő ƒäįľ. Főř ľőʼnģęř męşşäģęş, ŵę řęčőmmęʼnđ ūşįʼnģ äʼn äľŧęřʼnäŧįvę čőʼnŧäčŧ męŧĥőđ.", "parse-mode-warning-title": "Ŧęľęģřäm męşşäģęş äřę ľįmįŧęđ ŧő 4096 ŮŦF-8 čĥäřäčŧęřş." @@ -283,6 +336,11 @@ "contactPointFilter": { "label": "Cőʼnŧäčŧ pőįʼnŧ" }, + "copy-to-clipboard": "Cőpy \"{{label}}\" ŧő čľįpþőäřđ", + "dag": { + "missing-reference": "Ēχpřęşşįőʼn \"{{source}}\" ƒäįľęđ ŧő řūʼn þęčäūşę \"{{target}}\" įş mįşşįʼnģ őř äľşő ƒäįľęđ.", + "self-reference": "Ÿőū čäʼn'ŧ ľįʼnĸ äʼn ęχpřęşşįőʼn ŧő įŧşęľƒ" + }, "export": { "subtitle": { "formats": "Ŝęľęčŧ ŧĥę ƒőřmäŧ äʼnđ đőŵʼnľőäđ ŧĥę ƒįľę őř čőpy ŧĥę čőʼnŧęʼnŧş ŧő čľįpþőäřđ", @@ -349,6 +407,20 @@ "title": "Åľęřŧ įʼnşŧäʼnčę řőūŧįʼnģ přęvįęŵ", "uninitialized": "Ŵĥęʼn yőū ĥävę yőūř ƒőľđęř şęľęčŧęđ äʼnđ yőūř qūęřy äʼnđ ľäþęľş äřę čőʼnƒįģūřęđ, čľįčĸ \"Přęvįęŵ řőūŧįʼnģ\" ŧő şęę ŧĥę řęşūľŧş ĥęřę." }, + "notification-templates": { + "duplicate": { + "subTitle": "Đūpľįčäŧę ä ģřőūp őƒ ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧęş", + "title": "Đūpľįčäŧę ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧę ģřőūp" + }, + "edit": { + "subTitle": "Ēđįŧ ä ģřőūp őƒ ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧęş", + "title": "Ēđįŧ ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧę ģřőūp" + }, + "new": { + "subTitle": "Cřęäŧę ä ʼnęŵ ģřőūp őƒ ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧęş", + "title": "Ńęŵ ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧę ģřőūp" + } + }, "policies": { "default-policy": { "description": "Åľľ äľęřŧ įʼnşŧäʼnčęş ŵįľľ þę ĥäʼnđľęđ þy ŧĥę đęƒäūľŧ pőľįčy įƒ ʼnő őŧĥęř mäŧčĥįʼnģ pőľįčįęş äřę ƒőūʼnđ.", @@ -473,6 +545,19 @@ }, "pause": { "label": "Päūşę ęväľūäŧįőʼn" + }, + "threshold": { + "recovery": { + "stop-alerting-above": "Ŝŧőp äľęřŧįʼnģ ŵĥęʼn äþővę", + "stop-alerting-bellow": "Ŝŧőp äľęřŧįʼnģ ŵĥęʼn þęľőŵ", + "stop-alerting-equal": "Ŝŧőp äľęřŧįʼnģ ŵĥęʼn ęqūäľ ŧő", + "stop-alerting-inside-range": "Ŝŧőp äľęřŧįʼnģ ŵĥęʼn įʼnşįđę řäʼnģę", + "stop-alerting-less": "Ŝŧőp äľęřŧįʼnģ ŵĥęʼn ľęşş ŧĥäʼn", + "stop-alerting-more": "Ŝŧőp äľęřŧįʼnģ ŵĥęʼn mőřę ŧĥäʼn", + "stop-alerting-not-equal": "Ŝŧőp äľęřŧįʼnģ ŵĥęʼn ʼnőŧ ęqūäľ ŧő", + "stop-alerting-outside-range": "Ŝŧőp äľęřŧįʼnģ ŵĥęʼn őūŧşįđę řäʼnģę", + "title": "Cūşŧőm řęčővęřy ŧĥřęşĥőľđ" + } } }, "rule-groups": { @@ -506,7 +591,8 @@ }, "return-button": { "title": "Åľęřŧ řūľęş" - } + }, + "rulerrule-loading-error": "Fäįľęđ ŧő ľőäđ ŧĥę řūľę" }, "rule-state": { "creating": "Cřęäŧįʼnģ", @@ -514,7 +600,16 @@ "paused": "Päūşęđ", "recording-rule": "Ŗęčőřđįʼnģ řūľę" }, + "rule-view": { + "query": { + "datasources-na": { + "description": "Cäʼnʼnőŧ đįşpľäy ŧĥę qūęřy přęvįęŵ. Ŝőmę őƒ ŧĥę đäŧä şőūřčęş ūşęđ įʼn ŧĥę qūęřįęş äřę ʼnőŧ äväįľäþľę.", + "title": "Qūęřy ʼnőŧ äväįľäþľę" + } + } + }, "rule-viewer": { + "error-loading": "Ŝőmęŧĥįʼnģ ŵęʼnŧ ŵřőʼnģ ľőäđįʼnģ ŧĥę řūľę", "prometheus-consistency-check": { "alert-message": "Åľęřŧ řūľę ĥäş þęęʼn ūpđäŧęđ. Cĥäʼnģęş mäy ŧäĸę ūp ŧő ä mįʼnūŧę ŧő äppęäř őʼn ŧĥę Åľęřŧ řūľęş ľįşŧ vįęŵ.", "alert-title": "Ůpđäŧę įʼn přőģřęşş" @@ -634,15 +729,15 @@ }, "counts": { "alertRule_one": "{{count}} äľęřŧ řūľę", - "alertRule_other": "{{count}} äľęřŧ řūľęş", + "alertRule_other": "{{count}} äľęřŧ řūľę", "dashboard_one": "{{count}} đäşĥþőäřđ", - "dashboard_other": "{{count}} đäşĥþőäřđş", + "dashboard_other": "{{count}} đäşĥþőäřđ", "folder_one": "{{count}} ƒőľđęř", - "folder_other": "{{count}} ƒőľđęřş", + "folder_other": "{{count}} ƒőľđęř", "libraryPanel_one": "{{count}} ľįþřäřy päʼnęľ", - "libraryPanel_other": "{{count}} ľįþřäřy päʼnęľş", + "libraryPanel_other": "{{count}} ľįþřäřy päʼnęľ", "total_one": "{{count}} įŧęm", - "total_other": "{{count}} įŧęmş" + "total_other": "{{count}} įŧęm" }, "dashboards-tree": { "collapse-folder-button": "Cőľľäpşę ƒőľđęř {{title}}", @@ -708,7 +803,10 @@ "title": "Cľęäř väľūę" }, "custom-value": { - "label": "Cūşŧőm väľūę: " + "description": "Ůşę čūşŧőm väľūę" + }, + "group": { + "undefined": "Ńő ģřőūp" }, "options": { "no-found": "Ńő őpŧįőʼnş ƒőūʼnđ." @@ -743,6 +841,7 @@ "collapse": "Cőľľäpşę", "edit": "Ēđįŧ", "help": "Ħęľp", + "loading": "Ŀőäđįʼnģ...", "locale": { "default": "Đęƒäūľŧ" }, @@ -766,6 +865,25 @@ "placeholder": "Ŝęäřčĥ äľľ" } }, + "core": { + "versionHistory": { + "comparison": { + "header": { + "hide-json-diff": "Ħįđę ĴŜØŃ đįƒƒ ", + "show-json-diff": "Ŝĥőŵ ĴŜØŃ đįƒƒ ", + "text": "Vęřşįőʼn {{version}} ūpđäŧęđ þy {{createdBy}} ({{ageString}}) {{message}}" + }, + "select": "Ŝęľęčŧ ŧŵő vęřşįőʼnş ŧő şŧäřŧ čőmpäřįʼnģ" + }, + "no-properties-changed": "Ńő řęľęväʼnŧ přőpęřŧįęş čĥäʼnģęđ", + "table": { + "updated": "Đäŧę", + "updatedBy": "Ůpđäŧęđ ßy", + "version": "Vęřşįőʼn" + }, + "view-json-diff": "Vįęŵ ĴŜØŃ đįƒƒ ŧő şęę äľľ čĥäʼnģęş" + } + }, "correlations": { "add-new": "Åđđ ʼnęŵ", "alert": { @@ -881,6 +999,87 @@ "redirect-link": "Ŀįşŧ įʼn Ğřäƒäʼnä Åľęřŧįʼnģ", "subtitle": "Åľęřŧ řūľęş řęľäŧęđ ŧő ŧĥįş đäşĥþőäřđ" }, + "default-layout": { + "description": "Ŧĥę đęƒäūľŧ ģřįđ ľäyőūŧ", + "item-options": { + "repeat": { + "direction": { + "horizontal": "Ħőřįžőʼnŧäľ", + "title": "Ŗępęäŧ đįřęčŧįőʼn", + "vertical": "Vęřŧįčäľ" + }, + "max": "Mäχ pęř řőŵ", + "title": "Ŗępęäŧ őpŧįőʼnş", + "variable": { + "description": "Ŗępęäŧ ŧĥįş päʼnęľ ƒőř ęäčĥ väľūę įʼn ŧĥę şęľęčŧęđ väřįäþľę. Ŧĥįş įş ʼnőŧ vįşįþľę ŵĥįľę įʼn ęđįŧ mőđę. Ÿőū ʼnęęđ ŧő ģő þäčĸ ŧő đäşĥþőäřđ äʼnđ ŧĥęʼn ūpđäŧę ŧĥę väřįäþľę őř řęľőäđ ŧĥę đäşĥþőäřđ.", + "title": "Ŗępęäŧ þy väřįäþľę" + } + } + }, + "name": "Đęƒäūľŧ ģřįđ", + "row-actions": { + "delete": "Đęľęŧę řőŵ", + "modal": { + "alt-action": "Đęľęŧę řőŵ őʼnľy", + "text": "Åřę yőū şūřę yőū ŵäʼnŧ ŧő řęmővę ŧĥįş řőŵ äʼnđ äľľ įŧş päʼnęľş?", + "title": "Đęľęŧę řőŵ" + } + }, + "row-options": { + "button": { + "label": "Ŗőŵ őpŧįőʼnş" + }, + "form": { + "cancel": "Cäʼnčęľ", + "repeat-for": { + "label": "Ŗępęäŧ ƒőř", + "learn-more": "Ŀęäřʼn mőřę", + "warning": { + "text": "Päʼnęľş įʼn ŧĥįş řőŵ ūşę ŧĥę {{SHARED_DASHBOARD_QUERY}} đäŧä şőūřčę. Ŧĥęşę päʼnęľş ŵįľľ řęƒęřęʼnčę ŧĥę päʼnęľ įʼn ŧĥę őřįģįʼnäľ řőŵ, ʼnőŧ ŧĥę őʼnęş įʼn ŧĥę řępęäŧęđ řőŵş." + } + }, + "title": "Ŧįŧľę", + "update": "Ůpđäŧę" + }, + "modal": { + "title": "Ŗőŵ őpŧįőʼnş" + } + } + }, + "edit-pane": { + "objects": { + "multi-select": { + "selection-number": "Ńő. őƒ őþĵęčŧş şęľęčŧęđ: {{length}}" + } + }, + "open": "Øpęʼn őpŧįőʼnş päʼnę", + "panels": { + "multi-select": { + "selection-number": "Ńő. őƒ päʼnęľş şęľęčŧęđ: {{length}}" + } + }, + "row": { + "header": { + "hide": "Ħįđę", + "title": "Ŗőŵ ĥęäđęř" + }, + "multi-select": { + "options-header": "Mūľŧį-şęľęčŧęđ Ŗőŵ őpŧįőʼnş", + "selection-number": "Ńő. őƒ řőŵş şęľęčŧęđ: {{length}}" + } + }, + "tab": { + "multi-select": { + "options-header": "Mūľŧį-şęľęčŧęđ Ŧäþ őpŧįőʼnş", + "selection-number": "Ńő. őƒ ŧäþş şęľęčŧęđ: " + } + } + }, + "editpane": { + "add": "Åđđ", + "configure": "Cőʼnƒįģūřę", + "outline": "Øūŧľįʼnę" + }, "empty": { "add-library-panel-body": "Åđđ vįşūäľįžäŧįőʼnş ŧĥäŧ äřę şĥäřęđ ŵįŧĥ őŧĥęř đäşĥþőäřđş.", "add-library-panel-button": "Åđđ ľįþřäřy päʼnęľ", @@ -892,6 +1091,9 @@ "import-a-dashboard-header": "Ĩmpőřŧ ä đäşĥþőäřđ", "import-dashboard-button": "Ĩmpőřŧ đäşĥþőäřđ" }, + "errors": { + "failed-to-load": "Fäįľęđ ŧő ľőäđ đäşĥþőäřđ" + }, "inspect": { "data-tab": "Đäŧä", "error-tab": "Ēřřőř", @@ -944,20 +1146,134 @@ "rows": "Ŧőŧäľ ʼnūmþęř řőŵş", "table-title": "Ŝŧäŧş" }, + "options": { + "description": "Đęşčřįpŧįőʼn", + "title": "Đäşĥþőäřđ őpŧįőʼnş", + "title-option": "Ŧįŧľę" + }, + "panel-edit": { + "alerting-tab": { + "dashboard-not-saved": "Đäşĥþőäřđ mūşŧ þę şävęđ þęƒőřę äľęřŧş čäʼn þę äđđęđ.", + "no-rules": "Ŧĥęřę äřę ʼnő äľęřŧ řūľęş ľįʼnĸęđ ŧő ŧĥįş päʼnęľ." + } + }, + "responsive-layout": { + "description": "CŜŜ ľäyőūŧ ŧĥäŧ äđĵūşŧş ŧő ŧĥę äväįľäþľę şpäčę", + "item-options": { + "hide-no-data": "Ħįđę ŵĥęʼn ʼnő đäŧä", + "title": "Ŀäyőūŧ őpŧįőʼnş" + }, + "name": "Ŗęşpőʼnşįvę ģřįđ", + "options": { + "columns": "Cőľūmʼnş", + "fixed": "Fįχęđ: {{size}}pχ", + "min": "Mįʼn: {{size}}pχ", + "one-column": "1 čőľūmʼn", + "rows": "Ŗőŵş", + "three-columns": "3 čőľūmʼnş", + "two-columns": "2 čőľūmʼnş" + } + }, + "rows-layout": { + "description": "Ŗőŵş ľäyőūŧ", + "name": "Ŗőŵş", + "row": { + "collapse": "Cőľľäpşę řőŵ", + "expand": "Ēχpäʼnđ řőŵ", + "new": "Ńęŵ řőŵ", + "repeat": { + "learn-more": "Ŀęäřʼn mőřę", + "warning": "Päʼnęľş įʼn ŧĥįş řőŵ ūşę ŧĥę {{SHARED_DASHBOARD_QUERY}} đäŧä şőūřčę. Ŧĥęşę päʼnęľş ŵįľľ řęƒęřęʼnčę ŧĥę päʼnęľ įʼn ŧĥę őřįģįʼnäľ řőŵ, ʼnőŧ ŧĥę őʼnęş įʼn ŧĥę řępęäŧęđ řőŵş." + } + }, + "row-options": { + "height": { + "expand": "Ēχpäʼnđ", + "hide-row-header": "Ħįđę řőŵ ĥęäđęř", + "min": "Mįʼn", + "title": "Ħęįģĥŧ" + }, + "repeat": { + "title": "Ŗępęäŧ őpŧįőʼnş", + "variable": { + "title": "Väřįäþľę" + } + }, + "title": "Ŗőŵ őpŧįőʼnş", + "title-option": "Ŧįŧľę" + } + }, + "tabs-layout": { + "description": "Ŧäþş ľäyőūŧ", + "name": "Ŧäþş", + "tab": { + "new": "Ńęŵ ŧäþ" + }, + "tab-options": { + "title": "Ŧäþ őpŧįőʼnş", + "title-option": "Ŧįŧľę" + } + }, "toolbar": { "add": "Åđđ", + "add-panel": "Päʼnęľ", + "add-panel-description": "Å čőʼnŧäįʼnęř ƒőř vįşūäľįžäŧįőʼnş äʼnđ őŧĥęř čőʼnŧęʼnŧ", + "add-panel-lib": "Ĩmpőřŧ ľįþřäřy päʼnęľ", + "add-row": "Ŗőŵ", + "add-tab": "Ŧäþ", "alert-rules": "Åľęřŧ řūľęş", + "back-to-dashboard": "ßäčĸ ŧő đäşĥþőäřđ", + "dashboard-settings": { + "label": "Ŝęŧŧįʼnģş", + "tooltip": "Đäşĥþőäřđ şęŧŧįʼnģş" + }, + "discard-library-panel-changes": "Đįşčäřđ ľįþřäřy päʼnęľ čĥäʼnģęş", + "discard-panel": "Đįşčäřđ päʼnęľ čĥäʼnģęş", + "discard-panel-new": "Đįşčäřđ päʼnęľ", + "edit": { + "label": "Ēđįŧ", + "tooltip": "Ēʼnŧęř ęđįŧ mőđę" + }, + "edit-dashboard-v2-schema": "Ēđįŧ đäşĥþőäřđ v2 şčĥęmä", + "enter-edit-mode": { + "label": "Mäĸę ęđįŧäþľę", + "tooltip": "Ŧĥįş đäşĥþőäřđ ŵäş mäřĸęđ äş řęäđ őʼnľy" + }, + "exit-edit-mode": { + "label": "Ēχįŧ ęđįŧ", + "tooltip": "Ēχįŧş ęđįŧ mőđę äʼnđ đįşčäřđş ūʼnşävęđ čĥäʼnģęş" + }, + "libray-panel-description": "Ŀįþřäy päʼnęľş äľľőŵ yőū şĥäřę äʼnđ řęūşę päʼnęľş þęŧŵęęʼn đäşĥþőäřđş", "mark-favorite": "Mäřĸ äş ƒävőřįŧę", + "more-save-options": "Mőřę şävę őpŧįőʼnş", "open-original": "Øpęʼn őřįģįʼnäľ đäşĥþőäřđ", "playlist-next": "Ğő ŧő ʼnęχŧ đäşĥþőäřđ", "playlist-previous": "Ğő ŧő přęvįőūş đäşĥþőäřđ", "playlist-stop": "Ŝŧőp pľäyľįşŧ", + "public-dashboard": "Pūþľįč", "refresh": "Ŗęƒřęşĥ đäşĥþőäřđ", + "row-description": "Å ģřőūp őƒ päʼnęľş ŵįŧĥ äʼn őpŧįőʼnäľ ĥęäđęř", "save": "Ŝävę đäşĥþőäřđ", + "save-dashboard": { + "label": "Ŝävę đäşĥþőäřđ", + "tooltip": "Ŝävę čĥäʼnģęş" + }, + "save-dashboard-copy": { + "label": "Ŝävę äş čőpy", + "tooltip": "Ŝävę äş čőpy" + }, + "save-dashboard-short": "Ŝävę", + "save-library-panel": "Ŝävę ľįþřäřy päʼnęľ", "settings": "Đäşĥþőäřđ şęŧŧįʼnģş", - "share": "Ŝĥäřę đäşĥþőäřđ", + "share": { + "label": "Ŝĥäřę", + "tooltip": "Ŝĥäřę đäşĥþőäřđ" + }, "share-button": "Ŝĥäřę", "show-hidden-elements": "Ŝĥőŵ ĥįđđęʼn", + "switch-old-dashboard": "Ŝŵįŧčĥ ŧő őľđ đäşĥþőäřđ päģę", + "tabs-description": "ßřęäĸ ūp yőūř đäşĥþőäřđ įʼnŧő đįƒƒęřęʼnŧ ĥőřįžőʼnŧäľ ŧäþş", + "unlink-library-panel": "Ůʼnľįʼnĸ ľįþřäřy päʼnęľ", "unmark-favorite": "Ůʼnmäřĸ äş ƒävőřįŧę" }, "validation": { @@ -965,6 +1281,14 @@ "invalid-json": "Ńőŧ väľįđ ĴŜØŃ", "tags-expected-array": "ŧäģş ęχpęčŧęđ äřřäy", "tags-expected-strings": "ŧäģş ęχpęčŧęđ äřřäy őƒ şŧřįʼnģş" + }, + "viz-panel": { + "options": { + "description": "Đęşčřįpŧįőʼn", + "title": "Päʼnęľ őpŧįőʼnş", + "title-option": "Ŧįŧľę", + "transparent-background": "Ŧřäʼnşpäřęʼnŧ þäčĸģřőūʼnđ" + } } }, "dashboard-import": { @@ -1054,6 +1378,12 @@ "time-zone-label": "Ŧįmę žőʼnę", "week-start-label": "Ŵęęĸ şŧäřŧ" }, + "time-regions": { + "advanced-description-cron": "Cřőʼn şyʼnŧäχ", + "advanced-description-rest": " ŧő đęƒįʼnę ä řęčūřřęʼnčę şčĥęđūľę äʼnđ đūřäŧįőʼn", + "advanced-description-use": "Ůşę ", + "advanced-label": "Åđväʼnčęđ" + }, "variables": { "title": "Väřįäþľęş" }, @@ -1067,6 +1397,9 @@ "angular-deprecation-description": "Åʼnģūľäř päʼnęľş őpŧįőʼnş čäʼn őʼnľy þę ęđįŧęđ ūşįʼnģ ŧĥę ĴŜØŃ ęđįŧőř.", "angular-deprecation-heading": "Päʼnęľ őpŧįőʼnş" }, + "panel-queries": { + "add-query-from-library": "Åđđ qūęřy ƒřőm ľįþřäřy" + }, "settings": { "variables": { "dependencies": { @@ -1133,6 +1466,11 @@ }, "explore": { "add-to-dashboard": "Åđđ ŧő đäşĥþőäřđ", + "drilldownInfo": { + "action": "Ğő ŧő Ğřäƒäʼnä Đřįľľđőŵʼn", + "description": "Ŀőőĸįʼnģ ƒőř ŧĥę Ğřäƒäʼnä Ēχpľőřę äppş? Ŧĥęy äřę ʼnőŵ čäľľęđ ŧĥę Ğřäƒäʼnä Đřįľľđőŵʼn äppş äʼnđ čäʼn þę ƒőūʼnđ ūʼnđęř <1>Męʼnū > Đřįľľđőŵʼn", + "title": "Ēχpľőřę Męŧřįčş, Ŀőģş, Ŧřäčęş äʼnđ Přőƒįľęş ĥävę mővęđ!" + }, "logs": { "logs-volume": { "add-filters": "Åđđ mőřę ľäþęľş ŧő yőūř qūęřy ŧő ʼnäřřőŵ đőŵʼn yőūř şęäřčĥ.", @@ -1144,36 +1482,6 @@ "scan-for-older-logs": "Ŝčäʼn ƒőř őľđęř ľőģş", "stop-scan": "Ŝŧőp şčäʼn" }, - "query-library": { - "add-edit-description": "Åđđ/ęđįŧ đęşčřįpŧįőʼn", - "cancel": "Cäʼnčęľ", - "default-description": "Pūþľįč", - "delete-query": "Đęľęŧę qūęřy", - "delete-query-text": "Ÿőū'řę äþőūŧ ŧő řęmővę ŧĥįş qūęřy ƒřőm ŧĥę qūęřy ľįþřäřy. Ŧĥįş äčŧįőʼn čäʼnʼnőŧ þę ūʼnđőʼnę. Đő yőū ŵäʼnŧ ŧő čőʼnŧįʼnūę?", - "delete-query-title": "Đęľęŧę qūęřy", - "private": "Přįväŧę", - "public": "Pūþľįč", - "query-deleted": "Qūęřy đęľęŧęđ", - "query-template-add-error": "Ēřřőř äŧŧęmpŧįʼnģ ŧő şävę ŧĥįş qūęřy ŧő ŧĥę ľįþřäřy", - "query-template-added": "Qūęřy şūččęşşƒūľľy şävęđ ŧő ŧĥę ľįþřäřy", - "query-template-edit-error": "Ēřřőř äŧŧęmpŧįʼnģ ŧő ęđįŧ ŧĥįş qūęřy", - "query-template-edited": "Qūęřy ŧęmpľäŧę şūččęşşƒūľľy ęđįŧęđ", - "save": "Ŝävę" - }, - "query-template-modal": { - "add-info": "Ÿőū'řę äþőūŧ ŧő şävę ŧĥįş qūęřy. Øʼnčę şävęđ, yőū čäʼn ęäşįľy äččęşş įŧ įʼn ŧĥę Qūęřy Ŀįþřäřy ŧäþ ƒőř ƒūŧūřę ūşę äʼnđ řęƒęřęʼnčę.", - "add-title": "Åđđ qūęřy ŧő Qūęřy Ŀįþřäřy", - "auto-star": "Åūŧő-şŧäř ŧĥįş qūęřy ŧő äđđ įŧ ŧő yőūř şŧäřřęđ ľįşŧ įʼn ŧĥę Qūęřy Ŀįþřäřy.", - "data-source-name": "Đäŧä şőūřčę ʼnämę", - "description": "Đęşčřįpŧįőʼn", - "edit-info": "Ÿőū'řę äþőūŧ ŧő ęđįŧ ŧĥįş qūęřy. Øʼnčę şävęđ, yőū čäʼn ęäşįľy äččęşş įŧ įʼn ŧĥę Qūęřy Ŀįþřäřy ŧäþ ƒőř ƒūŧūřę ūşę äʼnđ řęƒęřęʼnčę.", - "edit-title": "Ēđįŧ qūęřy", - "query": "Qūęřy", - "visibility": "Vįşįþįľįŧy" - }, - "query-template-modall": { - "data-source-type": "Đäŧä şőūřčę ŧypę" - }, "rich-history": { "close-tooltip": "Cľőşę qūęřy ĥįşŧőřy", "datasource-a-z": "Đäŧä şőūřčę Å-Ż", @@ -1281,6 +1589,7 @@ "switch-datasource-button": "Ŝŵįŧčĥ đäŧä şőūřčę äʼnđ řūʼn qūęřy" }, "secondary-actions": { + "add-from-query-library": "Åđđ qūęřy ƒřőm ľįþřäřy", "query-add-button": "Åđđ qūęřy", "query-add-button-aria-label": "Åđđ qūęřy", "query-history-button": "Qūęřy ĥįşŧőřy", @@ -1374,11 +1683,42 @@ "incomplete-request-error": "Ŝőřřy, Ĩ ŵäş ūʼnäþľę ŧő čőmpľęŧę yőūř řęqūęşŧ. Pľęäşę ŧřy äģäįʼn.", "send-custom-feedback": "Ŝęʼnđ" }, + "get-enterprise": { + "requires-license": "Ŗęqūįřęş ä Ğřäƒäʼnä Ēʼnŧęřpřįşę ľįčęʼnşę", + "title": "Ēʼnŧęřpřįşę" + }, "grafana-ui": { + "action-editor": { + "button": { + "confirm": "Cőʼnƒįřm", + "confirm-action": "Cőʼnƒįřm äčŧįőʼn" + }, + "inline": { + "add-action": "Åđđ äčŧįőʼn", + "edit-action": "Ēđįŧ äčŧįőʼn" + }, + "modal": { + "action-body": "ßőđy", + "action-method": "Męŧĥőđ", + "action-query-params": "Qūęřy päřämęŧęřş", + "action-title": "Ŧįŧľę", + "action-title-placeholder": "Åčŧįőʼn ŧįŧľę", + "one-click-description": "Øʼnľy őʼnę ľįʼnĸ őř äčŧįőʼn čäʼn ĥävę őʼnę čľįčĸ ęʼnäþľęđ äŧ ä ŧįmę" + } + }, + "alert": { + "close-button": "Cľőşę äľęřŧ" + }, "auto-save-field": { "saved": "Ŝävęđ!", "saving": "Ŝävįʼnģ <1>" }, + "card": { + "option": "őpŧįőʼn" + }, + "cascader": { + "clear-button": "Cľęäř şęľęčŧįőʼn" + }, "color-picker-popover": { "palette-tab": "Cőľőřş", "spectrum-tab": "Cūşŧőm" @@ -1386,47 +1726,115 @@ "confirm-button": { "cancel": "Cäʼnčęľ" }, + "confirm-content": { + "placeholder": "Ŧypę \"{{confirmPromptText}}\" ŧő čőʼnƒįřm" + }, "data-link-editor": { - "info": "Ŵįŧĥ đäŧä ľįʼnĸş yőū čäʼn řęƒęřęʼnčę đäŧä väřįäþľęş ľįĸę şęřįęş ʼnämę, ľäþęľş äʼnđ väľūęş. Ŧypę CMĐ+Ŝpäčę, CŦŖĿ+Ŝpäčę, őř $ ŧő őpęʼn väřįäþľę şūģģęşŧįőʼnş." + "info": "Ŵįŧĥ đäŧä ľįʼnĸş yőū čäʼn řęƒęřęʼnčę đäŧä väřįäþľęş ľįĸę şęřįęş ʼnämę, ľäþęľş äʼnđ väľūęş. Ŧypę CMĐ+Ŝpäčę, CŦŖĿ+Ŝpäčę, őř $ ŧő őpęʼn väřįäþľę şūģģęşŧįőʼnş.", + "new-tab-label": "Øpęʼn įʼn ʼnęŵ ŧäþ", + "title-label": "Ŧįŧľę", + "title-placeholder": "Ŝĥőŵ đęŧäįľş", + "url-label": "ŮŖĿ" }, "data-link-editor-modal": { "cancel": "Cäʼnčęľ", + "one-click-description": "Øʼnľy őʼnę ľįʼnĸ čäʼn ĥävę őʼnę čľįčĸ ęʼnäþľęđ äŧ ä ŧįmę", "save": "Ŝävę" }, + "data-link-inline-editor": { + "one-click": "Øʼnę čľįčĸ" + }, "data-links-inline-editor": { "add-link": "Åđđ ľįʼnĸ", - "one-click-link": "Øʼnę-čľįčĸ ľįʼnĸ" + "edit-link": "Ēđįŧ ľįʼnĸ", + "one-click": "Øʼnę čľįčĸ", + "one-click-enabled": "Øʼnę čľįčĸ ęʼnäþľęđ", + "title-not-provided": "Ŧįŧľę ʼnőŧ přővįđęđ", + "tooltip-edit": "Ēđįŧ", + "tooltip-remove": "Ŗęmővę", + "url-not-provided": "Đäŧä ľįʼnĸ ūřľ ʼnőŧ přővįđęđ" + }, + "data-source-basic-auth-settings": { + "user-label": "Ůşęř", + "user-placeholder": "ūşęř" + }, + "data-source-http-proxy-settings": { + "oauth-identity-label": "Főřŵäřđ ØÅūŧĥ Ĩđęʼnŧįŧy", + "oauth-identity-tooltip": "Főřŵäřđ ŧĥę ūşęř'ş ūpşŧřęäm ØÅūŧĥ įđęʼnŧįŧy ŧő ŧĥę đäŧä şőūřčę (Ŧĥęįř äččęşş ŧőĸęʼn ģęŧş päşşęđ äľőʼnģ).", + "skip-tls-verify-label": "Ŝĸįp ŦĿŜ Vęřįƒy", + "ts-client-auth-label": "ŦĿŜ Cľįęʼnŧ Åūŧĥ", + "with-ca-cert-label": "Ŵįŧĥ CÅ Cęřŧ", + "with-ca-cert-tooltip": "Ńęęđęđ ƒőř vęřįƒyįʼnģ şęľƒ-şįģʼnęđ ŦĿŜ Cęřŧş" }, "data-source-http-settings": { "access-help": "Ħęľp <1>", "access-help-details": "Åččęşş mőđę čőʼnŧřőľş ĥőŵ řęqūęşŧş ŧő ŧĥę đäŧä şőūřčę ŵįľľ þę ĥäʼnđľęđ.<1> <1>Ŝęřvęř şĥőūľđ þę ŧĥę přęƒęřřęđ ŵäy įƒ ʼnőŧĥįʼnģ ęľşę įş şŧäŧęđ.", + "access-label": "Åččęşş", + "access-options-browser": "ßřőŵşęř", + "access-options-proxy": "Ŝęřvęř (đęƒäūľŧ)", "allowed-cookies": "Åľľőŵęđ čőőĸįęş", + "allowed-cookies-tooltip": "Ğřäƒäʼnä přőχy đęľęŧęş ƒőřŵäřđęđ čőőĸįęş þy đęƒäūľŧ. Ŝpęčįƒy čőőĸįęş þy ʼnämę ŧĥäŧ şĥőūľđ þę ƒőřŵäřđęđ ŧő ŧĥę đäŧä şőūřčę.", "auth": "Åūŧĥ", + "azure-auth-label": "Åžūřę Åūŧĥęʼnŧįčäŧįőʼn", + "azure-auth-tooltip": "Ůşę Åžūřę äūŧĥęʼnŧįčäŧįőʼn ƒőř Åžūřę ęʼnđpőįʼnŧ.", "basic-auth": "ßäşįč Åūŧĥ Đęŧäįľş", + "basic-auth-label": "ßäşįč äūŧĥ", "browser-mode-description": "Åľľ řęqūęşŧş ŵįľľ þę mäđę ƒřőm ŧĥę þřőŵşęř đįřęčŧľy ŧő ŧĥę đäŧä şőūřčę äʼnđ mäy þę şūþĵęčŧ ŧő Cřőşş-Øřįģįʼn Ŗęşőūřčę Ŝĥäřįʼnģ (CØŖŜ) řęqūįřęmęʼnŧş. Ŧĥę ŮŖĿ ʼnęęđş ŧő þę äččęşşįþľę ƒřőm ŧĥę þřőŵşęř įƒ yőū şęľęčŧ ŧĥįş äččęşş mőđę.", "browser-mode-title": "<0>ßřőŵşęř äččęşş mőđę:", + "default-url-access-select": "Åččęşş", "default-url-tooltip": "Ŝpęčįƒy ä čőmpľęŧę ĦŦŦP ŮŖĿ (ƒőř ęχämpľę ĥŧŧp://yőūř_şęřvęř:8080)", "direct-url-tooltip": "Ÿőūř äččęşş męŧĥőđ įş <1>ßřőŵşęř, ŧĥįş męäʼnş ŧĥę ŮŖĿ ʼnęęđş ŧő þę äččęşşįþľę ƒřőm ŧĥę þřőŵşęř.", "heading": "ĦŦŦP", "proxy-url-tooltip": "Ÿőūř äččęşş męŧĥőđ įş <1>Ŝęřvęř, ŧĥįş męäʼnş ŧĥę ŮŖĿ ʼnęęđş ŧő þę äččęşşįþľę ƒřőm ŧĥę ģřäƒäʼnä þäčĸęʼnđ/şęřvęř.", "server-mode-description": "Åľľ řęqūęşŧş ŵįľľ þę mäđę ƒřőm ŧĥę þřőŵşęř ŧő Ğřäƒäʼnä þäčĸęʼnđ/şęřvęř ŵĥįčĥ įʼn ŧūřʼn ŵįľľ ƒőřŵäřđ ŧĥę řęqūęşŧş ŧő ŧĥę đäŧä şőūřčę äʼnđ þy ŧĥäŧ čįřčūmvęʼnŧ pőşşįþľę Cřőşş-Øřįģįʼn Ŗęşőūřčę Ŝĥäřįʼnģ (CØŖŜ) řęqūįřęmęʼnŧş. Ŧĥę ŮŖĿ ʼnęęđş ŧő þę äččęşşįþľę ƒřőm ŧĥę ģřäƒäʼnä þäčĸęʼnđ/şęřvęř įƒ yőū şęľęčŧ ŧĥįş äččęşş mőđę.", - "server-mode-title": "<0>Ŝęřvęř äččęşş mőđę (Đęƒäūľŧ):" + "server-mode-title": "<0>Ŝęřvęř äččęşş mőđę (Đęƒäūľŧ):", + "timeout-form-label": "Ŧįmęőūŧ", + "timeout-label": "Ŧįmęőūŧ įʼn şęčőʼnđş", + "timeout-tooltip": "ĦŦŦP řęqūęşŧ ŧįmęőūŧ įʼn şęčőʼnđş", + "url-label": "ŮŖĿ", + "with-credential-label": "Ŵįŧĥ Cřęđęʼnŧįäľş", + "with-credential-tooltip": "Ŵĥęŧĥęř čřęđęʼnŧįäľş şūčĥ äş čőőĸįęş őř äūŧĥ ĥęäđęřş şĥőūľđ þę şęʼnŧ ŵįŧĥ čřőşş-şįŧę řęqūęşŧş." }, "data-source-settings": { "alerting-settings-heading": "Åľęřŧįʼnģ", + "alerting-settings-label": "Mäʼnäģę äľęřŧ řūľęş įʼn Åľęřŧįʼnģ ŮĨ", + "alerting-settings-tooltip": "Mäʼnäģę äľęřŧ řūľęş ƒőř ŧĥįş đäŧä şőūřčę. Ŧő mäʼnäģę őŧĥęř äľęřŧįʼnģ řęşőūřčęş, äđđ äʼn Åľęřŧmäʼnäģęř đäŧä şőūřčę.", "cert-key-reset": "Ŗęşęŧ", "custom-headers-add": "Åđđ ĥęäđęř", + "custom-headers-header": "Ħęäđęř", + "custom-headers-header-placeholder": "Ħęäđęř Väľūę", + "custom-headers-header-remove": "Ŗęmővę ĥęäđęř", + "custom-headers-header-value": "Väľūę", "custom-headers-title": "Cūşŧőm ĦŦŦP Ħęäđęřş", "secure-socks-heading": "Ŝęčūřę Ŝőčĸş Přőχy", - "tls-heading": "ŦĿŜ/ŜŜĿ Åūŧĥ Đęŧäįľş" + "secure-socks-label": "Ēʼnäþľęđ", + "secure-socks-tooltip": "Cőʼnʼnęčŧ ŧő ŧĥįş đäŧäşőūřčę vįä ŧĥę şęčūřę şőčĸş přőχy.", + "tls-certification-label": "CÅ Cęřŧ", + "tls-certification-placeholder": "ßęģįʼnş ŵįŧĥ {{certificateBeginsWith}}", + "tls-client-certification-label": "Cľįęʼnŧ Cęřŧ", + "tls-client-key-label": "Cľįęʼnŧ Ķęy", + "tls-client-key-placeholder": "ßęģįʼnş ŵįŧĥ {{privateKeyBeginsWith}}", + "tls-heading": "ŦĿŜ/ŜŜĿ Åūŧĥ Đęŧäįľş", + "tls-server-name-label": "ŜęřvęřŃämę", + "tls-tooltip": "ŦĿŜ/ŜŜĿ Cęřŧş äřę ęʼnčřypŧęđ äʼnđ şŧőřęđ įʼn ŧĥę Ğřäƒäʼnä đäŧäþäşę." }, "date-time-picker": { "apply": "Åppľy", - "cancel": "Cäʼnčęľ" + "calendar-icon-label": "Ŧįmę pįčĸęř", + "cancel": "Cäʼnčęľ", + "next-label": "Ńęχŧ mőʼnŧĥ", + "previous-label": "Přęvįőūş mőʼnŧĥ", + "select-placeholder": "Ŝęľęčŧ đäŧę/ŧįmę" }, "drawer": { "close": "Cľőşę" }, + "feature-badge": { + "experimental": "Ēχpęřįmęʼnŧäľ", + "new": "Ńęŵ!", + "preview": "Přęvįęŵ", + "private-preview": "Přįväŧę přęvįęŵ" + }, "field-link-list": { "external-links-heading": "Ēχŧęřʼnäľ ľįʼnĸş" }, @@ -1440,6 +1848,10 @@ "modal": { "close-tooltip": "Cľőşę" }, + "named-colors-palette": { + "text-color-swatch": "Ŧęχŧ čőľőř", + "transparent-swatch": "Ŧřäʼnşpäřęʼnŧ" + }, "secret-form-field": { "reset": "Ŗęşęŧ" }, @@ -1453,6 +1865,9 @@ "no-options-label": "Ńő őpŧįőʼnş ƒőūʼnđ", "placeholder": "Cĥőőşę" }, + "series-color-picker-popover": { + "y-axis-usage": "Ůşę řįģĥŧ y-äχįş" + }, "spinner": { "aria-label": "Ŀőäđįʼnģ" }, @@ -1472,11 +1887,19 @@ "user-icon": { "active-text": "Åčŧįvę ľäşŧ 15m" }, + "value-pill": { + "remove-button": "Ŗęmővę {{children}}" + }, "viz-legend": { "right-axis-indicator": "(řįģĥŧ y-äχįş)" }, "viz-tooltip": { - "footer-add-annotation": "Åđđ äʼnʼnőŧäŧįőʼn" + "actions-confirmation-input-placeholder": "Åřę yőū şūřę yőū ŵäʼnŧ ŧő {{ actionTitle }}?", + "actions-confirmation-label": "Cőʼnƒįřmäŧįőʼn męşşäģę", + "actions-confirmation-message": "Přővįđę ä đęşčřįpŧįvę přőmpŧ ŧő čőʼnƒįřm őř čäʼnčęľ ŧĥę äčŧįőʼn.", + "footer-add-annotation": "Åđđ äʼnʼnőŧäŧįőʼn", + "footer-click-to-action": "Cľįčĸ ŧő {{actionTitle}}", + "footer-click-to-navigate": "Cľįčĸ ŧő őpęʼn {{linkTitle}}" } }, "graph": { @@ -1744,6 +2167,7 @@ "render-image-error-description": "Åʼn ęřřőř őččūřřęđ ŵĥęʼn ģęʼnęřäŧįʼnģ ŧĥę įmäģę" } }, + "lock-icon": "ľőčĸ įčőʼn", "login": { "divider": { "connecting-text": "őř" @@ -1788,6 +2212,10 @@ }, "logs": { "infinite-scroll": { + "end-of-range": "Ēʼnđ őƒ ŧĥę şęľęčŧęđ ŧįmę řäʼnģę.", + "load-more": "Ŝčřőľľ ŧő ľőäđ mőřę", + "load-newer": "Ŀőäđįʼnģ ʼnęŵęř ľőģş...", + "load-older": "Ŀőäđįʼnģ őľđęř ľőģş...", "older-logs": "Øľđęř ľőģş" }, "log-details": { @@ -1798,7 +2226,8 @@ }, "log-row-message": { "ellipsis": "… ", - "more": "mőřę" + "more": "mőřę", + "see-details": "Ŝęę ľőģ đęŧäįľş" }, "log-rows": { "disable-popover": { @@ -1810,6 +2239,13 @@ "shortcut": "äľŧ+şęľęčŧ ŧő ęʼnäþľę äģäįʼn" } }, + "logs-navigation": { + "newer-logs": "Ńęŵęř ľőģş", + "older-logs": "Øľđęř ľőģş", + "scroll-bottom": "Ŝčřőľľ ŧő þőŧŧőm", + "scroll-top": "Ŝčřőľľ ŧő ŧőp", + "start-of-range": "Ŝŧäřŧ őƒ řäʼnģę" + }, "popover-menu": { "copy": "Cőpy şęľęčŧįőʼn", "disable-menu": "Đįşäþľę męʼnū", @@ -1899,6 +2335,7 @@ "title": "Ŀęŧ ūş ĥęľp yőū mįģřäŧę ŧő ŧĥįş şŧäčĸ" }, "migrated-counts": { + "alert_rule_groups": "äľęřŧ řūľę ģřőūpş", "alert_rules": "äľęřŧ řūľęş", "contact_points": "čőʼnŧäčŧ pőįʼnŧş", "dashboards": "đäşĥþőäřđş", @@ -1958,10 +2395,9 @@ }, "public-preview": { "button-text": "Ğįvę ƒęęđþäčĸ", - "message": "Ńő ŜĿÅş äřę äväįľäþľę yęŧ. <2>Vįşįŧ őūř đőčş ŧő ľęäřʼn mőřę äþőūŧ ŧĥįş ƒęäŧūřę!", - "message-plugins": "Øʼnľy Cőmmūʼnįŧy äʼnđ Cőmmęřčįäľ şįģʼnęđ pľūģįʼnş äřę ęľįģįþľę ƒőř mįģřäŧįőʼn. Ŧĥęįř ľäŧęşŧ vęřşįőʼn ŵįľľ þę įʼnşŧäľľęđ įʼn ŧĥę čľőūđ įʼnşŧäʼnčę, pľęäşę ūpģřäđę yőūř pľūģįʼnş þęƒőřę şŧäřŧįʼnģ ŧĥę mįģřäŧįőʼn přőčęşş.", - "title": "Mįģřäŧę ŧő Ğřäƒäʼnä Cľőūđ įş įʼn pūþľįč přęvįęŵ", - "title-plugins": "Mįģřäŧįőʼn őƒ pľūģįʼnş" + "message": "<0>Vįşįŧ őūř đőčş ŧő ľęäřʼn mőřę äþőūŧ ŧĥįş ƒęäŧūřę!", + "message-cloud": "Ÿőūř şęľƒ-mäʼnäģęđ įʼnşŧäʼnčę őƒ Ğřäƒäʼnä řęqūįřęş vęřşįőʼn 11.5+, őř 11.2+ ŵįŧĥ ŧĥę őʼnPřęmŦőCľőūđMįģřäŧįőʼnş ƒęäŧūřę ƒľäģ ęʼnäþľęđ.", + "title": "Mįģřäŧę ŧő Ğřäƒäʼnä Cľőūđ įş įʼn pūþľįč přęvįęŵ" }, "resource-details": { "dismiss-button": "ØĶ", @@ -2004,6 +2440,7 @@ }, "resource-type": { "alert_rule": "Åľęřŧ Ŗūľę", + "alert_rule_group": "Åľęřŧ Ŗūľę Ğřőūp", "contact_point": "Cőʼnŧäčŧ Pőįʼnŧ", "dashboard": "Đäşĥþőäřđ", "datasource": "Đäŧä şőūřčę", @@ -2053,6 +2490,9 @@ "all": { "title": "Åľľ", "title-filtered": "Åľľ (ƒįľŧęřęđ)" + }, + "clear": { + "title": "Cľęäř äľľ" } }, "nav": { @@ -2193,6 +2633,9 @@ "detect": { "title": "Đęŧęčŧ" }, + "drilldown": { + "title": "Đřįľľđőŵʼn" + }, "explore": { "title": "Ēχpľőřę" }, @@ -2230,6 +2673,8 @@ "history-wrapper": { "collapse": "Cőľľäpşę", "expand": "Ēχpäʼnđ", + "icon-selected": "Ŝęľęčŧęđ Ēʼnŧřy", + "icon-unselected": "Ńőřmäľ Ēʼnŧřy", "show-more": "Ŝĥőŵ mőřę", "today": "Ŧőđäy", "yesterday": "Ÿęşŧęřđäy" @@ -2408,7 +2853,8 @@ "list-label": "Ńävįģäŧįőʼn", "open": "Øpęʼn męʼnū", "undock": "Ůʼnđőčĸ męʼnū" - } + }, + "rss-button": "Ŀäŧęşŧ ƒřőm ŧĥę þľőģ" }, "news": { "drawer": { @@ -2515,10 +2961,11 @@ }, "plugins": { "catalog": { + "no-updates-available": "Ńő ūpđäŧęş äväįľäþľę", "update-all": { "all-plugins-updated": "Åľľ pľūģįʼnş ūpđäŧęđ!", "available-header": "Åväįľäþľę", - "button": "Ůpđäŧę äľľ", + "button": "Ůpđäŧę äľľ ({{length}})", "cloud-update-message": "* Ĩŧ mäy ŧäĸę ä ƒęŵ mįʼnūŧęş ƒőř ŧĥę pľūģįʼnş ŧő þę äväįľäþľę ƒőř ūşäģę.", "error": "Ēřřőř ūpđäŧįʼnģ pľūģįʼn:", "error-status-text": "ƒäįľęđ - şęę ęřřőř męşşäģęş", @@ -2545,22 +2992,41 @@ }, "labels": { "contactGrafanaLabs": "Cőʼnŧäčŧ Ğřäƒäʼnä Ŀäþş", + "customLinks": "Cūşŧőm ľįʼnĸş ", + "customLinksTooltip": "Ŧĥęşę ľįʼnĸş äřę přővįđęđ þy ŧĥę pľūģįʼn đęvęľőpęř ŧő őƒƒęř äđđįŧįőʼnäľ, đęvęľőpęř-şpęčįƒįč řęşőūřčęş äʼnđ įʼnƒőřmäŧįőʼn", "dependencies": "Đępęʼnđęʼnčįęş", + "documentation": "Đőčūmęʼnŧäŧįőʼn", "downloads": "Đőŵʼnľőäđş", "from": "Fřőm", "installedVersion": "Ĩʼnşŧäľľęđ Vęřşįőʼn", "lastCommitDate": "Ŀäşŧ čőmmįŧ đäŧę:", "latestVersion": "Ŀäŧęşŧ Vęřşįőʼn", - "links": "Ŀįʼnĸş ", + "license": "Ŀįčęʼnşę", + "raiseAnIssue": "Ŗäįşę äʼn įşşūę", "reportAbuse": "Ŗępőřŧ ä čőʼnčęřʼn ", + "reportAbuseTooltip": "Ŗępőřŧ įşşūęş řęľäŧęđ ŧő mäľįčįőūş őř ĥäřmƒūľ pľūģįʼnş đįřęčŧľy ŧő Ğřäƒäʼnä Ŀäþş.", + "repository": "Ŗępőşįŧőřy", "signature": "Ŝįģʼnäŧūřę", "status": "Ŝŧäŧūş", "updatedAt": "Ŀäşŧ ūpđäŧęđ:" + }, + "modal": { + "cancel": "Cäʼnčęľ", + "copyEmail": "Cőpy ęmäįľ äđđřęşş", + "description": "Ŧĥįş ƒęäŧūřę įş ƒőř řępőřŧįʼnģ mäľįčįőūş őř ĥäřmƒūľ þęĥävįőūř ŵįŧĥįʼn pľūģįʼnş. Főř pľūģįʼn čőʼnčęřʼnş, ęmäįľ ūş äŧ: ", + "node": "Ńőŧę: Főř ģęʼnęřäľ pľūģįʼn įşşūęş ľįĸę þūģş őř ƒęäŧūřę řęqūęşŧş, pľęäşę čőʼnŧäčŧ ŧĥę pľūģįʼn äūŧĥőř ūşįʼnģ ŧĥę přővįđęđ ľįʼnĸş. ", + "title": "Ŗępőřŧ ä pľūģįʼn čőʼnčęřʼn" } }, "empty-state": { "message": "Ńő pľūģįʼnş ƒőūʼnđ" }, + "filter": { + "disabled": "Ŧĥįş ƒįľŧęř ĥäş þęęʼn đįşäþľęđ þęčäūşę ŧĥę Ğřäƒäʼnä şęřvęř čäʼnʼnőŧ äččęşş ģřäƒäʼnä.čőm", + "sort": "Ŝőřŧ", + "sort-list": "Ŝőřŧ Pľūģįʼnş Ŀįşŧ", + "state": "Ŝŧäŧę" + }, "plugin-help": { "error": "Åʼn ęřřőř őččūřřęđ ŵĥęʼn ľőäđįʼnģ ĥęľp.", "not-found": "Ńő qūęřy ĥęľp čőūľđ þę ƒőūʼnđ." @@ -2758,7 +3224,7 @@ "shared-dashboard-modal-title": "Ŝĥäřęđ đäşĥþőäřđş" }, "table-body": { - "dashboard-count_one": "{{count}} đäşĥþőäřđ", + "dashboard-count_one": "{{count}} đäşĥþőäřđş", "dashboard-count_other": "{{count}} đäşĥþőäřđş" }, "table-header": { @@ -2770,14 +3236,6 @@ "role-label": "Ŗőľę" } }, - "query-library": { - "datasource-names": "Đäŧäşőūřčę ʼnämę(ş):", - "delete-query-button": "Đęľęŧę qūęřy", - "query-template-get-error": "Ēřřőř äŧŧęmpŧįʼnģ ŧő ģęŧ qūęřy ŧęmpľäŧę ƒřőm ŧĥę ľįþřäřy: {{error}}", - "search": "Ŝęäřčĥ þy đäŧä şőūřčę, qūęřy čőʼnŧęʼnŧ őř đęşčřįpŧįőʼn", - "user-info-get-error": "Ēřřőř äŧŧęmpŧįʼnģ ŧő ģęŧ ūşęř įʼnƒő ƒřőm ŧĥę ľįþřäřy: {{error}}", - "user-names": "Ůşęř ʼnämę(ş):" - }, "query-operation": { "header": { "collapse-row": "Cőľľäpşę qūęřy řőŵ", @@ -2787,7 +3245,6 @@ "expand-row": "Ēχpäʼnđ qūęřy řőŵ", "hide-response": "Ħįđę řęşpőʼnşę", "remove-query": "Ŗęmővę qūęřy", - "save-to-query-library": "Ŝävę ŧő qūęřy ľįþřäřy", "show-response": "Ŝĥőŵ řęşpőʼnşę", "toggle-edit-mode": "Ŧőģģľę ŧęχŧ ęđįŧ mőđę" }, @@ -3176,6 +3633,7 @@ "home-dashboard-placeholder": "Đęƒäūľŧ đäşĥþőäřđ", "locale-label": "Ŀäʼnģūäģę", "locale-placeholder": "Cĥőőşę ľäʼnģūäģę", + "theme-description": "Ēʼnĵőyįʼnģ ŧĥę ľįmįŧęđ ęđįŧįőʼn ŧĥęmęş? Ŧęľľ ūş ŵĥäŧ yőū'đ ľįĸę ŧő şęę <2>ĥęřę.", "theme-label": "Ĩʼnŧęřƒäčę ŧĥęmę", "week-start-label": "Ŵęęĸ şŧäřŧ" }, @@ -3268,6 +3726,16 @@ "title": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny ŧęämş yęŧ" } }, + "theme-preview": { + "breadcrumbs": { + "dashboards": "Đäşĥþőäřđş", + "home": "Ħőmę" + }, + "panel": { + "form-label": "Főřm ľäþęľ", + "title": "Päʼnęľ" + } + }, "time-picker": { "absolute": { "recent-title": "Ŗęčęʼnŧľy ūşęđ äþşőľūŧę řäʼnģęş", @@ -3324,10 +3792,13 @@ "example": "Ēχämpľę: ŧő şęľęčŧ ä ŧįmę řäʼnģę ƒřőm 10 mįʼnūŧęş äģő ŧő ʼnőŵ", "example-details": "Fřőm: ʼnőŵ-10m Ŧő: ʼnőŵ", "example-title": "Ēχämpľę ŧįmę řäʼnģęş", + "from-label": "Fřőm", "from-to": "{{timeOptionFrom}} ŧő {{timeOptionTo}}", "more-info": "Főř mőřę įʼnƒőřmäŧįőʼn şęę <2>đőčş <1>.", "specify": "Ŝpęčįƒy ŧįmę řäʼnģę <1>", - "supported-formats": "Ŝūppőřŧęđ ƒőřmäŧş: <1>ʼnőŵ-[đįģįŧ]ş/m/ĥ/đ/ŵ" + "submit-button-label": "ŦįmęPįčĸęř şūþmįŧ þūŧŧőʼn", + "supported-formats": "Ŝūppőřŧęđ ƒőřmäŧş: <1>ʼnőŵ-[đįģįŧ]ş/m/ĥ/đ/ŵ", + "to-label": "Ŧő" }, "zone": { "select-aria-label": "Ŧįmę žőʼnę pįčĸęř", diff --git a/public/locales/pt-BR/grafana.json b/public/locales/pt-BR/grafana.json index 5e0b7cdc2d4..c10c3c0eaa4 100644 --- a/public/locales/pt-BR/grafana.json +++ b/public/locales/pt-BR/grafana.json @@ -34,12 +34,6 @@ "save-button": "" } }, - "actions-editor": { - "inline": { - "add-button": "", - "one-click-link": "" - } - }, "admin": { "anon-users": { "not-found": "" @@ -174,6 +168,24 @@ } }, "alerting": { + "alert": { + "alert-state": "", + "annotations": "", + "evaluation": "", + "evaluation-paused": "", + "evaluation-paused-description": "", + "last-evaluated": "", + "last-evaluation-duration": "", + "last-updated-at": "", + "last-updated-by": "", + "no-annotations": "", + "pending-period": "", + "rule": "", + "rule-identifier": "", + "rule-type": "", + "state-error-timeout": "", + "state-no-data": "" + }, "alert-recording-rule-form": { "evaluation-behaviour": { "description": { @@ -193,6 +205,41 @@ "recording": "" } }, + "alertVersionHistory": { + "alerting": "", + "alerting-change-description": "", + "annotations": "", + "compareVersions": "", + "comparing-versions": "", + "condition": "", + "contactPointRouting": "", + "description": "", + "errorloading": "", + "execErrorState": "", + "intervalSeconds": "", + "labels": "", + "latest": "", + "name": "", + "namespace_uid": "", + "noDataState": "", + "noVersionsFound": "", + "paused": "", + "pendingPeriod": "", + "provisioning": "", + "provisioning-change-description": "", + "queryAndAlertCondition": "", + "reset": "", + "restore": "", + "restore-modal": { + "body": "", + "summary": "", + "title": "" + }, + "rule_group": "", + "unknown": "", + "unknown-change-description": "", + "user-id": "" + }, "annotations": { "description": "", "title": "" @@ -271,6 +318,9 @@ "no-delivery-attempts": "", "no-integrations": "", "only-firing": "", + "receiver-summary": { + "jira": "" + }, "telegram": { "parse-mode-warning-body": "", "parse-mode-warning-title": "" @@ -283,6 +333,11 @@ "contactPointFilter": { "label": "" }, + "copy-to-clipboard": "", + "dag": { + "missing-reference": "", + "self-reference": "" + }, "export": { "subtitle": { "formats": "", @@ -299,6 +354,13 @@ } } }, + "group-actions": { + "actions-trigger": "", + "delete": "", + "edit": "", + "export": "", + "reorder": "" + }, "list-view": { "empty": { "new-alert-rule": "", @@ -466,6 +528,19 @@ }, "pause": { "label": "" + }, + "threshold": { + "recovery": { + "stop-alerting-above": "", + "stop-alerting-bellow": "", + "stop-alerting-equal": "", + "stop-alerting-inside-range": "", + "stop-alerting-less": "", + "stop-alerting-more": "", + "stop-alerting-not-equal": "", + "stop-alerting-outside-range": "", + "title": "" + } } }, "rule-groups": { @@ -484,11 +559,23 @@ }, "rule-list": { "configure-datasource": "", + "ds-error-boundary": { + "description": "", + "title": "" + }, "filter-view": { "no-more-results": "", "no-rules-found": "" }, - "new-alert-rule": "" + "new-alert-rule": "", + "pagination": { + "next-page": "", + "previous-page": "" + }, + "return-button": { + "title": "" + }, + "rulerrule-loading-error": "" }, "rule-state": { "creating": "", @@ -496,6 +583,14 @@ "paused": "", "recording-rule": "" }, + "rule-view": { + "query": { + "datasources-na": { + "description": "", + "title": "" + } + } + }, "rule-viewer": { "prometheus-consistency-check": { "alert-message": "", @@ -690,7 +785,10 @@ "title": "" }, "custom-value": { - "label": "" + "description": "" + }, + "group": { + "undefined": "" }, "options": { "no-found": "" @@ -725,6 +823,7 @@ "collapse": "", "edit": "", "help": "", + "loading": "", "locale": { "default": "Padrão" }, @@ -748,6 +847,25 @@ "placeholder": "Buscar tudo" } }, + "core": { + "versionHistory": { + "comparison": { + "header": { + "hide-json-diff": "", + "show-json-diff": "", + "text": "" + }, + "select": "" + }, + "no-properties-changed": "", + "table": { + "updated": "", + "updatedBy": "", + "version": "" + }, + "view-json-diff": "" + } + }, "correlations": { "add-new": "Adicionar novo", "alert": { @@ -863,6 +981,87 @@ "redirect-link": "Lista no alerta do Grafana", "subtitle": "Regras de alerta relacionadas a este painel de controle" }, + "default-layout": { + "description": "", + "item-options": { + "repeat": { + "direction": { + "horizontal": "", + "title": "", + "vertical": "" + }, + "max": "", + "title": "", + "variable": { + "description": "", + "title": "" + } + } + }, + "name": "", + "row-actions": { + "delete": "", + "modal": { + "alt-action": "", + "text": "", + "title": "" + } + }, + "row-options": { + "button": { + "label": "" + }, + "form": { + "cancel": "", + "repeat-for": { + "label": "", + "learn-more": "", + "warning": { + "text": "" + } + }, + "title": "", + "update": "" + }, + "modal": { + "title": "" + } + } + }, + "edit-pane": { + "objects": { + "multi-select": { + "selection-number": "" + } + }, + "open": "", + "panels": { + "multi-select": { + "selection-number": "" + } + }, + "row": { + "header": { + "hide": "", + "title": "" + }, + "multi-select": { + "options-header": "", + "selection-number": "" + } + }, + "tab": { + "multi-select": { + "options-header": "", + "selection-number": "" + } + } + }, + "editpane": { + "add": "", + "configure": "", + "outline": "" + }, "empty": { "add-library-panel-body": "Adicione visualizações que são compartilhadas com outros painéis de controle.", "add-library-panel-button": "Adicionar painel de biblioteca", @@ -874,6 +1073,9 @@ "import-a-dashboard-header": "Importar um painel de controle", "import-dashboard-button": "Importar painel de controle" }, + "errors": { + "failed-to-load": "" + }, "inspect": { "data-tab": "Dados", "error-tab": "Erro", @@ -926,19 +1128,134 @@ "rows": "Número total de linhas", "table-title": "Estatísticas" }, + "options": { + "description": "", + "title": "", + "title-option": "" + }, + "panel-edit": { + "alerting-tab": { + "dashboard-not-saved": "", + "no-rules": "" + } + }, + "responsive-layout": { + "description": "", + "item-options": { + "hide-no-data": "", + "title": "" + }, + "name": "", + "options": { + "columns": "", + "fixed": "", + "min": "", + "one-column": "", + "rows": "", + "three-columns": "", + "two-columns": "" + } + }, + "rows-layout": { + "description": "", + "name": "", + "row": { + "collapse": "", + "expand": "", + "new": "", + "repeat": { + "learn-more": "", + "warning": "" + } + }, + "row-options": { + "height": { + "expand": "", + "hide-row-header": "", + "min": "", + "title": "" + }, + "repeat": { + "title": "", + "variable": { + "title": "" + } + }, + "title": "", + "title-option": "" + } + }, + "tabs-layout": { + "description": "", + "name": "", + "tab": { + "new": "" + }, + "tab-options": { + "title": "", + "title-option": "" + } + }, "toolbar": { "add": "Adicionar", + "add-panel": "", + "add-panel-description": "", + "add-panel-lib": "", + "add-row": "", + "add-tab": "", "alert-rules": "Regras de alerta", + "back-to-dashboard": "", + "dashboard-settings": { + "label": "", + "tooltip": "" + }, + "discard-library-panel-changes": "", + "discard-panel": "", + "discard-panel-new": "", + "edit": { + "label": "", + "tooltip": "" + }, + "edit-dashboard-v2-schema": "", + "enter-edit-mode": { + "label": "", + "tooltip": "" + }, + "exit-edit-mode": { + "label": "", + "tooltip": "" + }, + "libray-panel-description": "", "mark-favorite": "Marcar como favorito", + "more-save-options": "", "open-original": "Abrir painel de controle original", "playlist-next": "Ir para o próximo painel de controle", "playlist-previous": "Ir para o painel de controle anterior", "playlist-stop": "Parar lista de reprodução", + "public-dashboard": "", "refresh": "Atualizar painel de controle", + "row-description": "", "save": "Salvar painel de controle", + "save-dashboard": { + "label": "", + "tooltip": "" + }, + "save-dashboard-copy": { + "label": "", + "tooltip": "" + }, + "save-dashboard-short": "", + "save-library-panel": "", "settings": "Configurações do painel de controle", - "share": "Compartilhar painel de controle", + "share": { + "label": "", + "tooltip": "" + }, "share-button": "Compartilhar", + "show-hidden-elements": "", + "switch-old-dashboard": "", + "tabs-description": "", + "unlink-library-panel": "", "unmark-favorite": "Desmarcar como favorito" }, "validation": { @@ -946,6 +1263,14 @@ "invalid-json": "JSON inválido", "tags-expected-array": "matriz de tags esperada", "tags-expected-strings": "matriz de strings de tags esperada" + }, + "viz-panel": { + "options": { + "description": "", + "title": "", + "title-option": "", + "transparent-background": "" + } } }, "dashboard-import": { @@ -1035,6 +1360,12 @@ "time-zone-label": "Fuso horário", "week-start-label": "Início da semana" }, + "time-regions": { + "advanced-description-cron": "", + "advanced-description-rest": "", + "advanced-description-use": "", + "advanced-label": "" + }, "variables": { "title": "Variáveis" }, @@ -1048,6 +1379,9 @@ "angular-deprecation-description": "", "angular-deprecation-heading": "" }, + "panel-queries": { + "add-query-from-library": "" + }, "settings": { "variables": { "dependencies": { @@ -1114,6 +1448,11 @@ }, "explore": { "add-to-dashboard": "Adicionar ao painel de controle", + "drilldownInfo": { + "action": "", + "description": "", + "title": "" + }, "logs": { "logs-volume": { "add-filters": "", @@ -1125,36 +1464,6 @@ "scan-for-older-logs": "", "stop-scan": "" }, - "query-library": { - "add-edit-description": "", - "cancel": "", - "default-description": "", - "delete-query": "", - "delete-query-text": "", - "delete-query-title": "", - "private": "", - "public": "", - "query-deleted": "", - "query-template-add-error": "", - "query-template-added": "", - "query-template-edit-error": "", - "query-template-edited": "", - "save": "" - }, - "query-template-modal": { - "add-info": "", - "add-title": "", - "auto-star": "", - "data-source-name": "", - "description": "", - "edit-info": "", - "edit-title": "", - "query": "", - "visibility": "" - }, - "query-template-modall": { - "data-source-type": "" - }, "rich-history": { "close-tooltip": "Fechar histórico de consultas", "datasource-a-z": "Fonte de dados A-Z", @@ -1262,6 +1571,7 @@ "switch-datasource-button": "" }, "secondary-actions": { + "add-from-query-library": "", "query-add-button": "Adicionar consulta", "query-add-button-aria-label": "Adicionar consulta", "query-history-button": "Histórico de consultas", @@ -1305,10 +1615,9 @@ "sortBy": "" }, "related-logs": { - "docsLink": "", + "LrrDocsLink": "", "openExploreLogs": "", - "relatedLogsUnavailableAfterDocsLink": "", - "relatedLogsUnavailableBeforeDocsLink": "", + "relatedLogsUnavailable": "", "warnExperimentalFeature": "" }, "viewBy": "" @@ -1356,7 +1665,29 @@ "incomplete-request-error": "", "send-custom-feedback": "" }, + "get-enterprise": { + "requires-license": "", + "title": "" + }, "grafana-ui": { + "action-editor": { + "button": { + "confirm": "", + "confirm-action": "" + }, + "inline": { + "add-action": "", + "edit-action": "" + }, + "modal": { + "action-body": "", + "action-method": "", + "action-query-params": "", + "action-title": "", + "action-title-placeholder": "", + "one-click-description": "" + } + }, "auto-save-field": { "saved": "", "saving": "" @@ -1373,11 +1704,21 @@ }, "data-link-editor-modal": { "cancel": "", + "one-click-description": "", "save": "" }, + "data-link-inline-editor": { + "one-click": "" + }, "data-links-inline-editor": { "add-link": "", - "one-click-link": "" + "edit-link": "", + "one-click": "", + "one-click-enabled": "", + "title-not-provided": "", + "tooltip-edit": "", + "tooltip-remove": "", + "url-not-provided": "" }, "data-source-http-settings": { "access-help": "", @@ -1409,6 +1750,12 @@ "drawer": { "close": "Fechar" }, + "feature-badge": { + "experimental": "", + "new": "", + "preview": "", + "private-preview": "" + }, "field-link-list": { "external-links-heading": "" }, @@ -1458,7 +1805,12 @@ "right-axis-indicator": "" }, "viz-tooltip": { - "footer-add-annotation": "" + "actions-confirmation-input-placeholder": "", + "actions-confirmation-label": "", + "actions-confirmation-message": "", + "footer-add-annotation": "", + "footer-click-to-action": "", + "footer-click-to-navigate": "" } }, "graph": { @@ -1726,6 +2078,7 @@ "render-image-error-description": "" } }, + "lock-icon": "", "login": { "divider": { "connecting-text": "" @@ -1770,6 +2123,10 @@ }, "logs": { "infinite-scroll": { + "end-of-range": "", + "load-more": "", + "load-newer": "", + "load-older": "", "older-logs": "" }, "log-details": { @@ -1780,7 +2137,8 @@ }, "log-row-message": { "ellipsis": "", - "more": "" + "more": "", + "see-details": "" }, "log-rows": { "disable-popover": { @@ -1792,6 +2150,13 @@ "shortcut": "" } }, + "logs-navigation": { + "newer-logs": "", + "older-logs": "", + "scroll-bottom": "", + "scroll-top": "", + "start-of-range": "" + }, "popover-menu": { "copy": "", "disable-menu": "", @@ -1881,6 +2246,7 @@ "title": "" }, "migrated-counts": { + "alert_rule_groups": "", "alert_rules": "", "contact_points": "", "dashboards": "", @@ -1941,9 +2307,8 @@ "public-preview": { "button-text": "", "message": "", - "message-plugins": "", - "title": "", - "title-plugins": "" + "message-cloud": "", + "title": "" }, "resource-details": { "dismiss-button": "", @@ -1986,6 +2351,7 @@ }, "resource-type": { "alert_rule": "", + "alert_rule_group": "", "contact_point": "", "dashboard": "Painel de controle", "datasource": "Fonte de dados", @@ -2031,6 +2397,15 @@ "title": "Por que hospedar com o Grafana?" } }, + "multicombobox": { + "all": { + "title": "", + "title-filtered": "" + }, + "clear": { + "title": "" + } + }, "nav": { "add-new-connections": { "title": "Adicionar nova conexão" @@ -2169,6 +2544,9 @@ "detect": { "title": "Detectar" }, + "drilldown": { + "title": "" + }, "explore": { "title": "Explorar" }, @@ -2206,6 +2584,8 @@ "history-wrapper": { "collapse": "", "expand": "", + "icon-selected": "", + "icon-unselected": "", "show-more": "", "today": "", "yesterday": "" @@ -2384,7 +2764,8 @@ "list-label": "Navegação", "open": "", "undock": "Desacoplar menu" - } + }, + "rss-button": "" }, "news": { "drawer": { @@ -2491,6 +2872,7 @@ }, "plugins": { "catalog": { + "no-updates-available": "", "update-all": { "all-plugins-updated": "", "available-header": "", @@ -2516,24 +2898,46 @@ } }, "details": { + "connections-tab": { + "description": "" + }, "labels": { "contactGrafanaLabs": "", + "customLinks": "", + "customLinksTooltip": "", "dependencies": "", + "documentation": "", "downloads": "", "from": "", "installedVersion": "", "lastCommitDate": "", "latestVersion": "", - "links": "", + "license": "", + "raiseAnIssue": "", "reportAbuse": "", + "reportAbuseTooltip": "", + "repository": "", "signature": "", "status": "", "updatedAt": "" + }, + "modal": { + "cancel": "", + "copyEmail": "", + "description": "", + "node": "", + "title": "" } }, "empty-state": { "message": "" }, + "filter": { + "disabled": "", + "sort": "", + "sort-list": "", + "state": "" + }, "plugin-help": { "error": "", "not-found": "" @@ -2743,14 +3147,6 @@ "role-label": "Função" } }, - "query-library": { - "datasource-names": "", - "delete-query-button": "", - "query-template-get-error": "", - "search": "", - "user-info-get-error": "", - "user-names": "" - }, "query-operation": { "header": { "collapse-row": "Recolher linha de consulta", @@ -2760,7 +3156,6 @@ "expand-row": "Expandir linha de consulta", "hide-response": "", "remove-query": "Remover consulta", - "save-to-query-library": "", "show-response": "", "toggle-edit-mode": "Alternar modo de edição de texto" }, @@ -2862,6 +3257,10 @@ "title": "" }, "save-dashboards": { + "message-length": { + "info": "", + "title": "" + }, "name-exists": { "message-info": "", "message-suggestion": "", @@ -3145,6 +3544,7 @@ "home-dashboard-placeholder": "Painel de controle padrão", "locale-label": "Idioma", "locale-placeholder": "Escolher idioma", + "theme-description": "", "theme-label": "Tema da interface", "week-start-label": "Início da semana" }, @@ -3216,6 +3616,13 @@ "url-column-header": "URL da captura", "view-button": "Visualizar" }, + "table": { + "container": { + "content": "", + "show-all-series": "", + "show-only-series": "" + } + }, "tag-filter": { "clear-button": "", "loading": "Carregando...", @@ -3309,20 +3716,26 @@ "start-your-metrics-exploration": "", "subtitle": "" }, - "metric-overview": { - "description-label": "", - "labels": "", - "metric-attributes": "", - "no-description": "", - "type-label": "", - "unit-label": "", - "unknown-type": "" - }, "metric-select": { "filter-by": "", + "native-histogram": "", "new-badge": "", "otel-switch": "" }, + "native-histogram-banner": { + "ch-heatmap": "", + "ch-histogram": "", + "click-histogram": "", + "hide-examples": "", + "learn-more": "", + "metric-examples": "", + "nh-heatmap": "", + "nh-histogram": "", + "now": "", + "previously": "", + "see-examples": "", + "sentence": "" + }, "recent-metrics": { "or-view-a-recent-exploration": "" }, diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json index c5cba66ace2..eb52a53913b 100644 --- a/public/locales/zh-Hans/grafana.json +++ b/public/locales/zh-Hans/grafana.json @@ -34,12 +34,6 @@ "save-button": "" } }, - "actions-editor": { - "inline": { - "add-button": "", - "one-click-link": "" - } - }, "admin": { "anon-users": { "not-found": "" @@ -174,6 +168,24 @@ } }, "alerting": { + "alert": { + "alert-state": "", + "annotations": "", + "evaluation": "", + "evaluation-paused": "", + "evaluation-paused-description": "", + "last-evaluated": "", + "last-evaluation-duration": "", + "last-updated-at": "", + "last-updated-by": "", + "no-annotations": "", + "pending-period": "", + "rule": "", + "rule-identifier": "", + "rule-type": "", + "state-error-timeout": "", + "state-no-data": "" + }, "alert-recording-rule-form": { "evaluation-behaviour": { "description": { @@ -193,6 +205,41 @@ "recording": "" } }, + "alertVersionHistory": { + "alerting": "", + "alerting-change-description": "", + "annotations": "", + "compareVersions": "", + "comparing-versions": "", + "condition": "", + "contactPointRouting": "", + "description": "", + "errorloading": "", + "execErrorState": "", + "intervalSeconds": "", + "labels": "", + "latest": "", + "name": "", + "namespace_uid": "", + "noDataState": "", + "noVersionsFound": "", + "paused": "", + "pendingPeriod": "", + "provisioning": "", + "provisioning-change-description": "", + "queryAndAlertCondition": "", + "reset": "", + "restore": "", + "restore-modal": { + "body": "", + "summary": "", + "title": "" + }, + "rule_group": "", + "unknown": "", + "unknown-change-description": "", + "user-id": "" + }, "annotations": { "description": "", "title": "" @@ -271,6 +318,9 @@ "no-delivery-attempts": "", "no-integrations": "", "only-firing": "", + "receiver-summary": { + "jira": "" + }, "telegram": { "parse-mode-warning-body": "", "parse-mode-warning-title": "" @@ -281,6 +331,11 @@ "contactPointFilter": { "label": "" }, + "copy-to-clipboard": "", + "dag": { + "missing-reference": "", + "self-reference": "" + }, "export": { "subtitle": { "formats": "", @@ -297,6 +352,13 @@ } } }, + "group-actions": { + "actions-trigger": "", + "delete": "", + "edit": "", + "export": "", + "reorder": "" + }, "list-view": { "empty": { "new-alert-rule": "", @@ -462,6 +524,19 @@ }, "pause": { "label": "" + }, + "threshold": { + "recovery": { + "stop-alerting-above": "", + "stop-alerting-bellow": "", + "stop-alerting-equal": "", + "stop-alerting-inside-range": "", + "stop-alerting-less": "", + "stop-alerting-more": "", + "stop-alerting-not-equal": "", + "stop-alerting-outside-range": "", + "title": "" + } } }, "rule-groups": { @@ -480,11 +555,23 @@ }, "rule-list": { "configure-datasource": "", + "ds-error-boundary": { + "description": "", + "title": "" + }, "filter-view": { "no-more-results": "", "no-rules-found": "" }, - "new-alert-rule": "" + "new-alert-rule": "", + "pagination": { + "next-page": "", + "previous-page": "" + }, + "return-button": { + "title": "" + }, + "rulerrule-loading-error": "" }, "rule-state": { "creating": "", @@ -492,6 +579,14 @@ "paused": "", "recording-rule": "" }, + "rule-view": { + "query": { + "datasources-na": { + "description": "", + "title": "" + } + } + }, "rule-viewer": { "prometheus-consistency-check": { "alert-message": "", @@ -681,7 +776,10 @@ "title": "" }, "custom-value": { - "label": "" + "description": "" + }, + "group": { + "undefined": "" }, "options": { "no-found": "" @@ -716,6 +814,7 @@ "collapse": "", "edit": "", "help": "", + "loading": "", "locale": { "default": "默认" }, @@ -739,6 +838,25 @@ "placeholder": "搜索全部" } }, + "core": { + "versionHistory": { + "comparison": { + "header": { + "hide-json-diff": "", + "show-json-diff": "", + "text": "" + }, + "select": "" + }, + "no-properties-changed": "", + "table": { + "updated": "", + "updatedBy": "", + "version": "" + }, + "view-json-diff": "" + } + }, "correlations": { "add-new": "添加新的", "alert": { @@ -854,6 +972,87 @@ "redirect-link": "Grafana Alerting 中的列表", "subtitle": "与此仪表板相关的警报规则" }, + "default-layout": { + "description": "", + "item-options": { + "repeat": { + "direction": { + "horizontal": "", + "title": "", + "vertical": "" + }, + "max": "", + "title": "", + "variable": { + "description": "", + "title": "" + } + } + }, + "name": "", + "row-actions": { + "delete": "", + "modal": { + "alt-action": "", + "text": "", + "title": "" + } + }, + "row-options": { + "button": { + "label": "" + }, + "form": { + "cancel": "", + "repeat-for": { + "label": "", + "learn-more": "", + "warning": { + "text": "" + } + }, + "title": "", + "update": "" + }, + "modal": { + "title": "" + } + } + }, + "edit-pane": { + "objects": { + "multi-select": { + "selection-number": "" + } + }, + "open": "", + "panels": { + "multi-select": { + "selection-number": "" + } + }, + "row": { + "header": { + "hide": "", + "title": "" + }, + "multi-select": { + "options-header": "", + "selection-number": "" + } + }, + "tab": { + "multi-select": { + "options-header": "", + "selection-number": "" + } + } + }, + "editpane": { + "add": "", + "configure": "", + "outline": "" + }, "empty": { "add-library-panel-body": "添加与其他仪表板共享的可视化。", "add-library-panel-button": "添加库面板", @@ -865,6 +1064,9 @@ "import-a-dashboard-header": "导入仪表板", "import-dashboard-button": "导入仪表板" }, + "errors": { + "failed-to-load": "" + }, "inspect": { "data-tab": "数据", "error-tab": "错误", @@ -917,19 +1119,134 @@ "rows": "总行数", "table-title": "统计信息" }, + "options": { + "description": "", + "title": "", + "title-option": "" + }, + "panel-edit": { + "alerting-tab": { + "dashboard-not-saved": "", + "no-rules": "" + } + }, + "responsive-layout": { + "description": "", + "item-options": { + "hide-no-data": "", + "title": "" + }, + "name": "", + "options": { + "columns": "", + "fixed": "", + "min": "", + "one-column": "", + "rows": "", + "three-columns": "", + "two-columns": "" + } + }, + "rows-layout": { + "description": "", + "name": "", + "row": { + "collapse": "", + "expand": "", + "new": "", + "repeat": { + "learn-more": "", + "warning": "" + } + }, + "row-options": { + "height": { + "expand": "", + "hide-row-header": "", + "min": "", + "title": "" + }, + "repeat": { + "title": "", + "variable": { + "title": "" + } + }, + "title": "", + "title-option": "" + } + }, + "tabs-layout": { + "description": "", + "name": "", + "tab": { + "new": "" + }, + "tab-options": { + "title": "", + "title-option": "" + } + }, "toolbar": { "add": "添加", + "add-panel": "", + "add-panel-description": "", + "add-panel-lib": "", + "add-row": "", + "add-tab": "", "alert-rules": "警报规则", + "back-to-dashboard": "", + "dashboard-settings": { + "label": "", + "tooltip": "" + }, + "discard-library-panel-changes": "", + "discard-panel": "", + "discard-panel-new": "", + "edit": { + "label": "", + "tooltip": "" + }, + "edit-dashboard-v2-schema": "", + "enter-edit-mode": { + "label": "", + "tooltip": "" + }, + "exit-edit-mode": { + "label": "", + "tooltip": "" + }, + "libray-panel-description": "", "mark-favorite": "标记为收藏", + "more-save-options": "", "open-original": "打开原始仪表板", "playlist-next": "前往下一个仪表板", "playlist-previous": "前往上一个仪表板", "playlist-stop": "停止播放列表", + "public-dashboard": "", "refresh": "刷新仪表板", + "row-description": "", "save": "保存仪表板", + "save-dashboard": { + "label": "", + "tooltip": "" + }, + "save-dashboard-copy": { + "label": "", + "tooltip": "" + }, + "save-dashboard-short": "", + "save-library-panel": "", "settings": "仪表板设置", - "share": "分享仪表板或面板", + "share": { + "label": "", + "tooltip": "" + }, "share-button": "分享", + "show-hidden-elements": "", + "switch-old-dashboard": "", + "tabs-description": "", + "unlink-library-panel": "", "unmark-favorite": "取消标记为收藏" }, "validation": { @@ -937,6 +1254,14 @@ "invalid-json": "无有效的 JSON", "tags-expected-array": "标签预期数组", "tags-expected-strings": "标签预期字符串数组" + }, + "viz-panel": { + "options": { + "description": "", + "title": "", + "title-option": "", + "transparent-background": "" + } } }, "dashboard-import": { @@ -1026,6 +1351,12 @@ "time-zone-label": "时区", "week-start-label": "每周开始日" }, + "time-regions": { + "advanced-description-cron": "", + "advanced-description-rest": "", + "advanced-description-use": "", + "advanced-label": "" + }, "variables": { "title": "变量" }, @@ -1039,6 +1370,9 @@ "angular-deprecation-description": "", "angular-deprecation-heading": "" }, + "panel-queries": { + "add-query-from-library": "" + }, "settings": { "variables": { "dependencies": { @@ -1105,6 +1439,11 @@ }, "explore": { "add-to-dashboard": "添加到仪表板", + "drilldownInfo": { + "action": "", + "description": "", + "title": "" + }, "logs": { "logs-volume": { "add-filters": "", @@ -1116,36 +1455,6 @@ "scan-for-older-logs": "", "stop-scan": "" }, - "query-library": { - "add-edit-description": "", - "cancel": "", - "default-description": "", - "delete-query": "", - "delete-query-text": "", - "delete-query-title": "", - "private": "", - "public": "", - "query-deleted": "", - "query-template-add-error": "", - "query-template-added": "", - "query-template-edit-error": "", - "query-template-edited": "", - "save": "" - }, - "query-template-modal": { - "add-info": "", - "add-title": "", - "auto-star": "", - "data-source-name": "", - "description": "", - "edit-info": "", - "edit-title": "", - "query": "", - "visibility": "" - }, - "query-template-modall": { - "data-source-type": "" - }, "rich-history": { "close-tooltip": "关闭查询历史记录", "datasource-a-z": "数据源 A-Z", @@ -1253,6 +1562,7 @@ "switch-datasource-button": "" }, "secondary-actions": { + "add-from-query-library": "", "query-add-button": "添加查询", "query-add-button-aria-label": "添加查询", "query-history-button": "查询历史记录", @@ -1296,10 +1606,9 @@ "sortBy": "" }, "related-logs": { - "docsLink": "", + "LrrDocsLink": "", "openExploreLogs": "", - "relatedLogsUnavailableAfterDocsLink": "", - "relatedLogsUnavailableBeforeDocsLink": "", + "relatedLogsUnavailable": "", "warnExperimentalFeature": "" }, "viewBy": "" @@ -1347,7 +1656,29 @@ "incomplete-request-error": "", "send-custom-feedback": "" }, + "get-enterprise": { + "requires-license": "", + "title": "" + }, "grafana-ui": { + "action-editor": { + "button": { + "confirm": "", + "confirm-action": "" + }, + "inline": { + "add-action": "", + "edit-action": "" + }, + "modal": { + "action-body": "", + "action-method": "", + "action-query-params": "", + "action-title": "", + "action-title-placeholder": "", + "one-click-description": "" + } + }, "auto-save-field": { "saved": "", "saving": "" @@ -1364,11 +1695,21 @@ }, "data-link-editor-modal": { "cancel": "", + "one-click-description": "", "save": "" }, + "data-link-inline-editor": { + "one-click": "" + }, "data-links-inline-editor": { "add-link": "", - "one-click-link": "" + "edit-link": "", + "one-click": "", + "one-click-enabled": "", + "title-not-provided": "", + "tooltip-edit": "", + "tooltip-remove": "", + "url-not-provided": "" }, "data-source-http-settings": { "access-help": "", @@ -1400,6 +1741,12 @@ "drawer": { "close": "关闭" }, + "feature-badge": { + "experimental": "", + "new": "", + "preview": "", + "private-preview": "" + }, "field-link-list": { "external-links-heading": "" }, @@ -1449,7 +1796,12 @@ "right-axis-indicator": "" }, "viz-tooltip": { - "footer-add-annotation": "" + "actions-confirmation-input-placeholder": "", + "actions-confirmation-label": "", + "actions-confirmation-message": "", + "footer-add-annotation": "", + "footer-click-to-action": "", + "footer-click-to-navigate": "" } }, "graph": { @@ -1716,6 +2068,7 @@ "render-image-error-description": "" } }, + "lock-icon": "", "login": { "divider": { "connecting-text": "" @@ -1760,6 +2113,10 @@ }, "logs": { "infinite-scroll": { + "end-of-range": "", + "load-more": "", + "load-newer": "", + "load-older": "", "older-logs": "" }, "log-details": { @@ -1770,7 +2127,8 @@ }, "log-row-message": { "ellipsis": "", - "more": "" + "more": "", + "see-details": "" }, "log-rows": { "disable-popover": { @@ -1782,6 +2140,13 @@ "shortcut": "" } }, + "logs-navigation": { + "newer-logs": "", + "older-logs": "", + "scroll-bottom": "", + "scroll-top": "", + "start-of-range": "" + }, "popover-menu": { "copy": "", "disable-menu": "", @@ -1871,6 +2236,7 @@ "title": "" }, "migrated-counts": { + "alert_rule_groups": "", "alert_rules": "", "contact_points": "", "dashboards": "", @@ -1931,9 +2297,8 @@ "public-preview": { "button-text": "", "message": "", - "message-plugins": "", - "title": "", - "title-plugins": "" + "message-cloud": "", + "title": "" }, "resource-details": { "dismiss-button": "", @@ -1976,6 +2341,7 @@ }, "resource-type": { "alert_rule": "", + "alert_rule_group": "", "contact_point": "", "dashboard": "仪表板", "datasource": "数据源", @@ -2021,6 +2387,15 @@ "title": "为什么使用 Grafana 托管?" } }, + "multicombobox": { + "all": { + "title": "", + "title-filtered": "" + }, + "clear": { + "title": "" + } + }, "nav": { "add-new-connections": { "title": "添加新连接" @@ -2159,6 +2534,9 @@ "detect": { "title": "检测" }, + "drilldown": { + "title": "" + }, "explore": { "title": "探索" }, @@ -2196,6 +2574,8 @@ "history-wrapper": { "collapse": "", "expand": "", + "icon-selected": "", + "icon-unselected": "", "show-more": "", "today": "", "yesterday": "" @@ -2374,7 +2754,8 @@ "list-label": "导航", "open": "", "undock": "取消停靠菜单" - } + }, + "rss-button": "" }, "news": { "drawer": { @@ -2481,6 +2862,7 @@ }, "plugins": { "catalog": { + "no-updates-available": "", "update-all": { "all-plugins-updated": "", "available-header": "", @@ -2506,24 +2888,46 @@ } }, "details": { + "connections-tab": { + "description": "" + }, "labels": { "contactGrafanaLabs": "", + "customLinks": "", + "customLinksTooltip": "", "dependencies": "", + "documentation": "", "downloads": "", "from": "", "installedVersion": "", "lastCommitDate": "", "latestVersion": "", - "links": "", + "license": "", + "raiseAnIssue": "", "reportAbuse": "", + "reportAbuseTooltip": "", + "repository": "", "signature": "", "status": "", "updatedAt": "" + }, + "modal": { + "cancel": "", + "copyEmail": "", + "description": "", + "node": "", + "title": "" } }, "empty-state": { "message": "" }, + "filter": { + "disabled": "", + "sort": "", + "sort-list": "", + "state": "" + }, "plugin-help": { "error": "", "not-found": "" @@ -2732,14 +3136,6 @@ "role-label": "角色" } }, - "query-library": { - "datasource-names": "", - "delete-query-button": "", - "query-template-get-error": "", - "search": "", - "user-info-get-error": "", - "user-names": "" - }, "query-operation": { "header": { "collapse-row": "折叠查询行", @@ -2749,7 +3145,6 @@ "expand-row": "展开查询行", "hide-response": "", "remove-query": "删除查询", - "save-to-query-library": "", "show-response": "", "toggle-edit-mode": "切换文本编辑模式" }, @@ -2848,6 +3243,10 @@ "title": "" }, "save-dashboards": { + "message-length": { + "info": "", + "title": "" + }, "name-exists": { "message-info": "", "message-suggestion": "", @@ -3131,6 +3530,7 @@ "home-dashboard-placeholder": "默认仪表板", "locale-label": "语言", "locale-placeholder": "选择语言", + "theme-description": "", "theme-label": "UI 主题", "week-start-label": "每周开始日" }, @@ -3202,6 +3602,13 @@ "url-column-header": "快照网址", "view-button": "查看" }, + "table": { + "container": { + "content": "", + "show-all-series": "", + "show-only-series": "" + } + }, "tag-filter": { "clear-button": "", "loading": "加载中...", @@ -3295,20 +3702,26 @@ "start-your-metrics-exploration": "", "subtitle": "" }, - "metric-overview": { - "description-label": "", - "labels": "", - "metric-attributes": "", - "no-description": "", - "type-label": "", - "unit-label": "", - "unknown-type": "" - }, "metric-select": { "filter-by": "", + "native-histogram": "", "new-badge": "", "otel-switch": "" }, + "native-histogram-banner": { + "ch-heatmap": "", + "ch-histogram": "", + "click-histogram": "", + "hide-examples": "", + "learn-more": "", + "metric-examples": "", + "nh-heatmap": "", + "nh-histogram": "", + "now": "", + "previously": "", + "see-examples": "", + "sentence": "" + }, "recent-metrics": { "or-view-a-recent-exploration": "" }, diff --git a/public/openapi3.json b/public/openapi3.json index b8c9a75b21d..8f339a39b59 100644 --- a/public/openapi3.json +++ b/public/openapi3.json @@ -2956,6 +2956,9 @@ "format": "double", "type": "number" }, + "folderUid": { + "type": "string" + }, "health": { "type": "string" }, @@ -2995,10 +2998,15 @@ }, "type": { "type": "string" + }, + "uid": { + "type": "string" } }, "required": [ + "uid", "name", + "folderUid", "query", "health", "type", @@ -3949,6 +3957,20 @@ }, "type": "array" }, + "ConvertPrometheusResponse": { + "properties": { + "error": { + "type": "string" + }, + "errorType": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "type": "object" + }, "CookiePreferences": { "properties": { "analytics": {}, @@ -5465,6 +5487,14 @@ "CounterResetHint": { "$ref": "#/components/schemas/CounterResetHint" }, + "CustomValues": { + "description": "Holds the custom (usually upper) bounds for bucket definitions, otherwise nil.\nThis slice is interned, to be treated as immutable and copied by reference.\nThese numbers should be strictly increasing. This field is only used when the\nschema is for custom buckets, and the ZeroThreshold, ZeroCount, NegativeSpans\nand NegativeBuckets fields are not used in that case.", + "items": { + "format": "double", + "type": "number" + }, + "type": "array" + }, "PositiveBuckets": { "description": "Observation counts in buckets. Each represents an absolute count and\nmust be zero or positive.", "items": { @@ -5481,7 +5511,7 @@ "type": "array" }, "Schema": { - "description": "Currently valid schema numbers are -4 \u003c= n \u003c= 8. They are all for\nbase-2 bucket schemas, where 1 is a bucket boundary in each case, and\nthen each power of two is divided into 2^n logarithmic buckets. Or\nin other words, each bucket boundary is the previous boundary times\n2^(2^-n).", + "description": "Currently valid schema numbers are -4 \u003c= n \u003c= 8 for exponential buckets.\nThey are all for base-2 bucket schemas, where 1 is a bucket boundary in\neach case, and then each power of two is divided into 2^n logarithmic buckets.\nOr in other words, each bucket boundary is the previous boundary times\n2^(2^-n). Another valid schema number is -53 for custom buckets, defined by\nthe CustomValues field.", "format": "int32", "type": "integer" }, @@ -5551,6 +5581,10 @@ }, "type": "array" }, + "repository": { + "description": "When the folder belongs to a repository\nNOTE: this is only populated when folders are managed by unified storage", + "type": "string" + }, "title": { "type": "string" }, @@ -5583,6 +5617,10 @@ "parentUid": { "type": "string" }, + "repository": { + "description": "When the folder belongs to a repository\nNOTE: this is only populated when folders are managed by unified storage", + "type": "string" + }, "title": { "type": "string" }, @@ -6063,10 +6101,6 @@ ], "type": "string" }, - "id": { - "format": "int64", - "type": "integer" - }, "intervalSeconds": { "format": "int64", "type": "integer" @@ -6091,10 +6125,6 @@ "notification_settings": { "$ref": "#/components/schemas/AlertRuleNotificationSettings" }, - "orgId": { - "format": "int64", - "type": "integer" - }, "provenance": { "$ref": "#/components/schemas/Provenance" }, @@ -6114,6 +6144,9 @@ "format": "date-time", "type": "string" }, + "updated_by": { + "$ref": "#/components/schemas/UserInfo" + }, "version": { "format": "int64", "type": "integer" @@ -6198,6 +6231,12 @@ }, "type": "object" }, + "GettableRuleVersions": { + "items": { + "$ref": "#/components/schemas/GettableExtendedRuleNode" + }, + "type": "array" + }, "GettableStatus": { "properties": { "cluster": { @@ -7225,6 +7264,7 @@ "FOLDER", "LIBRARY_ELEMENT", "ALERT_RULE", + "ALERT_RULE_GROUP", "CONTACT_POINT", "NOTIFICATION_POLICY", "NOTIFICATION_TEMPLATE", @@ -8569,6 +8609,21 @@ }, "type": "object" }, + "PrometheusNamespace": { + "properties": { + "Body": { + "additionalProperties": { + "items": { + "$ref": "#/components/schemas/PrometheusRuleGroup" + }, + "type": "array" + }, + "description": "in: body", + "type": "object" + } + }, + "type": "object" + }, "PrometheusRemoteWriteTargetJSON": { "properties": { "data_source_uid": { @@ -8583,6 +8638,55 @@ }, "type": "object" }, + "PrometheusRule": { + "properties": { + "Alert": { + "type": "string" + }, + "Annotations": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "Expr": { + "type": "string" + }, + "For": { + "type": "string" + }, + "KeepFiringFor": { + "type": "string" + }, + "Labels": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "Record": { + "type": "string" + } + }, + "type": "object" + }, + "PrometheusRuleGroup": { + "properties": { + "Interval": { + "$ref": "#/components/schemas/Duration" + }, + "Name": { + "type": "string" + }, + "Rules": { + "items": { + "$ref": "#/components/schemas/PrometheusRule" + }, + "type": "array" + } + }, + "type": "object" + }, "Provenance": { "type": "string" }, @@ -9880,6 +9984,9 @@ "format": "double", "type": "number" }, + "folderUid": { + "type": "string" + }, "health": { "type": "string" }, @@ -9901,10 +10008,15 @@ }, "type": { "type": "string" + }, + "uid": { + "type": "string" } }, "required": [ + "uid", "name", + "folderUid", "query", "health", "type" @@ -9944,6 +10056,9 @@ "file": { "type": "string" }, + "folderUid": { + "type": "string" + }, "interval": { "format": "double", "type": "number" @@ -9973,6 +10088,7 @@ "required": [ "name", "file", + "folderUid", "rules", "interval" ], @@ -10075,6 +10191,10 @@ "Sample": { "description": "Sample is a single sample belonging to a metric. It represents either a float\nsample or a histogram sample. If H is nil, it is a float sample. Otherwise,\nit is a histogram sample.", "properties": { + "DropName": { + "description": "DropName is used to indicate whether the __name__ label should be dropped\nas part of the query evaluation.", + "type": "boolean" + }, "F": { "format": "double", "type": "number" @@ -12098,6 +12218,18 @@ }, "type": "object" }, + "UserInfo": { + "properties": { + "name": { + "type": "string" + }, + "uid": { + "type": "string" + } + }, + "title": "UserInfo represents user-related information, including a unique identifier and a name.", + "type": "object" + }, "UserLookupDTO": { "properties": { "avatarUrl": { @@ -15183,7 +15315,7 @@ } }, { - "description": "Find annotations for a specified alert.", + "description": "Find annotations for a specified alert rule by its ID.\ndeprecated: AlertID is deprecated and will be removed in future versions. Please use AlertUID instead.", "in": "query", "name": "alertId", "schema": { @@ -15191,6 +15323,14 @@ "type": "integer" } }, + { + "description": "Find annotations for a specified alert rule by its UID.", + "in": "query", + "name": "alertUID", + "schema": { + "type": "string" + } + }, { "description": "Find annotations that are scoped to a specific dashboard", "in": "query", diff --git a/public/sass/_variables.light.generated.scss b/public/sass/_variables.light.generated.scss index 3ff0e66fab2..1781e899712 100644 --- a/public/sass/_variables.light.generated.scss +++ b/public/sass/_variables.light.generated.scss @@ -138,7 +138,7 @@ $alert-warning-bg: #ff9900; $alert-info-bg: #ff9900; // Tooltips and popovers -$tooltipBackground: #f4f5f5; +$tooltipBackground: #ffffff; $tooltipColor: rgba(36, 41, 46, 1); $popover-bg: #ffffff; diff --git a/public/test/helpers/alertingRuleEditor.tsx b/public/test/helpers/alertingRuleEditor.tsx index 39898405bdd..4b50dce768e 100644 --- a/public/test/helpers/alertingRuleEditor.tsx +++ b/public/test/helpers/alertingRuleEditor.tsx @@ -4,7 +4,8 @@ import { byRole, byTestId, byText } from 'testing-library-selector'; import { selectors } from '@grafana/e2e-selectors'; import { AppNotificationList } from 'app/core/components/AppNotifications/AppNotificationList'; -import RuleEditor from 'app/features/alerting/unified/RuleEditor'; +import RuleEditor from 'app/features/alerting/unified/rule-editor/RuleEditor'; + export enum GrafanaRuleFormStep { Query = 2, Notification = 5, @@ -31,8 +32,10 @@ export const ui = { contactPoint: byTestId('contact-point-picker'), routingOptions: byText(/muting, grouping and timings \(optional\)/i), }, - switchModeBasic: (stepNo: GrafanaRuleFormStep) => byTestId(`advanced-switch-${stepNo}-basic`), - switchModeAdvanced: (stepNo: GrafanaRuleFormStep) => byTestId(`advanced-switch-${stepNo}-advanced`), + switchModeBasic: (stepNo: GrafanaRuleFormStep) => + byTestId(selectors.components.AlertRules.stepAdvancedModeSwitch(stepNo.toString())), + switchModeAdvanced: (stepNo: GrafanaRuleFormStep) => + byTestId(selectors.components.AlertRules.stepAdvancedModeSwitch(stepNo.toString())), }, buttons: { saveAndExit: byRole('button', { name: 'Save rule and exit' }), diff --git a/public/views/index.html b/public/views/index.html index 54069a7e67c..cd539b05c9e 100644 --- a/public/views/index.html +++ b/public/views/index.html @@ -33,6 +33,7 @@ performance.mark('frontend_boot_css_time_seconds'); + diff --git a/scripts/build/publish.sh b/scripts/build/publish.sh deleted file mode 100755 index 5da55e36b49..00000000000 --- a/scripts/build/publish.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh - -# no relation to publish.go - -# shellcheck disable=SC2124 - -EXTRA_OPTS="$@" - -# Right now we hack this in into the publish script. -# Eventually we might want to keep a list of all previous releases somewhere. -_releaseNoteUrl="https://community.grafana.com/t/release-notes-v7-0-x/29381" -_whatsNewUrl="https://grafana.com/docs/grafana/latest/guides/whats-new-in-v7-0/" - -./scripts/build/release_publisher/release_publisher \ - --wn "${_whatsNewUrl}" \ - --rn "${_releaseNoteUrl}" \ - --version "${CIRCLE_TAG}" \ - --apikey "${GRAFANA_COM_API_KEY}" "${EXTRA_OPTS}" diff --git a/scripts/build/release_publisher/externalrelease.go b/scripts/build/release_publisher/externalrelease.go deleted file mode 100644 index 31c2daf15ca..00000000000 --- a/scripts/build/release_publisher/externalrelease.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "fmt" - "io" - "net/http" - "strings" - "time" -) - -type releaseFromExternalContent struct { - getter urlGetter - rawVersion string - artifactConfigurations []buildArtifact -} - -func (re releaseFromExternalContent) prepareRelease(baseArchiveURL, whatsNewURL string, releaseNotesURL string, nightly bool) (*release, error) { - version := re.rawVersion[1:] - beta := strings.Contains(version, "beta") - var rt releaseType - if beta { - rt = BETA - } else if nightly { - rt = NIGHTLY - } else { - rt = STABLE - } - - builds := []build{} - for _, ba := range re.artifactConfigurations { - url := ba.getURL(baseArchiveURL, version, rt) - sha256, err := re.getter.getContents(fmt.Sprintf("%s.sha256", url)) - if err != nil { - return nil, err - } - builds = append(builds, newBuild(url, ba, sha256)) - } - - r := release{ - Version: version, - ReleaseDate: time.Now().UTC(), - Stable: rt.stable(), - Beta: rt.beta(), - Nightly: rt.nightly(), - WhatsNewURL: whatsNewURL, - ReleaseNotesURL: releaseNotesURL, - Builds: builds, - } - return &r, nil -} - -type urlGetter interface { - getContents(url string) (string, error) -} - -type getHTTPContents struct{} - -func (getHTTPContents) getContents(url string) (string, error) { - response, err := http.Get(url) - if err != nil { - return "", err - } - - defer response.Body.Close() - all, err := io.ReadAll(response.Body) - if err != nil { - return "", err - } - - return string(all), nil -} diff --git a/scripts/build/release_publisher/main.go b/scripts/build/release_publisher/main.go deleted file mode 100644 index 97e70f5465f..00000000000 --- a/scripts/build/release_publisher/main.go +++ /dev/null @@ -1,80 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - "os" -) - -func main() { - var version string - var whatsNewURL string - var releaseNotesURL string - var dryRun bool - var enterprise bool - var nightly bool - var apiKey string - - flag.StringVar(&version, "version", "", "Grafana version (ex: --version v5.2.0-beta1)") - flag.StringVar(&whatsNewURL, "wn", "", "What's new url (ex: --wn http://docs.grafana.org/guides/whats-new-in-v5-2/)") - flag.StringVar(&releaseNotesURL, "rn", "", "Grafana version (ex: --rn https://community.grafana.com/t/release-notes-v5-2-x/7894)") - flag.StringVar(&apiKey, "apikey", "", "Grafana.com API key (ex: --apikey ABCDEF)") - flag.BoolVar(&dryRun, "dry-run", false, "--dry-run") - flag.BoolVar(&enterprise, "enterprise", false, "--enterprise") - flag.BoolVar(&nightly, "nightly", false, "--nightly (default: false)") - flag.Parse() - - if len(os.Args) == 1 { - fmt.Println("Usage: go run publisher.go main.go --version --wn --rn --apikey --dry-run false --enterprise false --nightly false") - fmt.Println("example: go run publisher.go main.go --version v5.2.0-beta2 --wn http://docs.grafana.org/guides/whats-new-in-v5-2/ --rn https://community.grafana.com/t/release-notes-v5-2-x/7894 --apikey ASDF123 --dry-run --enterprise") - os.Exit(1) - } - - if dryRun { - log.Println("Dry-run has been enabled.") - } - var baseURL string - var builder releaseBuilder - var product string - - archiveProviderRoot := "https://dl.grafana.com" - buildArtifacts := completeBuildArtifactConfigurations - - if enterprise { - product = "grafana-enterprise" - baseURL = createBaseURL(archiveProviderRoot, "enterprise", product, nightly) - } else { - product = "grafana" - baseURL = createBaseURL(archiveProviderRoot, "oss", product, nightly) - } - - builder = releaseFromExternalContent{ - getter: getHTTPContents{}, - rawVersion: version, - artifactConfigurations: buildArtifacts, - } - - p := publisher{ - apiKey: apiKey, - apiURI: "https://grafana.com/api", - product: product, - dryRun: dryRun, - enterprise: enterprise, - baseArchiveURL: baseURL, - builder: builder, - } - if err := p.doRelease(whatsNewURL, releaseNotesURL, nightly); err != nil { - log.Fatalf("error: %v", err) - } -} -func createBaseURL(root string, bucketName string, product string, nightly bool) string { - var subPath string - if nightly { - subPath = "main" - } else { - subPath = "release" - } - - return fmt.Sprintf("%s/%s/%s/%s", root, bucketName, subPath, product) -} diff --git a/scripts/build/release_publisher/publisher_test.go b/scripts/build/release_publisher/publisher_test.go deleted file mode 100644 index d31d161d21a..00000000000 --- a/scripts/build/release_publisher/publisher_test.go +++ /dev/null @@ -1,201 +0,0 @@ -package main - -import ( - "reflect" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPreparingReleaseFromRemote(t *testing.T) { - cases := []struct { - version string - expectedVersion string - whatsNewURL string - relNotesURL string - nightly bool - expectedBeta bool - expectedStable bool - expectedArch string - expectedOs string - expectedURL string - baseArchiveURL string - buildArtifacts []buildArtifact - }{ - { - version: "v5.2.0-beta1", - expectedVersion: "5.2.0-beta1", - whatsNewURL: "https://whatsnews.foo/", - relNotesURL: "https://relnotes.foo/", - nightly: false, - expectedBeta: true, - expectedStable: false, - expectedArch: "amd64", - expectedOs: "linux", - expectedURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.2.0-beta1.linux-amd64.tar.gz", - baseArchiveURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana", - buildArtifacts: []buildArtifact{{"linux", "amd64", ".linux-amd64.tar.gz", ""}}, - }, - { - version: "v5.2.3", - expectedVersion: "5.2.3", - whatsNewURL: "https://whatsnews.foo/", - relNotesURL: "https://relnotes.foo/", - nightly: false, - expectedBeta: false, - expectedStable: true, - expectedArch: "amd64", - expectedOs: "rhel", - expectedURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.2.3-1.x86_64.rpm", - baseArchiveURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana", - buildArtifacts: []buildArtifact{{"rhel", "amd64", ".x86_64.rpm", ""}}, - }, - { - version: "v5.4.0-pre1asdf", - expectedVersion: "5.4.0-pre1asdf", - whatsNewURL: "https://whatsnews.foo/", - relNotesURL: "https://relnotes.foo/", - nightly: true, - expectedBeta: false, - expectedStable: false, - expectedArch: "amd64", - expectedOs: "rhel", - expectedURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.4.0~pre1asdf-1.x86_64.rpm", - baseArchiveURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana", - buildArtifacts: []buildArtifact{{"rhel", "amd64", ".x86_64.rpm", ""}}, - }, - { - version: "v5.4.0-pre1asdf", - expectedVersion: "5.4.0-pre1asdf", - whatsNewURL: "https://whatsnews.foo/", - relNotesURL: "https://relnotes.foo/", - nightly: true, - expectedBeta: false, - expectedStable: false, - expectedArch: "armv6", - expectedOs: "deb", - expectedURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-rpi_5.4.0~pre1asdf_armhf.deb", - baseArchiveURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana", - buildArtifacts: []buildArtifact{ - {os: "deb", arch: "armv6", urlPostfix: "_armhf.deb", packagePostfix: "-rpi"}, - }, - }, - { - version: "v5.4.0-pre1asdf", - expectedVersion: "5.4.0-pre1asdf", - whatsNewURL: "https://whatsnews.foo/", - relNotesURL: "https://relnotes.foo/", - nightly: true, - expectedBeta: false, - expectedStable: false, - expectedArch: "amd64", - expectedOs: "win-installer", - expectedURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.4.0-pre1asdf.windows-amd64.msi", - baseArchiveURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana", - buildArtifacts: []buildArtifact{{"win-installer", "amd64", ".windows-amd64.msi", ""}}, - }, - { - version: "v5.4.0-pre1asdf", - expectedVersion: "5.4.0-pre1asdf", - whatsNewURL: "https://whatsnews.foo/", - relNotesURL: "https://relnotes.foo/", - nightly: true, - expectedBeta: false, - expectedStable: false, - expectedArch: "amd64", - expectedOs: "win", - expectedURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.4.0-pre1asdf.windows-amd64.zip", - baseArchiveURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana", - buildArtifacts: []buildArtifact{{"win", "amd64", ".windows-amd64.zip", ""}}, - }, - } - - for _, test := range cases { - builder := releaseFromExternalContent{ - getter: mockHTTPGetter{}, - rawVersion: test.version, - artifactConfigurations: test.buildArtifacts, - } - - t.Log("Preparing release", "baseArchiveURL", test.baseArchiveURL, "nightly", test.nightly) - rel, err := builder.prepareRelease(test.baseArchiveURL, test.whatsNewURL, test.relNotesURL, test.nightly) - require.NoError(t, err) - - assert.Equal(t, test.expectedBeta, rel.Beta) - assert.Equal(t, test.expectedStable, rel.Stable) - assert.Equal(t, test.expectedVersion, rel.Version) - - assert.Len(t, rel.Builds, len(test.buildArtifacts)) - - build := rel.Builds[0] - assert.Equal(t, test.expectedArch, build.Arch) - assert.Equal(t, test.expectedOs, build.Os) - assert.Equal(t, test.expectedURL, build.URL) - } -} - -type mockHTTPGetter struct{} - -func (mockHTTPGetter) getContents(url string) (string, error) { - return url, nil -} - -func TestFilterBuildArtifacts(t *testing.T) { - buildArtifacts, _ := filterBuildArtifacts(completeBuildArtifactConfigurations, Add, []artifactFilter{ - {os: "deb", arch: "amd64"}, - {os: "rhel", arch: "amd64"}, - {os: "linux", arch: "amd64"}, - {os: "win", arch: "amd64"}, - }) - - if len(buildArtifacts) != 4 { - t.Errorf("Expected 4 build artifacts after filtering, but was %v", len(buildArtifacts)) - } - - buildArtifacts, err := filterBuildArtifacts([]buildArtifact{ - { - os: "linux", - arch: "amd64", - }, - { - os: "arm", - arch: "amd64", - }, - { - os: "darwin", - arch: "amd64", - }, - }, Remove, []artifactFilter{ - {os: "darwin", arch: "amd64"}, - }) - - if err != nil { - t.Error() - } - - if len(buildArtifacts) != 2 { - t.Errorf("Expected 2 artifacts, was %v", len(buildArtifacts)) - } - - for _, ba := range buildArtifacts { - if ba.arch == "amd64" && ba.os == "darwin" { - t.Errorf("darwin/amd64 should be gone due to filtering") - } - } - - left := []buildArtifact{ - { - os: "linux", - arch: "amd64", - }, - { - os: "arm", - arch: "amd64", - }, - } - - if !reflect.DeepEqual(left, buildArtifacts) { - t.Errorf("Lists should have been equal but was, expected=%v, actual=%v", left, buildArtifacts) - } -} diff --git a/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.linux-amd64.tar.gz.sha256 b/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.linux-amd64.tar.gz.sha256 deleted file mode 100644 index c3068040269..00000000000 --- a/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.linux-amd64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 diff --git a/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.windows-amd64.msi.sha256 b/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.windows-amd64.msi.sha256 deleted file mode 100644 index c3068040269..00000000000 --- a/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.windows-amd64.msi.sha256 +++ /dev/null @@ -1 +0,0 @@ -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 diff --git a/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.windows-amd64.zip.sha256 b/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.windows-amd64.zip.sha256 deleted file mode 100644 index c3068040269..00000000000 --- a/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.windows-amd64.zip.sha256 +++ /dev/null @@ -1 +0,0 @@ -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 diff --git a/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.x86_64.rpm b/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.x86_64.rpm deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.x86_64.rpm.sha256 b/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.x86_64.rpm.sha256 deleted file mode 100644 index c3068040269..00000000000 --- a/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.x86_64.rpm.sha256 +++ /dev/null @@ -1 +0,0 @@ -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 diff --git a/scripts/build/release_publisher/testdata/grafana-enterprise_5.4.0-123pre1_amd64.deb b/scripts/build/release_publisher/testdata/grafana-enterprise_5.4.0-123pre1_amd64.deb deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/scripts/build/release_publisher/testdata/grafana-enterprise_5.4.0-123pre1_amd64.deb.sha256 b/scripts/build/release_publisher/testdata/grafana-enterprise_5.4.0-123pre1_amd64.deb.sha256 deleted file mode 100644 index c3068040269..00000000000 --- a/scripts/build/release_publisher/testdata/grafana-enterprise_5.4.0-123pre1_amd64.deb.sha256 +++ /dev/null @@ -1 +0,0 @@ -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 diff --git a/scripts/cli/reportBettererStats.mjs b/scripts/cli/reportBettererStats.ts similarity index 74% rename from scripts/cli/reportBettererStats.mjs rename to scripts/cli/reportBettererStats.ts index 0f1eda1d646..135f98c7752 100644 --- a/scripts/cli/reportBettererStats.mjs +++ b/scripts/cli/reportBettererStats.ts @@ -1,8 +1,7 @@ -// @ts-check import { betterer } from '@betterer/betterer'; -import _ from 'lodash'; +import { camelCase } from 'lodash'; -function logStat(name, value) { +function logStat(name: string, value: number) { // Note that this output format must match the parsing in ci-frontend-metrics.sh // which expects the two values to be separated by a space console.log(`${name} ${value}`); @@ -13,11 +12,11 @@ async function main() { for (const testResults of results.resultSummaries) { const countByMessage = {}; - const name = _.camelCase(testResults.name); + const name = camelCase(testResults.name); Object.values(testResults.details) .flatMap((v) => v) .forEach((detail) => { - const message = _.camelCase(detail.message); + const message = camelCase(detail.message); const metricName = `${name}_${message}`; if (metricName in countByMessage) { countByMessage[metricName]++; @@ -26,7 +25,7 @@ async function main() { } }); - for (const [metricName, count] of Object.entries(countByMessage)) { + for (const [metricName, count] of Object.entries(countByMessage)) { logStat(metricName, count); } } diff --git a/scripts/cli/tsconfig.json b/scripts/cli/tsconfig.json index 9ad59a649d1..0d18e875c5e 100644 --- a/scripts/cli/tsconfig.json +++ b/scripts/cli/tsconfig.json @@ -6,6 +6,9 @@ "extends": "../../tsconfig.json", "ts-node": { "transpileOnly": true, - "swc": true + "swc": true, + "compilerOptions": { + "module": "commonjs" + } } } diff --git a/scripts/drone/events/pr.star b/scripts/drone/events/pr.star index 9a57ec4cc53..7d42325bef3 100644 --- a/scripts/drone/events/pr.star +++ b/scripts/drone/events/pr.star @@ -116,6 +116,7 @@ def pr_pipelines(): "public/app/plugins/**/plugin.json", "docs/sources/setup-grafana/configure-grafana/feature-toggles/**", "devenv/**", + "apps/**", ], ), ver_mode, @@ -134,6 +135,7 @@ def pr_pipelines(): "public/app/plugins/**/plugin.json", "devenv/**", ".bingo/**", + "apps/**", ], ), ver_mode, diff --git a/scripts/drone/pipelines/lint_frontend.star b/scripts/drone/pipelines/lint_frontend.star index 1f98e371bb9..381f79373db 100644 --- a/scripts/drone/pipelines/lint_frontend.star +++ b/scripts/drone/pipelines/lint_frontend.star @@ -12,6 +12,7 @@ load( "enterprise_setup_step", "identify_runner_step", "lint_frontend_step", + "verify_api_clients_step", "verify_i18n_step", "yarn_install_step", ) @@ -35,6 +36,7 @@ def lint_frontend_pipeline(trigger, ver_mode): init_steps = [] lint_step = lint_frontend_step() i18n_step = verify_i18n_step() + api_clients_step = verify_api_clients_step() volumes = [] @@ -54,6 +56,7 @@ def lint_frontend_pipeline(trigger, ver_mode): test_steps = [ lint_step, i18n_step, + api_clients_step, ] return pipeline( diff --git a/scripts/drone/pipelines/whats_new_checker.star b/scripts/drone/pipelines/whats_new_checker.star deleted file mode 100644 index 2c56be573ce..00000000000 --- a/scripts/drone/pipelines/whats_new_checker.star +++ /dev/null @@ -1,42 +0,0 @@ -""" -This module contains logic for checking if the package.json whats new url matches with the in-flight tag. -""" - -load( - "scripts/drone/steps/lib.star", - "compile_build_cmd", -) -load( - "scripts/drone/utils/images.star", - "images", -) -load( - "scripts/drone/utils/utils.star", - "pipeline", -) - -def whats_new_checker_step(): - return { - "name": "whats-new-checker", - "image": images["go"], - "depends_on": [ - "compile-build-cmd", - ], - "commands": [ - "./bin/build whatsnew-checker", - ], - } - -def whats_new_checker_pipeline(trigger): - environment = {"EDITION": "oss"} - steps = [ - compile_build_cmd(), - whats_new_checker_step(), - ] - return pipeline( - name = "release-whatsnew-checker", - trigger = trigger, - services = [], - steps = steps, - environment = environment, - ) diff --git a/scripts/drone/rgm.star b/scripts/drone/rgm.star index 61694556c37..37b4fad4f70 100644 --- a/scripts/drone/rgm.star +++ b/scripts/drone/rgm.star @@ -16,13 +16,10 @@ load( "scripts/drone/pipelines/test_frontend.star", "test_frontend", ) -load( - "scripts/drone/pipelines/whats_new_checker.star", - "whats_new_checker_pipeline", -) load( "scripts/drone/steps/github.star", "github_app_generate_token_step", + "github_app_pipeline_volumes", "github_app_step_volumes", ) load( @@ -291,7 +288,6 @@ def rgm_tag_pipeline(): return [ build, - whats_new_checker_pipeline(tag_trigger), verify_release_pipeline( trigger = tag_trigger, name = "rgm-tag-verify-prerelease-assets", @@ -355,7 +351,7 @@ def rgm_promotion_pipeline(): "--grafana-ref=$${GRAFANA_REF} " + "--enterprise-ref=$${ENTERPRISE_REF} " + "--grafana-repo=$${GRAFANA_REPO} " + - "--version=$${VERSION} ", + "--version=$${VERSION} " + "--go-version={}".format(golang_version), ], "environment": rgm_env_secrets(env), @@ -369,6 +365,11 @@ def rgm_promotion_pipeline(): build_step["depends_on"] = [ generate_token_step["name"], ] + + publish_step["depends_on"] = [ + build_step["name"], + ] + steps = [ generate_token_step, build_step, @@ -380,7 +381,7 @@ def rgm_promotion_pipeline(): name = "rgm-promotion", trigger = promotion_trigger, steps = steps, - volumes = github_app_step_volumes(), + volumes = github_app_step_volumes() + github_app_pipeline_volumes(), ), ] diff --git a/scripts/drone/steps/lib.star b/scripts/drone/steps/lib.star index 36cd3a2db86..949a6fc9242 100644 --- a/scripts/drone/steps/lib.star +++ b/scripts/drone/steps/lib.star @@ -611,7 +611,7 @@ def betterer_frontend_step(): ], "commands": [ "apk add --update git bash", - "yarn betterer ci", + "yarn betterer:ci", ], } @@ -676,6 +676,28 @@ def verify_i18n_step(): ], } +def verify_api_clients_step(): + uncommited_error_message = "\nAPI client generation has not been committed. Please run 'yarn generate-apis', commit the changes and push again." + return { + "name": "verify-api-clients", + "image": images["node_deb"], + "depends_on": [ + "yarn-install", + ], + "commands": [ + "yarn generate-apis", + # Verify that client generation has been run and committed + ''' + file_diff=$(git diff ':!conf') + if [ -n "$file_diff" ]; then + echo $file_diff + echo "{}" + exit 1 + fi + '''.format(uncommited_error_message), + ], + } + def test_a11y_frontend_step(ver_mode, port = 3001): """Runs automated accessiblity tests against the frontend. diff --git a/scripts/drone/steps/rgm.star b/scripts/drone/steps/rgm.star index fd96a60d156..68dd2a082e5 100644 --- a/scripts/drone/steps/rgm.star +++ b/scripts/drone/steps/rgm.star @@ -47,7 +47,9 @@ def rgm_artifacts_step( "_EXPERIMENTAL_DAGGER_CLOUD_TOKEN": from_secret(rgm_dagger_token), }, "commands": [ - "docker run --privileged --rm tonistiigi/binfmt --install all", + "docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version", + "docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*'", + "docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all", cmd + "--go-version={} ".format(golang_version) + "--yarn-cache=$$YARN_CACHE_FOLDER " + @@ -78,7 +80,9 @@ def rgm_build_docker_step(ubuntu, alpine, depends_on = ["yarn-install"], file = "_EXPERIMENTAL_DAGGER_CLOUD_TOKEN": from_secret(rgm_dagger_token), }, "commands": [ - "docker run --privileged --rm tonistiigi/binfmt --install all", + "docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version", + "docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*'", + "docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all", "/src/grafana-build artifacts " + "-a docker:grafana:linux/amd64 " + "-a docker:grafana:linux/amd64:ubuntu " + diff --git a/scripts/drone/utils/images.star b/scripts/drone/utils/images.star index c0809849811..d51f9905df0 100644 --- a/scripts/drone/utils/images.star +++ b/scripts/drone/utils/images.star @@ -16,7 +16,7 @@ images = { "node_deb": "node:{}-bookworm".format(nodejs_version[:2]), "cloudsdk": "google/cloud-sdk:431.0.0", "publish": "grafana/grafana-ci-deploy:1.3.3", - "alpine": "alpine:3.20.3", + "alpine": "alpine:3.21.3", "ubuntu": "ubuntu:22.04", "curl": "byrnedo/alpine-curl:0.1.8", "plugins_slack": "plugins/slack", diff --git a/scripts/drone/variables.star b/scripts/drone/variables.star index 01147f6df17..6eeb6cfa03c 100644 --- a/scripts/drone/variables.star +++ b/scripts/drone/variables.star @@ -2,7 +2,7 @@ global variables """ -grabpl_version = "v3.1.1" +grabpl_version = "v3.1.2" golang_version = "1.23.5" # nodejs_version should match what's in ".nvmrc", but without the v prefix. diff --git a/scripts/generate-rtk-apis.ts b/scripts/generate-rtk-apis.ts index 00ad97e9037..691e7aeaa1f 100644 --- a/scripts/generate-rtk-apis.ts +++ b/scripts/generate-rtk-apis.ts @@ -2,15 +2,16 @@ import type { ConfigFile } from '@rtk-query/codegen-openapi'; const config: ConfigFile = { - schemaFile: '../public/openapi3.json', + schemaFile: '', // leave this empty, and instead populate the outputFiles object below apiFile: '', // leave this empty, and instead populate the outputFiles object below - hooks: true, exportName: 'generatedAPI', outputFiles: { '../public/app/features/migrate-to-cloud/api/endpoints.gen.ts': { + schemaFile: '../public/openapi3.json', apiFile: '../public/app/features/migrate-to-cloud/api/baseAPI.ts', apiImport: 'baseAPI', + hooks: true, filterEndpoints: [ 'getSessionList', 'getSession', @@ -32,10 +33,21 @@ const config: ConfigFile = { ], }, '../public/app/features/preferences/api/user/endpoints.gen.ts': { + schemaFile: '../public/openapi3.json', + hooks: true, apiFile: '../public/app/features/preferences/api/user/baseAPI.ts', apiImport: 'baseAPI', filterEndpoints: ['getUserPreferences', 'updateUserPreferences', 'patchUserPreferences'], }, + '../public/app/features/iam/api/endpoints.gen.ts': { + schemaFile: '../data/openapi/iam.grafana.app-v0alpha1.json', + apiFile: '../public/app/features/iam/api/api.ts', + apiImport: 'iamApi', + filterEndpoints: ['getDisplayMapping'], + exportName: 'generatedIamApi', + flattenArg: false, + tag: true, + }, }, }; diff --git a/scripts/grafana-server/custom.ini b/scripts/grafana-server/custom.ini index 8be26fb511c..98a518778ec 100644 --- a/scripts/grafana-server/custom.ini +++ b/scripts/grafana-server/custom.ini @@ -15,3 +15,7 @@ type=sqlite3 wal=true max_idle_conn = 2 max_open_conn = 2 + +[smtp] +enabled = true +host = localhost:7777 diff --git a/scripts/grafana-server/start-server b/scripts/grafana-server/start-server index a4e9be7f2bd..fdb4499697e 100755 --- a/scripts/grafana-server/start-server +++ b/scripts/grafana-server/start-server @@ -48,6 +48,11 @@ elif [ -d "../e2e/test-plugins" ]; then cp -r "../e2e/test-plugins" "$RUNDIR/data/plugins" fi +if [ "$INSTALL_IMAGE_RENDERER" ]; then + echo -e "Installing image renderer" + $RUNDIR/bin/"$ARCH"grafana cli --pluginsDir "$RUNDIR/data/plugins" plugins install grafana-image-renderer +fi + echo -e "Copy provisioning setup from devenv" cp devenv/datasources.yaml $PROV_DIR/datasources diff --git a/scripts/import_many_dashboards.sh b/scripts/import_many_dashboards.sh index 60d4fcd55b3..47cbb2c36c2 100755 --- a/scripts/import_many_dashboards.sh +++ b/scripts/import_many_dashboards.sh @@ -3,6 +3,6 @@ for index in {0..3000} do echo -n "index $index" - curl 'http://localhost:3000/api/dashboards/import' -H 'Pragma: no-cache' -H 'Origin: http://localhost:3000' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: en-US,en;q=0.8,sv;q=0.6' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.101 Safari/537.36' -H 'Content-Type: application/json;charset=UTF-8' -H 'Accept: application/json, text/plain, */*' -H 'Cache-Control: no-cache' -H 'Referer: http://localhost:3000/dashboard/new?editview=import' -H 'Cookie: grafana_sess=662a67f11b47e657; grafana_user=admin; grafana_remember=bd839923f24f648c7cb53ede6ff9ef40826204e9a22df8f9; toggles=%7B%7D' -H 'Connection: keep-alive' --data-binary $'{"dashboard":{"__inputs":[{"name":"DS_GRAPHITE","label":"graphite","description":"","type":"datasource","pluginId":"graphite","pluginName":"Graphite"}],"__requires":[{"type":"panel","id":"singlestat","name":"Singlestat","version":""},{"type":"panel","id":"graph","name":"Graph","version":""},{"type":"grafana","id":"grafana","name":"Grafana","version":"3.1.0"},{"type":"datasource","id":"graphite","name":"Graphite","version":"1.0.0"}],"id":null,"title":"Big Dashboard dashname '"$index"$'","tags":["startpage","home","presentation"],"style":"dark","timezone":"browser","editable":true,"hideControls":false,"sharedCrosshair":true,"rows":[{"collapse":false,"editable":true,"height":"100px","panels":[{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"id":16,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":3,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"refId":"A","target":"apps.backend.backend_02.counters.requests.count"}],"thresholds":"100,270","title":"Sign ups","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"id":15,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":3,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.7)"}],"thresholds":"100,270","title":"Logins","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"id":17,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":3,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"refId":"A","target":"apps.backend.backend_04.counters.requests.count"}],"thresholds":"100,270","title":"Sign outs","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"id":18,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":3,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"refId":"A","target":"scale(apps.backend.backend_03.counters.requests.count, 0.3)"}],"thresholds":"100,270","title":"Support calls","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1}],"title":"New row"},{"collapse":false,"editable":true,"height":218.4375,"panels":[{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":20,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.7)"}],"thresholds":"200,270","title":"Logins","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":24,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.2)"}],"thresholds":"200,270","title":"Google hits","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"bytes","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":22,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.4)"}],"thresholds":"200,270","title":"Memory","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":21,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.8)"}],"thresholds":"200,270","title":"Logouts","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":26,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.2)"}],"thresholds":"200,270","title":"Google hits","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":25,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.8)"}],"thresholds":"200,270","title":"Logouts","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1}],"title":"New row"},{"collapsable":true,"collapse":false,"editable":true,"height":"250px","notice":false,"panels":[{"aliasColors":{"cpu":"#E24D42","memory":"#6ED0E0","statsd.fakesite.counters.session_start.desktop.count":"#6ED0E0"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":3,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":4,"interactive":true,"legend":{"avg":false,"current":true,"max":false,"min":true,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[{"alias":"cpu","fill":0,"lines":true,"yaxis":2,"zindex":2},{"alias":"memory","pointradius":2,"points":true}],"span":4,"spyable":true,"stack":false,"steppedLine":false,"targets":[{"hide":false,"refId":"A","target":"alias(movingAverage(scaleToSeconds(apps.fakesite.web_server_01.counters.request_status.code_302.count, 10), 20), \'cpu\')"},{"refId":"B","target":"alias(statsd.fakesite.counters.session_start.desktop.count, \'memory\')"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"Memory / CPU","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"bytes","logBase":1,"max":null,"min":null,"show":true},{"format":"percent","logBase":1,"max":null,"min":0,"show":true}],"zerofill":true},{"aliasColors":{"logins":"#7EB26D","logins (-1 day)":"#447EBC"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":1,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":3,"interactive":true,"legend":{"alignAsTable":false,"avg":false,"current":true,"max":true,"min":true,"rightSide":false,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":1,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[],"span":4,"spyable":true,"stack":true,"steppedLine":false,"targets":[{"refId":"A","target":"alias(movingAverage(scaleToSeconds(apps.fakesite.web_server_01.counters.requests.count, 1), 2), \'logins\')"},{"refId":"B","target":"alias(movingAverage(timeShift(scaleToSeconds(apps.fakesite.web_server_01.counters.requests.count, 1), \'1h\'), 2), \'logins (-1 hour)\')"}],"timeFrom":null,"timeShift":"1h","timezone":"browser","title":"logins","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"short","logBase":1,"max":null,"min":null,"show":true},{"format":"short","logBase":1,"max":null,"min":null,"show":true}],"zerofill":true},{"aliasColors":{"cpu":"#E24D42","memory":"#6ED0E0","statsd.fakesite.counters.session_start.desktop.count":"#6ED0E0"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":3,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":19,"interactive":true,"legend":{"avg":false,"current":true,"max":false,"min":true,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[{"alias":"cpu","fill":0,"lines":true,"yaxis":2,"zindex":2},{"alias":"memory","pointradius":2,"points":true}],"span":4,"spyable":true,"stack":false,"steppedLine":false,"targets":[{"hide":false,"refId":"A","target":"alias(movingAverage(scaleToSeconds(apps.fakesite.web_server_01.counters.request_status.code_302.count, 10), 20), \'cpu\')"},{"refId":"B","target":"alias(statsd.fakesite.counters.session_start.desktop.count, \'memory\')"}],"timeFrom":null,"timeShift":"1h","timezone":"browser","title":"Memory / CPU","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"bytes","logBase":1,"max":null,"min":null,"show":true},{"format":"percent","logBase":1,"max":null,"min":0,"show":true}],"zerofill":true}],"title":"test"},{"collapsable":true,"collapse":false,"editable":true,"height":"300px","notice":false,"panels":[{"aliasColors":{"web_server_01":"#B7DBAB","web_server_02":"#7EB26D","web_server_03":"#508642","web_server_04":"#3F6833"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":8,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":2,"interactive":true,"legend":{"alignAsTable":false,"avg":false,"current":false,"max":false,"min":false,"rightSide":false,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[],"span":4,"spyable":true,"stack":true,"steppedLine":false,"targets":[{"refId":"A","target":"aliasByNode(movingAverage(scaleToSeconds(apps.fakesite.*.counters.requests.count, 1), 2), 2)"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"server requests","tooltip":{"msResolution":false,"query_as_alias":true,"shared":true,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"short","logBase":1,"max":null,"min":null,"show":true},{"format":"short","logBase":1,"max":null,"min":null,"show":true}],"zerofill":true},{"aliasColors":{"upper_25":"#F9E2D2","upper_50":"#F2C96D","upper_75":"#EAB839"},"annotate":{"enable":false},"bars":true,"datasource":"${DS_GRAPHITE}","editable":true,"fill":1,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":5,"interactive":true,"legend":{"alignAsTable":true,"avg":true,"current":false,"max":false,"min":false,"rightSide":true,"show":true,"total":false,"values":true},"legend_counts":true,"lines":false,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[],"span":4,"spyable":true,"stack":true,"steppedLine":false,"targets":[{"refId":"A","target":"aliasByNode(summarize(statsd.fakesite.timers.ads_timer.*, \'4min\', \'avg\'), 4)"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"client side full page load","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"ms","logBase":1,"max":null,"min":null,"show":true},{"format":"short","logBase":1,"max":null,"min":null,"show":true}],"zerofill":true},{"aliasColors":{"web_server_01":"#B7DBAB","web_server_02":"#7EB26D","web_server_03":"#508642","web_server_04":"#3F6833"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":8,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":14,"interactive":true,"legend":{"alignAsTable":false,"avg":false,"current":false,"max":false,"min":false,"rightSide":false,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[],"span":4,"spyable":true,"stack":true,"steppedLine":false,"targets":[{"refId":"A","target":"aliasByNode(movingAverage(scaleToSeconds(apps.fakesite.*.counters.requests.count, 1), 2), 2)"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"server requests","tooltip":{"msResolution":false,"query_as_alias":true,"shared":true,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"short","logBase":1,"max":null,"min":null,"show":true},{"format":"short","logBase":1,"max":null,"min":null,"show":true}],"zerofill":true}],"title":""},{"collapsable":true,"collapse":false,"editable":true,"height":"200px","notice":false,"panels":[{"aliasColors":{"cpu1":"#EF843C","cpu2":"#EAB839","upper_25":"#B7DBAB","upper_50":"#7EB26D","upper_75":"#629E51","upper_90":"#629E51","upper_95":"#508642"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":3,"grid":{"max":null,"min":null,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":6,"interactive":true,"legend":{"alignAsTable":true,"avg":true,"current":true,"legendSideLastValue":true,"max":false,"min":false,"rightSide":true,"show":false,"total":false,"values":true},"legend_counts":true,"lines":true,"linewidth":2,"links":[],"nullPointMode":"connected","options":false,"percentage":false,"pointradius":1,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[{"alias":"this is test of breaking","yaxis":1}],"span":12,"spyable":true,"stack":false,"steppedLine":false,"targets":[{"refId":"A","target":"aliasByNode(statsd.fakesite.timers.ads_timer.*,4)"},{"refId":"B","target":"alias(scale(statsd.fakesite.timers.ads_timer.upper_95,-1),\'cpu1\')"},{"refId":"C","target":"alias(scale(statsd.fakesite.timers.ads_timer.upper_75,-1),\'cpu2\')"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"transparent":true,"type":"graph","xaxis":{"show":false},"yaxes":[{"format":"ms","logBase":1,"max":null,"min":null,"show":false},{"format":"short","logBase":1,"max":null,"min":null,"show":false}],"zerofill":true}],"title":"test"}],"time":{"from":"now-30m","to":"now"},"timepicker":{"collapse":false,"enable":true,"notice":false,"now":true,"refresh_intervals":["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"],"status":"Stable","time_options":["5m","15m","1h","2h"," 6h","12h","24h","2d","7d","30d"],"type":"timepicker"},"templating":{"enable":false,"list":[]},"annotations":{"enable":false,"list":[]},"refresh":false,"schemaVersion":12,"version":5,"links":[],"gnetId":null},"overwrite":true,"inputs":[{"name":"DS_GRAPHITE","type":"datasource","pluginId":"graphite","value":"graphite"}]}' --compressed + curl 'http://localhost:3000/api/dashboards/import' -H 'Pragma: no-cache' -H 'Origin: http://localhost:3000' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: en-US,en;q=0.8,sv;q=0.6' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.101 Safari/537.36' -H 'Content-Type: application/json;charset=UTF-8' -H 'Accept: application/json, text/plain, */*' -H 'Cache-Control: no-cache' -H 'Referer: http://localhost:3000/dashboard/new?editview=import' -H 'Cookie: grafana_sess=662a67f11b47e657; grafana_user=admin; grafana_remember=bd839923f24f648c7cb53ede6ff9ef40826204e9a22df8f9; toggles=%7B%7D' -H 'Connection: keep-alive' --data-binary $'{"dashboard":{"__inputs":[{"name":"DS_GRAPHITE","label":"graphite","description":"","type":"datasource","pluginId":"graphite","pluginName":"Graphite"}],"__requires":[{"type":"panel","id":"singlestat","name":"Singlestat","version":""},{"type":"panel","id":"graph","name":"Graph","version":""},{"type":"grafana","id":"grafana","name":"Grafana","version":"3.1.0"},{"type":"datasource","id":"graphite","name":"Graphite","version":"1.0.0"}],"id":null,"title":"Big Dashboard dashname '"$index"$'","tags":["startpage","home","presentation"],"style":"dark","timezone":"browser","editable":true,"hideControls":false,"sharedCrosshair":true,"rows":[{"collapse":false,"editable":true,"height":"100px","panels":[{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"id":16,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":3,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"refId":"A","target":"apps.backend.backend_02.counters.requests.count"}],"thresholds":"100,270","title":"Sign ups","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"id":15,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":3,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.7)"}],"thresholds":"100,270","title":"Logins","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"id":17,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":3,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"refId":"A","target":"apps.backend.backend_04.counters.requests.count"}],"thresholds":"100,270","title":"Sign outs","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"id":18,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":3,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"refId":"A","target":"scale(apps.backend.backend_03.counters.requests.count, 0.3)"}],"thresholds":"100,270","title":"Support calls","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1}],"title":"New row"},{"collapse":false,"editable":true,"height":218.4375,"panels":[{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":20,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.7)"}],"thresholds":"200,270","title":"Logins","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":24,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.2)"}],"thresholds":"200,270","title":"Google hits","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"bytes","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":22,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.4)"}],"thresholds":"200,270","title":"Memory","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":21,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.8)"}],"thresholds":"200,270","title":"Logouts","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":26,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.2)"}],"thresholds":"200,270","title":"Google hits","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":25,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.8)"}],"thresholds":"200,270","title":"Logouts","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1}],"title":"New row"},{"collapsable":true,"collapse":false,"editable":true,"height":"250px","notice":false,"panels":[{"aliasColors":{"cpu":"#E24D42","memory":"#6ED0E0","statsd.fakesite.counters.session_start.desktop.count":"#6ED0E0"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":3,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":4,"interactive":true,"legend":{"avg":false,"current":true,"max":false,"min":true,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[{"alias":"cpu","fill":0,"lines":true,"yaxis":2,"zindex":2},{"alias":"memory","pointradius":2,"points":true}],"span":4,"spyable":true,"stack":false,"steppedLine":false,"targets":[{"hide":false,"refId":"A","target":"alias(movingAverage(scaleToSeconds(apps.fakesite.web_server_01.counters.request_status.code_302.count, 10), 20), \'cpu\')"},{"refId":"B","target":"alias(statsd.fakesite.counters.session_start.desktop.count, \'memory\')"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"Memory / CPU","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"bytes","logBase":1,"max":null,"min":null,"show":true},{"format":"percent","logBase":1,"max":null,"min":0,"show":true}],"zerofill":true},{"aliasColors":{"logins":"#7EB26D","logins (-1 day)":"#447EBC"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":1,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":3,"interactive":true,"legend":{"alignAsTable":false,"avg":false,"current":true,"max":true,"min":true,"rightSide":false,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":1,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[],"span":4,"spyable":true,"stack":true,"steppedLine":false,"targets":[{"refId":"A","target":"alias(movingAverage(scaleToSeconds(apps.fakesite.web_server_01.counters.requests.count, 1), 2), \'logins\')"},{"refId":"B","target":"alias(movingAverage(timeShift(scaleToSeconds(apps.fakesite.web_server_01.counters.requests.count, 1), \'1h\'), 2), \'logins (-1 hour)\')"}],"timeFrom":null,"timeShift":"1h","timezone":"browser","title":"logins","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"short","logBase":1,"max":null,"min":null,"show":true},{"format":"short","logBase":1,"max":null,"min":null,"show":true}],"zerofill":true},{"aliasColors":{"cpu":"#E24D42","memory":"#6ED0E0","statsd.fakesite.counters.session_start.desktop.count":"#6ED0E0"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":3,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":19,"interactive":true,"legend":{"avg":false,"current":true,"max":false,"min":true,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[{"alias":"cpu","fill":0,"lines":true,"yaxis":2,"zindex":2},{"alias":"memory","pointradius":2,"points":true}],"span":4,"spyable":true,"stack":false,"steppedLine":false,"targets":[{"hide":false,"refId":"A","target":"alias(movingAverage(scaleToSeconds(apps.fakesite.web_server_01.counters.request_status.code_302.count, 10), 20), \'cpu\')"},{"refId":"B","target":"alias(statsd.fakesite.counters.session_start.desktop.count, \'memory\')"}],"timeFrom":null,"timeShift":"1h","timezone":"browser","title":"Memory / CPU","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"bytes","logBase":1,"max":null,"min":null,"show":true},{"format":"percent","logBase":1,"max":null,"min":0,"show":true}],"zerofill":true}],"title":"test"},{"collapsable":true,"collapse":false,"editable":true,"height":"300px","notice":false,"panels":[{"aliasColors":{"web_server_01":"#B7DBAB","web_server_02":"#7EB26D","web_server_03":"#508642","web_server_04":"#3F6833"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":8,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":2,"interactive":true,"legend":{"alignAsTable":false,"avg":false,"current":false,"max":false,"min":false,"rightSide":false,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[],"span":4,"spyable":true,"stack":true,"steppedLine":false,"targets":[{"refId":"A","target":"aliasByNode(movingAverage(scaleToSeconds(apps.fakesite.*.counters.requests.count, 1), 2), 2)"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"server requests","tooltip":{"msResolution":false,"query_as_alias":true,"shared":true,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"short","logBase":1,"max":null,"min":null,"show":true},{"format":"short","logBase":1,"max":null,"min":null,"show":true}],"zerofill":true},{"aliasColors":{"upper_25":"#F9E2D2","upper_50":"#F2C96D","upper_75":"#EAB839"},"annotate":{"enable":false},"bars":true,"datasource":"${DS_GRAPHITE}","editable":true,"fill":1,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":5,"interactive":true,"legend":{"alignAsTable":true,"avg":true,"current":false,"max":false,"min":false,"rightSide":true,"show":true,"total":false,"values":true},"legend_counts":true,"lines":false,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[],"span":4,"spyable":true,"stack":true,"steppedLine":false,"targets":[{"refId":"A","target":"aliasByNode(summarize(statsd.fakesite.timers.ads_timer.*, \'4min\', \'avg\'), 4)"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"client side full page load","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"ms","logBase":1,"max":null,"min":null,"show":true},{"format":"short","logBase":1,"max":null,"min":null,"show":true}],"zerofill":true},{"aliasColors":{"web_server_01":"#B7DBAB","web_server_02":"#7EB26D","web_server_03":"#508642","web_server_04":"#3F6833"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":8,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":14,"interactive":true,"legend":{"alignAsTable":false,"avg":false,"current":false,"max":false,"min":false,"rightSide":false,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[],"span":4,"spyable":true,"stack":true,"steppedLine":false,"targets":[{"refId":"A","target":"aliasByNode(movingAverage(scaleToSeconds(apps.fakesite.*.counters.requests.count, 1), 2), 2)"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"server requests","tooltip":{"msResolution":false,"query_as_alias":true,"shared":true,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"short","logBase":1,"max":null,"min":null,"show":true},{"format":"short","logBase":1,"max":null,"min":null,"show":true}],"zerofill":true}],"title":""},{"collapsable":true,"collapse":false,"editable":true,"height":"200px","notice":false,"panels":[{"aliasColors":{"cpu1":"#EF843C","cpu2":"#EAB839","upper_25":"#B7DBAB","upper_50":"#7EB26D","upper_75":"#629E51","upper_90":"#629E51","upper_95":"#508642"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":3,"grid":{"max":null,"min":null,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":6,"interactive":true,"legend":{"alignAsTable":true,"avg":true,"current":true,"legendSideLastValue":true,"max":false,"min":false,"rightSide":true,"show":false,"total":false,"values":true},"legend_counts":true,"lines":true,"linewidth":2,"links":[],"nullPointMode":"connected","options":false,"percentage":false,"pointradius":1,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[{"alias":"this is test of breaking","yaxis":1}],"span":12,"spyable":true,"stack":false,"steppedLine":false,"targets":[{"refId":"A","target":"aliasByNode(statsd.fakesite.timers.ads_timer.*,4)"},{"refId":"B","target":"alias(scale(statsd.fakesite.timers.ads_timer.upper_95,-1),\'cpu1\')"},{"refId":"C","target":"alias(scale(statsd.fakesite.timers.ads_timer.upper_75,-1),\'cpu2\')"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"transparent":true,"type":"graph","xaxis":{"show":false},"yaxes":[{"format":"ms","logBase":1,"max":null,"min":null,"show":false},{"format":"short","logBase":1,"max":null,"min":null,"show":false}],"zerofill":true}],"title":"test"}],"time":{"from":"now-30m","to":"now"},"timepicker":{"collapse":false,"enable":true,"notice":false,"now":true,"refresh_intervals":["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"],"status":"Stable","type":"timepicker"},"templating":{"enable":false,"list":[]},"annotations":{"enable":false,"list":[]},"refresh":false,"schemaVersion":12,"version":5,"links":[],"gnetId":null},"overwrite":true,"inputs":[{"name":"DS_GRAPHITE","type":"datasource","pluginId":"graphite","value":"graphite"}]}' --compressed done diff --git a/scripts/process-specs.ts b/scripts/process-specs.ts new file mode 100644 index 00000000000..f13fdf7cb74 --- /dev/null +++ b/scripts/process-specs.ts @@ -0,0 +1,178 @@ +import fs from 'fs'; +import { OpenAPIV3 } from 'openapi-types'; +import path from 'path'; + +/** + * Process an OpenAPI spec to remove k8s metadata from names and paths: + * - Remove paths containing "/watch/" as they're deprecated. + * - Remove 'ForAllNamespaces' endpoints + * - Remove the prefix: "/apis///namespaces/{namespace}" from paths. + * - Filter out `namespace` from path parameters. + * - Update all $ref fields to remove k8s metadata from schema names. + * - Simplify schema names in "components.schemas". + */ +function processOpenAPISpec(spec: OpenAPIV3.Document) { + // Create a deep copy of the spec to avoid mutating the original + const newSpec = JSON.parse(JSON.stringify(spec)); + + // Process 'paths' property + const newPaths: Record = {}; + for (const [path, pathItem] of Object.entries(newSpec.paths)) { + // Remove empty path items + if (!pathItem) { + continue; + } + // Remove the specified part from the path key + const newPathKey = path.replace(/^\/apis\/[^\/]+\/[^\/]+\/namespaces\/\{namespace}/, ''); + + // Process each method in the path (e.g., get, post) + const newPathItem: Record = {}; + + // Filter out namespace parameter at path level + if (Array.isArray(pathItem.parameters)) { + pathItem.parameters = filterNamespaceParameters(pathItem.parameters); + } + + for (const method of Object.keys(pathItem)) { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + const operation = pathItem[method as keyof OpenAPIV3.PathItemObject]; + + if ( + typeof operation === 'object' && + operation !== null && + 'operationId' in operation && + operation.operationId?.includes('ForAllNamespaces') + ) { + continue; + } + + // Filter out namespace parameter at operation level + if ( + operation && + typeof operation === 'object' && + 'parameters' in operation && + Array.isArray(operation.parameters) + ) { + operation.parameters = filterNamespaceParameters(operation.parameters); + } + + updateRefs(operation); + + newPathItem[method] = operation; + } + + newPaths[newPathKey] = newPathItem; + } + newSpec.paths = newPaths; + + // Process 'components.schemas', i.e., type definitions + const newSchemas: Record = {}; + for (const schemaKey of Object.keys(newSpec.components.schemas)) { + const newKey = simplifySchemaName(schemaKey); + + const schemaObject = newSpec.components.schemas[schemaKey]; + updateRefs(schemaObject); + + newSchemas[newKey] = schemaObject; + } + newSpec.components.schemas = newSchemas; + + return newSpec; +} + +/** + * Filter out namespace parameters from an array of parameters + */ +function filterNamespaceParameters(parameters: Array) { + return parameters.filter((param) => 'name' in param && param.name !== 'namespace'); +} + +/** + * Recursively update all $ref fields to remove k8s metadata from names + */ +function updateRefs(obj: unknown) { + if (Array.isArray(obj)) { + for (const item of obj) { + updateRefs(item); + } + } else if (typeof obj === 'object' && obj !== null) { + if ('$ref' in obj && typeof obj.$ref === 'string') { + const refParts = obj.$ref.split('/'); + const lastRefPart = refParts[refParts.length - 1]; + const newRefName = simplifySchemaName(lastRefPart); + obj.$ref = `#/components/schemas/${newRefName}`; + } + for (const key in obj) { + if (key !== '$ref') { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + updateRefs(obj[key as keyof typeof obj]); + } + } + } +} + +/** + * Simplify a schema name by removing the version prefix if present. + * For example, 'io.k8s.apimachinery.pkg.apis.meta.v1.Time' becomes 'Time'. + */ +function simplifySchemaName(schemaName: string) { + const parts = schemaName.split('.'); + + // Regex to match version segments like 'v1', 'v1beta1', 'v0alpha1', etc. + const versionRegex = /^v\d+[a-zA-Z0-9]*$/; + const versionIndex = parts.findIndex((part) => versionRegex.test(part)); + + if (versionIndex !== -1 && versionIndex + 1 < parts.length) { + return parts.slice(versionIndex + 1).join('.'); + } else { + return schemaName; + } +} + +/** + * Process all files in a source directory and write results to output directory + */ +function processDirectory(sourceDir: string, outputDir: string) { + // Skip if source directory doesn't exist + if (!fs.existsSync(sourceDir)) { + return; + } + + // Create the output directory if it doesn't exist + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); + } + + const files = fs.readdirSync(sourceDir).filter((file: string) => file.endsWith('.json')); + + for (const file of files) { + const inputPath = path.join(sourceDir, file); + const outputPath = path.join(outputDir, file); + + console.log(`Processing file "${file}"...`); + + const fileContent = fs.readFileSync(inputPath, 'utf-8'); + + let inputSpec; + try { + inputSpec = JSON.parse(fileContent); + } catch (err) { + console.error(`Invalid JSON file "${file}". Skipping this file.`); + continue; + } + + const outputSpec = processOpenAPISpec(inputSpec); + fs.writeFileSync(outputPath, JSON.stringify(outputSpec, null, 2), 'utf-8'); + console.log(`Processing completed for file "${file}".`); + } +} + +const sourceDirs = [ + path.resolve(import.meta.dirname, '../pkg/tests/apis/openapi_snapshots'), + path.resolve(import.meta.dirname, '../pkg/extensions/apiserver/tests/openapi_snapshots'), +]; +const outputDir = path.resolve(import.meta.dirname, '../data/openapi'); + +for (const sourceDir of sourceDirs) { + processDirectory(sourceDir, outputDir); +} diff --git a/tools/setup_grafana_alertmanager_integration_test_images.go b/tools/setup_grafana_alertmanager_integration_test_images.go new file mode 100644 index 00000000000..3b6e1436446 --- /dev/null +++ b/tools/setup_grafana_alertmanager_integration_test_images.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "os" + "os/exec" + "sync" + + amtests "github.com/grafana/grafana/pkg/tests/alertmanager" +) + +func docker(args []string) { + cmd := exec.Command("docker", args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Printf("docker pull failed: %v\n", err) + os.Exit(1) + } +} + +func main() { + var wg sync.WaitGroup + + for _, cmd := range [][]string{ + {"pull", amtests.GetGrafanaImage()}, + {"pull", amtests.GetLokiImage()}, + {"pull", amtests.GetPostgresImage()}, + {"build", "-t", "webhook-receiver", "devenv/docker/blocks/stateful_webhook"}, + } { + wg.Add(1) + + go func(cmd []string) { + defer wg.Done() + + docker(cmd) + }(cmd) + } + + wg.Wait() +} diff --git a/yarn.lock b/yarn.lock index 0687b253263..eb8360924ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -81,7 +81,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.3, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.24.2, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.0": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.3, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.24.2, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.2": version: 7.26.2 resolution: "@babel/code-frame@npm:7.26.2" dependencies: @@ -92,46 +92,46 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.25.9, @babel/compat-data@npm:^7.26.0": - version: 7.26.2 - resolution: "@babel/compat-data@npm:7.26.2" - checksum: 10/ed9eed6b62ce803ef4a320b1dac76b0302abbb29c49dddf96f3e3207d9717eb34e299a8651bb1582e9c3346ead74b6d595ffced5b3dae718afa08b18741f8402 +"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.26.5": + version: 7.26.5 + resolution: "@babel/compat-data@npm:7.26.5" + checksum: 10/afe35751f27bda80390fa221d5e37be55b7fc42cec80de9896086e20394f2306936c4296fcb4d62b683e3b49ba2934661ea7e06196ca2dacdc2e779fbea4a1a9 languageName: node linkType: hard -"@babel/core@npm:7.26.0, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.18.9, @babel/core@npm:^7.21.3, @babel/core@npm:^7.22.9": - version: 7.26.0 - resolution: "@babel/core@npm:7.26.0" +"@babel/core@npm:7.26.7, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.18.9, @babel/core@npm:^7.21.3, @babel/core@npm:^7.22.9": + version: 7.26.7 + resolution: "@babel/core@npm:7.26.7" dependencies: "@ampproject/remapping": "npm:^2.2.0" - "@babel/code-frame": "npm:^7.26.0" - "@babel/generator": "npm:^7.26.0" - "@babel/helper-compilation-targets": "npm:^7.25.9" + "@babel/code-frame": "npm:^7.26.2" + "@babel/generator": "npm:^7.26.5" + "@babel/helper-compilation-targets": "npm:^7.26.5" "@babel/helper-module-transforms": "npm:^7.26.0" - "@babel/helpers": "npm:^7.26.0" - "@babel/parser": "npm:^7.26.0" + "@babel/helpers": "npm:^7.26.7" + "@babel/parser": "npm:^7.26.7" "@babel/template": "npm:^7.25.9" - "@babel/traverse": "npm:^7.25.9" - "@babel/types": "npm:^7.26.0" + "@babel/traverse": "npm:^7.26.7" + "@babel/types": "npm:^7.26.7" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10/65767bfdb1f02e80d3af4f138066670ef8fdd12293de85ef151758a901c191c797e86d2e99b11c4cdfca33c72385ecaf38bbd7fa692791ec44c77763496b9b93 + checksum: 10/1ca1c9b1366a1ee77ade9c72302f288b2b148e4190e0f36bc032d09c686b2c7973d3309e4eec2c57243508c16cf907c17dec4e34ba95e7a18badd57c61bbcb7c languageName: node linkType: hard -"@babel/generator@npm:^7.22.9, @babel/generator@npm:^7.25.9, @babel/generator@npm:^7.26.0, @babel/generator@npm:^7.7.2": - version: 7.26.2 - resolution: "@babel/generator@npm:7.26.2" +"@babel/generator@npm:^7.22.9, @babel/generator@npm:^7.26.5, @babel/generator@npm:^7.7.2": + version: 7.26.5 + resolution: "@babel/generator@npm:7.26.5" dependencies: - "@babel/parser": "npm:^7.26.2" - "@babel/types": "npm:^7.26.0" + "@babel/parser": "npm:^7.26.5" + "@babel/types": "npm:^7.26.5" "@jridgewell/gen-mapping": "npm:^0.3.5" "@jridgewell/trace-mapping": "npm:^0.3.25" jsesc: "npm:^3.0.2" - checksum: 10/71ace82b5b07a554846a003624bfab93275ccf73cdb9f1a37a4c1094bf9dc94bb677c67e8b8c939dbd6c5f0eda2e8f268aa2b0d9c3b9511072565660e717e045 + checksum: 10/aa5f176155431d1fb541ca11a7deddec0fc021f20992ced17dc2f688a0a9584e4ff4280f92e8a39302627345cd325762f70f032764806c579c6fd69432542bcb languageName: node linkType: hard @@ -144,26 +144,16 @@ __metadata: languageName: node linkType: hard -"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.25.9" +"@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.25.9, @babel/helper-compilation-targets@npm:^7.26.5": + version: 7.26.5 + resolution: "@babel/helper-compilation-targets@npm:7.26.5" dependencies: - "@babel/traverse": "npm:^7.25.9" - "@babel/types": "npm:^7.25.9" - checksum: 10/e1bb465b3b0155702d82cfef09e3813e87a6d777cdd2c513796861eac14953340491eafea1d4109278bf4ceb48b54074c45758f042c0544d00c498090bee5a6f - languageName: node - linkType: hard - -"@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/helper-compilation-targets@npm:7.25.9" - dependencies: - "@babel/compat-data": "npm:^7.25.9" + "@babel/compat-data": "npm:^7.26.5" "@babel/helper-validator-option": "npm:^7.25.9" browserslist: "npm:^4.24.0" lru-cache: "npm:^5.1.1" semver: "npm:^6.3.1" - checksum: 10/8053fbfc21e8297ab55c8e7f9f119e4809fa7e505268691e1bedc2cf5e7a5a7de8c60ad13da2515378621b7601c42e101d2d679904da395fa3806a1edef6b92e + checksum: 10/f3b5f0bfcd7b6adf03be1a494b269782531c6e415afab2b958c077d570371cf1bfe001c442508092c50ed3711475f244c05b8f04457d8dea9c34df2b741522bf languageName: node linkType: hard @@ -264,10 +254,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.7, @babel/helper-plugin-utils@npm:^7.25.9, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": - version: 7.25.9 - resolution: "@babel/helper-plugin-utils@npm:7.25.9" - checksum: 10/e347d87728b1ab10b6976d46403941c8f9008c045ea6d99997a7ffca7b852dc34b6171380f7b17edf94410e0857ff26f3a53d8618f11d73744db86e8ca9b8c64 +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.7, @babel/helper-plugin-utils@npm:^7.25.9, @babel/helper-plugin-utils@npm:^7.26.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": + version: 7.26.5 + resolution: "@babel/helper-plugin-utils@npm:7.26.5" + checksum: 10/1cc0fd8514da3bb249bed6c27227696ab5e84289749d7258098701cffc0c599b7f61ec40dd332f8613030564b79899d9826813c96f966330bcfc7145a8377857 languageName: node linkType: hard @@ -297,16 +287,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-simple-access@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/helper-simple-access@npm:7.25.9" - dependencies: - "@babel/traverse": "npm:^7.25.9" - "@babel/types": "npm:^7.25.9" - checksum: 10/a16a6cfa5e8ac7144e856bcdaaf0022cf5de028fc0c56ce21dd664a6e900999a4285c587a209f2acf9de438c0d60bfb497f5f34aa34cbaf29da3e2f8d8d7feb7 - languageName: node - linkType: hard - "@babel/helper-skip-transparent-expression-wrappers@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.25.9" @@ -349,13 +329,13 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.26.0": - version: 7.26.0 - resolution: "@babel/helpers@npm:7.26.0" +"@babel/helpers@npm:^7.26.7": + version: 7.26.7 + resolution: "@babel/helpers@npm:7.26.7" dependencies: "@babel/template": "npm:^7.25.9" - "@babel/types": "npm:^7.26.0" - checksum: 10/fd4757f65d10b64cfdbf4b3adb7ea6ffff9497c53e0786452f495d1f7794da7e0898261b4db65e1c62bbb9a360d7d78a1085635c23dfc3af2ab6dcba06585f86 + "@babel/types": "npm:^7.26.7" + checksum: 10/97593a0c9b3c5e2e7cf824e549b5f6fa6dc739593ad93d5bb36d06883d8124beac63ee2154c9a514dbee68a169d5683ab463e0ac6713ad92fb4854cea35ed4d4 languageName: node linkType: hard @@ -371,14 +351,14 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.25.9, @babel/parser@npm:^7.26.0, @babel/parser@npm:^7.26.2": - version: 7.26.2 - resolution: "@babel/parser@npm:7.26.2" +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.25.9, @babel/parser@npm:^7.26.5, @babel/parser@npm:^7.26.7": + version: 7.26.7 + resolution: "@babel/parser@npm:7.26.7" dependencies: - "@babel/types": "npm:^7.26.0" + "@babel/types": "npm:^7.26.7" bin: parser: ./bin/babel-parser.js - checksum: 10/8baee43752a3678ad9f9e360ec845065eeee806f1fdc8e0f348a8a0e13eef0959dabed4a197c978896c493ea205c804d0a1187cc52e4a1ba017c7935bab4983d + checksum: 10/3ccc384366ca9a9b49c54f5b24c9d8cff9a505f2fbdd1cfc04941c8e1897084cc32f100e77900c12bc14a176cf88daa3c155faad680d9a23491b997fd2a59ffc languageName: node linkType: hard @@ -709,14 +689,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoped-functions@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.25.9" +"@babel/plugin-transform-block-scoped-functions@npm:^7.26.5": + version: 7.26.5 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.26.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.25.9" + "@babel/helper-plugin-utils": "npm:^7.26.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/bf31896556b33a80f017af3d445ceb532ec0f5ca9d69bc211a963ac92514d172d5c24c5ac319f384d9dfa7f1a4d8dc23032c2fe3e74f98a59467ecd86f7033ae + checksum: 10/f2046c09bf8e588bfb1a6342d0eee733189102cf663ade27adb0130f3865123af5816b40a55ec8d8fa09271b54dfdaf977cd2f8e0b3dc97f18e690188d5a2174 languageName: node linkType: hard @@ -840,15 +820,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-exponentiation-operator@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.25.9" +"@babel/plugin-transform-exponentiation-operator@npm:^7.26.3": + version: 7.26.3 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.26.3" dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor": "npm:^7.25.9" "@babel/helper-plugin-utils": "npm:^7.25.9" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/57e1bb4135dd16782fe84b49dd360cce8f9bf5f62eb10424dcdaf221e54a8bacdf50f2541c5ac01dea9f833a6c628613d71be915290938a93454389cba4de06b + checksum: 10/0d8da2e552a50a775fe8e6e3c32621d20d3c5d1af7ab40ca2f5c7603de057b57b1b5850f74040e4ecbe36c09ac86d92173ad1e223a2a3b3df3cc359ca4349738 languageName: node linkType: hard @@ -944,16 +923,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.22.5, @babel/plugin-transform-modules-commonjs@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/plugin-transform-modules-commonjs@npm:7.25.9" +"@babel/plugin-transform-modules-commonjs@npm:^7.22.5, @babel/plugin-transform-modules-commonjs@npm:^7.26.3": + version: 7.26.3 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.26.3" dependencies: - "@babel/helper-module-transforms": "npm:^7.25.9" + "@babel/helper-module-transforms": "npm:^7.26.0" "@babel/helper-plugin-utils": "npm:^7.25.9" - "@babel/helper-simple-access": "npm:^7.25.9" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/a7390ca999373ccdef91075f274d1ace3a5cb79f9b9118ed6f76e94867ed454cf798a6f312ce2c4cdc1e035a25d810d754e4cb2e4d866acb4219490f3585de60 + checksum: 10/f817f02fa04d13f1578f3026239b57f1003bebcf9f9b8d854714bed76a0e4986c79bd6d2e0ac14282c5d309454a8dab683c179709ca753b0152a69c69f3a78e3 languageName: node linkType: hard @@ -1006,14 +984,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.25.9" +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.26.6": + version: 7.26.6 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.26.6" dependencies: - "@babel/helper-plugin-utils": "npm:^7.25.9" + "@babel/helper-plugin-utils": "npm:^7.26.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/26e03b1c2c0408cc300e46d8f8cb639653ff3a7b03456d0d8afbb53c44f33a89323f51d99991dade3a5676921119bbdf869728bb7911799b5ef99ffafa2cdd24 + checksum: 10/3832609f043dd1cd8076ab6a00a201573ef3f95bb2144d57787e4a973b3189884c16b4e77ff8e84a6ca47bc3b65bb7df10dca2f6163dfffc316ac96c37b0b5a6 languageName: node linkType: hard @@ -1252,14 +1230,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typeof-symbol@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/plugin-transform-typeof-symbol@npm:7.25.9" +"@babel/plugin-transform-typeof-symbol@npm:^7.26.7": + version: 7.26.7 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.26.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.25.9" + "@babel/helper-plugin-utils": "npm:^7.26.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/3ae240358f0b0cd59f8610d6c59d395c216fd1bab407f7de58b86d592f030fb42b4d18e2456a29bee4a2ff014c4c1e3404c8ae64462b1155d1c053b2f9d73438 + checksum: 10/c4ed244c9f252f941f4dff4b6ad06f6d6f5860e9aa5a6cccb5725ead670f2dab58bba4bad9c2b7bd25685e5205fde810857df964d417072c5c282bbfa4f6bf7a languageName: node linkType: hard @@ -1320,13 +1298,13 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:7.26.0": - version: 7.26.0 - resolution: "@babel/preset-env@npm:7.26.0" +"@babel/preset-env@npm:7.26.7": + version: 7.26.7 + resolution: "@babel/preset-env@npm:7.26.7" dependencies: - "@babel/compat-data": "npm:^7.26.0" - "@babel/helper-compilation-targets": "npm:^7.25.9" - "@babel/helper-plugin-utils": "npm:^7.25.9" + "@babel/compat-data": "npm:^7.26.5" + "@babel/helper-compilation-targets": "npm:^7.26.5" + "@babel/helper-plugin-utils": "npm:^7.26.5" "@babel/helper-validator-option": "npm:^7.25.9" "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "npm:^7.25.9" "@babel/plugin-bugfix-safari-class-field-initializer-scope": "npm:^7.25.9" @@ -1340,7 +1318,7 @@ __metadata: "@babel/plugin-transform-arrow-functions": "npm:^7.25.9" "@babel/plugin-transform-async-generator-functions": "npm:^7.25.9" "@babel/plugin-transform-async-to-generator": "npm:^7.25.9" - "@babel/plugin-transform-block-scoped-functions": "npm:^7.25.9" + "@babel/plugin-transform-block-scoped-functions": "npm:^7.26.5" "@babel/plugin-transform-block-scoping": "npm:^7.25.9" "@babel/plugin-transform-class-properties": "npm:^7.25.9" "@babel/plugin-transform-class-static-block": "npm:^7.26.0" @@ -1351,7 +1329,7 @@ __metadata: "@babel/plugin-transform-duplicate-keys": "npm:^7.25.9" "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "npm:^7.25.9" "@babel/plugin-transform-dynamic-import": "npm:^7.25.9" - "@babel/plugin-transform-exponentiation-operator": "npm:^7.25.9" + "@babel/plugin-transform-exponentiation-operator": "npm:^7.26.3" "@babel/plugin-transform-export-namespace-from": "npm:^7.25.9" "@babel/plugin-transform-for-of": "npm:^7.25.9" "@babel/plugin-transform-function-name": "npm:^7.25.9" @@ -1360,12 +1338,12 @@ __metadata: "@babel/plugin-transform-logical-assignment-operators": "npm:^7.25.9" "@babel/plugin-transform-member-expression-literals": "npm:^7.25.9" "@babel/plugin-transform-modules-amd": "npm:^7.25.9" - "@babel/plugin-transform-modules-commonjs": "npm:^7.25.9" + "@babel/plugin-transform-modules-commonjs": "npm:^7.26.3" "@babel/plugin-transform-modules-systemjs": "npm:^7.25.9" "@babel/plugin-transform-modules-umd": "npm:^7.25.9" "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.25.9" "@babel/plugin-transform-new-target": "npm:^7.25.9" - "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.25.9" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.26.6" "@babel/plugin-transform-numeric-separator": "npm:^7.25.9" "@babel/plugin-transform-object-rest-spread": "npm:^7.25.9" "@babel/plugin-transform-object-super": "npm:^7.25.9" @@ -1382,7 +1360,7 @@ __metadata: "@babel/plugin-transform-spread": "npm:^7.25.9" "@babel/plugin-transform-sticky-regex": "npm:^7.25.9" "@babel/plugin-transform-template-literals": "npm:^7.25.9" - "@babel/plugin-transform-typeof-symbol": "npm:^7.25.9" + "@babel/plugin-transform-typeof-symbol": "npm:^7.26.7" "@babel/plugin-transform-unicode-escapes": "npm:^7.25.9" "@babel/plugin-transform-unicode-property-regex": "npm:^7.25.9" "@babel/plugin-transform-unicode-regex": "npm:^7.25.9" @@ -1395,7 +1373,7 @@ __metadata: semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/a7a80314f845deea713985a6316361c476621c76cfe5c6c28e8b9558f01634b49bbfdd3581ef94b5d6cff5c2b8830468aa53a73f5b5c1224db2dfea5db7e676f + checksum: 10/d5833ac61580ca8ca672466d06edcf523b49f400caa8f4b8f21358a30875a8ca1628a250b89369e8a0be3439f6ae0002af6f64335794b06acaf603906055f43a languageName: node linkType: hard @@ -1438,12 +1416,12 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:7.26.0, @babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.14.0, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.24.5, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.25.0, @babel/runtime@npm:^7.25.6, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": - version: 7.26.0 - resolution: "@babel/runtime@npm:7.26.0" +"@babel/runtime@npm:7.26.7, @babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.14.0, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.24.5, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.25.0, @babel/runtime@npm:^7.25.6, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": + version: 7.26.7 + resolution: "@babel/runtime@npm:7.26.7" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: 10/9f4ea1c1d566c497c052d505587554e782e021e6ccd302c2ad7ae8291c8e16e3f19d4a7726fb64469e057779ea2081c28b7dbefec6d813a22f08a35712c0f699 + checksum: 10/c7a661a6836b332d9d2e047cba77ba1862c1e4f78cec7146db45808182ef7636d8a7170be9797e5d8fd513180bffb9fa16f6ca1c69341891efec56113cf22bfc languageName: node linkType: hard @@ -1458,28 +1436,28 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.22.8, @babel/traverse@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/traverse@npm:7.25.9" +"@babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.22.8, @babel/traverse@npm:^7.25.9, @babel/traverse@npm:^7.26.7": + version: 7.26.7 + resolution: "@babel/traverse@npm:7.26.7" dependencies: - "@babel/code-frame": "npm:^7.25.9" - "@babel/generator": "npm:^7.25.9" - "@babel/parser": "npm:^7.25.9" + "@babel/code-frame": "npm:^7.26.2" + "@babel/generator": "npm:^7.26.5" + "@babel/parser": "npm:^7.26.7" "@babel/template": "npm:^7.25.9" - "@babel/types": "npm:^7.25.9" + "@babel/types": "npm:^7.26.7" debug: "npm:^4.3.1" globals: "npm:^11.1.0" - checksum: 10/7431614d76d4a053e429208db82f2846a415833f3d9eb2e11ef72eeb3c64dfd71f4a4d983de1a4a047b36165a1f5a64de8ca2a417534cc472005c740ffcb9c6a + checksum: 10/c821c9682fe0b9edf7f7cbe9cc3e0787ffee3f73b52c13b21b463f8979950a6433f5e7e482a74348d22c0b7a05180e6f72b23eb6732328b49c59fc6388ebf6e5 languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.5, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.9, @babel/types@npm:^7.26.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": - version: 7.26.0 - resolution: "@babel/types@npm:7.26.0" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.5, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.9, @babel/types@npm:^7.26.5, @babel/types@npm:^7.26.7, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": + version: 7.26.7 + resolution: "@babel/types@npm:7.26.7" dependencies: "@babel/helper-string-parser": "npm:^7.25.9" "@babel/helper-validator-identifier": "npm:^7.25.9" - checksum: 10/40780741ecec886ed9edae234b5eb4976968cc70d72b4e5a40d55f83ff2cc457de20f9b0f4fe9d858350e43dab0ea496e7ef62e2b2f08df699481a76df02cd6e + checksum: 10/2264efd02cc261ca5d1c5bc94497c8995238f28afd2b7483b24ea64dd694cf46b00d51815bf0c87f0d0061ea221569c77893aeecb0d4b4bb254e9c2f938d7669 languageName: node linkType: hard @@ -1861,7 +1839,7 @@ __metadata: languageName: node linkType: hard -"@emotion/babel-plugin@npm:^11.11.0, @emotion/babel-plugin@npm:^11.13.5": +"@emotion/babel-plugin@npm:^11.11.0, @emotion/babel-plugin@npm:^11.12.0, @emotion/babel-plugin@npm:^11.13.5": version: 11.13.5 resolution: "@emotion/babel-plugin@npm:11.13.5" dependencies: @@ -1880,7 +1858,7 @@ __metadata: languageName: node linkType: hard -"@emotion/cache@npm:^11.11.0, @emotion/cache@npm:^11.13.5, @emotion/cache@npm:^11.14.0, @emotion/cache@npm:^11.4.0": +"@emotion/cache@npm:^11.11.0, @emotion/cache@npm:^11.13.0, @emotion/cache@npm:^11.13.5, @emotion/cache@npm:^11.14.0, @emotion/cache@npm:^11.4.0": version: 11.14.0 resolution: "@emotion/cache@npm:11.14.0" dependencies: @@ -1906,6 +1884,19 @@ __metadata: languageName: node linkType: hard +"@emotion/css@npm:11.13.4": + version: 11.13.4 + resolution: "@emotion/css@npm:11.13.4" + dependencies: + "@emotion/babel-plugin": "npm:^11.12.0" + "@emotion/cache": "npm:^11.13.0" + "@emotion/serialize": "npm:^1.3.0" + "@emotion/sheet": "npm:^1.4.0" + "@emotion/utils": "npm:^1.4.0" + checksum: 10/57565a8bd9b712b0ade1c8b972bf2f84d2026e4372b3b035fb9d93a85a8f36ca7f2fbe67ecf32cc3fd03956587ece56ab89dd5bd43a76d3aed542a76841c76e5 + languageName: node + linkType: hard + "@emotion/css@npm:11.13.5, @emotion/css@npm:^11.11.2": version: 11.13.5 resolution: "@emotion/css@npm:11.13.5" @@ -1960,6 +1951,27 @@ __metadata: languageName: node linkType: hard +"@emotion/react@npm:11.13.3": + version: 11.13.3 + resolution: "@emotion/react@npm:11.13.3" + dependencies: + "@babel/runtime": "npm:^7.18.3" + "@emotion/babel-plugin": "npm:^11.12.0" + "@emotion/cache": "npm:^11.13.0" + "@emotion/serialize": "npm:^1.3.1" + "@emotion/use-insertion-effect-with-fallbacks": "npm:^1.1.0" + "@emotion/utils": "npm:^1.4.0" + "@emotion/weak-memoize": "npm:^0.4.0" + hoist-non-react-statics: "npm:^3.3.1" + peerDependencies: + react: ">=16.8.0" + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/ee70d3afc2e8dd771e6fe176d27dd87a5e21a54e54d871438fd1caa5aa2312d848c6866292fdc65a6ea1c945147c8422bda2d22ed739178af9902dc86d6b298a + languageName: node + linkType: hard + "@emotion/react@npm:11.14.0, @emotion/react@npm:^11.8.1": version: 11.14.0 resolution: "@emotion/react@npm:11.14.0" @@ -1981,7 +1993,20 @@ __metadata: languageName: node linkType: hard -"@emotion/serialize@npm:1.3.3, @emotion/serialize@npm:^1.1.2, @emotion/serialize@npm:^1.3.3": +"@emotion/serialize@npm:1.3.2": + version: 1.3.2 + resolution: "@emotion/serialize@npm:1.3.2" + dependencies: + "@emotion/hash": "npm:^0.9.2" + "@emotion/memoize": "npm:^0.9.0" + "@emotion/unitless": "npm:^0.10.0" + "@emotion/utils": "npm:^1.4.1" + csstype: "npm:^3.0.2" + checksum: 10/ead557c1ff19d917ef8169c02738ef36f0851fbfdf0bf69a543045bddea3b7281dc8252ee466cc5fb44ed27d1e61280ff943bb60a2c04158751fb07b3457cc93 + languageName: node + linkType: hard + +"@emotion/serialize@npm:1.3.3, @emotion/serialize@npm:^1.1.2, @emotion/serialize@npm:^1.3.0, @emotion/serialize@npm:^1.3.1, @emotion/serialize@npm:^1.3.3": version: 1.3.3 resolution: "@emotion/serialize@npm:1.3.3" dependencies: @@ -2008,7 +2033,7 @@ __metadata: languageName: node linkType: hard -"@emotion/use-insertion-effect-with-fallbacks@npm:^1.2.0": +"@emotion/use-insertion-effect-with-fallbacks@npm:^1.1.0, @emotion/use-insertion-effect-with-fallbacks@npm:^1.2.0": version: 1.2.0 resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.2.0" peerDependencies: @@ -2017,7 +2042,7 @@ __metadata: languageName: node linkType: hard -"@emotion/utils@npm:^1.2.1, @emotion/utils@npm:^1.4.2": +"@emotion/utils@npm:^1.2.1, @emotion/utils@npm:^1.4.0, @emotion/utils@npm:^1.4.1, @emotion/utils@npm:^1.4.2": version: 1.4.2 resolution: "@emotion/utils@npm:1.4.2" checksum: 10/e5f3b8bca066b3361a7ad9064baeb9d01ed1bf51d98416a67359b62cb3affec6bb0249802c4ed11f4f8030f93cc4b67506909420bdb110adec6983d712897208 @@ -2042,13 +2067,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/aix-ppc64@npm:0.21.5" - conditions: os=aix & cpu=ppc64 - languageName: node - linkType: hard - "@esbuild/aix-ppc64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/aix-ppc64@npm:0.24.2" @@ -2056,10 +2074,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/android-arm64@npm:0.21.5" - conditions: os=android & cpu=arm64 +"@esbuild/aix-ppc64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/aix-ppc64@npm:0.25.0" + conditions: os=aix & cpu=ppc64 languageName: node linkType: hard @@ -2070,10 +2088,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/android-arm@npm:0.21.5" - conditions: os=android & cpu=arm +"@esbuild/android-arm64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/android-arm64@npm:0.25.0" + conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -2084,10 +2102,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/android-x64@npm:0.21.5" - conditions: os=android & cpu=x64 +"@esbuild/android-arm@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/android-arm@npm:0.25.0" + conditions: os=android & cpu=arm languageName: node linkType: hard @@ -2098,10 +2116,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/darwin-arm64@npm:0.21.5" - conditions: os=darwin & cpu=arm64 +"@esbuild/android-x64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/android-x64@npm:0.25.0" + conditions: os=android & cpu=x64 languageName: node linkType: hard @@ -2112,10 +2130,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/darwin-x64@npm:0.21.5" - conditions: os=darwin & cpu=x64 +"@esbuild/darwin-arm64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/darwin-arm64@npm:0.25.0" + conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -2126,10 +2144,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/freebsd-arm64@npm:0.21.5" - conditions: os=freebsd & cpu=arm64 +"@esbuild/darwin-x64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/darwin-x64@npm:0.25.0" + conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -2140,10 +2158,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/freebsd-x64@npm:0.21.5" - conditions: os=freebsd & cpu=x64 +"@esbuild/freebsd-arm64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/freebsd-arm64@npm:0.25.0" + conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard @@ -2154,10 +2172,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-arm64@npm:0.21.5" - conditions: os=linux & cpu=arm64 +"@esbuild/freebsd-x64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/freebsd-x64@npm:0.25.0" + conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -2168,10 +2186,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-arm@npm:0.21.5" - conditions: os=linux & cpu=arm +"@esbuild/linux-arm64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/linux-arm64@npm:0.25.0" + conditions: os=linux & cpu=arm64 languageName: node linkType: hard @@ -2182,10 +2200,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-ia32@npm:0.21.5" - conditions: os=linux & cpu=ia32 +"@esbuild/linux-arm@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/linux-arm@npm:0.25.0" + conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -2196,10 +2214,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-loong64@npm:0.21.5" - conditions: os=linux & cpu=loong64 +"@esbuild/linux-ia32@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/linux-ia32@npm:0.25.0" + conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -2210,10 +2228,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-mips64el@npm:0.21.5" - conditions: os=linux & cpu=mips64el +"@esbuild/linux-loong64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/linux-loong64@npm:0.25.0" + conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -2224,10 +2242,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-ppc64@npm:0.21.5" - conditions: os=linux & cpu=ppc64 +"@esbuild/linux-mips64el@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/linux-mips64el@npm:0.25.0" + conditions: os=linux & cpu=mips64el languageName: node linkType: hard @@ -2238,10 +2256,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-riscv64@npm:0.21.5" - conditions: os=linux & cpu=riscv64 +"@esbuild/linux-ppc64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/linux-ppc64@npm:0.25.0" + conditions: os=linux & cpu=ppc64 languageName: node linkType: hard @@ -2252,10 +2270,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-s390x@npm:0.21.5" - conditions: os=linux & cpu=s390x +"@esbuild/linux-riscv64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/linux-riscv64@npm:0.25.0" + conditions: os=linux & cpu=riscv64 languageName: node linkType: hard @@ -2266,10 +2284,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-x64@npm:0.21.5" - conditions: os=linux & cpu=x64 +"@esbuild/linux-s390x@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/linux-s390x@npm:0.25.0" + conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -2280,6 +2298,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/linux-x64@npm:0.25.0" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-arm64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/netbsd-arm64@npm:0.24.2" @@ -2287,10 +2312,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/netbsd-x64@npm:0.21.5" - conditions: os=netbsd & cpu=x64 +"@esbuild/netbsd-arm64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/netbsd-arm64@npm:0.25.0" + conditions: os=netbsd & cpu=arm64 languageName: node linkType: hard @@ -2301,6 +2326,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/netbsd-x64@npm:0.25.0" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-arm64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/openbsd-arm64@npm:0.24.2" @@ -2308,10 +2340,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/openbsd-x64@npm:0.21.5" - conditions: os=openbsd & cpu=x64 +"@esbuild/openbsd-arm64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/openbsd-arm64@npm:0.25.0" + conditions: os=openbsd & cpu=arm64 languageName: node linkType: hard @@ -2322,10 +2354,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/sunos-x64@npm:0.21.5" - conditions: os=sunos & cpu=x64 +"@esbuild/openbsd-x64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/openbsd-x64@npm:0.25.0" + conditions: os=openbsd & cpu=x64 languageName: node linkType: hard @@ -2336,10 +2368,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/win32-arm64@npm:0.21.5" - conditions: os=win32 & cpu=arm64 +"@esbuild/sunos-x64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/sunos-x64@npm:0.25.0" + conditions: os=sunos & cpu=x64 languageName: node linkType: hard @@ -2350,10 +2382,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/win32-ia32@npm:0.21.5" - conditions: os=win32 & cpu=ia32 +"@esbuild/win32-arm64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/win32-arm64@npm:0.25.0" + conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -2364,10 +2396,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/win32-x64@npm:0.21.5" - conditions: os=win32 & cpu=x64 +"@esbuild/win32-ia32@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/win32-ia32@npm:0.25.0" + conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -2378,6 +2410,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.25.0": + version: 0.25.0 + resolution: "@esbuild/win32-x64@npm:0.25.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" @@ -2433,10 +2472,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.18.0": - version: 9.18.0 - resolution: "@eslint/js@npm:9.18.0" - checksum: 10/364a7d030dad9dbda1458d8dbcea0199fe7d48bcfefe4b49389df6c45cdc5a2449f70e5d8a794e46ed9fb34af3fe5a3f53e30020d306b6ee791e2a1b2b9fa25f +"@eslint/js@npm:9.19.0": + version: 9.19.0 + resolution: "@eslint/js@npm:9.19.0" + checksum: 10/d8133a83330676d5f0827713af2e9bbf35530631a93520fb59ead6b827a325c54fdd7ad99f2158f895fb393c47bbc55dfdaa945998a647f3b9230f1d5324a626 languageName: node linkType: hard @@ -2511,6 +2550,20 @@ __metadata: languageName: node linkType: hard +"@floating-ui/react@npm:0.26.24": + version: 0.26.24 + resolution: "@floating-ui/react@npm:0.26.24" + dependencies: + "@floating-ui/react-dom": "npm:^2.1.2" + "@floating-ui/utils": "npm:^0.2.8" + tabbable: "npm:^6.0.0" + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 10/903ffbee2c6726d117086e2a83f43d6ad339970758ce7979fd16cc7cf8dc0f5b869bd72c2c8ee1bcd6c63b190bb0960effd4d403e63685fb5aeed6b185041b08 + languageName: node + linkType: hard + "@floating-ui/react@npm:0.27.3": version: 0.27.3 resolution: "@floating-ui/react@npm:0.27.3" @@ -2665,21 +2718,21 @@ __metadata: resolution: "@grafana-plugins/grafana-azure-monitor-datasource@workspace:public/app/plugins/datasource/azuremonitor" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/experimental": "npm:2.1.6" - "@grafana/plugin-configs": "npm:11.5.0-pre" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" + "@grafana/plugin-ui": "npm:0.10.1" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@kusto/monaco-kusto": "npm:^10.0.0" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.3" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/jest": "npm:29.5.14" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/prismjs": "npm:1.26.5" "@types/react": "npm:18.3.18" "@types/react-dom": "npm:18.3.5" @@ -2691,7 +2744,7 @@ __metadata: prismjs: "npm:1.29.0" react: "npm:18.3.1" react-dom: "npm:18.3.1" - react-select: "npm:5.9.0" + react-select: "npm:5.10.0" react-select-event: "npm:5.5.1" react-use: "npm:17.6.0" rxjs: "npm:7.8.1" @@ -2709,19 +2762,19 @@ __metadata: resolution: "@grafana-plugins/grafana-postgresql-datasource@workspace:public/app/plugins/datasource/grafana-postgresql-datasource" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/plugin-configs": "npm:11.5.0-pre" - "@grafana/plugin-ui": "npm:^0.9.6" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/sql": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" + "@grafana/plugin-ui": "npm:0.10.1" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/sql": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/jest": "npm:29.5.14" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/react": "npm:18.3.18" lodash: "npm:4.17.21" react: "npm:18.3.1" @@ -2740,18 +2793,18 @@ __metadata: resolution: "@grafana-plugins/grafana-pyroscope-datasource@workspace:public/app/plugins/datasource/grafana-pyroscope-datasource" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/plugin-configs": "npm:11.5.0-pre" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.3" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/jest": "npm:29.5.14" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/prismjs": "npm:1.26.5" "@types/react": "npm:18.3.18" "@types/react-dom": "npm:18.3.5" @@ -2780,20 +2833,19 @@ __metadata: resolution: "@grafana-plugins/grafana-testdata-datasource@workspace:public/app/plugins/datasource/grafana-testdata-datasource" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/experimental": "npm:2.1.6" - "@grafana/plugin-configs": "npm:11.5.0-pre" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/d3-random": "npm:^3.0.2" "@types/jest": "npm:29.5.14" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/react": "npm:18.3.18" "@types/react-dom": "npm:18.3.5" "@types/uuid": "npm:10.0.0" @@ -2802,7 +2854,7 @@ __metadata: micro-memoize: "npm:^4.1.2" react: "npm:18.3.1" react-dom: "npm:18.3.1" - react-select: "npm:5.9.0" + react-select: "npm:5.10.0" react-use: "npm:17.6.0" rxjs: "npm:7.8.1" ts-node: "npm:10.9.2" @@ -2824,17 +2876,17 @@ __metadata: "@grafana/e2e-selectors": "workspace:*" "@grafana/o11y-ds-frontend": "workspace:*" "@grafana/plugin-configs": "workspace:*" - "@grafana/plugin-ui": "npm:^0.9.6" + "@grafana/plugin-ui": "npm:0.10.1" "@grafana/runtime": "workspace:*" "@grafana/ui": "workspace:*" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.3" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/jest": "npm:29.5.14" - "@types/lodash": "npm:4.17.14" + "@types/lodash": "npm:4.17.15" "@types/logfmt": "npm:^1.2.3" - "@types/node": "npm:22.10.7" + "@types/node": "npm:22.12.0" "@types/react": "npm:18.3.18" "@types/react-dom": "npm:18.3.5" "@types/react-window": "npm:1.8.8" @@ -2843,7 +2895,7 @@ __metadata: logfmt: "npm:^1.3.2" react: "npm:18.3.1" react-dom: "npm:18.3.1" - react-select: "npm:5.9.0" + react-select: "npm:5.10.0" react-window: "npm:1.8.11" rxjs: "npm:7.8.1" stream-browserify: "npm:3.0.0" @@ -2862,19 +2914,19 @@ __metadata: resolution: "@grafana-plugins/mssql@workspace:public/app/plugins/datasource/mssql" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/experimental": "npm:2.1.6" - "@grafana/plugin-configs": "npm:11.5.0-pre" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/sql": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" + "@grafana/plugin-ui": "npm:0.10.1" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/sql": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/jest": "npm:29.5.14" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/react": "npm:18.3.18" lodash: "npm:4.17.21" react: "npm:18.3.1" @@ -2893,19 +2945,19 @@ __metadata: resolution: "@grafana-plugins/mysql@workspace:public/app/plugins/datasource/mysql" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/plugin-configs": "npm:11.5.0-pre" - "@grafana/plugin-ui": "npm:^0.9.6" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/sql": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" + "@grafana/plugin-ui": "npm:0.10.1" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/sql": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/jest": "npm:29.5.14" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/react": "npm:18.3.18" lodash: "npm:4.17.21" react: "npm:18.3.1" @@ -2924,16 +2976,16 @@ __metadata: resolution: "@grafana-plugins/parca@workspace:public/app/plugins/datasource/parca" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/plugin-configs": "npm:11.5.0-pre" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/react": "npm:18.3.18" "@types/react-dom": "npm:18.3.5" lodash: "npm:4.17.21" @@ -2956,22 +3008,22 @@ __metadata: resolution: "@grafana-plugins/stackdriver@workspace:public/app/plugins/datasource/cloud-monitoring" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/experimental": "npm:2.1.6" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" "@grafana/google-sdk": "npm:0.1.2" - "@grafana/plugin-configs": "npm:11.5.0-pre" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" + "@grafana/plugin-ui": "npm:0.10.1" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.3" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/debounce-promise": "npm:3.1.9" "@types/jest": "npm:29.5.14" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/prismjs": "npm:1.26.5" "@types/react": "npm:18.3.18" "@types/react-dom": "npm:18.3.5" @@ -2984,7 +3036,7 @@ __metadata: prismjs: "npm:1.29.0" react: "npm:18.3.1" react-dom: "npm:18.3.1" - react-select: "npm:5.9.0" + react-select: "npm:5.10.0" react-select-event: "npm:5.5.1" react-use: "npm:17.6.0" rxjs: "npm:7.8.1" @@ -3004,12 +3056,12 @@ __metadata: "@emotion/css": "npm:11.13.5" "@grafana/data": "workspace:*" "@grafana/e2e-selectors": "workspace:*" - "@grafana/experimental": "npm:2.1.6" "@grafana/lezer-logql": "npm:0.2.6" "@grafana/lezer-traceql": "npm:0.0.20" "@grafana/monaco-logql": "npm:^0.0.8" "@grafana/o11y-ds-frontend": "workspace:*" - "@grafana/plugin-configs": "npm:11.5.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" + "@grafana/plugin-ui": "npm:0.10.1" "@grafana/runtime": "workspace:*" "@grafana/schema": "workspace:*" "@grafana/ui": "workspace:*" @@ -3020,11 +3072,11 @@ __metadata: "@opentelemetry/semantic-conventions": "npm:1.28.0" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.3" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/jest": "npm:29.5.14" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/prismjs": "npm:1.26.5" "@types/react": "npm:18.3.18" "@types/react-dom": "npm:18.3.5" @@ -3040,11 +3092,11 @@ __metadata: prismjs: "npm:1.29.0" react: "npm:18.3.1" react-dom: "npm:18.3.1" - react-select: "npm:5.9.0" + react-select: "npm:5.10.0" react-select-event: "npm:5.5.1" react-use: "npm:17.6.0" rxjs: "npm:7.8.1" - semver: "npm:7.6.3" + semver: "npm:7.7.0" stream-browserify: "npm:3.0.0" string_decoder: "npm:1.3.0" ts-node: "npm:10.9.2" @@ -3066,21 +3118,21 @@ __metadata: "@grafana/e2e-selectors": "workspace:*" "@grafana/o11y-ds-frontend": "workspace:*" "@grafana/plugin-configs": "workspace:*" - "@grafana/plugin-ui": "npm:^0.9.6" + "@grafana/plugin-ui": "npm:0.10.1" "@grafana/runtime": "workspace:*" "@grafana/ui": "workspace:*" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.3" - "@testing-library/react": "npm:16.1.0" + "@testing-library/react": "npm:16.2.0" "@types/jest": "npm:29.5.14" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/react": "npm:18.3.18" "@types/react-dom": "npm:18.3.5" lodash: "npm:4.17.21" react: "npm:18.3.1" react-dom: "npm:18.3.1" - react-select: "npm:5.9.0" + react-select: "npm:5.10.0" react-use: "npm:17.6.0" rxjs: "npm:7.8.1" ts-node: "npm:10.9.2" @@ -3121,18 +3173,53 @@ __metadata: languageName: node linkType: hard -"@grafana/data@npm:11.5.0-pre, @grafana/data@workspace:*, @grafana/data@workspace:packages/grafana-data": +"@grafana/data@npm:11.4.0, @grafana/data@npm:^10.4.0 ||^11": + version: 11.4.0 + resolution: "@grafana/data@npm:11.4.0" + dependencies: + "@braintree/sanitize-url": "npm:7.0.1" + "@grafana/schema": "npm:11.4.0" + "@types/d3-interpolate": "npm:^3.0.0" + "@types/string-hash": "npm:1.1.3" + d3-interpolate: "npm:3.0.1" + date-fns: "npm:3.6.0" + dompurify: "npm:^3.0.0" + eventemitter3: "npm:5.0.1" + fast_array_intersect: "npm:1.1.0" + history: "npm:4.10.1" + lodash: "npm:4.17.21" + marked: "npm:12.0.2" + marked-mangle: "npm:1.1.9" + moment: "npm:2.30.1" + moment-timezone: "npm:0.5.46" + ol: "npm:7.4.0" + papaparse: "npm:5.4.1" + react-use: "npm:17.5.1" + rxjs: "npm:7.8.1" + string-hash: "npm:^1.1.3" + tinycolor2: "npm:1.6.0" + tslib: "npm:2.7.0" + uplot: "npm:1.6.31" + xss: "npm:^1.0.14" + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: 10/14bbf83a7c1fe3f8bbc3ddf44e2fd6393f46aa0cdf802e61ac4e2b2719e05008ceef8ddb1e4d0d164ae86ef01afef8310cf79b927bf6f1cfce45ed0c48a21af3 + languageName: node + linkType: hard + +"@grafana/data@npm:11.6.0-pre, @grafana/data@workspace:*, @grafana/data@workspace:packages/grafana-data": version: 0.0.0-use.local resolution: "@grafana/data@workspace:packages/grafana-data" dependencies: "@braintree/sanitize-url": "npm:7.0.1" - "@grafana/schema": "npm:11.5.0-pre" + "@grafana/schema": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" "@rollup/plugin-node-resolve": "npm:16.0.0" "@types/d3-interpolate": "npm:^3.0.0" "@types/history": "npm:4.7.11" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/papaparse": "npm:5.3.15" "@types/react": "npm:18.3.18" "@types/react-dom": "npm:18.3.5" @@ -3140,8 +3227,8 @@ __metadata: "@types/tinycolor2": "npm:1.4.6" d3-interpolate: "npm:3.0.1" date-fns: "npm:4.1.0" - dompurify: "npm:3.2.3" - esbuild: "npm:0.24.2" + dompurify: "npm:3.2.4" + esbuild: "npm:0.25.0" eventemitter3: "npm:5.0.1" fast_array_intersect: "npm:1.1.0" history: "npm:4.10.1" @@ -3149,16 +3236,16 @@ __metadata: marked: "npm:15.0.6" marked-mangle: "npm:1.1.10" moment: "npm:2.30.1" - moment-timezone: "npm:0.5.46" + moment-timezone: "npm:0.5.47" ol: "npm:7.4.0" - papaparse: "npm:5.5.1" + papaparse: "npm:5.5.2" react: "npm:18.3.1" react-dom: "npm:18.3.1" react-use: "npm:17.6.0" rimraf: "npm:6.0.1" rollup: "npm:^4.22.4" rollup-plugin-dts: "npm:^6.1.1" - rollup-plugin-esbuild: "npm:6.1.1" + rollup-plugin-esbuild: "npm:6.2.0" rollup-plugin-node-externals: "npm:^8.0.0" rxjs: "npm:7.8.1" string-hash: "npm:^1.1.3" @@ -3173,21 +3260,32 @@ __metadata: languageName: unknown linkType: soft -"@grafana/e2e-selectors@npm:11.5.0-pre, @grafana/e2e-selectors@workspace:*, @grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors": +"@grafana/e2e-selectors@npm:11.4.0": + version: 11.4.0 + resolution: "@grafana/e2e-selectors@npm:11.4.0" + dependencies: + "@grafana/tsconfig": "npm:^2.0.0" + tslib: "npm:2.7.0" + typescript: "npm:5.5.4" + checksum: 10/dd6861415430ab8e9a5e66d8f008dd4ceadd3a7f2ba50be6d6fa7c24455e43773a12ef094838e72ede617f14d202693e01b08db44f02657039ded2858fc9fa24 + languageName: node + linkType: hard + +"@grafana/e2e-selectors@npm:11.6.0-pre, @grafana/e2e-selectors@workspace:*, @grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors": version: 0.0.0-use.local resolution: "@grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors" dependencies: "@grafana/tsconfig": "npm:^2.0.0" "@rollup/plugin-node-resolve": "npm:16.0.0" - "@types/node": "npm:22.10.7" + "@types/node": "npm:22.12.0" "@types/semver": "npm:7.5.8" - esbuild: "npm:0.24.2" + esbuild: "npm:0.25.0" rimraf: "npm:6.0.1" rollup: "npm:^4.22.4" rollup-plugin-dts: "npm:^6.1.1" - rollup-plugin-esbuild: "npm:6.1.1" + rollup-plugin-esbuild: "npm:6.2.0" rollup-plugin-node-externals: "npm:^8.0.0" - semver: "npm:7.6.3" + semver: "npm:7.7.0" tslib: "npm:2.8.1" typescript: "npm:5.7.3" languageName: unknown @@ -3222,63 +3320,37 @@ __metadata: dependencies: "@typescript-eslint/types": "npm:^8.9.0" "@typescript-eslint/utils": "npm:^8.9.0" - eslint: "npm:9.18.0" + eslint: "npm:9.19.0" tslib: "npm:2.8.1" languageName: unknown linkType: soft -"@grafana/experimental@npm:2.1.6": - version: 2.1.6 - resolution: "@grafana/experimental@npm:2.1.6" - dependencies: - "@hello-pangea/dnd": "npm:^16.6.0" - "@types/uuid": "npm:^8.3.3" - lodash: "npm:^4.17.21" - prismjs: "npm:^1.29.0" - react-popper-tooltip: "npm:^4.4.2" - react-use: "npm:^17.4.2" - semver: "npm:^7.5.4" - uuid: "npm:^8.3.2" - peerDependencies: - "@emotion/css": ^11.11.2 - "@grafana/data": ^10.4.0 || ^11.0.0 - "@grafana/e2e-selectors": ^10.0.0 || ^11.0.0 - "@grafana/runtime": ^10.4.0 || ^11.0.0 - "@grafana/ui": ^10.4.0 || ^11.0.0 - react: ^18.2.0 - react-dom: ^18.2.0 - react-select: ^5.8.0 - rxjs: ^7.8.1 - checksum: 10/3df81fef944e6dfdf843bf5954a586fe3eb90a1a2671088ae526b29fb9118793c05be96a3edd3d3babfdcbfa801fdfb328e87e6cbb1fd0de7f8f2d2e5c32f66f - languageName: node - linkType: hard - -"@grafana/faro-core@npm:^1.12.3, @grafana/faro-core@npm:^1.3.6": - version: 1.12.3 - resolution: "@grafana/faro-core@npm:1.12.3" +"@grafana/faro-core@npm:^1.13.1, @grafana/faro-core@npm:^1.13.2": + version: 1.13.2 + resolution: "@grafana/faro-core@npm:1.13.2" dependencies: "@opentelemetry/api": "npm:^1.9.0" "@opentelemetry/otlp-transformer": "npm:^0.57.1" - checksum: 10/616592a8e65c72be763f27baa3a44c228fc61aeff9cd6c895dac7b4adc24a836accc4766d574a3b5156896b3068c8d7df51c1d715679f3f1a1feef90dcaf82e3 + checksum: 10/513fabf5e90121e1e0a28d36b61aa994b5ad556635366be518e2c23acbaa43e1d0ac744929bfb5930fb1776a2fc6eba33190ce08c058007555e136078987332c languageName: node linkType: hard -"@grafana/faro-web-sdk@npm:1.12.3, @grafana/faro-web-sdk@npm:^1.12.3, @grafana/faro-web-sdk@npm:^1.3.6": - version: 1.12.3 - resolution: "@grafana/faro-web-sdk@npm:1.12.3" +"@grafana/faro-web-sdk@npm:^1.13.2, @grafana/faro-web-sdk@npm:^1.3.6": + version: 1.13.2 + resolution: "@grafana/faro-web-sdk@npm:1.13.2" dependencies: - "@grafana/faro-core": "npm:^1.12.3" + "@grafana/faro-core": "npm:^1.13.1" ua-parser-js: "npm:^1.0.32" web-vitals: "npm:^4.0.1" - checksum: 10/433b118ff5b8bc231e209902c20b8a645efe6efd9ecc4ef57947111fa076842becc9760b5bcc8957c1f04cd20c801241ba9cca0a6d4637b8aa2b034d7a7c1888 + checksum: 10/a6896cbbe3b89867a23ffa4f482eb9510d0e9cd637c92a9e04e06157471bb7ffd35d68b7f6077791393e2203cad69f48ade00e86e54d454da10bbb6179cdc357 languageName: node linkType: hard -"@grafana/faro-web-tracing@npm:^1.8.2": - version: 1.12.3 - resolution: "@grafana/faro-web-tracing@npm:1.12.3" +"@grafana/faro-web-tracing@npm:^1.13.2": + version: 1.13.2 + resolution: "@grafana/faro-web-tracing@npm:1.13.2" dependencies: - "@grafana/faro-web-sdk": "npm:^1.12.3" + "@grafana/faro-web-sdk": "npm:^1.13.2" "@opentelemetry/api": "npm:^1.9.0" "@opentelemetry/context-zone": "npm:1.30.1" "@opentelemetry/core": "npm:^1.30.0" @@ -3290,7 +3362,7 @@ __metadata: "@opentelemetry/resources": "npm:^1.30.0" "@opentelemetry/sdk-trace-web": "npm:^1.30.0" "@opentelemetry/semantic-conventions": "npm:^1.28.0" - checksum: 10/c56873dfa37e4a6f43d5a7fec854031b5e871b1974c90041b243a02e868215ca1bb39f31512730f30aa1268d0601689ff239eedbb534e300b76ded3a83426435 + checksum: 10/bfc67e073457f50d3f5c95d47dfadc900358c08bd1b7d65db2c08bd2b45edcd6ac661bfa82683f1c9ea3710946152aa441a5db665e1e3e578de186fde51fe887 languageName: node linkType: hard @@ -3298,29 +3370,29 @@ __metadata: version: 0.0.0-use.local resolution: "@grafana/flamegraph@workspace:packages/grafana-flamegraph" dependencies: - "@babel/core": "npm:7.26.0" - "@babel/preset-env": "npm:7.26.0" + "@babel/core": "npm:7.26.7" + "@babel/preset-env": "npm:7.26.7" "@babel/preset-react": "npm:7.26.3" "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@leeoniya/ufuzzy": "npm:1.0.18" "@rollup/plugin-node-resolve": "npm:16.0.0" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:^6.1.2" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/d3": "npm:^7" "@types/jest": "npm:^29.5.4" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/react": "npm:18.3.18" "@types/react-virtualized-auto-sizer": "npm:1.0.4" "@types/tinycolor2": "npm:1.4.6" babel-jest: "npm:29.7.0" d3: "npm:^7.8.5" - esbuild: "npm:0.24.2" + esbuild: "npm:0.25.0" jest: "npm:^29.6.4" jest-canvas-mock: "npm:2.5.2" lodash: "npm:4.17.21" @@ -3329,7 +3401,7 @@ __metadata: react-virtualized-auto-sizer: "npm:1.0.25" rollup: "npm:^4.22.4" rollup-plugin-dts: "npm:^6.1.1" - rollup-plugin-esbuild: "npm:6.1.1" + rollup-plugin-esbuild: "npm:6.2.0" rollup-plugin-node-externals: "npm:^8.0.0" tinycolor2: "npm:1.6.0" ts-jest: "npm:29.2.5" @@ -3381,6 +3453,21 @@ __metadata: languageName: node linkType: hard +"@grafana/llm@npm:0.12.0": + version: 0.12.0 + resolution: "@grafana/llm@npm:0.12.0" + dependencies: + "@grafana/data": "npm:^10.4.0 ||^11" + "@grafana/runtime": "npm:^10.4.0 || ^11" + react: "npm:^18" + react-use: "npm:^17.5.0" + rxjs: "npm:^7.8.1" + semver: "npm:^7.6.3" + uuid: "npm:^10.0.0" + checksum: 10/5214e244f9ead7fdb17775d83a0463e151e63aa4c716a1e5b92e47b3675aff66dd8891a87fed3ace8361cfe24966c45baadfa821d0b6f46d910c6faecad8021d + languageName: node + linkType: hard + "@grafana/monaco-logql@npm:^0.0.8": version: 0.0.8 resolution: "@grafana/monaco-logql@npm:0.0.8" @@ -3395,24 +3482,24 @@ __metadata: resolution: "@grafana/o11y-ds-frontend@workspace:packages/grafana-o11y-ds-frontend" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/experimental": "npm:2.1.6" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/plugin-ui": "npm:0.10.1" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:^6.1.2" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/jest": "npm:^29.5.4" - "@types/node": "npm:22.10.7" + "@types/node": "npm:22.12.0" "@types/react": "npm:18.3.18" "@types/systemjs": "npm:6.15.1" jest: "npm:^29.6.4" react: "npm:18.3.1" - react-select: "npm:5.9.0" + react-select: "npm:5.10.0" react-use: "npm:17.6.0" rxjs: "npm:7.8.1" ts-jest: "npm:29.2.5" @@ -3425,16 +3512,16 @@ __metadata: languageName: unknown linkType: soft -"@grafana/plugin-configs@npm:11.5.0-pre, @grafana/plugin-configs@workspace:*, @grafana/plugin-configs@workspace:packages/grafana-plugin-configs": +"@grafana/plugin-configs@npm:11.6.0-pre, @grafana/plugin-configs@workspace:*, @grafana/plugin-configs@workspace:packages/grafana-plugin-configs": version: 0.0.0-use.local resolution: "@grafana/plugin-configs@workspace:packages/grafana-plugin-configs" dependencies: "@grafana/tsconfig": "npm:^2.0.0" - "@swc/core": "npm:1.10.9" + "@swc/core": "npm:1.10.12" "@types/eslint": "npm:9.6.1" "@types/webpack-bundle-analyzer": "npm:^4.7.0" copy-webpack-plugin: "npm:12.0.2" - eslint: "npm:9.18.0" + eslint: "npm:9.19.0" eslint-webpack-plugin: "npm:4.2.0" fork-ts-checker-webpack-plugin: "npm:9.0.2" glob: "npm:11.0.1" @@ -3447,37 +3534,37 @@ __metadata: languageName: unknown linkType: soft -"@grafana/plugin-e2e@npm:^1.11.0": - version: 1.16.0 - resolution: "@grafana/plugin-e2e@npm:1.16.0" +"@grafana/plugin-e2e@npm:1.17.1": + version: 1.17.1 + resolution: "@grafana/plugin-e2e@npm:1.17.1" dependencies: - "@grafana/e2e-selectors": "npm:^11.5.0-216908" + "@grafana/e2e-selectors": "npm:^11.5.0-221187" semver: "npm:^7.5.4" uuid: "npm:^11.0.2" yaml: "npm:^2.3.4" peerDependencies: "@playwright/test": ^1.41.2 - checksum: 10/9d04359ca5da7f302c95fc57bef1c218a62708aff35d2f321af753490e790b625700a573f6dfbddc637771ac86c2fb7d0d46cae8bf6b4633cf1c08e7fc7b374f + checksum: 10/d98b06fa04f59e07a8385fd81e8a7b456332d431aa529a0eb748087524f8b7b1bee867e616e01e7d1842b9e3c8dd508e8122fd5fb4d5a0a25162a4657b3dd4c0 languageName: node linkType: hard -"@grafana/plugin-ui@npm:^0.9.3, @grafana/plugin-ui@npm:^0.9.6": - version: 0.9.6 - resolution: "@grafana/plugin-ui@npm:0.9.6" +"@grafana/plugin-ui@npm:0.10.1": + version: 0.10.1 + resolution: "@grafana/plugin-ui@npm:0.10.1" dependencies: + "@emotion/css": "npm:^11.11.2" "@hello-pangea/dnd": "npm:^17.0.0" + "@types/prismjs": "npm:^1.26.4" lodash: "npm:^4.17.21" prismjs: "npm:^1.29.0" - prompts: "npm:^2.4.2" - rc-cascader: "npm:1.0.1" react-awesome-query-builder: "npm:^5.3.1" + react-calendar: "npm:^4.8.0" react-popper-tooltip: "npm:^4.4.2" - react-use: "npm:17.3.1" + react-use: "npm:^17.3.1" react-virtualized-auto-sizer: "npm:^1.0.6" sql-formatter-plus: "npm:^1.3.6" - uuid: "npm:^8.3.2" + uuid: "npm:^11.0.0" peerDependencies: - "@changesets/cli": ">=2.x" "@grafana/data": ^10.4.0 || ^11.0.0 "@grafana/e2e-selectors": ^10.4.0 || ^11.0.0 "@grafana/runtime": ^10.4.0 || ^11.0.0 @@ -3485,13 +3572,11 @@ __metadata: react: ^18.2.0 react-dom: ^18.2.0 rxjs: ^7.8.1 - bin: - changeset-improved: dist/utils/changeset/index.js - checksum: 10/260317fd20becd3bed4b7beb758e855075c707952622169e1cd1c8624ff40e2d297b8b92584abb55a012cecbf81f1712372bc5420c3b93cfb0079504e649f858 + checksum: 10/9b152c751b90ab414366d1f0ad6687862fcc1d72e4ecaab99f84d7a020ecbb31ce06267f621aaee70944037c3c3f01d9ff63040cd3964a6d957b9aab4ba114f4 languageName: node linkType: hard -"@grafana/plugin-ui@npm:^0.9.6": +"@grafana/plugin-ui@npm:^0.9.3": version: 0.9.6 resolution: "@grafana/plugin-ui@npm:0.9.6" dependencies: @@ -3526,38 +3611,29 @@ __metadata: resolution: "@grafana/prometheus@workspace:packages/grafana-prometheus" dependencies: "@emotion/css": "npm:11.13.5" - "@emotion/eslint-plugin": "npm:11.12.0" "@floating-ui/react": "npm:0.27.3" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/experimental": "npm:2.1.6" - "@grafana/faro-web-sdk": "npm:1.12.3" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/plugin-ui": "npm:0.10.1" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@hello-pangea/dnd": "npm:17.0.0" "@leeoniya/ufuzzy": "npm:1.0.18" "@lezer/common": "npm:1.2.3" - "@lezer/highlight": "npm:1.2.1" "@lezer/lr": "npm:1.4.2" "@prometheus-io/lezer-promql": "npm:0.301.0" - "@reduxjs/toolkit": "npm:2.5.0" + "@reduxjs/toolkit": "npm:2.5.1" "@rollup/plugin-image": "npm:3.0.3" "@rollup/plugin-node-resolve": "npm:16.0.0" - "@swc/core": "npm:1.10.9" - "@swc/helpers": "npm:0.5.15" - "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.3" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" - "@types/d3": "npm:7.4.3" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/debounce-promise": "npm:3.1.9" - "@types/eslint": "npm:9.6.1" "@types/jest": "npm:29.5.14" - "@types/jquery": "npm:3.5.32" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/pluralize": "npm:^0.0.33" "@types/prismjs": "npm:1.26.5" "@types/react": "npm:18.3.18" @@ -3566,102 +3642,70 @@ __metadata: "@types/react-window": "npm:1.8.8" "@types/semver": "npm:7.5.8" "@types/uuid": "npm:10.0.0" - "@typescript-eslint/eslint-plugin": "npm:8.18.1" - "@typescript-eslint/parser": "npm:8.18.1" - copy-webpack-plugin: "npm:12.0.2" - css-loader: "npm:7.1.2" - d3: "npm:7.9.0" - date-fns: "npm:4.1.0" debounce-promise: "npm:3.1.2" - esbuild: "npm:0.24.2" - eslint: "npm:9.18.0" - eslint-config-prettier: "npm:9.1.0" - eslint-plugin-import: "npm:^2.31.0" - eslint-plugin-jest: "npm:28.11.0" - eslint-plugin-jsdoc: "npm:50.6.2" - eslint-plugin-jsx-a11y: "npm:6.10.2" - eslint-plugin-lodash: "npm:8.0.0" - eslint-plugin-react: "npm:7.37.4" - eslint-plugin-react-hooks: "npm:5.1.0" - eslint-webpack-plugin: "npm:4.2.0" - eventemitter3: "npm:5.0.1" - fork-ts-checker-webpack-plugin: "npm:9.0.2" - glob: "npm:11.0.1" + esbuild: "npm:0.25.0" jest: "npm:29.7.0" jest-environment-jsdom: "npm:29.7.0" - jest-matcher-utils: "npm:29.7.0" lodash: "npm:4.17.21" - lru-cache: "npm:11.0.2" - marked: "npm:15.0.6" - marked-mangle: "npm:1.1.10" moment: "npm:2.30.1" - moment-timezone: "npm:0.5.46" + moment-timezone: "npm:0.5.47" monaco-promql: "npm:1.7.4" pluralize: "npm:8.0.0" - prettier: "npm:3.4.2" prismjs: "npm:1.29.0" react: "npm:18.3.1" react-dom: "npm:18.3.1" - react-highlight-words: "npm:0.20.0" - react-select: "npm:5.9.0" + react-highlight-words: "npm:0.21.0" react-select-event: "npm:5.5.1" react-use: "npm:17.6.0" react-window: "npm:1.8.11" rollup: "npm:^4.22.4" rollup-plugin-dts: "npm:^6.1.1" - rollup-plugin-esbuild: "npm:6.1.1" + rollup-plugin-esbuild: "npm:6.2.0" rollup-plugin-node-externals: "npm:^8.0.0" rxjs: "npm:7.8.1" - sass: "npm:1.83.4" - sass-loader: "npm:16.0.4" - semver: "npm:7.6.3" - style-loader: "npm:4.0.0" + semver: "npm:7.7.0" testing-library-selector: "npm:0.3.1" - ts-node: "npm:10.9.2" - tslib: "npm:2.8.1" typescript: "npm:5.7.3" uuid: "npm:11.0.5" - webpack: "npm:5.97.1" - webpack-cli: "npm:6.0.1" - whatwg-fetch: "npm:3.6.20" peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 languageName: unknown linkType: soft -"@grafana/runtime@npm:11.5.0-pre, @grafana/runtime@workspace:*, @grafana/runtime@workspace:packages/grafana-runtime": +"@grafana/runtime@npm:11.6.0-pre, @grafana/runtime@workspace:*, @grafana/runtime@workspace:packages/grafana-runtime": version: 0.0.0-use.local resolution: "@grafana/runtime@workspace:packages/grafana-runtime" dependencies: - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/faro-web-sdk": "npm:^1.3.6" - "@grafana/schema": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/faro-web-sdk": "npm:^1.13.2" + "@grafana/schema": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@rollup/plugin-node-resolve": "npm:16.0.0" "@rollup/plugin-terser": "npm:0.4.4" "@testing-library/dom": "npm:10.4.0" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/angular": "npm:1.8.9" "@types/history": "npm:4.7.11" "@types/jest": "npm:29.5.14" - "@types/lodash": "npm:4.17.14" + "@types/lodash": "npm:4.17.15" "@types/react": "npm:18.3.18" "@types/react-dom": "npm:18.3.5" "@types/systemjs": "npm:6.15.1" - esbuild: "npm:0.24.2" + esbuild: "npm:0.25.0" history: "npm:4.10.1" lodash: "npm:4.17.21" react: "npm:18.3.1" react-dom: "npm:18.3.1" + react-loading-skeleton: "npm:3.5.0" react-use: "npm:17.6.0" rimraf: "npm:6.0.1" rollup: "npm:^4.22.4" rollup-plugin-dts: "npm:^6.1.1" - rollup-plugin-esbuild: "npm:6.1.1" + rollup-plugin-esbuild: "npm:6.2.0" rollup-plugin-node-externals: "npm:^8.0.0" rollup-plugin-sourcemaps: "npm:0.6.3" rxjs: "npm:7.8.1" @@ -3673,11 +3717,31 @@ __metadata: languageName: unknown linkType: soft +"@grafana/runtime@npm:^10.4.0 || ^11": + version: 11.4.0 + resolution: "@grafana/runtime@npm:11.4.0" + dependencies: + "@grafana/data": "npm:11.4.0" + "@grafana/e2e-selectors": "npm:11.4.0" + "@grafana/faro-web-sdk": "npm:^1.3.6" + "@grafana/schema": "npm:11.4.0" + "@grafana/ui": "npm:11.4.0" + history: "npm:4.10.1" + lodash: "npm:4.17.21" + rxjs: "npm:7.8.1" + tslib: "npm:2.7.0" + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: 10/4a462803fb4e5f0fff05a8b50bd6a518a4d30bb3a6bb8e2d3d2acbd411f62dffa5606d322b91ed8bc76a37258c429c07f55b5d8539d41b7c8d111d706fb9caff + languageName: node + linkType: hard + "@grafana/saga-icons@workspace:*, @grafana/saga-icons@workspace:packages/grafana-icons": version: 0.0.0-use.local resolution: "@grafana/saga-icons@workspace:packages/grafana-icons" dependencies: - "@babel/core": "npm:7.26.0" + "@babel/core": "npm:7.26.7" "@grafana/tsconfig": "npm:^2.0.0" "@rollup/plugin-node-resolve": "npm:^16.0.0" "@rollup/plugin-typescript": "npm:^12.1.0" @@ -3688,17 +3752,17 @@ __metadata: "@svgr/plugin-prettier": "npm:^8.1.0" "@svgr/plugin-svgo": "npm:^8.1.0" "@types/babel__core": "npm:^7" - "@types/node": "npm:22.10.7" + "@types/node": "npm:22.12.0" "@types/react": "npm:18.3.18" "@types/react-dom": "npm:18.3.5" - esbuild: "npm:0.24.2" + esbuild: "npm:0.25.0" prettier: "npm:3.4.2" react: "npm:18.3.1" react-dom: "npm:18.3.1" rimraf: "npm:6.0.1" rollup: "npm:^4.22.4" rollup-plugin-dts: "npm:^6.1.1" - rollup-plugin-esbuild: "npm:6.1.1" + rollup-plugin-esbuild: "npm:6.2.0" rollup-plugin-node-externals: "npm:8.0.0" ts-node: "npm:10.9.2" typescript: "npm:5.7.3" @@ -3708,11 +3772,11 @@ __metadata: languageName: unknown linkType: soft -"@grafana/scenes-react@npm:5.37.1": - version: 5.37.1 - resolution: "@grafana/scenes-react@npm:5.37.1" +"@grafana/scenes-react@npm:6.0.2": + version: 6.0.2 + resolution: "@grafana/scenes-react@npm:6.0.2" dependencies: - "@grafana/scenes": "npm:5.37.1" + "@grafana/scenes": "npm:6.0.2" lru-cache: "npm:^10.2.2" react-use: "npm:^17.4.0" peerDependencies: @@ -3723,20 +3787,21 @@ __metadata: "@grafana/ui": ^11.0.0 react: ^18.0.0 react-dom: ^18.0.0 - checksum: 10/bedcda5da7220bb7e190f35cf4f9178f03f854fb7b9dc8c253a069321d7a6ff412af7c7692de80f86ea1ddb612ca09b65761ffb6eac5f12bd92f08649a1357d0 + react-router-dom: ^6.28.0 + checksum: 10/9744e01f2ff912229e43cedfa41d626ccdfd034f5b9718b57c593bc90edadade960f76baf1d8ad19eed03709c17c62397df1871b89acc635172aa14f6a20e096 languageName: node linkType: hard -"@grafana/scenes@npm:5.37.1": - version: 5.37.1 - resolution: "@grafana/scenes@npm:5.37.1" +"@grafana/scenes@npm:6.0.2": + version: 6.0.2 + resolution: "@grafana/scenes@npm:6.0.2" dependencies: "@floating-ui/react": "npm:^0.26.16" "@leeoniya/ufuzzy": "npm:^1.0.16" "@tanstack/react-virtual": "npm:^3.9.0" - react-grid-layout: "npm:^1.3.4" - react-use: "npm:^17.5.0" - react-virtualized-auto-sizer: "npm:^1.0.24" + react-grid-layout: "npm:1.3.4" + react-use: "npm:17.5.0" + react-virtualized-auto-sizer: "npm:1.0.24" uuid: "npm:^9.0.0" peerDependencies: "@grafana/data": ">=10.4" @@ -3746,47 +3811,57 @@ __metadata: "@grafana/ui": ">=10.4" react: ^18.0.0 react-dom: ^18.0.0 - checksum: 10/82a3d98444bf8a66f21bee88635d057af8b9eb35e0752cb77c21dda136b278a056d6ddaf3deccc404c9964b203820d4c2f68333c5a9090b86190642d2733097f + react-router-dom: ^6.28.0 + checksum: 10/2584f296db6299ef0a09d51f5c267ebcf7e44bd17b4d6516e38d3220f8f1d7aebc63c5fc6523979c4ac4d3f555416ca573e85e03bd36eb33a11941a5b3497149 languageName: node linkType: hard -"@grafana/schema@npm:11.5.0-pre, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema": +"@grafana/schema@npm:11.4.0": + version: 11.4.0 + resolution: "@grafana/schema@npm:11.4.0" + dependencies: + tslib: "npm:2.7.0" + checksum: 10/ed115437cf4a3c95194c4eeb1c2723be06887fc5c5b5e4f5eea1beb49f5bab15fab3d20de95e918019bd333f4e6505a2dace57cef60cbc9327bd4e643139b1b8 + languageName: node + linkType: hard + +"@grafana/schema@npm:11.6.0-pre, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema": version: 0.0.0-use.local resolution: "@grafana/schema@workspace:packages/grafana-schema" dependencies: "@grafana/tsconfig": "npm:^2.0.0" "@rollup/plugin-node-resolve": "npm:16.0.0" - esbuild: "npm:0.24.2" + esbuild: "npm:0.25.0" glob: "npm:^11.0.0" rimraf: "npm:6.0.1" rollup: "npm:^4.22.4" rollup-plugin-dts: "npm:^6.1.1" - rollup-plugin-esbuild: "npm:6.1.1" + rollup-plugin-esbuild: "npm:6.2.0" rollup-plugin-node-externals: "npm:^8.0.0" tslib: "npm:2.8.1" typescript: "npm:5.7.3" languageName: unknown linkType: soft -"@grafana/sql@npm:11.5.0-pre, @grafana/sql@workspace:*, @grafana/sql@workspace:packages/grafana-sql": +"@grafana/sql@npm:11.6.0-pre, @grafana/sql@workspace:*, @grafana/sql@workspace:packages/grafana-sql": version: 0.0.0-use.local resolution: "@grafana/sql@workspace:packages/grafana-sql" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/experimental": "npm:2.1.6" - "@grafana/runtime": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/plugin-ui": "npm:0.10.1" + "@grafana/runtime": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@react-awesome-query-builder/ui": "npm:6.6.4" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:^6.1.2" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/jest": "npm:^29.5.4" - "@types/lodash": "npm:4.17.14" - "@types/node": "npm:22.10.7" + "@types/lodash": "npm:4.17.15" + "@types/node": "npm:22.12.0" "@types/react": "npm:18.3.18" "@types/react-dom": "npm:18.3.5" "@types/react-virtualized-auto-sizer": "npm:1.0.4" @@ -3797,7 +3872,7 @@ __metadata: lodash: "npm:4.17.21" react: "npm:18.3.1" react-dom: "npm:18.3.1" - react-select: "npm:5.9.0" + react-select: "npm:5.10.0" react-use: "npm:17.6.0" react-virtualized-auto-sizer: "npm:1.0.25" rxjs: "npm:7.8.1" @@ -3819,20 +3894,97 @@ __metadata: languageName: node linkType: hard -"@grafana/ui@npm:11.5.0-pre, @grafana/ui@workspace:*, @grafana/ui@workspace:packages/grafana-ui": +"@grafana/ui@npm:11.4.0": + version: 11.4.0 + resolution: "@grafana/ui@npm:11.4.0" + dependencies: + "@emotion/css": "npm:11.13.4" + "@emotion/react": "npm:11.13.3" + "@emotion/serialize": "npm:1.3.2" + "@floating-ui/react": "npm:0.26.24" + "@grafana/data": "npm:11.4.0" + "@grafana/e2e-selectors": "npm:11.4.0" + "@grafana/faro-web-sdk": "npm:^1.3.6" + "@grafana/schema": "npm:11.4.0" + "@hello-pangea/dnd": "npm:16.6.0" + "@leeoniya/ufuzzy": "npm:1.0.14" + "@monaco-editor/react": "npm:4.6.0" + "@popperjs/core": "npm:2.11.8" + "@react-aria/dialog": "npm:3.5.18" + "@react-aria/focus": "npm:3.18.3" + "@react-aria/overlays": "npm:3.23.3" + "@react-aria/utils": "npm:3.25.3" + "@tanstack/react-virtual": "npm:^3.5.1" + "@types/jquery": "npm:3.5.31" + "@types/lodash": "npm:4.17.10" + "@types/react-table": "npm:7.7.20" + ansicolor: "npm:1.1.100" + calculate-size: "npm:1.1.1" + classnames: "npm:2.5.1" + d3: "npm:7.9.0" + date-fns: "npm:3.6.0" + downshift: "npm:^9.0.6" + hoist-non-react-statics: "npm:3.3.2" + i18next: "npm:^23.0.0" + i18next-browser-languagedetector: "npm:^7.0.2" + immutable: "npm:4.3.7" + is-hotkey: "npm:0.2.0" + jquery: "npm:3.7.1" + lodash: "npm:4.17.21" + micro-memoize: "npm:^4.1.2" + moment: "npm:2.30.1" + monaco-editor: "npm:0.34.1" + ol: "npm:7.4.0" + prismjs: "npm:1.29.0" + rc-cascader: "npm:3.28.1" + rc-drawer: "npm:7.2.0" + rc-slider: "npm:11.1.7" + rc-time-picker: "npm:^3.7.3" + rc-tooltip: "npm:6.2.1" + react-calendar: "npm:5.0.0" + react-colorful: "npm:5.6.1" + react-custom-scrollbars-2: "npm:4.5.0" + react-dropzone: "npm:14.2.9" + react-highlight-words: "npm:0.20.0" + react-hook-form: "npm:^7.49.2" + react-i18next: "npm:^14.0.0" + react-inlinesvg: "npm:3.0.2" + react-loading-skeleton: "npm:3.5.0" + react-router-dom-v5-compat: "npm:^6.26.1" + react-select: "npm:5.8.1" + react-table: "npm:7.8.0" + react-transition-group: "npm:4.4.5" + react-use: "npm:17.5.1" + react-window: "npm:1.8.10" + rxjs: "npm:7.8.1" + slate: "npm:0.47.9" + slate-plain-serializer: "npm:0.7.13" + slate-react: "npm:0.22.10" + tinycolor2: "npm:1.6.0" + tslib: "npm:2.7.0" + uplot: "npm:1.6.31" + uuid: "npm:9.0.1" + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: 10/9019f6b549ae70808476902bdbbda1d3d80077dd84663d64f9bbecd86708c3e96f56dd1f7c8eff605710bbdf644b52520dbf2848ba4f0684f202ab896827d1e2 + languageName: node + linkType: hard + +"@grafana/ui@npm:11.6.0-pre, @grafana/ui@workspace:*, @grafana/ui@workspace:packages/grafana-ui": version: 0.0.0-use.local resolution: "@grafana/ui@workspace:packages/grafana-ui" dependencies: - "@babel/core": "npm:7.26.0" + "@babel/core": "npm:7.26.7" "@emotion/css": "npm:11.13.5" "@emotion/react": "npm:11.14.0" "@emotion/serialize": "npm:1.3.3" "@faker-js/faker": "npm:^9.0.0" "@floating-ui/react": "npm:0.27.3" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/faro-web-sdk": "npm:^1.3.6" - "@grafana/schema": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/faro-web-sdk": "npm:^1.13.2" + "@grafana/schema": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" "@hello-pangea/dnd": "npm:17.0.0" "@leeoniya/ufuzzy": "npm:1.0.18" @@ -3862,8 +4014,8 @@ __metadata: "@tanstack/react-virtual": "npm:^3.5.1" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.3" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/chance": "npm:1.1.6" "@types/common-tags": "npm:^1.8.0" "@types/d3": "npm:7.4.3" @@ -3871,9 +4023,9 @@ __metadata: "@types/is-hotkey": "npm:0.1.10" "@types/jest": "npm:29.5.14" "@types/jquery": "npm:3.5.32" - "@types/lodash": "npm:4.17.14" + "@types/lodash": "npm:4.17.15" "@types/mock-raf": "npm:1.0.6" - "@types/node": "npm:22.10.7" + "@types/node": "npm:22.12.0" "@types/prismjs": "npm:1.26.5" "@types/react": "npm:18.3.18" "@types/react-color": "npm:3.0.13" @@ -3897,7 +4049,7 @@ __metadata: d3: "npm:7.9.0" date-fns: "npm:4.1.0" downshift: "npm:^9.0.6" - esbuild: "npm:0.24.2" + esbuild: "npm:0.25.0" expose-loader: "npm:5.0.0" fs-extra: "npm:^11.2.0" hoist-non-react-statics: "npm:3.3.2" @@ -3925,14 +4077,14 @@ __metadata: react-custom-scrollbars-2: "npm:4.5.0" react-dom: "npm:18.3.1" react-dropzone: "npm:14.3.5" - react-highlight-words: "npm:0.20.0" + react-highlight-words: "npm:0.21.0" react-hook-form: "npm:^7.49.2" react-i18next: "npm:^15.0.0" - react-inlinesvg: "npm:4.1.5" + react-inlinesvg: "npm:4.2.0" react-loading-skeleton: "npm:3.5.0" - react-router-dom: "npm:5.3.3" + react-router-dom: "npm:5.3.4" react-router-dom-v5-compat: "npm:^6.26.1" - react-select: "npm:5.9.0" + react-select: "npm:5.10.0" react-select-event: "npm:^5.1.0" react-table: "npm:7.8.0" react-transition-group: "npm:4.4.5" @@ -3942,7 +4094,7 @@ __metadata: rollup: "npm:^4.22.4" rollup-plugin-copy: "npm:3.5.0" rollup-plugin-dts: "npm:^6.1.1" - rollup-plugin-esbuild: "npm:6.1.1" + rollup-plugin-esbuild: "npm:6.2.0" rollup-plugin-node-externals: "npm:^8.0.0" rollup-plugin-svg-import: "npm:3.0.0" rxjs: "npm:7.8.1" @@ -3951,7 +4103,6 @@ __metadata: slate-plain-serializer: "npm:0.7.13" slate-react: "npm:0.22.10" storybook: "npm:^8.4.2" - storybook-dark-mode: "npm:4.0.1" style-loader: "npm:4.0.0" tinycolor2: "npm:1.6.0" tslib: "npm:2.8.1" @@ -3974,6 +4125,24 @@ __metadata: languageName: node linkType: hard +"@hello-pangea/dnd@npm:16.6.0": + version: 16.6.0 + resolution: "@hello-pangea/dnd@npm:16.6.0" + dependencies: + "@babel/runtime": "npm:^7.24.1" + css-box-model: "npm:^1.2.1" + memoize-one: "npm:^6.0.0" + raf-schd: "npm:^4.0.3" + react-redux: "npm:^8.1.3" + redux: "npm:^4.2.1" + use-memo-one: "npm:^1.1.3" + peerDependencies: + react: ^16.8.5 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0 + checksum: 10/f377461d400c8223174745e4d7ecf4fb0146f9e807413f98120ebbcf075282e631273988d336daaf1fb8e6b6c6a1a8e4f99beefecd7a6b68ccc3bb064d38f13f + languageName: node + linkType: hard + "@hello-pangea/dnd@npm:17.0.0, @hello-pangea/dnd@npm:^17.0.0": version: 17.0.0 resolution: "@hello-pangea/dnd@npm:17.0.0" @@ -3992,24 +4161,6 @@ __metadata: languageName: node linkType: hard -"@hello-pangea/dnd@npm:^16.6.0": - version: 16.6.0 - resolution: "@hello-pangea/dnd@npm:16.6.0" - dependencies: - "@babel/runtime": "npm:^7.24.1" - css-box-model: "npm:^1.2.1" - memoize-one: "npm:^6.0.0" - raf-schd: "npm:^4.0.3" - react-redux: "npm:^8.1.3" - redux: "npm:^4.2.1" - use-memo-one: "npm:^1.1.3" - peerDependencies: - react: ^16.8.5 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0 - checksum: 10/f377461d400c8223174745e4d7ecf4fb0146f9e807413f98120ebbcf075282e631273988d336daaf1fb8e6b6c6a1a8e4f99beefecd7a6b68ccc3bb064d38f13f - languageName: node - linkType: hard - "@humanfs/core@npm:^0.19.1": version: 0.19.1 resolution: "@humanfs/core@npm:0.19.1" @@ -4408,6 +4559,335 @@ __metadata: languageName: node linkType: hard +"@jimp/core@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/core@npm:1.6.0" + dependencies: + "@jimp/file-ops": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + await-to-js: "npm:^3.0.0" + exif-parser: "npm:^0.1.12" + file-type: "npm:^16.0.0" + mime: "npm:3" + checksum: 10/02a12c937e1d7a9054bdc57fa3d97385599eafcba4fa42a0f56b5c7bdd1bc2e2f2ddac176968d2f30c4b4817b68b86bdbb02598739ab9a616a4d49e0f6b995a8 + languageName: node + linkType: hard + +"@jimp/diff@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/diff@npm:1.6.0" + dependencies: + "@jimp/plugin-resize": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + pixelmatch: "npm:^5.3.0" + checksum: 10/6207cf0e3069c9c3ead6ebd604323d1cbda97f16f9c8b3b244551df6f67db5896c726504c8451137efebdce39679d64cfcea7773e02e99c5ff0c0cf967fb8bf4 + languageName: node + linkType: hard + +"@jimp/file-ops@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/file-ops@npm:1.6.0" + checksum: 10/dd27c6a178731f7a6d5e315086665daccdc0504f9e97784e4255bfdbebcb24bb01acd865debd66e7348248dd6a1d2994af080905d1e29593e3af0f722fd69c18 + languageName: node + linkType: hard + +"@jimp/js-bmp@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/js-bmp@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + bmp-ts: "npm:^1.0.9" + checksum: 10/71385238cd7fb965f45cc03d1f09a96ca3b5aea00c5291e46fc905ae27d095a2afac3543d93c3c04228edbc20e80506dfc7b8948bf82b0a5d5a776797d89c4bb + languageName: node + linkType: hard + +"@jimp/js-gif@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/js-gif@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + gifwrap: "npm:^0.10.1" + omggif: "npm:^1.0.10" + checksum: 10/a5a5c12a4c9f44f799b99427a721f85a411c6559982bfac15a4d40fa96e704477bf938e56596d42b21f1513473a8af355c87e4e0afc39e4ac75b87641c274dcf + languageName: node + linkType: hard + +"@jimp/js-jpeg@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/js-jpeg@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + jpeg-js: "npm:^0.4.4" + checksum: 10/31462dfa94ea7e5255894cce22a756de9ce50d43c1820487fcef8cee449c5b3e28615fc855de6559e94011f564d22482c45840da1cc8aef87b37bb27316f37a3 + languageName: node + linkType: hard + +"@jimp/js-png@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/js-png@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + pngjs: "npm:^7.0.0" + checksum: 10/ad5d4a8935c63c8c72fa9815c8fec6e3ae7232709c3adf51c78c4c6f40eba4046515a19813cc3e6a128fbf09f2c667d355cfe5396448c38d489e59757ddb38e2 + languageName: node + linkType: hard + +"@jimp/js-tiff@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/js-tiff@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + utif2: "npm:^4.1.0" + checksum: 10/3d68e5835c0f38aa842841a236c0ab01af7b337b1b4d82b1c63e4c69abb7c25b41af39872bc995726bac7627a61cc7930dc1b6435bc5c8d8f1975f93dce010da + languageName: node + linkType: hard + +"@jimp/plugin-blit@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-blit@npm:1.6.0" + dependencies: + "@jimp/types": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + zod: "npm:^3.23.8" + checksum: 10/a80ee8da0aee33d6f375320ccef4b0a1ffbb84c9d5149b611095b7052826d3f3a1f40e2dcd5eba6143f3320916cb3249de190e13fe3a8dfe6f89d1ea9e10bb93 + languageName: node + linkType: hard + +"@jimp/plugin-blur@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-blur@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + checksum: 10/ede99df9c400311548c94af1b663db9b392ec059f115916b3c53b422871e1607304f2b7488a9a99e7a376905744873d70945e601ff1f6567459161e808b5670a + languageName: node + linkType: hard + +"@jimp/plugin-circle@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-circle@npm:1.6.0" + dependencies: + "@jimp/types": "npm:1.6.0" + zod: "npm:^3.23.8" + checksum: 10/31ccc2c885191a0090c18f8d8dd5083426f356e33c2fe23901775cd0ac6abcf0925a86b1296b6516a03b7b4bd9ce103308f6a3cfcab1244055c18692b5e1984a + languageName: node + linkType: hard + +"@jimp/plugin-color@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-color@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + tinycolor2: "npm:^1.6.0" + zod: "npm:^3.23.8" + checksum: 10/3efe730389d26a7ced711ef354bb244116ef6d2040cd0ef88aadcb9517ece95e33d98e30de9a2171564ff6c2cecd12b9d47025f36de7a90241fdebeadb93e0dd + languageName: node + linkType: hard + +"@jimp/plugin-contain@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-contain@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/plugin-blit": "npm:1.6.0" + "@jimp/plugin-resize": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + zod: "npm:^3.23.8" + checksum: 10/f36961b7360a7eb42e8ba0c713f6e942fbaa19083d3feb71ca9d7dec61b988a09fc19655ff2a9fece94b1c9d6609f5dfd42e322a012fecb0a9a5d2bbe250b531 + languageName: node + linkType: hard + +"@jimp/plugin-cover@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-cover@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/plugin-crop": "npm:1.6.0" + "@jimp/plugin-resize": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + zod: "npm:^3.23.8" + checksum: 10/ee29822317ad70979e4d45930bb6c73558fe442e72f50cff831370e076f5de0afa827dea8c129b15ada161c25c1575cdbed67db7154b839cc63a955d209cbf3e + languageName: node + linkType: hard + +"@jimp/plugin-crop@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-crop@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + zod: "npm:^3.23.8" + checksum: 10/410a09d0482c5006b3e4f8fc181c68dd04cf7528fd644917e9a5be4e38b77a230f7a3d2906577b86710ef23b8201220cb0c35b886d45151b8df493413af0411b + languageName: node + linkType: hard + +"@jimp/plugin-displace@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-displace@npm:1.6.0" + dependencies: + "@jimp/types": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + zod: "npm:^3.23.8" + checksum: 10/4c809a39436c19dcb11abdcb3ba6c5821099a804d1ee900735b32442c2ef68bf801665fa68775b65f3a335f11375c274746b1b428317f61ed3f6bc34f339ed87 + languageName: node + linkType: hard + +"@jimp/plugin-dither@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-dither@npm:1.6.0" + dependencies: + "@jimp/types": "npm:1.6.0" + checksum: 10/4bbde749314770f230058c6b9211a399ea8b67eac2d0f22304d3bb3aa329094b54a2155ec4b9aae17a6e0385c6543013ee48e453b4ee366531b4fd0a90e97b2a + languageName: node + linkType: hard + +"@jimp/plugin-fisheye@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-fisheye@npm:1.6.0" + dependencies: + "@jimp/types": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + zod: "npm:^3.23.8" + checksum: 10/aff3084f662fbb610af84a00dcca8aba8b03436e2afa6afafca4ccc80734904754591af15bc8684c0c0821defe7854eb11546ec7a7fde9b092e2283b07135087 + languageName: node + linkType: hard + +"@jimp/plugin-flip@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-flip@npm:1.6.0" + dependencies: + "@jimp/types": "npm:1.6.0" + zod: "npm:^3.23.8" + checksum: 10/db37237e2277ad1e5832cda507ae18c13bfa93e3d3791bdf225559897bcbfd5beda2606bab8d3b4ffb87d7843983439264539872879db62247095b9403d0349c + languageName: node + linkType: hard + +"@jimp/plugin-hash@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-hash@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/js-bmp": "npm:1.6.0" + "@jimp/js-jpeg": "npm:1.6.0" + "@jimp/js-png": "npm:1.6.0" + "@jimp/js-tiff": "npm:1.6.0" + "@jimp/plugin-color": "npm:1.6.0" + "@jimp/plugin-resize": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + any-base: "npm:^1.1.0" + checksum: 10/d20e020bb404c297678771c4cefe9bc6c305d6dee503d55bc47bc3d04c54ee988e6c8e9df5c7f113e368cb1900097d6bf308f93f18d98dd49e350bc59033b3c1 + languageName: node + linkType: hard + +"@jimp/plugin-mask@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-mask@npm:1.6.0" + dependencies: + "@jimp/types": "npm:1.6.0" + zod: "npm:^3.23.8" + checksum: 10/b85b1cce64ae7bf30a421645b6da022dd66a69d98a8e998ad744cab3823f488d03cd69f56c4278cef4a9683eb21ca5dc3c05377a6208525e63fe0b664b10c801 + languageName: node + linkType: hard + +"@jimp/plugin-print@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-print@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/js-jpeg": "npm:1.6.0" + "@jimp/js-png": "npm:1.6.0" + "@jimp/plugin-blit": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + parse-bmfont-ascii: "npm:^1.0.6" + parse-bmfont-binary: "npm:^1.0.6" + parse-bmfont-xml: "npm:^1.1.6" + simple-xml-to-json: "npm:^1.2.2" + zod: "npm:^3.23.8" + checksum: 10/14797550c7b5805825e4898fc6fa3d0314e767b1dd2d84444e6cb25a54a8872f0e659fe2efd75421e89c3328a0b6ed92b54724fc350c4edf8e76323dcd4507e7 + languageName: node + linkType: hard + +"@jimp/plugin-quantize@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-quantize@npm:1.6.0" + dependencies: + image-q: "npm:^4.0.0" + zod: "npm:^3.23.8" + checksum: 10/1dad868c5b86f6059b565c053a29913238be47d155937ba907ef3c8d3bb5f1d08f5783139b3e8d4aabaffb297081d9783bff5c2833027c0e6baa67447fd0e7d9 + languageName: node + linkType: hard + +"@jimp/plugin-resize@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-resize@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + zod: "npm:^3.23.8" + checksum: 10/1b0665b9cd9e7f0665b76f82cc30f8eebfbc848d5c92707b0d156296b9be7054f04de42a482c5707b4675b32d74949cea423cb3b286456e9c4130c47ef71170d + languageName: node + linkType: hard + +"@jimp/plugin-rotate@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-rotate@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/plugin-crop": "npm:1.6.0" + "@jimp/plugin-resize": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + zod: "npm:^3.23.8" + checksum: 10/fc48b7cd6eee84f283feadeb488a38cb554be02d8dfd9afa991de84e640a9677eee8c1e88eb87b1ed3d68df3ed89a7a43d911585c72f8ec4bfb5546c50b1031f + languageName: node + linkType: hard + +"@jimp/plugin-threshold@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/plugin-threshold@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/plugin-color": "npm:1.6.0" + "@jimp/plugin-hash": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + zod: "npm:^3.23.8" + checksum: 10/7dbdf94ddc69dba21ab19ddac6e240aecafaecb7e0b1c1bdec9a1f29414f8629f9aec186f17092e6884f01a15713c1f63336ca038255044304e4810283160e6e + languageName: node + linkType: hard + +"@jimp/types@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/types@npm:1.6.0" + dependencies: + zod: "npm:^3.23.8" + checksum: 10/b600ca2077bcb0f07873240e9c16d496baf7dcd5aafbc14b2dab4ad4402eea7d3c8300336ed772ed5c4a0c2504de5ef181da88cb110faeb616aa742171cbb56f + languageName: node + linkType: hard + +"@jimp/utils@npm:1.6.0": + version: 1.6.0 + resolution: "@jimp/utils@npm:1.6.0" + dependencies: + "@jimp/types": "npm:1.6.0" + tinycolor2: "npm:^1.6.0" + checksum: 10/dc9740e8ad21bc1911ce1562824c3e8a04fcf97781156b1baff691dca1df7e960b4e4071ba606d180f768086f48822c37751904436f882c45befc0febb3ddabc + languageName: node + linkType: hard + "@jridgewell/gen-mapping@npm:^0.1.0": version: 0.1.1 resolution: "@jridgewell/gen-mapping@npm:0.1.1" @@ -4487,6 +4967,15 @@ __metadata: languageName: node linkType: hard +"@keyv/serialize@npm:^1.0.2": + version: 1.0.2 + resolution: "@keyv/serialize@npm:1.0.2" + dependencies: + buffer: "npm:^6.0.3" + checksum: 10/6a42a5778a6b4542f6903ba7e6a17c5bd116441798d75c95fba9908c76c7606db527fad710b5c54abc6175e49b1bbaaafe3b836ad4b91e1af701394134f1d504 + languageName: node + linkType: hard + "@kusto/language-service-next@npm:11.5.3": version: 11.5.3 resolution: "@kusto/language-service-next@npm:11.5.3" @@ -4534,6 +5023,13 @@ __metadata: languageName: node linkType: hard +"@leeoniya/ufuzzy@npm:1.0.14": + version: 1.0.14 + resolution: "@leeoniya/ufuzzy@npm:1.0.14" + checksum: 10/852b580a8eaaf92e2d448f5b720e3c53e4bea22187bf5e8459256677c47183321b47b8384982e15751f42da7e77a216fd86c80e6185677d8270adeab4a4fb771 + languageName: node + linkType: hard + "@leeoniya/ufuzzy@npm:1.0.18, @leeoniya/ufuzzy@npm:^1.0.16": version: 1.0.18 resolution: "@leeoniya/ufuzzy@npm:1.0.18" @@ -4937,16 +5433,6 @@ __metadata: languageName: node linkType: hard -"@nodelib/fs.scandir@npm:4.0.1": - version: 4.0.1 - resolution: "@nodelib/fs.scandir@npm:4.0.1" - dependencies: - "@nodelib/fs.stat": "npm:4.0.0" - run-parallel: "npm:^1.2.0" - checksum: 10/44b2b2b34e48ca88ee004413f5033db31cd6d5ecf8c7bbef0e33b6672d603f3e23b57d5fbb1bd5f83f8992df58381be6600006d92a903f085e698a37bdfe3c89 - languageName: node - linkType: hard - "@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": version: 2.0.5 resolution: "@nodelib/fs.stat@npm:2.0.5" @@ -4954,23 +5440,6 @@ __metadata: languageName: node linkType: hard -"@nodelib/fs.stat@npm:4.0.0": - version: 4.0.0 - resolution: "@nodelib/fs.stat@npm:4.0.0" - checksum: 10/1f87199fdab938d2ed6f5e10debc006f7965081e2cd147ed3d2333049a030cad1949bd76556a5f5364f062c3e1edcc3d0981189b065336fc92c503ead463f4e1 - languageName: node - linkType: hard - -"@nodelib/fs.walk@npm:3.0.1": - version: 3.0.1 - resolution: "@nodelib/fs.walk@npm:3.0.1" - dependencies: - "@nodelib/fs.scandir": "npm:4.0.1" - fastq: "npm:^1.15.0" - checksum: 10/7b76a0139dec52e3f2a3a0bb4f13dbf72a6b79d8076ec4b5deea9e75bd1b79d7abda53776f93b5aefda9a5e40f0e31f49f6e35bf5460a402f0aee7bcf3b26d85 - languageName: node - linkType: hard - "@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.6": version: 1.2.8 resolution: "@nodelib/fs.walk@npm:1.2.8" @@ -5756,13 +6225,20 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/semantic-conventions@npm:1.28.0, @opentelemetry/semantic-conventions@npm:^1.28.0": +"@opentelemetry/semantic-conventions@npm:1.28.0": version: 1.28.0 resolution: "@opentelemetry/semantic-conventions@npm:1.28.0" checksum: 10/c182a3206769b5d5a8ab89a5c674d046fd789421cef27ea55af179990e314732433c98e5017aa23e99f15fd2b0e13cb129bb6c2282da6860ce9419adf32b2e87 languageName: node linkType: hard +"@opentelemetry/semantic-conventions@npm:^1.28.0": + version: 1.29.0 + resolution: "@opentelemetry/semantic-conventions@npm:1.29.0" + checksum: 10/94008a2e374f18dd71a30fe8b91cb58dccbbe3e13e1de2200907e42fd1441459acff65c5eadcbfff30f7e9575895df5dc8ced01228f434e555a9a3d72b595752 + languageName: node + linkType: hard + "@parcel/watcher-android-arm64@npm:2.4.1": version: 2.4.1 resolution: "@parcel/watcher-android-arm64@npm:2.4.1" @@ -5959,14 +6435,14 @@ __metadata: languageName: node linkType: hard -"@playwright/test@npm:1.49.1": - version: 1.49.1 - resolution: "@playwright/test@npm:1.49.1" +"@playwright/test@npm:1.50.1": + version: 1.50.1 + resolution: "@playwright/test@npm:1.50.1" dependencies: - playwright: "npm:1.49.1" + playwright: "npm:1.50.1" bin: playwright: cli.js - checksum: 10/bb0d5eda58ee0b5bbca732d2aa57782fadf420d101e08e16d5760179459c667907bd8d224ee3d6f43f3088378e377ef63d32ed605fec37605debf217c3efe8da + checksum: 10/0d8d2291d6554c492cb163b4d463e1e9cc6d3ae50680d790473f693f36a243c16c3620406849dd40115046c47a6ad5cc36a24511caec6d054dc1a1d9fffb4138 languageName: node linkType: hard @@ -6213,6 +6689,23 @@ __metadata: languageName: node linkType: hard +"@react-aria/dialog@npm:3.5.18": + version: 3.5.18 + resolution: "@react-aria/dialog@npm:3.5.18" + dependencies: + "@react-aria/focus": "npm:^3.18.3" + "@react-aria/overlays": "npm:^3.23.3" + "@react-aria/utils": "npm:^3.25.3" + "@react-types/dialog": "npm:^3.5.13" + "@react-types/shared": "npm:^3.25.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 10/dbd40d14baeea7dae56956985234e29ada74a93899177c737c3312ec788b22a6d65179b7132cdbd6609e973d410f749071c68ceb6620d3cf6f60a03ddf648983 + languageName: node + linkType: hard + "@react-aria/dialog@npm:3.5.21": version: 3.5.21 resolution: "@react-aria/dialog@npm:3.5.21" @@ -6230,7 +6723,22 @@ __metadata: languageName: node linkType: hard -"@react-aria/focus@npm:3.19.1, @react-aria/focus@npm:^3.19.1": +"@react-aria/focus@npm:3.18.3": + version: 3.18.3 + resolution: "@react-aria/focus@npm:3.18.3" + dependencies: + "@react-aria/interactions": "npm:^3.22.3" + "@react-aria/utils": "npm:^3.25.3" + "@react-types/shared": "npm:^3.25.0" + "@swc/helpers": "npm:^0.5.0" + clsx: "npm:^2.0.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 10/b11632e638de2f40ec12a4a8c818059b9bf7e90b288a93b46985350c887ae7ecdf037391537f86fbacb2a186dec7e7c41a8f2ff767fd232a8cac3189f03735b2 + languageName: node + linkType: hard + +"@react-aria/focus@npm:3.19.1, @react-aria/focus@npm:^3.18.3, @react-aria/focus@npm:^3.19.1": version: 3.19.1 resolution: "@react-aria/focus@npm:3.19.1" dependencies: @@ -6246,7 +6754,7 @@ __metadata: languageName: node linkType: hard -"@react-aria/i18n@npm:^3.12.5": +"@react-aria/i18n@npm:^3.12.3, @react-aria/i18n@npm:^3.12.5": version: 3.12.5 resolution: "@react-aria/i18n@npm:3.12.5" dependencies: @@ -6265,7 +6773,7 @@ __metadata: languageName: node linkType: hard -"@react-aria/interactions@npm:^3.23.0": +"@react-aria/interactions@npm:^3.22.3, @react-aria/interactions@npm:^3.23.0": version: 3.23.0 resolution: "@react-aria/interactions@npm:3.23.0" dependencies: @@ -6280,7 +6788,29 @@ __metadata: languageName: node linkType: hard -"@react-aria/overlays@npm:3.25.0, @react-aria/overlays@npm:^3.25.0": +"@react-aria/overlays@npm:3.23.3": + version: 3.23.3 + resolution: "@react-aria/overlays@npm:3.23.3" + dependencies: + "@react-aria/focus": "npm:^3.18.3" + "@react-aria/i18n": "npm:^3.12.3" + "@react-aria/interactions": "npm:^3.22.3" + "@react-aria/ssr": "npm:^3.9.6" + "@react-aria/utils": "npm:^3.25.3" + "@react-aria/visually-hidden": "npm:^3.8.16" + "@react-stately/overlays": "npm:^3.6.11" + "@react-types/button": "npm:^3.10.0" + "@react-types/overlays": "npm:^3.8.10" + "@react-types/shared": "npm:^3.25.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 10/c70af63d4ae828963b9fa780330cabf49e5a70f8981ae65d173e32934fa190fc8df1283de65d6a8b71b6340050718df19c2e7353b406114962d85ee5deb811ee + languageName: node + linkType: hard + +"@react-aria/overlays@npm:3.25.0, @react-aria/overlays@npm:^3.23.3, @react-aria/overlays@npm:^3.25.0": version: 3.25.0 resolution: "@react-aria/overlays@npm:3.25.0" dependencies: @@ -6302,7 +6832,7 @@ __metadata: languageName: node linkType: hard -"@react-aria/ssr@npm:^3.9.7": +"@react-aria/ssr@npm:^3.9.6, @react-aria/ssr@npm:^3.9.7": version: 3.9.7 resolution: "@react-aria/ssr@npm:3.9.7" dependencies: @@ -6313,7 +6843,22 @@ __metadata: languageName: node linkType: hard -"@react-aria/utils@npm:3.27.0, @react-aria/utils@npm:^3.27.0": +"@react-aria/utils@npm:3.25.3": + version: 3.25.3 + resolution: "@react-aria/utils@npm:3.25.3" + dependencies: + "@react-aria/ssr": "npm:^3.9.6" + "@react-stately/utils": "npm:^3.10.4" + "@react-types/shared": "npm:^3.25.0" + "@swc/helpers": "npm:^0.5.0" + clsx: "npm:^2.0.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 10/86aed35da5cb0d48d949e40bf8226d5a6d6c92a8cdc60e3e12d524d1f3cc91ab6b54c5e1642823773cbb889fb61af7da22e89488b704b56fc5f4d8d59da7519b + languageName: node + linkType: hard + +"@react-aria/utils@npm:3.27.0, @react-aria/utils@npm:^3.25.3, @react-aria/utils@npm:^3.27.0": version: 3.27.0 resolution: "@react-aria/utils@npm:3.27.0" dependencies: @@ -6329,7 +6874,7 @@ __metadata: languageName: node linkType: hard -"@react-aria/visually-hidden@npm:^3.8.19": +"@react-aria/visually-hidden@npm:^3.8.16, @react-aria/visually-hidden@npm:^3.8.19": version: 3.8.19 resolution: "@react-aria/visually-hidden@npm:3.8.19" dependencies: @@ -6378,7 +6923,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/overlays@npm:^3.6.13": +"@react-stately/overlays@npm:^3.6.11, @react-stately/overlays@npm:^3.6.13": version: 3.6.13 resolution: "@react-stately/overlays@npm:3.6.13" dependencies: @@ -6391,7 +6936,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/utils@npm:^3.10.5": +"@react-stately/utils@npm:^3.10.4, @react-stately/utils@npm:^3.10.5": version: 3.10.5 resolution: "@react-stately/utils@npm:3.10.5" dependencies: @@ -6402,7 +6947,7 @@ __metadata: languageName: node linkType: hard -"@react-types/button@npm:3.10.2, @react-types/button@npm:^3.10.2": +"@react-types/button@npm:3.10.2, @react-types/button@npm:^3.10.0, @react-types/button@npm:^3.10.2": version: 3.10.2 resolution: "@react-types/button@npm:3.10.2" dependencies: @@ -6413,7 +6958,7 @@ __metadata: languageName: node linkType: hard -"@react-types/dialog@npm:^3.5.15": +"@react-types/dialog@npm:^3.5.13, @react-types/dialog@npm:^3.5.15": version: 3.5.15 resolution: "@react-types/dialog@npm:3.5.15" dependencies: @@ -6437,7 +6982,7 @@ __metadata: languageName: node linkType: hard -"@react-types/overlays@npm:3.8.12, @react-types/overlays@npm:^3.8.12": +"@react-types/overlays@npm:3.8.12, @react-types/overlays@npm:^3.8.10, @react-types/overlays@npm:^3.8.12": version: 3.8.12 resolution: "@react-types/overlays@npm:3.8.12" dependencies: @@ -6448,7 +6993,7 @@ __metadata: languageName: node linkType: hard -"@react-types/shared@npm:3.27.0, @react-types/shared@npm:^3.27.0": +"@react-types/shared@npm:3.27.0, @react-types/shared@npm:^3.25.0, @react-types/shared@npm:^3.27.0": version: 3.27.0 resolution: "@react-types/shared@npm:3.27.0" peerDependencies: @@ -6457,9 +7002,9 @@ __metadata: languageName: node linkType: hard -"@reduxjs/toolkit@npm:2.5.0": - version: 2.5.0 - resolution: "@reduxjs/toolkit@npm:2.5.0" +"@reduxjs/toolkit@npm:2.5.1": + version: 2.5.1 + resolution: "@reduxjs/toolkit@npm:2.5.1" dependencies: immer: "npm:^10.0.3" redux: "npm:^5.0.1" @@ -6473,7 +7018,7 @@ __metadata: optional: true react-redux: optional: true - checksum: 10/b24ee7e89bd00c5f3ee5df12cbc1b6395784ff215b9bf75d2c1b211d8494af220057ced666f49c6e815b723252bc3eb704f7be4a3c261ab06ee7b776f4565d4b + checksum: 10/f291b560a16d66092086b222aa950fc19493d6775ce0359d263d99486302fbf013eec1989b1e87921b0d84e5008a8f1a029522999cfb3be485cfa3c015fef197 languageName: node linkType: hard @@ -6565,7 +7110,7 @@ __metadata: languageName: node linkType: hard -"@rollup/pluginutils@npm:^5.0.1, @rollup/pluginutils@npm:^5.0.5, @rollup/pluginutils@npm:^5.1.0": +"@rollup/pluginutils@npm:^5.0.1, @rollup/pluginutils@npm:^5.1.0": version: 5.1.2 resolution: "@rollup/pluginutils@npm:5.1.2" dependencies: @@ -6581,135 +7126,135 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.28.1" +"@rollup/rollup-android-arm-eabi@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.32.1" conditions: os=android & cpu=arm languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-android-arm64@npm:4.28.1" +"@rollup/rollup-android-arm64@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-android-arm64@npm:4.32.1" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-darwin-arm64@npm:4.28.1" +"@rollup/rollup-darwin-arm64@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-darwin-arm64@npm:4.32.1" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-darwin-x64@npm:4.28.1" +"@rollup/rollup-darwin-x64@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-darwin-x64@npm:4.32.1" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-freebsd-arm64@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-freebsd-arm64@npm:4.28.1" +"@rollup/rollup-freebsd-arm64@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.32.1" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-freebsd-x64@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-freebsd-x64@npm:4.28.1" +"@rollup/rollup-freebsd-x64@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-freebsd-x64@npm:4.32.1" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.28.1" +"@rollup/rollup-linux-arm-gnueabihf@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.32.1" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm-musleabihf@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.28.1" +"@rollup/rollup-linux-arm-musleabihf@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.32.1" conditions: os=linux & cpu=arm & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.28.1" +"@rollup/rollup-linux-arm64-gnu@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.32.1" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.28.1" +"@rollup/rollup-linux-arm64-musl@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.32.1" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-loongarch64-gnu@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.28.1" +"@rollup/rollup-linux-loongarch64-gnu@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.32.1" conditions: os=linux & cpu=loong64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-powerpc64le-gnu@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.28.1" +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.32.1" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.28.1" +"@rollup/rollup-linux-riscv64-gnu@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.32.1" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.28.1" +"@rollup/rollup-linux-s390x-gnu@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.32.1" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.28.1" +"@rollup/rollup-linux-x64-gnu@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.32.1" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.28.1" +"@rollup/rollup-linux-x64-musl@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.32.1" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.28.1" +"@rollup/rollup-win32-arm64-msvc@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.32.1" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.28.1" +"@rollup/rollup-win32-ia32-msvc@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.32.1" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.28.1": - version: 4.28.1 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.28.1" +"@rollup/rollup-win32-x64-msvc@npm:4.32.1": + version: 4.32.1 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.32.1" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -7015,19 +7560,6 @@ __metadata: languageName: node linkType: hard -"@snyk/github-codeowners@npm:1.1.0": - version: 1.1.0 - resolution: "@snyk/github-codeowners@npm:1.1.0" - dependencies: - commander: "npm:^4.1.1" - ignore: "npm:^5.1.8" - p-map: "npm:^4.0.0" - bin: - github-codeowners: dist/cli.js - checksum: 10/34120ef622616fef1ed8af12869d8c1803842aafa3fbacca263805ee7c85f58d11bdc301ef698c9b41268b275b9fd090f5d9f6d89c556abe9d52196e72d1c510 - languageName: node - linkType: hard - "@socket.io/component-emitter@npm:~3.1.0": version: 3.1.2 resolution: "@socket.io/component-emitter@npm:3.1.2" @@ -7261,7 +7793,7 @@ __metadata: languageName: node linkType: hard -"@storybook/components@npm:8.4.4, @storybook/components@npm:^8.0.0, @storybook/components@npm:^8.4.2": +"@storybook/components@npm:8.4.4, @storybook/components@npm:^8.4.2": version: 8.4.4 resolution: "@storybook/components@npm:8.4.4" peerDependencies: @@ -7270,7 +7802,7 @@ __metadata: languageName: node linkType: hard -"@storybook/core-events@npm:^8.0.0, @storybook/core-events@npm:^8.4.2": +"@storybook/core-events@npm:^8.4.2": version: 8.4.4 resolution: "@storybook/core-events@npm:8.4.4" peerDependencies: @@ -7342,7 +7874,7 @@ __metadata: languageName: node linkType: hard -"@storybook/icons@npm:^1.2.12, @storybook/icons@npm:^1.2.5": +"@storybook/icons@npm:^1.2.12": version: 1.2.12 resolution: "@storybook/icons@npm:1.2.12" peerDependencies: @@ -7352,7 +7884,7 @@ __metadata: languageName: node linkType: hard -"@storybook/manager-api@npm:8.4.4, @storybook/manager-api@npm:^8.0.0, @storybook/manager-api@npm:^8.4.2": +"@storybook/manager-api@npm:8.4.4, @storybook/manager-api@npm:^8.4.2": version: 8.4.4 resolution: "@storybook/manager-api@npm:8.4.4" peerDependencies: @@ -7503,7 +8035,7 @@ __metadata: languageName: node linkType: hard -"@storybook/theming@npm:8.4.4, @storybook/theming@npm:^8.0.0, @storybook/theming@npm:^8.4.2": +"@storybook/theming@npm:8.4.4, @storybook/theming@npm:^8.4.2": version: 8.4.4 resolution: "@storybook/theming@npm:8.4.4" peerDependencies: @@ -7512,16 +8044,16 @@ __metadata: languageName: node linkType: hard -"@stylistic/eslint-plugin-ts@npm:^2.9.0": - version: 2.13.0 - resolution: "@stylistic/eslint-plugin-ts@npm:2.13.0" +"@stylistic/eslint-plugin-ts@npm:^3.0.0": + version: 3.0.1 + resolution: "@stylistic/eslint-plugin-ts@npm:3.0.1" dependencies: "@typescript-eslint/utils": "npm:^8.13.0" eslint-visitor-keys: "npm:^4.2.0" espree: "npm:^10.3.0" peerDependencies: eslint: ">=8.40.0" - checksum: 10/222d6ec0094177f40ea43f28af257192655539fe4bb1495341bc9ff521bca975da2066e229798fcc8e28ea24e91d0ef4db05b4f212f615cad752ade5c1c68561 + checksum: 10/24dc9f67ff56bc200340807a1fd055e8f49332c244b0ba214ea68ed91773d922790645db34f014e0f703644cc605171e5190df385597dad12d127d92fd72d29b languageName: node linkType: hard @@ -7715,57 +8247,57 @@ __metadata: languageName: node linkType: hard -"@swagger-api/apidom-ast@npm:^1.0.0-beta.5": - version: 1.0.0-beta.5 - resolution: "@swagger-api/apidom-ast@npm:1.0.0-beta.5" +"@swagger-api/apidom-ast@npm:^1.0.0-beta.11, @swagger-api/apidom-ast@npm:^1.0.0-beta.5": + version: 1.0.0-beta.11 + resolution: "@swagger-api/apidom-ast@npm:1.0.0-beta.11" dependencies: "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.5" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" unraw: "npm:^3.0.0" - checksum: 10/b2ea32b8ed589a3aff122e9209d5f0c873364bb34b234d13796422d4fce6b9f52fab599ef47956f655316256cf6af821c233117c0ca96a677b867e075b70cb5d + checksum: 10/efe7caf37735f6b1a9b56ca780ce59134540c00c50ef00606607a458905142b7794466cacbffdcd9afe11ece9f5a99651f2f62d24b05f71727d86718e2dea91c languageName: node linkType: hard -"@swagger-api/apidom-core@npm:>=1.0.0-beta.3 <1.0.0-rc.0, @swagger-api/apidom-core@npm:^1.0.0-beta.5": - version: 1.0.0-beta.5 - resolution: "@swagger-api/apidom-core@npm:1.0.0-beta.5" +"@swagger-api/apidom-core@npm:>=1.0.0-beta.11 <1.0.0-rc.0, @swagger-api/apidom-core@npm:^1.0.0-beta.11, @swagger-api/apidom-core@npm:^1.0.0-beta.5": + version: 1.0.0-beta.11 + resolution: "@swagger-api/apidom-core@npm:1.0.0-beta.11" dependencies: "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-ast": "npm:^1.0.0-beta.5" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.5" + "@swagger-api/apidom-ast": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" "@types/ramda": "npm:~0.30.0" minim: "npm:~0.23.8" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" short-unique-id: "npm:^5.0.2" ts-mixer: "npm:^6.0.3" - checksum: 10/c034ef286738b2b5aab525b068fd22e1b54145e3024477abcafde926f3783c280c69e4de23cc28d9cc568a62fb02719a0a89e6fd2011136cf447f42ed66fca55 + checksum: 10/bb81c1ef603b80985c8e7ad8808a12eaaf2daa3de2a41ac2f9f8c041c7a7f26bcbfa9713738248db198af1a6b424127b10abd5ac7e6fcad95d852cea8fb8a526 languageName: node linkType: hard -"@swagger-api/apidom-error@npm:>=1.0.0-beta.3 <1.0.0-rc.0, @swagger-api/apidom-error@npm:^1.0.0-beta.3 <1.0.0-rc.0, @swagger-api/apidom-error@npm:^1.0.0-beta.5": - version: 1.0.0-beta.5 - resolution: "@swagger-api/apidom-error@npm:1.0.0-beta.5" +"@swagger-api/apidom-error@npm:>=1.0.0-beta.11 <1.0.0-rc.0, @swagger-api/apidom-error@npm:^1.0.0-beta.11, @swagger-api/apidom-error@npm:^1.0.0-beta.3 <1.0.0-rc.0, @swagger-api/apidom-error@npm:^1.0.0-beta.5": + version: 1.0.0-beta.11 + resolution: "@swagger-api/apidom-error@npm:1.0.0-beta.11" dependencies: "@babel/runtime-corejs3": "npm:^7.20.7" - checksum: 10/defb3ba3775be8a511ff01be4ed7d2eca66faf5ab478f65a39845c8981510a0286e622268240de56613fd3ee37de906a7c6947a82aceb214fdd89d0988b972bb + checksum: 10/d63ec68067a17c10fa51b2929c18d18f397d91ab45961e398ad99341b45e87d60a0ec6f634ac3883733ea7f4a5155e9929207dd44572f65754fa0f5b7d004466 languageName: node linkType: hard -"@swagger-api/apidom-json-pointer@npm:>=1.0.0-beta.3 <1.0.0-rc.0, @swagger-api/apidom-json-pointer@npm:^1.0.0-beta.3 <1.0.0-rc.0, @swagger-api/apidom-json-pointer@npm:^1.0.0-beta.5": - version: 1.0.0-beta.5 - resolution: "@swagger-api/apidom-json-pointer@npm:1.0.0-beta.5" +"@swagger-api/apidom-json-pointer@npm:>=1.0.0-beta.11 <1.0.0-rc.0, @swagger-api/apidom-json-pointer@npm:^1.0.0-beta.11, @swagger-api/apidom-json-pointer@npm:^1.0.0-beta.3 <1.0.0-rc.0": + version: 1.0.0-beta.11 + resolution: "@swagger-api/apidom-json-pointer@npm:1.0.0-beta.11" dependencies: "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.5" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.5" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - checksum: 10/68b3b196de1d2cb86663350ba70f885599a12c21804cd9bc1be1777ee5e99e9f570623599d476d241bda298e0890c34d2c9a516f7a47cf060992107c31e9d9f5 + checksum: 10/dee858ee4b4a0f93ab082451a703c8b9e0250f7b486d8d9ef243b77f92acc49741cfdd1e10a9cc44d946b64e469aa4016a1380c7a28d660fe048a2897098afc7 languageName: node linkType: hard @@ -7800,50 +8332,82 @@ __metadata: languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-draft-4@npm:^1.0.0-beta.5": - version: 1.0.0-beta.5 - resolution: "@swagger-api/apidom-ns-json-schema-draft-4@npm:1.0.0-beta.5" +"@swagger-api/apidom-ns-json-schema-2019-09@npm:^1.0.0-beta.11": + version: 1.0.0-beta.11 + resolution: "@swagger-api/apidom-ns-json-schema-2019-09@npm:1.0.0-beta.11" dependencies: "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-ast": "npm:^1.0.0-beta.5" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.5" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-ns-json-schema-draft-7": "npm:^1.0.0-beta.11" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.4" - checksum: 10/2518b3aa9b7387a90553565132101616dc24c6d1f8aee581f3562bac87403f87887217fdb3cf17b113f685e051980fc66ec25d9cab7e4a0bf4922b4bcd3bc502 + checksum: 10/b91f90e7376922f1112752520bcac265c20245c1d2d682cd5ba9344a3eb7287962f18e3430f501af4d4db43397a2977267b15ef02a3bcd6e8ba384901e195672 languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-draft-6@npm:^1.0.0-beta.5": - version: 1.0.0-beta.5 - resolution: "@swagger-api/apidom-ns-json-schema-draft-6@npm:1.0.0-beta.5" +"@swagger-api/apidom-ns-json-schema-2020-12@npm:^1.0.0-beta.11": + version: 1.0.0-beta.11 + resolution: "@swagger-api/apidom-ns-json-schema-2020-12@npm:1.0.0-beta.11" dependencies: "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.5" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.5" - "@swagger-api/apidom-ns-json-schema-draft-4": "npm:^1.0.0-beta.5" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-ns-json-schema-2019-09": "npm:^1.0.0-beta.11" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.4" - checksum: 10/c439c3679dc6ea1807affa0a2913deea52ff8d63b48722c741458da147e849be9acc0c13955757a7b6904a4e7148cdb7845291da758a085083c61610a96fc36f + checksum: 10/847103b0923fa7a0fcff124f5359f735f56e38a88666051e5959403e0060e33bea63b30098dec99d36bcda6619b2a3d13ce61c26e6ca03c508ea8518e871c123 languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-draft-7@npm:^1.0.0-beta.5": - version: 1.0.0-beta.5 - resolution: "@swagger-api/apidom-ns-json-schema-draft-7@npm:1.0.0-beta.5" +"@swagger-api/apidom-ns-json-schema-draft-4@npm:^1.0.0-beta.11, @swagger-api/apidom-ns-json-schema-draft-4@npm:^1.0.0-beta.5": + version: 1.0.0-beta.11 + resolution: "@swagger-api/apidom-ns-json-schema-draft-4@npm:1.0.0-beta.11" dependencies: "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.5" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.5" - "@swagger-api/apidom-ns-json-schema-draft-6": "npm:^1.0.0-beta.5" + "@swagger-api/apidom-ast": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.4" - checksum: 10/28c8f989e26a453f2ca6c655d6822d63f7bc57137375c460c707caa964a5097566969031f322d772b49de0ce2924aeb69e2c5a6ff66191b457844e68a40f013e + checksum: 10/9e0ba9e29849915c0067227a82d8d9a741221b3ac73592b73e23d3cfe0c57548b21cbbb66cdeba914218c5f675f4ec497232d465880013f8aec8f486e4c3d91c + languageName: node + linkType: hard + +"@swagger-api/apidom-ns-json-schema-draft-6@npm:^1.0.0-beta.11": + version: 1.0.0-beta.11 + resolution: "@swagger-api/apidom-ns-json-schema-draft-6@npm:1.0.0-beta.11" + dependencies: + "@babel/runtime-corejs3": "npm:^7.20.7" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-ns-json-schema-draft-4": "npm:^1.0.0-beta.11" + "@types/ramda": "npm:~0.30.0" + ramda: "npm:~0.30.0" + ramda-adjunct: "npm:^5.0.0" + ts-mixer: "npm:^6.0.4" + checksum: 10/0df53880202cdb8dbe1e534314842c2c10fe4cd03f6eecd56d67174492807df03134f360e1c0724610fc4531520c49bb41d81e6fd7373118031ad883e0101338 + languageName: node + linkType: hard + +"@swagger-api/apidom-ns-json-schema-draft-7@npm:^1.0.0-beta.11, @swagger-api/apidom-ns-json-schema-draft-7@npm:^1.0.0-beta.5": + version: 1.0.0-beta.11 + resolution: "@swagger-api/apidom-ns-json-schema-draft-7@npm:1.0.0-beta.11" + dependencies: + "@babel/runtime-corejs3": "npm:^7.20.7" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-ns-json-schema-draft-6": "npm:^1.0.0-beta.11" + "@types/ramda": "npm:~0.30.0" + ramda: "npm:~0.30.0" + ramda-adjunct: "npm:^5.0.0" + ts-mixer: "npm:^6.0.4" + checksum: 10/9ef5261e58b2e6798a3c22ad01940c613475a6515d2cd79d2f150b02152118036ef472bb75a271c0d1c3b617d51ccb2262680469824335f27150600f695eb6d8 languageName: node linkType: hard @@ -7863,36 +8427,37 @@ __metadata: languageName: node linkType: hard -"@swagger-api/apidom-ns-openapi-3-0@npm:^1.0.0-beta.3 <1.0.0-rc.0, @swagger-api/apidom-ns-openapi-3-0@npm:^1.0.0-beta.5": - version: 1.0.0-beta.5 - resolution: "@swagger-api/apidom-ns-openapi-3-0@npm:1.0.0-beta.5" +"@swagger-api/apidom-ns-openapi-3-0@npm:^1.0.0-beta.11, @swagger-api/apidom-ns-openapi-3-0@npm:^1.0.0-beta.3 <1.0.0-rc.0, @swagger-api/apidom-ns-openapi-3-0@npm:^1.0.0-beta.5": + version: 1.0.0-beta.11 + resolution: "@swagger-api/apidom-ns-openapi-3-0@npm:1.0.0-beta.11" dependencies: "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.5" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.5" - "@swagger-api/apidom-ns-json-schema-draft-4": "npm:^1.0.0-beta.5" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-ns-json-schema-draft-4": "npm:^1.0.0-beta.11" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.3" - checksum: 10/5bb9c191a3d79d9a69aa04deb5dd6ce2eaa09700210f5a89fdeb0668ec76eb3f2d4a26950405f001459e59ad199ddd4632251992b9fc1b9fa03955e1ad600810 + checksum: 10/44eabda02fb8ad965b7756931bba7ac2ae9f49ef720f401179efb9eafbf94ff6b3c8cb8fd260c9ddb1affe35c3876f780cef4cb090b755736847adb7eba699c8 languageName: node linkType: hard -"@swagger-api/apidom-ns-openapi-3-1@npm:>=1.0.0-beta.3 <1.0.0-rc.0, @swagger-api/apidom-ns-openapi-3-1@npm:^1.0.0-beta.3 <1.0.0-rc.0, @swagger-api/apidom-ns-openapi-3-1@npm:^1.0.0-beta.5": - version: 1.0.0-beta.5 - resolution: "@swagger-api/apidom-ns-openapi-3-1@npm:1.0.0-beta.5" +"@swagger-api/apidom-ns-openapi-3-1@npm:>=1.0.0-beta.11 <1.0.0-rc.0, @swagger-api/apidom-ns-openapi-3-1@npm:^1.0.0-beta.3 <1.0.0-rc.0, @swagger-api/apidom-ns-openapi-3-1@npm:^1.0.0-beta.5": + version: 1.0.0-beta.11 + resolution: "@swagger-api/apidom-ns-openapi-3-1@npm:1.0.0-beta.11" dependencies: "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-ast": "npm:^1.0.0-beta.5" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.5" - "@swagger-api/apidom-json-pointer": "npm:^1.0.0-beta.5" - "@swagger-api/apidom-ns-openapi-3-0": "npm:^1.0.0-beta.5" + "@swagger-api/apidom-ast": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-json-pointer": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-ns-json-schema-2020-12": "npm:^1.0.0-beta.11" + "@swagger-api/apidom-ns-openapi-3-0": "npm:^1.0.0-beta.11" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.3" - checksum: 10/69018147465c78a25efc5e7bc4439561c2d620c65c9cf86bdc503dbad9b40e52db77bb0cb11800a4c34bdc88d462126e3dc355bad290cdfe7c9fbeda391405c8 + checksum: 10/6ed3a3d7bc231c8f3273c60dcd459cdb117246e372ed9d0d2483601a966a3109b9617a4d8f327f494a31013d5bcf2bc372c5f3297bbf953c61ff4770c87414a5 languageName: node linkType: hard @@ -8129,12 +8694,12 @@ __metadata: languageName: node linkType: hard -"@swagger-api/apidom-reference@npm:>=1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.5 - resolution: "@swagger-api/apidom-reference@npm:1.0.0-beta.5" +"@swagger-api/apidom-reference@npm:>=1.0.0-beta.11 <1.0.0-rc.0": + version: 1.0.0-beta.11 + resolution: "@swagger-api/apidom-reference@npm:1.0.0-beta.11" dependencies: "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.5" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" "@swagger-api/apidom-error": "npm:^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-json-pointer": "npm:^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-ns-asyncapi-2": "npm:^1.0.0-beta.3 <1.0.0-rc.0" @@ -8205,94 +8770,103 @@ __metadata: optional: true "@swagger-api/apidom-parser-adapter-yaml-1-2": optional: true - checksum: 10/e118278a4a460545de4e393aecaef16f92828cc0af02f4b9b3e8773b9f61c495e0afbce97e68ebd901858dc4b73a178aec2ec18fc1cec2a54d3ccbd3f6bc5331 + checksum: 10/1ae856fd2d13884f8f063421c010f166da1602075ae3b08c041c98199c724615b389110672e2c1b129b7b551a21b8738d32df87c066cc105669a03b6b21897e6 languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.10.9": - version: 1.10.9 - resolution: "@swc/core-darwin-arm64@npm:1.10.9" +"@swaggerexpert/cookie@npm:^1.4.1": + version: 1.4.1 + resolution: "@swaggerexpert/cookie@npm:1.4.1" + dependencies: + apg-lite: "npm:^1.0.3" + checksum: 10/936590cb70fb7af4ec988e7ee7c5e965b66d09046d3fd2b52c140f9777cda1452f2f24d80eb64ff9678b79f566dd0ef51699050aea9c9a3fd5345dcc73a3fa7a + languageName: node + linkType: hard + +"@swc/core-darwin-arm64@npm:1.10.12": + version: 1.10.12 + resolution: "@swc/core-darwin-arm64@npm:1.10.12" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.10.9": - version: 1.10.9 - resolution: "@swc/core-darwin-x64@npm:1.10.9" +"@swc/core-darwin-x64@npm:1.10.12": + version: 1.10.12 + resolution: "@swc/core-darwin-x64@npm:1.10.12" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.10.9": - version: 1.10.9 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.10.9" +"@swc/core-linux-arm-gnueabihf@npm:1.10.12": + version: 1.10.12 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.10.12" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.10.9": - version: 1.10.9 - resolution: "@swc/core-linux-arm64-gnu@npm:1.10.9" +"@swc/core-linux-arm64-gnu@npm:1.10.12": + version: 1.10.12 + resolution: "@swc/core-linux-arm64-gnu@npm:1.10.12" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.10.9": - version: 1.10.9 - resolution: "@swc/core-linux-arm64-musl@npm:1.10.9" +"@swc/core-linux-arm64-musl@npm:1.10.12": + version: 1.10.12 + resolution: "@swc/core-linux-arm64-musl@npm:1.10.12" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.10.9": - version: 1.10.9 - resolution: "@swc/core-linux-x64-gnu@npm:1.10.9" +"@swc/core-linux-x64-gnu@npm:1.10.12": + version: 1.10.12 + resolution: "@swc/core-linux-x64-gnu@npm:1.10.12" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.10.9": - version: 1.10.9 - resolution: "@swc/core-linux-x64-musl@npm:1.10.9" +"@swc/core-linux-x64-musl@npm:1.10.12": + version: 1.10.12 + resolution: "@swc/core-linux-x64-musl@npm:1.10.12" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.10.9": - version: 1.10.9 - resolution: "@swc/core-win32-arm64-msvc@npm:1.10.9" +"@swc/core-win32-arm64-msvc@npm:1.10.12": + version: 1.10.12 + resolution: "@swc/core-win32-arm64-msvc@npm:1.10.12" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.10.9": - version: 1.10.9 - resolution: "@swc/core-win32-ia32-msvc@npm:1.10.9" +"@swc/core-win32-ia32-msvc@npm:1.10.12": + version: 1.10.12 + resolution: "@swc/core-win32-ia32-msvc@npm:1.10.12" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.10.9": - version: 1.10.9 - resolution: "@swc/core-win32-x64-msvc@npm:1.10.9" +"@swc/core-win32-x64-msvc@npm:1.10.12": + version: 1.10.12 + resolution: "@swc/core-win32-x64-msvc@npm:1.10.12" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@swc/core@npm:1.10.9, @swc/core@npm:^1.7.3": - version: 1.10.9 - resolution: "@swc/core@npm:1.10.9" +"@swc/core@npm:1.10.12, @swc/core@npm:^1.7.3": + version: 1.10.12 + resolution: "@swc/core@npm:1.10.12" dependencies: - "@swc/core-darwin-arm64": "npm:1.10.9" - "@swc/core-darwin-x64": "npm:1.10.9" - "@swc/core-linux-arm-gnueabihf": "npm:1.10.9" - "@swc/core-linux-arm64-gnu": "npm:1.10.9" - "@swc/core-linux-arm64-musl": "npm:1.10.9" - "@swc/core-linux-x64-gnu": "npm:1.10.9" - "@swc/core-linux-x64-musl": "npm:1.10.9" - "@swc/core-win32-arm64-msvc": "npm:1.10.9" - "@swc/core-win32-ia32-msvc": "npm:1.10.9" - "@swc/core-win32-x64-msvc": "npm:1.10.9" + "@swc/core-darwin-arm64": "npm:1.10.12" + "@swc/core-darwin-x64": "npm:1.10.12" + "@swc/core-linux-arm-gnueabihf": "npm:1.10.12" + "@swc/core-linux-arm64-gnu": "npm:1.10.12" + "@swc/core-linux-arm64-musl": "npm:1.10.12" + "@swc/core-linux-x64-gnu": "npm:1.10.12" + "@swc/core-linux-x64-musl": "npm:1.10.12" + "@swc/core-win32-arm64-msvc": "npm:1.10.12" + "@swc/core-win32-ia32-msvc": "npm:1.10.12" + "@swc/core-win32-x64-msvc": "npm:1.10.12" "@swc/counter": "npm:^0.1.3" "@swc/types": "npm:^0.1.17" peerDependencies: @@ -8321,7 +8895,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10/543e79c249f6052883d656035321d449cf6c0f2ea54f786d5e3b96394d4cf201b293d6c3f897cc604eb145b21cce82f904306931fe9efbc6a50c714a5d5d97f0 + checksum: 10/62b77009f267f28a486ded1de9ac250b65dc618a4c551a86ad8e438ee54d96b0d44f52332fb242eaa9a6e00db499436753ef99747e6c1a14bd6894554c10e799 languageName: node linkType: hard @@ -8351,21 +8925,21 @@ __metadata: linkType: hard "@tanstack/react-virtual@npm:^3.5.1, @tanstack/react-virtual@npm:^3.9.0": - version: 3.11.2 - resolution: "@tanstack/react-virtual@npm:3.11.2" + version: 3.11.3 + resolution: "@tanstack/react-virtual@npm:3.11.3" dependencies: - "@tanstack/virtual-core": "npm:3.11.2" + "@tanstack/virtual-core": "npm:3.11.3" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: 10/a1136da0ec4c2ecbd4f996d8b84f228f0b8d851b15806e01049a160ad1d9b2eef0e0a491035fe017c6f84a0e125334f69ea23b32c180df23614ea4a8eeb7490c + checksum: 10/eb39f8a015f4dc98070f0c18bbb1f9c094b7182133554ef3ee31d2678cd3a66edd28ce854d533e830f88f1f0ad1d5b065de184438a08fe774a9acc1dc62da436 languageName: node linkType: hard -"@tanstack/virtual-core@npm:3.11.2": - version: 3.11.2 - resolution: "@tanstack/virtual-core@npm:3.11.2" - checksum: 10/8433044a5c801052ba2e4cdda098cdc8e32adfd3a76ba31af7064bbdda60062fe221a3558096987baa66cd94f528855e887c282cb0f9eb99d3751457c2a62872 +"@tanstack/virtual-core@npm:3.11.3": + version: 3.11.3 + resolution: "@tanstack/virtual-core@npm:3.11.3" + checksum: 10/24a3369dd0290d4f19aa1af7d0a6fb1b843741d722c6a5cf786416657bbf978f4f82a0b257eaee867d0798d8334374f5e940868a7b71dc065939fb7eeee19ad1 languageName: node linkType: hard @@ -8407,7 +8981,7 @@ __metadata: dependencies: "@emotion/css": "npm:11.11.2" "@grafana/data": "workspace:*" - "@grafana/plugin-configs": "npm:11.5.0-pre" + "@grafana/plugin-configs": "workspace:*" "@grafana/runtime": "workspace:*" "@grafana/schema": "workspace:*" "@grafana/ui": "workspace:*" @@ -8464,9 +9038,9 @@ __metadata: languageName: node linkType: hard -"@testing-library/react@npm:16.1.0": - version: 16.1.0 - resolution: "@testing-library/react@npm:16.1.0" +"@testing-library/react@npm:16.2.0": + version: 16.2.0 + resolution: "@testing-library/react@npm:16.2.0" dependencies: "@babel/runtime": "npm:^7.12.5" peerDependencies: @@ -8480,16 +9054,23 @@ __metadata: optional: true "@types/react-dom": optional: true - checksum: 10/2a20e0dbfadbc93d45a84e82281ed47deed54a6a5fc1461a523172d7fbc0481e8502cf98a2080f38aba94290b3d745671a1c9e320e6f76ad6afcca67c580b963 + checksum: 10/cf10bfa9a363384e6861417696fff4a464a64f98ec6f0bb7f1fa7cbb550d075d23a2f6a943b7df85dded7bde3234f6ea6b6e36f95211f4544b846ea72c288289 languageName: node linkType: hard -"@testing-library/user-event@npm:14.5.2": - version: 14.5.2 - resolution: "@testing-library/user-event@npm:14.5.2" +"@testing-library/user-event@npm:14.6.1": + version: 14.6.1 + resolution: "@testing-library/user-event@npm:14.6.1" peerDependencies: "@testing-library/dom": ">=7.21.4" - checksum: 10/49821459d81c6bc435d97128d6386ca24f1e4b3ba8e46cb5a96fe3643efa6e002d88c1b02b7f2ec58da593e805c59b78d7fdf0db565c1f02ba782f63ee984040 + checksum: 10/34b74fff56a0447731a94b40d4cf246deb8dbc1c1e3aec93acd1c3377a760bb062e979f1572bb34ec164ad28ee2a391744b42d0d6d6cc16c4ce527e5e09610e1 + languageName: node + linkType: hard + +"@tokenizer/token@npm:^0.3.0": + version: 0.3.0 + resolution: "@tokenizer/token@npm:0.3.0" + checksum: 10/889c1f1e63ac7c92c0ea22d4a2861142f1b43c3d92eb70ec42aa9e9851fab2e9952211d50f541b287781280df2f979bf5600a9c1f91fbc61b7fcf9994e9376a5 languageName: node linkType: hard @@ -8630,9 +9211,9 @@ __metadata: linkType: hard "@types/babel__preset-env@npm:^7": - version: 7.9.7 - resolution: "@types/babel__preset-env@npm:7.9.7" - checksum: 10/624425a84d9149aec04795fed6b1ac2f27dfd5d7976fde479bb1a4d754de34c92cdc28a1a373a5826382a68127b536420a0e090aa5fae522cb62724b7a571cb5 + version: 7.10.0 + resolution: "@types/babel__preset-env@npm:7.10.0" + checksum: 10/7d4d12758d89708afe327079d7d7580e8af3292295f087b8a9a48e12ac1d90aadc18ac3bc00f9b0cbc8778f3ce9fe778801d4d49b7691a75e3f13a901b69fd07 languageName: node linkType: hard @@ -9042,9 +9623,9 @@ __metadata: linkType: hard "@types/diff@npm:^7.0.0": - version: 7.0.0 - resolution: "@types/diff@npm:7.0.0" - checksum: 10/11464fe70a81a0d02fdaad4ff4b0a0a5ac71825fa32dde9a98a1a82b1cac50dda302fb1621bea71db6c254e00ce002ea14543cc3502ac44f012598ca2c0fbcae + version: 7.0.1 + resolution: "@types/diff@npm:7.0.1" + checksum: 10/ef8c5fe0ea56737e8967c3db5e78518134f704e8e2971cb4ba6f2ed46ed5e13b4bbe41a6b2b78122b8f47c618ac25550f85a681633636a3a020369042523295d languageName: node linkType: hard @@ -9303,6 +9884,15 @@ __metadata: languageName: node linkType: hard +"@types/jquery@npm:3.5.31": + version: 3.5.31 + resolution: "@types/jquery@npm:3.5.31" + dependencies: + "@types/sizzle": "npm:*" + checksum: 10/c14b3db4d2c34eb44b30ae119f1983d9d94231a02d44357b08f3ef406852c777edd928eb35875e879515a96eb8eb2188ed5572a0de35f322019bf6de858ce610 + languageName: node + linkType: hard + "@types/jquery@npm:3.5.32": version: 3.5.32 resolution: "@types/jquery@npm:3.5.32" @@ -9376,10 +9966,17 @@ __metadata: languageName: node linkType: hard -"@types/lodash@npm:*, @types/lodash@npm:4.17.14, @types/lodash@npm:^4.14.172": - version: 4.17.14 - resolution: "@types/lodash@npm:4.17.14" - checksum: 10/6ee40725f3e192f5ef1f493caca19210aa7acd7adc3136b8dba84d418a35be0abea0668105aed9f696ad62a54310a9c0d328971ad4b157f5bcda700424ed5aae +"@types/lodash@npm:*, @types/lodash@npm:4.17.15, @types/lodash@npm:^4.14.172": + version: 4.17.15 + resolution: "@types/lodash@npm:4.17.15" + checksum: 10/27b348b5971b9c670215331b52448a13d7d65bf1fbd320a7049c9c153c1186ff5d116ba75f05f07d32d7ece8a992b26a30c7bdc9be22a3d1e4e3e6068aa04603 + languageName: node + linkType: hard + +"@types/lodash@npm:4.17.10": + version: 4.17.10 + resolution: "@types/lodash@npm:4.17.10" + checksum: 10/10fe24a93adc6048cb23e4135c1ed1d52cc39033682e6513f4f51b74a9af6d7a24fbea92203c22dc4e01e35f1ab3aa0fd0a2b487e8a4a2bbdf1fc05970094066 languageName: node linkType: hard @@ -9467,12 +10064,19 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:22.10.7, @types/node@npm:>=10.0.0, @types/node@npm:>=13.7.0, @types/node@npm:>=13.7.4, @types/node@npm:^22.0.0": - version: 22.10.7 - resolution: "@types/node@npm:22.10.7" +"@types/node@npm:*, @types/node@npm:22.12.0, @types/node@npm:>=10.0.0, @types/node@npm:>=13.7.0, @types/node@npm:>=13.7.4, @types/node@npm:^22.0.0": + version: 22.12.0 + resolution: "@types/node@npm:22.12.0" dependencies: undici-types: "npm:~6.20.0" - checksum: 10/64cde1c2f5e5f7d597d3bd462f52c3c2d688a66623eb75d25e1d1d63d384ef553a27100635ad0dbb7d74da517048aa636947863eb624cf85f25d2f22370ce474 + checksum: 10/aac2b6f6a845ec3540c3d979b3150efe3162165bfda953af10b579df2d1cc4f5c48506922bf6bf661a2e5a7ebb571c5729bf1f9f12488a810bb1a5fa9522ef9d + languageName: node + linkType: hard + +"@types/node@npm:16.9.1": + version: 16.9.1 + resolution: "@types/node@npm:16.9.1" + checksum: 10/c144c007a42b3fbdea10dbb814b86d541197dfabcdc8b11e437ae984fd34dbac06e12b2b3a6a1d5453a8f877a505d3c269274c485cfc310ce14893094134a1d1 languageName: node linkType: hard @@ -9547,7 +10151,7 @@ __metadata: languageName: node linkType: hard -"@types/prismjs@npm:1.26.5": +"@types/prismjs@npm:1.26.5, @types/prismjs@npm:^1.26.4": version: 1.26.5 resolution: "@types/prismjs@npm:1.26.5" checksum: 10/617099479db9550119d0f84272dc79d64b2cf3e0d7a17167fe740d55fdf0f155697d935409464392d164e62080c2c88d649cf4bc4fdd30a87127337536657277 @@ -9884,12 +10488,12 @@ __metadata: languageName: node linkType: hard -"@types/swagger-ui-react@npm:4.18.3": - version: 4.18.3 - resolution: "@types/swagger-ui-react@npm:4.18.3" +"@types/swagger-ui-react@npm:5.18.0": + version: 5.18.0 + resolution: "@types/swagger-ui-react@npm:5.18.0" dependencies: "@types/react": "npm:*" - checksum: 10/4927314f1b0d68edf200ef15bca7555f12ec1bb8cc699fa397ef2f4e1e1873d17880b663bbc6d70e30149e50c3e7055178c1e0aad110520dfb31d781ab326dcb + checksum: 10/002bb3e38eb5917f5da942affa357811ccb078816bf53ba9007e43603f9b9a839eb930380b08051b9cc9a95ca2ebec06599a825e4d0542e436ea2c2027fc3a0a languageName: node linkType: hard @@ -9972,13 +10576,6 @@ __metadata: languageName: node linkType: hard -"@types/uuid@npm:^8.3.3": - version: 8.3.4 - resolution: "@types/uuid@npm:8.3.4" - checksum: 10/6f11f3ff70f30210edaa8071422d405e9c1d4e53abbe50fdce365150d3c698fe7bbff65c1e71ae080cbfb8fded860dbb5e174da96fdbbdfcaa3fb3daa474d20f - languageName: node - linkType: hard - "@types/webpack-assets-manifest@npm:^5": version: 5.1.4 resolution: "@types/webpack-assets-manifest@npm:5.1.4" @@ -10002,9 +10599,9 @@ __metadata: linkType: hard "@types/webpack-env@npm:^1.18.4": - version: 1.18.5 - resolution: "@types/webpack-env@npm:1.18.5" - checksum: 10/3c8dd0b23d45e2d33abdfbae7f1d8f75ce23d54588b08943e833f4dba81eb683ac68672a75eccbdba8e008bc1647638803c1bcadc8cdfd1dd7142fa2c3f612de + version: 1.18.8 + resolution: "@types/webpack-env@npm:1.18.8" + checksum: 10/f3932f3d6c2530f644cfc898eda1ab8182d6ae57f555c2f0179d813549b639078671b71e4041831fc306c5ebe61f5cdac794fe4ceae281fce8bf67e23661a488 languageName: node linkType: hard @@ -10042,40 +10639,40 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:8.18.1": - version: 8.18.1 - resolution: "@typescript-eslint/eslint-plugin@npm:8.18.1" +"@typescript-eslint/eslint-plugin@npm:8.22.0": + version: 8.22.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.22.0" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.18.1" - "@typescript-eslint/type-utils": "npm:8.18.1" - "@typescript-eslint/utils": "npm:8.18.1" - "@typescript-eslint/visitor-keys": "npm:8.18.1" + "@typescript-eslint/scope-manager": "npm:8.22.0" + "@typescript-eslint/type-utils": "npm:8.22.0" + "@typescript-eslint/utils": "npm:8.22.0" + "@typescript-eslint/visitor-keys": "npm:8.22.0" graphemer: "npm:^1.4.0" ignore: "npm:^5.3.1" natural-compare: "npm:^1.4.0" - ts-api-utils: "npm:^1.3.0" + ts-api-utils: "npm:^2.0.0" peerDependencies: "@typescript-eslint/parser": ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.8.0" - checksum: 10/ec061a9c64477260d1ef0fc6283d8754838181e17aa90b3b8b9a70936a2ca4bae11607070917a7701e13f5301ced2b6da4b4b6e5cf525c484f97481e540b5111 + checksum: 10/7211ad95f20a27182e2b55ef50102dfee4a7084c267c4e24cca24f0a28daa0360074a38bb71e407dad6d99db1165096b324b708cf35904b1d4f62fc9d5fd0f98 languageName: node linkType: hard -"@typescript-eslint/parser@npm:8.18.1": - version: 8.18.1 - resolution: "@typescript-eslint/parser@npm:8.18.1" +"@typescript-eslint/parser@npm:8.22.0": + version: 8.22.0 + resolution: "@typescript-eslint/parser@npm:8.22.0" dependencies: - "@typescript-eslint/scope-manager": "npm:8.18.1" - "@typescript-eslint/types": "npm:8.18.1" - "@typescript-eslint/typescript-estree": "npm:8.18.1" - "@typescript-eslint/visitor-keys": "npm:8.18.1" + "@typescript-eslint/scope-manager": "npm:8.22.0" + "@typescript-eslint/types": "npm:8.22.0" + "@typescript-eslint/typescript-estree": "npm:8.22.0" + "@typescript-eslint/visitor-keys": "npm:8.22.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.8.0" - checksum: 10/09a601ef8b837962e5bb2687358520f337f9d0bbac5c6d5e159654faa5caaffb24d990e8d6bc4dc51ff5008dd9e182315c35bc5e9e3789090ccef8b8040e7659 + checksum: 10/6b7fee52345e8a32d8cfea1ac4aeb563cb0c44ba46290686afde1cd541b787fcf61bec0e6960559f544e9ba3b72670a68f8eda860384aebb5744101f0f1a68c9 languageName: node linkType: hard @@ -10089,28 +10686,28 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.18.1, @typescript-eslint/scope-manager@npm:^8.15.0": - version: 8.18.1 - resolution: "@typescript-eslint/scope-manager@npm:8.18.1" +"@typescript-eslint/scope-manager@npm:8.22.0, @typescript-eslint/scope-manager@npm:^8.15.0": + version: 8.22.0 + resolution: "@typescript-eslint/scope-manager@npm:8.22.0" dependencies: - "@typescript-eslint/types": "npm:8.18.1" - "@typescript-eslint/visitor-keys": "npm:8.18.1" - checksum: 10/14f7c09924c3a006b20752e5204b33c2b6974fc00bea16c23f471e65f2fb089fcbd3fb5296bcfd6727ac95c32ba24ebb15ba84fbf1deadc17b4cc5ca7f41c72a + "@typescript-eslint/types": "npm:8.22.0" + "@typescript-eslint/visitor-keys": "npm:8.22.0" + checksum: 10/7fb4bae6d9f8b86a43405b24828cd36ba0751cce4346d86821a4827cded93227f92668044e5e6d802a32096b50cfcaf2ce9ab65322310fa68f5e3819bef70168 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.18.1": - version: 8.18.1 - resolution: "@typescript-eslint/type-utils@npm:8.18.1" +"@typescript-eslint/type-utils@npm:8.22.0": + version: 8.22.0 + resolution: "@typescript-eslint/type-utils@npm:8.22.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:8.18.1" - "@typescript-eslint/utils": "npm:8.18.1" + "@typescript-eslint/typescript-estree": "npm:8.22.0" + "@typescript-eslint/utils": "npm:8.22.0" debug: "npm:^4.3.4" - ts-api-utils: "npm:^1.3.0" + ts-api-utils: "npm:^2.0.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.8.0" - checksum: 10/cde53d05f4ca6e172239918cba2b560b9f837aa1fc7d5220784b1a6af9c8c525db020a5160822087e320305492fe359b7fb191420789b5f1e47a01e0cda21ac9 + checksum: 10/1da2447ce12f09370082daeef88f8922842e39d2a7b0abe3def21442f85bf4250524c60cbb97276d5cd876783b976dcb0ed85aeb8c0b100d83b7f3a59cdfccbf languageName: node linkType: hard @@ -10121,10 +10718,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:8.18.1, @typescript-eslint/types@npm:^8.9.0": - version: 8.18.1 - resolution: "@typescript-eslint/types@npm:8.18.1" - checksum: 10/57a6141ba17be929291a644991f3a76f94fce330376f6a079decb20fb53378d636ad6878f8f9b6fcb8244cf1ca8b118f9e8901ae04cf3de2aa9f9ff57791d97a +"@typescript-eslint/types@npm:8.22.0, @typescript-eslint/types@npm:^8.9.0": + version: 8.22.0 + resolution: "@typescript-eslint/types@npm:8.22.0" + checksum: 10/b43ea5b05ed0b43dcee8d2fa98b2c3f79c604780cbd56e6ba7f89e3066798b7169848694f59523fd2003e8fa699ddc97f28b0860a4eb04eea26c96d5ac9346bd languageName: node linkType: hard @@ -10146,36 +10743,36 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.18.1": - version: 8.18.1 - resolution: "@typescript-eslint/typescript-estree@npm:8.18.1" +"@typescript-eslint/typescript-estree@npm:8.22.0": + version: 8.22.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.22.0" dependencies: - "@typescript-eslint/types": "npm:8.18.1" - "@typescript-eslint/visitor-keys": "npm:8.18.1" + "@typescript-eslint/types": "npm:8.22.0" + "@typescript-eslint/visitor-keys": "npm:8.22.0" debug: "npm:^4.3.4" fast-glob: "npm:^3.3.2" is-glob: "npm:^4.0.3" minimatch: "npm:^9.0.4" semver: "npm:^7.6.0" - ts-api-utils: "npm:^1.3.0" + ts-api-utils: "npm:^2.0.0" peerDependencies: typescript: ">=4.8.4 <5.8.0" - checksum: 10/8ecc1b50b9fc32116eee1b3b00f3fb29cf18026c0bbb50ab5f6e01db58ef62b8ac01824f2950f132479be6e1b82466a2bfd1e2cb4525aa8dbce4c27fc2494cfc + checksum: 10/e3c0b191e2a0f55101c3e3333904f3a255d635e4ea0d026981cc25e83b62660a3a8a7993ac4a3d0c8756afb7dc272099eec48fd93e100a2b8467a5b80ef0026c languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.18.1, @typescript-eslint/utils@npm:^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/utils@npm:^8.13.0, @typescript-eslint/utils@npm:^8.15.0, @typescript-eslint/utils@npm:^8.9.0": - version: 8.18.1 - resolution: "@typescript-eslint/utils@npm:8.18.1" +"@typescript-eslint/utils@npm:8.22.0, @typescript-eslint/utils@npm:^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/utils@npm:^8.13.0, @typescript-eslint/utils@npm:^8.15.0, @typescript-eslint/utils@npm:^8.9.0": + version: 8.22.0 + resolution: "@typescript-eslint/utils@npm:8.22.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:8.18.1" - "@typescript-eslint/types": "npm:8.18.1" - "@typescript-eslint/typescript-estree": "npm:8.18.1" + "@typescript-eslint/scope-manager": "npm:8.22.0" + "@typescript-eslint/types": "npm:8.22.0" + "@typescript-eslint/typescript-estree": "npm:8.22.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.8.0" - checksum: 10/7b33d2ac273ad606a3dcb776bcf02c901812952550cdc93d4ece272b3b0e5d2a4e05fa92f9bd466f4a296ddd5992902d3b6623aa1c29d09e8e392897103e42a8 + checksum: 10/92a5ae5d79a5988e88fdda8d5e88f73e7b9ce24b339098d72698dba766ded274c24d0e2857bcb799c0aa7a59257e54a273eabdaaab39a5cd20283669201eeb53 languageName: node linkType: hard @@ -10207,13 +10804,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.18.1": - version: 8.18.1 - resolution: "@typescript-eslint/visitor-keys@npm:8.18.1" +"@typescript-eslint/visitor-keys@npm:8.22.0": + version: 8.22.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.22.0" dependencies: - "@typescript-eslint/types": "npm:8.18.1" + "@typescript-eslint/types": "npm:8.22.0" eslint-visitor-keys: "npm:^4.2.0" - checksum: 10/00e88b1640a68c3afea08731395eb09a8216892248fee819cb7526e99093256743239d6b9e880a499f1c0ddfe2ffa4d1ad895d9e778b5d42e702d5880db1a594 + checksum: 10/1a172620d46e23362c5d1e1e7c8186856dff6b6f1c2697d67f9aac1b3dfd0de96c2c73487e4deed80fad3bfa5cf74cfed3519221657c6ede602b04ac091525a4 languageName: node linkType: hard @@ -10960,6 +11557,13 @@ __metadata: languageName: node linkType: hard +"ansicolor@npm:1.1.100": + version: 1.1.100 + resolution: "ansicolor@npm:1.1.100" + checksum: 10/9420e96f44b578153dfd11e2d829d633ca2699452419aff48314219d66b7b69a9b6d994d2f2350b1d29a9826e33500b87fe85606629a8889cd52ce806908f4a9 + languageName: node + linkType: hard + "ansicolor@npm:2.0.3": version: 2.0.3 resolution: "ansicolor@npm:2.0.3" @@ -10974,6 +11578,13 @@ __metadata: languageName: node linkType: hard +"any-base@npm:^1.1.0": + version: 1.1.0 + resolution: "any-base@npm:1.1.0" + checksum: 10/c1fd040de52e710e2de7d9ae4df52bac589f35622adb24686c98ce21c7b824859a8db9614bc119ed8614f42fd08918b2612e6a6c385480462b3100a1af59289d + languageName: node + linkType: hard + "anymatch@npm:^3.0.3, anymatch@npm:^3.1.1, anymatch@npm:^3.1.3, anymatch@npm:~3.1.2": version: 3.1.3 resolution: "anymatch@npm:3.1.3" @@ -10984,7 +11595,7 @@ __metadata: languageName: node linkType: hard -"apg-lite@npm:^1.0.3": +"apg-lite@npm:^1.0.3, apg-lite@npm:^1.0.4": version: 1.0.4 resolution: "apg-lite@npm:1.0.4" checksum: 10/9c5eb431497415b738e332e5805f836c64ac9b75a399afaec0354859f3f44c95e203fd5c7b8fee9f28fe1e184dd30f6073ed9df062849c3df03c51624901a8a6 @@ -11311,7 +11922,7 @@ __metadata: languageName: node linkType: hard -"attr-accept@npm:^2.2.4": +"attr-accept@npm:^2.2.2, attr-accept@npm:^2.2.4": version: 2.2.5 resolution: "attr-accept@npm:2.2.5" checksum: 10/474b1c53e62c5b881c745d1f098196f190c8b493245e95d4b0fea9298d3acb56f551868fc12806885277e55e9d8ad3c5963e92d93456f4e4081dfc5190977bfd @@ -11354,6 +11965,13 @@ __metadata: languageName: node linkType: hard +"await-to-js@npm:^3.0.0": + version: 3.0.0 + resolution: "await-to-js@npm:3.0.0" + checksum: 10/b0445e4cbf9cf98482537f09b0a708be01b1e4d85465465545a7718b79cbefe2409a8cd0d4441d95503b1fabf29303fd9b540a8c71ed2a4b899446e6b93f9075 + languageName: node + linkType: hard + "aws-sign2@npm:~0.7.0": version: 0.7.0 resolution: "aws-sign2@npm:0.7.0" @@ -11541,6 +12159,16 @@ __metadata: languageName: node linkType: hard +"babel-runtime@npm:6.x, babel-runtime@npm:^6.26.0": + version: 6.26.0 + resolution: "babel-runtime@npm:6.26.0" + dependencies: + core-js: "npm:^2.4.0" + regenerator-runtime: "npm:^0.11.0" + checksum: 10/2cdf0f083b9598a43cdb11cbf1e7060584079a9a2230f06aec997ba81e887ef17fdcb5ad813a484ee099e06d2de0cea832bdd3011c06325acb284284c754ee8f + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -11714,6 +12342,13 @@ __metadata: languageName: node linkType: hard +"bmp-ts@npm:^1.0.9": + version: 1.0.9 + resolution: "bmp-ts@npm:1.0.9" + checksum: 10/f21712998a4f0b7ca9b201868d0c01d001d13465e3e90e0bfc9e8f7bf365b41b3bd4a79dc3076790f381ea30f16254352f8b7da745791e2ab5b51da96d8b3f63 + languageName: node + linkType: hard + "body-parser@npm:1.20.3": version: 1.20.3 resolution: "body-parser@npm:1.20.3" @@ -12010,6 +12645,16 @@ __metadata: languageName: node linkType: hard +"cacheable@npm:^1.8.7": + version: 1.8.7 + resolution: "cacheable@npm:1.8.7" + dependencies: + hookified: "npm:^1.6.0" + keyv: "npm:^5.2.3" + checksum: 10/dce96e947c5b879a58ce024fd2d08a4c44ee328e8406cd6243da2d0e3a17579d63108c80e9272f64190d2d2f00e26a307b3e3ff65b6665abc70956714442897b + languageName: node + linkType: hard + "cachedir@npm:^2.3.0": version: 2.3.0 resolution: "cachedir@npm:2.3.0" @@ -12168,13 +12813,13 @@ __metadata: languageName: node linkType: hard -"centrifuge@npm:5.3.0": - version: 5.3.0 - resolution: "centrifuge@npm:5.3.0" +"centrifuge@npm:5.3.2": + version: 5.3.2 + resolution: "centrifuge@npm:5.3.2" dependencies: events: "npm:^3.3.0" protobufjs: "npm:^7.2.5" - checksum: 10/a55619852b5f36073a522777ad6e47a0a9bf297badcd1309eb4ad6db8270c64335e48eb3c7dc5089c15fcfe83a4e72e7d705e8a89ac1874cbb70858d79fb70ee + checksum: 10/110c88c01206761dffb148f28aa89eb14ec345b564b2613d2aea9cf686db35b77330098df983fd6feed70a97ea4926791ac095c9bc2401894695840173e18eb4 languageName: node linkType: hard @@ -12755,13 +13400,6 @@ __metadata: languageName: node linkType: hard -"commander@npm:^4.1.1": - version: 4.1.1 - resolution: "commander@npm:4.1.1" - checksum: 10/3b2dc4125f387dab73b3294dbcb0ab2a862f9c0ad748ee2b27e3544d25325b7a8cdfbcc228d103a98a716960b14478114a5206b5415bd48cdafa38797891562c - languageName: node - linkType: hard - "commander@npm:^6.2.0, commander@npm:^6.2.1": version: 6.2.1 resolution: "commander@npm:6.2.1" @@ -12828,6 +13466,22 @@ __metadata: languageName: node linkType: hard +"component-classes@npm:^1.2.5": + version: 1.2.6 + resolution: "component-classes@npm:1.2.6" + dependencies: + component-indexof: "npm:0.0.3" + checksum: 10/aa70f282b85a19d7a190dabb2c72c9b8a2a5565fc42d72ca0661e8ce47e55e53da29f468467771d7e0bf4ba8c9723e5a21954fc38b942d9d873ee56be856adfc + languageName: node + linkType: hard + +"component-indexof@npm:0.0.3": + version: 0.0.3 + resolution: "component-indexof@npm:0.0.3" + checksum: 10/34a720e96fc0be1043a4517845b1b7483989736c559089eca285f7ac9ef049eacc8ab12fc31f924dfa2d4ecea0714611a695d087ce11caf30502b5a80646e9e9 + languageName: node + linkType: hard + "compressible@npm:~2.0.16": version: 2.0.18 resolution: "compressible@npm:2.0.18" @@ -13119,7 +13773,7 @@ __metadata: languageName: node linkType: hard -"core-js@npm:^2.6.5": +"core-js@npm:^2.4.0, core-js@npm:^2.6.5": version: 2.6.12 resolution: "core-js@npm:2.6.12" checksum: 10/7c624eb00a59c74c769d5d80f751f3bf1fc6201205b6562f27286ad5e00bbca1483f2f7eb0c2854b86f526ef5c7dc958b45f2ff536f8a31b8e9cb1a13a96efca @@ -13280,6 +13934,13 @@ __metadata: languageName: node linkType: hard +"croner@npm:^9.0.0": + version: 9.0.0 + resolution: "croner@npm:9.0.0" + checksum: 10/b3cea758eedfe92e35c4ebae46c9db615565348ad898b5938fd94ea77abc5ff68d86539db248a1666b007b0726da642c9046879e8fd598220afcc87c8135e656 + languageName: node + linkType: hard + "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" @@ -13291,6 +13952,16 @@ __metadata: languageName: node linkType: hard +"css-animation@npm:^1.3.2": + version: 1.6.1 + resolution: "css-animation@npm:1.6.1" + dependencies: + babel-runtime: "npm:6.x" + component-classes: "npm:^1.2.5" + checksum: 10/5aea8fd333300c6b15f523ac741dd2ed367489485c7a9a43647f7779fef9e5f338c405e3ad9ed8db338503b4aefea86108112a01bf78651b623f8bd2e146e0e0 + languageName: node + linkType: hard + "css-box-model@npm:^1.2.1": version: 1.2.1 resolution: "css-box-model@npm:1.2.1" @@ -13483,7 +14154,7 @@ __metadata: languageName: node linkType: hard -"css-tree@npm:^3.0.1": +"css-tree@npm:^3.1.0": version: 3.1.0 resolution: "css-tree@npm:3.1.0" dependencies: @@ -14150,6 +14821,13 @@ __metadata: languageName: node linkType: hard +"date-fns@npm:3.6.0": + version: 3.6.0 + resolution: "date-fns@npm:3.6.0" + checksum: 10/cac35c58926a3b5d577082ff2b253612ec1c79eb6754fddef46b6a8e826501ea2cb346ecbd211205f1ba382ddd1f9d8c3f00bf433ad63cc3063454d294e3a6b8 + languageName: node + linkType: hard + "date-fns@npm:4.1.0": version: 4.1.0 resolution: "date-fns@npm:4.1.0" @@ -14194,15 +14872,15 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.6, debug@npm:^4.3.7, debug@npm:~4.3.1, debug@npm:~4.3.2, debug@npm:~4.3.4": - version: 4.3.7 - resolution: "debug@npm:4.3.7" +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.6, debug@npm:^4.3.7, debug@npm:^4.4.0": + version: 4.4.0 + resolution: "debug@npm:4.4.0" dependencies: ms: "npm:^2.1.3" peerDependenciesMeta: supports-color: optional: true - checksum: 10/71168908b9a78227ab29d5d25fe03c5867750e31ce24bf2c44a86efc5af041758bb56569b0a3d48a9b5344c00a24a777e6f4100ed6dfd9534a42c1dde285125a + checksum: 10/1847944c2e3c2c732514b93d11886575625686056cd765336212dc15de2d2b29612b6cd80e1afba767bb8e1803b778caf9973e98169ef1a24a7a7009e1820367 languageName: node linkType: hard @@ -14215,6 +14893,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:~4.3.1, debug@npm:~4.3.2, debug@npm:~4.3.4": + version: 4.3.7 + resolution: "debug@npm:4.3.7" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10/71168908b9a78227ab29d5d25fe03c5867750e31ce24bf2c44a86efc5af041758bb56569b0a3d48a9b5344c00a24a777e6f4100ed6dfd9534a42c1dde285125a + languageName: node + linkType: hard + "decamelize-keys@npm:^1.1.0": version: 1.1.1 resolution: "decamelize-keys@npm:1.1.1" @@ -14233,9 +14923,9 @@ __metadata: linkType: hard "decimal.js@npm:10, decimal.js@npm:^10.4.1": - version: 10.4.3 - resolution: "decimal.js@npm:10.4.3" - checksum: 10/de663a7bc4d368e3877db95fcd5c87b965569b58d16cdc4258c063d231ca7118748738df17cd638f7e9dd0be8e34cec08d7234b20f1f2a756a52fc5a38b188d0 + version: 10.5.0 + resolution: "decimal.js@npm:10.5.0" + checksum: 10/714d49cf2f2207b268221795ede330e51452b7c451a0c02a770837d2d4faed47d603a729c2aa1d952eb6c4102d999e91c9b952c1aa016db3c5cba9fc8bf4cda2 languageName: node linkType: hard @@ -14646,15 +15336,15 @@ __metadata: languageName: node linkType: hard -"dompurify@npm:3.2.3": - version: 3.2.3 - resolution: "dompurify@npm:3.2.3" +"dompurify@npm:3.2.4, dompurify@npm:^3.0.0": + version: 3.2.4 + resolution: "dompurify@npm:3.2.4" dependencies: "@types/trusted-types": "npm:^2.0.7" dependenciesMeta: "@types/trusted-types": optional: true - checksum: 10/aad472bcdff40afdbb307fd02abbca86acefee9c39cb35e9634ebbc5e047750a7eeb021b02cd66894d60cf75ad021f69394de2e9e8786b0dd91c5832f497a9af + checksum: 10/98570c53385518a2f9b617f796926338856acfdd3369c88b5905bddf96bd7d391bf8a5433127155e0046e6faa2bfb767185fcd571b865dfabe624c099e2537f5 languageName: node linkType: hard @@ -14790,19 +15480,6 @@ __metadata: languageName: node linkType: hard -"easy-table@npm:1.2.0": - version: 1.2.0 - resolution: "easy-table@npm:1.2.0" - dependencies: - ansi-regex: "npm:^5.0.1" - wcwidth: "npm:^1.0.1" - dependenciesMeta: - wcwidth: - optional: true - checksum: 10/0d1be7cd9419cd1b56ca5a978646b3cff241ccd8cf95bdb2742f36854084b3aef2e9af6ec14142855aa80e4cab1f4baad0f610a99c77509f23676b8330730177 - languageName: node - linkType: hard - "ecc-jsbn@npm:~0.1.1": version: 0.1.2 resolution: "ecc-jsbn@npm:0.1.2" @@ -14975,7 +15652,7 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.17.1, enhanced-resolve@npm:^5.18.0": +"enhanced-resolve@npm:^5.17.1": version: 5.18.0 resolution: "enhanced-resolve@npm:5.18.0" dependencies: @@ -15178,10 +15855,10 @@ __metadata: languageName: node linkType: hard -"es-module-lexer@npm:^1.2.1, es-module-lexer@npm:^1.3.1, es-module-lexer@npm:^1.5.0, es-module-lexer@npm:^1.5.3": - version: 1.5.4 - resolution: "es-module-lexer@npm:1.5.4" - checksum: 10/f29c7c97a58eb17640dcbd71bd6ef754ad4f58f95c3073894573d29dae2cad43ecd2060d97ed5b866dfb7804d5590fb7de1d2c5339a5fceae8bd60b580387fc5 +"es-module-lexer@npm:^1.2.1, es-module-lexer@npm:^1.5.0, es-module-lexer@npm:^1.5.3, es-module-lexer@npm:^1.6.0": + version: 1.6.0 + resolution: "es-module-lexer@npm:1.6.0" + checksum: 10/807ee7020cc46a9c970c78cad1f2f3fc139877e5ebad7f66dbfbb124d451189ba1c48c1c632bd5f8ce1b8af2caef3fca340ba044a410fa890d17b080a59024bb languageName: node linkType: hard @@ -15252,30 +15929,30 @@ __metadata: languageName: node linkType: hard -"esbuild-loader@npm:4.2.2": - version: 4.2.2 - resolution: "esbuild-loader@npm:4.2.2" +"esbuild-loader@npm:4.3.0": + version: 4.3.0 + resolution: "esbuild-loader@npm:4.3.0" dependencies: - esbuild: "npm:^0.21.0" + esbuild: "npm:^0.25.0" get-tsconfig: "npm:^4.7.0" loader-utils: "npm:^2.0.4" webpack-sources: "npm:^1.4.3" peerDependencies: webpack: ^4.40.0 || ^5.0.0 - checksum: 10/235d06c60e26827333c3c66df6e2f13fef31e1b84b8310ba36ebdb6aaa31abdf7628a940326e4923824cdb501c8ce6a6e9129c4e465daaeab112f0b5db3423f4 + checksum: 10/451bf9b344419870b6a10a9d80c1e07ccb4e2871867c8e4dbdf7b491171f0de31571933a8a96627034b360627a034e6f43edd004b1f3e3fe291100ded5985fa6 languageName: node linkType: hard -"esbuild-plugin-browserslist@npm:^0.15.0": - version: 0.15.0 - resolution: "esbuild-plugin-browserslist@npm:0.15.0" +"esbuild-plugin-browserslist@npm:^0.16.0": + version: 0.16.0 + resolution: "esbuild-plugin-browserslist@npm:0.16.0" dependencies: - debug: "npm:^4.3.7" - zod: "npm:^3.23.8" + debug: "npm:^4.4.0" + zod: "npm:^3.24.1" peerDependencies: browserslist: ^4.21.8 - esbuild: ~0.24.0 - checksum: 10/b1afe26f5c013a37664ff6ca2f8190d50e36f719afca8aba2c0b18f0fd0f34de3334db1435154a70dbfeb5e91acf732cc20296bb3fd3cbdfe68ae37dadcffe32 + esbuild: ~0.25.0 + checksum: 10/9f3986901521627270a74500a77a3ddf3599016729de6ebe04e675230188a0e5dd3f5c6a5a1ab781a0b9cb54952df0edb066a3a5737166ef8e8d9db4a2eb44ab languageName: node linkType: hard @@ -15290,7 +15967,93 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:0.24.2, esbuild@npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0, esbuild@npm:^0.24.0": +"esbuild@npm:0.25.0, esbuild@npm:^0.25.0": + version: 0.25.0 + resolution: "esbuild@npm:0.25.0" + dependencies: + "@esbuild/aix-ppc64": "npm:0.25.0" + "@esbuild/android-arm": "npm:0.25.0" + "@esbuild/android-arm64": "npm:0.25.0" + "@esbuild/android-x64": "npm:0.25.0" + "@esbuild/darwin-arm64": "npm:0.25.0" + "@esbuild/darwin-x64": "npm:0.25.0" + "@esbuild/freebsd-arm64": "npm:0.25.0" + "@esbuild/freebsd-x64": "npm:0.25.0" + "@esbuild/linux-arm": "npm:0.25.0" + "@esbuild/linux-arm64": "npm:0.25.0" + "@esbuild/linux-ia32": "npm:0.25.0" + "@esbuild/linux-loong64": "npm:0.25.0" + "@esbuild/linux-mips64el": "npm:0.25.0" + "@esbuild/linux-ppc64": "npm:0.25.0" + "@esbuild/linux-riscv64": "npm:0.25.0" + "@esbuild/linux-s390x": "npm:0.25.0" + "@esbuild/linux-x64": "npm:0.25.0" + "@esbuild/netbsd-arm64": "npm:0.25.0" + "@esbuild/netbsd-x64": "npm:0.25.0" + "@esbuild/openbsd-arm64": "npm:0.25.0" + "@esbuild/openbsd-x64": "npm:0.25.0" + "@esbuild/sunos-x64": "npm:0.25.0" + "@esbuild/win32-arm64": "npm:0.25.0" + "@esbuild/win32-ia32": "npm:0.25.0" + "@esbuild/win32-x64": "npm:0.25.0" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10/451daf6a442df29ec5d528587caa4ce783d41ff4acb93252da5a852b8d36c22e9f84d17f6721d4fbef9a1ba9855bc9fe1f167dd732c11665fe53032f2b89f114 + languageName: node + linkType: hard + +"esbuild@npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0": version: 0.24.2 resolution: "esbuild@npm:0.24.2" dependencies: @@ -15376,86 +16139,6 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.21.0": - version: 0.21.5 - resolution: "esbuild@npm:0.21.5" - dependencies: - "@esbuild/aix-ppc64": "npm:0.21.5" - "@esbuild/android-arm": "npm:0.21.5" - "@esbuild/android-arm64": "npm:0.21.5" - "@esbuild/android-x64": "npm:0.21.5" - "@esbuild/darwin-arm64": "npm:0.21.5" - "@esbuild/darwin-x64": "npm:0.21.5" - "@esbuild/freebsd-arm64": "npm:0.21.5" - "@esbuild/freebsd-x64": "npm:0.21.5" - "@esbuild/linux-arm": "npm:0.21.5" - "@esbuild/linux-arm64": "npm:0.21.5" - "@esbuild/linux-ia32": "npm:0.21.5" - "@esbuild/linux-loong64": "npm:0.21.5" - "@esbuild/linux-mips64el": "npm:0.21.5" - "@esbuild/linux-ppc64": "npm:0.21.5" - "@esbuild/linux-riscv64": "npm:0.21.5" - "@esbuild/linux-s390x": "npm:0.21.5" - "@esbuild/linux-x64": "npm:0.21.5" - "@esbuild/netbsd-x64": "npm:0.21.5" - "@esbuild/openbsd-x64": "npm:0.21.5" - "@esbuild/sunos-x64": "npm:0.21.5" - "@esbuild/win32-arm64": "npm:0.21.5" - "@esbuild/win32-ia32": "npm:0.21.5" - "@esbuild/win32-x64": "npm:0.21.5" - dependenciesMeta: - "@esbuild/aix-ppc64": - optional: true - "@esbuild/android-arm": - optional: true - "@esbuild/android-arm64": - optional: true - "@esbuild/android-x64": - optional: true - "@esbuild/darwin-arm64": - optional: true - "@esbuild/darwin-x64": - optional: true - "@esbuild/freebsd-arm64": - optional: true - "@esbuild/freebsd-x64": - optional: true - "@esbuild/linux-arm": - optional: true - "@esbuild/linux-arm64": - optional: true - "@esbuild/linux-ia32": - optional: true - "@esbuild/linux-loong64": - optional: true - "@esbuild/linux-mips64el": - optional: true - "@esbuild/linux-ppc64": - optional: true - "@esbuild/linux-riscv64": - optional: true - "@esbuild/linux-s390x": - optional: true - "@esbuild/linux-x64": - optional: true - "@esbuild/netbsd-x64": - optional: true - "@esbuild/openbsd-x64": - optional: true - "@esbuild/sunos-x64": - optional: true - "@esbuild/win32-arm64": - optional: true - "@esbuild/win32-ia32": - optional: true - "@esbuild/win32-x64": - optional: true - bin: - esbuild: bin/esbuild - checksum: 10/d2ff2ca84d30cce8e871517374d6c2290835380dc7cd413b2d49189ed170d45e407be14de2cb4794cf76f75cf89955c4714726ebd3de7444b3046f5cab23ab6b - languageName: node - linkType: hard - "escalade@npm:^3.1.1, escalade@npm:^3.2.0": version: 3.2.0 resolution: "escalade@npm:3.2.0" @@ -15606,9 +16289,9 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-jsdoc@npm:50.6.2": - version: 50.6.2 - resolution: "eslint-plugin-jsdoc@npm:50.6.2" +"eslint-plugin-jsdoc@npm:50.6.3": + version: 50.6.3 + resolution: "eslint-plugin-jsdoc@npm:50.6.3" dependencies: "@es-joy/jsdoccomment": "npm:~0.49.0" are-docs-informative: "npm:^0.0.2" @@ -15623,7 +16306,7 @@ __metadata: synckit: "npm:^0.9.1" peerDependencies: eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 - checksum: 10/604e0ccaa4c43a9c95f072dca3833b2e7274ee89d481247d4beccffc7c6bbb4062169271cedc79e839ed3b770699bf20f13c4e92e006ae5d64f8012bfdfa1cb2 + checksum: 10/2c8fa8f493730e326d53bfdaccdb4d8502ee836cd7893f56cf9b12d36831a0cd8c3e9740fe0a7fc67232d1fb6866c17385b2d89b7ce56543056403b982b5f1a6 languageName: node linkType: hard @@ -15797,16 +16480,16 @@ __metadata: languageName: node linkType: hard -"eslint@npm:9.18.0": - version: 9.18.0 - resolution: "eslint@npm:9.18.0" +"eslint@npm:9.19.0": + version: 9.19.0 + resolution: "eslint@npm:9.19.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.12.1" "@eslint/config-array": "npm:^0.19.0" "@eslint/core": "npm:^0.10.0" "@eslint/eslintrc": "npm:^3.2.0" - "@eslint/js": "npm:9.18.0" + "@eslint/js": "npm:9.19.0" "@eslint/plugin-kit": "npm:^0.2.5" "@humanfs/node": "npm:^0.16.6" "@humanwhocodes/module-importer": "npm:^1.0.1" @@ -15842,7 +16525,7 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10/85f22991aab4b0809fdfc557ec2bd309062e7211b631674e71827a73c45e44febaa80dedda35150154e331a2d372c3a25e8e5dd4a99dc8a982fe8f7d645d859f + checksum: 10/850d19fd6a34702d1e3d9bdad6aef84a20a5c2de006a8fa6380843384b13944b180232ddd74b8725ffcdf8f296399037f0e8eb4783d5f7393f13c059112b843d languageName: node linkType: hard @@ -16024,6 +16707,20 @@ __metadata: languageName: node linkType: hard +"exenv@npm:^1.2.2": + version: 1.2.2 + resolution: "exenv@npm:1.2.2" + checksum: 10/6840185e421394bcb143debb866d31d19c3e4a4bca87d2f319d68d61afff353b3c678f2eb389e3b98ab9aecbec19f6bebbdc4193984378af0a3366c498a7efc8 + languageName: node + linkType: hard + +"exif-parser@npm:^0.1.12": + version: 0.1.12 + resolution: "exif-parser@npm:0.1.12" + checksum: 10/72bffba154fd33b270908ea1f9f63a6c5dffadf4eb427c85ab82d6006204ed762dfeb76969e1577614b8d18dadd411b11583569e54ed2beea0af8b61c8f4de29 + languageName: node + linkType: hard + "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -16259,12 +16956,12 @@ __metadata: languageName: node linkType: hard -"fastq@npm:^1.13.0, fastq@npm:^1.15.0, fastq@npm:^1.6.0": - version: 1.18.0 - resolution: "fastq@npm:1.18.0" +"fastq@npm:^1.13.0, fastq@npm:^1.6.0": + version: 1.17.1 + resolution: "fastq@npm:1.17.1" dependencies: reusify: "npm:^1.0.4" - checksum: 10/c5b501333dc8f5d188d828ea162aad03ff5a81aed185b6d4a5078aaeae0a42babc34296d7af13ebce86401cccd93c9b7b3cbf61280821c5f20af233378b42fbb + checksum: 10/a443180068b527dd7b3a63dc7f2a47ceca2f3e97b9c00a1efe5538757e6cc4056a3526df94308075d7727561baf09ebaa5b67da8dcbddb913a021c5ae69d1f69 languageName: node linkType: hard @@ -16329,6 +17026,15 @@ __metadata: languageName: node linkType: hard +"file-entry-cache@npm:^10.0.5": + version: 10.0.5 + resolution: "file-entry-cache@npm:10.0.5" + dependencies: + flat-cache: "npm:^6.1.5" + checksum: 10/57439b39635e75aa900ccdaad9167b85a500a559dd8f0902509189849784e9255d43cd3d8307f918dbd19f48b02562b5e354bcf7bfa23afe3985485504eb7a5e + languageName: node + linkType: hard + "file-entry-cache@npm:^8.0.0": version: 8.0.0 resolution: "file-entry-cache@npm:8.0.0" @@ -16338,15 +17044,6 @@ __metadata: languageName: node linkType: hard -"file-entry-cache@npm:^9.1.0": - version: 9.1.0 - resolution: "file-entry-cache@npm:9.1.0" - dependencies: - flat-cache: "npm:^5.0.0" - checksum: 10/fd67a9552f272ac4a1731c545e1350bd135e208659144cc5311baac6b8bbf55da7c8c3a0bf25c71ed78eff2bdd26d2a3a8f9ba3d8bec968fe8d1eeba6ab14a96 - languageName: node - linkType: hard - "file-saver@npm:2.0.5": version: 2.0.5 resolution: "file-saver@npm:2.0.5" @@ -16354,6 +17051,15 @@ __metadata: languageName: node linkType: hard +"file-selector@npm:^0.6.0": + version: 0.6.0 + resolution: "file-selector@npm:0.6.0" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/6add4098ae07fd1e9050b1e8d3fd9f128680c1d6648c0676af54ace4586e6e5bfcb8fdfa45b69e9131ffd8175bf630d54a445a5facf9be244f85b99ce309183e + languageName: node + linkType: hard + "file-selector@npm:^2.1.0": version: 2.1.0 resolution: "file-selector@npm:2.1.0" @@ -16363,6 +17069,17 @@ __metadata: languageName: node linkType: hard +"file-type@npm:^16.0.0": + version: 16.5.4 + resolution: "file-type@npm:16.5.4" + dependencies: + readable-web-to-node-stream: "npm:^3.0.0" + strtok3: "npm:^6.2.4" + token-types: "npm:^4.1.1" + checksum: 10/46ced46bb925ab547e0a6d43108a26d043619d234cb0588d7abce7b578dafac142bcfd2e23a6adb0a4faa4b951bd1b14b355134a193362e07cd352f9bf0dc349 + languageName: node + linkType: hard + "filelist@npm:^1.0.1": version: 1.0.4 resolution: "filelist@npm:1.0.4" @@ -16486,11 +17203,11 @@ __metadata: linkType: hard "fishery@npm:^2.2.2": - version: 2.2.2 - resolution: "fishery@npm:2.2.2" + version: 2.2.3 + resolution: "fishery@npm:2.2.3" dependencies: lodash.mergewith: "npm:^4.6.2" - checksum: 10/68120995b0cd7827f0d50ef3b552a74289c99fa6195cafb6e5d376aa14421f12873274e1fc5aa101073fdae35ad6543cb5b04687bb295d8c37ff3b99dbdeb9ae + checksum: 10/a8855c1949524996d2ba3b84cf6fe13f2a8ade8296e090bedbc58d2879dc481d85a0aced1e33f80b1a1ec9cb1a2eb7562494c2ae514915730d14285694f595bf languageName: node linkType: hard @@ -16515,13 +17232,14 @@ __metadata: languageName: node linkType: hard -"flat-cache@npm:^5.0.0": - version: 5.0.0 - resolution: "flat-cache@npm:5.0.0" +"flat-cache@npm:^6.1.5": + version: 6.1.5 + resolution: "flat-cache@npm:6.1.5" dependencies: - flatted: "npm:^3.3.1" - keyv: "npm:^4.5.4" - checksum: 10/42570762052b17a1dec221d73a1e417d0ba07137de6debaabb51389cac265a12a027a895dc84e1725bc5cdde04fe8b706ad836860b05488e9a04bda9301d2529 + cacheable: "npm:^1.8.7" + flatted: "npm:^3.3.2" + hookified: "npm:^1.6.0" + checksum: 10/c98d7635316ceee08b74c1dd3f05eda6a5f2f073688af927de68193ca992ab09eb9f0ee7d5fac40f53280844f11cf368b90dc43f73fee358165dc84e13828674 languageName: node linkType: hard @@ -16534,10 +17252,10 @@ __metadata: languageName: node linkType: hard -"flatted@npm:^3.2.9, flatted@npm:^3.3.1": - version: 3.3.1 - resolution: "flatted@npm:3.3.1" - checksum: 10/7b8376061d5be6e0d3658bbab8bde587647f68797cf6bfeae9dea0e5137d9f27547ab92aaff3512dd9d1299086a6d61be98e9d48a56d17531b634f77faadbc49 +"flatted@npm:^3.2.9, flatted@npm:^3.3.2": + version: 3.3.2 + resolution: "flatted@npm:3.3.2" + checksum: 10/ac3c159742e01d0e860a861164bcfd35bb567ccbebb8a0dd041e61cf3c64a435b917dd1e7ed1c380c2ebca85735fb16644485ec33665bc6aafc3b316aa1eed44 languageName: node linkType: hard @@ -16624,13 +17342,13 @@ __metadata: linkType: hard "form-data@npm:^4.0.0": - version: 4.0.0 - resolution: "form-data@npm:4.0.0" + version: 4.0.1 + resolution: "form-data@npm:4.0.1" dependencies: asynckit: "npm:^0.4.0" combined-stream: "npm:^1.0.8" mime-types: "npm:^2.1.12" - checksum: 10/7264aa760a8cf09482816d8300f1b6e2423de1b02bba612a136857413fdc96d7178298ced106817655facc6b89036c6e12ae31c9eb5bdc16aabf502ae8a5d805 + checksum: 10/6adb1cff557328bc6eb8a68da205f9ae44ab0e88d4d9237aaf91eed591ffc64f77411efb9016af7d87f23d0a038c45a788aa1c6634e51175c4efa36c2bc53774 languageName: node linkType: hard @@ -17015,12 +17733,12 @@ __metadata: languageName: node linkType: hard -"get-tsconfig@npm:^4.7.0, get-tsconfig@npm:^4.7.2": - version: 4.8.1 - resolution: "get-tsconfig@npm:4.8.1" +"get-tsconfig@npm:^4.10.0, get-tsconfig@npm:^4.7.0": + version: 4.10.0 + resolution: "get-tsconfig@npm:4.10.0" dependencies: resolve-pkg-maps: "npm:^1.0.0" - checksum: 10/3fb5a8ad57b9633eaea085d81661e9e5c9f78b35d8f8689eaf8b8b45a2a3ebf3b3422266d4d7df765e308cc1e6231648d114803ab3d018332e29916f2c1de036 + checksum: 10/5259b5c99a1957114337d9d0603b4a305ec9e29fa6cac7d2fbf634ba6754a0cc88bfd281a02416ce64e604b637d3cb239185381a79a5842b17fb55c097b38c4b languageName: node linkType: hard @@ -17061,6 +17779,16 @@ __metadata: languageName: node linkType: hard +"gifwrap@npm:^0.10.1": + version: 0.10.1 + resolution: "gifwrap@npm:0.10.1" + dependencies: + image-q: "npm:^4.0.0" + omggif: "npm:^1.0.10" + checksum: 10/5d7213d9b09b4d8c32998a060ef002d2180223baec96d012c938959564b78ae86dcb24d50b60afb1e9c4e8ac8f598e1577a8d3578477d8388d72288e2c8b05cf + languageName: node + linkType: hard + "git-raw-commits@npm:^3.0.0": version: 3.0.0 resolution: "git-raw-commits@npm:3.0.0" @@ -17364,9 +18092,9 @@ __metadata: version: 0.0.0-use.local resolution: "grafana@workspace:." dependencies: - "@babel/core": "npm:7.26.0" - "@babel/preset-env": "npm:7.26.0" - "@babel/runtime": "npm:7.26.0" + "@babel/core": "npm:7.26.7" + "@babel/preset-env": "npm:7.26.7" + "@babel/runtime": "npm:7.26.7" "@betterer/betterer": "npm:5.4.0" "@betterer/cli": "npm:5.4.0" "@bsull/augurs": "npm:^0.9.0" @@ -17384,21 +18112,22 @@ __metadata: "@grafana/e2e-selectors": "workspace:*" "@grafana/eslint-config": "npm:8.0.0" "@grafana/eslint-plugin": "link:./packages/grafana-eslint-rules" - "@grafana/faro-core": "npm:^1.3.6" - "@grafana/faro-web-sdk": "npm:^1.3.6" - "@grafana/faro-web-tracing": "npm:^1.8.2" + "@grafana/faro-core": "npm:^1.13.2" + "@grafana/faro-web-sdk": "npm:^1.13.2" + "@grafana/faro-web-tracing": "npm:^1.13.2" "@grafana/flamegraph": "workspace:*" "@grafana/google-sdk": "npm:0.1.2" "@grafana/lezer-logql": "npm:0.2.7" + "@grafana/llm": "npm:0.12.0" "@grafana/monaco-logql": "npm:^0.0.8" "@grafana/o11y-ds-frontend": "workspace:*" - "@grafana/plugin-e2e": "npm:^1.11.0" - "@grafana/plugin-ui": "npm:^0.9.6" + "@grafana/plugin-e2e": "npm:1.17.1" + "@grafana/plugin-ui": "npm:0.10.1" "@grafana/prometheus": "workspace:*" "@grafana/runtime": "workspace:*" "@grafana/saga-icons": "workspace:*" - "@grafana/scenes": "npm:5.37.1" - "@grafana/scenes-react": "npm:5.37.1" + "@grafana/scenes": "npm:6.0.2" + "@grafana/scenes-react": "npm:6.0.2" "@grafana/schema": "workspace:*" "@grafana/sql": "workspace:*" "@grafana/tsconfig": "npm:^2.0.0" @@ -17418,7 +18147,7 @@ __metadata: "@opentelemetry/api": "npm:1.9.0" "@opentelemetry/exporter-collector": "npm:0.25.0" "@opentelemetry/semantic-conventions": "npm:1.28.0" - "@playwright/test": "npm:1.49.1" + "@playwright/test": "npm:1.50.1" "@pmmmwh/react-refresh-webpack-plugin": "npm:0.5.15" "@popperjs/core": "npm:2.11.8" "@react-aria/dialog": "npm:3.5.21" @@ -17430,17 +18159,17 @@ __metadata: "@react-types/menu": "npm:3.9.14" "@react-types/overlays": "npm:3.8.12" "@react-types/shared": "npm:3.27.0" - "@reduxjs/toolkit": "npm:2.5.0" + "@reduxjs/toolkit": "npm:2.5.1" "@rsdoctor/webpack-plugin": "npm:^0.4.6" "@rtk-query/codegen-openapi": "npm:^2.0.0" "@rtsao/plugin-proposal-class-properties": "npm:7.0.1-patch.1" - "@stylistic/eslint-plugin-ts": "npm:^2.9.0" - "@swc/core": "npm:1.10.9" + "@stylistic/eslint-plugin-ts": "npm:^3.0.0" + "@swc/core": "npm:1.10.12" "@swc/helpers": "npm:0.5.15" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.3" - "@testing-library/react": "npm:16.1.0" - "@testing-library/user-event": "npm:14.5.2" + "@testing-library/react": "npm:16.2.0" + "@testing-library/user-event": "npm:14.6.1" "@types/angular": "npm:1.8.9" "@types/angular-route": "npm:1.7.6" "@types/babel__core": "npm:^7" @@ -17464,10 +18193,10 @@ __metadata: "@types/jquery": "npm:3.5.32" "@types/js-yaml": "npm:^4.0.5" "@types/jsurl": "npm:^1.2.28" - "@types/lodash": "npm:4.17.14" + "@types/lodash": "npm:4.17.15" "@types/logfmt": "npm:^1.2.3" "@types/lucene": "npm:^2" - "@types/node": "npm:22.10.7" + "@types/node": "npm:22.12.0" "@types/node-forge": "npm:^1" "@types/ol-ext": "npm:@siedlerchr/types-ol-ext@3.2.4" "@types/pluralize": "npm:^0.0.33" @@ -17489,15 +18218,15 @@ __metadata: "@types/slate": "npm:0.47.11" "@types/slate-plain-serializer": "npm:0.7.5" "@types/slate-react": "npm:0.22.9" - "@types/swagger-ui-react": "npm:4.18.3" + "@types/swagger-ui-react": "npm:5.18.0" "@types/systemjs": "npm:6.15.1" "@types/tinycolor2": "npm:1.4.6" "@types/uuid": "npm:10.0.0" "@types/webpack-assets-manifest": "npm:^5" "@types/webpack-env": "npm:^1.18.4" "@types/yargs": "npm:17.0.33" - "@typescript-eslint/eslint-plugin": "npm:8.18.1" - "@typescript-eslint/parser": "npm:8.18.1" + "@typescript-eslint/eslint-plugin": "npm:8.22.0" + "@typescript-eslint/parser": "npm:8.22.0" "@visx/event": "npm:3.12.0" "@visx/gradient": "npm:3.12.0" "@visx/group": "npm:3.12.0" @@ -17515,7 +18244,7 @@ __metadata: blob-polyfill: "npm:9.0.20240710" brace: "npm:0.11.1" browserslist: "npm:^4.21.4" - centrifuge: "npm:5.3.0" + centrifuge: "npm:5.3.2" chance: "npm:^1.0.10" chrome-remote-interface: "npm:0.33.2" classnames: "npm:2.5.1" @@ -17526,6 +18255,7 @@ __metadata: copy-webpack-plugin: "npm:12.0.2" core-js: "npm:3.40.0" crashme: "npm:0.0.15" + croner: "npm:^9.0.0" css-loader: "npm:7.1.2" css-minimizer-webpack-plugin: "npm:7.0.0" cypress: "npm:13.10.0" @@ -17538,15 +18268,15 @@ __metadata: date-fns: "npm:4.1.0" debounce-promise: "npm:3.1.2" diff: "npm:^7.0.0" - esbuild: "npm:0.24.2" - esbuild-loader: "npm:4.2.2" - esbuild-plugin-browserslist: "npm:^0.15.0" - eslint: "npm:9.18.0" + esbuild: "npm:0.25.0" + esbuild-loader: "npm:4.3.0" + esbuild-plugin-browserslist: "npm:^0.16.0" + eslint: "npm:9.19.0" eslint-config-prettier: "npm:9.1.0" eslint-plugin-import: "npm:^2.31.0" eslint-plugin-jest: "npm:28.11.0" eslint-plugin-jest-dom: "npm:^5.4.0" - eslint-plugin-jsdoc: "npm:50.6.2" + eslint-plugin-jsdoc: "npm:50.6.3" eslint-plugin-jsx-a11y: "npm:6.10.2" eslint-plugin-lodash: "npm:8.0.0" eslint-plugin-no-barrel-files: "npm:^1.1.1" @@ -17569,7 +18299,7 @@ __metadata: http-server: "npm:14.1.1" i18next: "npm:^24.0.0" i18next-browser-languagedetector: "npm:^8.0.0" - i18next-parser: "npm:9.1.0" + i18next-parser: "npm:9.3.0" immer: "npm:10.1.1" immutable: "npm:5.0.3" ini: "npm:^5.0.0" @@ -17582,6 +18312,7 @@ __metadata: jest-junit: "npm:16.0.0" jest-matcher-utils: "npm:29.7.0" jest-watch-typeahead: "npm:^2.2.2" + jimp: "npm:^1.6.0" jquery: "npm:3.7.1" js-yaml: "npm:^4.1.0" jsdom-testing-mocks: "npm:^1.13.1" @@ -17589,7 +18320,6 @@ __metadata: json-source-map: "npm:0.6.1" jsurl: "npm:^0.1.5" kbar: "npm:0.1.0-beta.45" - knip: "npm:^5.10.0" lerna: "npm:8.1.8" leven: "npm:^4.0.0" lodash: "npm:4.17.21" @@ -17604,7 +18334,7 @@ __metadata: ml-regression-polynomial: "npm:^3.0.0" ml-regression-simple-linear: "npm:^3.0.0" moment: "npm:2.30.1" - moment-timezone: "npm:0.5.46" + moment-timezone: "npm:0.5.47" monaco-editor: "npm:0.34.1" moveable: "npm:0.53.0" msw: "npm:2.7.0" @@ -17615,7 +18345,9 @@ __metadata: node-notifier: "npm:10.0.1" nx: "npm:19.8.2" ol: "npm:7.4.0" - ol-ext: "npm:4.0.24" + ol-ext: "npm:4.0.26" + openapi-types: "npm:^12.1.3" + pdf-parse: "npm:^1.1.1" pluralize: "npm:^8.0.0" postcss: "npm:8.5.1" postcss-loader: "npm:8.1.1" @@ -17633,20 +18365,20 @@ __metadata: react-draggable: "npm:4.4.6" react-dropzone: "npm:^14.2.3" react-grid-layout: "patch:react-grid-layout@npm%3A1.4.4#~/.yarn/patches/react-grid-layout-npm-1.4.4-4024c5395b.patch" - react-highlight-words: "npm:0.20.0" + react-highlight-words: "npm:0.21.0" react-hook-form: "npm:^7.49.2" react-i18next: "npm:^15.0.0" - react-inlinesvg: "npm:4.1.5" + react-inlinesvg: "npm:4.2.0" react-loading-skeleton: "npm:3.5.0" react-moveable: "npm:0.56.0" react-redux: "npm:9.2.0" react-refresh: "npm:0.14.0" react-resizable: "npm:3.0.5" react-responsive-carousel: "npm:^3.2.23" - react-router: "npm:5.3.3" - react-router-dom: "npm:5.3.3" + react-router: "npm:5.3.4" + react-router-dom: "npm:5.3.4" react-router-dom-v5-compat: "npm:^6.26.1" - react-select: "npm:5.9.0" + react-select: "npm:5.10.0" react-select-event: "npm:5.5.1" react-split-pane: "npm:0.1.92" react-table: "npm:7.8.0" @@ -17655,7 +18387,7 @@ __metadata: react-virtual: "npm:2.10.4" react-virtualized-auto-sizer: "npm:1.0.25" react-window: "npm:1.8.11" - react-window-infinite-loader: "npm:1.0.9" + react-window-infinite-loader: "npm:1.0.10" react-zoom-pan-pinch: "npm:^3.3.0" reduce-reducers: "npm:^1.0.4" redux: "npm:5.0.1" @@ -17664,20 +18396,20 @@ __metadata: regenerator-runtime: "npm:0.14.1" reselect: "npm:5.1.1" rimraf: "npm:6.0.1" - rudder-sdk-js: "npm:2.48.43" + rudder-sdk-js: "npm:2.48.44" rxjs: "npm:7.8.1" sass: "npm:1.83.4" sass-loader: "npm:16.0.4" selecto: "npm:1.26.3" - semver: "npm:7.6.3" + semver: "npm:7.7.0" slate: "npm:0.47.9" slate-plain-serializer: "npm:0.7.13" slate-react: "npm:0.22.10" smtp-tester: "npm:^2.1.0" style-loader: "npm:4.0.0" - stylelint: "npm:16.12.0" + stylelint: "npm:16.14.1" stylelint-config-sass-guidelines: "npm:12.1.0" - swagger-ui-react: "npm:5.18.2" + swagger-ui-react: "npm:5.18.3" symbol-observable: "npm:4.0.0" systemjs: "npm:6.15.1" systemjs-cjs-extra: "npm:0.2.1" @@ -17704,7 +18436,6 @@ __metadata: webpack-merge: "npm:6.0.1" webpackbar: "npm:^7.0.0" whatwg-fetch: "npm:3.6.20" - xlsx: "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" yaml: "npm:^2.0.0" yargs: "npm:^17.5.1" dependenciesMeta: @@ -17977,6 +18708,13 @@ __metadata: languageName: node linkType: hard +"hookified@npm:^1.6.0": + version: 1.7.0 + resolution: "hookified@npm:1.7.0" + checksum: 10/87fb8f2ae170f28b1e0b903f2b0b40fb2a92a0364baab7c54121db1c871a2e81c589d778b48e1a35462181b8840764caf6cac3744330e5687a05a4b4d2ad729d + languageName: node + linkType: hard + "hosted-git-info@npm:^2.1.4": version: 2.8.9 resolution: "hosted-git-info@npm:2.8.9" @@ -18391,6 +19129,15 @@ __metadata: languageName: node linkType: hard +"i18next-browser-languagedetector@npm:^7.0.2": + version: 7.2.2 + resolution: "i18next-browser-languagedetector@npm:7.2.2" + dependencies: + "@babel/runtime": "npm:^7.23.2" + checksum: 10/6f6dd5db3e83c2ed3b24d7fb754d0c41fd2056a0f06fb3d2a4604541c6ee0031d3f2cbbcfad726bbfc9741bcbbd6cde3acf94d339d600b6d9f49f0bbe51179d9 + languageName: node + linkType: hard + "i18next-browser-languagedetector@npm:^8.0.0": version: 8.0.2 resolution: "i18next-browser-languagedetector@npm:8.0.2" @@ -18400,9 +19147,9 @@ __metadata: languageName: node linkType: hard -"i18next-parser@npm:9.1.0": - version: 9.1.0 - resolution: "i18next-parser@npm:9.1.0" +"i18next-parser@npm:9.3.0": + version: 9.3.0 + resolution: "i18next-parser@npm:9.3.0" dependencies: "@babel/runtime": "npm:^7.25.0" broccoli-plugin: "npm:^4.0.7" @@ -18410,12 +19157,12 @@ __metadata: colors: "npm:^1.4.0" commander: "npm:^12.1.0" eol: "npm:^0.9.1" - esbuild: "npm:^0.24.0" + esbuild: "npm:^0.25.0" fs-extra: "npm:^11.2.0" gulp-sort: "npm:^2.0.0" i18next: "npm:^23.5.1 || ^24.2.0" js-yaml: "npm:^4.1.0" - lilconfig: "npm:^3.1.2" + lilconfig: "npm:^3.1.3" rsvp: "npm:^4.8.5" sort-keys: "npm:^5.0.0" typescript: "npm:^5.0.4" @@ -18423,11 +19170,11 @@ __metadata: vinyl-fs: "npm:^4.0.0" bin: i18next: bin/cli.js - checksum: 10/7582daba05ae2ef0878cb63e805cb6302bbcf03b17dd2d150594fd5aa220e1431e558611616b8b2f7d8c6c318d085822cd5db108b6b837dcfa0a1d7ff28d3bd7 + checksum: 10/32b81864aa9cc27b7db41fde6fdf2c686c985e07a9b62a8dd10b4e86c8c0db7fe6ef3e569461a593521932265b31d12c091a6dda461406e54ebb0fc9ff6a55d0 languageName: node linkType: hard -"i18next@npm:^23.11.5": +"i18next@npm:^23.0.0, i18next@npm:^23.11.5": version: 23.16.8 resolution: "i18next@npm:23.16.8" dependencies: @@ -18437,8 +19184,8 @@ __metadata: linkType: hard "i18next@npm:^23.5.1 || ^24.2.0, i18next@npm:^24.0.0": - version: 24.2.1 - resolution: "i18next@npm:24.2.1" + version: 24.2.2 + resolution: "i18next@npm:24.2.2" dependencies: "@babel/runtime": "npm:^7.23.2" peerDependencies: @@ -18446,7 +19193,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/74836c3ca3365155906f95162bf75461f8f82a86034b03fc8efc670e10f610299dc5b51923acc1ab9d52ec7e7e717e44fb95b91ff1560ff45c6bad0c383517af + checksum: 10/f66ed9e56d9412e59502f5df39163631daf9f1264774732fb21edbd66a528ca7a6b67dc2e2aec95683c6c7956e42c651587a54bd8ee082bd12008880ce6cd326 languageName: node linkType: hard @@ -18500,17 +19247,26 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.0.4, ignore@npm:^5.1.1, ignore@npm:^5.1.8, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.1": +"ignore@npm:^5.0.4, ignore@npm:^5.1.1, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.1": version: 5.3.2 resolution: "ignore@npm:5.3.2" checksum: 10/cceb6a457000f8f6a50e1196429750d782afce5680dd878aa4221bd79972d68b3a55b4b1458fc682be978f4d3c6a249046aa0880637367216444ab7b014cfc98 languageName: node linkType: hard -"ignore@npm:^6.0.2": - version: 6.0.2 - resolution: "ignore@npm:6.0.2" - checksum: 10/af39e49996cd989763920e445eff897d0ae1e36b5f27b0e09e14a4fd2df89b362f92e720ecf06ef729056842366527db8561d310e904718810b92ffbcd23056d +"ignore@npm:^7.0.3": + version: 7.0.3 + resolution: "ignore@npm:7.0.3" + checksum: 10/ce5e812af3acd6607a3fe0a9f9b5f01d53f009a5ace8cbf5b6491d05a481b55d65186e6a7eaa13126e93f15276bcf3d1e8d6ff3ce5549c312f9bb313fff33365 + languageName: node + linkType: hard + +"image-q@npm:^4.0.0": + version: 4.0.0 + resolution: "image-q@npm:4.0.0" + dependencies: + "@types/node": "npm:16.9.1" + checksum: 10/1f7c6c84d0b4d83d50603a38443a51bd45d0aee38ff4284135b30744df9481fa18ae818f906a648d0a6a9a78c884147b7632424199d107998427bb98e96f2584 languageName: node linkType: hard @@ -18521,6 +19277,13 @@ __metadata: languageName: node linkType: hard +"immutable@npm:4.3.7, immutable@npm:^4.3.6": + version: 4.3.7 + resolution: "immutable@npm:4.3.7" + checksum: 10/37d963c5050f03ae5f3714ba7a43d469aa482051087f4c65d673d1501c309ea231d87480c792e19fa85e2eaf965f76af5d0aa92726505f3cfe4af91619dfb80b + languageName: node + linkType: hard + "immutable@npm:5.0.3, immutable@npm:^5.0.2": version: 5.0.3 resolution: "immutable@npm:5.0.3" @@ -18535,13 +19298,6 @@ __metadata: languageName: node linkType: hard -"immutable@npm:^4.3.6": - version: 4.3.7 - resolution: "immutable@npm:4.3.7" - checksum: 10/37d963c5050f03ae5f3714ba7a43d469aa482051087f4c65d673d1501c309ea231d87480c792e19fa85e2eaf965f76af5d0aa92726505f3cfe4af91619dfb80b - languageName: node - linkType: hard - "import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" @@ -20090,6 +20846,41 @@ __metadata: languageName: node linkType: hard +"jimp@npm:^1.6.0": + version: 1.6.0 + resolution: "jimp@npm:1.6.0" + dependencies: + "@jimp/core": "npm:1.6.0" + "@jimp/diff": "npm:1.6.0" + "@jimp/js-bmp": "npm:1.6.0" + "@jimp/js-gif": "npm:1.6.0" + "@jimp/js-jpeg": "npm:1.6.0" + "@jimp/js-png": "npm:1.6.0" + "@jimp/js-tiff": "npm:1.6.0" + "@jimp/plugin-blit": "npm:1.6.0" + "@jimp/plugin-blur": "npm:1.6.0" + "@jimp/plugin-circle": "npm:1.6.0" + "@jimp/plugin-color": "npm:1.6.0" + "@jimp/plugin-contain": "npm:1.6.0" + "@jimp/plugin-cover": "npm:1.6.0" + "@jimp/plugin-crop": "npm:1.6.0" + "@jimp/plugin-displace": "npm:1.6.0" + "@jimp/plugin-dither": "npm:1.6.0" + "@jimp/plugin-fisheye": "npm:1.6.0" + "@jimp/plugin-flip": "npm:1.6.0" + "@jimp/plugin-hash": "npm:1.6.0" + "@jimp/plugin-mask": "npm:1.6.0" + "@jimp/plugin-print": "npm:1.6.0" + "@jimp/plugin-quantize": "npm:1.6.0" + "@jimp/plugin-resize": "npm:1.6.0" + "@jimp/plugin-rotate": "npm:1.6.0" + "@jimp/plugin-threshold": "npm:1.6.0" + "@jimp/types": "npm:1.6.0" + "@jimp/utils": "npm:1.6.0" + checksum: 10/dac22396957d7d12cb8823a346dc1f43e03f2b1698b57feeb75f61f7dc5a7f5ff4db3490e4152fe297f164ebeb6e454c5440fe5d0acfadc06393c820f951673b + languageName: node + linkType: hard + "jiti@npm:^1.20.0": version: 1.21.6 resolution: "jiti@npm:1.21.6" @@ -20099,15 +20890,6 @@ __metadata: languageName: node linkType: hard -"jiti@npm:^2.4.2": - version: 2.4.2 - resolution: "jiti@npm:2.4.2" - bin: - jiti: lib/jiti-cli.mjs - checksum: 10/e2b07eb2e3fbb245e29ad288dddecab31804967fc84d5e01d39858997d2743b5e248946defcecf99272275a00284ecaf7ec88b8c841331324f0c946d8274414b - languageName: node - linkType: hard - "jju@npm:^1.4.0": version: 1.4.0 resolution: "jju@npm:1.4.0" @@ -20115,6 +20897,13 @@ __metadata: languageName: node linkType: hard +"jpeg-js@npm:^0.4.4": + version: 0.4.4 + resolution: "jpeg-js@npm:0.4.4" + checksum: 10/30bb6e16e79db4bd6edbecf1140039bbab326de876f2d20685f04a38d03f70ef9fada1886f0bf58832a4dba4aa179311fc4be7df3d756cab8f1c74d745542f38 + languageName: node + linkType: hard + "jquery@npm:3.7.1": version: 3.7.1 resolution: "jquery@npm:3.7.1" @@ -20545,6 +21334,15 @@ __metadata: languageName: node linkType: hard +"keyv@npm:^5.2.3": + version: 5.2.3 + resolution: "keyv@npm:5.2.3" + dependencies: + "@keyv/serialize": "npm:^1.0.2" + checksum: 10/47b4e9deb33e6a80e5ea79f3022ed3a14bc9fe553b7527ffff0a70b10c7a6c1a5d7e49b9bcfdbd8e8b9fb4632d68baa19d09e82628bcf853103e750e56d49a9e + languageName: node + linkType: hard + "kind-of@npm:^6.0.2, kind-of@npm:^6.0.3": version: 6.0.3 resolution: "kind-of@npm:6.0.3" @@ -20559,36 +21357,6 @@ __metadata: languageName: node linkType: hard -"knip@npm:^5.10.0": - version: 5.42.2 - resolution: "knip@npm:5.42.2" - dependencies: - "@nodelib/fs.walk": "npm:3.0.1" - "@snyk/github-codeowners": "npm:1.1.0" - easy-table: "npm:1.2.0" - enhanced-resolve: "npm:^5.18.0" - fast-glob: "npm:^3.3.3" - jiti: "npm:^2.4.2" - js-yaml: "npm:^4.1.0" - minimist: "npm:^1.2.8" - picocolors: "npm:^1.1.0" - picomatch: "npm:^4.0.1" - pretty-ms: "npm:^9.0.0" - smol-toml: "npm:^1.3.1" - strip-json-comments: "npm:5.0.1" - summary: "npm:2.1.0" - zod: "npm:^3.22.4" - zod-validation-error: "npm:^3.0.3" - peerDependencies: - "@types/node": ">=18" - typescript: ">=5.0.4" - bin: - knip: bin/knip.js - knip-bun: bin/knip-bun.js - checksum: 10/1e540ad66e8e5cd2dfceb0c333ca46446300f4e40a51599ca6cd8de705bf0f928332ca108de28306ae7b54cc8fc1f66667135193baf32d4ccd30560797825935 - languageName: node - linkType: hard - "known-css-properties@npm:^0.29.0": version: 0.29.0 resolution: "known-css-properties@npm:0.29.0" @@ -20850,7 +21618,7 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:^3.1.2": +"lilconfig@npm:^3.1.2, lilconfig@npm:^3.1.3": version: 3.1.3 resolution: "lilconfig@npm:3.1.3" checksum: 10/b932ce1af94985f0efbe8896e57b1f814a48c8dbd7fc0ef8469785c6303ed29d0090af3ccad7e36b626bfca3a4dc56cc262697e9a8dd867623cf09a39d54e4c3 @@ -21425,6 +22193,24 @@ __metadata: languageName: node linkType: hard +"marked-mangle@npm:1.1.9": + version: 1.1.9 + resolution: "marked-mangle@npm:1.1.9" + peerDependencies: + marked: ">=4 <15" + checksum: 10/745e44bea9b52bc9c52e41f5d2b146eb21072a92ddf85b4b2f210b091da93fcbf2f5447b02f849fd19e79224bdca385462a42b96882493778d1d0ff0a3da9a8c + languageName: node + linkType: hard + +"marked@npm:12.0.2": + version: 12.0.2 + resolution: "marked@npm:12.0.2" + bin: + marked: bin/marked.js + checksum: 10/24d4fc58d37c1779197fa7f93c504d8c71d4df54eb69cbbc14a55ba2a8e2ad83d723801fc25452c21ce74b38a483c5863c53449f130253a597be9e9c1d3e7e2b + languageName: node + linkType: hard + "marked@npm:15.0.6": version: 15.0.6 resolution: "marked@npm:15.0.6" @@ -21637,6 +22423,15 @@ __metadata: languageName: node linkType: hard +"mime@npm:3": + version: 3.0.0 + resolution: "mime@npm:3.0.0" + bin: + mime: cli.js + checksum: 10/b2d31580deb58be89adaa1877cbbf152b7604b980fd7ef8f08b9e96bfedf7d605d9c23a8ba62aa12c8580b910cd7c1d27b7331d0f40f7a14e17d5a0bbec3b49f + languageName: node + linkType: hard + "mimic-fn@npm:^2.1.0": version: 2.1.0 resolution: "mimic-fn@npm:2.1.0" @@ -21651,19 +22446,6 @@ __metadata: languageName: node linkType: hard -"mini-create-react-context@npm:^0.4.0": - version: 0.4.1 - resolution: "mini-create-react-context@npm:0.4.1" - dependencies: - "@babel/runtime": "npm:^7.12.1" - tiny-warning: "npm:^1.0.3" - peerDependencies: - prop-types: ^15.0.0 - react: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10/c816c785b7dccd67fdfa6a5edc673363b11845b6abca8a9d9f3ffa74520266d979b56f5db0dfc62ed912a90553c15be28c816311fc9c7856ab66a81d461d50e6 - languageName: node - linkType: hard - "mini-css-extract-plugin@npm:2.9.2": version: 2.9.2 resolution: "mini-css-extract-plugin@npm:2.9.2" @@ -22035,7 +22817,16 @@ __metadata: languageName: node linkType: hard -"moment@npm:2.30.1, moment@npm:^2.20.1, moment@npm:^2.29.4, moment@npm:^2.30.1": +"moment-timezone@npm:0.5.47": + version: 0.5.47 + resolution: "moment-timezone@npm:0.5.47" + dependencies: + moment: "npm:^2.29.4" + checksum: 10/b2ad32e6b7ea4ce99756c9f571f8eb1bdd6592ecdc2bf3f7fb988ae980a49a7e87b986776090571b9608c8576205f839b736dc7589c180ce36c82fad4b8416c6 + languageName: node + linkType: hard + +"moment@npm:2.30.1, moment@npm:2.x, moment@npm:^2.20.1, moment@npm:^2.29.4, moment@npm:^2.30.1": version: 2.30.1 resolution: "moment@npm:2.30.1" checksum: 10/ae42d876d4ec831ef66110bdc302c0657c664991e45cf2afffc4b0f6cd6d251dde11375c982a5c0564ccc0fa593fc564576ddceb8c8845e87c15f58aa6baca69 @@ -22185,7 +22976,7 @@ __metadata: languageName: node linkType: hard -"nano-css@npm:^5.3.1, nano-css@npm:^5.6.2": +"nano-css@npm:^5.3.1, nano-css@npm:^5.6.1, nano-css@npm:^5.6.2": version: 5.6.2 resolution: "nano-css@npm:5.6.2" dependencies: @@ -22302,6 +23093,13 @@ __metadata: languageName: node linkType: hard +"node-ensure@npm:^0.0.0": + version: 0.0.0 + resolution: "node-ensure@npm:0.0.0" + checksum: 10/1124623beb1dec66889794286ec1761ac3bfac42ce93b8652903efa5af14227edce5bafa06c4e0675cdc850360931d7c9413e3b5406150f05b2c9bf5bb3ccce4 + languageName: node + linkType: hard + "node-fetch-commonjs@npm:^3.3.2": version: 3.3.2 resolution: "node-fetch-commonjs@npm:3.3.2" @@ -22937,12 +23735,12 @@ __metadata: languageName: node linkType: hard -"ol-ext@npm:4.0.24": - version: 4.0.24 - resolution: "ol-ext@npm:4.0.24" +"ol-ext@npm:4.0.26": + version: 4.0.26 + resolution: "ol-ext@npm:4.0.26" peerDependencies: ol: ">= 5.3.0" - checksum: 10/76ab4ee468429171be7495998c0ae80853733f9ff6241dfd7b66208f265e180357bb80c304b2d185c50a4afa635a36ed8ed67fcd5374b49d790e71b9be039df9 + checksum: 10/68ecf27895e8cb0d4b179151836e853a0d8ce224e27448db10d11844afbea0e85781e2c9d6a9c1e65063f5a81d54755f8002c7f9259506a5bddc020024d5b931 languageName: node linkType: hard @@ -22970,6 +23768,13 @@ __metadata: languageName: node linkType: hard +"omggif@npm:^1.0.10": + version: 1.0.10 + resolution: "omggif@npm:1.0.10" + checksum: 10/a7b063d702969a911a8a337a4e2b17a370bfb66f0615344f8d7a7cfff5ee6e8c201a6a4ab41895fa9adfb51cb653894c52a306cf07bd7ceca355f240fea93261 + languageName: node + linkType: hard + "on-finished@npm:2.4.1, on-finished@npm:^2.4.1": version: 2.4.1 resolution: "on-finished@npm:2.4.1" @@ -23036,21 +23841,28 @@ __metadata: languageName: node linkType: hard -"openapi-path-templating@npm:^1.5.1": - version: 1.6.0 - resolution: "openapi-path-templating@npm:1.6.0" +"openapi-path-templating@npm:^2.0.1": + version: 2.1.0 + resolution: "openapi-path-templating@npm:2.1.0" dependencies: - apg-lite: "npm:^1.0.3" - checksum: 10/35353a0ce712dd79b7a60beca75d6a67fb6ad4bbdac4ceef87a6ab78d916f88dd6b426bd81e8ecf12c51798221fb15e63da8f9cadb1bcab4bc7aace8e8a266c1 + apg-lite: "npm:^1.0.4" + checksum: 10/de3ba30a19cc4bed5ace5dad0314bea66e09689001bd3510224a441f7ced53d854655daea846f884ed024d90a8f2af2d4bd0f28256dbda0c49b337684a802da1 languageName: node linkType: hard -"openapi-server-url-templating@npm:^1.0.0": - version: 1.1.0 - resolution: "openapi-server-url-templating@npm:1.1.0" +"openapi-server-url-templating@npm:^1.2.0": + version: 1.3.0 + resolution: "openapi-server-url-templating@npm:1.3.0" dependencies: - apg-lite: "npm:^1.0.3" - checksum: 10/932f08f390269506e1ea1d24208319f0f2a90023368562f1ce167a30d4199f0e39236e17f250749359b4c7b64d86c510e68e8eb9c7f6168da35f0475534ef3ed + apg-lite: "npm:^1.0.4" + checksum: 10/4a98f67cedc0958d3a30cc6db91f35970b0aeae113d0403639adbe3f5e123ff02abebc0468c10639bd0d6db165a9bb0f21c4e367836e984c2345a5a4213620a9 + languageName: node + linkType: hard + +"openapi-types@npm:^12.1.3": + version: 12.1.3 + resolution: "openapi-types@npm:12.1.3" + checksum: 10/9d1d7ed848622b63d0a4c3f881689161b99427133054e46b8e3241e137f1c78bb0031c5d80b420ee79ac2e91d2e727ffd6fc13c553d1b0488ddc8ad389dcbef8 languageName: node linkType: hard @@ -23347,6 +24159,13 @@ __metadata: languageName: node linkType: hard +"pako@npm:^1.0.11": + version: 1.0.11 + resolution: "pako@npm:1.0.11" + checksum: 10/1ad07210e894472685564c4d39a08717e84c2a68a70d3c1d9e657d32394ef1670e22972a433cbfe48976cb98b154ba06855dcd3fcfba77f60f1777634bec48c0 + languageName: node + linkType: hard + "pako@npm:^2.0.4": version: 2.0.4 resolution: "pako@npm:2.0.4" @@ -23354,10 +24173,17 @@ __metadata: languageName: node linkType: hard -"papaparse@npm:5.5.1": - version: 5.5.1 - resolution: "papaparse@npm:5.5.1" - checksum: 10/6c3b47bd316cf11936641abe5590c6fb1e5b04297d1c594965a44dba89cc0f26b4e4d61eb1933352c538a30c4915d110628d14cf4d60cef0b8245c9a26477602 +"papaparse@npm:5.4.1": + version: 5.4.1 + resolution: "papaparse@npm:5.4.1" + checksum: 10/5e6dc978187182ad2efa1d264ffe73d2042cd23b8fb1dcb0b0f5c8c7c772c11e3eb4e166fb0893880ed24529a96abe9065d704cc5b4cb96abf037413cfe43788 + languageName: node + linkType: hard + +"papaparse@npm:5.5.2": + version: 5.5.2 + resolution: "papaparse@npm:5.5.2" + checksum: 10/f5d25ccb10b1b9e87a0fe2d2965c7f8a83223cd79ec8963dd7e8766e8e97a6228dc6c20738a78148dfc74f32b2372003de732a7d50593f38b6a2bb5092710bc7 languageName: node linkType: hard @@ -23380,6 +24206,30 @@ __metadata: languageName: node linkType: hard +"parse-bmfont-ascii@npm:^1.0.6": + version: 1.0.6 + resolution: "parse-bmfont-ascii@npm:1.0.6" + checksum: 10/9dd46f8ad8db8e067904c97a21546a1e338eaabb909abe070c643e4e06dbf76fa685277114ca22a05a4a35d38197512b2826d5de46a03b10e9bf49119ced2e39 + languageName: node + linkType: hard + +"parse-bmfont-binary@npm:^1.0.6": + version: 1.0.6 + resolution: "parse-bmfont-binary@npm:1.0.6" + checksum: 10/728fbc05876c3f0ab116ea238be99f8c1188551e54997965038db558aab08c71f0ae1fee64c2a18c8d629c6b2aaea43e84a91783ec4f114ac400faf0b5170b86 + languageName: node + linkType: hard + +"parse-bmfont-xml@npm:^1.1.6": + version: 1.1.6 + resolution: "parse-bmfont-xml@npm:1.1.6" + dependencies: + xml-parse-from-string: "npm:^1.0.0" + xml2js: "npm:^0.5.0" + checksum: 10/71a202da289a124db7bb7bee1b2a01b8a38b5ba36f93d6a98cea6fc1d140c16c8bc7bcccff48864ec886da035944d337b04cf70723393c411991af952fc6086b + languageName: node + linkType: hard + "parse-color@npm:^1.0.0": version: 1.0.0 resolution: "parse-color@npm:1.0.0" @@ -23453,13 +24303,6 @@ __metadata: languageName: node linkType: hard -"parse-ms@npm:^4.0.0": - version: 4.0.0 - resolution: "parse-ms@npm:4.0.0" - checksum: 10/673c801d9f957ff79962d71ed5a24850163f4181a90dd30c4e3666b3a804f53b77f1f0556792e8b2adbb5d58757907d1aa51d7d7dc75997c2a56d72937cbc8b7 - languageName: node - linkType: hard - "parse-path@npm:^7.0.0": version: 7.0.0 resolution: "parse-path@npm:7.0.0" @@ -23655,6 +24498,13 @@ __metadata: languageName: node linkType: hard +"pathe@npm:^2.0.2": + version: 2.0.2 + resolution: "pathe@npm:2.0.2" + checksum: 10/027dd246720ec6d3b5567e2b0201f1a815b6a69f2912a4dcafed59620afc729af15b4aff4bc780504c88d11dfb081c051e37327b928a093e714c3e09bf35aff3 + languageName: node + linkType: hard + "pbf@npm:3.2.1": version: 3.2.1 resolution: "pbf@npm:3.2.1" @@ -23667,6 +24517,16 @@ __metadata: languageName: node linkType: hard +"pdf-parse@npm:^1.1.1": + version: 1.1.1 + resolution: "pdf-parse@npm:1.1.1" + dependencies: + debug: "npm:^3.1.0" + node-ensure: "npm:^0.0.0" + checksum: 10/5ab94d8cdd52975b861d6ca541ad728e127c0aa51c5d8923229f0910ed2931aa703022750632fe2ddf4512fd14dad50501521a35e9a3492d75f80b1f32cc4b86 + languageName: node + linkType: hard + "peberminta@npm:^0.9.0": version: 0.9.0 resolution: "peberminta@npm:0.9.0" @@ -23674,6 +24534,13 @@ __metadata: languageName: node linkType: hard +"peek-readable@npm:^4.1.0": + version: 4.1.0 + resolution: "peek-readable@npm:4.1.0" + checksum: 10/97373215dcf382748645c3d22ac5e8dbd31759f7bd0c539d9fdbaaa7d22021838be3e55110ad0ed8f241c489342304b14a50dfee7ef3bcee2987d003b24ecc41 + languageName: node + linkType: hard + "pend@npm:~1.2.0": version: 1.2.0 resolution: "pend@npm:1.2.0" @@ -23702,7 +24569,7 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^4.0.1": +"picomatch@npm:^4.0.2": version: 4.0.2 resolution: "picomatch@npm:4.0.2" checksum: 10/ce617b8da36797d09c0baacb96ca8a44460452c89362d7cb8f70ca46b4158ba8bc3606912de7c818eb4a939f7f9015cef3c766ec8a0c6bfc725fdc078e39c717 @@ -23744,6 +24611,17 @@ __metadata: languageName: node linkType: hard +"pixelmatch@npm:^5.3.0": + version: 5.3.0 + resolution: "pixelmatch@npm:5.3.0" + dependencies: + pngjs: "npm:^6.0.0" + bin: + pixelmatch: bin/pixelmatch + checksum: 10/10778aaa432211253ab0ae9160233d8aa56769ab6312b6bf8375100b67aaa126821626a0c3b433fb2a977864a8d2d145d754d4afa9ac14b84fcb1a0bdf98a4ae + languageName: node + linkType: hard + "pkg-dir@npm:^4.1.0, pkg-dir@npm:^4.2.0": version: 4.2.0 resolution: "pkg-dir@npm:4.2.0" @@ -23762,27 +24640,27 @@ __metadata: languageName: node linkType: hard -"playwright-core@npm:1.49.1": - version: 1.49.1 - resolution: "playwright-core@npm:1.49.1" +"playwright-core@npm:1.50.1": + version: 1.50.1 + resolution: "playwright-core@npm:1.50.1" bin: playwright-core: cli.js - checksum: 10/baa39a53024ec7744708410f2b952ac3aa2e1a6d311dabfa303523712848eba142fce5c20f1b2ed2a66fbd9a415d22ea8642b0f70423360aaebd4b41c47d364e + checksum: 10/9a310b8a66bf7fd926e620c1c8e27be29bdbdce91640e5f975b2fd4dc706d0307faec2bb0456cc8e7dedb1e71c0b5eb35c6a58acd5cedc7d8fd849a9067e637b languageName: node linkType: hard -"playwright@npm:1.49.1": - version: 1.49.1 - resolution: "playwright@npm:1.49.1" +"playwright@npm:1.50.1": + version: 1.50.1 + resolution: "playwright@npm:1.50.1" dependencies: fsevents: "npm:2.3.2" - playwright-core: "npm:1.49.1" + playwright-core: "npm:1.50.1" dependenciesMeta: fsevents: optional: true bin: playwright: cli.js - checksum: 10/49fb063f4a107b8090f66d2d351ebd51fbb66843a8f95a161fa0c0e0b5156515961e75cc10f4249f61b9d2af51f762dda505c62b096d8f61cd47d1ff73ab39d2 + checksum: 10/a3687614ac3238a81cbe3018e4f4a2ae92c71f3f65110cc6087068c020f6134f0628308da33177b9b08102644706e835d4053f6890beeb4a935f433bc4ac107a languageName: node linkType: hard @@ -23793,6 +24671,20 @@ __metadata: languageName: node linkType: hard +"pngjs@npm:^6.0.0": + version: 6.0.0 + resolution: "pngjs@npm:6.0.0" + checksum: 10/692751ccd5e762623103900922caac982caa90258d9c6c04a6e2bc3397b1dedbaf9db826fc0fa068a29d607cad3df1d1eded0dec2ee35a0015c65cb5ef33ad18 + languageName: node + linkType: hard + +"pngjs@npm:^7.0.0": + version: 7.0.0 + resolution: "pngjs@npm:7.0.0" + checksum: 10/e843ebbb0df092ee0f3a3e7dbd91ff87a239a4e4c4198fff202916bfb33b67622f4b83b3c29f3ccae94fcb97180c289df06068624554f61686fe6b9a4811f7db + languageName: node + linkType: hard + "polished@npm:^4.2.2": version: 4.2.2 resolution: "polished@npm:4.2.2" @@ -24262,7 +25154,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.5.1, postcss@npm:^8.4.33, postcss@npm:^8.4.38, postcss@npm:^8.4.49": +"postcss@npm:8.5.1, postcss@npm:^8.4.33, postcss@npm:^8.4.38, postcss@npm:^8.5.1": version: 8.5.1 resolution: "postcss@npm:8.5.1" dependencies: @@ -24344,15 +25236,6 @@ __metadata: languageName: node linkType: hard -"pretty-ms@npm:^9.0.0": - version: 9.1.0 - resolution: "pretty-ms@npm:9.1.0" - dependencies: - parse-ms: "npm:^4.0.0" - checksum: 10/3622a8999e4b2aa05ff64bf48c7e58143b3ede6e3434f8ce5588def90ebcf6af98edf79532344c4c9e14d5ad25deb3f0f5ca9f9b91e5d2d1ac26dad9cf428fc0 - languageName: node - linkType: hard - "pretty-time@npm:^1.1.0": version: 1.1.0 resolution: "pretty-time@npm:1.1.0" @@ -24614,7 +25497,7 @@ __metadata: languageName: node linkType: hard -"qs@npm:6.13.0, qs@npm:^6.11.2, qs@npm:^6.4.0": +"qs@npm:6.13.0": version: 6.13.0 resolution: "qs@npm:6.13.0" dependencies: @@ -24623,6 +25506,15 @@ __metadata: languageName: node linkType: hard +"qs@npm:^6.11.2, qs@npm:^6.4.0": + version: 6.13.1 + resolution: "qs@npm:6.13.1" + dependencies: + side-channel: "npm:^1.0.6" + checksum: 10/53cf5fdc5f342a9ffd3968f20c8c61624924cf928d86fff525240620faba8ca5cfd6c3f12718cc755561bfc3dc9721bc8924e38f53d8925b03940f0b8a902212 + languageName: node + linkType: hard + "querystringify@npm:^2.1.1": version: 2.2.0 resolution: "querystringify@npm:2.2.0" @@ -24692,7 +25584,7 @@ __metadata: languageName: node linkType: hard -"raf@npm:^3.1.0, raf@npm:^3.4.1": +"raf@npm:^3.1.0, raf@npm:^3.4.0, raf@npm:^3.4.1": version: 3.4.1 resolution: "raf@npm:3.4.1" dependencies: @@ -24774,6 +25666,18 @@ __metadata: languageName: node linkType: hard +"rc-align@npm:^2.4.0": + version: 2.4.5 + resolution: "rc-align@npm:2.4.5" + dependencies: + babel-runtime: "npm:^6.26.0" + dom-align: "npm:^1.7.0" + prop-types: "npm:^15.5.8" + rc-util: "npm:^4.0.4" + checksum: 10/6a82f7b47dda397b90c7b6d41e5500b9e3e427891d15a54f95708abf31a8aba19d882bd9e5ab42f4979b455aa2236b96cf6403152507bcd1fd7663be08a0ceb5 + languageName: node + linkType: hard + "rc-align@npm:^4.0.0": version: 4.0.15 resolution: "rc-align@npm:4.0.15" @@ -24790,6 +25694,21 @@ __metadata: languageName: node linkType: hard +"rc-animate@npm:2.x": + version: 2.11.1 + resolution: "rc-animate@npm:2.11.1" + dependencies: + babel-runtime: "npm:6.x" + classnames: "npm:^2.2.6" + css-animation: "npm:^1.3.2" + prop-types: "npm:15.x" + raf: "npm:^3.4.0" + rc-util: "npm:^4.15.3" + react-lifecycles-compat: "npm:^3.0.4" + checksum: 10/afb54ad896c9d50af212ae7a56a216b47b38238a4e8e187437fe965c2cf100f1ca82668ed90d38dcd3b0e3ced53f54383e417af09c6dc09f6db0f78cba2aa9e4 + languageName: node + linkType: hard + "rc-cascader@npm:1.0.1": version: 1.0.1 resolution: "rc-cascader@npm:1.0.1" @@ -24805,6 +25724,23 @@ __metadata: languageName: node linkType: hard +"rc-cascader@npm:3.28.1": + version: 3.28.1 + resolution: "rc-cascader@npm:3.28.1" + dependencies: + "@babel/runtime": "npm:^7.12.5" + array-tree-filter: "npm:^2.1.0" + classnames: "npm:^2.3.1" + rc-select: "npm:~14.15.0" + rc-tree: "npm:~5.9.0" + rc-util: "npm:^5.37.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10/bb2feb79c0db19f459b265e9a0afb87611f4ba06c4776a5ea8ddcb0bfc81403292ada3a1c29cd7511872f8d2bfda7c29eab6dbc32052a60baf49576a50866d77 + languageName: node + linkType: hard + "rc-cascader@npm:3.33.0": version: 3.33.0 resolution: "rc-cascader@npm:3.33.0" @@ -24926,6 +25862,24 @@ __metadata: languageName: node linkType: hard +"rc-select@npm:~14.15.0": + version: 14.15.2 + resolution: "rc-select@npm:14.15.2" + dependencies: + "@babel/runtime": "npm:^7.10.1" + "@rc-component/trigger": "npm:^2.1.1" + classnames: "npm:2.x" + rc-motion: "npm:^2.0.1" + rc-overflow: "npm:^1.3.1" + rc-util: "npm:^5.16.1" + rc-virtual-list: "npm:^3.5.2" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 10/707d9de38aaf83063ede754a925b56d6f02740197a3bed93f886c132ce797321d9e70a2fe32cff0546c54d9a11414d6d2c8fc1f914ac665fa11ad2b30a08bc85 + languageName: node + linkType: hard + "rc-select@npm:~14.16.2": version: 14.16.3 resolution: "rc-select@npm:14.16.3" @@ -24944,6 +25898,20 @@ __metadata: languageName: node linkType: hard +"rc-slider@npm:11.1.7": + version: 11.1.7 + resolution: "rc-slider@npm:11.1.7" + dependencies: + "@babel/runtime": "npm:^7.10.1" + classnames: "npm:^2.2.5" + rc-util: "npm:^5.36.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10/3b484d7ba4e4b6fc695666c27b767622c64b5819d0386cc0afb6d186c08f8ed4f93dd72f55377af9a957c77ee77db4d7fa73f85ccdaab0f39d9670daf42a17c5 + languageName: node + linkType: hard + "rc-slider@npm:11.1.8": version: 11.1.8 resolution: "rc-slider@npm:11.1.8" @@ -24958,6 +25926,34 @@ __metadata: languageName: node linkType: hard +"rc-time-picker@npm:^3.7.3": + version: 3.7.3 + resolution: "rc-time-picker@npm:3.7.3" + dependencies: + classnames: "npm:2.x" + moment: "npm:2.x" + prop-types: "npm:^15.5.8" + raf: "npm:^3.4.1" + rc-trigger: "npm:^2.2.0" + react-lifecycles-compat: "npm:^3.0.4" + checksum: 10/236ba0dd1b1cee4dd398d2542c251a7e0da21b31d3390a23c45cc7f2ea266cfddd1f0b7f681fe205f2d92edaa72771cb087505790b5124eb0ca31668feb74da5 + languageName: node + linkType: hard + +"rc-tooltip@npm:6.2.1": + version: 6.2.1 + resolution: "rc-tooltip@npm:6.2.1" + dependencies: + "@babel/runtime": "npm:^7.11.2" + "@rc-component/trigger": "npm:^2.0.0" + classnames: "npm:^2.3.1" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10/a82064d6d521ba4c03d074505402f6c38f9f50439037235969546b694e38977bab3420b46080bde295aca2436e6725d64b0a330a5ccdd51932deabbb1bf7585b + languageName: node + linkType: hard + "rc-tooltip@npm:6.4.0": version: 6.4.0 resolution: "rc-tooltip@npm:6.4.0" @@ -24989,6 +25985,37 @@ __metadata: languageName: node linkType: hard +"rc-tree@npm:~5.9.0": + version: 5.9.0 + resolution: "rc-tree@npm:5.9.0" + dependencies: + "@babel/runtime": "npm:^7.10.1" + classnames: "npm:2.x" + rc-motion: "npm:^2.0.1" + rc-util: "npm:^5.16.1" + rc-virtual-list: "npm:^3.5.1" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 10/d7525c4a524c6de8e177ebc90fe9b924046951a02bacee85efd4529fb05a66add936802b43d5ca8d84469f9c63b8d542437365b911480f62a78e03e2c7fbaca0 + languageName: node + linkType: hard + +"rc-trigger@npm:^2.2.0": + version: 2.6.5 + resolution: "rc-trigger@npm:2.6.5" + dependencies: + babel-runtime: "npm:6.x" + classnames: "npm:^2.2.6" + prop-types: "npm:15.x" + rc-align: "npm:^2.4.0" + rc-animate: "npm:2.x" + rc-util: "npm:^4.4.0" + react-lifecycles-compat: "npm:^3.0.4" + checksum: 10/a3ed5f0c453a37ab00fce302e9f40daa64870eb001576a3409a94550802af1e01cbd7b050b3adf7225af03e82e2070d095477957fcd07209ee32602a2f3fba31 + languageName: node + linkType: hard + "rc-trigger@npm:^4.0.0": version: 4.4.3 resolution: "rc-trigger@npm:4.4.3" @@ -25003,7 +26030,7 @@ __metadata: languageName: node linkType: hard -"rc-util@npm:^4.0.4": +"rc-util@npm:^4.0.4, rc-util@npm:^4.15.3, rc-util@npm:^4.4.0": version: 4.21.1 resolution: "rc-util@npm:4.21.1" dependencies: @@ -25094,6 +26121,45 @@ __metadata: languageName: node linkType: hard +"react-calendar@npm:5.0.0": + version: 5.0.0 + resolution: "react-calendar@npm:5.0.0" + dependencies: + "@wojtekmaj/date-utils": "npm:^1.1.3" + clsx: "npm:^2.0.0" + get-user-locale: "npm:^2.2.1" + warning: "npm:^4.0.0" + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/1172828652e796a946beec4f7f4125bfbe775a39c4cdab2179cef04b0688e892062f628ec9263bdfea6d9be41c3de1414586036d03d6694e6008cd4763649581 + languageName: node + linkType: hard + +"react-calendar@npm:^4.8.0": + version: 4.8.0 + resolution: "react-calendar@npm:4.8.0" + dependencies: + "@wojtekmaj/date-utils": "npm:^1.1.3" + clsx: "npm:^2.0.0" + get-user-locale: "npm:^2.2.1" + prop-types: "npm:^15.6.0" + warning: "npm:^4.0.0" + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/26a58d9bbe9b1bbe1201612628d0f6bdeec0355ed3730013d066b9e87adbbee4b6f3f1c83352978c0e2f317d8d32ac31ac2cbb3c6f83833bef0ba047c5bb6907 + languageName: node + linkType: hard + "react-calendar@npm:^5.1.0": version: 5.1.0 resolution: "react-calendar@npm:5.1.0" @@ -25239,6 +26305,19 @@ __metadata: languageName: node linkType: hard +"react-dropzone@npm:14.2.9": + version: 14.2.9 + resolution: "react-dropzone@npm:14.2.9" + dependencies: + attr-accept: "npm:^2.2.2" + file-selector: "npm:^0.6.0" + prop-types: "npm:^15.8.1" + peerDependencies: + react: ">= 16.8 || 18.0.0" + checksum: 10/a8ff584a9dbf952dbd630f4ddf59b0b7a010eff49c3b97b363e30ab357f9cc7b8a0c7694069badeb4cf32361a00f3bbd1063964bd6438e9a68c6fe49ff879a38 + languageName: node + linkType: hard + "react-dropzone@npm:14.3.5, react-dropzone@npm:^14.2.3": version: 14.3.5 resolution: "react-dropzone@npm:14.3.5" @@ -25268,12 +26347,21 @@ __metadata: languageName: node linkType: hard -"react-from-dom@npm:^0.7.3": - version: 0.7.3 - resolution: "react-from-dom@npm:0.7.3" +"react-from-dom@npm:^0.6.2": + version: 0.6.2 + resolution: "react-from-dom@npm:0.6.2" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 10/55d6365af5b2aeaa0f2d80808dfa96114367e63849821b7ea277d193d4f6b1fce020e9754d4527ebc7f8628c5333f19dae38fc7b7956f69d5f640ac28b37ab0f + checksum: 10/f3954737c2677e82f72ecedcdcf5f187d2a1b86a6c5b915f7300796ac153437581ee0111c9f524e7c18124d25d0d31391d54cdee318ea390722ca57e66813ed7 + languageName: node + linkType: hard + +"react-from-dom@npm:^0.7.5": + version: 0.7.5 + resolution: "react-from-dom@npm:0.7.5" + peerDependencies: + react: 16.8 - 19 + checksum: 10/57459e775b2e2a12f3fc6bcc5365b88505ae856bd82188edd9e3b15c6318295316071789d06666533ee89ba4ccfd43ec7a5c68fbf4fbcaccb9e495eb02961947 languageName: node linkType: hard @@ -25324,6 +26412,18 @@ __metadata: languageName: node linkType: hard +"react-highlight-words@npm:0.21.0": + version: 0.21.0 + resolution: "react-highlight-words@npm:0.21.0" + dependencies: + highlight-words-core: "npm:^1.2.0" + memoize-one: "npm:^4.0.0" + peerDependencies: + react: ^0.14.0 || ^15.0.0 || ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0 + checksum: 10/5054e1d3f536bf672047cc22d954837289061754cd342c5439b153069fca6997890f44af4cf9154149f70844dc9cdbc18d5fd82b7458b430fc42b7f1c11a6bf2 + languageName: node + linkType: hard + "react-hook-form@npm:^7.49.2": version: 7.54.2 resolution: "react-hook-form@npm:7.54.2" @@ -25342,9 +26442,27 @@ __metadata: languageName: node linkType: hard +"react-i18next@npm:^14.0.0": + version: 14.1.3 + resolution: "react-i18next@npm:14.1.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + html-parse-stringify: "npm:^3.0.1" + peerDependencies: + i18next: ">= 23.2.3" + react: ">= 16.8.0" + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + checksum: 10/d0fa0f2717103c60758f9ddc1710e529f52e341465ca3f106ffa9168d88ad2db1bdbae58c77cca389933ae14bc39835abb37d1982049551ca15f6d310e2b3f57 + languageName: node + linkType: hard + "react-i18next@npm:^15.0.0": - version: 15.2.0 - resolution: "react-i18next@npm:15.2.0" + version: 15.4.0 + resolution: "react-i18next@npm:15.4.0" dependencies: "@babel/runtime": "npm:^7.25.0" html-parse-stringify: "npm:^3.0.1" @@ -25356,7 +26474,7 @@ __metadata: optional: true react-native: optional: true - checksum: 10/9b2937f7beab763c494d55a801f21bfdbfe98e9509994c350d24fa404ded573f41e8607eeba290c686d5877d34f0ddefe48e9d6876720d5ed0e1243bcdd5dda6 + checksum: 10/4b3666d819f01cf96a256af4419b26938d314e33c6388eafccc29f67ad02994e5d53e7bf82eac656cade7f7bcd04f4a237f0b293165d7eda91d62e3fde605a38 languageName: node linkType: hard @@ -25382,14 +26500,26 @@ __metadata: languageName: node linkType: hard -"react-inlinesvg@npm:4.1.5": - version: 4.1.5 - resolution: "react-inlinesvg@npm:4.1.5" +"react-inlinesvg@npm:3.0.2": + version: 3.0.2 + resolution: "react-inlinesvg@npm:3.0.2" dependencies: - react-from-dom: "npm:^0.7.3" + exenv: "npm:^1.2.2" + react-from-dom: "npm:^0.6.2" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 10/740fa33c7a09012bb96509f9003dc26e4e412eed2fc861ca40bfee9a3dddcf7c4d86fd20f824d4c017e44526aa9d747d6c9543ef3f2215bc0ace72754e025316 + languageName: node + linkType: hard + +"react-inlinesvg@npm:4.2.0": + version: 4.2.0 + resolution: "react-inlinesvg@npm:4.2.0" + dependencies: + react-from-dom: "npm:^0.7.5" peerDependencies: react: 16.8 - 19 - checksum: 10/475666855056007bfec56968d61793530f2089997d8a2956d603e03d9da8cd353a00368f220f07ef554c65feb4f5839112cefb2c84b22e511d8a470bea8eee61 + checksum: 10/cf55657efce21c4891ab5875800722cf0184017bc821606ddc3775561c47e0648da1956bc4af14e13e18139d206af9eb834d7601098ff7b37caad7d010e99c2d languageName: node linkType: hard @@ -25617,20 +26747,20 @@ __metadata: languageName: node linkType: hard -"react-router-dom@npm:5.3.3": - version: 5.3.3 - resolution: "react-router-dom@npm:5.3.3" +"react-router-dom@npm:5.3.4": + version: 5.3.4 + resolution: "react-router-dom@npm:5.3.4" dependencies: "@babel/runtime": "npm:^7.12.13" history: "npm:^4.9.0" loose-envify: "npm:^1.3.1" prop-types: "npm:^15.6.2" - react-router: "npm:5.3.3" + react-router: "npm:5.3.4" tiny-invariant: "npm:^1.0.2" tiny-warning: "npm:^1.0.0" peerDependencies: react: ">=15" - checksum: 10/49552596f1a4c753b99324a5f4345b3ee91fbb780aa65851a7113f053044ef96c083d2ded12937e593b23a0fcdf58b9e49780df6bf6e27d9eeb348b3c85ae611 + checksum: 10/5e0696ae2d86f466ff700944758a227e1dcd79b48797d567776506e4e3b4a08b81336155feb86a33be9f38c17c4d3d94212b5c60c8ee9a086022e4fd3961db29 languageName: node linkType: hard @@ -25647,15 +26777,14 @@ __metadata: languageName: node linkType: hard -"react-router@npm:5.3.3": - version: 5.3.3 - resolution: "react-router@npm:5.3.3" +"react-router@npm:5.3.4": + version: 5.3.4 + resolution: "react-router@npm:5.3.4" dependencies: "@babel/runtime": "npm:^7.12.13" history: "npm:^4.9.0" hoist-non-react-statics: "npm:^3.1.0" loose-envify: "npm:^1.3.1" - mini-create-react-context: "npm:^0.4.0" path-to-regexp: "npm:^1.7.0" prop-types: "npm:^15.6.2" react-is: "npm:^16.6.0" @@ -25663,7 +26792,7 @@ __metadata: tiny-warning: "npm:^1.0.0" peerDependencies: react: ">=15" - checksum: 10/4631eed91020c73950804c7c7454e74b2eb495f803c5ca60c8b5572ca72cc06e336f3b08d9ee3fa730128a52c4d9e16d1aa7e8b7f85560629117e16d99a01cef + checksum: 10/99d54a99af6bc6d7cad2e5ea7eee9485b62a8b8e16a1182b18daa7fad7dafa5e526850eaeebff629848b297ae055a9cb5b4aba8760e81af8b903efc049d48f5c languageName: node linkType: hard @@ -25687,9 +26816,9 @@ __metadata: languageName: node linkType: hard -"react-select@npm:5.9.0": - version: 5.9.0 - resolution: "react-select@npm:5.9.0" +"react-select@npm:5.10.0": + version: 5.10.0 + resolution: "react-select@npm:5.10.0" dependencies: "@babel/runtime": "npm:^7.12.0" "@emotion/cache": "npm:^11.4.0" @@ -25703,7 +26832,27 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: 10/2206b6687d6584ff1426056a779d8014ef4d8c4d1d5253dea4f03b01569fdedab8ae6b683ef475ead7da5a824934008b682838d734841d9734e2a8a63b9959fd + checksum: 10/70ccb74ef31a1fe24c3f7cb94459dd33289999ceda104b1b75256758b7f25ead5dcb4a1877d513f9e22d9ac62d2656414c51dd57b9436d49bd6834a7c8b1c727 + languageName: node + linkType: hard + +"react-select@npm:5.8.1": + version: 5.8.1 + resolution: "react-select@npm:5.8.1" + dependencies: + "@babel/runtime": "npm:^7.12.0" + "@emotion/cache": "npm:^11.4.0" + "@emotion/react": "npm:^11.8.1" + "@floating-ui/dom": "npm:^1.0.1" + "@types/react-transition-group": "npm:^4.4.0" + memoize-one: "npm:^6.0.0" + prop-types: "npm:^15.6.0" + react-transition-group: "npm:^4.3.0" + use-isomorphic-layout-effect: "npm:^1.1.2" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 10/53168b156435c5bef7c271ae7ebe67bff912e568dd1638f37859ea0d76cbd273d422714b6cb9669aa811d3fb44bda0f666b5e397a90a76ac2888a9b0ab47495a languageName: node linkType: hard @@ -25839,7 +26988,57 @@ __metadata: languageName: node linkType: hard -"react-use@npm:17.6.0, react-use@npm:^17.4.0, react-use@npm:^17.4.2, react-use@npm:^17.5.0": +"react-use@npm:17.5.0": + version: 17.5.0 + resolution: "react-use@npm:17.5.0" + dependencies: + "@types/js-cookie": "npm:^2.2.6" + "@xobotyi/scrollbar-width": "npm:^1.9.5" + copy-to-clipboard: "npm:^3.3.1" + fast-deep-equal: "npm:^3.1.3" + fast-shallow-equal: "npm:^1.0.0" + js-cookie: "npm:^2.2.1" + nano-css: "npm:^5.6.1" + react-universal-interface: "npm:^0.6.2" + resize-observer-polyfill: "npm:^1.5.1" + screenfull: "npm:^5.1.0" + set-harmonic-interval: "npm:^1.0.1" + throttle-debounce: "npm:^3.0.1" + ts-easing: "npm:^0.2.0" + tslib: "npm:^2.1.0" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 10/5d81fe0902303d3ed7810cdd56c6cae12b08124a3d4fcbfa3924327105b81447b039ea9d6aff20aac3c13999f949000870a7a2fa29fe20ed844ac26606462fa0 + languageName: node + linkType: hard + +"react-use@npm:17.5.1": + version: 17.5.1 + resolution: "react-use@npm:17.5.1" + dependencies: + "@types/js-cookie": "npm:^2.2.6" + "@xobotyi/scrollbar-width": "npm:^1.9.5" + copy-to-clipboard: "npm:^3.3.1" + fast-deep-equal: "npm:^3.1.3" + fast-shallow-equal: "npm:^1.0.0" + js-cookie: "npm:^2.2.1" + nano-css: "npm:^5.6.2" + react-universal-interface: "npm:^0.6.2" + resize-observer-polyfill: "npm:^1.5.1" + screenfull: "npm:^5.1.0" + set-harmonic-interval: "npm:^1.0.1" + throttle-debounce: "npm:^3.0.1" + ts-easing: "npm:^0.2.0" + tslib: "npm:^2.1.0" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 10/2da403a9949dbd964b9b8e20dcd354db66b7f7d5ca1f42572fbcdb06bd49ee828c295be4912cb87abc163d1b54820bb8c5fa85314a16c4579d9e30bf9cbd5759 + languageName: node + linkType: hard + +"react-use@npm:17.6.0, react-use@npm:^17.3.1, react-use@npm:^17.4.0, react-use@npm:^17.5.0": version: 17.6.0 resolution: "react-use@npm:17.6.0" dependencies: @@ -25875,7 +27074,17 @@ __metadata: languageName: node linkType: hard -"react-virtualized-auto-sizer@npm:1.0.25, react-virtualized-auto-sizer@npm:^1.0.24, react-virtualized-auto-sizer@npm:^1.0.6": +"react-virtualized-auto-sizer@npm:1.0.24": + version: 1.0.24 + resolution: "react-virtualized-auto-sizer@npm:1.0.24" + peerDependencies: + react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 + react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 + checksum: 10/02101a340bdbe3e40c49dbc52e524eb7ca18832690e91f045a25675600d7adc0a63e800a4ace6a014132adcdcce0e12a8137971de408427a5a3112d7c87c9f3e + languageName: node + linkType: hard + +"react-virtualized-auto-sizer@npm:1.0.25, react-virtualized-auto-sizer@npm:^1.0.6": version: 1.0.25 resolution: "react-virtualized-auto-sizer@npm:1.0.25" peerDependencies: @@ -25885,13 +27094,26 @@ __metadata: languageName: node linkType: hard -"react-window-infinite-loader@npm:1.0.9": - version: 1.0.9 - resolution: "react-window-infinite-loader@npm:1.0.9" +"react-window-infinite-loader@npm:1.0.10": + version: 1.0.10 + resolution: "react-window-infinite-loader@npm:1.0.10" peerDependencies: - react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 - react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 - checksum: 10/4a11ad949443cd76f9304d0558516c53474f53ca370e54bc29c2c362657b43545c89aec3e7df42d8e8511df379aed79b6fdd2c8fc52e25a9559b64e41ac7aefe + react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10/4f4c097a2948f8da71d13199289d85c89720e41888ea50039d2b8e6d7cf160300e97f66421c53420d8cb95b3ece74940c07ebd23bddd7a705a679d7e2dc2957e + languageName: node + linkType: hard + +"react-window@npm:1.8.10": + version: 1.8.10 + resolution: "react-window@npm:1.8.10" + dependencies: + "@babel/runtime": "npm:^7.0.0" + memoize-one: "npm:>=3.1.1 <6" + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + checksum: 10/6f4a713a2012d605370ef4c7026a45ddd6801e428faa4cad558b12b05ba54c00de72de9a360db109db9666f972a3d955b63af9e5a4cd5fbc52411a382273107b languageName: node linkType: hard @@ -25909,16 +27131,16 @@ __metadata: linkType: hard "react-zoom-pan-pinch@npm:^3.3.0": - version: 3.6.1 - resolution: "react-zoom-pan-pinch@npm:3.6.1" + version: 3.7.0 + resolution: "react-zoom-pan-pinch@npm:3.7.0" peerDependencies: react: "*" react-dom: "*" - checksum: 10/9146aa5c427dd6d0c8a4ebe3db0c720718eef6262d1b4b36033ee433bc76a9c84e30ca91311211ab95446305d3e2813d9abc576d093efbf5562be984431896cb + checksum: 10/5ae7f1ffea86fd19ae57f7b4c6818b282e13e00523200d23759db95a1518333044017583c2af3c179b2634c88c76162aa0ce8de79cd26359f07339415842ba34 languageName: node linkType: hard -"react@npm:18.3.1, react@npm:^16.8.0 || ^17.0.0 || ^18.0.0": +"react@npm:18.3.1, react@npm:^16.8.0 || ^17.0.0 || ^18.0.0, react@npm:^18": version: 18.3.1 resolution: "react@npm:18.3.1" dependencies: @@ -26024,7 +27246,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.0.0, readable-stream@npm:^3.0.2, readable-stream@npm:^3.0.6, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0": +"readable-stream@npm:^3.0.0, readable-stream@npm:^3.0.2, readable-stream@npm:^3.0.6, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -26035,6 +27257,15 @@ __metadata: languageName: node linkType: hard +"readable-web-to-node-stream@npm:^3.0.0": + version: 3.0.2 + resolution: "readable-web-to-node-stream@npm:3.0.2" + dependencies: + readable-stream: "npm:^3.6.0" + checksum: 10/d3a5bf9d707c01183d546a64864aa63df4d9cb835dfd2bf89ac8305e17389feef2170c4c14415a10d38f9b9bfddf829a57aaef7c53c8b40f11d499844bf8f1a4 + languageName: node + linkType: hard + "readdirp@npm:^4.0.1": version: 4.0.1 resolution: "readdirp@npm:4.0.1" @@ -26183,6 +27414,13 @@ __metadata: languageName: node linkType: hard +"regenerator-runtime@npm:^0.11.0": + version: 0.11.1 + resolution: "regenerator-runtime@npm:0.11.1" + checksum: 10/64e62d78594c227e7d5269811bca9e4aa6451332adaae8c79a30cab0fa98733b1ad90bdb9d038095c340c6fad3b414a49a8d9e0b6b424ab7ff8f94f35704f8a2 + languageName: node + linkType: hard + "regenerator-runtime@npm:^0.13.4, regenerator-runtime@npm:^0.13.7": version: 0.13.11 resolution: "regenerator-runtime@npm:0.13.11" @@ -26641,18 +27879,18 @@ __metadata: languageName: node linkType: hard -"rollup-plugin-esbuild@npm:6.1.1": - version: 6.1.1 - resolution: "rollup-plugin-esbuild@npm:6.1.1" +"rollup-plugin-esbuild@npm:6.2.0": + version: 6.2.0 + resolution: "rollup-plugin-esbuild@npm:6.2.0" dependencies: - "@rollup/pluginutils": "npm:^5.0.5" - debug: "npm:^4.3.4" - es-module-lexer: "npm:^1.3.1" - get-tsconfig: "npm:^4.7.2" + debug: "npm:^4.4.0" + es-module-lexer: "npm:^1.6.0" + get-tsconfig: "npm:^4.10.0" + unplugin-utils: "npm:^0.2.3" peerDependencies: esbuild: ">=0.18.0" rollup: ^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 - checksum: 10/bba2d1dfb92a193823ac9dd1cdd44a8fd8cd9f25868e9a22ca077e1b7445feb4eaaf6df051148e367fc902d7d59c9f50efab49086c24c367972f05c86f3a656d + checksum: 10/305563dfa72a843fd7db7b885583f22f62d4d584cbce7e8d80f138692ce67eb34e45dc1227b3420662f22fa75fa603ca09b64e7d1670919275a8577da653fada languageName: node linkType: hard @@ -26693,28 +27931,28 @@ __metadata: linkType: hard "rollup@npm:^4.22.4": - version: 4.28.1 - resolution: "rollup@npm:4.28.1" + version: 4.32.1 + resolution: "rollup@npm:4.32.1" dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.28.1" - "@rollup/rollup-android-arm64": "npm:4.28.1" - "@rollup/rollup-darwin-arm64": "npm:4.28.1" - "@rollup/rollup-darwin-x64": "npm:4.28.1" - "@rollup/rollup-freebsd-arm64": "npm:4.28.1" - "@rollup/rollup-freebsd-x64": "npm:4.28.1" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.28.1" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.28.1" - "@rollup/rollup-linux-arm64-gnu": "npm:4.28.1" - "@rollup/rollup-linux-arm64-musl": "npm:4.28.1" - "@rollup/rollup-linux-loongarch64-gnu": "npm:4.28.1" - "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.28.1" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.28.1" - "@rollup/rollup-linux-s390x-gnu": "npm:4.28.1" - "@rollup/rollup-linux-x64-gnu": "npm:4.28.1" - "@rollup/rollup-linux-x64-musl": "npm:4.28.1" - "@rollup/rollup-win32-arm64-msvc": "npm:4.28.1" - "@rollup/rollup-win32-ia32-msvc": "npm:4.28.1" - "@rollup/rollup-win32-x64-msvc": "npm:4.28.1" + "@rollup/rollup-android-arm-eabi": "npm:4.32.1" + "@rollup/rollup-android-arm64": "npm:4.32.1" + "@rollup/rollup-darwin-arm64": "npm:4.32.1" + "@rollup/rollup-darwin-x64": "npm:4.32.1" + "@rollup/rollup-freebsd-arm64": "npm:4.32.1" + "@rollup/rollup-freebsd-x64": "npm:4.32.1" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.32.1" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.32.1" + "@rollup/rollup-linux-arm64-gnu": "npm:4.32.1" + "@rollup/rollup-linux-arm64-musl": "npm:4.32.1" + "@rollup/rollup-linux-loongarch64-gnu": "npm:4.32.1" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.32.1" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.32.1" + "@rollup/rollup-linux-s390x-gnu": "npm:4.32.1" + "@rollup/rollup-linux-x64-gnu": "npm:4.32.1" + "@rollup/rollup-linux-x64-musl": "npm:4.32.1" + "@rollup/rollup-win32-arm64-msvc": "npm:4.32.1" + "@rollup/rollup-win32-ia32-msvc": "npm:4.32.1" + "@rollup/rollup-win32-x64-msvc": "npm:4.32.1" "@types/estree": "npm:1.0.6" fsevents: "npm:~2.3.2" dependenciesMeta: @@ -26760,7 +27998,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 10/4337898d07e646835b52494b43b4ccd6929da87af2b0febc05ab217fd2425cfda05af5efaea6037c1641c90d803eb5b3e491eefdd47b28fda85af4f46a0dad34 + checksum: 10/5a64860df9d0c1b88d142b8502cb2e858e8314025ed35c605c70dc5c7c099fcecc9340cac269412c9a8b53705b911f1454b01164d23400c7d84cafb241be255f languageName: node linkType: hard @@ -26794,10 +28032,10 @@ __metadata: languageName: node linkType: hard -"rudder-sdk-js@npm:2.48.43": - version: 2.48.43 - resolution: "rudder-sdk-js@npm:2.48.43" - checksum: 10/183888b1bd922d3e811fae5729b6a0ac93d5dbb44e8ee753ced80ae496d19c9127ef8de8908718d43ed4b33e4e9b738c3bbc562ea6cb02c050702f9b850ae2a5 +"rudder-sdk-js@npm:2.48.44": + version: 2.48.44 + resolution: "rudder-sdk-js@npm:2.48.44" + checksum: 10/538bc405c0cb7faf33b910a39d51bd6df53fbdb176d4e83c33821e7a454178b65ced216ad0f055b6dffd201ccc45cead814138e20683e15424a02fb54b894e15 languageName: node linkType: hard @@ -26815,7 +28053,7 @@ __metadata: languageName: node linkType: hard -"run-parallel@npm:^1.1.9, run-parallel@npm:^1.2.0": +"run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" dependencies: @@ -26831,7 +28069,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:7.8.1, rxjs@npm:^7.5.1, rxjs@npm:^7.5.5": +"rxjs@npm:7.8.1, rxjs@npm:^7.5.1, rxjs@npm:^7.5.5, rxjs@npm:^7.8.1": version: 7.8.1 resolution: "rxjs@npm:7.8.1" dependencies: @@ -26945,6 +28183,13 @@ __metadata: languageName: node linkType: hard +"sax@npm:>=0.6.0": + version: 1.4.1 + resolution: "sax@npm:1.4.1" + checksum: 10/b1c784b545019187b53a0c28edb4f6314951c971e2963a69739c6ce222bfbc767e54d320e689352daba79b7d5e06d22b5d7113b99336219d6e93718e2f99d335 + languageName: node + linkType: hard + "saxes@npm:^6.0.0": version: 6.0.0 resolution: "saxes@npm:6.0.0" @@ -27060,12 +28305,12 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.6.3, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.2, semver@npm:^7.6.3": - version: 7.6.3 - resolution: "semver@npm:7.6.3" +"semver@npm:7.7.0, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.2, semver@npm:^7.6.3": + version: 7.7.0 + resolution: "semver@npm:7.7.0" bin: semver: bin/semver.js - checksum: 10/36b1fbe1a2b6f873559cd57b238f1094a053dbfd997ceeb8757d79d1d2089c56d1321b9f1069ce263dc64cfa922fa1d2ad566b39426fe1ac6c723c1487589e10 + checksum: 10/5d615860a54ff563955c451e467bff3aaf74c8d060489f936f25551d5ca05f5ac683eb46c9ed7ade082e1e53b313f205ed9c5df0b25ebb3517ec25c79e1f0d9c languageName: node linkType: hard @@ -27427,6 +28672,13 @@ __metadata: languageName: node linkType: hard +"simple-xml-to-json@npm:^1.2.2": + version: 1.2.3 + resolution: "simple-xml-to-json@npm:1.2.3" + checksum: 10/67014ee9b61c838c8d631ca5cdd37b69fbe0f420abc91755f8f1428b1a5504d65cddb5124eb58c29c1ecf807ab31eeba282fc3f81769314d352267e4b73e95f3 + languageName: node + linkType: hard + "sirv@npm:^2.0.3": version: 2.0.4 resolution: "sirv@npm:2.0.4" @@ -27619,13 +28871,6 @@ __metadata: languageName: node linkType: hard -"smol-toml@npm:^1.3.1": - version: 1.3.1 - resolution: "smol-toml@npm:1.3.1" - checksum: 10/b999828ea46cf44ae90b6293884d6a139dfb4545ac6f86cbd1002568a943a43d8895ad82413855d095eec0c0bc21d23413c0a25a26c7fad6395c2ce42c2fdbd0 - languageName: node - linkType: hard - "smtp-server@npm:^3.11.0": version: 3.13.4 resolution: "smtp-server@npm:3.13.4" @@ -28124,22 +29369,6 @@ __metadata: languageName: node linkType: hard -"storybook-dark-mode@npm:4.0.1": - version: 4.0.1 - resolution: "storybook-dark-mode@npm:4.0.1" - dependencies: - "@storybook/components": "npm:^8.0.0" - "@storybook/core-events": "npm:^8.0.0" - "@storybook/global": "npm:^5.0.0" - "@storybook/icons": "npm:^1.2.5" - "@storybook/manager-api": "npm:^8.0.0" - "@storybook/theming": "npm:^8.0.0" - fast-deep-equal: "npm:^3.1.3" - memoizerific: "npm:^1.11.3" - checksum: 10/3225e5bdaba0ea76b65d642202d9712d7de234e3b5673fb46e444892ab114be207dd287778e2002b662ec35bb8153d2624ff280ce51c5299fb13c711431dad40 - languageName: node - linkType: hard - "storybook@npm:^8.4.2": version: 8.4.4 resolution: "storybook@npm:8.4.4" @@ -28432,13 +29661,6 @@ __metadata: languageName: node linkType: hard -"strip-json-comments@npm:5.0.1": - version: 5.0.1 - resolution: "strip-json-comments@npm:5.0.1" - checksum: 10/b314af70c6666a71133e309a571bdb87687fc878d9fd8b38ebed393a77b89835b92f191aa6b0bc10dfd028ba99eed6b6365985001d64c5aef32a4a82456a156b - languageName: node - linkType: hard - "strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" @@ -28459,6 +29681,16 @@ __metadata: languageName: node linkType: hard +"strtok3@npm:^6.2.4": + version: 6.3.0 + resolution: "strtok3@npm:6.3.0" + dependencies: + "@tokenizer/token": "npm:^0.3.0" + peek-readable: "npm:^4.1.0" + checksum: 10/98fba564d3830202aa3a6bcd5ccaf2cbd849bd87ae79ece91d337e1913916705a8e633c9577138d030a984f8ec987dea51807e01252f995cf5e183fdea35eb2b + languageName: node + linkType: hard + "style-loader@npm:4.0.0": version: 4.0.0 resolution: "style-loader@npm:4.0.0" @@ -28525,9 +29757,9 @@ __metadata: languageName: node linkType: hard -"stylelint@npm:16.12.0, stylelint@npm:^16.8.2": - version: 16.12.0 - resolution: "stylelint@npm:16.12.0" +"stylelint@npm:16.14.1, stylelint@npm:^16.8.2": + version: 16.14.1 + resolution: "stylelint@npm:16.14.1" dependencies: "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" @@ -28538,16 +29770,16 @@ __metadata: colord: "npm:^2.9.3" cosmiconfig: "npm:^9.0.0" css-functions-list: "npm:^3.2.3" - css-tree: "npm:^3.0.1" + css-tree: "npm:^3.1.0" debug: "npm:^4.3.7" - fast-glob: "npm:^3.3.2" + fast-glob: "npm:^3.3.3" fastest-levenshtein: "npm:^1.0.16" - file-entry-cache: "npm:^9.1.0" + file-entry-cache: "npm:^10.0.5" global-modules: "npm:^2.0.0" globby: "npm:^11.1.0" globjoin: "npm:^0.1.4" html-tags: "npm:^3.3.1" - ignore: "npm:^6.0.2" + ignore: "npm:^7.0.3" imurmurhash: "npm:^0.1.4" is-plain-object: "npm:^5.0.0" known-css-properties: "npm:^0.35.0" @@ -28556,7 +29788,7 @@ __metadata: micromatch: "npm:^4.0.8" normalize-path: "npm:^3.0.0" picocolors: "npm:^1.1.1" - postcss: "npm:^8.4.49" + postcss: "npm:^8.5.1" postcss-resolve-nested-selector: "npm:^0.1.6" postcss-safe-parser: "npm:^7.0.1" postcss-selector-parser: "npm:^7.0.0" @@ -28569,7 +29801,7 @@ __metadata: write-file-atomic: "npm:^5.0.1" bin: stylelint: bin/stylelint.mjs - checksum: 10/8ab174441f3909a79b84efe62b99061db42ef232262abf0af50ab2458822c39781886171912362eb018a293b70c33bd20b8ff7194ef64af4aee4541787aee2e9 + checksum: 10/2c0c95e5ee77d946efdbe58573da75ff4b12bc89c220e78b69f0c2f94251f5e5a1096f5ab583f28f56ae14f298b1335e212d52fe066719d63b8a41d6cc8083b6 languageName: node linkType: hard @@ -28587,13 +29819,6 @@ __metadata: languageName: node linkType: hard -"summary@npm:2.1.0": - version: 2.1.0 - resolution: "summary@npm:2.1.0" - checksum: 10/10ac12ce12c013b56ad44c37cfac206961f0993d98867b33b1b03a27b38a1cf8dd2db0b788883356c5335bbbb37d953772ef4a381d6fc8f408faf99f2bc54af5 - languageName: node - linkType: hard - "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -28676,35 +29901,35 @@ __metadata: languageName: node linkType: hard -"swagger-client@npm:^3.31.0": - version: 3.32.2 - resolution: "swagger-client@npm:3.32.2" +"swagger-client@npm:^3.34.0": + version: 3.34.0 + resolution: "swagger-client@npm:3.34.0" dependencies: "@babel/runtime-corejs3": "npm:^7.22.15" "@scarf/scarf": "npm:=1.4.0" - "@swagger-api/apidom-core": "npm:>=1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-error": "npm:>=1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-json-pointer": "npm:>=1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-ns-openapi-3-1": "npm:>=1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-reference": "npm:>=1.0.0-beta.3 <1.0.0-rc.0" - cookie: "npm:~0.7.2" + "@swagger-api/apidom-core": "npm:>=1.0.0-beta.11 <1.0.0-rc.0" + "@swagger-api/apidom-error": "npm:>=1.0.0-beta.11 <1.0.0-rc.0" + "@swagger-api/apidom-json-pointer": "npm:>=1.0.0-beta.11 <1.0.0-rc.0" + "@swagger-api/apidom-ns-openapi-3-1": "npm:>=1.0.0-beta.11 <1.0.0-rc.0" + "@swagger-api/apidom-reference": "npm:>=1.0.0-beta.11 <1.0.0-rc.0" + "@swaggerexpert/cookie": "npm:^1.4.1" deepmerge: "npm:~4.3.0" fast-json-patch: "npm:^3.0.0-1" js-yaml: "npm:^4.1.0" neotraverse: "npm:=0.6.18" node-abort-controller: "npm:^3.1.1" node-fetch-commonjs: "npm:^3.3.2" - openapi-path-templating: "npm:^1.5.1" - openapi-server-url-templating: "npm:^1.0.0" + openapi-path-templating: "npm:^2.0.1" + openapi-server-url-templating: "npm:^1.2.0" ramda: "npm:^0.30.1" ramda-adjunct: "npm:^5.0.0" - checksum: 10/651f8e6446b57c42de61684440e854f146c9e6a0d9eab28892d07a00bc0dd9e742205c285d4db2dea85d657c62da3a81a44cf417db2383753a75d69377877253 + checksum: 10/ed89c44ba172abb9cd6a36a189cf2810ef9b1983cb71657772ede94ea804105b07549b90ba36bbead87e4ac71e609b51f0977d9f9c90112952756c7f4f8bde82 languageName: node linkType: hard -"swagger-ui-react@npm:5.18.2": - version: 5.18.2 - resolution: "swagger-ui-react@npm:5.18.2" +"swagger-ui-react@npm:5.18.3": + version: 5.18.3 + resolution: "swagger-ui-react@npm:5.18.3" dependencies: "@babel/runtime-corejs3": "npm:^7.24.7" "@braintree/sanitize-url": "npm:=7.0.4" @@ -28735,7 +29960,7 @@ __metadata: reselect: "npm:^5.1.1" serialize-error: "npm:^8.1.0" sha.js: "npm:^2.4.11" - swagger-client: "npm:^3.31.0" + swagger-client: "npm:^3.34.0" url-parse: "npm:^1.5.10" xml: "npm:=1.0.1" xml-but-prettier: "npm:^1.0.1" @@ -28743,7 +29968,7 @@ __metadata: peerDependencies: react: ">=16.8.0 <19" react-dom: ">=16.8.0 <19" - checksum: 10/04d4bd8e67ac19472d7915ed779933108b660babd14740c74efa33c76a6bd17e94ebcabd4449e365da3527937f3c60f53c1c392c3e13e5ac5f2e984e6cdcee42 + checksum: 10/4437fc3fd9a869ab3ef357c78137e20fc9d96dbc71332e9ac53b45decd41382c9467d610ebf0c15a628485c67475a8f89c37882ca548305d633e2dd4b632db5f languageName: node linkType: hard @@ -29065,14 +30290,14 @@ __metadata: languageName: node linkType: hard -"tiny-warning@npm:^1.0.0, tiny-warning@npm:^1.0.3": +"tiny-warning@npm:^1.0.0": version: 1.0.3 resolution: "tiny-warning@npm:1.0.3" checksum: 10/da62c4acac565902f0624b123eed6dd3509bc9a8d30c06e017104bedcf5d35810da8ff72864400ad19c5c7806fc0a8323c68baf3e326af7cb7d969f846100d71 languageName: node linkType: hard -"tinycolor2@npm:1.6.0": +"tinycolor2@npm:1.6.0, tinycolor2@npm:^1.6.0": version: 1.6.0 resolution: "tinycolor2@npm:1.6.0" checksum: 10/066c3acf4f82b81c58a0d3ab85f49407efe95ba87afc3c7a16b1d77625193dfbe10dd46c26d0a263c1137361dd5a6a68bff2fb71def5fb9b9aec940fb030bcd4 @@ -29170,6 +30395,16 @@ __metadata: languageName: node linkType: hard +"token-types@npm:^4.1.1": + version: 4.2.1 + resolution: "token-types@npm:4.2.1" + dependencies: + "@tokenizer/token": "npm:^0.3.0" + ieee754: "npm:^1.2.1" + checksum: 10/2995257d246387e773758c3c92a3cc99d0c0bf13cbafe0de5d712e4c35ed298da6704e21545cb123fa1f1b42ad62936c35bbd0611018b735e78c30b8b22b42d9 + languageName: node + linkType: hard + "totalist@npm:^3.0.0": version: 3.0.1 resolution: "totalist@npm:3.0.1" @@ -29262,12 +30497,12 @@ __metadata: languageName: node linkType: hard -"ts-api-utils@npm:^1.3.0": - version: 1.3.0 - resolution: "ts-api-utils@npm:1.3.0" +"ts-api-utils@npm:^2.0.0": + version: 2.0.0 + resolution: "ts-api-utils@npm:2.0.0" peerDependencies: - typescript: ">=4.2.0" - checksum: 10/3ee44faa24410cd649b5c864e068d438aa437ef64e9e4a66a41646a6d3024d3097a695eeb3fb26ee364705d3cb9653a65756d009e6a53badb6066a5f447bf7ed + typescript: ">=4.8.4" + checksum: 10/485bdf8bbba98d58712243d958f4fd44742bbe49e559cd77882fb426d866eec6dd05c67ef91935dc4f8a3c776f235859735e1f05be399e4dc9e7ffd580120974 languageName: node linkType: hard @@ -29427,6 +30662,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:2.7.0": + version: 2.7.0 + resolution: "tslib@npm:2.7.0" + checksum: 10/9a5b47ddac65874fa011c20ff76db69f97cf90c78cff5934799ab8894a5342db2d17b4e7613a087046bc1d133d21547ddff87ac558abeec31ffa929c88b7fce6 + languageName: node + linkType: hard + "tslib@npm:^1.10.0, tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" @@ -29559,9 +30801,9 @@ __metadata: linkType: hard "type-fest@npm:^4.18.2, type-fest@npm:^4.26.1": - version: 4.30.2 - resolution: "type-fest@npm:4.30.2" - checksum: 10/c5168b159c366e4fd5b74c7f7b786bed9248c03f67e6e07d52dd5d51354447468fa7c92b9f2142c7fe9279814031f783959370242c3520de848931b65ddb48bb + version: 4.33.0 + resolution: "type-fest@npm:4.33.0" + checksum: 10/0d179e66fa765bd0a25a785b12dc797f90f2f92bdb8c9c8a789f3fd8e5a4492444e7ef83551b3b8463aeab24fd6195761e26b03174722de636b4b75aa5726fb7 languageName: node linkType: hard @@ -29875,6 +31117,16 @@ __metadata: languageName: node linkType: hard +"unplugin-utils@npm:^0.2.3": + version: 0.2.3 + resolution: "unplugin-utils@npm:0.2.3" + dependencies: + pathe: "npm:^2.0.2" + picomatch: "npm:^4.0.2" + checksum: 10/95a18001b4a3aa92e58259162ee70be74f49780bff7aca0272035b206143772d07f3b117922ec9d7caaed8bd24578afcf7baa4330943796ea9f011dcc96af1d9 + languageName: node + linkType: hard + "unplugin@npm:^1.3.1": version: 1.5.0 resolution: "unplugin@npm:1.5.0" @@ -29965,7 +31217,7 @@ __metadata: languageName: node linkType: hard -"use-isomorphic-layout-effect@npm:^1.2.0": +"use-isomorphic-layout-effect@npm:^1.1.2, use-isomorphic-layout-effect@npm:^1.2.0": version: 1.2.0 resolution: "use-isomorphic-layout-effect@npm:1.2.0" peerDependencies: @@ -29995,6 +31247,15 @@ __metadata: languageName: node linkType: hard +"utif2@npm:^4.1.0": + version: 4.1.0 + resolution: "utif2@npm:4.1.0" + dependencies: + pako: "npm:^1.0.11" + checksum: 10/d2cf11cf13e1ad2f7de3f0dc573a951352e8363866d611f15f6fef9f9e8bf2b189a346176e7c072b870ebe5f86bb21acc92461e94e2b03d41d91d9b42c6d0be2 + languageName: node + linkType: hard + "util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" @@ -30038,7 +31299,7 @@ __metadata: languageName: node linkType: hard -"uuid@npm:11.0.5, uuid@npm:^11.0.2": +"uuid@npm:11.0.5, uuid@npm:^11.0.0, uuid@npm:^11.0.2": version: 11.0.5 resolution: "uuid@npm:11.0.5" bin: @@ -30047,6 +31308,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:9.0.1, uuid@npm:^9.0.0": + version: 9.0.1 + resolution: "uuid@npm:9.0.1" + bin: + uuid: dist/bin/uuid + checksum: 10/9d0b6adb72b736e36f2b1b53da0d559125ba3e39d913b6072f6f033e0c87835b414f0836b45bcfaf2bdf698f92297fea1c3cc19b0b258bc182c9c43cc0fab9f2 + languageName: node + linkType: hard + "uuid@npm:^10.0.0": version: 10.0.0 resolution: "uuid@npm:10.0.0" @@ -30065,15 +31335,6 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^9.0.0": - version: 9.0.1 - resolution: "uuid@npm:9.0.1" - bin: - uuid: dist/bin/uuid - checksum: 10/9d0b6adb72b736e36f2b1b53da0d559125ba3e39d913b6072f6f033e0c87835b414f0836b45bcfaf2bdf698f92297fea1c3cc19b0b258bc182c9c43cc0fab9f2 - languageName: node - linkType: hard - "v8-compile-cache-lib@npm:^3.0.1": version: 3.0.1 resolution: "v8-compile-cache-lib@npm:3.0.1" @@ -31020,15 +32281,6 @@ __metadata: languageName: node linkType: hard -"xlsx@https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz": - version: 0.20.2 - resolution: "xlsx@https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" - bin: - xlsx: ./bin/xlsx.njs - checksum: 10/2d8e0644888f90fa9145ea74ed90b844154ce89c4f0e4f92fcce3f224fa71654da99aa48d99d65ba86eb0632a4858ba2dea7eef8b54fd8bd23954a09d1884aa1 - languageName: node - linkType: hard - "xml-but-prettier@npm:^1.0.1": version: 1.0.1 resolution: "xml-but-prettier@npm:1.0.1" @@ -31045,6 +32297,13 @@ __metadata: languageName: node linkType: hard +"xml-parse-from-string@npm:^1.0.0": + version: 1.0.1 + resolution: "xml-parse-from-string@npm:1.0.1" + checksum: 10/628eda047d93bed85165b2605d68bd86a18cab2d362ed29553ee0d4124cec348ffa6dfb0f73361f46329ce9ee1079bb152af49caf1b5f694232c554a8d5daaa4 + languageName: node + linkType: hard + "xml-utils@npm:^1.0.2": version: 1.0.2 resolution: "xml-utils@npm:1.0.2" @@ -31052,6 +32311,16 @@ __metadata: languageName: node linkType: hard +"xml2js@npm:^0.5.0": + version: 0.5.0 + resolution: "xml2js@npm:0.5.0" + dependencies: + sax: "npm:>=0.6.0" + xmlbuilder: "npm:~11.0.0" + checksum: 10/27c4d759214e99be5ec87ee5cb1290add427fa43df509d3b92d10152b3806fd2f7c9609697a18b158ccf2caa01e96af067cdba93196f69ca10c90e4f79a08896 + languageName: node + linkType: hard + "xml@npm:=1.0.1, xml@npm:^1.0.1": version: 1.0.1 resolution: "xml@npm:1.0.1" @@ -31059,6 +32328,13 @@ __metadata: languageName: node linkType: hard +"xmlbuilder@npm:~11.0.0": + version: 11.0.1 + resolution: "xmlbuilder@npm:11.0.1" + checksum: 10/c8c3d208783718db5b285101a736cd8e6b69a5c265199a0739abaa93d1a1b7de5489fd16df4e776e18b2c98cb91f421a7349e99fd8c1ebeb44ecfed72a25091a + languageName: node + linkType: hard + "xmlchars@npm:^2.2.0": version: 2.2.0 resolution: "xmlchars@npm:2.2.0" @@ -31123,11 +32399,11 @@ __metadata: linkType: hard "yaml@npm:^2.0.0, yaml@npm:^2.3.4": - version: 2.6.1 - resolution: "yaml@npm:2.6.1" + version: 2.7.0 + resolution: "yaml@npm:2.7.0" bin: yaml: bin.mjs - checksum: 10/cf412f03a33886db0a3aac70bb4165588f4c5b3c6f8fc91520b71491e5537800b6c2c73ed52015617f6e191eb4644c73c92973960a1999779c62a200ee4c231d + checksum: 10/c8c314c62fbd49244a6a51b06482f6d495b37ab10fa685fcafa1bbaae7841b7233ee7d12cab087bcca5a0b28adc92868b6e437322276430c28d00f1c1732eeec languageName: node linkType: hard @@ -31220,19 +32496,10 @@ __metadata: languageName: node linkType: hard -"zod-validation-error@npm:^3.0.3": - version: 3.2.0 - resolution: "zod-validation-error@npm:3.2.0" - peerDependencies: - zod: ^3.18.0 - checksum: 10/2e2aec95e43cc34b741faf2863019be2558da5eb902b7eeb8bbf4c11c24c1b1f91e92e6f97077584b8301bc927061b9b2f1c0ede562f0bc350726c26870f75c1 - languageName: node - linkType: hard - -"zod@npm:^3.22.4, zod@npm:^3.23.8": - version: 3.23.8 - resolution: "zod@npm:3.23.8" - checksum: 10/846fd73e1af0def79c19d510ea9e4a795544a67d5b34b7e1c4d0425bf6bfd1c719446d94cdfa1721c1987d891321d61f779e8236fde517dc0e524aa851a6eff1 +"zod@npm:^3.23.8, zod@npm:^3.24.1": + version: 3.24.1 + resolution: "zod@npm:3.24.1" + checksum: 10/54e25956495dec22acb9399c168c6ba657ff279801a7fcd0530c414d867f1dcca279335e160af9b138dd70c332e17d548be4bc4d2f7eaf627dead50d914fec27 languageName: node linkType: hard