diff --git a/public/app/core/utils/dag.test.ts b/public/app/core/utils/dag.test.ts index ee3feaa27e7..d1d7f539d27 100644 --- a/public/app/core/utils/dag.test.ts +++ b/public/app/core/utils/dag.test.ts @@ -122,5 +122,9 @@ describe('Directed acyclic graph', () => { expect(() => dag.link('A', 'B')).toThrow('cannot link A to B since it would create a cycle'); expect(() => dag.link('A', 'E')).toThrow('cannot link A to E since it would create a cycle'); }); + + it('should return undefined for unknown node id', () => { + expect(dag.getNode('404')).toBeUndefined(); + }); }); }); diff --git a/public/app/core/utils/dag.ts b/public/app/core/utils/dag.ts index c4e84f520ac..c5ecbd5410c 100644 --- a/public/app/core/utils/dag.ts +++ b/public/app/core/utils/dag.ts @@ -252,7 +252,7 @@ export class Graph { return new Edge(); } - getNode(name: string): Node { + getNode(name: string): Node | undefined { return this.nodes[name]; } } diff --git a/public/app/features/alerting/unified/components/expressions/Expression.tsx b/public/app/features/alerting/unified/components/expressions/Expression.tsx index 1369da58299..8284061a208 100644 --- a/public/app/features/alerting/unified/components/expressions/Expression.tsx +++ b/public/app/features/alerting/unified/components/expressions/Expression.tsx @@ -3,7 +3,15 @@ import { uniqueId } from 'lodash'; import { FC, useCallback, useState } from 'react'; import { useFormContext } from 'react-hook-form'; -import { DataFrame, GrafanaTheme2, LoadingState, PanelData, dateTimeFormat, isTimeSeriesFrames } from '@grafana/data'; +import { + CoreApp, + DataFrame, + GrafanaTheme2, + LoadingState, + PanelData, + dateTimeFormat, + isTimeSeriesFrames, +} from '@grafana/data'; import { Alert, AutoSizeInput, Button, IconButton, Stack, Text, clearButtonStyles, useStyles2 } from '@grafana/ui'; import { ClassicConditions } from 'app/features/expressions/components/ClassicConditions'; import { Math } from 'app/features/expressions/components/Math'; @@ -95,7 +103,15 @@ export const Expression: FC = ({ return {}} />; case ExpressionQueryType.reduce: - return ; + return ( + + ); case ExpressionQueryType.resample: return ; diff --git a/public/app/features/alerting/unified/components/rule-editor/dag.test.ts b/public/app/features/alerting/unified/components/rule-editor/dag.test.ts index 513d41abffe..914420e5b06 100644 --- a/public/app/features/alerting/unified/components/rule-editor/dag.test.ts +++ b/public/app/features/alerting/unified/components/rule-editor/dag.test.ts @@ -56,21 +56,21 @@ describe('working with dag', () => { dag.getNode('D'); }).not.toThrow(); - expect(dag.getNode('A').inputEdges).toHaveLength(0); - expect(dag.getNode('A').outputEdges).toHaveLength(0); + expect(dag.getNode('A')!.inputEdges).toHaveLength(0); + expect(dag.getNode('A')!.outputEdges).toHaveLength(0); - expect(dag.getNode('B').inputEdges).toHaveLength(0); - expect(dag.getNode('B').outputEdges).toHaveLength(2); - expect(dag.getNode('B').outputEdges[0].outputNode).toHaveProperty('name', 'C'); - expect(dag.getNode('B').outputEdges[1].outputNode).toHaveProperty('name', 'D'); + expect(dag.getNode('B')!.inputEdges).toHaveLength(0); + expect(dag.getNode('B')!.outputEdges).toHaveLength(2); + expect(dag.getNode('B')!.outputEdges[0].outputNode).toHaveProperty('name', 'C'); + expect(dag.getNode('B')!.outputEdges[1].outputNode).toHaveProperty('name', 'D'); - expect(dag.getNode('C').inputEdges).toHaveLength(1); - expect(dag.getNode('C').inputEdges[0].inputNode).toHaveProperty('name', 'B'); - expect(dag.getNode('C').outputEdges).toHaveLength(0); + expect(dag.getNode('C')!.inputEdges).toHaveLength(1); + expect(dag.getNode('C')!.inputEdges[0].inputNode).toHaveProperty('name', 'B'); + expect(dag.getNode('C')!.outputEdges).toHaveLength(0); - expect(dag.getNode('D').inputEdges).toHaveLength(1); - expect(dag.getNode('D').inputEdges[0].inputNode).toHaveProperty('name', 'B'); - expect(dag.getNode('D').outputEdges).toHaveLength(0); + expect(dag.getNode('D')!.inputEdges).toHaveLength(1); + expect(dag.getNode('D')!.inputEdges[0].inputNode).toHaveProperty('name', 'B'); + expect(dag.getNode('D')!.outputEdges).toHaveLength(0); }); test('data queries cannot have references', () => { diff --git a/public/app/features/alerting/unified/components/rule-editor/dag.ts b/public/app/features/alerting/unified/components/rule-editor/dag.ts index 4900ea70025..51d519854f5 100644 --- a/public/app/features/alerting/unified/components/rule-editor/dag.ts +++ b/public/app/features/alerting/unified/components/rule-editor/dag.ts @@ -66,6 +66,9 @@ export const getDescendants = memoize(_getDescendants, (refId, graph) => refId + export function _getOriginsOfRefId(refId: string, graph: Graph): string[] { const node = graph.getNode(refId); + if (!node) { + return []; + } const origins: Node[] = []; @@ -92,6 +95,10 @@ export function _getOriginsOfRefId(refId: string, graph: Graph): string[] { // get all children (and children's children etc) from a given node export function _getDescendants(refId: string, graph: Graph): string[] { const node = graph.getNode(refId); + if (!node) { + return []; + } + const descendants: Node[] = []; // recurse through "node > outputEdges > outputNode" diff --git a/public/app/features/expressions/ExpressionQueryEditor.tsx b/public/app/features/expressions/ExpressionQueryEditor.tsx index 6cf394e2ef1..fdb6c9b7343 100644 --- a/public/app/features/expressions/ExpressionQueryEditor.tsx +++ b/public/app/features/expressions/ExpressionQueryEditor.tsx @@ -58,7 +58,7 @@ function useExpressionsCache() { } export function ExpressionQueryEditor(props: Props) { - const { query, queries, onRunQuery, onChange } = props; + const { query, queries, onRunQuery, onChange, app } = props; const { getCachedExpression, setCachedExpression } = useExpressionsCache(); useEffect(() => { @@ -83,7 +83,7 @@ export function ExpressionQueryEditor(props: Props) { return ; case ExpressionQueryType.reduce: - return ; + return ; case ExpressionQueryType.resample: return ; diff --git a/public/app/features/expressions/components/Reduce.tsx b/public/app/features/expressions/components/Reduce.tsx index 6793b32af50..d6d203e52fd 100644 --- a/public/app/features/expressions/components/Reduce.tsx +++ b/public/app/features/expressions/components/Reduce.tsx @@ -1,19 +1,20 @@ import * as React from 'react'; -import { SelectableValue } from '@grafana/data'; -import { InlineField, InlineFieldRow, Input, Select, Alert } from '@grafana/ui'; +import { CoreApp, SelectableValue } from '@grafana/data'; +import { Alert, InlineField, InlineFieldRow, Input, Select, TextLink } from '@grafana/ui'; import { Trans, t } from 'app/core/internationalization'; import { ExpressionQuery, ExpressionQuerySettings, ReducerMode, reducerModes, reducerTypes } from '../types'; interface Props { + app?: CoreApp; labelWidth?: number | 'auto'; refIds: Array>; query: ExpressionQuery; onChange: (query: ExpressionQuery) => void; } -export const Reduce = ({ labelWidth = 'auto', onChange, refIds, query }: Props) => { +export const Reduce = ({ labelWidth = 'auto', onChange, app, refIds, query }: Props) => { const reducer = reducerTypes.find((o) => o.value === query.reducer); const onRefIdChange = (value: SelectableValue) => { @@ -72,22 +73,21 @@ export const Reduce = ({ labelWidth = 'auto', onChange, refIds, query }: Props) ); }; + // for Alerting we really don't want to add additional confusing messages that would be unhelpful to the majority of our users const strictModeNotification = () => { - if (mode !== ReducerMode.Strict) { + const isWithinAlerting = app === CoreApp.UnifiedAlerting; + if (mode !== ReducerMode.Strict || isWithinAlerting) { return null; } + return ( When Reduce Strict mode is used, the fill(null) function (InfluxQL) will result in{' '} NaN.{' '} - + See the documentation for more details. - + );