Skip to content

Commit

Permalink
feat: Manage org read-only state + superadmin view refresh (#1909)
Browse files Browse the repository at this point in the history
- Displays org status in superadmin view
- Enables superadmins to update org read-only status
- Sorts orgs alphabetically
- Refactors org list to `TailwindComponent`
  • Loading branch information
SuaYoo authored Jul 8, 2024
2 parents f1274a6 + c97900e commit d697706
Show file tree
Hide file tree
Showing 10 changed files with 487 additions and 132 deletions.
69 changes: 55 additions & 14 deletions backend/btrixcloud/orgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from datetime import datetime
from tempfile import NamedTemporaryFile

from typing import Optional, TYPE_CHECKING, Callable, List, AsyncGenerator
from typing import Optional, TYPE_CHECKING, Dict, Callable, List, AsyncGenerator, Any

from pymongo import ReturnDocument
from pymongo.errors import AutoReconnect, DuplicateKeyError
Expand Down Expand Up @@ -155,27 +155,62 @@ async def get_orgs_for_user(
role: UserRole = UserRole.VIEWER,
page_size: int = DEFAULT_PAGE_SIZE,
page: int = 1,
calculate_total=True,
sort_by: str = "name",
sort_direction: int = 1,
):
"""Get all orgs a user is a member of"""
# pylint: disable=duplicate-code,too-many-locals

# Zero-index page for query
page = page - 1
skip = page_size * page

if user.is_superuser:
query = {}
else:
query = {f"users.{user.id}": {"$gte": role.value}}
query: Dict[str, Any] = {}
if not user.is_superuser:
query[f"users.{user.id}"] = {"$gte": role.value}

aggregate: List[Dict[str, Any]] = [{"$match": query}]

# Ensure default org is always first, then sort on sort_by if set
sort_query = {"default": -1}

if sort_by:
sort_fields = ("name", "slug", "readOnly")
if sort_by not in sort_fields:
raise HTTPException(status_code=400, detail="invalid_sort_by")
if sort_direction not in (1, -1):
raise HTTPException(status_code=400, detail="invalid_sort_direction")

sort_query[sort_by] = sort_direction

total = 0
if calculate_total:
total = await self.orgs.count_documents(query)
aggregate.extend([{"$sort": sort_query}])

cursor = self.orgs.find(query, skip=skip, limit=page_size)
results = await cursor.to_list(length=page_size)
orgs = [Organization.from_dict(res) for res in results]
aggregate.extend(
[
{
"$facet": {
"items": [
{"$skip": skip},
{"$limit": page_size},
],
"total": [{"$count": "count"}],
}
},
]
)

return orgs, total
# Get total
cursor = self.orgs.aggregate(aggregate)
results = await cursor.to_list(length=1)
result = results[0]
items = result["items"]

try:
total = int(result["total"][0]["count"])
except (IndexError, ValueError):
total = 0

return [Organization.from_dict(data) for data in items], total

async def get_org_for_user_by_id(
self, oid: UUID, user: User, role: UserRole = UserRole.VIEWER
Expand Down Expand Up @@ -1164,9 +1199,15 @@ async def get_orgs(
user: User = Depends(user_dep),
pageSize: int = DEFAULT_PAGE_SIZE,
page: int = 1,
sortBy: str = "name",
sortDirection: int = 1,
):
results, total = await ops.get_orgs_for_user(
user, page_size=pageSize, page=page
user,
page_size=pageSize,
page=page,
sort_by=sortBy,
sort_direction=sortDirection,
)
serialized_results = [
await res.serialize_for_user(user, user_manager) for res in results
Expand Down
2 changes: 1 addition & 1 deletion backend/btrixcloud/profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ async def list_profiles(
sort_direction: int = -1,
):
"""list all profiles"""
# pylint: disable=too-many-locals
# pylint: disable=too-many-locals,duplicate-code

# Zero-index page for query
page = page - 1
Expand Down
1 change: 0 additions & 1 deletion backend/btrixcloud/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ async def get_user_info_with_orgs(self, user: User) -> UserOut:
user,
# Set high so that we get all orgs even after reducing default page size
page_size=1_000,
calculate_total=False,
)

if user_orgs:
Expand Down
57 changes: 57 additions & 0 deletions backend/test/test_org.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,3 +759,60 @@ def test_login_existing_user_for_invite():
"giftedExecMinutes": 0,
}
assert "subData" not in org


def test_sort_orgs(admin_auth_headers):
# Create a few new orgs for testing
r = requests.post(
f"{API_PREFIX}/orgs/create",
headers=admin_auth_headers,
json={"name": "abc", "slug": "abc"},
)
assert r.status_code == 200

r = requests.post(
f"{API_PREFIX}/orgs/create",
headers=admin_auth_headers,
json={"name": "mno", "slug": "mno"},
)
assert r.status_code == 200

r = requests.post(
f"{API_PREFIX}/orgs/create",
headers=admin_auth_headers,
json={"name": "xyz", "slug": "xyz"},
)
assert r.status_code == 200

# Check default sorting
# Default org should come first, followed by alphabetical sorting ascending
r = requests.get(f"{API_PREFIX}/orgs", headers=admin_auth_headers)
data = r.json()
orgs = data["items"]

assert orgs[0]["default"]

other_orgs = orgs[1:]
last_name = None
for org in other_orgs:
org_name = org["name"]
if last_name:
assert org_name > last_name
last_name = org_name

# Sort by name descending, ensure default org still first
r = requests.get(
f"{API_PREFIX}/orgs?sortBy=name&sortDirection=-1", headers=admin_auth_headers
)
data = r.json()
orgs = data["items"]

assert orgs[0]["default"]

other_orgs = orgs[1:]
last_name = None
for org in other_orgs:
org_name = org["name"]
if last_name:
assert org_name < last_name
last_name = org_name
Loading

0 comments on commit d697706

Please sign in to comment.