Browse Source

fix(proj-creation): Add toast for proj creation (#50127)

Include a toast for creating a project. If using the project creation flow, the toast will include info about the created team. Also show a toast on project creation failure.
Seiji Chew 1 year ago
parent
commit
01592b6a97

+ 3 - 1
src/sentry/api/endpoints/organization_projects_experiment.py

@@ -188,5 +188,7 @@ class OrganizationProjectsExperimentEndpoint(OrganizationEndpoint):
             "created team through project creation flow",
             extra={"team_slug": default_team_slug, "project_slug": project_name},
         )
+        serialized_response = serialize(project, request.user)
+        serialized_response["team_slug"] = team.slug
 
-        return Response(serialize(project, request.user), status=201)
+        return Response(serialized_response, status=201)

+ 122 - 1
static/app/views/projectInstall/createProject.spec.tsx

@@ -7,12 +7,16 @@ import {
   waitFor,
 } from 'sentry-test/reactTestingLibrary';
 
+import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
+import {tct} from 'sentry/locale';
 import OrganizationStore from 'sentry/stores/organizationStore';
 import TeamStore from 'sentry/stores/teamStore';
 import {Organization} from 'sentry/types';
 import * as useExperiment from 'sentry/utils/useExperiment';
 import {CreateProject} from 'sentry/views/projectInstall/createProject';
 
+jest.mock('sentry/actionCreators/indicator');
+
 function renderFrameworkModalMockRequests({
   organization,
   teamSlug,
@@ -43,9 +47,16 @@ function renderFrameworkModalMockRequests({
   const projectCreationMockRequest = MockApiClient.addMockResponse({
     url: `/teams/${organization.slug}/${teamSlug}/projects/`,
     method: 'POST',
+    body: {slug: 'testProj'},
   });
 
-  return {projectCreationMockRequest};
+  const experimentalprojectCreationMockRequest = MockApiClient.addMockResponse({
+    url: `/organizations/${organization.slug}/experimental/projects/`,
+    method: 'POST',
+    body: {slug: 'testProj', team_slug: 'testTeam'},
+  });
+
+  return {projectCreationMockRequest, experimentalprojectCreationMockRequest};
 }
 
 describe('CreateProject', function () {
@@ -267,6 +278,116 @@ describe('CreateProject', function () {
     expect(container).toSnapshot();
   });
 
+  it('should display success message on proj creation', async function () {
+    const {organization} = initializeOrg({
+      organization: {
+        access: ['project:read'],
+      },
+    });
+
+    const frameWorkModalMockRequests = renderFrameworkModalMockRequests({
+      organization,
+      teamSlug: teamWithAccess.slug,
+    });
+    TeamStore.loadUserTeams([teamWithAccess]);
+
+    render(<CreateProject />, {
+      organization,
+    });
+
+    renderGlobalModal();
+    await userEvent.click(screen.getByTestId('platform-apple-ios'));
+    await userEvent.click(screen.getByRole('button', {name: 'Create Project'}));
+
+    expect(frameWorkModalMockRequests.projectCreationMockRequest).toHaveBeenCalledTimes(
+      1
+    );
+    expect(addSuccessMessage).toHaveBeenCalledWith(
+      tct('Created project [project]', {
+        project: 'testProj',
+      })
+    );
+  });
+
+  it('should display error message on proj creation failure', async function () {
+    const {organization} = initializeOrg({
+      organization: {
+        access: ['project:read'],
+      },
+    });
+
+    const frameWorkModalMockRequests = renderFrameworkModalMockRequests({
+      organization,
+      teamSlug: teamWithAccess.slug,
+    });
+    frameWorkModalMockRequests.projectCreationMockRequest = MockApiClient.addMockResponse(
+      {
+        url: `/teams/${organization.slug}/${teamWithAccess.slug}/projects/`,
+        method: 'POST',
+        body: {slug: 'testProj'},
+        statusCode: 404,
+      }
+    );
+    TeamStore.loadUserTeams([teamWithAccess]);
+
+    render(<CreateProject />, {
+      organization,
+    });
+
+    renderGlobalModal();
+    await userEvent.click(screen.getByTestId('platform-apple-ios'));
+    await userEvent.click(screen.getByRole('button', {name: 'Create Project'}));
+
+    expect(frameWorkModalMockRequests.projectCreationMockRequest).toHaveBeenCalledTimes(
+      1
+    );
+    expect(addErrorMessage).toHaveBeenCalledWith(
+      tct('Failed to create project [project]', {
+        project: 'apple-ios',
+      })
+    );
+  });
+
+  it('should display success message when using experimental endpoint', async function () {
+    const {organization} = initializeOrg({
+      organization: {
+        access: ['project:read'],
+        features: ['team-project-creation-all'],
+      },
+    });
+
+    const frameWorkModalMockRequests = renderFrameworkModalMockRequests({
+      organization,
+      teamSlug: teamNoAccess.slug,
+    });
+    render(<CreateProject />, {
+      context: TestStubs.routerContext([
+        {
+          organization: {
+            id: '1',
+            slug: 'testOrg',
+            access: ['project:read'],
+          },
+        },
+      ]),
+      organization,
+    });
+
+    renderGlobalModal();
+    await userEvent.click(screen.getByTestId('platform-apple-ios'));
+    await userEvent.click(screen.getByRole('button', {name: 'Create Project'}));
+
+    expect(
+      frameWorkModalMockRequests.experimentalprojectCreationMockRequest
+    ).toHaveBeenCalledTimes(1);
+    expect(addSuccessMessage).toHaveBeenCalledWith(
+      tct('Created [project] under new team [team]', {
+        project: 'testProj',
+        team: '#testTeam',
+      })
+    );
+  });
+
   it('does not render framework selection modal if vanilla js is NOT selected', async function () {
     const {organization} = initializeOrg({
       organization: {

+ 32 - 14
static/app/views/projectInstall/createProject.tsx

@@ -6,7 +6,7 @@ import omit from 'lodash/omit';
 import startCase from 'lodash/startCase';
 import {PlatformIcon} from 'platformicons';
 
-import {addErrorMessage} from 'sentry/actionCreators/indicator';
+import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
 import {openCreateTeamModal, openModal} from 'sentry/actionCreators/modal';
 import Access from 'sentry/components/acl/access';
 import {Alert} from 'sentry/components/alert';
@@ -105,19 +105,17 @@ function CreateProject() {
       setInFlight(true);
 
       try {
-        const projectData = await api.requestPromise(
-          team
-            ? `/teams/${slug}/${team}/projects/`
-            : `/organizations/${slug}/experimental/projects/`,
-          {
-            method: 'POST',
-            data: {
-              name: projectName,
-              platform: selectedPlatform.key,
-              default_rules: defaultRules ?? true,
-            },
-          }
-        );
+        const url = team
+          ? `/teams/${slug}/${team}/projects/`
+          : `/organizations/${slug}/experimental/projects/`;
+        const projectData = await api.requestPromise(url, {
+          method: 'POST',
+          data: {
+            name: projectName,
+            platform: selectedPlatform.key,
+            default_rules: defaultRules ?? true,
+          },
+        });
 
         let ruleId: string | undefined;
         if (shouldCreateCustomRule) {
@@ -149,6 +147,21 @@ function CreateProject() {
 
         ProjectsStore.onCreateSuccess(projectData, organization.slug);
 
+        if (team) {
+          addSuccessMessage(
+            tct('Created project [project]', {
+              project: `${projectData.slug}`,
+            })
+          );
+        } else {
+          addSuccessMessage(
+            tct('Created [project] under new team [team]', {
+              project: `${projectData.slug}`,
+              team: `#${projectData.team_slug}`,
+            })
+          );
+        }
+
         browserHistory.push(
           normalizeUrl(
             `/organizations/${organization.slug}/projects/${projectData.slug}/getting-started/`
@@ -157,6 +170,11 @@ function CreateProject() {
       } catch (err) {
         setInFlight(false);
         setErrors(err.responseJSON);
+        addErrorMessage(
+          tct('Failed to create project [project]', {
+            project: `${projectName}`,
+          })
+        );
 
         // Only log this if the error is something other than:
         // * The user not having access to create a project, or,