Skip to content

Commit 0633202

Browse files
malwilleyshashjar
authored andcommitted
feat(aci): Lock editing of transaction metric monitors (#102107)
Mirrors the changes in metric alerts. Existing transaction metric queries cannot be edited and must be changed to span queries.
1 parent 0e93745 commit 0633202

File tree

8 files changed

+110
-17
lines changed

8 files changed

+110
-17
lines changed

static/app/views/detectors/components/details/metric/transactionsDatasetWarning.tsx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ import {ExternalLink} from 'sentry/components/core/link';
33
import {tctCode} from 'sentry/locale';
44
import useOrganization from 'sentry/utils/useOrganization';
55

6+
export const TRANSACTIONS_DATASET_DEPRECATION_MESSAGE = tctCode(
7+
'The transaction dataset is being deprecated. Please use Span alerts instead. Spans are a superset of transactions, you can isolate transactions by using the [code:is_transaction:true] filter. Please read these [FAQLink:FAQs] for more information.',
8+
{
9+
FAQLink: (
10+
<ExternalLink href="https://sentry.zendesk.com/hc/en-us/articles/40366087871515-FAQ-Transactions-Spans-Migration" />
11+
),
12+
}
13+
);
14+
615
export function TransactionsDatasetWarning() {
716
const organization = useOrganization();
817
const hasWarning = organization.features.includes(
@@ -12,16 +21,5 @@ export function TransactionsDatasetWarning() {
1221
return null;
1322
}
1423

15-
return (
16-
<Alert type="warning">
17-
{tctCode(
18-
'The transaction dataset is being deprecated. Please use Span alerts instead. Spans are a superset of transactions, you can isolate transactions by using the [code:is_transaction:true] filter. Please read these [FAQLink:FAQs] for more information.',
19-
{
20-
FAQLink: (
21-
<ExternalLink href="https://sentry.zendesk.com/hc/en-us/articles/40366087871515-FAQ-Transactions-Spans-Migration" />
22-
),
23-
}
24-
)}
25-
</Alert>
26-
);
24+
return <Alert type="warning">{TRANSACTIONS_DATASET_DEPRECATION_MESSAGE}</Alert>;
2725
}

static/app/views/detectors/components/forms/metric/metric.tsx

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ import {
3232
Dataset,
3333
} from 'sentry/views/alerts/rules/metric/types';
3434
import {hasLogAlerts} from 'sentry/views/alerts/wizard/utils';
35-
import {TransactionsDatasetWarning} from 'sentry/views/detectors/components/details/metric/transactionsDatasetWarning';
35+
import {
36+
TRANSACTIONS_DATASET_DEPRECATION_MESSAGE,
37+
TransactionsDatasetWarning,
38+
} from 'sentry/views/detectors/components/details/metric/transactionsDatasetWarning';
3639
import {AutomateSection} from 'sentry/views/detectors/components/forms/automateSection';
3740
import {AssignSection} from 'sentry/views/detectors/components/forms/common/assignSection';
3841
import {useDetectorFormContext} from 'sentry/views/detectors/components/forms/context';
@@ -212,6 +215,7 @@ function IntervalPicker() {
212215
}
213216
name={METRIC_DETECTOR_FORM_FIELDS.interval}
214217
choices={intervalChoices}
218+
disabled={dataset === DetectorDataset.TRANSACTIONS}
215219
/>
216220
);
217221
}
@@ -270,6 +274,8 @@ function DetectSection() {
270274
const aggregate = useMetricDetectorFormField(
271275
METRIC_DETECTOR_FORM_FIELDS.aggregateFunction
272276
);
277+
const dataset = useMetricDetectorFormField(METRIC_DETECTOR_FORM_FIELDS.dataset);
278+
const isTransactionsDataset = dataset === DetectorDataset.TRANSACTIONS;
273279

274280
return (
275281
<Container>
@@ -310,9 +316,26 @@ function DetectSection() {
310316
}
311317
}}
312318
/>
313-
<IntervalPicker />
319+
<Tooltip
320+
title={TRANSACTIONS_DATASET_DEPRECATION_MESSAGE}
321+
isHoverable
322+
disabled={!isTransactionsDataset}
323+
>
324+
<DisabledSection disabled={isTransactionsDataset}>
325+
<IntervalPicker />
326+
</DisabledSection>
327+
</Tooltip>
314328
</DatasetRow>
315-
<Visualize />
329+
<Tooltip
330+
title={TRANSACTIONS_DATASET_DEPRECATION_MESSAGE}
331+
isHoverable
332+
disabled={!isTransactionsDataset}
333+
>
334+
<DisabledSection disabled={isTransactionsDataset}>
335+
<Visualize />
336+
</DisabledSection>
337+
</Tooltip>
338+
316339
<DetectionType />
317340
<Flex direction="column">
318341
{(!detectionType || detectionType === 'static') && (
@@ -539,3 +562,7 @@ const IntervalField = styled(SelectField)`
539562
margin-left: 0;
540563
border-bottom: none;
541564
`;
565+
566+
const DisabledSection = styled('div')<{disabled: boolean}>`
567+
${p => (p.disabled ? `opacity: 0.6;` : '')}
568+
`;

static/app/views/detectors/components/forms/metric/queryFilterBuilder.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
SectionLabelSecondary,
1717
} from 'sentry/views/detectors/components/forms/sectionLabel';
1818
import {getDatasetConfig} from 'sentry/views/detectors/datasetConfig/getDatasetConfig';
19+
import {DetectorDataset} from 'sentry/views/detectors/datasetConfig/types';
1920

2021
export function DetectorQueryFilterBuilder() {
2122
const currentQuery = useMetricDetectorFormField(METRIC_DETECTOR_FORM_FIELDS.query);
@@ -45,6 +46,7 @@ export function DetectorQueryFilterBuilder() {
4546
flexibleControlStateSize
4647
label={t('Filter')}
4748
hideLabel
49+
disabled={dataset === DetectorDataset.TRANSACTIONS}
4850
>
4951
{({ref: _ref, ...fieldProps}) => (
5052
<Flex direction="column" gap="xs" flex={1}>

static/app/views/detectors/components/forms/metric/visualize.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ export function Visualize() {
202202
});
203203
const formContext = useContext(FormContext);
204204

205+
const isTransactionsDataset = dataset === DetectorDataset.TRANSACTIONS;
206+
205207
// Parse the aggregateFunction into UI components on each render
206208
const {aggregate, parameters} = useMemo(() => {
207209
return parseAggregateFunction(aggregateFunction);
@@ -329,6 +331,7 @@ export function Visualize() {
329331
onChange={option => {
330332
handleAggregateChange(String(option.value));
331333
}}
334+
disabled={isTransactionsDataset}
332335
/>
333336
</FieldContainer>
334337
{aggregateMetadata?.parameters?.map((param, index) => {
@@ -353,7 +356,7 @@ export function Visualize() {
353356
onChange={option => {
354357
handleParameterChange(index, String(option.value));
355358
}}
356-
disabled={lockSpanOptions}
359+
disabled={isTransactionsDataset || lockSpanOptions}
357360
/>
358361
) : param.kind === 'dropdown' && param.options ? (
359362
<StyledVisualizeSelect
@@ -370,6 +373,7 @@ export function Visualize() {
370373
onChange={option => {
371374
handleParameterChange(index, String(option.value));
372375
}}
376+
disabled={isTransactionsDataset}
373377
/>
374378
) : (
375379
<StyledParameterInput
@@ -378,6 +382,7 @@ export function Visualize() {
378382
onChange={e => {
379383
handleParameterChange(index, e.target.value);
380384
}}
385+
disabled={isTransactionsDataset}
381386
/>
382387
)}
383388
</FieldContainer>

static/app/views/detectors/datasetConfig/base.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface DetectorSearchBarProps {
2424
onSearch: (query: string) => void;
2525
projectIds: number[];
2626
dataset?: DiscoverDatasets;
27+
disabled?: boolean;
2728
}
2829

2930
interface DetectorSeriesQueryOptions {

static/app/views/detectors/datasetConfig/components/traceSearchBar.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export function TraceSearchBar({
1717
onClose,
1818
projectIds,
1919
dataset,
20+
disabled,
2021
}: DetectorSearchBarProps) {
2122
const isLogs = dataset === DiscoverDatasets.OURLOGS;
2223
const traceDataset = isLogs ? TraceItemDataset.LOGS : TraceItemDataset.SPANS;
@@ -63,6 +64,7 @@ export function TraceSearchBar({
6364
onChange={(query, state) => {
6465
onClose?.(query, {validSearch: state.queryIsValid});
6566
}}
67+
disabled={disabled}
6668
/>
6769
);
6870
}

static/app/views/detectors/edit.spec.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,5 +809,55 @@ describe('DetectorEdit', () => {
809809
expect(screen.getByRole('menuitemradio', {name: 'Spans'})).toBeInTheDocument();
810810
expect(screen.getByRole('menuitemradio', {name: 'Releases'})).toBeInTheDocument();
811811
});
812+
813+
it('disables interval, aggregate, and query when using the transactions dataset', async () => {
814+
const transactionsDetector = MetricDetectorFixture({
815+
id: '321',
816+
name: 'Transaction Detector',
817+
dataSources: [
818+
SnubaQueryDataSourceFixture({
819+
queryObj: {
820+
id: '1',
821+
status: 1,
822+
subscription: '1',
823+
snubaQuery: {
824+
aggregate: 'p75()',
825+
dataset: Dataset.GENERIC_METRICS,
826+
id: '',
827+
query: '',
828+
timeWindow: 60,
829+
eventTypes: [EventTypes.TRANSACTION],
830+
},
831+
},
832+
}),
833+
],
834+
});
835+
836+
MockApiClient.addMockResponse({
837+
url: `/organizations/${organization.slug}/detectors/${transactionsDetector.id}/`,
838+
body: transactionsDetector,
839+
});
840+
841+
render(<DetectorEdit />, {
842+
organization,
843+
initialRouterConfig: {
844+
route: '/organizations/:orgId/monitors/:detectorId/edit/',
845+
location: {
846+
pathname: `/organizations/${organization.slug}/monitors/${transactionsDetector.id}/edit/`,
847+
},
848+
},
849+
});
850+
851+
expect(
852+
await screen.findByRole('link', {name: transactionsDetector.name})
853+
).toBeInTheDocument();
854+
855+
// Interval select should be disabled
856+
const intervalField = screen.getByLabelText('Interval');
857+
expect(intervalField).toBeDisabled();
858+
859+
// Aggregate selector should be disabled
860+
expect(screen.getByRole('button', {name: 'p75'})).toBeDisabled();
861+
});
812862
});
813863
});

static/app/views/explore/components/traceItemSearchQueryBuilder.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export type TraceItemSearchQueryBuilderProps = {
2424
stringAttributes: TagCollection;
2525
stringSecondaryAliases: TagCollection;
2626
caseInsensitive?: CaseInsensitive;
27+
disabled?: boolean;
2728
matchKeySuggestions?: Array<{key: string; valuePattern: RegExp}>;
2829
namespace?: string;
2930
onCaseInsensitiveClick?: SetCaseInsensitive;
@@ -145,6 +146,7 @@ export function TraceItemSearchQueryBuilder({
145146
portalTarget,
146147
projects,
147148
supportedAggregates = [],
149+
disabled,
148150
}: TraceItemSearchQueryBuilderProps) {
149151
const searchQueryBuilderProps = useSearchQueryBuilderProps({
150152
itemType,
@@ -163,7 +165,13 @@ export function TraceItemSearchQueryBuilder({
163165
supportedAggregates,
164166
});
165167

166-
return <SearchQueryBuilder autoFocus={autoFocus} {...searchQueryBuilderProps} />;
168+
return (
169+
<SearchQueryBuilder
170+
autoFocus={autoFocus}
171+
disabled={disabled}
172+
{...searchQueryBuilderProps}
173+
/>
174+
);
167175
}
168176

169177
function useFunctionTags(

0 commit comments

Comments
 (0)