Browse Source

fix(domains) Don't use params.orgId in settings views (#42779)

Under customer-domains settings routes no longer have `params.orgId`
available as the organization slug is no longer a path parameter.
Instead we have to use the organization prop to access the organization
slug.

I've also fixed a problem on the member details view where adding a team
would not trigger a UI rebuild as `member.teams` was mutated in-place.

Fixes #42705
Mark Story 2 years ago
parent
commit
37959551c7

+ 13 - 10
static/app/views/settings/organizationApiKeys/index.tsx

@@ -11,11 +11,7 @@ import AsyncView from 'sentry/views/asyncView';
 import OrganizationApiKeysList from './organizationApiKeysList';
 import {DeprecatedApiKey} from './types';
 
-type RouteParams = {
-  orgId: string;
-};
-
-type Props = RouteComponentProps<RouteParams, {}> & {
+type Props = RouteComponentProps<{}, {}> & {
   organization: Organization;
 };
 
@@ -28,7 +24,8 @@ type State = {
  */
 class OrganizationApiKeys extends AsyncView<Props, State> {
   getEndpoints(): ReturnType<AsyncView['getEndpoints']> {
-    return [['keys', `/organizations/${this.props.params.orgId}/api-keys/`]];
+    const {organization} = this.props;
+    return [['keys', `/organizations/${organization.slug}/api-keys/`]];
   }
 
   getTitle() {
@@ -36,6 +33,7 @@ class OrganizationApiKeys extends AsyncView<Props, State> {
   }
 
   handleRemove = async (id: string) => {
+    const {organization} = this.props;
     const oldKeys = [...this.state.keys];
 
     this.setState(state => ({
@@ -44,7 +42,7 @@ class OrganizationApiKeys extends AsyncView<Props, State> {
 
     try {
       await this.api.requestPromise(
-        `/organizations/${this.props.params.orgId}/api-keys/${id}/`,
+        `/organizations/${organization.slug}/api-keys/${id}/`,
         {
           method: 'DELETE',
           data: {},
@@ -60,10 +58,11 @@ class OrganizationApiKeys extends AsyncView<Props, State> {
     this.setState({
       busy: true,
     });
+    const {organization} = this.props;
 
     try {
       const data = await this.api.requestPromise(
-        `/organizations/${this.props.params.orgId}/api-keys/`,
+        `/organizations/${organization.slug}/api-keys/`,
         {
           method: 'POST',
           data: {},
@@ -74,7 +73,7 @@ class OrganizationApiKeys extends AsyncView<Props, State> {
         this.setState({busy: false});
         browserHistory.push(
           recreateRoute(`${data.id}/`, {
-            params: this.props.params,
+            params: {orgId: organization.slug},
             routes: this.props.routes,
           })
         );
@@ -90,14 +89,18 @@ class OrganizationApiKeys extends AsyncView<Props, State> {
   }
 
   renderBody() {
+    const {organization} = this.props;
+    const params = {orgId: organization.slug};
+
     return (
       <OrganizationApiKeysList
+        {...this.props}
+        params={params}
         loading={this.state.loading}
         busy={this.state.busy}
         keys={this.state.keys}
         onRemove={this.handleRemove}
         onAddApiKey={this.handleAddApiKey}
-        {...this.props}
       />
     );
   }

+ 4 - 3
static/app/views/settings/organizationApiKeys/organizationApiKeyDetails.tsx

@@ -22,7 +22,6 @@ const API_CHOICES: Choices = API_ACCESS_SCOPES.map(s => [s, s]);
 
 type RouteParams = {
   apiKey: string;
-  orgId: string;
 };
 
 type Props = RouteComponentProps<RouteParams, {}> & {
@@ -35,10 +34,11 @@ type State = AsyncView['state'] & {
 
 class OrganizationApiKeyDetails extends AsyncView<Props, State> {
   getEndpoints(): ReturnType<AsyncView['getEndpoints']> {
+    const {organization} = this.props;
     return [
       [
         'apiKey',
-        `/organizations/${this.props.params.orgId}/api-keys/${this.props.params.apiKey}/`,
+        `/organizations/${organization.slug}/api-keys/${this.props.params.apiKey}/`,
       ],
     ];
   }
@@ -65,6 +65,7 @@ class OrganizationApiKeyDetails extends AsyncView<Props, State> {
   };
 
   renderBody() {
+    const {organization} = this.props;
     return (
       <div>
         <SettingsPageHeader title={t('Edit API Key')} />
@@ -73,7 +74,7 @@ class OrganizationApiKeyDetails extends AsyncView<Props, State> {
           <PanelHeader>{t('API Key')}</PanelHeader>
           <ApiForm
             apiMethod="PUT"
-            apiEndpoint={`/organizations/${this.props.params.orgId}/api-keys/${this.props.params.apiKey}/`}
+            apiEndpoint={`/organizations/${organization.slug}/api-keys/${this.props.params.apiKey}/`}
             initialData={this.state.apiKey}
             onSubmitSuccess={this.handleSubmitSuccess}
             onSubmitError={this.handleSubmitError}

+ 6 - 2
static/app/views/settings/organizationMembers/organizationMemberDetail.tsx

@@ -138,8 +138,12 @@ class OrganizationMemberDetail extends AsyncView<Props, State> {
 
   handleAddTeam = (team: Team) => {
     const {member} = this.state;
-    if (!member!.teams.includes(team.slug)) {
-      member!.teams.push(team.slug);
+    if (!member) {
+      return;
+    }
+    const teams = member.teams;
+    if (!teams.includes(team.slug)) {
+      member.teams = [...teams, team.slug];
     }
     this.setState({member});
   };

+ 53 - 77
static/app/views/settings/organizationMembers/organizationMembersList.spec.jsx

@@ -40,27 +40,18 @@ const roles = [
 describe('OrganizationMembersList', function () {
   const members = TestStubs.Members();
   const currentUser = members[1];
-  const defaultProps = {
-    orgId: 'org-slug',
-    orgName: 'Organization Name',
-    status: '',
-    router: {routes: []},
-    requireLink: false,
-    memberCanLeave: false,
-    canAddMembers: false,
-    canRemoveMembers: false,
-    currentUser,
-    onSendInvite: () => {},
-    onRemove: () => {},
-    onLeave: () => {},
-    location: {query: {}},
-  };
   const organization = TestStubs.Organization({
     access: ['member:admin', 'org:admin', 'member:write'],
     status: {
       id: 'active',
     },
   });
+  const defaultProps = {
+    organization,
+    params: {orgId: organization.slug},
+    router: {routes: []},
+    location: {query: {}},
+  };
 
   jest.spyOn(ConfigStore, 'get').mockImplementation(() => currentUser);
 
@@ -71,17 +62,17 @@ describe('OrganizationMembersList', function () {
   beforeEach(function () {
     Client.clearMockResponses();
     Client.addMockResponse({
-      url: '/organizations/org-id/members/me/',
+      url: '/organizations/org-slug/members/me/',
       method: 'GET',
       body: {roles},
     });
     Client.addMockResponse({
-      url: '/organizations/org-id/members/',
+      url: '/organizations/org-slug/members/',
       method: 'GET',
       body: TestStubs.Members(),
     });
     Client.addMockResponse({
-      url: '/organizations/org-id/access-requests/',
+      url: '/organizations/org-slug/access-requests/',
       method: 'GET',
       body: [
         {
@@ -102,7 +93,7 @@ describe('OrganizationMembersList', function () {
       ],
     });
     Client.addMockResponse({
-      url: '/organizations/org-id/auth-provider/',
+      url: '/organizations/org-slug/auth-provider/',
       method: 'GET',
       body: {
         ...TestStubs.AuthProvider(),
@@ -110,12 +101,12 @@ describe('OrganizationMembersList', function () {
       },
     });
     Client.addMockResponse({
-      url: '/organizations/org-id/teams/',
+      url: '/organizations/org-slug/teams/',
       method: 'GET',
       body: TestStubs.Team(),
     });
     Client.addMockResponse({
-      url: '/organizations/org-id/invite-requests/',
+      url: '/organizations/org-slug/invite-requests/',
       method: 'GET',
       body: [],
     });
@@ -125,11 +116,11 @@ describe('OrganizationMembersList', function () {
 
   it('can remove a member', async function () {
     const deleteMock = MockApiClient.addMockResponse({
-      url: `/organizations/org-id/members/${members[0].id}/`,
+      url: `/organizations/org-slug/members/${members[0].id}/`,
       method: 'DELETE',
     });
 
-    render(<OrganizationMembersList {...defaultProps} params={{orgId: 'org-id'}} />, {
+    render(<OrganizationMembersList {...defaultProps} />, {
       context: TestStubs.routerContext([{organization}]),
     });
 
@@ -147,12 +138,12 @@ describe('OrganizationMembersList', function () {
 
   it('displays error message when failing to remove member', async function () {
     const deleteMock = MockApiClient.addMockResponse({
-      url: `/organizations/org-id/members/${members[0].id}/`,
+      url: `/organizations/org-slug/members/${members[0].id}/`,
       method: 'DELETE',
       statusCode: 500,
     });
 
-    render(<OrganizationMembersList {...defaultProps} params={{orgId: 'org-id'}} />, {
+    render(<OrganizationMembersList {...defaultProps} />, {
       context: TestStubs.routerContext([{organization}]),
     });
 
@@ -170,11 +161,11 @@ describe('OrganizationMembersList', function () {
 
   it('can leave org', async function () {
     const deleteMock = Client.addMockResponse({
-      url: `/organizations/org-id/members/${members[1].id}/`,
+      url: `/organizations/org-slug/members/${members[1].id}/`,
       method: 'DELETE',
     });
 
-    render(<OrganizationMembersList {...defaultProps} params={{orgId: 'org-id'}} />, {
+    render(<OrganizationMembersList {...defaultProps} />, {
       context: TestStubs.routerContext([{organization}]),
     });
 
@@ -192,7 +183,7 @@ describe('OrganizationMembersList', function () {
 
   it('can redirect to remaining org after leaving', async function () {
     const deleteMock = Client.addMockResponse({
-      url: `/organizations/org-id/members/${members[1].id}/`,
+      url: `/organizations/org-slug/members/${members[1].id}/`,
       method: 'DELETE',
     });
     const secondOrg = TestStubs.Organization({
@@ -203,7 +194,7 @@ describe('OrganizationMembersList', function () {
     });
     OrganizationsStore.addOrReplace(secondOrg);
 
-    render(<OrganizationMembersList {...defaultProps} params={{orgId: 'org-id'}} />, {
+    render(<OrganizationMembersList {...defaultProps} />, {
       context: TestStubs.routerContext([{organization}]),
     });
 
@@ -222,12 +213,12 @@ describe('OrganizationMembersList', function () {
 
   it('displays error message when failing to leave org', async function () {
     const deleteMock = Client.addMockResponse({
-      url: `/organizations/org-id/members/${members[1].id}/`,
+      url: `/organizations/org-slug/members/${members[1].id}/`,
       method: 'DELETE',
       statusCode: 500,
     });
 
-    render(<OrganizationMembersList {...defaultProps} params={{orgId: 'org-id'}} />, {
+    render(<OrganizationMembersList {...defaultProps} />, {
       context: TestStubs.routerContext([{organization}]),
     });
 
@@ -245,14 +236,14 @@ describe('OrganizationMembersList', function () {
 
   it('can re-send SSO link to member', function () {
     const inviteMock = MockApiClient.addMockResponse({
-      url: `/organizations/org-id/members/${members[0].id}/`,
+      url: `/organizations/org-slug/members/${members[0].id}/`,
       method: 'PUT',
       body: {
         id: '1234',
       },
     });
 
-    render(<OrganizationMembersList {...defaultProps} params={{orgId: 'org-id'}} />, {
+    render(<OrganizationMembersList {...defaultProps} />, {
       context: TestStubs.routerContext([{organization}]),
     });
 
@@ -264,14 +255,14 @@ describe('OrganizationMembersList', function () {
 
   it('can re-send invite to member', function () {
     const inviteMock = MockApiClient.addMockResponse({
-      url: `/organizations/org-id/members/${members[1].id}/`,
+      url: `/organizations/org-slug/members/${members[1].id}/`,
       method: 'PUT',
       body: {
         id: '1234',
       },
     });
 
-    render(<OrganizationMembersList {...defaultProps} params={{orgId: 'org-id'}} />, {
+    render(<OrganizationMembersList {...defaultProps} />, {
       context: TestStubs.routerContext([{organization}]),
     });
 
@@ -283,20 +274,20 @@ describe('OrganizationMembersList', function () {
 
   it('can search organization members', function () {
     const searchMock = MockApiClient.addMockResponse({
-      url: '/organizations/org-id/members/',
+      url: '/organizations/org-slug/members/',
       body: [],
     });
 
     const routerContext = TestStubs.routerContext();
 
-    render(<OrganizationMembersList {...defaultProps} params={{orgId: 'org-id'}} />, {
+    render(<OrganizationMembersList {...defaultProps} />, {
       context: routerContext,
     });
 
     userEvent.type(screen.getByPlaceholderText('Search Members'), 'member');
 
     expect(searchMock).toHaveBeenLastCalledWith(
-      '/organizations/org-id/members/',
+      '/organizations/org-slug/members/',
       expect.objectContaining({
         method: 'GET',
         query: {
@@ -312,11 +303,11 @@ describe('OrganizationMembersList', function () {
 
   it('can filter members', function () {
     const searchMock = MockApiClient.addMockResponse({
-      url: '/organizations/org-id/members/',
+      url: '/organizations/org-slug/members/',
       body: [],
     });
     const routerContext = TestStubs.routerContext();
-    render(<OrganizationMembersList {...defaultProps} params={{orgId: 'org-id'}} />, {
+    render(<OrganizationMembersList {...defaultProps} />, {
       context: routerContext,
     });
 
@@ -324,7 +315,7 @@ describe('OrganizationMembersList', function () {
     userEvent.click(screen.getByRole('checkbox', {name: 'Member'}));
 
     expect(searchMock).toHaveBeenLastCalledWith(
-      '/organizations/org-id/members/',
+      '/organizations/org-slug/members/',
       expect.objectContaining({
         method: 'GET',
         query: {query: 'role:member'},
@@ -341,7 +332,7 @@ describe('OrganizationMembersList', function () {
       userEvent.click(screen.getByRole('checkbox', {name: `Enable ${label} filter`}));
 
       expect(searchMock).toHaveBeenLastCalledWith(
-        '/organizations/org-id/members/',
+        '/organizations/org-slug/members/',
         expect.objectContaining({
           method: 'GET',
           query: {query: `${filter}:true`},
@@ -351,7 +342,7 @@ describe('OrganizationMembersList', function () {
       userEvent.click(screen.getByRole('checkbox', {name: `Toggle ${label}`}));
 
       expect(searchMock).toHaveBeenLastCalledWith(
-        '/organizations/org-id/members/',
+        '/organizations/org-slug/members/',
         expect.objectContaining({
           method: 'GET',
           query: {query: `${filter}:false`},
@@ -387,20 +378,20 @@ describe('OrganizationMembersList', function () {
         },
       });
       MockApiClient.addMockResponse({
-        url: '/organizations/org-id/invite-requests/',
+        url: '/organizations/org-slug/invite-requests/',
         method: 'GET',
         body: [inviteRequest],
       });
       MockApiClient.addMockResponse({
-        url: `/organizations/org-id/invite-requests/${inviteRequest.id}/`,
+        url: `/organizations/org-slug/invite-requests/${inviteRequest.id}/`,
         method: 'PUT',
       });
 
       render(
         <OrganizationMembersList
           {...defaultProps}
-          params={{orgId: 'org-id'}}
           organization={org}
+          params={{orgId: org.slug}}
         />,
         {context: TestStubs.routerContext([{organization: org}])}
       );
@@ -417,23 +408,18 @@ describe('OrganizationMembersList', function () {
         },
       });
       MockApiClient.addMockResponse({
-        url: '/organizations/org-id/invite-requests/',
+        url: '/organizations/org-slug/invite-requests/',
         method: 'GET',
         body: [inviteRequest],
       });
       MockApiClient.addMockResponse({
-        url: `/organizations/org-id/invite-requests/${inviteRequest.id}/`,
+        url: `/organizations/org-slug/invite-requests/${inviteRequest.id}/`,
         method: 'PUT',
       });
 
-      render(
-        <OrganizationMembersList
-          {...defaultProps}
-          params={{orgId: 'org-id'}}
-          organization={org}
-        />,
-        {context: TestStubs.routerContext([{organization: org}])}
-      );
+      render(<OrganizationMembersList {...defaultProps} />, {
+        context: TestStubs.routerContext([{organization: org}]),
+      });
 
       expect(screen.getByText('Pending Members')).toBeInTheDocument();
 
@@ -462,23 +448,18 @@ describe('OrganizationMembersList', function () {
         },
       });
       MockApiClient.addMockResponse({
-        url: '/organizations/org-id/invite-requests/',
+        url: '/organizations/org-slug/invite-requests/',
         method: 'GET',
         body: [joinRequest],
       });
       MockApiClient.addMockResponse({
-        url: `/organizations/org-id/invite-requests/${joinRequest.id}/`,
+        url: `/organizations/org-slug/invite-requests/${joinRequest.id}/`,
         method: 'DELETE',
       });
 
-      render(
-        <OrganizationMembersList
-          {...defaultProps}
-          params={{orgId: 'org-id'}}
-          organization={org}
-        />,
-        {context: TestStubs.routerContext([{organization: org}])}
-      );
+      render(<OrganizationMembersList {...defaultProps} />, {
+        context: TestStubs.routerContext([{organization: org}]),
+      });
 
       expect(screen.getByText('Pending Members')).toBeInTheDocument();
 
@@ -501,24 +482,19 @@ describe('OrganizationMembersList', function () {
         },
       });
       MockApiClient.addMockResponse({
-        url: '/organizations/org-id/invite-requests/',
+        url: '/organizations/org-slug/invite-requests/',
         method: 'GET',
         body: [inviteRequest],
       });
 
       const updateWithApprove = MockApiClient.addMockResponse({
-        url: `/organizations/org-id/invite-requests/${inviteRequest.id}/`,
+        url: `/organizations/org-slug/invite-requests/${inviteRequest.id}/`,
         method: 'PUT',
       });
 
-      render(
-        <OrganizationMembersList
-          {...defaultProps}
-          params={{orgId: 'org-id'}}
-          organization={org}
-        />,
-        {context: TestStubs.routerContext([{organization: org}])}
-      );
+      render(<OrganizationMembersList {...defaultProps} />, {
+        context: TestStubs.routerContext([{organization: org}]),
+      });
 
       await selectEvent.select(screen.getAllByRole('textbox')[1], ['Admin']);
 
@@ -528,7 +504,7 @@ describe('OrganizationMembersList', function () {
       userEvent.click(screen.getByTestId('confirm-button'));
 
       expect(updateWithApprove).toHaveBeenCalledWith(
-        `/organizations/org-id/invite-requests/${inviteRequest.id}/`,
+        `/organizations/org-slug/invite-requests/${inviteRequest.id}/`,
         expect.objectContaining({data: expect.objectContaining({role: 'admin'})})
       );
     });

+ 12 - 11
static/app/views/settings/organizationMembers/organizationMembersList.tsx

@@ -34,7 +34,7 @@ import OrganizationMemberRow from './organizationMemberRow';
 
 type Props = {
   organization: Organization;
-} & RouteComponentProps<{orgId: string}, {}>;
+} & RouteComponentProps<{}, {}>;
 
 type State = AsyncView['state'] & {
   inviteRequests: Member[];
@@ -68,24 +68,24 @@ class OrganizationMembersList extends AsyncView<Props, State> {
   }
 
   getEndpoints(): ReturnType<AsyncView['getEndpoints']> {
-    const {orgId} = this.props.params;
+    const {organization} = this.props;
 
     return [
-      ['members', `/organizations/${orgId}/members/`, {}, {paginate: true}],
+      ['members', `/organizations/${organization.slug}/members/`, {}, {paginate: true}],
       [
         'member',
-        `/organizations/${orgId}/members/me/`,
+        `/organizations/${organization.slug}/members/me/`,
         {},
         {allowError: error => error.status === 404},
       ],
       [
         'authProvider',
-        `/organizations/${orgId}/auth-provider/`,
+        `/organizations/${organization.slug}/auth-provider/`,
         {},
         {allowError: error => error.status === 403},
       ],
 
-      ['inviteRequests', `/organizations/${orgId}/invite-requests/`],
+      ['inviteRequests', `/organizations/${organization.slug}/invite-requests/`],
     ];
   }
 
@@ -95,9 +95,9 @@ class OrganizationMembersList extends AsyncView<Props, State> {
   }
 
   removeMember = async (id: string) => {
-    const {orgId} = this.props.params;
+    const {organization} = this.props;
 
-    await this.api.requestPromise(`/organizations/${orgId}/members/${id}/`, {
+    await this.api.requestPromise(`/organizations/${organization.slug}/members/${id}/`, {
       method: 'DELETE',
       data: {},
     });
@@ -140,10 +140,11 @@ class OrganizationMembersList extends AsyncView<Props, State> {
     this.setState(state => ({
       invited: {...state.invited, [id]: 'loading'},
     }));
+    const {organization} = this.props;
 
     try {
       await resendMemberInvite(this.api, {
-        orgId: this.props.params.orgId,
+        orgId: organization.slug,
         memberId: id,
         regenerate: expired,
       });
@@ -179,7 +180,7 @@ class OrganizationMembersList extends AsyncView<Props, State> {
     errorMessage,
     eventKey,
   }) => {
-    const {params, organization} = this.props;
+    const {organization} = this.props;
 
     this.setState(state => ({
       inviteRequestBusy: {...state.inviteRequestBusy, [inviteRequest.id]: true},
@@ -187,7 +188,7 @@ class OrganizationMembersList extends AsyncView<Props, State> {
 
     try {
       await this.api.requestPromise(
-        `/organizations/${params.orgId}/invite-requests/${inviteRequest.id}/`,
+        `/organizations/${organization.slug}/invite-requests/${inviteRequest.id}/`,
         {
           method,
           data,

+ 17 - 15
static/app/views/settings/organizationTeams/teamMembers.tsx

@@ -32,7 +32,6 @@ import AsyncView from 'sentry/views/asyncView';
 import TeamMembersRow from './teamMembersRow';
 
 type RouteParams = {
-  orgId: string;
   teamId: string;
 };
 
@@ -74,13 +73,15 @@ class TeamMembers extends AsyncView<Props, State> {
   );
 
   fetchMembersRequest = async (query: string) => {
-    const {params, api} = this.props;
-    const {orgId} = params;
+    const {organization, api} = this.props;
 
     try {
-      const data = await api.requestPromise(`/organizations/${orgId}/members/`, {
-        query: {query},
-      });
+      const data = await api.requestPromise(
+        `/organizations/${organization.slug}/members/`,
+        {
+          query: {query},
+        }
+      );
       this.setState({
         orgMembers: data,
         dropdownBusy: false,
@@ -97,12 +98,12 @@ class TeamMembers extends AsyncView<Props, State> {
   };
 
   getEndpoints(): ReturnType<AsyncView['getEndpoints']> {
-    const {params} = this.props;
+    const {organization, params} = this.props;
 
     return [
       [
         'teamMembers',
-        `/teams/${params.orgId}/${params.teamId}/members/`,
+        `/teams/${organization.slug}/${params.teamId}/members/`,
         {},
         {paginate: true},
       ],
@@ -110,7 +111,7 @@ class TeamMembers extends AsyncView<Props, State> {
   }
 
   addTeamMember = (selection: Item) => {
-    const {params} = this.props;
+    const {organization, params} = this.props;
     const {orgMembers, teamMembers} = this.state;
 
     this.setState({loading: true});
@@ -121,7 +122,7 @@ class TeamMembers extends AsyncView<Props, State> {
     joinTeam(
       this.props.api,
       {
-        orgId: params.orgId,
+        orgId: organization.slug,
         teamId: params.teamId,
         memberId: selection.value,
       },
@@ -147,12 +148,12 @@ class TeamMembers extends AsyncView<Props, State> {
   };
 
   removeTeamMember = (member: Member) => {
-    const {params} = this.props;
+    const {organization, params} = this.props;
     const {teamMembers} = this.state;
     leaveTeam(
       this.props.api,
       {
-        orgId: params.orgId,
+        orgId: organization.slug,
         teamId: params.teamId,
         memberId: member.id,
       },
@@ -172,8 +173,9 @@ class TeamMembers extends AsyncView<Props, State> {
   };
 
   updateTeamMemberRole = (member: Member, newRole: string) => {
-    const {orgId, teamId} = this.props.params;
-    const endpoint = `/organizations/${orgId}/members/${member.id}/teams/${teamId}/`;
+    const {organization} = this.props;
+    const {teamId} = this.props.params;
+    const endpoint = `/organizations/${organization.slug}/members/${member.id}/teams/${teamId}/`;
 
     this.props.api.request(endpoint, {
       method: 'PUT',
@@ -252,7 +254,7 @@ class TeamMembers extends AsyncView<Props, State> {
             : selection =>
                 openTeamAccessRequestModal({
                   teamId: params.teamId,
-                  orgId: params.orgId,
+                  orgId: organization.slug,
                   memberId: selection.value,
                 })
         }

+ 1 - 0
static/app/views/settings/project/projectEnvironments.spec.jsx

@@ -17,6 +17,7 @@ function renderComponent(isHidden) {
         projectId: project.slug,
       }}
       location={{pathname}}
+      organization={org}
       routes={[]}
     />
   );

+ 14 - 8
static/app/views/settings/project/projectEnvironments.tsx

@@ -15,7 +15,7 @@ import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
 import {ALL_ENVIRONMENTS_KEY} from 'sentry/constants';
 import {t, tct} from 'sentry/locale';
 import space from 'sentry/styles/space';
-import {Environment, Project} from 'sentry/types';
+import {Environment, Organization, Project} from 'sentry/types';
 import {getDisplayName, getUrlRoutingName} from 'sentry/utils/environment';
 import recreateRoute from 'sentry/utils/recreateRoute';
 import withApi from 'sentry/utils/withApi';
@@ -24,7 +24,8 @@ import PermissionAlert from 'sentry/views/settings/project/permissionAlert';
 
 type Props = {
   api: Client;
-} & RouteComponentProps<{orgId: string; projectId: string}, {}>;
+  organization: Organization;
+} & RouteComponentProps<{projectId: string}, {}>;
 
 type State = {
   environments: null | Environment[];
@@ -59,8 +60,9 @@ class ProjectEnvironments extends Component<Props, State> {
       this.setState({isLoading: true});
     }
 
-    const {orgId, projectId} = this.props.params;
-    this.props.api.request(`/projects/${orgId}/${projectId}/environments/`, {
+    const {organization} = this.props;
+    const {projectId} = this.props.params;
+    this.props.api.request(`/projects/${organization.slug}/${projectId}/environments/`, {
       query: {
         visibility: isHidden ? 'hidden' : 'visible',
       },
@@ -71,8 +73,9 @@ class ProjectEnvironments extends Component<Props, State> {
   }
 
   fetchProjectDetails() {
-    const {orgId, projectId} = this.props.params;
-    this.props.api.request(`/projects/${orgId}/${projectId}/`, {
+    const {organization} = this.props;
+    const {projectId} = this.props.params;
+    this.props.api.request(`/projects/${organization.slug}/${projectId}/`, {
       success: project => {
         this.setState({project});
       },
@@ -81,10 +84,13 @@ class ProjectEnvironments extends Component<Props, State> {
 
   // Toggle visibility of environment
   toggleEnv = (env: Environment, shouldHide: boolean) => {
-    const {orgId, projectId} = this.props.params;
+    const {organization} = this.props;
+    const {projectId} = this.props.params;
 
     this.props.api.request(
-      `/projects/${orgId}/${projectId}/environments/${getUrlRoutingName(env)}/`,
+      `/projects/${organization.slug}/${projectId}/environments/${getUrlRoutingName(
+        env
+      )}/`,
       {
         method: 'PUT',
         data: {

+ 6 - 1
static/app/views/settings/project/projectFilters/index.spec.jsx

@@ -21,6 +21,7 @@ describe('ProjectFilters', function () {
         params={{projectId: project.slug, orgId: org.slug}}
         location={{}}
         project={project}
+        organization={org}
       />
     );
   }
@@ -200,6 +201,7 @@ describe('ProjectFilters', function () {
   it('has custom inbound filters with flag + can change', function () {
     render(
       <ProjectFilters
+        organization={org}
         params={{projectId: project.slug, orgId: org.slug}}
         location={{}}
         project={{
@@ -240,8 +242,9 @@ describe('ProjectFilters', function () {
 
     render(
       <ProjectFilters
-        params={{projectId: project.slug, orgId: org.slug}}
+        organization={org}
         location={{}}
+        params={{projectId: project.slug, orgId: org.slug}}
         project={project}
       />,
       {context}
@@ -255,6 +258,7 @@ describe('ProjectFilters', function () {
   it('shows disclaimer if error message filter is populated', function () {
     render(
       <ProjectFilters
+        organization={org}
         params={{projectId: project.slug, orgId: org.slug}}
         project={{
           ...project,
@@ -283,6 +287,7 @@ describe('ProjectFilters', function () {
 
     render(
       <ProjectFilters
+        organization={org}
         params={{
           projectId: discardProject.slug,
           orgId: discardOrg.slug,

+ 17 - 7
static/app/views/settings/project/projectFilters/index.tsx

@@ -5,7 +5,7 @@ import Link from 'sentry/components/links/link';
 import NavTabs from 'sentry/components/navTabs';
 import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
 import {t} from 'sentry/locale';
-import {Project} from 'sentry/types';
+import {Organization, Project} from 'sentry/types';
 import recreateRoute from 'sentry/utils/recreateRoute';
 import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
 import TextBlock from 'sentry/views/settings/components/text/textBlock';
@@ -15,12 +15,13 @@ import ProjectFiltersChart from 'sentry/views/settings/project/projectFilters/pr
 import ProjectFiltersSettings from 'sentry/views/settings/project/projectFilters/projectFiltersSettings';
 
 type Props = {
+  organization: Organization;
   project: Project;
-} & RouteComponentProps<{filterType: string; orgId: string; projectId: string}, {}>;
+} & RouteComponentProps<{filterType: string; projectId: string}, {}>;
 
 function ProjectFilters(props: Props) {
-  const {project, params, location} = props;
-  const {orgId, projectId, filterType} = params;
+  const {organization, project, params, location} = props;
+  const {projectId, filterType} = params;
   if (!project) {
     return null;
   }
@@ -40,7 +41,7 @@ function ProjectFilters(props: Props) {
       </TextBlock>
 
       <div>
-        <ProjectFiltersChart project={project} params={params} />
+        <ProjectFiltersChart project={project} organization={organization} />
 
         {features.has('discard-groups') && (
           <NavTabs underlined style={{paddingTop: '30px'}}>
@@ -58,9 +59,18 @@ function ProjectFilters(props: Props) {
         )}
 
         {filterType === 'discarded-groups' ? (
-          <GroupTombstones orgId={orgId} projectId={project.slug} location={location} />
+          <GroupTombstones
+            orgId={organization.slug}
+            projectId={project.slug}
+            location={location}
+          />
         ) : (
-          <ProjectFiltersSettings project={project} params={params} features={features} />
+          <ProjectFiltersSettings
+            organization={organization}
+            project={project}
+            params={params}
+            features={features}
+          />
         )}
       </div>
     </Fragment>

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