Skip to content

Commit

Permalink
Merge pull request #77 from NERSC/clients
Browse files Browse the repository at this point in the history
Add support to API clients
  • Loading branch information
cjh1 authored Jul 18, 2024
2 parents d262a5c + 8ec881f commit 6a7d22d
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 8 deletions.
15 changes: 15 additions & 0 deletions src/sfapi_client/_async/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .._models import (
UserInfo as UserBase,
GroupList as GroupsResponse,
Client as APIClient,
)
from .projects import AsyncProject, AsyncRole
from ..exceptions import SfApiError
Expand Down Expand Up @@ -107,3 +108,17 @@ async def roles(self) -> List[AsyncRole]:
]

return roles

async def clients(self) -> List[APIClient]:
"""
Get information about the authenticated user's SFAPI clients.
:return: the api clients
"""
r = await self.client.get("account/clients")

json_response = r.json()

clients = [APIClient.model_validate(c) for c in json_response]

return clients
41 changes: 34 additions & 7 deletions src/sfapi_client/_models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# generated by datamodel-codegen:
# filename: https://api.nersc.gov/api/v1.2/openapi.json
# timestamp: 2023-10-20T20:57:45+00:00
# filename: https://api-dev.nersc.gov/api/v1.2/openapi.json
# timestamp: 2024-07-18T15:58:27+00:00

from __future__ import annotations

from datetime import date as date_, datetime
from enum import Enum
from typing import Dict, List, Optional
from typing import Any, Dict, List, Optional, Union

from pydantic import BaseModel, Field

Expand Down Expand Up @@ -37,6 +37,9 @@ class BodySubmitJobComputeJobsMachinePost(BaseModel):
isPath: bool = Field(..., title="Ispath")
job: str = Field(..., title="Job")
args: Optional[List[str]] = Field([], title="Args")
callbackTimeout: Optional[int] = Field(None, title="Callbacktimeout")
callbackEmail: Optional[str] = Field(None, title="Callbackemail")
callbackUrl: Optional[str] = Field(None, title="Callbackurl")


class BodyUpdateGroupMembershipAccountGroupsGroupPut(BaseModel):
Expand All @@ -53,6 +56,13 @@ class Changelog(BaseModel):
change: Optional[str] = Field(None, title="Change")


class Client(BaseModel):
name: str = Field(..., title="Name")
clientId: str = Field(..., title="Clientid")
expiresAt: str = Field(..., title="Expiresat")
comments: Optional[str] = Field(None, title="Comments")


class Config(BaseModel):
key: str = Field(..., title="Key")
value: Optional[str] = Field(None, title="Value")
Expand Down Expand Up @@ -88,6 +98,7 @@ class Outage(BaseModel):

class PublicHost(str, Enum):
dtn01 = "dtn01"
dtns = "dtns"
perlmutter = "perlmutter"


Expand Down Expand Up @@ -117,8 +128,8 @@ class StorageStats(BaseModel):

class Task(BaseModel):
id: str = Field(..., title="Id")
status: Optional[str] = Field(None, title="Status")
result: Optional[str] = Field(None, title="Result")
status: Optional[str] = Field(..., title="Status")
result: Optional[str] = Field(..., title="Result")


class Tasks(BaseModel):
Expand All @@ -142,7 +153,7 @@ class UserStats(BaseModel):


class ValidationError(BaseModel):
loc: List[str] = Field(..., title="Location")
loc: List[Union[str, int]] = Field(..., title="Location")
msg: str = Field(..., title="Message")
type: str = Field(..., title="Error Type")

Expand Down Expand Up @@ -212,6 +223,22 @@ class ProjectStats(BaseModel):
hours_used: Optional[float] = Field(None, title="Hours Used")
project_hours_given: Optional[float] = Field(None, title="Project Hours Given")
project_hours_used: Optional[float] = Field(None, title="Project Hours Used")
cpu_hours_given: Optional[float] = Field(None, title="Cpu Hours Given")
cpu_hours_used: Optional[float] = Field(None, title="Cpu Hours Used")
cpu_project_hours_given: Optional[float] = Field(
None, title="Cpu Project Hours Given"
)
cpu_project_hours_used: Optional[float] = Field(
None, title="Cpu Project Hours Used"
)
gpu_hours_given: Optional[float] = Field(None, title="Gpu Hours Given")
gpu_hours_used: Optional[float] = Field(None, title="Gpu Hours Used")
gpu_project_hours_given: Optional[float] = Field(
None, title="Gpu Project Hours Given"
)
gpu_project_hours_used: Optional[float] = Field(
None, title="Gpu Project Hours Used"
)
projdir_usage: Optional[List[StorageStats]] = Field(None, title="Projdir Usage")
project_projdir_usage: Optional[StorageStats] = None
hpss_usage: Optional[List[StorageStats]] = Field(None, title="Hpss Usage")
Expand All @@ -226,7 +253,7 @@ class QueueOutput(BaseModel):
class TransferResult(BaseModel):
task_id: str = Field(..., title="Task Id")
status: AppRoutersStorageModelsStatus
reason: Optional[str] = Field(None, title="Reason")
reason: Optional[str] = Field(..., title="Reason")


class UploadResult(BaseModel):
Expand Down
18 changes: 17 additions & 1 deletion src/sfapi_client/_sync/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .._models import (
UserInfo as UserBase,
GroupList as GroupsResponse,
Client as APIClient,
)
from .projects import Project, Role
from ..exceptions import SfApiError
Expand All @@ -31,7 +32,8 @@ class User(UserBase):
@staticmethod
@check_auth
def _fetch_user(
client: "Client", username: Optional[str] = None # noqa: F821
client: "Client", # noqa: F821
username: Optional[str] = None, # noqa: F821
): # noqa: F821
url = "account/"
if username is not None:
Expand Down Expand Up @@ -106,3 +108,17 @@ def roles(self) -> List[Role]:
]

return roles

def clients(self) -> List[APIClient]:
"""
Get information about the authenticated user's SFAPI clients.
:return: the api clients
"""
r = self.client.get("account/clients")

json_response = r.json()

clients = [APIClient.model_validate(c) for c in json_response]

return clients
16 changes: 16 additions & 0 deletions tests/test_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,19 @@ def test_get_user_projects_different_user(authenticated_client, test_another_use

with pytest.raises(SfApiError):
user.projects()


def test_user_api_clients(authenticated_client, client_id, test_username):
with authenticated_client as client:
user = client.user(test_username)
clients = user.clients()

assert clients
# Check that we can at least find the current client
found_current_client = False
for c in clients:
if c.clientId == client_id:
found_current_client = True
break

assert found_current_client
17 changes: 17 additions & 0 deletions tests/test_users_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,20 @@ async def test_get_user_projects_different_user(

with pytest.raises(SfApiError):
await user.projects()


@pytest.mark.asyncio
async def test_user_api_clients(async_authenticated_client, client_id, test_username):
async with async_authenticated_client as client:
user = await client.user(test_username)
clients = await user.clients()

assert clients
# Check that we can at least find the current client
found_current_client = False
for c in clients:
if c.clientId == client_id:
found_current_client = True
break

assert found_current_client

0 comments on commit 6a7d22d

Please sign in to comment.