Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.

Commit 4a0bcd9

Browse files
committed
Remove ordering on organizations list, allow users of an account to see activatedUserCount on that account's orgs
1 parent 3b84808 commit 4a0bcd9

File tree

9 files changed

+62
-114
lines changed

9 files changed

+62
-114
lines changed

graphql_api/helpers/mutation.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,5 +75,32 @@ def authenticated_resolver(queried_owner, info, *args, **kwargs):
7575
return authenticated_resolver
7676

7777

78+
def require_shared_account_or_part_of_org(resolver):
79+
def authenticated_resolver(queried_owner, info, *args, **kwargs):
80+
current_user = info.context["request"].user
81+
current_owner = info.context["request"].current_owner
82+
83+
if (
84+
current_user
85+
and current_user.is_authenticated
86+
and queried_owner
87+
and queried_owner.account
88+
and current_user in queried_owner.account.users.all()
89+
):
90+
return resolver(queried_owner, info, *args, **kwargs)
91+
92+
if (
93+
not current_user
94+
or not current_user.is_authenticated
95+
or not current_owner
96+
or not current_user_part_of_org(current_owner, queried_owner)
97+
):
98+
return None
99+
100+
return resolver(queried_owner, info, *args, **kwargs)
101+
102+
return authenticated_resolver
103+
104+
78105
def resolve_union_error_type(error, *_):
79106
return error.get_graphql_type()

graphql_api/tests/test_account.py

Lines changed: 3 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -126,55 +126,9 @@ def test_fetch_organizations(self) -> None:
126126
for node in result["owner"]["account"]["organizations"]["edges"]
127127
]
128128

129-
assert orgs == ["owner-2", "owner-1", "owner-0"]
130-
131-
def test_fetch_organizations_order_by_activated_users_asc(self) -> None:
132-
account = AccountFactory(name="account")
133-
owner = OwnerFactory(
134-
username="owner-0",
135-
plan_activated_users=[],
136-
account=account,
137-
)
138-
OwnerFactory(
139-
username="owner-1",
140-
plan_activated_users=[0],
141-
account=account,
142-
)
143-
OwnerFactory(
144-
username="owner-2",
145-
plan_activated_users=[0, 1],
146-
account=account,
147-
)
148-
149-
query = """
150-
query {
151-
owner(username: "%s") {
152-
account {
153-
organizations(first: 20, ordering: ACTIVATED_USERS, orderingDirection: ASC) {
154-
edges {
155-
node {
156-
username
157-
activatedUserCount
158-
}
159-
}
160-
}
161-
}
162-
}
163-
}
164-
""" % (owner.username)
165-
166-
result = self.gql_request(query, owner=owner)
167-
168-
assert "errors" not in result
169-
170-
orgs = [
171-
node["node"]["username"]
172-
for node in result["owner"]["account"]["organizations"]["edges"]
173-
]
174-
175129
assert orgs == ["owner-0", "owner-1", "owner-2"]
176130

177-
def test_fetch_organizations_order_by_name(self) -> None:
131+
def test_fetch_organizations_desc(self) -> None:
178132
account = AccountFactory(name="account")
179133
owner = OwnerFactory(
180134
username="owner-0",
@@ -196,7 +150,7 @@ def test_fetch_organizations_order_by_name(self) -> None:
196150
query {
197151
owner(username: "%s") {
198152
account {
199-
organizations(first: 20, ordering: NAME) {
153+
organizations(first: 20, orderingDirection: DESC) {
200154
edges {
201155
node {
202156
username
@@ -220,52 +174,6 @@ def test_fetch_organizations_order_by_name(self) -> None:
220174

221175
assert orgs == ["owner-2", "owner-1", "owner-0"]
222176

223-
def test_fetch_organizations_order_by_name_asc(self) -> None:
224-
account = AccountFactory(name="account")
225-
owner = OwnerFactory(
226-
username="owner-0",
227-
plan_activated_users=[],
228-
account=account,
229-
)
230-
OwnerFactory(
231-
username="owner-1",
232-
plan_activated_users=[0],
233-
account=account,
234-
)
235-
OwnerFactory(
236-
username="owner-2",
237-
plan_activated_users=[0, 1],
238-
account=account,
239-
)
240-
241-
query = """
242-
query {
243-
owner(username: "%s") {
244-
account {
245-
organizations(first: 20, ordering: NAME, orderingDirection: ASC) {
246-
edges {
247-
node {
248-
username
249-
activatedUserCount
250-
}
251-
}
252-
}
253-
}
254-
}
255-
}
256-
""" % (owner.username)
257-
258-
result = self.gql_request(query, owner=owner)
259-
260-
assert "errors" not in result
261-
262-
orgs = [
263-
node["node"]["username"]
264-
for node in result["owner"]["account"]["organizations"]["edges"]
265-
]
266-
267-
assert orgs == ["owner-0", "owner-1", "owner-2"]
268-
269177
def test_fetch_organizations_pagination(self) -> None:
270178
account = AccountFactory(name="account")
271179
owner = OwnerFactory(
@@ -318,7 +226,7 @@ def test_fetch_organizations_pagination(self) -> None:
318226
for node in result["owner"]["account"]["organizations"]["edges"]
319227
]
320228

321-
assert orgs == ["owner-2", "owner-1"]
229+
assert orgs == ["owner-0", "owner-1"]
322230

323231
hasNextPage = result["owner"]["account"]["organizations"]["pageInfo"][
324232
"hasNextPage"

graphql_api/tests/test_owner.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
from freezegun import freeze_time
88
from graphql import GraphQLError
99
from prometheus_client import REGISTRY
10-
from shared.django_apps.codecov_auth.tests.factories import OktaSettingsFactory
10+
from shared.django_apps.codecov_auth.tests.factories import (
11+
AccountsUsersFactory,
12+
OktaSettingsFactory,
13+
)
1114
from shared.django_apps.reports.models import ReportType
1215
from shared.upload.utils import UploaderType, insert_coverage_measurement
1316

@@ -1027,3 +1030,23 @@ def test_fetch_activated_user_count_returns_null_if_not_in_org(self):
10271030
""" % (owner.username)
10281031
data = self.gql_request(query, owner=user)
10291032
assert data["owner"]["activatedUserCount"] is None
1033+
1034+
def test_fetch_activated_user_count_when_not_in_org_but_has_shared_account(self):
1035+
owner = OwnerFactory(username="sample-user")
1036+
AccountsUsersFactory(user=owner.user, account=self.account)
1037+
user2 = OwnerFactory(username="sample-user-2")
1038+
user3 = OwnerFactory(username="sample-user-3")
1039+
owner = OwnerFactory(
1040+
username="sample-org",
1041+
plan_activated_users=[user2.ownerid, user3.ownerid],
1042+
account=self.account,
1043+
)
1044+
1045+
query = """{
1046+
owner(username: "%s") {
1047+
activatedUserCount
1048+
}
1049+
}
1050+
""" % (owner.username)
1051+
data = self.gql_request(query, owner=owner)
1052+
assert data["owner"]["activatedUserCount"] == 2

graphql_api/types/account/account.graphql

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ type Account {
44
totalSeatCount: Int!
55
activatedUserCount: Int!
66
organizations(
7-
ordering: AccountOrganizationOrdering
87
orderingDirection: OrderingDirection
98
first: Int
109
after: String

graphql_api/types/account/account.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
build_connection_graphql,
99
queryset_to_connection,
1010
)
11-
from graphql_api.types.enums.enums import AccountOrganizationOrdering, OrderingDirection
11+
from graphql_api.types.enums.enums import OrderingDirection
1212

1313
account = ariadne_load_local_graphql(__file__, "account.graphql")
1414
account = account + build_connection_graphql("AccountOrganizationConnection", "Owner")
@@ -43,13 +43,12 @@ def resolve_activated_user_count(account: Account, info: GraphQLResolveInfo) ->
4343
def resolve_organizations(
4444
account: Account,
4545
info: GraphQLResolveInfo,
46-
ordering=AccountOrganizationOrdering.ACTIVATED_USERS,
47-
ordering_direction=OrderingDirection.DESC,
46+
ordering_direction=OrderingDirection.ASC,
4847
**kwargs,
4948
):
5049
return queryset_to_connection(
5150
account.organizations,
52-
ordering=(ordering,),
51+
ordering=("username",),
5352
ordering_direction=ordering_direction,
5453
**kwargs,
5554
)

graphql_api/types/enums/account_organization_ordering.graphql

Lines changed: 0 additions & 4 deletions
This file was deleted.

graphql_api/types/enums/enum_types.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from timeseries.models import MeasurementName
1212

1313
from .enums import (
14-
AccountOrganizationOrdering,
1514
AssetOrdering,
1615
BundleLoadTypes,
1716
CoverageLine,
@@ -57,5 +56,4 @@
5756
EnumType("TestResultsOrderingParameter", TestResultsOrderingParameter),
5857
EnumType("TestResultsFilterParameter", TestResultsFilterParameter),
5958
EnumType("AssetOrdering", AssetOrdering),
60-
EnumType("AccountOrganizationOrdering", AccountOrganizationOrdering),
6159
]

graphql_api/types/enums/enums.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,6 @@ class RepositoryOrdering(enum.Enum):
4141
NAME = "name"
4242

4343

44-
class AccountOrganizationOrdering(enum.Enum):
45-
NAME = "username"
46-
ACTIVATED_USERS = "plan_activated_users"
47-
48-
4944
class OrderingDirection(enum.Enum):
5045
ASC = "ascending"
5146
DESC = "descending"

graphql_api/types/owner/owner.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
build_connection_graphql,
3030
queryset_to_connection,
3131
)
32-
from graphql_api.helpers.mutation import require_part_of_org
32+
from graphql_api.helpers.mutation import (
33+
require_part_of_org,
34+
require_shared_account_or_part_of_org,
35+
)
3336
from graphql_api.types.enums import OrderingDirection, RepositoryOrdering
3437
from graphql_api.types.errors.errors import NotFoundError, OwnerNotActivatedError
3538
from plan.constants import FREE_PLAN_REPRESENTATIONS, PlanData, PlanName
@@ -377,6 +380,6 @@ def resolve_upload_token_required(owner: Owner, info) -> bool | None:
377380

378381
@owner_bindable.field("activatedUserCount")
379382
@sync_to_async
380-
@require_part_of_org
383+
@require_shared_account_or_part_of_org
381384
def resolve_activated_user_count(owner: Owner, info: GraphQLResolveInfo) -> int:
382385
return owner.activated_user_count

0 commit comments

Comments
 (0)