Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion static/app/components/arithmeticBuilder/grammar.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ yes_arg

arg = attr

attr = typed_attr / untyped_attr
attr = typed_attr / backtick_attr / untyped_attr

backtick_attr
= "`" [^`]* "`" {
return tc.tokenAttribute(text(), undefined, location());
}

typed_attr
= "tags[" name:name "," spaces type:type_name "]" {
Expand Down
52 changes: 52 additions & 0 deletions static/app/components/arithmeticBuilder/tokenizer.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ describe('tokenizeExpression', () => {
['avg(tags[foo, number])', f(0, 'avg', [a(0, 'foo', 'number')])],
['avg( tags[foo, number] )', f(0, 'avg', [a(0, 'foo', 'number')])],
['epm()', f(0, 'epm', [])],
['count_if(`test:foo`)', f(0, 'count_if', [a(0, '`test:foo`')])],
['count_if(`test:"blah blah"`)', f(0, 'count_if', [a(0, '`test:"blah blah"`')])],
])('tokenizes function `%s`', (expression, expected) => {
expect(tokenizeExpression(expression)).toEqual([s(0), expected, s(1)]);
});
Expand All @@ -116,6 +118,20 @@ describe('tokenizeExpression', () => {
'avg( tags[foo, number], equals, 30 )',
f(0, 'avg', [a(0, 'foo', 'number'), a(1, 'equals'), a(2, '30')]),
],
[
'count_if(`test:"blah blah"`,test,test)',
f(0, 'count_if', [a(0, '`test:"blah blah"`'), a(1, 'test'), a(2, 'test')]),
],
[
'sum_if(`agent_name:["Agent Run","Assisted Query Agent - Traces"]`,value,agent.invocations.error,counter,none)',
f(0, 'sum_if', [
a(0, '`agent_name:["Agent Run","Assisted Query Agent - Traces"]`'),
a(1, 'value'),
a(2, 'agent.invocations.error'),
a(3, 'counter'),
a(4, 'none'),
]),
],
])('tokenizes multi-param function `%s`', (expression, expected) => {
expect(tokenizeExpression(expression)).toEqual([s(0), expected, s(1)]);
});
Expand Down Expand Up @@ -293,6 +309,42 @@ describe('tokenizeExpression', () => {
s(3),
],
],
[
'count_if(`test:foo`) + epm()',
[
s(0),
f(0, 'count_if', [a(0, '`test:foo`')]),
s(1),
o(0, '+'),
s(2),
f(1, 'epm', []),
s(3),
],
],
[
'count_if(`test:"blah blah"`) + epm()',
[
s(0),
f(0, 'count_if', [a(0, '`test:"blah blah"`')]),
s(1),
o(0, '+'),
s(2),
f(1, 'epm', []),
s(3),
],
],
[
'count_if(`test:"blah blah"`,test,test) + sum_if(`test:"blah\'blah\'blah"`)',
[
s(0),
f(0, 'count_if', [a(0, '`test:"blah blah"`'), a(1, 'test'), a(2, 'test')]),
s(1),
o(0, '+'),
s(2),
f(1, 'sum_if', [a(3, '`test:"blah\'blah\'blah"`')]),
s(3),
],
],
])('tokenizes binary expressions `%s`', (expression, expected) => {
expect(tokenizeExpression(expression)).toEqual(expected);
});
Expand Down
156 changes: 0 additions & 156 deletions static/app/views/explore/metrics/equationBuilder.tsx

This file was deleted.

69 changes: 69 additions & 0 deletions static/app/views/explore/metrics/equationBuilder/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import {useCallback, useEffect, useMemo, useTransition} from 'react';

import {ArithmeticBuilder} from 'sentry/components/arithmeticBuilder';
import {Expression} from 'sentry/components/arithmeticBuilder/expression';
import {
extractReferenceLabels,
resolveExpression,
unresolveExpression,
} from 'sentry/views/explore/metrics/equationBuilder/utils';

/**
* A component that takes an equation in full resolved form and allows
* the user to edit it using "references" to refer to the different components
* of the equation.
*
* The references are used to resolve the equation into a format that is
* compatible with our querying endpoints.
*/
export function EquationBuilder({
expression,
referenceMap,
handleExpressionChange,
onReferenceLabelsChange,
}: {
expression: string;
handleExpressionChange: (expression: Expression) => void;
onReferenceLabelsChange?: (labels: string[]) => void;
referenceMap?: Record<string, string>;
}) {
const [_, startTransition] = useTransition();
const references = useMemo(
() => new Set(Object.keys(referenceMap ?? {})),
[referenceMap]
);

const internalExpression = unresolveExpression(expression, referenceMap);

// Report which labels this equation references after unresolving.
// Cleans up on unmount so deleted equations don't block metric deletion.
useEffect(() => {
const expr = new Expression(internalExpression, references);
onReferenceLabelsChange?.(extractReferenceLabels(expr));
return () => {
onReferenceLabelsChange?.([]);
};
}, [internalExpression, references, onReferenceLabelsChange]);

const handleInternalExpressionChange = useCallback(
(newExpression: Expression) => {
startTransition(() => {
if (newExpression.isValid) {
handleExpressionChange(resolveExpression(newExpression, referenceMap));
}
});
},
[handleExpressionChange, referenceMap]
);

return (
<ArithmeticBuilder
aggregations={[]}
expression={internalExpression}
functionArguments={[]}
getFieldDefinition={() => null}
references={references}
setExpression={handleInternalExpressionChange}
/>
);
}
Loading
Loading