feat(environments): Environment switcher, behind flag (#25009)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
@ -3,6 +3,8 @@ from posthog.api.test.test_team import EnvironmentToProjectRewriteClient
|
||||
from posthog.models.organization import Organization, OrganizationMembership
|
||||
from posthog.models.project import Project
|
||||
from posthog.models.team.team import Team
|
||||
from posthog.models.user import User
|
||||
from posthog.test.base import FuzzyInt
|
||||
|
||||
|
||||
class TestProjectEnterpriseAPI(team_enterprise_api_test_factory()):
|
||||
@ -14,6 +16,46 @@ class TestProjectEnterpriseAPI(team_enterprise_api_test_factory()):
|
||||
|
||||
client_class = EnvironmentToProjectRewriteClient
|
||||
|
||||
def test_create_team(self):
|
||||
self.organization_membership.level = OrganizationMembership.Level.ADMIN
|
||||
self.organization_membership.save()
|
||||
self.assertEqual(Team.objects.count(), 1)
|
||||
self.assertEqual(Project.objects.count(), 1)
|
||||
response = self.client.post("/api/projects/@current/environments/", {"name": "Test"})
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(Team.objects.count(), 2)
|
||||
self.assertEqual(Project.objects.count(), 2)
|
||||
response_data = response.json()
|
||||
self.assertDictContainsSubset(
|
||||
{
|
||||
"name": "Test",
|
||||
"access_control": False,
|
||||
"effective_membership_level": OrganizationMembership.Level.ADMIN,
|
||||
},
|
||||
response_data,
|
||||
)
|
||||
self.assertEqual(self.organization.teams.count(), 2)
|
||||
|
||||
def test_create_team_with_access_control(self):
|
||||
self.organization_membership.level = OrganizationMembership.Level.ADMIN
|
||||
self.organization_membership.save()
|
||||
self.assertEqual(Team.objects.count(), 1)
|
||||
self.assertEqual(Project.objects.count(), 1)
|
||||
response = self.client.post("/api/projects/@current/environments/", {"name": "Test", "access_control": True})
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(Team.objects.count(), 2)
|
||||
self.assertEqual(Project.objects.count(), 2)
|
||||
response_data = response.json()
|
||||
self.assertDictContainsSubset(
|
||||
{
|
||||
"name": "Test",
|
||||
"access_control": True,
|
||||
"effective_membership_level": OrganizationMembership.Level.ADMIN,
|
||||
},
|
||||
response_data,
|
||||
)
|
||||
self.assertEqual(self.organization.teams.count(), 2)
|
||||
|
||||
def test_user_create_project_for_org_via_url(self):
|
||||
# Set both current and new org to high enough membership level
|
||||
self.organization_membership.level = OrganizationMembership.Level.ADMIN
|
||||
@ -46,3 +88,83 @@ class TestProjectEnterpriseAPI(team_enterprise_api_test_factory()):
|
||||
response = self.client.post(f"/api/organizations/{other_org.id}/projects/", {"name": "Via path org"})
|
||||
self.assertEqual(response.status_code, 403, msg=response.json())
|
||||
assert response.json() == self.permission_denied_response("Your organization access level is insufficient.")
|
||||
|
||||
def test_user_that_does_not_belong_to_an_org_cannot_create_a_projec(self):
|
||||
user = User.objects.create(email="no_org@posthog.com")
|
||||
self.client.force_login(user)
|
||||
|
||||
response = self.client.post("/api/projects/", {"name": "Test"})
|
||||
self.assertEqual(response.status_code, 404, response.content)
|
||||
self.assertEqual(
|
||||
response.json(),
|
||||
{
|
||||
"type": "invalid_request",
|
||||
"code": "not_found",
|
||||
"detail": "You need to belong to an organization.",
|
||||
"attr": None,
|
||||
},
|
||||
)
|
||||
|
||||
def test_list_projects_restricted_ones_hidden(self):
|
||||
self.organization_membership.level = OrganizationMembership.Level.MEMBER
|
||||
self.organization_membership.save()
|
||||
Team.objects.create(
|
||||
organization=self.organization,
|
||||
name="Other",
|
||||
access_control=True,
|
||||
)
|
||||
|
||||
# The other team should not be returned as it's restricted for the logged-in user
|
||||
projects_response = self.client.get(f"/api/environments/")
|
||||
|
||||
# 9 (above):
|
||||
with self.assertNumQueries(FuzzyInt(10, 11)):
|
||||
current_org_response = self.client.get(f"/api/organizations/{self.organization.id}/")
|
||||
|
||||
self.assertEqual(projects_response.status_code, 200)
|
||||
self.assertEqual(
|
||||
projects_response.json().get("results"),
|
||||
[
|
||||
{
|
||||
"id": self.team.id,
|
||||
"uuid": str(self.team.uuid),
|
||||
"organization": str(self.organization.id),
|
||||
"api_token": self.team.api_token,
|
||||
"name": self.team.name,
|
||||
"completed_snippet_onboarding": False,
|
||||
"has_completed_onboarding_for": {"product_analytics": True},
|
||||
"ingested_event": False,
|
||||
"is_demo": False,
|
||||
"timezone": "UTC",
|
||||
"access_control": False,
|
||||
}
|
||||
],
|
||||
)
|
||||
self.assertEqual(current_org_response.status_code, 200)
|
||||
self.assertEqual(
|
||||
current_org_response.json().get("teams"),
|
||||
[
|
||||
{
|
||||
"id": self.team.id,
|
||||
"uuid": str(self.team.uuid),
|
||||
"organization": str(self.organization.id),
|
||||
"project_id": self.team.project.id, # type: ignore
|
||||
"api_token": self.team.api_token,
|
||||
"name": self.team.name,
|
||||
"completed_snippet_onboarding": False,
|
||||
"has_completed_onboarding_for": {"product_analytics": True},
|
||||
"ingested_event": False,
|
||||
"is_demo": False,
|
||||
"timezone": "UTC",
|
||||
"access_control": False,
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
def test_cannot_create_project_in_org_without_access(self):
|
||||
self.organization_membership.delete()
|
||||
|
||||
response = self.client.post(f"/api/organizations/{self.organization.id}/projects/", {"name": "Test"})
|
||||
|
||||
self.assertEqual(response.status_code, 404, response.json())
|
||||
self.assertEqual(response.json(), self.not_found_response("Organization not found."))
|
||||
|
@ -1,3 +1,7 @@
|
||||
from typing import Optional
|
||||
from unittest.mock import patch
|
||||
import uuid
|
||||
from freezegun import freeze_time
|
||||
from rest_framework.status import (
|
||||
HTTP_200_OK,
|
||||
HTTP_204_NO_CONTENT,
|
||||
@ -12,6 +16,7 @@ from posthog.models.dashboard import Dashboard
|
||||
from posthog.models.organization import Organization, OrganizationMembership
|
||||
from posthog.models.project import Project
|
||||
from posthog.models.team import Team
|
||||
from posthog.models.team.team_caching import get_team_in_cache
|
||||
from posthog.models.user import User
|
||||
from posthog.test.base import FuzzyInt
|
||||
|
||||
@ -20,53 +25,21 @@ def team_enterprise_api_test_factory(): # type: ignore
|
||||
class TestTeamEnterpriseAPI(APILicensedTest):
|
||||
CLASS_DATA_LEVEL_SETUP = False
|
||||
|
||||
def _assert_activity_log(self, expected: list[dict], team_id: Optional[int] = None) -> None:
|
||||
if not team_id:
|
||||
team_id = self.team.pk
|
||||
|
||||
starting_log_response = self.client.get(f"/api/environments/{team_id}/activity")
|
||||
assert starting_log_response.status_code == 200, starting_log_response.json()
|
||||
assert starting_log_response.json()["results"] == expected
|
||||
|
||||
# Creating projects
|
||||
|
||||
def test_create_team(self):
|
||||
self.organization_membership.level = OrganizationMembership.Level.ADMIN
|
||||
self.organization_membership.save()
|
||||
self.assertEqual(Team.objects.count(), 1)
|
||||
self.assertEqual(Project.objects.count(), 1)
|
||||
response = self.client.post("/api/environments/", {"name": "Test"})
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(Team.objects.count(), 2)
|
||||
self.assertEqual(Project.objects.count(), 2)
|
||||
response_data = response.json()
|
||||
self.assertDictContainsSubset(
|
||||
{
|
||||
"name": "Test",
|
||||
"access_control": False,
|
||||
"effective_membership_level": OrganizationMembership.Level.ADMIN,
|
||||
},
|
||||
response_data,
|
||||
)
|
||||
self.assertEqual(self.organization.teams.count(), 2)
|
||||
|
||||
def test_create_team_with_access_control(self):
|
||||
self.organization_membership.level = OrganizationMembership.Level.ADMIN
|
||||
self.organization_membership.save()
|
||||
self.assertEqual(Team.objects.count(), 1)
|
||||
self.assertEqual(Project.objects.count(), 1)
|
||||
response = self.client.post("/api/environments/", {"name": "Test", "access_control": True})
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(Team.objects.count(), 2)
|
||||
self.assertEqual(Project.objects.count(), 2)
|
||||
response_data = response.json()
|
||||
self.assertDictContainsSubset(
|
||||
{
|
||||
"name": "Test",
|
||||
"access_control": True,
|
||||
"effective_membership_level": OrganizationMembership.Level.ADMIN,
|
||||
},
|
||||
response_data,
|
||||
)
|
||||
self.assertEqual(self.organization.teams.count(), 2)
|
||||
|
||||
def test_non_admin_cannot_create_team(self):
|
||||
self.organization_membership.level = OrganizationMembership.Level.MEMBER
|
||||
self.organization_membership.save()
|
||||
count = Team.objects.count()
|
||||
response = self.client.post("/api/environments/", {"name": "Test"})
|
||||
response = self.client.post("/api/projects/@current/environments/", {"name": "Test"})
|
||||
self.assertEqual(response.status_code, HTTP_403_FORBIDDEN)
|
||||
self.assertEqual(Team.objects.count(), count)
|
||||
self.assertEqual(
|
||||
@ -78,7 +51,9 @@ def team_enterprise_api_test_factory(): # type: ignore
|
||||
dashboard_x = Dashboard.objects.create(team=self.team, name="Test")
|
||||
self.organization_membership.level = OrganizationMembership.Level.ADMIN
|
||||
self.organization_membership.save()
|
||||
response = self.client.post("/api/environments/", {"name": "Test", "primary_dashboard": dashboard_x.id})
|
||||
response = self.client.post(
|
||||
"/api/projects/@current/environments/", {"name": "Test", "primary_dashboard": dashboard_x.id}
|
||||
)
|
||||
self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST, response.json())
|
||||
self.assertEqual(
|
||||
response.json(),
|
||||
@ -87,12 +62,14 @@ def team_enterprise_api_test_factory(): # type: ignore
|
||||
),
|
||||
)
|
||||
|
||||
@patch("posthog.demo.matrix.manager.MatrixManager.run_on_team") # We don't actually need demo data, it's slow
|
||||
def test_create_demo_team(self, *args):
|
||||
self.organization_membership.level = OrganizationMembership.Level.ADMIN
|
||||
self.organization_membership.save()
|
||||
response = self.client.post("/api/environments/", {"name": "Hedgebox", "is_demo": True})
|
||||
self.assertEqual(Team.objects.count(), 3)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(Team.objects.count(), 1)
|
||||
response = self.client.post("/api/projects/@current/environments/", {"name": "Hedgebox", "is_demo": True})
|
||||
self.assertEqual(response.status_code, 201, response.json())
|
||||
self.assertEqual(Team.objects.count(), 2)
|
||||
response_data = response.json()
|
||||
self.assertDictContainsSubset(
|
||||
{
|
||||
@ -104,12 +81,15 @@ def team_enterprise_api_test_factory(): # type: ignore
|
||||
)
|
||||
self.assertEqual(self.organization.teams.count(), 2)
|
||||
|
||||
@patch("posthog.demo.matrix.manager.MatrixManager.run_on_team") # We don't actually need demo data, it's slow
|
||||
def test_create_two_demo_teams(self, *args):
|
||||
self.organization_membership.level = OrganizationMembership.Level.ADMIN
|
||||
self.organization_membership.save()
|
||||
response = self.client.post("/api/environments/", {"name": "Hedgebox", "is_demo": True})
|
||||
self.assertEqual(Team.objects.count(), 3)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(Team.objects.count(), 1)
|
||||
response = self.client.post("/api/projects/@current/environments/", {"name": "Hedgebox", "is_demo": True})
|
||||
self.assertEqual(response.status_code, 201, response.json())
|
||||
self.assertEqual(Team.objects.count(), 2)
|
||||
|
||||
response_data = response.json()
|
||||
self.assertDictContainsSubset(
|
||||
{
|
||||
@ -119,11 +99,12 @@ def team_enterprise_api_test_factory(): # type: ignore
|
||||
},
|
||||
response_data,
|
||||
)
|
||||
response_2 = self.client.post("/api/environments/", {"name": "Hedgebox", "is_demo": True})
|
||||
self.assertEqual(Team.objects.count(), 3)
|
||||
response_2 = self.client.post("/api/projects/@current/environments/", {"name": "Hedgebox", "is_demo": True})
|
||||
self.assertEqual(Team.objects.count(), 2, response_2.json())
|
||||
response_2_data = response_2.json()
|
||||
self.assertDictContainsSubset(
|
||||
self.assertEqual(
|
||||
{
|
||||
"attr": None,
|
||||
"type": "authentication_error",
|
||||
"code": "permission_denied",
|
||||
"detail": "You must upgrade your PostHog plan to be able to create and manage multiple projects or environments.",
|
||||
@ -132,22 +113,6 @@ def team_enterprise_api_test_factory(): # type: ignore
|
||||
)
|
||||
self.assertEqual(self.organization.teams.count(), 2)
|
||||
|
||||
def test_user_that_does_not_belong_to_an_org_cannot_create_a_team(self):
|
||||
user = User.objects.create(email="no_org@posthog.com")
|
||||
self.client.force_login(user)
|
||||
|
||||
response = self.client.post("/api/environments/", {"name": "Test"})
|
||||
self.assertEqual(response.status_code, HTTP_404_NOT_FOUND, response.content)
|
||||
self.assertEqual(
|
||||
response.json(),
|
||||
{
|
||||
"type": "invalid_request",
|
||||
"code": "not_found",
|
||||
"detail": "You need to belong to an organization.",
|
||||
"attr": None,
|
||||
},
|
||||
)
|
||||
|
||||
# Deleting projects
|
||||
|
||||
def test_delete_team_as_org_admin_allowed(self):
|
||||
@ -483,63 +448,219 @@ def team_enterprise_api_test_factory(): # type: ignore
|
||||
self.assertEqual(response.status_code, HTTP_404_NOT_FOUND)
|
||||
self.assertEqual(self.not_found_response(), response_data)
|
||||
|
||||
def test_list_teams_restricted_ones_hidden(self):
|
||||
self.organization_membership.level = OrganizationMembership.Level.MEMBER
|
||||
@freeze_time("2022-02-08")
|
||||
def test_team_creation_is_in_activity_log(self):
|
||||
self.organization_membership.level = OrganizationMembership.Level.ADMIN
|
||||
self.organization_membership.save()
|
||||
Team.objects.create(
|
||||
organization=self.organization,
|
||||
name="Other",
|
||||
access_control=True,
|
||||
)
|
||||
|
||||
# The other team should not be returned as it's restricted for the logged-in user
|
||||
projects_response = self.client.get(f"/api/environments/")
|
||||
team_name = str(uuid.uuid4())
|
||||
response = self.client.post("/api/projects/@current/environments/", {"name": team_name, "is_demo": False})
|
||||
self.assertEqual(response.status_code, 201, response.json())
|
||||
|
||||
# 9 (above):
|
||||
with self.assertNumQueries(FuzzyInt(9, 10)):
|
||||
current_org_response = self.client.get(f"/api/organizations/{self.organization.id}/")
|
||||
|
||||
self.assertEqual(projects_response.status_code, HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
projects_response.json().get("results"),
|
||||
team_id = response.json()["id"]
|
||||
self._assert_activity_log(
|
||||
[
|
||||
{
|
||||
"id": self.team.id,
|
||||
"uuid": str(self.team.uuid),
|
||||
"organization": str(self.organization.id),
|
||||
"api_token": self.team.api_token,
|
||||
"name": self.team.name,
|
||||
"completed_snippet_onboarding": False,
|
||||
"has_completed_onboarding_for": {"product_analytics": True},
|
||||
"ingested_event": False,
|
||||
"is_demo": False,
|
||||
"timezone": "UTC",
|
||||
"access_control": False,
|
||||
}
|
||||
"activity": "created",
|
||||
"created_at": "2022-02-08T00:00:00Z",
|
||||
"detail": {
|
||||
"changes": None,
|
||||
"name": team_name,
|
||||
"short_id": None,
|
||||
"trigger": None,
|
||||
"type": None,
|
||||
},
|
||||
"item_id": str(team_id),
|
||||
"scope": "Team",
|
||||
"user": {
|
||||
"email": "user1@posthog.com",
|
||||
"first_name": "",
|
||||
},
|
||||
},
|
||||
],
|
||||
team_id=team_id,
|
||||
)
|
||||
self.assertEqual(current_org_response.status_code, HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
current_org_response.json().get("teams"),
|
||||
[
|
||||
{
|
||||
"id": self.team.id,
|
||||
"uuid": str(self.team.uuid),
|
||||
"organization": str(self.organization.id),
|
||||
"api_token": self.team.api_token,
|
||||
"name": self.team.name,
|
||||
"completed_snippet_onboarding": False,
|
||||
"has_completed_onboarding_for": {"product_analytics": True},
|
||||
"ingested_event": False,
|
||||
"is_demo": False,
|
||||
"timezone": "UTC",
|
||||
"access_control": False,
|
||||
}
|
||||
],
|
||||
|
||||
def test_team_is_cached_on_create_and_update(self):
|
||||
self.organization_membership.level = OrganizationMembership.Level.ADMIN
|
||||
self.organization_membership.save()
|
||||
|
||||
response = self.client.post("/api/projects/@current/environments/", {"name": "Test", "is_demo": False})
|
||||
self.assertEqual(response.status_code, 201, response.json())
|
||||
self.assertEqual(response.json()["name"], "Test")
|
||||
|
||||
token = response.json()["api_token"]
|
||||
team_id = response.json()["id"]
|
||||
|
||||
cached_team = get_team_in_cache(token)
|
||||
|
||||
assert cached_team is not None
|
||||
self.assertEqual(cached_team.name, "Test")
|
||||
self.assertEqual(cached_team.uuid, response.json()["uuid"])
|
||||
self.assertEqual(cached_team.id, response.json()["id"])
|
||||
|
||||
response = self.client.patch(
|
||||
f"/api/environments/{team_id}/",
|
||||
{"timezone": "Europe/Istanbul", "session_recording_opt_in": True},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
cached_team = get_team_in_cache(token)
|
||||
assert cached_team is not None
|
||||
|
||||
self.assertEqual(cached_team.name, "Test")
|
||||
self.assertEqual(cached_team.uuid, response.json()["uuid"])
|
||||
self.assertEqual(cached_team.session_recording_opt_in, True)
|
||||
|
||||
# only things in CachedTeamSerializer are cached!
|
||||
self.assertEqual(cached_team.timezone, "UTC")
|
||||
|
||||
# reset token should update cache as well
|
||||
response = self.client.patch(f"/api/environments/{team_id}/reset_token/")
|
||||
response_data = response.json()
|
||||
|
||||
cached_team = get_team_in_cache(token)
|
||||
assert cached_team is None
|
||||
|
||||
cached_team = get_team_in_cache(response_data["api_token"])
|
||||
assert cached_team is not None
|
||||
self.assertEqual(cached_team.name, "Test")
|
||||
self.assertEqual(cached_team.uuid, response.json()["uuid"])
|
||||
self.assertEqual(cached_team.session_recording_opt_in, True)
|
||||
|
||||
return TestTeamEnterpriseAPI
|
||||
|
||||
|
||||
class TestTeamEnterpriseAPI(team_enterprise_api_test_factory()):
|
||||
pass
|
||||
def test_create_team(self):
|
||||
self.organization_membership.level = OrganizationMembership.Level.ADMIN
|
||||
self.organization_membership.save()
|
||||
self.assertEqual(Team.objects.count(), 1)
|
||||
self.assertEqual(Project.objects.count(), 1)
|
||||
response = self.client.post("/api/projects/@current/environments/", {"name": "Test"})
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(Team.objects.count(), 2)
|
||||
self.assertEqual(Project.objects.count(), 1) # Created under the same project, not a new one!
|
||||
response_data = response.json()
|
||||
self.assertDictContainsSubset(
|
||||
{
|
||||
"name": "Test",
|
||||
"access_control": False,
|
||||
"effective_membership_level": OrganizationMembership.Level.ADMIN,
|
||||
},
|
||||
response_data,
|
||||
)
|
||||
self.assertEqual(self.organization.teams.count(), 2)
|
||||
|
||||
def test_create_team_with_access_control(self):
|
||||
self.organization_membership.level = OrganizationMembership.Level.ADMIN
|
||||
self.organization_membership.save()
|
||||
self.assertEqual(Team.objects.count(), 1)
|
||||
self.assertEqual(Project.objects.count(), 1)
|
||||
response = self.client.post("/api/projects/@current/environments/", {"name": "Test", "access_control": True})
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(Team.objects.count(), 2)
|
||||
self.assertEqual(Project.objects.count(), 1) # Created under the same project, not a new one!
|
||||
response_data = response.json()
|
||||
self.assertDictContainsSubset(
|
||||
{
|
||||
"name": "Test",
|
||||
"access_control": True,
|
||||
"effective_membership_level": OrganizationMembership.Level.ADMIN,
|
||||
},
|
||||
response_data,
|
||||
)
|
||||
self.assertEqual(self.organization.teams.count(), 2)
|
||||
|
||||
def test_cannot_create_team_not_under_project(self):
|
||||
self.organization_membership.level = OrganizationMembership.Level.ADMIN
|
||||
self.organization_membership.save()
|
||||
self.assertEqual(Team.objects.count(), 1)
|
||||
self.assertEqual(Project.objects.count(), 1)
|
||||
response = self.client.post("/api/environments/", {"name": "Test"})
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(Team.objects.count(), 1)
|
||||
self.assertEqual(Project.objects.count(), 1)
|
||||
self.assertEqual(
|
||||
response.json(),
|
||||
self.validation_error_response(
|
||||
"Environments must be created under a specific project. Send the POST request to /api/projects/<project_id>/environments/ instead."
|
||||
),
|
||||
)
|
||||
|
||||
def test_cannot_create_team_in_nonexistent_project(self):
|
||||
_, _, team = Organization.objects.bootstrap(self.user, name="other_org")
|
||||
self.organization_membership.delete()
|
||||
|
||||
response = self.client.post("/api/projects/4444444/environments/", {"name": "Test"})
|
||||
|
||||
self.assertEqual(response.status_code, 404, response.json())
|
||||
self.assertEqual(response.json(), self.not_found_response("Project not found."))
|
||||
|
||||
def test_cannot_create_team_in_project_without_org_access(self):
|
||||
self.organization_membership.delete()
|
||||
|
||||
response = self.client.post(f"/api/projects/{self.project.id}/environments/", {"name": "Test"})
|
||||
|
||||
self.assertEqual(response.status_code, 404, response.json())
|
||||
self.assertEqual(
|
||||
response.json(),
|
||||
self.not_found_response("Organization not found."),
|
||||
)
|
||||
|
||||
def test_list_teams_restricted_ones_hidden(self):
|
||||
self.organization_membership.level = OrganizationMembership.Level.MEMBER
|
||||
self.organization_membership.save()
|
||||
Team.objects.create(
|
||||
organization=self.organization,
|
||||
name="Other",
|
||||
access_control=True,
|
||||
)
|
||||
|
||||
# The other team should not be returned as it's restricted for the logged-in user
|
||||
projects_response = self.client.get(f"/api/environments/")
|
||||
|
||||
# 9 (above):
|
||||
with self.assertNumQueries(FuzzyInt(10, 11)):
|
||||
current_org_response = self.client.get(f"/api/organizations/{self.organization.id}/")
|
||||
|
||||
self.assertEqual(projects_response.status_code, HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
projects_response.json().get("results"),
|
||||
[
|
||||
{
|
||||
"id": self.team.id,
|
||||
"uuid": str(self.team.uuid),
|
||||
"organization": str(self.organization.id),
|
||||
"project_id": self.team.project.id, # type: ignore
|
||||
"api_token": self.team.api_token,
|
||||
"name": self.team.name,
|
||||
"completed_snippet_onboarding": False,
|
||||
"has_completed_onboarding_for": {"product_analytics": True},
|
||||
"ingested_event": False,
|
||||
"is_demo": False,
|
||||
"timezone": "UTC",
|
||||
"access_control": False,
|
||||
}
|
||||
],
|
||||
)
|
||||
self.assertEqual(current_org_response.status_code, HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
current_org_response.json().get("teams"),
|
||||
[
|
||||
{
|
||||
"id": self.team.id,
|
||||
"uuid": str(self.team.uuid),
|
||||
"organization": str(self.organization.id),
|
||||
"project_id": self.team.project.id, # type: ignore
|
||||
"api_token": self.team.api_token,
|
||||
"name": self.team.name,
|
||||
"completed_snippet_onboarding": False,
|
||||
"has_completed_onboarding_for": {"product_analytics": True},
|
||||
"ingested_event": False,
|
||||
"is_demo": False,
|
||||
"timezone": "UTC",
|
||||
"access_control": False,
|
||||
}
|
||||
],
|
||||
)
|
||||
|
Before Width: | Height: | Size: 471 B After Width: | Height: | Size: 466 B |
Before Width: | Height: | Size: 445 B After Width: | Height: | Size: 412 B |
Before Width: | Height: | Size: 460 B After Width: | Height: | Size: 470 B |
Before Width: | Height: | Size: 414 B After Width: | Height: | Size: 423 B |
Before Width: | Height: | Size: 599 B After Width: | Height: | Size: 576 B |
Before Width: | Height: | Size: 579 B After Width: | Height: | Size: 543 B |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 471 B After Width: | Height: | Size: 466 B |
Before Width: | Height: | Size: 445 B After Width: | Height: | Size: 412 B |
Before Width: | Height: | Size: 426 B After Width: | Height: | Size: 418 B |
Before Width: | Height: | Size: 394 B After Width: | Height: | Size: 372 B |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 900 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 846 B |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 686 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 635 B |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 195 KiB |
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 201 KiB |
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 197 KiB |
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 203 KiB |
Before Width: | Height: | Size: 175 KiB After Width: | Height: | Size: 160 KiB |
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 158 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 99 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 103 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 164 KiB |
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 159 KiB |
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 162 KiB |
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 159 KiB |
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
Before Width: | Height: | Size: 155 KiB After Width: | Height: | Size: 154 KiB |
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 99 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 168 KiB |
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 160 KiB |
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 165 KiB |
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 159 KiB |
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 169 KiB |
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 162 KiB |
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 167 KiB |
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 161 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 132 KiB |