diff --git a/ee/api/test/test_project.py b/ee/api/test/test_project.py index 3c8880954fa3f..47fbfbb8d52b2 100644 --- a/ee/api/test/test_project.py +++ b/ee/api/test/test_project.py @@ -105,6 +105,16 @@ def test_user_that_does_not_belong_to_an_org_cannot_create_a_projec(self): }, ) + def test_rename_project_as_org_member_allowed(self): + self.organization_membership.level = OrganizationMembership.Level.MEMBER + self.organization_membership.save() + + response = self.client.patch(f"/api/projects/@current/", {"name": "Erinaceus europaeus"}) + self.project.refresh_from_db() + + self.assertEqual(response.status_code, 200) + self.assertEqual(self.project.name, "Erinaceus europaeus") + def test_list_projects_restricted_ones_hidden(self): self.organization_membership.level = OrganizationMembership.Level.MEMBER self.organization_membership.save() diff --git a/ee/api/test/test_team.py b/ee/api/test/test_team.py index dafdd6d9def8a..7cbb968fb9f3c 100644 --- a/ee/api/test/test_team.py +++ b/ee/api/test/test_team.py @@ -184,61 +184,6 @@ def test_no_delete_team_not_belonging_to_organization(self): # Updating projects - def test_rename_team_as_org_member_allowed(self): - self.organization_membership.level = OrganizationMembership.Level.MEMBER - self.organization_membership.save() - - response = self.client.patch(f"/api/environments/@current/", {"name": "Erinaceus europaeus"}) - self.team.refresh_from_db() - - self.assertEqual(response.status_code, HTTP_200_OK) - self.assertEqual(self.team.name, "Erinaceus europaeus") - - def test_rename_private_team_as_org_member_forbidden(self): - self.organization_membership.level = OrganizationMembership.Level.MEMBER - self.organization_membership.save() - self.team.access_control = True - self.team.save() - - response = self.client.patch(f"/api/environments/@current/", {"name": "Acherontia atropos"}) - self.team.refresh_from_db() - - self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) - self.assertEqual(self.team.name, "Default project") - - def test_rename_private_team_current_as_org_outsider_forbidden(self): - self.organization_membership.delete() - - response = self.client.patch(f"/api/environments/@current/", {"name": "Acherontia atropos"}) - self.team.refresh_from_db() - - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - def test_rename_private_team_id_as_org_outsider_forbidden(self): - self.organization_membership.delete() - - response = self.client.patch(f"/api/environments/{self.team.id}/", {"name": "Acherontia atropos"}) - self.team.refresh_from_db() - - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - def test_rename_private_team_as_org_member_and_team_member_allowed(self): - self.organization_membership.level = OrganizationMembership.Level.MEMBER - self.organization_membership.save() - self.team.access_control = True - self.team.save() - ExplicitTeamMembership.objects.create( - team=self.team, - parent_membership=self.organization_membership, - level=ExplicitTeamMembership.Level.MEMBER, - ) - - response = self.client.patch(f"/api/environments/@current/", {"name": "Acherontia atropos"}) - self.team.refresh_from_db() - - self.assertEqual(response.status_code, HTTP_200_OK) - self.assertEqual(self.team.name, "Acherontia atropos") - def test_enable_access_control_as_org_member_forbidden(self): self.organization_membership.level = OrganizationMembership.Level.MEMBER self.organization_membership.save() @@ -608,6 +553,61 @@ def test_cannot_create_team_in_project_without_org_access(self): self.not_found_response("Organization not found."), ) + def test_rename_team_as_org_member_allowed(self): + self.organization_membership.level = OrganizationMembership.Level.MEMBER + self.organization_membership.save() + + response = self.client.patch(f"/api/environments/@current/", {"name": "Erinaceus europaeus"}) + self.team.refresh_from_db() + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(self.team.name, "Erinaceus europaeus") + + def test_rename_private_team_as_org_member_forbidden(self): + self.organization_membership.level = OrganizationMembership.Level.MEMBER + self.organization_membership.save() + self.team.access_control = True + self.team.save() + + response = self.client.patch(f"/api/environments/@current/", {"name": "Acherontia atropos"}) + self.team.refresh_from_db() + + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + self.assertEqual(self.team.name, "Default project") + + def test_rename_private_team_current_as_org_outsider_forbidden(self): + self.organization_membership.delete() + + response = self.client.patch(f"/api/environments/@current/", {"name": "Acherontia atropos"}) + self.team.refresh_from_db() + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + def test_rename_private_team_id_as_org_outsider_forbidden(self): + self.organization_membership.delete() + + response = self.client.patch(f"/api/environments/{self.team.id}/", {"name": "Acherontia atropos"}) + self.team.refresh_from_db() + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + def test_rename_private_team_as_org_member_and_team_member_allowed(self): + self.organization_membership.level = OrganizationMembership.Level.MEMBER + self.organization_membership.save() + self.team.access_control = True + self.team.save() + ExplicitTeamMembership.objects.create( + team=self.team, + parent_membership=self.organization_membership, + level=ExplicitTeamMembership.Level.MEMBER, + ) + + response = self.client.patch(f"/api/environments/@current/", {"name": "Acherontia atropos"}) + self.team.refresh_from_db() + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(self.team.name, "Acherontia atropos") + def test_list_teams_restricted_ones_hidden(self): self.organization_membership.level = OrganizationMembership.Level.MEMBER self.organization_membership.save() diff --git a/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--dark.png b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--dark.png index ee27b11bed568..64260538a2c7b 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--dark.png and b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--dark.png differ diff --git a/frontend/src/scenes/projectLogic.ts b/frontend/src/scenes/projectLogic.ts index 8ebb2c2ee207f..d2b71d5f5e35e 100644 --- a/frontend/src/scenes/projectLogic.ts +++ b/frontend/src/scenes/projectLogic.ts @@ -8,6 +8,7 @@ import { getAppContext } from 'lib/utils/getAppContext' import { ProjectType } from '~/types' +import { organizationLogic } from './organizationLogic' import type { projectLogicType } from './projectLogicType' import { userLogic } from './userLogic' @@ -19,7 +20,7 @@ export const projectLogic = kea([ deleteProjectFailure: true, }), connect(() => ({ - actions: [userLogic, ['loadUser', 'switchTeam']], + actions: [userLogic, ['loadUser', 'switchTeam'], organizationLogic, ['loadCurrentOrganization']], })), reducers({ projectBeingDeleted: [ @@ -57,6 +58,8 @@ export const projectLogic = kea([ )) as ProjectType breakpoint() + // We need to reload current org (which lists its projects) in organizationLogic AND in userLogic + actions.loadCurrentOrganization() actions.loadUser() Object.keys(payload).map((property) => { @@ -88,7 +91,7 @@ export const projectLogic = kea([ selectors({ currentProjectId: [(s) => [s.currentProject], (currentProject) => currentProject?.id || null], }), - listeners(({ actions, values }) => ({ + listeners(({ actions }) => ({ loadCurrentProjectSuccess: ({ currentProject }) => { if (currentProject) { ApiConfig.setCurrentProjectId(currentProject.id) @@ -107,7 +110,7 @@ export const projectLogic = kea([ lemonToast.success('Project has been deleted') }, createProjectSuccess: ({ currentProject }) => { - if (currentProject && currentProject.id !== values.currentProject?.id) { + if (currentProject) { actions.switchTeam(currentProject.id) } }, diff --git a/frontend/src/scenes/settings/environment/TeamSettings.tsx b/frontend/src/scenes/settings/environment/TeamSettings.tsx index 83cd247821350..94a4ad90c5dcd 100644 --- a/frontend/src/scenes/settings/environment/TeamSettings.tsx +++ b/frontend/src/scenes/settings/environment/TeamSettings.tsx @@ -29,14 +29,6 @@ export function TeamDisplayName(): JSX.Element { const displayNoun = featureFlags[FEATURE_FLAGS.ENVIRONMENTS] ? 'environment' : 'project' - if (currentTeam?.is_demo) { - return ( -

- The demo {displayNoun} cannot be renamed. -

- ) - } - return (
diff --git a/frontend/src/scenes/teamLogic.tsx b/frontend/src/scenes/teamLogic.tsx index cc630ed721aca..b27c8621db68a 100644 --- a/frontend/src/scenes/teamLogic.tsx +++ b/frontend/src/scenes/teamLogic.tsx @@ -40,7 +40,7 @@ export interface FrequentMistakeAdvice { export const teamLogic = kea([ path(['scenes', 'teamLogic']), connect(() => ({ - actions: [userLogic, ['loadUser', 'switchTeam']], + actions: [userLogic, ['loadUser', 'switchTeam'], organizationLogic, ['loadCurrentOrganization']], values: [projectLogic, ['currentProject'], featureFlagLogic, ['featureFlags']], })), actions({ @@ -103,6 +103,8 @@ export const teamLogic = kea([ const [patchedTeam] = await Promise.all(promises) breakpoint() + // We need to reload current org (which lists its teams) in organizationLogic AND in userLogic + actions.loadCurrentOrganization() actions.loadUser() /* Notify user the update was successful */ diff --git a/posthog/api/project.py b/posthog/api/project.py index d7cda49e5c40b..9bc0d91cec45f 100644 --- a/posthog/api/project.py +++ b/posthog/api/project.py @@ -344,14 +344,13 @@ def update(self, instance: Project, validated_data: dict[str, Any]) -> Project: should_team_be_saved_too = False for attr, value in validated_data.items(): - if attr in self.Meta.team_passthrough_fields: + if attr not in self.Meta.team_passthrough_fields: + # This attr is a Project field + setattr(instance, attr, value) + else: + # This attr is actually on the Project's passthrough Team should_team_be_saved_too = True setattr(team, attr, value) - else: - if attr == "name": # `name` should be updated on _both_ the Project and Team - should_team_be_saved_too = True - setattr(team, attr, value) - setattr(instance, attr, value) instance.save() if should_team_be_saved_too: diff --git a/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr b/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr index 80349bbc75121..b386da3b52700 100644 --- a/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr +++ b/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr @@ -640,12 +640,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '437' + AND "ee_accesscontrol"."resource_id" = '438' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '437' + AND "ee_accesscontrol"."resource_id" = '438' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -1688,12 +1688,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -2441,12 +2441,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -3129,12 +3129,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -3881,12 +3881,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -4597,12 +4597,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -5395,12 +5395,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -5659,12 +5659,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -6091,12 +6091,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -6556,12 +6556,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -7248,12 +7248,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -7997,12 +7997,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '444' + AND "ee_accesscontrol"."resource_id" = '445' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL