Browse Source

chore(issues): Remove usage of streamline-targeting-context flag from the frontend (#64819)

From what I can tell, this flag is GA'd, so we can remove it and allow
self-hosted to access this view by default. Also removing
components/tests that were dependent on not having this flag
(`RuleBuilder`, and `CodeOwnersPanel`)
Leander Rodrigues 1 year ago
parent
commit
180882754e

+ 3 - 3
static/app/views/alerts/create.spec.tsx

@@ -458,7 +458,7 @@ describe('ProjectAlertsCreate', function () {
 
         // Add a new action
         await selectEvent.select(screen.getByText('Add action...'), [
-          'Issue Owners, Team, or Member',
+          'Suggested Assignees, Team, or Member',
         ]);
 
         // Update action interval
@@ -663,7 +663,7 @@ describe('ProjectAlertsCreate', function () {
     await userEvent.click((await screen.findAllByLabelText('Delete Node'))[0]);
 
     await selectEvent.select(screen.getByText('Add action...'), [
-      'Issue Owners, Team, or Member',
+      'Suggested Assignees, Team, or Member',
     ]);
 
     expect(
@@ -703,7 +703,7 @@ describe('ProjectAlertsCreate', function () {
     ).not.toBeInTheDocument();
 
     await selectEvent.select(screen.getByText('Add action...'), [
-      'Issue Owners, Team, or Member',
+      'Suggested Assignees, Team, or Member',
     ]);
 
     expect(

+ 2 - 6
static/app/views/alerts/rules/issue/ruleNode.spec.tsx

@@ -244,7 +244,7 @@ describe('RuleNode', () => {
     renderRuleNode(formNode(label), {targetType: 'IssueOwners'});
 
     expect(screen.getByText('Send a notification to')).toBeInTheDocument();
-    await selectEvent.select(screen.getByText('Issue Owners'), 'Team');
+    await selectEvent.select(screen.getByText('Suggested Assignees'), 'Team');
     expect(onPropertyChange).toHaveBeenCalledTimes(2);
     expect(onPropertyChange).toHaveBeenCalledWith(index, 'targetType', 'Team');
     expect(onPropertyChange).toHaveBeenCalledWith(index, 'targetIdentifier', '');
@@ -253,11 +253,7 @@ describe('RuleNode', () => {
   it('renders mail action field with suggested assignees', async () => {
     const fieldName = 'exampleMailActionField';
     const label = `Send a notification to {${fieldName}}`;
-    const organizationWithFeat = {
-      ...organization,
-      features: ['streamline-targeting-context'],
-    };
-    renderRuleNode(formNode(label), {targetType: 'IssueOwners'}, organizationWithFeat);
+    renderRuleNode(formNode(label), {targetType: 'IssueOwners'}, organization);
 
     expect(screen.getByText('Send a notification to')).toBeInTheDocument();
     await selectEvent.select(screen.getByText('Suggested Assignees'), 'Team');

+ 1 - 8
static/app/views/alerts/rules/issue/ruleNode.tsx

@@ -31,10 +31,6 @@ import SentryAppRuleModal from 'sentry/views/alerts/rules/issue/sentryAppRuleMod
 import TicketRuleModal from 'sentry/views/alerts/rules/issue/ticketRuleModal';
 import type {SchemaFormConfig} from 'sentry/views/settings/organizationIntegrations/sentryAppExternalForm';
 
-export function hasStreamlineTargeting(organization: Organization): boolean {
-  return organization.features.includes('streamline-targeting-context');
-}
-
 interface FieldProps {
   data: Props['data'];
   disabled: boolean;
@@ -119,10 +115,7 @@ function MailActionFields({
   onMemberTeamChange,
 }: FieldProps) {
   const isInitialized = data.targetType !== undefined && `${data.targetType}`.length > 0;
-  let issueOwnersLabel = t('Issue Owners');
-  if (hasStreamlineTargeting(organization)) {
-    issueOwnersLabel = t('Suggested Assignees');
-  }
+  const issueOwnersLabel = t('Suggested Assignees');
   return (
     <MemberTeamFields
       disabled={disabled}

+ 7 - 14
static/app/views/alerts/rules/issue/ruleNodeList.tsx

@@ -23,7 +23,7 @@ import {
 
 import {AlertRuleComparisonType} from '../metric/types';
 
-import RuleNode, {hasStreamlineTargeting} from './ruleNode';
+import RuleNode from './ruleNode';
 
 type Props = {
   disabled: boolean;
@@ -55,18 +55,14 @@ type Props = {
 };
 
 const createSelectOptions = (
-  actions: IssueAlertRuleActionTemplate[],
-  organization: Organization
+  actions: IssueAlertRuleActionTemplate[]
 ): Array<{
   label: React.ReactNode;
   value: IssueAlertRuleActionTemplate;
 }> => {
   return actions.map(node => {
     if (node.id === IssueAlertActionType.NOTIFY_EMAIL) {
-      let label = t('Issue Owners, Team, or Member');
-      if (hasStreamlineTargeting(organization)) {
-        label = t('Suggested Assignees, Team, or Member');
-      }
+      const label = t('Suggested Assignees, Team, or Member');
       return {
         value: node,
         label,
@@ -99,10 +95,7 @@ const groupLabels = {
 /**
  * Group options by category
  */
-const groupSelectOptions = (
-  actions: IssueAlertRuleActionTemplate[],
-  organization: Organization
-) => {
+const groupSelectOptions = (actions: IssueAlertRuleActionTemplate[]) => {
   const grouped = actions.reduce<
     Record<
       keyof typeof groupLabels,
@@ -142,7 +135,7 @@ const groupSelectOptions = (
     .map(([key, values]) => {
       return {
         label: groupLabels[key],
-        options: createSelectOptions(values, organization),
+        options: createSelectOptions(values),
       };
     });
 };
@@ -264,8 +257,8 @@ class RuleNodeList extends Component<Props> {
 
     const options =
       selectType === 'grouped'
-        ? groupSelectOptions(enabledNodes, organization)
-        : createSelectOptions(enabledNodes, organization);
+        ? groupSelectOptions(enabledNodes)
+        : createSelectOptions(enabledNodes);
 
     return (
       <Fragment>

+ 1 - 3
static/app/views/settings/project/navigationConfiguration.tsx

@@ -48,9 +48,7 @@ export default function getConfiguration({
         },
         {
           path: `${pathPrefix}/ownership/`,
-          title: organization?.features?.includes('streamline-targeting-context')
-            ? t('Ownership Rules')
-            : t('Issue Owners'),
+          title: t('Ownership Rules'),
           description: t('Manage ownership rules for a project'),
         },
         {

+ 0 - 105
static/app/views/settings/project/projectOwnership/codeowners.tsx

@@ -1,105 +0,0 @@
-import {Component, Fragment} from 'react';
-
-import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
-import type {Client} from 'sentry/api';
-import {Button} from 'sentry/components/button';
-import Confirm from 'sentry/components/confirm';
-import {IconDelete, IconSync} from 'sentry/icons';
-import {t} from 'sentry/locale';
-import type {CodeOwner, CodeownersFile, Organization, Project} from 'sentry/types';
-import withApi from 'sentry/utils/withApi';
-import RulesPanel from 'sentry/views/settings/project/projectOwnership/rulesPanel';
-
-type Props = {
-  api: Client;
-  codeowners: CodeOwner[];
-  disabled: boolean;
-  onDelete: (data: CodeOwner) => void;
-  onUpdate: (data: CodeOwner) => void;
-  organization: Organization;
-  project: Project;
-};
-
-class CodeOwnersPanel extends Component<Props> {
-  handleDelete = async (codeowner: CodeOwner) => {
-    const {api, organization, project, onDelete} = this.props;
-    const endpoint = `/api/0/projects/${organization.slug}/${project.slug}/codeowners/${codeowner.id}/`;
-    try {
-      await api.requestPromise(endpoint, {
-        method: 'DELETE',
-      });
-      onDelete(codeowner);
-      addSuccessMessage(t('Deletion successful'));
-    } catch {
-      // no 4xx errors should happen on delete
-      addErrorMessage(t('An error occurred'));
-    }
-  };
-
-  handleSync = async (codeowner: CodeOwner) => {
-    const {api, organization, project, onUpdate} = this.props;
-    try {
-      const codeownerFile: CodeownersFile = await api.requestPromise(
-        `/organizations/${organization.slug}/code-mappings/${codeowner.codeMappingId}/codeowners/`,
-        {
-          method: 'GET',
-        }
-      );
-
-      const data = await api.requestPromise(
-        `/projects/${organization.slug}/${project.slug}/codeowners/${codeowner.id}/`,
-        {
-          method: 'PUT',
-          data: {raw: codeownerFile.raw},
-        }
-      );
-      onUpdate({...codeowner, ...data});
-      addSuccessMessage(t('CODEOWNERS file sync successful.'));
-    } catch (_err) {
-      addErrorMessage(t('An error occurred trying to sync CODEOWNERS file.'));
-    }
-  };
-  render() {
-    const {codeowners, disabled} = this.props;
-    return codeowners.map(codeowner => {
-      const {dateUpdated, provider, codeMapping, ownershipSyntax} = codeowner;
-      return (
-        <Fragment key={codeowner.id}>
-          <RulesPanel
-            data-test-id="codeowners-panel"
-            type="codeowners"
-            raw={ownershipSyntax || ''}
-            dateUpdated={dateUpdated}
-            provider={provider}
-            repoName={codeMapping?.repoName}
-            controls={[
-              <Button
-                key="sync"
-                icon={<IconSync />}
-                size="xs"
-                onClick={() => this.handleSync(codeowner)}
-                disabled={disabled}
-                aria-label={t('Sync')}
-              />,
-              <Confirm
-                onConfirm={() => this.handleDelete(codeowner)}
-                message={t('Are you sure you want to remove this CODEOWNERS file?')}
-                key="confirm-delete"
-                disabled={disabled}
-              >
-                <Button
-                  key="delete"
-                  icon={<IconDelete />}
-                  aria-label={t('Delete')}
-                  size="xs"
-                />
-              </Confirm>,
-            ]}
-          />
-        </Fragment>
-      );
-    });
-  }
-}
-
-export default withApi(CodeOwnersPanel);

+ 0 - 14
static/app/views/settings/project/projectOwnership/editRulesModal.spec.tsx

@@ -48,20 +48,6 @@ describe('Project Ownership Input', () => {
       />
     );
 
-    expect(screen.getByText('Globbing Syntax')).toBeInTheDocument();
-  });
-
-  it('renders with streamline-targeting-context', () => {
-    render(
-      <EditOwnershipRules
-        organization={{...org, features: ['streamline-targeting-context']}}
-        ownership={ownership}
-        project={project}
-        onCancel={() => {}}
-        onSave={() => {}}
-      />
-    );
-
     expect(screen.getByText(/Assign issues based on custom rules/)).toBeInTheDocument();
   });
 });

+ 21 - 57
static/app/views/settings/project/projectOwnership/editRulesModal.tsx

@@ -6,7 +6,6 @@ import ExternalLink from 'sentry/components/links/externalLink';
 import {t, tct} from 'sentry/locale';
 import ConfigStore from 'sentry/stores/configStore';
 import {space} from 'sentry/styles/space';
-import TextBlock from 'sentry/views/settings/components/text/textBlock';
 import OwnerInput from 'sentry/views/settings/project/projectOwnership/ownerInput';
 
 interface EditOwnershipRulesModalProps extends EditOwnershipRulesModalOptions {
@@ -14,57 +13,31 @@ interface EditOwnershipRulesModalProps extends EditOwnershipRulesModalOptions {
 }
 
 export function EditOwnershipRules({ownership, ...props}: EditOwnershipRulesModalProps) {
-  const hasStreamlineTargetingFeature = props.organization.features.includes(
-    'streamline-targeting-context'
-  );
   const email = ConfigStore.get('user')?.email ?? '#team-slug';
 
   return (
     <Fragment>
-      {hasStreamlineTargetingFeature ? (
-        <Fragment>
-          <Description>
-            {tct(
-              'Assign issues based on custom rules. To learn more, [docs:read the docs].',
-              {
-                docs: (
-                  <ExternalLink href="https://docs.sentry.io/product/issues/issue-owners/" />
-                ),
-              }
-            )}
-          </Description>
-          <StyledPre>
-            # {t("Here's an example")}
-            <br />
-            path:src/views/checkout {email}
-            <br />
-            url:https://example.com/checkout {email}
-            <br />
-            tags.transaction:/checkout/:page {email}
-          </StyledPre>
-        </Fragment>
-      ) : (
-        <Fragment>
-          <Block>
-            {t('Globbing Syntax')}
-            <CodeBlock>
-              {'* matches everything\n? matches any single character'}
-            </CodeBlock>
-          </Block>
-          <Block>
-            {t('Examples')}
-            <CodeBlock>
-              path:src/example/pipeline/* person@sentry.io #infra
-              {'\n'}
-              module:com.module.name.example #sdks
-              {'\n'}
-              url:http://example.com/settings/* #product #infra
-              {'\n'}
-              tags.sku_class:enterprise #enterprise
-            </CodeBlock>
-          </Block>
-        </Fragment>
-      )}
+      <Fragment>
+        <Description>
+          {tct(
+            'Assign issues based on custom rules. To learn more, [docs:read the docs].',
+            {
+              docs: (
+                <ExternalLink href="https://docs.sentry.io/product/issues/issue-owners/" />
+              ),
+            }
+          )}
+        </Description>
+        <StyledPre>
+          # {t("Here's an example")}
+          <br />
+          path:src/views/checkout {email}
+          <br />
+          url:https://example.com/checkout {email}
+          <br />
+          tags.transaction:/checkout/:page {email}
+        </StyledPre>
+      </Fragment>
       {ownership && (
         <OwnerInput
           {...props}
@@ -77,15 +50,6 @@ export function EditOwnershipRules({ownership, ...props}: EditOwnershipRulesModa
   );
 }
 
-const Block = styled(TextBlock)`
-  margin-bottom: ${space(2)};
-`;
-
-const CodeBlock = styled('pre')`
-  word-break: break-all;
-  white-space: pre-wrap;
-`;
-
 const StyledPre = styled('pre')`
   word-break: break-word;
   padding: ${space(2)};

+ 1 - 1
static/app/views/settings/project/projectOwnership/index.spec.tsx

@@ -71,7 +71,7 @@ describe('Project Ownership', () => {
         {organization: OrganizationFixture({access: ['project:read']})}
       );
 
-      expect(screen.queryByRole('button', {name: 'Edit'})).toBeEnabled();
+      expect(screen.queryByRole('button', {name: 'Edit Rules'})).toBeEnabled();
       expect(screen.getByTestId('project-permission-alert')).toBeInTheDocument();
       // eslint-disable-next-line jest-dom/prefer-in-document
       expect(screen.getAllByTestId('project-permission-alert')).toHaveLength(1);

+ 27 - 73
static/app/views/settings/project/projectOwnership/index.tsx

@@ -20,9 +20,7 @@ import PermissionAlert from 'sentry/views/settings/project/permissionAlert';
 import AddCodeOwnerModal from 'sentry/views/settings/project/projectOwnership/addCodeOwnerModal';
 import {CodeOwnerErrors} from 'sentry/views/settings/project/projectOwnership/codeownerErrors';
 import {CodeOwnerFileTable} from 'sentry/views/settings/project/projectOwnership/codeOwnerFileTable';
-import CodeOwnersPanel from 'sentry/views/settings/project/projectOwnership/codeowners';
 import {OwnershipRulesTable} from 'sentry/views/settings/project/projectOwnership/ownershipRulesTable';
-import RulesPanel from 'sentry/views/settings/project/projectOwnership/rulesPanel';
 
 type Props = {
   organization: Organization;
@@ -35,12 +33,8 @@ type State = {
 } & DeprecatedAsyncView['state'];
 
 class ProjectOwnership extends DeprecatedAsyncView<Props, State> {
-  // TODO: Remove with `streamline-targeting-context`
   getOwnershipTitle() {
-    const {organization} = this.props;
-    return organization.features?.includes('streamline-targeting-context')
-      ? t('Ownership Rules')
-      : t('Issue Owners');
+    return t('Ownership Rules');
   }
 
   getTitle() {
@@ -126,9 +120,6 @@ tags.sku_class:enterprise #enterprise`;
       organization,
       project,
     });
-    const hasStreamlineTargetingContext = organization.features?.includes(
-      'streamline-targeting-context'
-    );
     const hasCodeowners = organization.features?.includes('integrations-codeowners');
 
     return (
@@ -151,25 +142,23 @@ tags.sku_class:enterprise #enterprise`;
                   )}
                 </Access>
               )}
-              {hasStreamlineTargetingContext && (
-                <Button
-                  type="button"
-                  size="sm"
-                  icon={<IconEdit />}
-                  priority="primary"
-                  onClick={() =>
-                    openEditOwnershipRules({
-                      organization,
-                      project,
-                      ownership: ownership!,
-                      onSave: this.handleOwnershipSave,
-                    })
-                  }
-                  disabled={!!ownership && editOwnershipRulesDisabled}
-                >
-                  {t('Edit Rules')}
-                </Button>
-              )}
+              <Button
+                type="button"
+                size="sm"
+                icon={<IconEdit />}
+                priority="primary"
+                onClick={() =>
+                  openEditOwnershipRules({
+                    organization,
+                    project,
+                    ownership: ownership!,
+                    onSave: this.handleOwnershipSave,
+                  })
+                }
+                disabled={!!ownership && editOwnershipRulesDisabled}
+              >
+                {t('Edit Rules')}
+              </Button>
             </ButtonBar>
           }
         />
@@ -194,7 +183,7 @@ tags.sku_class:enterprise #enterprise`;
           projectSlug={project.slug}
           codeowners={codeowners ?? []}
         />
-        {hasStreamlineTargetingContext && ownership && (
+        {ownership && (
           <ErrorBoundary mini>
             <OwnershipRulesTable
               projectRules={ownership.schema?.rules ?? []}
@@ -202,51 +191,16 @@ tags.sku_class:enterprise #enterprise`;
             />
           </ErrorBoundary>
         )}
-        {!hasStreamlineTargetingContext && ownership && (
-          <RulesPanel
-            data-test-id="issueowners-panel"
-            type="issueowners"
-            raw={ownership.raw || ''}
-            dateUpdated={ownership.lastUpdated}
-            placeholder={this.getPlaceholder()}
-            controls={[
-              <Button
-                key="edit"
-                size="xs"
-                onClick={() =>
-                  openEditOwnershipRules({
-                    organization,
-                    project,
-                    ownership,
-                    onSave: this.handleOwnershipSave,
-                  })
-                }
-                disabled={editOwnershipRulesDisabled}
-              >
-                {t('Edit')}
-              </Button>,
-            ]}
+        <PermissionAlert project={project} />
+        {hasCodeowners && (
+          <CodeOwnerFileTable
+            project={project}
+            codeowners={codeowners ?? []}
+            onDelete={this.handleCodeOwnerDeleted}
+            onUpdate={this.handleCodeOwnerUpdated}
+            disabled={disabled}
           />
         )}
-        <PermissionAlert project={project} />
-        {hasCodeowners &&
-          (hasStreamlineTargetingContext ? (
-            <CodeOwnerFileTable
-              project={project}
-              codeowners={codeowners ?? []}
-              onDelete={this.handleCodeOwnerDeleted}
-              onUpdate={this.handleCodeOwnerUpdated}
-              disabled={disabled}
-            />
-          ) : (
-            <CodeOwnersPanel
-              codeowners={codeowners || []}
-              onDelete={this.handleCodeOwnerDeleted}
-              onUpdate={this.handleCodeOwnerUpdated}
-              disabled={disabled}
-              {...this.props}
-            />
-          ))}
         {ownership && (
           <Form
             apiEndpoint={`/projects/${organization.slug}/${project.slug}/ownership/`}

Some files were not shown because too many files changed in this diff