import {Fragment} from 'react';
import styled from '@emotion/styled';
import {GroupingConfigItem} from 'sentry/components/events/groupingInfo';
import {Field} from 'sentry/components/forms/type';
import ExternalLink from 'sentry/components/links/externalLink';
import {t, tct} from 'sentry/locale';
import space from 'sentry/styles/space';
import marked from 'sentry/utils/marked';
// Export route to make these forms searchable by label/help
export const route = '/settings/:orgId/projects/:projectId/issue-grouping/';
const groupingConfigField: Field = {
name: 'groupingConfig',
type: 'select',
label: t('Grouping Config'),
saveOnBlur: false,
saveMessageAlertType: 'info',
saveMessage: t(
'Changing grouping config will apply to future events only (can take up to a minute).'
),
selectionInfoFunction: args => {
const {groupingConfigs, value} = args;
const selection = groupingConfigs.find(({id}) => id === value);
const changelog = selection?.changelog || '';
if (!changelog) {
return null;
}
return (
{tct('New in version [version]', {version: selection.id})}:
);
},
choices: ({groupingConfigs}) =>
groupingConfigs.map(({id, hidden}) => [
id.toString(),
{id}
,
]),
help: t('Sets the grouping algorithm to be used for new events.'),
visible: ({features}) => features.has('set-grouping-config'),
};
export const fields: Record = {
fingerprintingRules: {
name: 'fingerprintingRules',
type: 'string',
label: t('Fingerprint Rules'),
hideLabel: true,
placeholder: t(
'error.type:MyException -> fingerprint-value\nstack.function:some_panic_function -> fingerprint-value'
),
multiline: true,
monospace: true,
autosize: true,
inline: false,
maxRows: 20,
saveOnBlur: false,
saveMessageAlertType: 'info',
saveMessage: t(
'Changing fingerprint rules will apply to future events only (can take up to a minute).'
),
formatMessageValue: false,
help: () => (
{tct(
`This can be used to modify the fingerprint rules on the server with custom rules.
Rules follow the pattern [pattern]. To learn more about fingerprint rules, [docs:read the docs].`,
{
pattern: matcher:glob -> fingerprint, values
,
docs: (
),
}
)}
{`# force all errors of the same type to have the same fingerprint
error.type:DatabaseUnavailable -> system-down
# force all memory allocation errors to be grouped together
stack.function:malloc -> memory-allocation-error`}
),
visible: true,
},
groupingEnhancements: {
name: 'groupingEnhancements',
type: 'string',
label: t('Stack Trace Rules'),
hideLabel: true,
placeholder: t(
'stack.function:raise_an_exception ^-group\nstack.function:namespace::* +app'
),
multiline: true,
monospace: true,
autosize: true,
inline: false,
maxRows: 20,
saveOnBlur: false,
saveMessageAlertType: 'info',
saveMessage: t(
'Changing stack trace rules will apply to future events only (can take up to a minute).'
),
formatMessageValue: false,
help: () => (
{tct(
`This can be used to enhance the grouping algorithm with custom rules.
Rules follow the pattern [pattern]. To learn more about stack trace rules, [docs:read the docs].`,
{
pattern: matcher:glob [v^]?[+-]flag
,
docs: (
),
}
)}
{`# remove all frames above a certain function from grouping
stack.function:panic_handler ^-group
# mark all functions following a prefix in-app
stack.function:mylibrary_* +app`}
),
validate: () => [],
visible: true,
},
groupingConfig: groupingConfigField,
secondaryGroupingConfig: {
...groupingConfigField,
name: 'secondaryGroupingConfig',
label: t('Fallback/Secondary Grouping Config'),
help: t(
'Sets the secondary grouping algorithm that should be run in addition to avoid creating too many new groups. Controlled by expiration date below.'
),
saveMessage: t(
'Changing the secondary grouping strategy will affect how many new issues are created.'
),
},
secondaryGroupingExpiry: {
name: 'secondaryGroupingExpiry',
type: 'number',
label: t('Expiration date of secondary grouping'),
help: t(
'If this UNIX timestamp is in the past, the secondary grouping configuration stops applying automatically.'
),
saveOnBlur: false,
saveMessageAlertType: 'info',
saveMessage: t(
'Changing the expiration date will affect how many new issues are created.'
),
},
groupingAutoUpdate: {
name: 'groupingAutoUpdate',
type: 'boolean',
label: t('Automatically Update Grouping'),
saveOnBlur: false,
help: t(
'When enabled projects will in the future automatically update to the latest grouping algorithm. Right now this setting does nothing.'
),
saveMessage: ({value}) =>
value
? t(
'Enabling automatic upgrading will take effect on the next incoming event once auto updating has been rolled out.'
)
: t(
'Disabling auto updates will cause you to no longer receive improvements to the grouping algorithm.'
),
},
};
const RuleDescription = styled('div')`
margin-bottom: ${space(1)};
margin-top: -${space(1)};
margin-right: 36px;
`;
const RuleExample = styled('pre')`
margin-bottom: ${space(1)};
margin-right: 36px;
`;
const Changelog = styled('div')`
position: relative;
top: -1px;
margin-bottom: -1px;
padding: ${space(2)};
border-bottom: 1px solid ${p => p.theme.innerBorder};
background: ${p => p.theme.backgroundSecondary};
font-size: ${p => p.theme.fontSizeMedium};
&:last-child {
border: 0;
border-bottom-left-radius: ${p => p.theme.borderRadius};
border-bottom-right-radius: ${p => p.theme.borderRadius};
}
`;
const ChangelogTitle = styled('h3')`
font-size: ${p => p.theme.fontSizeMedium};
margin-bottom: ${space(0.75)} !important;
`;