import {Component} from 'react'; import styled from '@emotion/styled'; import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator'; import {openModal} from 'sentry/actionCreators/modal'; import {Client} from 'sentry/api'; import Button from 'sentry/components/button'; import ExternalLink from 'sentry/components/links/externalLink'; import {Panel, PanelAlert, PanelBody, PanelHeader} from 'sentry/components/panels'; import {t, tct} from 'sentry/locale'; import space from 'sentry/styles/space'; import {Organization, Project} from 'sentry/types'; import Add from './modals/add'; import Edit from './modals/edit'; import Content from './content'; import convertRelayPiiConfig from './convertRelayPiiConfig'; import OrganizationRules from './organizationRules'; import submitRules from './submitRules'; import {ProjectId, Rule} from './types'; const ADVANCED_DATASCRUBBING_LINK = 'https://docs.sentry.io/product/data-management-settings/scrubbing/advanced-datascrubbing/'; type Props = { endpoint: string; organization: Organization; additionalContext?: React.ReactNode; disabled?: boolean; onSubmitSuccess?: (data: T extends undefined ? Organization : Project) => void; projectId?: T; relayPiiConfig?: string; }; type State = { orgRules: Array; rules: Array; savedRules: Array; relayPiiConfig?: string; }; class DataScrubbing extends Component, State> { state: State = { rules: [], savedRules: [], relayPiiConfig: this.props.relayPiiConfig, orgRules: [], }; componentDidMount() { this.loadRules(); this.loadOrganizationRules(); } componentDidUpdate(_prevProps: Props, prevState: State) { if (prevState.relayPiiConfig !== this.state.relayPiiConfig) { this.loadRules(); } } componentWillUnmount() { this.api.clear(); } api = new Client(); loadOrganizationRules() { const {organization, projectId} = this.props; if (projectId) { try { this.setState({ orgRules: convertRelayPiiConfig(organization.relayPiiConfig), }); } catch { addErrorMessage(t('Unable to load organization rules')); } } } loadRules() { try { const convertedRules = convertRelayPiiConfig(this.state.relayPiiConfig); this.setState({ rules: convertedRules, savedRules: convertedRules, }); } catch { addErrorMessage(t('Unable to load project rules')); } } successfullySaved( response: T extends undefined ? Organization : Project, successMessage: string ) { const {onSubmitSuccess} = this.props; this.setState({rules: convertRelayPiiConfig(response.relayPiiConfig)}); addSuccessMessage(successMessage); onSubmitSuccess?.(response); } handleOpenAddModal = () => { const {rules} = this.state; openModal(modalProps => ( { this.successfullySaved(response, t('Successfully added data scrubbing rule')); }} /> )); }; handleOpenEditModal = (id: Rule['id']) => () => { const {rules} = this.state; openModal(modalProps => ( { this.successfullySaved(response, t('Successfully updated data scrubbing rule')); }} /> )); }; handleDelete = (id: Rule['id']) => async () => { const {rules} = this.state; const filteredRules = rules.filter(rule => rule.id !== id); try { const data = await submitRules(this.api, this.props.endpoint, filteredRules); if (data?.relayPiiConfig) { const convertedRules = convertRelayPiiConfig(data.relayPiiConfig); this.setState({rules: convertedRules}); addSuccessMessage(t('Successfully deleted data scrubbing rule')); } } catch { addErrorMessage(t('An unknown error occurred while deleting data scrubbing rule')); } }; render() { const {additionalContext, disabled, projectId} = this.props; const {orgRules, rules} = this.state; return (
{t('Advanced Data Scrubbing')}
{additionalContext}{' '} {`${t('The new rules will only apply to upcoming events. ')}`}{' '} {tct('For more details, see [linkToDocs].', { linkToDocs: ( {t('full documentation on data scrubbing')} ), })} {projectId && }
); } } export default DataScrubbing; const PanelAction = styled('div')` padding: ${space(1)} ${space(2)}; position: relative; display: grid; gap: ${space(1)}; grid-template-columns: auto auto; justify-content: flex-end; border-top: 1px solid ${p => p.theme.border}; `;