Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9f164d9f2 | |||
| 86fc051c58 |
@@ -89,7 +89,7 @@ require (
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 // @grafana/grafana-backend-group
|
||||
github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // @grafana/grafana-app-platform-squad
|
||||
github.com/grafana/alerting v0.0.0-20260114220314-c56cf1580dcf // @grafana/alerting-backend
|
||||
github.com/grafana/alerting v0.0.0-20260112172717-98a49ed9557f // @grafana/alerting-backend
|
||||
github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f // @grafana/identity-access-team
|
||||
github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 // @grafana/identity-access-team
|
||||
github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics
|
||||
|
||||
@@ -1627,8 +1627,6 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grafana/alerting v0.0.0-20260112172717-98a49ed9557f h1:3bXOyht68qkfvD6Y8z8XoenFbytSSOIkr/s+AqRzj0o=
|
||||
github.com/grafana/alerting v0.0.0-20260112172717-98a49ed9557f/go.mod h1:Ji0SfJChcwjgq8ljy6Y5CcYfHfAYKXjKYeysOoDS/6s=
|
||||
github.com/grafana/alerting v0.0.0-20260114220314-c56cf1580dcf h1:3q+bASuUgOPd3AVcmfaZCvqZjf7lhB6Oydt8OdShcy8=
|
||||
github.com/grafana/alerting v0.0.0-20260114220314-c56cf1580dcf/go.mod h1:Ji0SfJChcwjgq8ljy6Y5CcYfHfAYKXjKYeysOoDS/6s=
|
||||
github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f h1:Cbm6OKkOcJ+7CSZsGsEJzktC/SIa5bxVeYKQLuYK86o=
|
||||
github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f/go.mod h1:axY0cdOg3q0TZHwpHnIz5x16xZ8ZBxJHShsSHHXcHQg=
|
||||
github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 h1:Muoy+FMGrHj3GdFbvsMzUT7eusgii9PKf9L1ZaXDDbY=
|
||||
|
||||
@@ -22,7 +22,7 @@ import { getBarColorByDiff, getBarColorByPackage, getBarColorByValue } from './c
|
||||
import { CollapseConfig, CollapsedMap, FlameGraphDataContainer, LevelItem } from './dataTransform';
|
||||
|
||||
type RenderOptions = {
|
||||
canvasRef: RefObject<HTMLCanvasElement>;
|
||||
canvasRef: RefObject<HTMLCanvasElement | null>;
|
||||
data: FlameGraphDataContainer;
|
||||
root: LevelItem;
|
||||
direction: 'children' | 'parents';
|
||||
@@ -373,7 +373,7 @@ function useColorFunction(
|
||||
);
|
||||
}
|
||||
|
||||
function useSetupCanvas(canvasRef: RefObject<HTMLCanvasElement>, wrapperWidth: number, numberOfLevels: number) {
|
||||
function useSetupCanvas(canvasRef: RefObject<HTMLCanvasElement | null>, wrapperWidth: number, numberOfLevels: number) {
|
||||
const [ctx, setCtx] = useState<CanvasRenderingContext2D>();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -7,7 +7,7 @@ const CAUGHT_KEYS = ['ArrowUp', 'ArrowDown', 'Home', 'End', 'Enter', 'Tab'];
|
||||
|
||||
/** @internal */
|
||||
export interface UseListFocusProps {
|
||||
localRef: RefObject<HTMLUListElement>;
|
||||
localRef: RefObject<HTMLUListElement | null>;
|
||||
options: TimeOption[];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { RefObject, useRef } from 'react';
|
||||
|
||||
export function useFocus(): [RefObject<HTMLInputElement>, () => void] {
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
export function useFocus(): [RefObject<HTMLInputElement | null>, () => void] {
|
||||
const ref = useRef<HTMLInputElement | null>(null);
|
||||
const setFocus = () => {
|
||||
ref.current && ref.current.focus();
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ const UNFOCUSED = -1;
|
||||
|
||||
/** @internal */
|
||||
export interface UseMenuFocusProps {
|
||||
localRef: RefObject<HTMLDivElement>;
|
||||
localRef: RefObject<HTMLDivElement | null>;
|
||||
isMenuOpen?: boolean;
|
||||
close?: () => void;
|
||||
onOpen?: (focusOnItem: (itemId: number) => void) => void;
|
||||
|
||||
@@ -22,7 +22,7 @@ interface Props extends Omit<BoxProps, 'display' | 'direction' | 'element' | 'fl
|
||||
*
|
||||
* https://developers.grafana.com/ui/latest/index.html?path=/docs/layout-scrollcontainer--docs
|
||||
*/
|
||||
export const ScrollContainer = forwardRef<HTMLDivElement, PropsWithChildren<Props>>(
|
||||
export const ScrollContainer = forwardRef<HTMLDivElement | null, PropsWithChildren<Props>>(
|
||||
(
|
||||
{
|
||||
children,
|
||||
|
||||
@@ -20,7 +20,7 @@ export interface TableCellTooltipProps {
|
||||
field: Field;
|
||||
getActions: (field: Field, rowIdx: number) => ActionModel[];
|
||||
getTextColorForBackground: (bgColor: string) => string;
|
||||
gridRef: RefObject<DataGridHandle>;
|
||||
gridRef: RefObject<DataGridHandle | null>;
|
||||
height: number;
|
||||
placement?: TableCellTooltipPlacement;
|
||||
renderer: TableCellRenderer;
|
||||
|
||||
@@ -463,7 +463,7 @@ export function useColumnResize(
|
||||
return dataGridResizeHandler;
|
||||
}
|
||||
|
||||
export function useScrollbarWidth(ref: RefObject<DataGridHandle>, height: number) {
|
||||
export function useScrollbarWidth(ref: RefObject<DataGridHandle | null>, height: number) {
|
||||
const [scrollbarWidth, setScrollbarWidth] = useState(0);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
|
||||
@@ -135,7 +135,7 @@ export const Table = memo((props: Props) => {
|
||||
// `useTableStateReducer`, which is needed to construct options for `useTable` (the hook that returns
|
||||
// `toggleAllRowsExpanded`), and if we used a variable, that variable would be undefined at the time
|
||||
// we initialize `useTableStateReducer`.
|
||||
const toggleAllRowsExpandedRef = useRef<(value?: boolean) => void>();
|
||||
const toggleAllRowsExpandedRef = useRef<((value?: boolean) => void) | null>(null);
|
||||
|
||||
// Internal react table state reducer
|
||||
const stateReducer = useTableStateReducer({
|
||||
|
||||
@@ -14,8 +14,8 @@ import { GrafanaTableState } from './types';
|
||||
Select the scrollbar element from the VariableSizeList scope
|
||||
*/
|
||||
export function useFixScrollbarContainer(
|
||||
variableSizeListScrollbarRef: React.RefObject<HTMLDivElement>,
|
||||
tableDivRef: React.RefObject<HTMLDivElement>
|
||||
variableSizeListScrollbarRef: React.RefObject<HTMLDivElement | null>,
|
||||
tableDivRef: React.RefObject<HTMLDivElement | null>
|
||||
) {
|
||||
useEffect(() => {
|
||||
if (variableSizeListScrollbarRef.current && tableDivRef.current) {
|
||||
@@ -43,7 +43,7 @@ export function useFixScrollbarContainer(
|
||||
*/
|
||||
export function useResetVariableListSizeCache(
|
||||
extendedState: GrafanaTableState,
|
||||
listRef: React.RefObject<VariableSizeList>,
|
||||
listRef: React.RefObject<VariableSizeList | null>,
|
||||
data: DataFrame,
|
||||
hasUniqueId: boolean
|
||||
) {
|
||||
|
||||
@@ -19,7 +19,7 @@ interface EventsCanvasProps {
|
||||
}
|
||||
|
||||
export function EventsCanvas({ id, events, renderEventMarker, mapEventToXYCoords, config }: EventsCanvasProps) {
|
||||
const plotInstance = useRef<uPlot>();
|
||||
const plotInstance = useRef<uPlot | null>(null);
|
||||
// render token required to re-render annotation markers. Rendering lines happens in uPlot and the props do not change
|
||||
// so we need to force the re-render when the draw hook was performed by uPlot
|
||||
const [renderToken, setRenderToken] = useState(0);
|
||||
|
||||
@@ -140,7 +140,7 @@ export const TooltipPlugin2 = ({
|
||||
|
||||
const [{ plot, isHovering, isPinned, contents, style, dismiss }, setState] = useReducer(mergeState, null, initState);
|
||||
|
||||
const sizeRef = useRef<TooltipContainerSize>();
|
||||
const sizeRef = useRef<TooltipContainerSize | null>(null);
|
||||
const styles = useStyles2(getStyles, maxWidth);
|
||||
|
||||
const renderRef = useRef(render);
|
||||
|
||||
@@ -96,7 +96,7 @@ export interface GraphNGState {
|
||||
export class GraphNG extends Component<GraphNGProps, GraphNGState> {
|
||||
static contextType = PanelContextRoot;
|
||||
panelContext: PanelContext = {} as PanelContext;
|
||||
private plotInstance: React.RefObject<uPlot>;
|
||||
private plotInstance: React.RefObject<uPlot | null>;
|
||||
|
||||
private subscription = new Subscription();
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package definitions
|
||||
|
||||
import (
|
||||
"github.com/grafana/alerting/definition"
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
)
|
||||
|
||||
@@ -118,9 +117,8 @@ type MuteTimingHeaders struct {
|
||||
type MuteTimeInterval struct {
|
||||
UID string `json:"-" yaml:"-"`
|
||||
config.MuteTimeInterval `json:",inline" yaml:",inline"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Provenance Provenance `json:"provenance,omitempty"`
|
||||
Kind definition.TimeIntervalKind `json:"-" yaml:"-"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Provenance Provenance `json:"provenance,omitempty"`
|
||||
}
|
||||
|
||||
func (mt *MuteTimeInterval) ResourceType() string {
|
||||
|
||||
@@ -434,8 +434,8 @@ func (ng *AlertNG) init() error {
|
||||
// Provisioning
|
||||
policyService := provisioning.NewNotificationPolicyService(configStore, ng.store, ng.store, ng.Cfg.UnifiedAlerting, ng.Log)
|
||||
contactPointService := provisioning.NewContactPointService(configStore, ng.SecretsService, ng.store, ng.store, provisioningReceiverService, ng.Log, ng.store, ng.ResourcePermissions)
|
||||
templateService := provisioning.NewTemplateService(configStore, ng.store, ng.store, ng.Log).WithIncludeImported()
|
||||
muteTimingService := provisioning.NewMuteTimingService(configStore, ng.store, ng.store, ng.Log, ng.store).WithIncludeImported()
|
||||
templateService := provisioning.NewTemplateService(configStore, ng.store, ng.store, ng.Log)
|
||||
muteTimingService := provisioning.NewMuteTimingService(configStore, ng.store, ng.store, ng.Log, ng.store)
|
||||
alertRuleService := provisioning.NewAlertRuleService(ng.store, ng.store, ng.folderService, ng.QuotaService, ng.store,
|
||||
int64(ng.Cfg.UnifiedAlerting.DefaultRuleEvaluationInterval.Seconds()),
|
||||
int64(ng.Cfg.UnifiedAlerting.BaseInterval.Seconds()),
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/grafana/alerting/definition"
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
"github.com/prometheus/alertmanager/timeinterval"
|
||||
"golang.org/x/exp/maps"
|
||||
@@ -28,7 +27,6 @@ type MuteTimingService struct {
|
||||
log log.Logger
|
||||
validator validation.ProvenanceStatusTransitionValidator
|
||||
ruleNotificationsStore AlertRuleNotificationSettingsStore
|
||||
includeImported bool
|
||||
}
|
||||
|
||||
func NewMuteTimingService(config alertmanagerConfigStore, prov ProvisioningStore, xact TransactionManager, log log.Logger, ns AlertRuleNotificationSettingsStore) *MuteTimingService {
|
||||
@@ -39,19 +37,6 @@ func NewMuteTimingService(config alertmanagerConfigStore, prov ProvisioningStore
|
||||
log: log,
|
||||
validator: validation.ValidateProvenanceRelaxed,
|
||||
ruleNotificationsStore: ns,
|
||||
includeImported: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *MuteTimingService) WithIncludeImported() *MuteTimingService {
|
||||
return &MuteTimingService{
|
||||
configStore: svc.configStore,
|
||||
provenanceStore: svc.provenanceStore,
|
||||
xact: svc.xact,
|
||||
log: svc.log,
|
||||
validator: svc.validator,
|
||||
ruleNotificationsStore: svc.ruleNotificationsStore,
|
||||
includeImported: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,15 +47,9 @@ func (svc *MuteTimingService) GetMuteTimings(ctx context.Context, orgID int64) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get Grafana intervals
|
||||
grafanaIntervals := make([]config.MuteTimeInterval, 0, len(rev.Config.AlertmanagerConfig.TimeIntervals)+len(rev.Config.AlertmanagerConfig.MuteTimeIntervals))
|
||||
for _, interval := range rev.Config.AlertmanagerConfig.TimeIntervals {
|
||||
grafanaIntervals = append(grafanaIntervals, config.MuteTimeInterval(interval))
|
||||
}
|
||||
grafanaIntervals = append(grafanaIntervals, rev.Config.AlertmanagerConfig.MuteTimeIntervals...)
|
||||
intervals := getTimeIntervals(rev)
|
||||
|
||||
// Early return if no intervals (including imported)
|
||||
if len(grafanaIntervals) == 0 && (!svc.includeImported || len(rev.Config.ExtraConfigs) == 0) {
|
||||
if len(intervals) == 0 {
|
||||
return []definitions.MuteTimeInterval{}, nil
|
||||
}
|
||||
|
||||
@@ -79,51 +58,22 @@ func (svc *MuteTimingService) GetMuteTimings(ctx context.Context, orgID int64) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]definitions.MuteTimeInterval, 0, len(grafanaIntervals))
|
||||
for _, interval := range grafanaIntervals {
|
||||
slices.SortFunc(intervals, func(a, b config.MuteTimeInterval) int {
|
||||
return strings.Compare(a.Name, b.Name)
|
||||
})
|
||||
result := make([]definitions.MuteTimeInterval, 0, len(intervals))
|
||||
for _, interval := range intervals {
|
||||
version := calculateMuteTimeIntervalFingerprint(interval)
|
||||
def := definitions.MuteTimeInterval{
|
||||
UID: timeIntervalUID(string(definition.GrafanaTimeIntervalKind), interval.Name),
|
||||
UID: legacy_storage.NameToUid(interval.Name),
|
||||
MuteTimeInterval: interval,
|
||||
Version: version,
|
||||
Kind: definition.GrafanaTimeIntervalKind,
|
||||
}
|
||||
if prov, ok := provenances[def.ResourceID()]; ok {
|
||||
def.Provenance = definitions.Provenance(prov)
|
||||
}
|
||||
result = append(result, def)
|
||||
}
|
||||
|
||||
// Add imported Mimir intervals if requested
|
||||
if svc.includeImported && len(rev.Config.ExtraConfigs) > 0 {
|
||||
mimirCfg := rev.Config.ExtraConfigs[0]
|
||||
cfg, err := mimirCfg.GetAlertmanagerConfig()
|
||||
if err == nil {
|
||||
mimirIntervals := make([]config.MuteTimeInterval, 0, len(cfg.GetMuteTimeIntervals())+len(cfg.GetTimeIntervals()))
|
||||
mimirIntervals = append(mimirIntervals, cfg.GetMuteTimeIntervals()...)
|
||||
for _, ti := range cfg.GetTimeIntervals() {
|
||||
mimirIntervals = append(mimirIntervals, config.MuteTimeInterval(ti))
|
||||
}
|
||||
|
||||
for _, interval := range mimirIntervals {
|
||||
version := calculateMuteTimeIntervalFingerprint(interval)
|
||||
def := definitions.MuteTimeInterval{
|
||||
UID: timeIntervalUID(string(definition.MimirTimeIntervalKind), interval.Name),
|
||||
MuteTimeInterval: interval,
|
||||
Version: version,
|
||||
Kind: definition.MimirTimeIntervalKind,
|
||||
Provenance: definitions.Provenance(models.ProvenanceConvertedPrometheus),
|
||||
}
|
||||
result = append(result, def)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by name
|
||||
slices.SortFunc(result, func(a, b definitions.MuteTimeInterval) int {
|
||||
return strings.Compare(a.Name, b.Name)
|
||||
})
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -134,71 +84,28 @@ func (svc *MuteTimingService) GetMuteTiming(ctx context.Context, nameOrUID strin
|
||||
return definitions.MuteTimeInterval{}, err
|
||||
}
|
||||
|
||||
// Try to find in Grafana intervals first
|
||||
mt, found := getMuteTimingByName(rev, nameOrUID)
|
||||
kind := definition.GrafanaTimeIntervalKind
|
||||
provenance := models.ProvenanceNone
|
||||
|
||||
// If not found by name, try UID lookup in Grafana intervals
|
||||
if !found {
|
||||
grafanaIntervals := getTimeIntervals(rev, false)
|
||||
for _, interval := range grafanaIntervals {
|
||||
if timeIntervalUID(string(definition.GrafanaTimeIntervalKind), interval.Name) == nameOrUID {
|
||||
mt = interval
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If not found in Grafana and includeImported, try Mimir (by name or UID)
|
||||
if !found && svc.includeImported && len(rev.Config.ExtraConfigs) > 0 {
|
||||
mimirCfg := rev.Config.ExtraConfigs[0]
|
||||
cfg, err := mimirCfg.GetAlertmanagerConfig()
|
||||
name, err := legacy_storage.UidToName(nameOrUID)
|
||||
if err == nil {
|
||||
// Collect all Mimir intervals
|
||||
mimirIntervals := make([]config.MuteTimeInterval, 0, len(cfg.GetMuteTimeIntervals())+len(cfg.GetTimeIntervals()))
|
||||
mimirIntervals = append(mimirIntervals, cfg.GetMuteTimeIntervals()...)
|
||||
for _, ti := range cfg.GetTimeIntervals() {
|
||||
mimirIntervals = append(mimirIntervals, config.MuteTimeInterval(ti))
|
||||
}
|
||||
|
||||
for _, interval := range mimirIntervals {
|
||||
// Check by name or UID
|
||||
if interval.Name == nameOrUID ||
|
||||
timeIntervalUID(string(definition.MimirTimeIntervalKind), interval.Name) == nameOrUID {
|
||||
mt = interval
|
||||
found = true
|
||||
kind = definition.MimirTimeIntervalKind
|
||||
provenance = models.ProvenanceConvertedPrometheus
|
||||
break
|
||||
}
|
||||
}
|
||||
mt, found = getMuteTimingByName(rev, name)
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return definitions.MuteTimeInterval{}, ErrTimeIntervalNotFound.Errorf("")
|
||||
}
|
||||
|
||||
result := definitions.MuteTimeInterval{
|
||||
UID: timeIntervalUID(string(kind), mt.Name),
|
||||
UID: legacy_storage.NameToUid(mt.Name),
|
||||
MuteTimeInterval: mt,
|
||||
Version: calculateMuteTimeIntervalFingerprint(mt),
|
||||
Kind: kind,
|
||||
}
|
||||
|
||||
// Only check provenance store for Grafana intervals
|
||||
if kind == definition.GrafanaTimeIntervalKind {
|
||||
prov, err := svc.provenanceStore.GetProvenance(ctx, &result, orgID)
|
||||
if err != nil {
|
||||
return definitions.MuteTimeInterval{}, err
|
||||
}
|
||||
result.Provenance = definitions.Provenance(prov)
|
||||
} else {
|
||||
result.Provenance = definitions.Provenance(provenance)
|
||||
prov, err := svc.provenanceStore.GetProvenance(ctx, &result, orgID)
|
||||
if err != nil {
|
||||
return definitions.MuteTimeInterval{}, err
|
||||
}
|
||||
|
||||
result.Provenance = definitions.Provenance(prov)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -208,11 +115,6 @@ func (svc *MuteTimingService) CreateMuteTiming(ctx context.Context, mt definitio
|
||||
return definitions.MuteTimeInterval{}, MakeErrTimeIntervalInvalid(err)
|
||||
}
|
||||
|
||||
// Block creation of Mimir kind intervals
|
||||
if mt.Kind == definition.MimirTimeIntervalKind {
|
||||
return definitions.MuteTimeInterval{}, MakeErrTimeIntervalInvalid(fmt.Errorf("time intervals of kind 'mimir' cannot be created"))
|
||||
}
|
||||
|
||||
revision, err := svc.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return definitions.MuteTimeInterval{}, err
|
||||
@@ -266,16 +168,6 @@ func (svc *MuteTimingService) UpdateMuteTiming(ctx context.Context, mt definitio
|
||||
return definitions.MuteTimeInterval{}, ErrTimeIntervalNotFound.Errorf("")
|
||||
}
|
||||
|
||||
// Block updates to imported intervals
|
||||
existingMT := definitions.MuteTimeInterval{MuteTimeInterval: old}
|
||||
prov, err := svc.provenanceStore.GetProvenance(ctx, &existingMT, orgID)
|
||||
if err != nil {
|
||||
return definitions.MuteTimeInterval{}, err
|
||||
}
|
||||
if prov == models.ProvenanceConvertedPrometheus {
|
||||
return definitions.MuteTimeInterval{}, MakeErrTimeIntervalInvalid(fmt.Errorf("cannot update imported time interval from mimir/prometheus"))
|
||||
}
|
||||
|
||||
// check optimistic concurrency
|
||||
err = svc.checkOptimisticConcurrency(old, models.Provenance(mt.Provenance), mt.Version, "update")
|
||||
if err != nil {
|
||||
@@ -351,12 +243,6 @@ func (svc *MuteTimingService) DeleteMuteTiming(ctx context.Context, nameOrUID st
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Block deletes of imported intervals
|
||||
if storedProvenance == models.ProvenanceConvertedPrometheus {
|
||||
return MakeErrTimeIntervalInvalid(fmt.Errorf("cannot delete imported time interval from mimir/prometheus"))
|
||||
}
|
||||
|
||||
if err := svc.validator(storedProvenance, models.Provenance(provenance)); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -410,7 +296,7 @@ func isTimeIntervalInUseInRoutes(name string, route *definitions.Route) bool {
|
||||
}
|
||||
|
||||
func getMuteTimingByName(rev *legacy_storage.ConfigRevision, name string) (config.MuteTimeInterval, bool) {
|
||||
intervals := getTimeIntervals(rev, false)
|
||||
intervals := getTimeIntervals(rev)
|
||||
idx := slices.IndexFunc(intervals, func(interval config.MuteTimeInterval) bool {
|
||||
return interval.Name == name
|
||||
})
|
||||
@@ -420,26 +306,12 @@ func getMuteTimingByName(rev *legacy_storage.ConfigRevision, name string) (confi
|
||||
return intervals[idx], true
|
||||
}
|
||||
|
||||
func getTimeIntervals(rev *legacy_storage.ConfigRevision, includeImported bool) []config.MuteTimeInterval {
|
||||
func getTimeIntervals(rev *legacy_storage.ConfigRevision) []config.MuteTimeInterval {
|
||||
result := make([]config.MuteTimeInterval, 0, len(rev.Config.AlertmanagerConfig.TimeIntervals)+len(rev.Config.AlertmanagerConfig.MuteTimeIntervals))
|
||||
for _, interval := range rev.Config.AlertmanagerConfig.TimeIntervals {
|
||||
result = append(result, config.MuteTimeInterval(interval))
|
||||
}
|
||||
result = append(result, rev.Config.AlertmanagerConfig.MuteTimeIntervals...)
|
||||
|
||||
// Add imported intervals from Mimir config
|
||||
if includeImported && len(rev.Config.ExtraConfigs) > 0 {
|
||||
mimirCfg := rev.Config.ExtraConfigs[0]
|
||||
cfg, err := mimirCfg.GetAlertmanagerConfig()
|
||||
if err == nil {
|
||||
result = append(result, cfg.GetMuteTimeIntervals()...)
|
||||
for _, ti := range cfg.GetTimeIntervals() {
|
||||
result = append(result, config.MuteTimeInterval(ti))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
return append(result, rev.Config.AlertmanagerConfig.MuteTimeIntervals...)
|
||||
}
|
||||
|
||||
func updateTimeInterval(rev *legacy_storage.ConfigRevision, interval config.MuteTimeInterval) {
|
||||
@@ -466,10 +338,6 @@ func deleteTimeInterval(rev *legacy_storage.ConfigRevision, interval config.Mute
|
||||
})
|
||||
}
|
||||
|
||||
func timeIntervalUID(kind string, name string) string {
|
||||
return legacy_storage.NameToUid(fmt.Sprintf("%s|%s", kind, name))
|
||||
}
|
||||
|
||||
func calculateMuteTimeIntervalFingerprint(interval config.MuteTimeInterval) string {
|
||||
sum := fnv.New64()
|
||||
|
||||
|
||||
@@ -66,18 +66,17 @@ func TestGetMuteTimings(t *testing.T) {
|
||||
require.Equal(t, "Test1", result[0].Name)
|
||||
require.EqualValues(t, provenances["Test1"], result[0].Provenance)
|
||||
require.NotEmpty(t, result[0].Version)
|
||||
// UID now includes kind prefix
|
||||
require.Equal(t, legacy_storage.NameToUid(fmt.Sprintf("grafana|%s", result[0].Name)), result[0].UID)
|
||||
require.Equal(t, legacy_storage.NameToUid(result[0].Name), result[0].UID)
|
||||
|
||||
require.Equal(t, "Test2", result[1].Name)
|
||||
require.EqualValues(t, provenances["Test2"], result[1].Provenance)
|
||||
require.NotEmpty(t, result[1].Version)
|
||||
require.Equal(t, legacy_storage.NameToUid(fmt.Sprintf("grafana|%s", result[1].Name)), result[1].UID)
|
||||
require.Equal(t, legacy_storage.NameToUid(result[1].Name), result[1].UID)
|
||||
|
||||
require.Equal(t, "Test3", result[2].Name)
|
||||
require.EqualValues(t, "", result[2].Provenance)
|
||||
require.NotEmpty(t, result[2].Version)
|
||||
require.Equal(t, legacy_storage.NameToUid(fmt.Sprintf("grafana|%s", result[2].Name)), result[2].UID)
|
||||
require.Equal(t, legacy_storage.NameToUid(result[2].Name), result[2].UID)
|
||||
|
||||
require.Len(t, store.Calls, 1)
|
||||
require.Equal(t, "Get", store.Calls[0].Method)
|
||||
@@ -162,8 +161,7 @@ func TestGetMuteTiming(t *testing.T) {
|
||||
|
||||
require.Equal(t, "Test1", result.Name)
|
||||
require.EqualValues(t, models.ProvenanceAPI, result.Provenance)
|
||||
// UID now includes kind prefix
|
||||
require.Equal(t, legacy_storage.NameToUid(fmt.Sprintf("grafana|%s", result.Name)), result.UID)
|
||||
require.Equal(t, legacy_storage.NameToUid(result.Name), result.UID)
|
||||
require.NotEmpty(t, result.Version)
|
||||
|
||||
require.Len(t, store.Calls, 1)
|
||||
@@ -194,8 +192,7 @@ func TestGetMuteTiming(t *testing.T) {
|
||||
|
||||
require.Equal(t, "Test2", result.Name)
|
||||
require.EqualValues(t, models.ProvenanceFile, result.Provenance)
|
||||
// UID now includes kind prefix
|
||||
require.Equal(t, legacy_storage.NameToUid(fmt.Sprintf("grafana|%s", result.Name)), result.UID)
|
||||
require.Equal(t, legacy_storage.NameToUid(result.Name), result.UID)
|
||||
require.NotEmpty(t, result.Version)
|
||||
|
||||
require.Len(t, store.Calls, 1)
|
||||
|
||||
@@ -109,7 +109,7 @@ const defaultMatchers = {
|
||||
* "Time as X" core component, expects ascending x
|
||||
*/
|
||||
export class GraphNG extends Component<GraphNGProps, GraphNGState> {
|
||||
private plotInstance: React.RefObject<uPlot>;
|
||||
private plotInstance: React.RefObject<uPlot | null>;
|
||||
|
||||
constructor(props: GraphNGProps) {
|
||||
super(props);
|
||||
|
||||
@@ -33,7 +33,7 @@ global.ResizeObserver = jest.fn().mockImplementation((callback) => {
|
||||
});
|
||||
|
||||
// Helper function to assign a mock div to a ref
|
||||
function assignMockDivToRef(ref: React.RefObject<HTMLDivElement>, mockDiv: HTMLDivElement) {
|
||||
function assignMockDivToRef(ref: React.RefObject<HTMLDivElement | null>, mockDiv: HTMLDivElement) {
|
||||
// Use type assertion to bypass readonly restriction in tests
|
||||
(ref as { current: HTMLDivElement | null }).current = mockDiv;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import grafanaTextLogoDarkSvg from 'img/grafana_text_logo_dark.svg';
|
||||
import grafanaTextLogoLightSvg from 'img/grafana_text_logo_light.svg';
|
||||
|
||||
interface SoloPanelPageLogoProps {
|
||||
containerRef: React.RefObject<HTMLDivElement>;
|
||||
containerRef: React.RefObject<HTMLDivElement | null>;
|
||||
isHovered: boolean;
|
||||
hideLogo?: UrlQueryValue;
|
||||
}
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ interface State {
|
||||
|
||||
class UnThemedTransformationsEditor extends React.PureComponent<TransformationsEditorProps, State> {
|
||||
subscription?: Unsubscribable;
|
||||
ref: RefObject<HTMLDivElement>;
|
||||
ref: RefObject<HTMLDivElement | null>;
|
||||
|
||||
constructor(props: TransformationsEditorProps) {
|
||||
super(props);
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ export const LibraryPanelsView = ({
|
||||
}
|
||||
);
|
||||
const asyncDispatch = useMemo(() => asyncDispatcher(dispatch), [dispatch]);
|
||||
const abortControllerRef = useRef<AbortController>();
|
||||
const abortControllerRef = useRef<AbortController | null>(null);
|
||||
|
||||
useDebounce(
|
||||
() => {
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface Props {}
|
||||
|
||||
export const LiveConnectionWarning = memo(function LiveConnectionWarning() {
|
||||
const [show, setShow] = useState<boolean | undefined>(undefined);
|
||||
const subscriptionRef = useRef<Unsubscribable>();
|
||||
const subscriptionRef = useRef<Unsubscribable | null>(null);
|
||||
const styles = useStyles2(getStyle);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -38,7 +38,7 @@ export function useSearchKeyboardNavigation(
|
||||
): ItemSelection {
|
||||
const highlightIndexRef = useRef<ItemSelection>({ x: 0, y: -1 });
|
||||
const [highlightIndex, setHighlightIndex] = useState<ItemSelection>({ x: 0, y: -1 });
|
||||
const urlsRef = useRef<Field>();
|
||||
const urlsRef = useRef<Field | null>(null);
|
||||
|
||||
// Clear selection when the search results change
|
||||
useEffect(() => {
|
||||
|
||||
@@ -77,7 +77,7 @@ export const SuggestionsInput = ({
|
||||
const theme = useTheme2();
|
||||
const styles = getStyles(theme, inputHeight);
|
||||
|
||||
const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>();
|
||||
const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
scrollRef.current?.scrollTo(0, scrollTop);
|
||||
|
||||
@@ -11,7 +11,7 @@ import checkboxWhitePng from 'img/checkbox_white.png';
|
||||
|
||||
import { ALL_VARIABLE_VALUE } from '../../constants';
|
||||
|
||||
export interface Props extends React.HTMLProps<HTMLUListElement>, Themeable2 {
|
||||
export interface Props extends Omit<React.HTMLProps<HTMLUListElement>, 'onToggle'>, Themeable2 {
|
||||
multi: boolean;
|
||||
values: VariableOption[];
|
||||
selectedValues: VariableOption[];
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@ interface CodeEditorProps {
|
||||
export const LogsQLCodeEditor = (props: CodeEditorProps) => {
|
||||
const { query, datasource, onChange } = props;
|
||||
|
||||
const monacoRef = useRef<Monaco>();
|
||||
const disposalRef = useRef<monacoType.IDisposable>();
|
||||
const monacoRef = useRef<Monaco | null>(null);
|
||||
const disposalRef = useRef<monacoType.IDisposable | undefined>(undefined);
|
||||
|
||||
const onFocus = useCallback(async () => {
|
||||
disposalRef.current = await reRegisterCompletionProvider(
|
||||
|
||||
+2
-2
@@ -42,8 +42,8 @@ interface LogsCodeEditorProps {
|
||||
export const PPLQueryEditor = (props: LogsCodeEditorProps) => {
|
||||
const { query, datasource, onChange } = props;
|
||||
|
||||
const monacoRef = useRef<Monaco>();
|
||||
const disposalRef = useRef<monacoType.IDisposable>();
|
||||
const monacoRef = useRef<Monaco | null>(null);
|
||||
const disposalRef = useRef<monacoType.IDisposable | undefined>(undefined);
|
||||
|
||||
const onFocus = useCallback(async () => {
|
||||
disposalRef.current = await reRegisterCompletionProvider(
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@ interface SQLCodeEditorProps {
|
||||
export const SQLQueryEditor = (props: SQLCodeEditorProps) => {
|
||||
const { query, datasource, onChange } = props;
|
||||
|
||||
const monacoRef = useRef<Monaco>();
|
||||
const disposalRef = useRef<monacoType.IDisposable>();
|
||||
const monacoRef = useRef<Monaco | null>(null);
|
||||
const disposalRef = useRef<monacoType.IDisposable | undefined>(undefined);
|
||||
|
||||
const onFocus = useCallback(async () => {
|
||||
disposalRef.current = await reRegisterCompletionProvider(
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ const EDITOR_HEIGHT_OFFSET = 2;
|
||||
* Hook that returns function that will set up monaco autocomplete for the label selector
|
||||
*/
|
||||
function useAutocomplete(getLabelValues: (label: string) => Promise<string[]>, labels?: string[]) {
|
||||
const providerRef = useRef<CompletionProvider>();
|
||||
const providerRef = useRef<CompletionProvider | null>(null);
|
||||
if (providerRef.current === undefined) {
|
||||
providerRef.current = new CompletionProvider();
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ export function LokiContextUi(props: LokiContextUiProps) {
|
||||
window.localStorage.getItem(SHOULD_INCLUDE_PIPELINE_OPERATIONS) === 'true'
|
||||
);
|
||||
|
||||
const timerHandle = useRef<number>();
|
||||
const timerHandle = useRef<number | null>(null);
|
||||
const previousInitialized = useRef<boolean>(false);
|
||||
const previousContextFilters = useRef<ContextFilter[]>([]);
|
||||
|
||||
@@ -191,14 +191,18 @@ export function LokiContextUi(props: LokiContextUiProps) {
|
||||
}, 1500);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timerHandle.current);
|
||||
if (timerHandle.current) {
|
||||
clearTimeout(timerHandle.current);
|
||||
}
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [contextFilters, initialized]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
clearTimeout(timerHandle.current);
|
||||
if (timerHandle.current) {
|
||||
clearTimeout(timerHandle.current);
|
||||
}
|
||||
onClose();
|
||||
};
|
||||
}, [onClose]);
|
||||
|
||||
@@ -52,7 +52,7 @@ export function TraceQLEditor(props: Props) {
|
||||
const onRunQueryRef = useRef(onRunQuery);
|
||||
onRunQueryRef.current = onRunQuery;
|
||||
|
||||
const errorTimeoutId = useRef<number>();
|
||||
const errorTimeoutId = useRef<number | null>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -103,7 +103,9 @@ export function TraceQLEditor(props: Props) {
|
||||
}
|
||||
|
||||
// Remove previous callback if existing, to prevent squiggles from been shown while the user is still typing
|
||||
window.clearTimeout(errorTimeoutId.current);
|
||||
if (errorTimeoutId.current) {
|
||||
window.clearTimeout(errorTimeoutId.current);
|
||||
}
|
||||
|
||||
const errorNodes = getErrorNodes(model.getValue());
|
||||
const cursorPosition = changeEvent.changes[0].rangeOffset;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export function renderHistogram(
|
||||
can: React.RefObject<HTMLCanvasElement>,
|
||||
can: React.RefObject<HTMLCanvasElement | null>,
|
||||
histCanWidth: number,
|
||||
histCanHeight: number,
|
||||
xVals: number[],
|
||||
|
||||
@@ -196,7 +196,7 @@ export const LogsPanel = ({
|
||||
const dataSourcesMap = useDatasourcesFromTargets(panelData.request?.targets);
|
||||
// Prevents the scroll position to change when new data from infinite scrolling is received
|
||||
const keepScrollPositionRef = useRef<null | 'infinite-scroll' | 'user'>(null);
|
||||
let closeCallback = useRef<() => void>();
|
||||
const closeCallback = useRef<(() => void) | null>(null);
|
||||
const { app, eventBus, onAddAdHocFilter } = usePanelContext();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -70,7 +70,7 @@ export function useLayout(
|
||||
const currentSignature = createDataSignature(rawNodes, rawEdges);
|
||||
|
||||
const isMounted = useMountedState();
|
||||
const layoutWorkerCancelRef = useRef<(() => void) | undefined>();
|
||||
const layoutWorkerCancelRef = useRef<(() => void) | null>(null);
|
||||
|
||||
useUnmount(() => {
|
||||
if (layoutWorkerCancelRef.current) {
|
||||
|
||||
@@ -133,7 +133,7 @@ export const AnnotationsPlugin2 = ({
|
||||
const newRangeRef = useRef(newRange);
|
||||
newRangeRef.current = newRange;
|
||||
|
||||
const xAxisRef = useRef<HTMLDivElement>();
|
||||
const xAxisRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
config.addHook('ready', (u) => {
|
||||
|
||||
@@ -23,7 +23,7 @@ export const ExemplarsPlugin = ({
|
||||
maxHeight,
|
||||
maxWidth,
|
||||
}: ExemplarsPluginProps) => {
|
||||
const plotInstance = useRef<uPlot>();
|
||||
const plotInstance = useRef<uPlot | null>(null);
|
||||
|
||||
const [lockedExemplarRowIndex, setLockedExemplarRowIndex] = useState<number | undefined>();
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ interface ThresholdControlsPluginProps {
|
||||
}
|
||||
|
||||
export const OutsideRangePlugin = ({ config, onChangeTimeRange }: ThresholdControlsPluginProps) => {
|
||||
const plotInstance = useRef<uPlot>();
|
||||
const plotInstance = useRef<uPlot | null>(null);
|
||||
const [timevalues, setTimeValues] = useState<number[] | TypedArray>([]);
|
||||
const [timeRange, setTimeRange] = useState<Scale | undefined>();
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ interface ThresholdControlsPluginProps {
|
||||
}
|
||||
|
||||
export const ThresholdControlsPlugin = ({ config, fieldConfig, onThresholdsChange }: ThresholdControlsPluginProps) => {
|
||||
const plotInstance = useRef<uPlot>();
|
||||
const plotInstance = useRef<uPlot | null>(null);
|
||||
const [renderToken, setRenderToken] = useState(0);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user