Skip to content
This repository was archived by the owner on May 7, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,36 @@

This is a module to interact with the [Grouper Web Services API](https://spaces.at.internet2.edu/display/Grouper/Grouper+Web+Services).

## Basic Usage

Operations will start by creating a `GrouperClient` object.

``` python
from grouper_python import GrouperClient

grouper_client = GrouperClient(base_url, username, password)
```

`GrouperClient` can also be used as a context manager.

``` python
from grouper_python import GrouperClient

with GrouperClient(base_url, username, password) as grouper_client:
...
```

The `base_url` should end in something like
`grouper-ws/servicesRest/v2_6_000`.

With a `GrouperClient` object, you can query for a subject, stem, or group.
You can also "search" for groups or subjects.

Once you have an object, you can perform various operations against it.
To create a new group or stem for example, you would get the "parent" stem,
and then use `create_child_stem()` or `create_child_group()` to create that
stem or group in that parent.

## Installation

To install grouper library only:
Expand Down
13 changes: 6 additions & 7 deletions grouper_python/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from .objects.client import Client
from .objects.group import Group
from .objects.stem import Stem
from .objects.subject import Subject
from .objects.person import Person
"""grouper_python, a Python package for interacting with Grouper Web Services."""

__version__ = "0.1.0"
from .objects.client import GrouperClient

__all__ = ["Client", "Group", "Stem", "Subject", "Person"]
Client = GrouperClient

__version__ = "0.1.1"
__all__ = ["GrouperClient"]
99 changes: 88 additions & 11 deletions grouper_python/group.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
"""grouper-python.group - functions to interact with group objects.

These are "helper" functions that most likely will not be called directly.
Instead, a GrouperClient class should be created, then from there use that
GrouperClient's methods to find and create objects, and use those objects' methods.
These helper functions are used by those objects, but can be called
directly if needed.
"""

from __future__ import annotations
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING: # pragma: no cover
from .objects.group import CreateGroup, Group
from .objects.client import Client
from .objects.client import GrouperClient
from .objects.subject import Subject
from .objects.exceptions import (
GrouperGroupNotFoundException,
Expand All @@ -15,10 +24,25 @@

def find_group_by_name(
group_name: str,
client: Client,
client: GrouperClient,
stem: str | None = None,
act_as_subject: Subject | None = None,
) -> list[Group]:
"""Find a group or groups by approximate name.

:param group_name: The group name to search for
:type group_name: str
:param client: The GrouperClient to use
:type client: GrouperClient
:param stem: Optional stem to limit the search to, defaults to None
:type stem: str | None, optional
:param act_as_subject: Optional subject to act as, defaults to None
:type act_as_subject: Subject | None, optional
:raises GrouperStemNotFoundException: The specified stem cannot be found
:raises GrouperSuccessException: An otherwise unhandled issue with the result
:return: List of found groups, will be an empty list if no groups are found
:rtype: list[Group]
"""
from .objects.group import Group

body = {
Expand All @@ -45,10 +69,10 @@ def find_group_by_name(
raise GrouperStemNotFoundException(str(stem), r)
else: # pragma: no cover
# Some other issue, so pass the failure through
raise
raise err
if "groupResults" in r["WsFindGroupsResults"]:
return [
Group.from_results(client, grp)
Group(client, grp)
for grp in r["WsFindGroupsResults"]["groupResults"]
]
else:
Expand All @@ -57,9 +81,20 @@ def find_group_by_name(

def create_groups(
groups: list[CreateGroup],
client: Client,
client: GrouperClient,
act_as_subject: Subject | None = None,
) -> list[Group]:
"""Create groups.

:param groups: List of groups to create
:type groups: list[CreateGroup]
:param client: The GrouperClient to use
:type client: GrouperClient
:param act_as_subject: Optional subject to act as, defaults to None
:type act_as_subject: Subject | None, optional
:return: Group objects representing the created groups
:rtype: list[Group]
"""
from .objects.group import Group

groups_to_save = []
Expand Down Expand Up @@ -87,16 +122,29 @@ def create_groups(
act_as_subject=act_as_subject,
)
return [
Group.from_results(client, result["wsGroup"])
Group(client, result["wsGroup"])
for result in r["WsGroupSaveResults"]["results"]
]


def delete_groups(
group_names: list[str],
client: Client,
client: GrouperClient,
act_as_subject: Subject | None = None,
) -> None:
"""Delete the given groups.

:param group_names: The names of groups to delete
:type group_names: list[str]
:param client: The GrouperClient to use
:type client: GrouperClient
:param act_as_subject: Optional subject to act as, defaults to None
:type act_as_subject: Subject | None, optional
:raises GrouperPermissionDenied: Permission denied to complete the operation
:raises GrouperGroupNotFoundException: A group with the given name cannot
be found
:raises GrouperSuccessException: An otherwise unhandled issue with the result
"""
group_lookup = [{"groupName": group} for group in group_names]
body = {
"WsRestGroupDeleteRequest": {
Expand Down Expand Up @@ -139,10 +187,25 @@ def delete_groups(

def get_groups_by_parent(
parent_name: str,
client: Client,
client: GrouperClient,
recursive: bool = False,
act_as_subject: Subject | None = None,
) -> list[Group]:
"""Get Groups within the given parent stem.

:param parent_name: The parent stem to look in
:type parent_name: str
:param client: The GrouperClient to use
:type client: GrouperClient
:param recursive: Whether to look recursively through the entire subtree (True),
or only one level in the given parent (False), defaults to False
:type recursive: bool, optional
:param act_as_subject: Optional subject to act as, defaults to None
:type act_as_subject: Subject | None, optional
:raises GrouperSuccessException: An otherwise unhandled issue with the result
:return: The list of Groups found
:rtype: list[Group]
"""
from .objects.group import Group

body = {
Expand All @@ -162,7 +225,7 @@ def get_groups_by_parent(
)
if "groupResults" in r["WsFindGroupsResults"]:
return [
Group.from_results(client, grp)
Group(client, grp)
for grp in r["WsFindGroupsResults"]["groupResults"]
]
else:
Expand All @@ -171,9 +234,23 @@ def get_groups_by_parent(

def get_group_by_name(
group_name: str,
client: Client,
client: GrouperClient,
act_as_subject: Subject | None = None,
) -> Group:
"""Get a group with the given name.

:param group_name: The name of the group to get
:type group_name: str
:param client: The GrouperClient to use
:type client: GrouperClient
:param act_as_subject: Optional subject to act as, defaults to None
:type act_as_subject: Subject | None, optional
:raises GrouperGroupNotFoundException: A group with the given name cannot
be found
:raises GrouperSuccessException: An otherwise unhandled issue with the result
:return: The group with the given name
:rtype: Group
"""
from .objects.group import Group

body = {
Expand All @@ -186,4 +263,4 @@ def get_group_by_name(
r = client._call_grouper("/groups", body, act_as_subject=act_as_subject)
if "groupResults" not in r["WsFindGroupsResults"]:
raise GrouperGroupNotFoundException(group_name, r)
return Group.from_results(client, r["WsFindGroupsResults"]["groupResults"][0])
return Group(client, r["WsFindGroupsResults"]["groupResults"][0])
Loading