-
Notifications
You must be signed in to change notification settings - Fork 16
Implement script to setup workspace for tests #133
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,299 @@ | ||
""" | ||
Notice: This script will attempt to setup a test workspace on GitGuardian. | ||
GG-Yanne marked this conversation as resolved.
Show resolved
Hide resolved
GG-Yanne marked this conversation as resolved.
Show resolved
Hide resolved
|
||
This will allow the user to run tests without relying on cassettes, note that | ||
there are a few limitations due to actions that cannot be performed through | ||
the API, notably : | ||
- Create the workspace | ||
- We cannot create members, so there must exist a minimum amount of members in the workspace | ||
- This also means deleted members cannot be brought back from the script | ||
- We cannot integrate a source entirely from the public API | ||
- There must exist a source in the workspace | ||
""" | ||
|
||
import os | ||
from typing import Iterable, List, TypeVar | ||
|
||
from pygitguardian.client import GGClient | ||
from pygitguardian.models import ( | ||
AccessLevel, | ||
CreateInvitation, | ||
CreateTeam, | ||
CreateTeamInvitation, | ||
CreateTeamMember, | ||
Detail, | ||
IncidentPermission, | ||
InvitationParameters, | ||
Member, | ||
MembersParameters, | ||
Source, | ||
Team, | ||
TeamMember, | ||
TeamsParameters, | ||
UpdateMember, | ||
UpdateTeamSource, | ||
) | ||
from pygitguardian.models_utils import FromDictWithBase | ||
from tests.utils import CursorPaginatedResponse | ||
|
||
|
||
client = GGClient( | ||
api_key=os.environ["GITGUARDIAN_API_KEY"], | ||
base_uri=os.environ.get("GITGUARDIAN_API_URL"), | ||
) | ||
|
||
T = TypeVar("T") | ||
PaginatedDataType = TypeVar("PaginatedDataType", bound=FromDictWithBase) | ||
|
||
MIN_NB_TEAM = 2 | ||
MIN_NB_MEMBER = 3 # 1 owner, 1 manager and at least one member | ||
MIN_NB_TEAM_MEMBER = 2 | ||
# This is the team that is created in the tests, it should be deleted before we run the tests | ||
GG-Yanne marked this conversation as resolved.
Show resolved
Hide resolved
|
||
PYGITGUARDIAN_TEST_TEAM = "PyGitGuardian team" | ||
|
||
|
||
def ensure_success(var: T | Detail) -> T: | ||
if not isinstance(var, Detail): | ||
return var | ||
else: | ||
raise TypeError(var.detail) | ||
|
||
|
||
def unwrap_paginated_response( | ||
var: CursorPaginatedResponse[PaginatedDataType] | Detail, | ||
) -> List[PaginatedDataType]: | ||
data = ensure_success(var) | ||
|
||
return data.data | ||
|
||
|
||
def ensure_member_coherence(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would help to have comments explaining what we ensure here, either as a docstring or as comments to the different parts of the function. From what I read here we ensure that:
Can you add this here and to the other There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added docstring listing the requirements and if the script can create the required resources 👍 |
||
""" | ||
This function ensures that the workspace : | ||
- Has no deactivated members | ||
- If there are, they will be activated | ||
- Has at most 1 admin / manager (excluding owner) | ||
- It may demote some manager to member | ||
- There is at least `MIN_NB_MEMBER` | ||
""" | ||
|
||
deactivated_members = unwrap_paginated_response( | ||
client.list_members(MembersParameters(active=False)) | ||
) | ||
for member in deactivated_members: | ||
client.update_member(UpdateMember(member.id, AccessLevel.MEMBER, active=True)) | ||
|
||
admin_members = unwrap_paginated_response( | ||
client.list_members(MembersParameters(access_level=AccessLevel.MANAGER)) | ||
) | ||
|
||
if len(admin_members) > 1: | ||
for member in admin_members[1:]: | ||
ensure_success( | ||
client.update_member(UpdateMember(member.id, AccessLevel.MEMBER)) | ||
) | ||
else: | ||
members = unwrap_paginated_response( | ||
client.list_members(MembersParameters(access_level=AccessLevel.MEMBER)) | ||
) | ||
assert ( | ||
len(members) > 0 | ||
), "There must be at least one member with access level member in the workspace" | ||
|
||
ensure_success( | ||
client.update_member(UpdateMember(members[0].id, AccessLevel.MANAGER)) | ||
) | ||
|
||
members = ensure_success(client.list_members(MembersParameters(per_page=5))) | ||
|
||
assert ( | ||
len(members.data) > MIN_NB_MEMBER | ||
), "There must be at least 3 members in the workspace" | ||
|
||
|
||
def add_source_to_team(team: Team, available_sources: Iterable[Source] | None = None): | ||
if available_sources is None: | ||
available_sources = ensure_success(client.list_sources()).data | ||
|
||
ensure_success( | ||
client.update_team_source( | ||
UpdateTeamSource(team.id, [source.id for source in available_sources], []) | ||
) | ||
) | ||
|
||
|
||
def add_team_members( | ||
team: Team, | ||
team_members: Iterable[TeamMember], | ||
nb_members: int, | ||
available_members: Iterable[Member] | None = None, | ||
): | ||
assert nb_members > 0, "We should add at least one member" | ||
if available_members is None: | ||
available_members = unwrap_paginated_response(client.list_members()) | ||
|
||
# Every manager is by default a team leader | ||
has_admin = any(team_member.is_team_leader for team_member in team_members) | ||
|
||
if not has_admin: | ||
admin_member = next( | ||
( | ||
member | ||
for member in available_members | ||
if member.access_level == AccessLevel.MANAGER | ||
), | ||
None, | ||
) | ||
assert admin_member is not None, "There should be at least one admin member" | ||
|
||
ensure_success( | ||
client.create_team_member( | ||
team.id, | ||
CreateTeamMember( | ||
admin_member.id, | ||
is_team_leader=True, | ||
incident_permission=IncidentPermission.FULL_ACCESS, | ||
), | ||
) | ||
) | ||
nb_members -= 1 | ||
|
||
team_member_ids = {team_member.member_id for team_member in team_members} | ||
for _ in range(nb_members): | ||
to_add_member = next( | ||
( | ||
member | ||
for member in available_members | ||
if member.id not in team_member_ids | ||
and member.access_level not in {AccessLevel.OWNER, AccessLevel.MANAGER} | ||
), | ||
None, | ||
) | ||
assert to_add_member is not None, "There is not enough members in the workspace" | ||
is_team_leader = False | ||
permissions = IncidentPermission.FULL_ACCESS | ||
|
||
if to_add_member.access_level == AccessLevel.MANAGER: | ||
is_team_leader = True | ||
|
||
ensure_success( | ||
client.create_team_member( | ||
team.id, | ||
CreateTeamMember( | ||
to_add_member.id, | ||
is_team_leader=is_team_leader, | ||
incident_permission=permissions, | ||
), | ||
) | ||
) | ||
|
||
|
||
def ensure_team_coherence(): | ||
""" | ||
This function ensures that the workspace : | ||
- Has no team with name prefixed by `PYGITGUARDIAN_TEST_TEAM` | ||
- At least `MIN_NB_TEAM` exist | ||
- If not they will be created | ||
- Every team has at least one source | ||
- If possible, it will try to add at least one source | ||
- Every team has at least 2 members, an admin and a member | ||
- If possible, it will try to add those members | ||
""" | ||
|
||
pygitguardian_teams = [] | ||
try: | ||
pygitguardian_teams = unwrap_paginated_response( | ||
client.list_teams(TeamsParameters(search=PYGITGUARDIAN_TEST_TEAM)) | ||
) | ||
except TypeError as exc: | ||
if str(exc) != "Team not found.": | ||
raise | ||
finally: | ||
for team in pygitguardian_teams: | ||
ensure_success(client.delete_team(team.id)) | ||
|
||
teams = unwrap_paginated_response( | ||
# exclude global team since we can't add sources / members to it | ||
client.list_teams(TeamsParameters(is_global=False)) | ||
) | ||
|
||
nb_teams = len(teams) | ||
if nb_teams < MIN_NB_TEAM: | ||
for i in range(MIN_NB_TEAM - nb_teams): | ||
new_team = ensure_success( | ||
client.create_team(CreateTeam(name=f"PyGitGuardian Team {i}")) | ||
) | ||
teams.append(new_team) | ||
|
||
# Ensure every team has: | ||
# - At least one source | ||
# - At least two members, one with admin access and one with member access | ||
for team in teams: | ||
team_members = unwrap_paginated_response(client.list_team_members(team.id)) | ||
nb_team_members = len(team_members) | ||
if nb_team_members < MIN_NB_TEAM_MEMBER: | ||
add_team_members(team, team_members, MIN_NB_TEAM_MEMBER - nb_team_members) | ||
|
||
team_sources = unwrap_paginated_response(client.list_team_sources(team.id)) | ||
nb_team_sources = len(team_sources) | ||
if nb_team_sources == 0: | ||
add_source_to_team(team) | ||
|
||
|
||
def ensure_invitation_coherence(): | ||
""" | ||
This function ensures that the workspace : | ||
- Has no invitation for emails starting with `pygitguardian` | ||
- There is at least one pending invitation | ||
- If not, an invitation will be sent to `pygitguardian@example.com` | ||
- All team have attached team invitations | ||
- If not, they will be created | ||
""" | ||
|
||
test_invitation = unwrap_paginated_response( | ||
client.list_invitations(InvitationParameters(search="pygitguardian")) | ||
) | ||
|
||
for invitation in test_invitation: | ||
ensure_success(client.delete_invitation(invitation.id)) | ||
invitations = unwrap_paginated_response(client.list_invitations()) | ||
|
||
if len(invitations) < 1: | ||
invitation = ensure_success( | ||
client.create_invitation( | ||
CreateInvitation( | ||
email="pygitguardian@invitation.com", | ||
access_level=AccessLevel.MEMBER, | ||
) | ||
) | ||
) | ||
invitations.append(invitation) | ||
|
||
teams = unwrap_paginated_response(client.list_teams()) | ||
invitation = invitations[0] | ||
for team in teams: | ||
team_invitations = unwrap_paginated_response( | ||
client.list_team_invitations(team.id) | ||
) | ||
if not team_invitations: | ||
ensure_success( | ||
client.create_team_invitation( | ||
team.id, | ||
CreateTeamInvitation( | ||
invitation_id=invitation.id, | ||
is_team_leader=False, | ||
incident_permission=IncidentPermission.FULL_ACCESS, | ||
), | ||
) | ||
) | ||
|
||
|
||
def main(): | ||
ensure_member_coherence() | ||
ensure_team_coherence() | ||
ensure_invitation_coherence() | ||
|
||
print("Test workspace has been set up properly") | ||
sevbch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
if __name__ == "__main__": | ||
main() |
Uh oh!
There was an error while loading. Please reload this page.