Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sort envs returned by REST API by current build's scheduled_on time #881

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
12 changes: 10 additions & 2 deletions conda-store-server/conda_store_server/_internal/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,14 @@ class APIPaginatedResponse(APIResponse):
count: int


class APICursorPaginatedResponse(BaseModel):
data: Optional[Any] = None
status: APIStatus
message: Optional[str] = None
cursor: Optional[str] = None
count: int


class APIAckResponse(BaseModel):
status: APIStatus
message: Optional[str] = None
Expand Down Expand Up @@ -562,8 +570,8 @@ class APIDeleteNamespaceRole(BaseModel):


# GET /api/v1/environment
class APIListEnvironment(APIPaginatedResponse):
data: List[Environment]
class APIListEnvironment(APICursorPaginatedResponse):
data: List[Environment] = []


# GET /api/v1/environment/{namespace}/{name}
Expand Down
79 changes: 59 additions & 20 deletions conda-store-server/conda_store_server/_internal/server/views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,39 @@
from conda_store_server import __version__, api, app
from conda_store_server._internal import orm, schema
from conda_store_server._internal.environment import filter_environments
from conda_store_server._internal.schema import AuthenticationToken, Permissions
from conda_store_server._internal.schema import (
AuthenticationToken,
Permissions,
)
from conda_store_server._internal.server import dependencies
from conda_store_server._internal.server.views.pagination import (
Cursor,
CursorPaginatedArgs,
Ordering,
OrderingMetadata,
paginate,
)
from conda_store_server.exception import CondaStoreError
from conda_store_server.server.auth import Authentication


def get_cursor(cursor: Optional[str] = None) -> Cursor:
return Cursor.load(cursor)


def get_cursor_paginated_args(
order: Optional[Ordering] = Ordering.ASCENDING,
limit: Optional[int] = None,
sort_by: List[str] = Query([]),
server=Depends(dependencies.get_server),
) -> CursorPaginatedArgs:
return CursorPaginatedArgs(
limit=server.max_page_size if limit is None else limit,
order=order,
sort_by=sort_by,
)


class PaginatedArgs(TypedDict):
"""Dictionary type holding information about paginated requests."""

Expand Down Expand Up @@ -632,18 +659,20 @@ async def api_delete_namespace(
response_model=schema.APIListEnvironment,
)
async def api_list_environments(
request: Request,
auth: Authentication = Depends(dependencies.get_auth),
conda_store: app.CondaStore = Depends(dependencies.get_conda_store),
entity: AuthenticationToken = Depends(dependencies.get_entity),
paginated_args: PaginatedArgs = Depends(get_paginated_args),
paginated_args: CursorPaginatedArgs = Depends(get_cursor_paginated_args),
cursor: Cursor = Depends(get_cursor),
artifact: Optional[schema.BuildArtifactType] = None,
jwt: Optional[str] = None,
name: Optional[str] = None,
namespace: Optional[str] = None,
packages: Optional[List[str]] = Query([]),
search: Optional[str] = None,
status: Optional[schema.BuildStatus] = None,
):
) -> schema.APIListEnvironment:
"""Retrieve a list of environments.

Parameters
Expand All @@ -654,7 +683,7 @@ async def api_list_environments(
the request
entity : AuthenticationToken
Token of the user making the request
paginated_args : PaginatedArgs
paginated_args : CursorPaginatedArgs
Arguments for controlling pagination of the response
conda_store : app.CondaStore
The running conda store application
Expand All @@ -678,9 +707,11 @@ async def api_list_environments(

Returns
-------
Dict
Paginated JSON response containing the requested environments

schema.APIListEnvironment
Paginated JSON response containing the requested environments. Results are sorted by each
envrionment's build's scheduled_on time to ensure all results are returned when iterating
over pages in systems where the number of environments is changing while results are being
requested; see https://github.com/conda-incubator/conda-store/issues/859 for context
"""
with conda_store.get_db() as db:
if jwt:
Expand All @@ -693,7 +724,7 @@ async def api_list_environments(
else:
role_bindings = None

orm_environments = api.list_environments(
query = api.list_environments(
db,
search=search,
namespace=namespace,
Expand All @@ -706,21 +737,29 @@ async def api_list_environments(
)

# Filter by environments that the user who made the query has access to
orm_environments = filter_environments(
query=orm_environments,
query = filter_environments(
query=query,
role_bindings=auth.entity_bindings(entity),
)

return paginated_api_response(
orm_environments,
paginated_args,
schema.Environment,
exclude={"current_build"},
allowed_sort_bys={
"namespace": orm.Namespace.name,
"name": orm.Environment.name,
},
default_sort_by=["namespace", "name"],
paginated, next_cursor = paginate(
query=query,
ordering_metadata=OrderingMetadata(
order_names=["namespace", "name"],
column_names=["namespace.name", "name"],
column_objects=[orm.Namespace.name, orm.Environment.name],
),
cursor=cursor,
order_by=paginated_args.sort_by,
order=paginated_args.order,
limit=paginated_args.limit,
)

return schema.APIListEnvironment(
data=paginated,
status="ok",
cursor=next_cursor.dump(),
count=1000,
)


Expand Down
Loading