Skip to content

Commit

Permalink
Implement AuthClient.update_project (#774)
Browse files Browse the repository at this point in the history
Additionally, fix `create_project` in a minor way, to include a `*`
marker in its signature for keyword-only arguments.
  • Loading branch information
sirosen authored Jul 6, 2023
1 parent 3339d75 commit 22ff416
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 0 deletions.
5 changes: 5 additions & 0 deletions changelog.d/20230705_173841_sirosen_add_project_update.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Added
~~~~~

- Add ``AuthClient.update_project`` as a method for updating a Project via the
Globus Auth Developer API (:pr:`NUMBER`)
71 changes: 71 additions & 0 deletions src/globus_sdk/_testing/data/auth/update_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import uuid

from globus_sdk._testing.models import RegisteredResponse, ResponseSet

project_id = str(uuid.uuid1())
star_lord = {
"identity_provider": str(uuid.uuid1()),
"identity_type": "login",
"organization": "Guardians of the Galaxy",
"status": "used",
"id": str(uuid.uuid1()),
"name": "Star Lord",
"username": "star.lord@guardians.galaxy",
"email": "star.lord2@guardians.galaxy",
}
guardians_group = {
"id": str(uuid.uuid1()),
"name": "Guardians of the Galaxy",
"description": "A group of heroes",
"organization": "Guardians of the Galaxy",
}


RESPONSES = ResponseSet(
default=RegisteredResponse(
service="auth",
path=f"/v2/api/projects/{project_id}",
method="PUT",
json={
"project": {
"contact_email": "support@globus.org",
"id": project_id,
"admins": {
"identities": [star_lord],
"groups": [],
},
"project_name": "Guardians of the Galaxy",
"admin_ids": [star_lord["id"]],
"admin_group_ids": None,
"display_name": "Guardians of the Galaxy",
}
},
metadata={
"id": project_id,
"admin_id": star_lord["id"],
},
),
admin_group=RegisteredResponse(
service="auth",
path=f"/v2/api/projects/{project_id}",
method="PUT",
json={
"project": {
"contact_email": "support@globus.org",
"id": project_id,
"admins": {
"identities": [],
"groups": [guardians_group],
},
"project_name": "Guardians of the Galaxy",
"admin_ids": None,
"admin_group_ids": [guardians_group["id"]],
"display_name": "Guardians of the Galaxy",
}
},
metadata={
"id": project_id,
"admin_group_id": guardians_group["id"],
},
),
)
62 changes: 62 additions & 0 deletions src/globus_sdk/services/auth/client/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ def create_project(
self,
display_name: str,
contact_email: str,
*,
admin_ids: UUIDLike | t.Iterable[UUIDLike] | None = None,
admin_group_ids: UUIDLike | t.Iterable[UUIDLike] | None = None,
) -> GlobusHTTPResponse:
Expand Down Expand Up @@ -438,6 +439,67 @@ def create_project(
body["admin_group_ids"] = list(utils.safe_strseq_iter(admin_group_ids))
return self.post("/v2/api/projects", data={"project": body})

def update_project(
self,
project_id: UUIDLike,
*,
display_name: str | None = None,
contact_email: str | None = None,
admin_ids: UUIDLike | t.Iterable[UUIDLike] | None = None,
admin_group_ids: UUIDLike | t.Iterable[UUIDLike] | None = None,
) -> GlobusHTTPResponse:
"""
Update a project. Requires the ``manage_projects`` scope.
:param project_id: The ID of the project to update
:type project_id: str or uuid
:param display_name: The name of the project
:type display_name: str
:param contact_email: The email address of the project's point of contact
:type contact_email: str
:param admin_ids: A list of user IDs to be set as admins of the project
:type admin_ids: str or uuid or iterable of str or uuid, optional
:param admin_group_ids: A list of group IDs to be set as admins of the project
:type admin_group_ids: str or uuid or iterable of str or uuid, optional
.. tab-set::
.. tab-item:: Example Usage
The following snippet uses the ``manage_projects`` scope as well
as the ``email`` scope to get the current user email address and set it
as a project's contact email:
.. code-block:: pycon
>>> ac = globus_sdk.AuthClient(...)
>>> project_id = ...
>>> userinfo = ac.oauth2_userinfo()
>>> email = userinfo["email"]
>>> r = ac.update_project(project_id, contact_email=email)
.. tab-item:: Example Response Data
.. expandtestfixture:: auth.update_project
.. tab-item:: API Info
``POST /v2/api/projects``
.. extdoclink:: Update Project
:ref: auth/reference/#update_project
"""
body: dict[str, t.Any] = {}
if display_name is not None:
body["display_name"] = display_name
if contact_email is not None:
body["contact_email"] = contact_email
if admin_ids is not None:
body["admin_ids"] = list(utils.safe_strseq_iter(admin_ids))
if admin_group_ids is not None:
body["admin_group_ids"] = list(utils.safe_strseq_iter(admin_group_ids))
return self.put(f"/v2/api/projects/{project_id}", data={"project": body})

#
# OAuth2 Behaviors & APIs
#
Expand Down
81 changes: 81 additions & 0 deletions tests/functional/services/auth/base/test_update_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import json
import uuid

import pytest

from globus_sdk._testing import get_last_request, load_response


@pytest.mark.parametrize(
"admin_id_style", ("none", "string", "list", "set", "uuid", "uuid_list")
)
def test_update_project_admin_id_styles(client, admin_id_style):
meta = load_response(client.update_project).metadata

if admin_id_style == "none":
admin_ids = None
elif admin_id_style == "string":
admin_ids = meta["admin_id"]
elif admin_id_style == "list":
admin_ids = [meta["admin_id"]]
elif admin_id_style == "set":
admin_ids = {meta["admin_id"]}
elif admin_id_style == "uuid":
admin_ids = uuid.UUID(meta["admin_id"])
elif admin_id_style == "uuid_list":
admin_ids = [uuid.UUID(meta["admin_id"])]
else:
raise NotImplementedError(f"unknown admin_id_style {admin_id_style}")

project_id = meta["id"]
res = client.update_project(
project_id, display_name="My Project", admin_ids=admin_ids
)

assert res["project"]["id"] == meta["id"]

last_req = get_last_request()
data = json.loads(last_req.body)
assert list(data) == ["project"], data # 'project' is the only key
if admin_id_style == "none":
assert data["project"] == {"display_name": "My Project"}
else:
assert data["project"] == {
"display_name": "My Project",
"admin_ids": [meta["admin_id"]],
}


@pytest.mark.parametrize(
"group_id_style", ("string", "list", "set", "uuid", "uuid_list")
)
def test_update_project_group_id_styles(client, group_id_style):
meta = load_response(client.update_project, case="admin_group").metadata

if group_id_style == "string":
group_ids = meta["admin_group_id"]
elif group_id_style == "list":
group_ids = [meta["admin_group_id"]]
elif group_id_style == "set":
group_ids = {meta["admin_group_id"]}
elif group_id_style == "uuid":
group_ids = uuid.UUID(meta["admin_group_id"])
elif group_id_style == "uuid_list":
group_ids = [uuid.UUID(meta["admin_group_id"])]
else:
raise NotImplementedError(f"unknown group_id_style {group_id_style}")

project_id = meta["id"]
res = client.update_project(
project_id, contact_email="support@globus.org", admin_group_ids=group_ids
)

assert res["project"]["id"] == meta["id"]

last_req = get_last_request()
data = json.loads(last_req.body)
assert list(data) == ["project"], data # 'project' is the only key
assert data["project"] == {
"contact_email": "support@globus.org",
"admin_group_ids": [meta["admin_group_id"]],
}

0 comments on commit 22ff416

Please sign in to comment.