diff --git a/changelog.d/20241206_145636_yanne.bensafia_members_teams_endpoints.md b/changelog.d/20241206_145636_yanne.bensafia_members_teams_endpoints.md new file mode 100644 index 00000000..25253503 --- /dev/null +++ b/changelog.d/20241206_145636_yanne.bensafia_members_teams_endpoints.md @@ -0,0 +1,43 @@ + + + + +### Added + +- Added support for members and teams endpoints. +- Added support for invitations endpoints. +- Added support for sources endpoints. + + + + + diff --git a/doc/dev/tests.md b/doc/dev/tests.md new file mode 100644 index 00000000..927f9e3a --- /dev/null +++ b/doc/dev/tests.md @@ -0,0 +1,31 @@ +# Users, teams and sources + +It is possible to removed all cassettes from this repository and run them all by providing a valid personal access token. + +However, for users, teams and sources, this requires a workplace with enough data filled in. + +## For source related tests + +We require the workplace to have: + +- At least two sources + +## For member related tests + +We require the workplace to have: + +- At least two members, they should have different access levels +- At least one invitation sent out + +## For team related tests + +We require the workplace to have : + +- At least two teams +- These teams should have 2 or more members +- These teams should have at least one monitored source in their perimeter +- ⚠️ No team should have all available sources in their perimeter +- There should exist a team where at least one member is not invited +- There should exist one pending team invitation + +> Keep in mind that some of these tests will delete resources, so they should run in an isolated workplace diff --git a/pygitguardian/client.py b/pygitguardian/client.py index efddb25f..1e3fa749 100644 --- a/pygitguardian/client.py +++ b/pygitguardian/client.py @@ -26,14 +26,25 @@ ) from .models import ( APITokensResponse, + CreateInvitation, + CreateInvitationParameters, + CreateTeam, + CreateTeamInvitation, + CreateTeamMember, + CreateTeamMemberParameters, + DeleteMemberParameters, Detail, Document, DocumentSchema, HealthCheckResponse, HoneytokenResponse, HoneytokenWithContextResponse, + Invitation, + InvitationParameters, JWTResponse, JWTService, + Member, + MembersParameters, MultiScanResult, QuotaResponse, RemediationMessages, @@ -41,7 +52,20 @@ SecretIncident, SecretScanPreferences, ServerMetadata, + Source, + SourceParameters, + Team, + TeamInvitation, + TeamInvitationParameters, + TeamMember, + TeamMemberParameters, + TeamSourceParameters, + TeamsParameters, + UpdateMember, + UpdateTeam, + UpdateTeamSource, ) +from .models_utils import CursorPaginatedResponse from .sca_models import ( ComputeSCAFilesResult, SCAScanAllOutput, @@ -115,6 +139,13 @@ def is_create_ok(resp: Response) -> bool: ) +def is_delete_ok(resp: Response) -> bool: + """ + is_delete_ok returns True if the API returns code 204 + """ + return resp.status_code == codes.no_content + + def _create_tar(root_path: Path, filenames: List[str]) -> bytes: """ :param root_path: the root_path from which the tar is created @@ -335,6 +366,40 @@ def post( **kwargs, ) + def patch( + self, + endpoint: str, + data: Union[Dict[str, Any], List[Dict[str, Any]], None] = None, + version: str = DEFAULT_API_VERSION, + extra_headers: Optional[Dict[str, str]] = None, + **kwargs: Any, + ) -> Response: + return self.request( + "patch", + endpoint=endpoint, + json=data, + version=version, + extra_headers=extra_headers, + **kwargs, + ) + + def delete( + self, + endpoint: str, + data: Union[Dict[str, Any], List[Dict[str, Any]], None] = None, + version: str = DEFAULT_API_VERSION, + extra_headers: Optional[Dict[str, str]] = None, + **kwargs: Any, + ) -> Response: + return self.request( + "delete", + endpoint=endpoint, + json=data, + version=version, + extra_headers=extra_headers, + **kwargs, + ) + def health_check(self) -> HealthCheckResponse: """ health_check handles the /health endpoint of the API @@ -859,3 +924,409 @@ def scan_diff( result = load_detail(response) result.status_code = response.status_code return result + + def list_members( + self, + parameters: Optional[MembersParameters] = None, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, CursorPaginatedResponse[Member]]: + + response = self.get( + endpoint="members", + params=parameters.to_dict() if parameters else {}, + extra_headers=extra_headers, + ) + + obj: Union[Detail, CursorPaginatedResponse[Member]] + if is_ok(response): + obj = CursorPaginatedResponse[Member].from_response(response, Member) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def get_member( + self, + member_id: int, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, Member]: + response = self.get( + endpoint=f"members/{member_id}", + extra_headers=extra_headers, + ) + obj: Union[Detail, Member] + if is_ok(response): + obj = Member.from_dict(response.json()) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def update_member( + self, + payload: UpdateMember, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, Member]: + + member_id = payload.id + data = UpdateMember.to_dict(payload) + del data["id"] + + response = self.patch( + f"members/{member_id}", data=data, extra_headers=extra_headers + ) + obj: Union[Detail, Member] + if is_ok(response): + obj = Member.from_dict(response.json()) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def delete_member( + self, + member: DeleteMemberParameters, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Optional[Detail]: + member_id = member.id + data = member.to_dict() + del data["id"] + + response = self.delete( + f"members/{member_id}", params=data, extra_headers=extra_headers + ) + + # We bypass `is_ok` because the response content type is none + if not is_delete_ok(response): + return load_detail(response) + + def list_teams( + self, + parameters: Optional[TeamsParameters] = None, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, CursorPaginatedResponse[Team]]: + params = parameters.to_dict() if parameters else {} + response = self.get( + endpoint="teams", + params=params, + extra_headers=extra_headers, + ) + + obj: Union[Detail, CursorPaginatedResponse[Team]] + if is_ok(response): + obj = CursorPaginatedResponse[Team].from_response(response, Team) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def get_team( + self, + team_id: int, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, Team]: + response = self.get( + endpoint=f"teams/{team_id}", + extra_headers=extra_headers, + ) + + obj: Union[Detail, Team] + if is_ok(response): + obj = Team.from_dict(response.json()) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def create_team( + self, + team: CreateTeam, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, Team]: + response = self.post( + endpoint="teams", data=CreateTeam.to_dict(team), extra_headers=extra_headers + ) + + obj: Union[Detail, Team] + if is_create_ok(response): + obj = Team.from_dict(response.json()) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def update_team( + self, + payload: UpdateTeam, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, Team]: + team_id = payload.id + data = UpdateTeam.to_dict(payload) + del data["id"] + + response = self.patch( + endpoint=f"teams/{team_id}", + data=data, + extra_headers=extra_headers, + ) + + obj: Union[Detail, Team] + if is_ok(response): + obj = Team.from_dict(response.json()) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def delete_team( + self, + team_id: int, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Optional[Detail]: + response = self.delete( + endpoint=f"teams/{team_id}", + extra_headers=extra_headers, + ) + + if not is_delete_ok(response): + return load_detail(response) + + def list_team_invitations( + self, + team_id: int, + parameters: Optional[TeamInvitationParameters] = None, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, CursorPaginatedResponse[TeamInvitation]]: + response = self.get( + endpoint=f"teams/{team_id}/team_invitations", + params=parameters.to_dict() if parameters else {}, + extra_headers=extra_headers, + ) + + obj: Union[Detail, CursorPaginatedResponse[TeamInvitation]] + if is_ok(response): + obj = CursorPaginatedResponse[TeamInvitation].from_response( + response, TeamInvitation + ) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def create_team_invitation( + self, + team_id: int, + invitation: CreateTeamInvitation, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, TeamInvitation]: + response = self.post( + endpoint=f"teams/{team_id}/team_invitations", + data=CreateTeamInvitation.to_dict(invitation), + extra_headers=extra_headers, + ) + + obj: Union[Detail, TeamInvitation] + if is_create_ok(response): + obj = TeamInvitation.from_dict(response.json()) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def delete_team_invitation( + self, + team_id: int, + invitation_id: int, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Optional[Detail]: + response = self.delete( + endpoint=f"teams/{team_id}/team_invitations/{invitation_id}", + extra_headers=extra_headers, + ) + + if not is_delete_ok(response): + return load_detail(response) + + def list_team_members( + self, + team_id: int, + parameters: Optional[TeamMemberParameters] = None, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, CursorPaginatedResponse[TeamMember]]: + response = self.get( + endpoint=f"teams/{team_id}/team_memberships", + params=parameters.to_dict() if parameters else {}, + extra_headers=extra_headers, + ) + + obj: Union[Detail, CursorPaginatedResponse[TeamMember]] + if is_ok(response): + obj = CursorPaginatedResponse[TeamMember].from_response( + response, TeamMember + ) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def create_team_member( + self, + team_id: int, + member: CreateTeamMember, + parameters: Optional[CreateTeamMemberParameters] = None, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, TeamMember]: + response = self.post( + endpoint=f"teams/{team_id}/team_memberships", + data=CreateTeamMember.to_dict(member), + params=parameters.to_dict() if parameters else {}, + extra_headers=extra_headers, + ) + + obj: Union[Detail, TeamMember] + if is_create_ok(response): + obj = TeamMember.from_dict(response.json()) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def delete_team_member( + self, + team_id: int, + team_member_id: int, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Optional[Detail]: + response = self.delete( + endpoint=f"teams/{team_id}/team_memberships/{team_member_id}", + extra_headers=extra_headers, + ) + + if not is_delete_ok(response): + return load_detail(response) + + def list_sources( + self, + parameters: Optional[SourceParameters] = None, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, CursorPaginatedResponse[Source]]: + response = self.get( + endpoint="sources", + params=parameters.to_dict() if parameters else {}, + extra_headers=extra_headers, + ) + + obj: Union[Detail, CursorPaginatedResponse[Source]] + if is_ok(response): + obj = CursorPaginatedResponse[Source].from_response(response, Source) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def list_team_sources( + self, + team_id: int, + parameters: Optional[TeamSourceParameters] = None, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, CursorPaginatedResponse[Source]]: + response = self.get( + endpoint=f"teams/{team_id}/sources", + params=parameters.to_dict() if parameters else {}, + extra_headers=extra_headers, + ) + + obj: Union[Detail, CursorPaginatedResponse[Source]] + if is_ok(response): + obj = CursorPaginatedResponse[Source].from_response(response, Source) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def update_team_source( + self, + payload: UpdateTeamSource, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Optional[Detail]: + team_id = payload.team_id + data = payload.to_dict() + del data["team_id"] + + response = self.post( + endpoint=f"teams/{team_id}/sources", + data=data, + extra_headers=extra_headers, + ) + + if not response.status_code == 204: + return load_detail(response) + + def list_invitations( + self, + parameters: Optional[InvitationParameters] = None, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, CursorPaginatedResponse[Invitation]]: + response = self.get( + endpoint="invitations", + params=parameters.to_dict() if parameters else {}, + extra_headers=extra_headers, + ) + + obj: Union[Detail, CursorPaginatedResponse[Invitation]] + if is_ok(response): + obj = CursorPaginatedResponse[Invitation].from_response( + response, Invitation + ) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def create_invitation( + self, + invitation: CreateInvitation, + parameters: Optional[CreateInvitationParameters] = None, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Union[Detail, Invitation]: + response = self.post( + endpoint="invitations", + data=CreateInvitation.to_dict(invitation), + params=parameters.to_dict() if parameters else {}, + extra_headers=extra_headers, + ) + + obj: Union[Detail, Invitation] + if is_create_ok(response): + obj = Invitation.from_dict(response.json()) + else: + obj = load_detail(response) + + obj.status_code = response.status_code + return obj + + def delete_invitation( + self, + invitation_id: int, + extra_headers: Optional[Dict[str, str]] = None, + ) -> Optional[Detail]: + response = self.delete( + endpoint=f"invitations/{invitation_id}", extra_headers=extra_headers + ) + + if not is_delete_ok(response): + return load_detail(response) diff --git a/pygitguardian/models.py b/pygitguardian/models.py index ac84a258..979a6661 100644 --- a/pygitguardian/models.py +++ b/pygitguardian/models.py @@ -4,20 +4,18 @@ from dataclasses import dataclass, field from datetime import date, datetime from enum import Enum -from typing import Any, ClassVar, Dict, List, Literal, Optional, Type, cast +from typing import Any, Dict, List, Literal, Optional, Type, cast from uuid import UUID import marshmallow_dataclass from marshmallow import ( - EXCLUDE, - Schema, ValidationError, fields, + post_dump, post_load, pre_load, validate, ) -from typing_extensions import Self from .config import ( DEFAULT_PRE_COMMIT_MESSAGE, @@ -26,60 +24,15 @@ DOCUMENT_SIZE_THRESHOLD_BYTES, MULTI_DOCUMENT_LIMIT, ) - - -class ToDictMixin: - """ - Provides a type-safe `to_dict()` method for classes using Marshmallow - """ - - SCHEMA: ClassVar[Schema] - - def to_dict(self) -> Dict[str, Any]: - return cast(Dict[str, Any], self.SCHEMA.dump(self)) - - -class FromDictMixin: - """This class must be used as an additional base class for all classes whose schema - implements a `post_load` function turning the received dict into a class instance. - - It makes it possible to deserialize an object using `MyClass.from_dict(dct)` instead - of `MyClass.SCHEMA.load(dct)`. The `from_dict()` method is shorter, but more - importantly, type-safe: its return type is an instance of `MyClass`, not - `list[Any] | Any`. - - Reference: https://marshmallow.readthedocs.io/en/stable/quickstart.html#deserializing-to-objects E501 - """ - - SCHEMA: ClassVar[Schema] - - @classmethod - def from_dict(cls, dct: Dict[str, Any]) -> Self: - return cast(Self, cls.SCHEMA.load(dct)) - - -class BaseSchema(Schema): - class Meta: - ordered = True - unknown = EXCLUDE - - -class Base(ToDictMixin): - def __init__(self, status_code: Optional[int] = None) -> None: - self.status_code = status_code - - def to_json(self) -> str: - """ - to_json converts model to JSON string. - """ - return cast(str, self.SCHEMA.dumps(self)) - - @property - def success(self) -> bool: - return self.__bool__() - - def __bool__(self) -> bool: - return self.status_code == 200 +from .models_utils import ( + Base, + BaseSchema, + FromDictMixin, + FromDictWithBase, + PaginationParameter, + SearchParameter, + ToDictMixin, +) class DocumentSchema(BaseSchema): @@ -161,7 +114,7 @@ def make_detail_response(self, data: Dict[str, Any], **kwargs: Any) -> "Detail": return Detail(**data) -class Detail(Base, FromDictMixin): +class Detail(FromDictWithBase): """Detail is a response object mostly returned on error or when the api output is a simple string. @@ -194,7 +147,7 @@ def make_match(self, data: Dict[str, Any], **kwargs: Any) -> "Match": return Match(**data) -class Match(Base, FromDictMixin): +class Match(FromDictWithBase): """ Match describes an issue found by GitGuardian. @@ -279,7 +232,7 @@ def make_policy_break(self, data: Dict[str, Any], **kwargs: Any) -> "PolicyBreak return PolicyBreak(**data) -class PolicyBreak(Base, FromDictMixin): +class PolicyBreak(FromDictWithBase): """ PolicyBreak describes a GitGuardian policy break found in a scan. @@ -336,7 +289,7 @@ def make_scan_result(self, data: Dict[str, Any], **kwargs: Any) -> "ScanResult": return ScanResult(**data) -class ScanResult(Base, FromDictMixin): +class ScanResult(FromDictWithBase): """ScanResult is a response object returned on a Content Scan Attributes: @@ -426,7 +379,7 @@ def make_scan_result( return MultiScanResult(**data) -class MultiScanResult(Base, FromDictMixin): +class MultiScanResult(FromDictWithBase): """ScanResult is a response object returned on a Content Scan Attributes: @@ -966,24 +919,69 @@ class Feedback(Base, FromDictMixin): @dataclass -class Source(Base, FromDictMixin): +class SecretIncidentStats(Base, FromDictMixin): + total: int + severity_breakdown: Dict[Severity, int] + + +@dataclass +class SecretIncidentsBreakdown(Base, FromDictMixin): + open_secret_incidents: SecretIncidentStats + closed_secret_incidents: SecretIncidentStats + + +ScanStatus = Literal[ + "pending", + "running", + "canceled", + "failed", + "too_large", + "timeout", + "pending_timeout", + "finished", +] + + +@dataclass +class Scan(Base, FromDictMixin): + date: datetime + status: ScanStatus + failing_reason: str + commits_scanned: int + branches_scanned: int + duration: str + + +SourceHealth = Literal["safe", "unknown", "at_risk"] +SourceCriticality = Literal["critical", "high", "medium", "low", "unknown"] + + +@dataclass +class Source(FromDictWithBase): id: int url: str type: str full_name: str - health: Literal["safe", "unknown", "at_risk"] + health: SourceHealth default_branch: Optional[str] default_branch_head: Optional[str] open_incidents_count: int closed_incidents_count: int - secret_incidents_breakdown: Dict[str, Any] # TODO: add SecretIncidentsBreakdown + secret_incidents_breakdown: SecretIncidentsBreakdown visibility: Visibility external_id: str - source_criticality: str - last_scan: Optional[Dict[str, Any]] # TODO: add LastScan + source_criticality: SourceCriticality + last_scan: Optional[Scan] monitored: bool +SourceSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema(Source, base_schema=BaseSchema), +) +Source.SCHEMA = SourceSchema() + + @dataclass class OccurrenceMatch(Base, FromDictMixin): """ @@ -1077,3 +1075,470 @@ def __repr__(self) -> str: marshmallow_dataclass.class_schema(SecretIncident, base_schema=BaseSchema), ) SecretIncident.SCHEMA = SecretIncidentSchema() + + +class AccessLevel(str, Enum): + OWNER = "owner" + MANAGER = "manager" + MEMBER = "member" + RESTRICTED = "restricted" + + +@dataclass +class MembersParameters(PaginationParameter, SearchParameter, ToDictMixin): + """ + Members query parameters + """ + + access_level: Optional[AccessLevel] = None + active: Optional[bool] = None + ordering: Optional[ + Literal["id", "-id", "created_at", "-created_at", "last_login", "-last_login"] + ] = None + + +class MembersParametersSchema(BaseSchema): + access_level = fields.Enum(AccessLevel, by_value=True, allow_none=True) + active = fields.Bool(allow_none=True) + ordering = fields.Str(allow_none=True) + + @post_load + def make_members_parameters(self, data: Dict[str, Any], **kwargs: Any): + return MembersParameters(**data) + + +MembersParameters.SCHEMA = MembersParametersSchema() + + +@dataclass +class Member(FromDictWithBase): + """ + Member represents a user in a GitGuardian account. + """ + + id: int + access_level: AccessLevel + email: str + name: str + created_at: datetime + last_login: Optional[datetime] + active: bool + + +class MemberSchema(BaseSchema): + """ + This schema cannot be done through marshmallow_dataclass as we want to use the + values of the AccessLevel enum to create the enum field + """ + + id = fields.Int(required=True) + access_level = fields.Enum(AccessLevel, by_value=True, required=True) + email = fields.Str(required=True) + name = fields.Str(required=True) + created_at = fields.AwareDateTime(required=True) + last_login = fields.AwareDateTime(allow_none=True) + active = fields.Bool(required=True) + + @post_load + def return_member( + self, + data: Dict[str, Any], + **kwargs: Any, + ): + return Member(**data) + + +Member.SCHEMA = MemberSchema() + + +class UpdateMemberSchema(BaseSchema): + """ + This schema cannot be done through marshmallow_dataclass as we want to use the + values of the AccessLevel enum to create the enum field + """ + + id = fields.Int(required=True) + access_level = fields.Enum(AccessLevel, by_value=True, allow_none=True) + active = fields.Bool(allow_none=True) + + @post_dump + def access_level_value(self, data: Dict[str, Any], **kwargs: Any) -> Dict[str, Any]: + if "access_level" in data: + data["access_level"] = AccessLevel(data["access_level"]).value + return data + + +@dataclass +class UpdateMember(FromDictWithBase): + """ + UpdateMember represents the payload to update a member + """ + + id: int + access_level: Optional[AccessLevel] = None + active: Optional[bool] = None + + +UpdateMember.SCHEMA = UpdateMemberSchema() + + +@dataclass +class UpdateMemberParameters(FromDictWithBase): + send_email: Optional[bool] = None + + +UpdateMemberParametersSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema(UpdateMemberParameters, base_schema=BaseSchema), +) +UpdateMemberParameters.SCHEMA = UpdateMemberParametersSchema() + + +@dataclass +class DeleteMemberParameters(FromDictWithBase): + id: int + send_email: Optional[bool] = None + + +DeleteMemberParametersSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema(DeleteMemberParameters, base_schema=BaseSchema), +) +DeleteMemberParameters.SCHEMA = DeleteMemberParametersSchema() + + +@dataclass +class TeamsParameters(PaginationParameter, SearchParameter, FromDictMixin, ToDictMixin): + is_global: Optional[bool] = None + + +TeamsParameterSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema(TeamsParameters, base_schema=BaseSchema), +) +TeamsParameters.SCHEMA = TeamsParameterSchema() + + +@dataclass +class Team(FromDictWithBase): + id: int + name: str + is_global: bool + gitguardian_url: str + description: Optional[str] = None + + +TeamsSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema(Team, base_schema=BaseSchema), +) +Team.SCHEMA = TeamsSchema() + + +@dataclass +class CreateTeam(FromDictWithBase): + name: str + description: Optional[str] = "" + + +CreateTeamSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema(CreateTeam, base_schema=BaseSchema), +) + + +CreateTeam.SCHEMA = CreateTeamSchema() + + +@dataclass +class UpdateTeam(FromDictWithBase): + id: int + name: Optional[str] + description: Optional[str] = None + + +UpdateTeamSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema(UpdateTeam, base_schema=BaseSchema), +) +UpdateTeam.SCHEMA = UpdateTeamSchema() + + +class TeamPermission(str, Enum): + MANAGER = "can_manage" + MEMBER = "cannot_manage" + + +class IncidentPermission(str, Enum): + EDIT = "can_edit" + VIEW = "can_view" + FULL_ACCESS = "full_access" + + +@dataclass +class TeamInvitationParameters(PaginationParameter, ToDictMixin): + invitation_id: Optional[int] = None + is_team_leader: Optional[bool] = None + incident_permission: Optional[IncidentPermission] = None + + +class TeamInvitationParameterSchema(BaseSchema): + invitation_id = fields.Int(allow_none=True) + is_team_leader = fields.Bool(allow_none=True) + incident_permission = fields.Enum( + IncidentPermission, by_value=True, allow_none=True + ) + + class Meta: + exclude_none = True + + +TeamInvitationParameters.SCHEMA = TeamInvitationParameterSchema() + + +@dataclass +class TeamInvitation(FromDictWithBase): + id: int + invitation_id: int + team_id: int + team_permission: TeamPermission + incident_permission: IncidentPermission + + +class TeamInvitationSchema(BaseSchema): + many = False + + id = fields.Int(required=True) + invitation_id = fields.Int(required=True) + team_id = fields.Int(required=True) + team_permission = fields.Enum(TeamPermission, by_value=True, required=True) + incident_permission = fields.Enum(IncidentPermission, by_value=True, required=True) + + @post_load + def make_team_invitation( + self, + data: Dict[str, Any], + **kwargs: Any, + ): + return TeamInvitation(**data) + + +TeamInvitation.SCHEMA = TeamInvitationSchema() + + +@dataclass +class CreateTeamInvitation(FromDictWithBase): + invitation_id: int + is_team_leader: bool + incident_permission: IncidentPermission + + +class CreateTeamInvitationSchema(BaseSchema): + many = False + + invitation_id = fields.Int(required=True) + is_team_leader = fields.Bool(required=True) + incident_permission = fields.Enum(IncidentPermission, by_value=True, required=True) + + @post_load + def make_team_invitation(self, data: Dict[str, Any], **kwargs: Any): + return CreateTeamInvitation(**data) + + class Meta: + exclude_none = True + + +CreateTeamInvitation.SCHEMA = CreateTeamInvitationSchema() + + +@dataclass +class TeamMemberParameters(PaginationParameter, SearchParameter, ToDictMixin): + is_team_leader: Optional[bool] = None + incident_permission: Optional[IncidentPermission] = None + member_id: Optional[int] = None + + +class TeamMembershipParameterSchema(BaseSchema): + is_team_leader = fields.Bool(allow_none=True) + incident_permission = fields.Enum( + IncidentPermission, by_value=True, allow_none=True + ) + member_id = fields.Int(allow_none=True) + + @post_load + def make_team_member_parameter(self, data: Dict[str, Any], **kwargs: Any): + return TeamMemberParameters(**data) + + class Meta: + exclude_none = True + + +TeamMemberParameters.SCHEMA = TeamMembershipParameterSchema() + + +@dataclass +class TeamMember(FromDictWithBase): + id: int + team_id: int + member_id: int + is_team_leader: bool + team_permission: TeamPermission + incident_permission: IncidentPermission + + +class TeamMemberSchema(BaseSchema): + id = fields.Int(required=True) + team_id = fields.Int(required=True) + member_id = fields.Int(required=True) + is_team_leader = fields.Bool(required=True) + team_permission = fields.Enum(TeamPermission, by_value=True, required=True) + incident_permission = fields.Enum(IncidentPermission, by_value=True, required=True) + + @post_load + def make_team_member(self, data: Dict[str, Any], **kwargs: Any): + return TeamMember(**data) + + +TeamMember.SCHEMA = TeamMemberSchema() + + +@dataclass +class CreateTeamMemberParameters(ToDictMixin): + send_email: Optional[bool] = None + + +CreateTeamMemberParameterSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema( + CreateTeamMemberParameters, base_schema=BaseSchema + ), +) +CreateTeamMemberParameters.SCHEMA = CreateTeamMemberParameterSchema() + + +@dataclass +class CreateTeamMember(FromDictWithBase): + member_id: int + is_team_leader: bool + incident_permission: IncidentPermission + + +class CreateTeamMemberSchema(BaseSchema): + many = False + + member_id = fields.Int(required=True) + is_team_leader = fields.Bool(required=True) + incident_permission = fields.Enum(IncidentPermission, by_value=True, required=True) + + @post_load + def make_create_team_member(self, data: Dict[str, Any], **kwargs: Any): + return CreateTeamMember(**data) + + +CreateTeamMember.SCHEMA = CreateTeamMemberSchema() + + +@dataclass +class TeamSourceParameters(PaginationParameter, SearchParameter, ToDictMixin): + last_scan_status: Optional[ScanStatus] = None + type: Optional[str] = None + health: Optional[SourceHealth] = None + type: Optional[str] = None + ordering: Optional[Literal["last_scan_date", "-last_scan_date"]] = None + visibility: Optional[Visibility] = None + external_id: Optional[str] = None + + +TeamSourceParametersSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema(TeamSourceParameters, base_schema=BaseSchema), +) +TeamSourceParameters.SCHEMA = TeamSourceParametersSchema() + + +@dataclass +class UpdateTeamSource(FromDictWithBase): + team_id: int + sources_to_add: List[int] + sources_to_remove: List[int] + + +UpdateTeamSourceSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema(UpdateTeamSource, base_schema=BaseSchema), +) +UpdateTeamSource.SCHEMA = UpdateTeamSourceSchema() + + +@dataclass +class SourceParameters(TeamSourceParameters): + source_criticality: Optional[SourceCriticality] = None + monitored: Optional[bool] = None + + +SourceParametersSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema(SourceParameters, base_schema=BaseSchema), +) +SourceParameters.SCHEMA = SourceParametersSchema() + + +@dataclass +class InvitationParameters( + PaginationParameter, SearchParameter, FromDictMixin, ToDictMixin +): + ordering: Optional[Literal["date", "-date"]] = None + + +@dataclass +class Invitation(FromDictWithBase): + id: int + email: str + access_level: AccessLevel + date: datetime + + +class InvitationSchema(BaseSchema): + id = fields.Int(required=True) + email = fields.Str(required=True) + access_level = fields.Enum(AccessLevel, by_value=True, required=True) + date = fields.DateTime(required=True) + + @post_load + def make_invitation(self, data: Dict[str, Any], **kwargs: Any): + return Invitation(**data) + + +Invitation.SCHEMA = InvitationSchema() + + +@dataclass +class CreateInvitationParameters(FromDictMixin, ToDictMixin): + send_email: Optional[bool] = None + + +CreateInvitationParameterSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema( + CreateInvitationParameters, base_schema=BaseSchema + ), +) +CreateInvitationParameters.SCHEMA = CreateInvitationParameterSchema() + + +@dataclass +class CreateInvitation(FromDictMixin, ToDictMixin): + email: str + access_level: AccessLevel + + +class CreateInvitationSchema(BaseSchema): + email = fields.Str(required=True) + access_level = fields.Enum(AccessLevel, by_value=True, required=True) + + @post_load + def make_create_invitation(self, data: Dict[str, Any], **kwargs: Any): + return CreateInvitation(**data) + + +CreateInvitation.SCHEMA = CreateInvitationSchema() diff --git a/pygitguardian/models_utils.py b/pygitguardian/models_utils.py new file mode 100644 index 00000000..91801669 --- /dev/null +++ b/pygitguardian/models_utils.py @@ -0,0 +1,139 @@ +# pyright: reportIncompatibleVariableOverride=false +# Disable this check because of multiple non-dangerous violations (SCHEMA variables, +# BaseSchema.Meta class) +from dataclasses import dataclass +from typing import ( + TYPE_CHECKING, + Any, + ClassVar, + Dict, + Generic, + List, + Optional, + Type, + TypeVar, + cast, +) + +import marshmallow_dataclass +from marshmallow import EXCLUDE, Schema +from typing_extensions import Self + + +if TYPE_CHECKING: + import requests + + +class ToDictMixin: + """ + Provides a type-safe `to_dict()` method for classes using Marshmallow + """ + + SCHEMA: ClassVar[Schema] + + def to_dict(self) -> Dict[str, Any]: + return cast(Dict[str, Any], self.SCHEMA.dump(self)) + + +class FromDictMixin: + """This class must be used as an additional base class for all classes whose schema + implements a `post_load` function turning the received dict into a class instance. + + It makes it possible to deserialize an object using `MyClass.from_dict(dct)` instead + of `MyClass.SCHEMA.load(dct)`. The `from_dict()` method is shorter, but more + importantly, type-safe: its return type is an instance of `MyClass`, not + `list[Any] | Any`. + + Reference: https://marshmallow.readthedocs.io/en/stable/quickstart.html#deserializing-to-objects E501 + """ + + SCHEMA: ClassVar[Schema] + + @classmethod + def from_dict(cls, dct: Dict[str, Any]) -> Self: + return cast(Self, cls.SCHEMA.load(dct)) + + +class BaseSchema(Schema): + class Meta: + ordered = True + unknown = EXCLUDE + + +class Base(ToDictMixin): + def __init__(self, status_code: Optional[int] = None) -> None: + self.status_code = status_code + + def to_json(self) -> str: + """ + to_json converts model to JSON string. + """ + return cast(str, self.SCHEMA.dumps(self)) + + @property + def success(self) -> bool: + return self.__bool__() + + def __bool__(self) -> bool: + return self.status_code == 200 + + +@dataclass +class PaginationParameter(ToDictMixin): + """Pagination mixin used for endpoints that support pagination.""" + + cursor: str = "" + per_page: int = 20 + + +PaginationParameterSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema(PaginationParameter, base_schema=BaseSchema), +) +PaginationParameter.SCHEMA = PaginationParameterSchema() + + +@dataclass +class SearchParameter(ToDictMixin): + search: Optional[str] = None + + +SearchParameterSchema = cast( + Type[BaseSchema], + marshmallow_dataclass.class_schema(SearchParameter, base_schema=BaseSchema), +) +SearchParameter.SCHEMA = SearchParameterSchema() + + +class FromDictWithBase(FromDictMixin, Base): + pass + + +PaginatedData = TypeVar("PaginatedData", bound=FromDictWithBase) + + +@dataclass +class CursorPaginatedResponse(Generic[PaginatedData]): + status_code: int + data: List[PaginatedData] + prev: Optional[str] = None + next: Optional[str] = None + + @classmethod + def from_response( + cls, response: "requests.Response", data_type: Type[PaginatedData] + ) -> "CursorPaginatedResponse[PaginatedData]": + data = cast( + List[PaginatedData], [data_type.from_dict(obj) for obj in response.json()] + ) + for element in data: + element.status_code = response.status_code + + paginated_response = cls(status_code=response.status_code, data=data) + + if previous_page := response.links.get("prev"): + paginated_response.prev = previous_page["url"] + if next_page := response.links.get("next"): + paginated_response.next = next_page["url"] + + return paginated_response diff --git a/tests/cassettes/test_add_team_sources.yaml b/tests/cassettes/test_add_team_sources.yaml new file mode 100644 index 00000000..77d45799 --- /dev/null +++ b/tests/cassettes/test_add_team_sources.yaml @@ -0,0 +1,253 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":19,"is_global":false,"name":"This is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '438' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:33 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 58fa21985f6a41e52817d0eace32cfc5 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/sources + response: + body: + string: '[{"id":124,"type":"azure_devops","full_name":"gg-integration-test / + gg-test / default_branch","health":"at_risk","source_criticality":"unknown","default_branch":"test","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346395Z","status":"finished","failing_reason":"","commits_scanned":23,"duration":"0.437131","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"05b69081-f346-4022-8784-198f50aed182","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/default_branch"},{"id":125,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / gg-test","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346418Z","status":"finished","failing_reason":"","commits_scanned":43,"duration":"0.492489","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"7655868e-bb15-47ab-bd62-abd97212e5e8","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/gg-test"},{"id":126,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / huge_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346442Z","status":"finished","failing_reason":"","commits_scanned":1007,"duration":"1.102781","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"455c4e0c-6dc3-48ce-a0e7-819d9a8d7523","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/huge_repo"},{"id":127,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / new_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346464Z","status":"finished","failing_reason":"","commits_scanned":1,"duration":"0.216094","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"f9b583fb-dcfd-46ec-8938-44b427d3e596","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/new_repo"},{"id":128,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / t e s t i n g","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":115,"closed_incidents_count":1,"last_scan":{"date":"2024-12-06T12:27:52.346299Z","status":"finished","failing_reason":"","commits_scanned":113,"duration":"7.597231","branches_scanned":2,"progress":100},"monitored":true,"visibility":"private","external_id":"8a132329-0c77-4efc-a18a-882bda6ab28b","secret_incidents_breakdown":{"open_secret_incidents":{"total":115,"severity_breakdown":{"critical":0,"high":1,"medium":0,"low":0,"info":0,"unknown":114}},"closed_secret_incidents":{"total":1,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":1}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/t%20e%20s%20t%20i%20n%20g"},{"id":129,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / test abc","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":28,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346367Z","status":"finished","failing_reason":"","commits_scanned":55,"duration":"0.623082","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"2aa16b64-2fb2-4639-afeb-70c0c4e8a267","secret_incidents_breakdown":{"open_secret_incidents":{"total":28,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":28}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/test%20abc"},{"id":130,"type":"jira_cloud","full_name":"yanne-gg-integration + / Mouse 804250","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10240","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/MOUS804250"},{"id":131,"type":"jira_cloud","full_name":"yanne-gg-integration + / Cheese 609495","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10241","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/CHEE609495"},{"id":132,"type":"jira_cloud","full_name":"yanne-gg-integration + / Fantastic Frozen Bacon 413763","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10242","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/FANT413763"},{"id":133,"type":"jira_cloud","full_name":"yanne-gg-integration + / Awesome Cotton Salad 575805","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10243","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/AWES575805"},{"id":134,"type":"jira_cloud","full_name":"yanne-gg-integration + / Incredible Steel Hat 449092","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10244","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/INCR449092"},{"id":135,"type":"jira_cloud","full_name":"yanne-gg-integration + / Shoes 435418","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10245","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/SHOE435418"},{"id":136,"type":"jira_cloud","full_name":"yanne-gg-integration + / Steel Ball 217001","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10246","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/STEE217001"},{"id":137,"type":"jira_cloud","full_name":"yanne-gg-integration + / Table 485589","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10247","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/TABL485589"},{"id":138,"type":"jira_cloud","full_name":"yanne-gg-integration + / Sleek Pizza 418589","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10248","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/SLEE418589"},{"id":139,"type":"jira_cloud","full_name":"yanne-gg-integration + / Pants 160878","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10249","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/PANT160878"},{"id":140,"type":"jira_cloud","full_name":"yanne-gg-integration + / Concrete Car 970994","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10250","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/CONC970994"},{"id":141,"type":"jira_cloud","full_name":"yanne-gg-integration + / Pants 965973","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10251","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/PANT965973"},{"id":142,"type":"jira_cloud","full_name":"yanne-gg-integration + / Wooden Ball 683600","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10252","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/WOOD683600"},{"id":143,"type":"jira_cloud","full_name":"yanne-gg-integration + / Incredible Steel Pizza 509982","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10253","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/INCR509982"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '14417' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:33 GMT + Link: + - ; rel="next" + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 25509b031d7733a791a772c11c7483b3 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: '{"sources_to_add": [124], "sources_to_remove": []}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '50' + Content-Type: + - application/json + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: POST + uri: https://api.gitguardian.com/v1/teams/19/sources + response: + body: + string: '' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '0' + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:33 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - 74153e49ee0a825ae6f02b0784058380 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 204 + message: No Content + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/19/sources?cursor=&per_page=20&type=azure_devops + response: + body: + string: '[{"id":124,"type":"azure_devops","full_name":"gg-integration-test / + gg-test / default_branch","health":"at_risk","source_criticality":"unknown","default_branch":"test","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346395Z","status":"finished","failing_reason":"","commits_scanned":23,"duration":"0.437131","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"05b69081-f346-4022-8784-198f50aed182","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/default_branch"},{"id":125,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / gg-test","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346418Z","status":"finished","failing_reason":"","commits_scanned":43,"duration":"0.492489","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"7655868e-bb15-47ab-bd62-abd97212e5e8","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/gg-test"},{"id":126,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / huge_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346442Z","status":"finished","failing_reason":"","commits_scanned":1007,"duration":"1.102781","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"455c4e0c-6dc3-48ce-a0e7-819d9a8d7523","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/huge_repo"},{"id":127,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / new_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346464Z","status":"finished","failing_reason":"","commits_scanned":1,"duration":"0.216094","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"f9b583fb-dcfd-46ec-8938-44b427d3e596","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/new_repo"},{"id":128,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / t e s t i n g","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":115,"closed_incidents_count":1,"last_scan":{"date":"2024-12-06T12:27:52.346299Z","status":"finished","failing_reason":"","commits_scanned":113,"duration":"7.597231","branches_scanned":2,"progress":100},"monitored":true,"visibility":"private","external_id":"8a132329-0c77-4efc-a18a-882bda6ab28b","secret_incidents_breakdown":{"open_secret_incidents":{"total":115,"severity_breakdown":{"critical":0,"high":1,"medium":0,"low":0,"info":0,"unknown":114}},"closed_secret_incidents":{"total":1,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":1}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/t%20e%20s%20t%20i%20n%20g"},{"id":129,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / test abc","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":28,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346367Z","status":"finished","failing_reason":"","commits_scanned":55,"duration":"0.623082","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"2aa16b64-2fb2-4639-afeb-70c0c4e8a267","secret_incidents_breakdown":{"open_secret_incidents":{"total":28,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":28}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/test%20abc"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '5158' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:34 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - d87ddc466ebff365d07d2748c378b7ad + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_create_team.yaml b/tests/cassettes/test_create_team.yaml new file mode 100644 index 00000000..d2fe7943 --- /dev/null +++ b/tests/cassettes/test_create_team.yaml @@ -0,0 +1,58 @@ +interactions: + - request: + body: '{"name": "PyGitGuardian team", "description": ""}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '49' + Content-Type: + - application/json + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: POST + uri: https://api.gitguardian.com/v1/teams + response: + body: + string: '{"id":21,"is_global":false,"name":"PyGitGuardian team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '149' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:31 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - 0d603848ed5811425fb5384e2c165bf6 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 201 + message: Created +version: 1 diff --git a/tests/cassettes/test_create_team_invitation.yaml b/tests/cassettes/test_create_team_invitation.yaml new file mode 100644 index 00000000..8270d3c1 --- /dev/null +++ b/tests/cassettes/test_create_team_invitation.yaml @@ -0,0 +1,173 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":19,"is_global":false,"name":"This is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '438' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - c2b0a05b57e590cfaf8f9fe2221eda91 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/invitations + response: + body: + string: '[{"id":13,"date":"2024-12-12T16:53:59.247129Z","email":"pygitguardian@example.com","role":"member","access_level":"member"},{"id":14,"date":"2024-12-12T16:54:44.192249Z","email":"example@test.com","role":"member","access_level":"member"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '238' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - df09bccf6aea67a2215c5dd905f6033e + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: '{"invitation_id": 13, "is_team_leader": true, "incident_permission": "can_view"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '80' + Content-Type: + - application/json + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: POST + uri: https://api.gitguardian.com/v1/teams/19/team_invitations + response: + body: + string: '{"id":7,"team_id":19,"invitation_id":13,"is_team_leader":true,"team_permission":"can_manage","incident_permission":"can_view"}' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '126' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - 9ce9f089220243eca87b41bc81b72b73 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 201 + message: Created +version: 1 diff --git a/tests/cassettes/test_create_team_member.yaml b/tests/cassettes/test_create_team_member.yaml new file mode 100644 index 00000000..7b7d16a3 --- /dev/null +++ b/tests/cassettes/test_create_team_member.yaml @@ -0,0 +1,232 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/members + response: + body: + string: + '[{"id":6,"role":"owner","access_level":"owner","email":"toto@gg.com","name":"toto + tata","created_at":"2019-07-15T12:14:14.245000Z","last_login":"2024-12-03T09:29:43.181169Z","active":true},{"id":17,"role":"member","access_level":"member","email":"henri.delateamsecretetducorealerting@gg.com","name":"Henri + De la team secret et du core alerting","created_at":"2024-12-12T17:07:32.636754Z","last_login":null,"active":true}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '421' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 17:08:38 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - cfa0a8bd5e7f0c1617fca33915d7861d + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":19,"is_global":false,"name":"This is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '438' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 17:08:38 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 637b279a05f908b2b5fcfbf2c724113e + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/19/team_memberships + response: + body: + string: '[{"id":23,"team_id":19,"member_id":6,"is_team_leader":true,"team_permission":"can_manage","incident_permission":"full_access"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '127' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 17:08:38 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 128a5c3219e7182c88b624aa99dab334 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: '{"member_id": 17, "is_team_leader": false, "incident_permission": "can_view"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '77' + Content-Type: + - application/json + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: POST + uri: https://api.gitguardian.com/v1/teams/19/team_memberships + response: + body: + string: '{"id":28,"team_id":19,"member_id":17,"is_team_leader":false,"team_permission":"cannot_manage","incident_permission":"can_view"}' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '127' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 17:08:39 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - a55f190008b2b60e632a4e5917daec4f + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 201 + message: Created +version: 1 diff --git a/tests/cassettes/test_create_team_member_parameters.yaml b/tests/cassettes/test_create_team_member_parameters.yaml new file mode 100644 index 00000000..3a1c7983 --- /dev/null +++ b/tests/cassettes/test_create_team_member_parameters.yaml @@ -0,0 +1,232 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/members + response: + body: + string: + '[{"id":6,"role":"owner","access_level":"owner","email":"toto@gg.com","name":"toto + tata","created_at":"2019-07-15T12:14:14.245000Z","last_login":"2024-12-03T09:29:43.181169Z","active":true},{"id":17,"role":"member","access_level":"member","email":"henri.delateamsecretetducorealerting@gg.com","name":"Henri + De la team secret et du core alerting","created_at":"2024-12-12T17:07:32.636754Z","last_login":null,"active":true}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '421' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 17:10:56 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 606d6020da9ea30a7136283f62e2821e + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":19,"is_global":false,"name":"This is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '438' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 17:10:56 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - a19294f8bdcc49eb16a2f7156c6e77cf + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/19/team_memberships + response: + body: + string: '[{"id":23,"team_id":19,"member_id":6,"is_team_leader":true,"team_permission":"can_manage","incident_permission":"full_access"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '127' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 17:10:56 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 030f95424eca92846765743b6e814339 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: '{"member_id": 17, "is_team_leader": false, "incident_permission": "can_view"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '77' + Content-Type: + - application/json + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: POST + uri: https://api.gitguardian.com/v1/teams/19/team_memberships?send_email=False + response: + body: + string: '{"id":29,"team_id":19,"member_id":17,"is_team_leader":false,"team_permission":"cannot_manage","incident_permission":"can_view"}' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '127' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 17:10:56 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - aeeedf21c851edd2d8796d2c20e06d00 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 201 + message: Created +version: 1 diff --git a/tests/cassettes/test_delete_invitation.yaml b/tests/cassettes/test_delete_invitation.yaml new file mode 100644 index 00000000..b0b6e535 --- /dev/null +++ b/tests/cassettes/test_delete_invitation.yaml @@ -0,0 +1,110 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/invitations + response: + body: + string: '[{"id":13,"date":"2024-12-12T16:53:59.247129Z","email":"pygitguardian@example.com","role":"member","access_level":"member"},{"id":14,"date":"2024-12-12T16:54:44.192249Z","email":"example@test.com","role":"member","access_level":"member"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '238' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:34 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 308ee83bf3b6d61edde508ea06430075 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: DELETE + uri: https://api.gitguardian.com/v1/invitations/13 + response: + body: + string: '' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - DELETE, OPTIONS + Connection: + - keep-alive + Content-Length: + - '0' + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:34 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - f5eb9d8b1d00b83701a8d41c427be20e + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 204 + message: No Content +version: 1 diff --git a/tests/cassettes/test_delete_member.yaml b/tests/cassettes/test_delete_member.yaml new file mode 100644 index 00000000..b755a070 --- /dev/null +++ b/tests/cassettes/test_delete_member.yaml @@ -0,0 +1,112 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/members?access_level=member + response: + body: + string: + '[{"id":16,"role":"member","access_level":"member","email":"henri.delateamsecretetducorealerting@gg.com","name":"Henri + De la team secret et du core alerting","created_at":"2024-12-12T16:54:17.568327Z","last_login":null,"active":true}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '233' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:31 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - c56b4315637313b3d5eb7e70f9439a2c + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: DELETE + uri: https://api.gitguardian.com/v1/members/16 + response: + body: + string: '' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, PUT, PATCH, DELETE, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '0' + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:31 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - 2b154a78777604a093040ddbcdc76217 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 204 + message: No Content +version: 1 diff --git a/tests/cassettes/test_delete_team.yaml b/tests/cassettes/test_delete_team.yaml new file mode 100644 index 00000000..2a393f42 --- /dev/null +++ b/tests/cassettes/test_delete_team.yaml @@ -0,0 +1,115 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":18,"is_global":false,"name":"New PyGitGuardian team","description":"New + description","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/18"},{"id":19,"is_global":false,"name":"This + is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '607' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 2432b25fd8aa06e87453615a35e60888 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: DELETE + uri: https://api.gitguardian.com/v1/teams/18 + response: + body: + string: '' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, PUT, PATCH, DELETE, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '0' + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - 0e6123e4cdb31c0088a084e7a394c62b + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 204 + message: No Content +version: 1 diff --git a/tests/cassettes/test_delete_team_invitation.yaml b/tests/cassettes/test_delete_team_invitation.yaml new file mode 100644 index 00000000..e214696c --- /dev/null +++ b/tests/cassettes/test_delete_team_invitation.yaml @@ -0,0 +1,169 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":19,"is_global":false,"name":"This is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '438' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 69f3e07f70508d42fcda772509e25c8d + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/19/team_invitations + response: + body: + string: '[{"id":7,"team_id":19,"invitation_id":13,"is_team_leader":true,"team_permission":"can_manage","incident_permission":"can_view"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '128' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - ab97c68068c701932313f58691187827 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: DELETE + uri: https://api.gitguardian.com/v1/teams/19/team_invitations/7 + response: + body: + string: '' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - PUT, PATCH, DELETE, OPTIONS + Connection: + - keep-alive + Content-Length: + - '0' + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - 6328f39a8af957cbdfe428bf3becc33c + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 204 + message: No Content +version: 1 diff --git a/tests/cassettes/test_delete_team_member.yaml b/tests/cassettes/test_delete_team_member.yaml new file mode 100644 index 00000000..f6572fc6 --- /dev/null +++ b/tests/cassettes/test_delete_team_member.yaml @@ -0,0 +1,228 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/members + response: + body: + string: + '[{"id":6,"role":"owner","access_level":"owner","email":"toto@gg.com","name":"toto + tata","created_at":"2019-07-15T12:14:14.245000Z","last_login":"2024-12-03T09:29:43.181169Z","active":true},{"id":17,"role":"member","access_level":"member","email":"henri.delateamsecretetducorealerting@gg.com","name":"Henri + De la team secret et du core alerting","created_at":"2024-12-12T17:07:32.636754Z","last_login":null,"active":false}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '422' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Fri, 13 Dec 2024 10:30:11 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 9277bcebb2641d61bcd6564b9ee1a58b + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":19,"is_global":false,"name":"This is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '438' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Fri, 13 Dec 2024 10:30:11 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 5392863a13081e0dbe0e8f45ccce6ee6 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/19/team_memberships?is_team_leader=False + response: + body: + string: '[{"id":29,"team_id":19,"member_id":17,"is_team_leader":false,"team_permission":"cannot_manage","incident_permission":"can_view"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '129' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Fri, 13 Dec 2024 10:30:11 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - d01a1248a4b24bab5e57d86a128dead0 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: DELETE + uri: https://api.gitguardian.com/v1/teams/19/team_memberships/29 + response: + body: + string: '' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - PUT, PATCH, DELETE, OPTIONS + Connection: + - keep-alive + Content-Length: + - '0' + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Fri, 13 Dec 2024 10:30:11 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - a78ae43018e7c332f1e29eda0524d5de + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 204 + message: No Content +version: 1 diff --git a/tests/cassettes/test_delete_team_sources.yaml b/tests/cassettes/test_delete_team_sources.yaml new file mode 100644 index 00000000..ef92f861 --- /dev/null +++ b/tests/cassettes/test_delete_team_sources.yaml @@ -0,0 +1,238 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":19,"is_global":false,"name":"This is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '438' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:33 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - ea889edda539c08bb70d35eb8a240e7b + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/19/sources + response: + body: + string: '[{"id":124,"type":"azure_devops","full_name":"gg-integration-test / + gg-test / default_branch","health":"at_risk","source_criticality":"unknown","default_branch":"test","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346395Z","status":"finished","failing_reason":"","commits_scanned":23,"duration":"0.437131","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"05b69081-f346-4022-8784-198f50aed182","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/default_branch"},{"id":125,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / gg-test","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346418Z","status":"finished","failing_reason":"","commits_scanned":43,"duration":"0.492489","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"7655868e-bb15-47ab-bd62-abd97212e5e8","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/gg-test"},{"id":126,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / huge_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346442Z","status":"finished","failing_reason":"","commits_scanned":1007,"duration":"1.102781","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"455c4e0c-6dc3-48ce-a0e7-819d9a8d7523","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/huge_repo"},{"id":127,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / new_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346464Z","status":"finished","failing_reason":"","commits_scanned":1,"duration":"0.216094","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"f9b583fb-dcfd-46ec-8938-44b427d3e596","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/new_repo"},{"id":128,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / t e s t i n g","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":115,"closed_incidents_count":1,"last_scan":{"date":"2024-12-06T12:27:52.346299Z","status":"finished","failing_reason":"","commits_scanned":113,"duration":"7.597231","branches_scanned":2,"progress":100},"monitored":true,"visibility":"private","external_id":"8a132329-0c77-4efc-a18a-882bda6ab28b","secret_incidents_breakdown":{"open_secret_incidents":{"total":115,"severity_breakdown":{"critical":0,"high":1,"medium":0,"low":0,"info":0,"unknown":114}},"closed_secret_incidents":{"total":1,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":1}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/t%20e%20s%20t%20i%20n%20g"},{"id":129,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / test abc","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":28,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346367Z","status":"finished","failing_reason":"","commits_scanned":55,"duration":"0.623082","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"2aa16b64-2fb2-4639-afeb-70c0c4e8a267","secret_incidents_breakdown":{"open_secret_incidents":{"total":28,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":28}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/test%20abc"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '5158' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:33 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - cb0318be8ec3c9abb3c2decd4a2fe968 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: '{"sources_to_add": [], "sources_to_remove": [124]}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '50' + Content-Type: + - application/json + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: POST + uri: https://api.gitguardian.com/v1/teams/19/sources + response: + body: + string: '' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '0' + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:33 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - 33fae4e257cf966d4a84e57269819a3e + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 204 + message: No Content + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/19/sources + response: + body: + string: '[{"id":125,"type":"azure_devops","full_name":"gg-integration-test / + gg-test / gg-test","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346418Z","status":"finished","failing_reason":"","commits_scanned":43,"duration":"0.492489","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"7655868e-bb15-47ab-bd62-abd97212e5e8","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/gg-test"},{"id":126,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / huge_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346442Z","status":"finished","failing_reason":"","commits_scanned":1007,"duration":"1.102781","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"455c4e0c-6dc3-48ce-a0e7-819d9a8d7523","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/huge_repo"},{"id":127,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / new_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346464Z","status":"finished","failing_reason":"","commits_scanned":1,"duration":"0.216094","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"f9b583fb-dcfd-46ec-8938-44b427d3e596","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/new_repo"},{"id":128,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / t e s t i n g","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":115,"closed_incidents_count":1,"last_scan":{"date":"2024-12-06T12:27:52.346299Z","status":"finished","failing_reason":"","commits_scanned":113,"duration":"7.597231","branches_scanned":2,"progress":100},"monitored":true,"visibility":"private","external_id":"8a132329-0c77-4efc-a18a-882bda6ab28b","secret_incidents_breakdown":{"open_secret_incidents":{"total":115,"severity_breakdown":{"critical":0,"high":1,"medium":0,"low":0,"info":0,"unknown":114}},"closed_secret_incidents":{"total":1,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":1}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/t%20e%20s%20t%20i%20n%20g"},{"id":129,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / test abc","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":28,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346367Z","status":"finished","failing_reason":"","commits_scanned":55,"duration":"0.623082","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"2aa16b64-2fb2-4639-afeb-70c0c4e8a267","secret_incidents_breakdown":{"open_secret_incidents":{"total":28,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":28}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/test%20abc"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '4292' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:33 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - baa5cf1b0ac0cfac6d146990dbd457a3 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_get_team.yaml b/tests/cassettes/test_get_team.yaml new file mode 100644 index 00000000..9b400308 --- /dev/null +++ b/tests/cassettes/test_get_team.yaml @@ -0,0 +1,114 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":18,"is_global":false,"name":"Another test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/18"},{"id":19,"is_global":false,"name":"This + is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '582' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:31 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 46866d5aa60198b84fb637afe392cc0c + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/18 + response: + body: + string: '{"id":18,"is_global":false,"name":"Another test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/18"}' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, PUT, PATCH, DELETE, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '143' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:31 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - a8a5dd77ff9c16177901fbed8e52f67e + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_global_team.yaml b/tests/cassettes/test_global_team.yaml new file mode 100644 index 00000000..b4b1f221 --- /dev/null +++ b/tests/cassettes/test_global_team.yaml @@ -0,0 +1,58 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=True + response: + body: + string: '[{"id":6,"is_global":true,"name":"All incidents","description":null,"gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/6"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '145' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - e6bf3c868e36b66e05d2830d35d66b6f + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_list_invitations.yaml b/tests/cassettes/test_list_invitations.yaml new file mode 100644 index 00000000..520bd4d3 --- /dev/null +++ b/tests/cassettes/test_list_invitations.yaml @@ -0,0 +1,58 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/invitations + response: + body: + string: '[{"id":13,"date":"2024-12-12T16:53:59.247129Z","email":"pygitguardian@example.com","role":"member","access_level":"member"},{"id":14,"date":"2024-12-12T16:54:44.192249Z","email":"example@test.com","role":"member","access_level":"member"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '238' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:34 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - f762eb19d4027c92b2a3bce21967a08e + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_list_members.yaml b/tests/cassettes/test_list_members.yaml new file mode 100644 index 00000000..1f6f2309 --- /dev/null +++ b/tests/cassettes/test_list_members.yaml @@ -0,0 +1,61 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/members + response: + body: + string: + '[{"id":6,"role":"owner","access_level":"owner","email":"toto@gg.com","name":"toto + tata","created_at":"2019-07-15T12:14:14.245000Z","last_login":"2024-12-03T09:29:43.181169Z","active":true},{"id":16,"role":"member","access_level":"member","email":"henri.delateamsecretetducorealerting@gg.com","name":"Henri + De la team secret et du core alerting","created_at":"2024-12-12T16:54:17.568327Z","last_login":null,"active":true}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '421' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:31 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - fd213ee6c290b4ccfd8d3dc720260cc7 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_list_members_parameters.yaml b/tests/cassettes/test_list_members_parameters.yaml new file mode 100644 index 00000000..6930726f --- /dev/null +++ b/tests/cassettes/test_list_members_parameters.yaml @@ -0,0 +1,58 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/members?access_level=manager + response: + body: + string: '[]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '2' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:31 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 7bd603369ef14f95121ce24ee32fc754 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_list_sources.yaml b/tests/cassettes/test_list_sources.yaml new file mode 100644 index 00000000..42ff9934 --- /dev/null +++ b/tests/cassettes/test_list_sources.yaml @@ -0,0 +1,78 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/sources + response: + body: + string: '[{"id":124,"type":"azure_devops","full_name":"gg-integration-test / + gg-test / default_branch","health":"at_risk","source_criticality":"unknown","default_branch":"test","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346395Z","status":"finished","failing_reason":"","commits_scanned":23,"duration":"0.437131","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"05b69081-f346-4022-8784-198f50aed182","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/default_branch"},{"id":125,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / gg-test","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346418Z","status":"finished","failing_reason":"","commits_scanned":43,"duration":"0.492489","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"7655868e-bb15-47ab-bd62-abd97212e5e8","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/gg-test"},{"id":126,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / huge_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346442Z","status":"finished","failing_reason":"","commits_scanned":1007,"duration":"1.102781","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"455c4e0c-6dc3-48ce-a0e7-819d9a8d7523","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/huge_repo"},{"id":127,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / new_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346464Z","status":"finished","failing_reason":"","commits_scanned":1,"duration":"0.216094","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"f9b583fb-dcfd-46ec-8938-44b427d3e596","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/new_repo"},{"id":128,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / t e s t i n g","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":115,"closed_incidents_count":1,"last_scan":{"date":"2024-12-06T12:27:52.346299Z","status":"finished","failing_reason":"","commits_scanned":113,"duration":"7.597231","branches_scanned":2,"progress":100},"monitored":true,"visibility":"private","external_id":"8a132329-0c77-4efc-a18a-882bda6ab28b","secret_incidents_breakdown":{"open_secret_incidents":{"total":115,"severity_breakdown":{"critical":0,"high":1,"medium":0,"low":0,"info":0,"unknown":114}},"closed_secret_incidents":{"total":1,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":1}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/t%20e%20s%20t%20i%20n%20g"},{"id":129,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / test abc","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":28,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346367Z","status":"finished","failing_reason":"","commits_scanned":55,"duration":"0.623082","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"2aa16b64-2fb2-4639-afeb-70c0c4e8a267","secret_incidents_breakdown":{"open_secret_incidents":{"total":28,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":28}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/test%20abc"},{"id":130,"type":"jira_cloud","full_name":"yanne-gg-integration + / Mouse 804250","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10240","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/MOUS804250"},{"id":131,"type":"jira_cloud","full_name":"yanne-gg-integration + / Cheese 609495","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10241","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/CHEE609495"},{"id":132,"type":"jira_cloud","full_name":"yanne-gg-integration + / Fantastic Frozen Bacon 413763","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10242","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/FANT413763"},{"id":133,"type":"jira_cloud","full_name":"yanne-gg-integration + / Awesome Cotton Salad 575805","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10243","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/AWES575805"},{"id":134,"type":"jira_cloud","full_name":"yanne-gg-integration + / Incredible Steel Hat 449092","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10244","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/INCR449092"},{"id":135,"type":"jira_cloud","full_name":"yanne-gg-integration + / Shoes 435418","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10245","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/SHOE435418"},{"id":136,"type":"jira_cloud","full_name":"yanne-gg-integration + / Steel Ball 217001","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10246","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/STEE217001"},{"id":137,"type":"jira_cloud","full_name":"yanne-gg-integration + / Table 485589","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10247","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/TABL485589"},{"id":138,"type":"jira_cloud","full_name":"yanne-gg-integration + / Sleek Pizza 418589","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10248","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/SLEE418589"},{"id":139,"type":"jira_cloud","full_name":"yanne-gg-integration + / Pants 160878","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10249","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/PANT160878"},{"id":140,"type":"jira_cloud","full_name":"yanne-gg-integration + / Concrete Car 970994","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10250","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/CONC970994"},{"id":141,"type":"jira_cloud","full_name":"yanne-gg-integration + / Pants 965973","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10251","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/PANT965973"},{"id":142,"type":"jira_cloud","full_name":"yanne-gg-integration + / Wooden Ball 683600","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10252","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/WOOD683600"},{"id":143,"type":"jira_cloud","full_name":"yanne-gg-integration + / Incredible Steel Pizza 509982","health":"unknown","source_criticality":"unknown","default_branch":null,"default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":null,"monitored":true,"visibility":"private","external_id":"10253","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://yanne-gg-integration.atlassian.net/browse/INCR509982"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '14417' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:33 GMT + Link: + - ; rel="next" + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - bad417e5cb8390018bf5c37c5d5c317f + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_list_team_invitations.yaml b/tests/cassettes/test_list_team_invitations.yaml new file mode 100644 index 00000000..56598f84 --- /dev/null +++ b/tests/cassettes/test_list_team_invitations.yaml @@ -0,0 +1,117 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":19,"is_global":false,"name":"This is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '438' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 4ce50e7f32dc7b99902b4d9788e2863b + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/19/team_invitations + response: + body: + string: '[{"id":7,"team_id":19,"invitation_id":13,"is_team_leader":true,"team_permission":"can_manage","incident_permission":"can_view"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '128' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 7066f9361cee35ae5be7ba9d9f86dfba + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_list_team_members.yaml b/tests/cassettes/test_list_team_members.yaml new file mode 100644 index 00000000..409f3eef --- /dev/null +++ b/tests/cassettes/test_list_team_members.yaml @@ -0,0 +1,117 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":19,"is_global":false,"name":"This is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '438' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - c2885f6c4d1d7ee41ed4336eeabb0f49 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/19/team_memberships + response: + body: + string: '[{"id":23,"team_id":19,"member_id":6,"is_team_leader":true,"team_permission":"can_manage","incident_permission":"full_access"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '127' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - f73b41d095b51683ece4341247ad96c8 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_list_teams.yaml b/tests/cassettes/test_list_teams.yaml new file mode 100644 index 00000000..9347dd0c --- /dev/null +++ b/tests/cassettes/test_list_teams.yaml @@ -0,0 +1,63 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams + response: + body: + string: + '[{"id":6,"is_global":true,"name":"All incidents","description":null,"gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/6"},{"id":18,"is_global":false,"name":"New + PyGitGuardian team","description":"New description","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/18"},{"id":19,"is_global":false,"name":"This + is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '751' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - ae45a816ed4d7b8482480df12113661c + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_list_teams_sources.yaml b/tests/cassettes/test_list_teams_sources.yaml new file mode 100644 index 00000000..7010d2ab --- /dev/null +++ b/tests/cassettes/test_list_teams_sources.yaml @@ -0,0 +1,123 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":19,"is_global":false,"name":"This is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '438' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:33 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - a87a3376738fec1bdc1019d2f164ccb9 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/19/sources + response: + body: + string: '[{"id":124,"type":"azure_devops","full_name":"gg-integration-test / + gg-test / default_branch","health":"at_risk","source_criticality":"unknown","default_branch":"test","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346395Z","status":"finished","failing_reason":"","commits_scanned":23,"duration":"0.437131","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"05b69081-f346-4022-8784-198f50aed182","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/default_branch"},{"id":125,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / gg-test","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346418Z","status":"finished","failing_reason":"","commits_scanned":43,"duration":"0.492489","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"7655868e-bb15-47ab-bd62-abd97212e5e8","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/gg-test"},{"id":126,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / huge_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346442Z","status":"finished","failing_reason":"","commits_scanned":1007,"duration":"1.102781","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"455c4e0c-6dc3-48ce-a0e7-819d9a8d7523","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/huge_repo"},{"id":127,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / new_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346464Z","status":"finished","failing_reason":"","commits_scanned":1,"duration":"0.216094","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"f9b583fb-dcfd-46ec-8938-44b427d3e596","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/new_repo"},{"id":128,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / t e s t i n g","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":115,"closed_incidents_count":1,"last_scan":{"date":"2024-12-06T12:27:52.346299Z","status":"finished","failing_reason":"","commits_scanned":113,"duration":"7.597231","branches_scanned":2,"progress":100},"monitored":true,"visibility":"private","external_id":"8a132329-0c77-4efc-a18a-882bda6ab28b","secret_incidents_breakdown":{"open_secret_incidents":{"total":115,"severity_breakdown":{"critical":0,"high":1,"medium":0,"low":0,"info":0,"unknown":114}},"closed_secret_incidents":{"total":1,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":1}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/t%20e%20s%20t%20i%20n%20g"},{"id":129,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / test abc","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":28,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346367Z","status":"finished","failing_reason":"","commits_scanned":55,"duration":"0.623082","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"2aa16b64-2fb2-4639-afeb-70c0c4e8a267","secret_incidents_breakdown":{"open_secret_incidents":{"total":28,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":28}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/test%20abc"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '5158' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:33 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 3862189652edcc8c29ca4adb962264ae + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_search_sources.yaml b/tests/cassettes/test_search_sources.yaml new file mode 100644 index 00000000..0307ba2c --- /dev/null +++ b/tests/cassettes/test_search_sources.yaml @@ -0,0 +1,64 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/sources?cursor=&per_page=20&type=azure_devops + response: + body: + string: '[{"id":124,"type":"azure_devops","full_name":"gg-integration-test / + gg-test / default_branch","health":"at_risk","source_criticality":"unknown","default_branch":"test","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346395Z","status":"finished","failing_reason":"","commits_scanned":23,"duration":"0.437131","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"05b69081-f346-4022-8784-198f50aed182","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/default_branch"},{"id":125,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / gg-test","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346418Z","status":"finished","failing_reason":"","commits_scanned":43,"duration":"0.492489","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"7655868e-bb15-47ab-bd62-abd97212e5e8","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/gg-test"},{"id":126,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / huge_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346442Z","status":"finished","failing_reason":"","commits_scanned":1007,"duration":"1.102781","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"455c4e0c-6dc3-48ce-a0e7-819d9a8d7523","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/huge_repo"},{"id":127,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / new_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346464Z","status":"finished","failing_reason":"","commits_scanned":1,"duration":"0.216094","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"f9b583fb-dcfd-46ec-8938-44b427d3e596","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/new_repo"},{"id":128,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / t e s t i n g","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":115,"closed_incidents_count":1,"last_scan":{"date":"2024-12-06T12:27:52.346299Z","status":"finished","failing_reason":"","commits_scanned":113,"duration":"7.597231","branches_scanned":2,"progress":100},"monitored":true,"visibility":"private","external_id":"8a132329-0c77-4efc-a18a-882bda6ab28b","secret_incidents_breakdown":{"open_secret_incidents":{"total":115,"severity_breakdown":{"critical":0,"high":1,"medium":0,"low":0,"info":0,"unknown":114}},"closed_secret_incidents":{"total":1,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":1}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/t%20e%20s%20t%20i%20n%20g"},{"id":129,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / test abc","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":28,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346367Z","status":"finished","failing_reason":"","commits_scanned":55,"duration":"0.623082","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"2aa16b64-2fb2-4639-afeb-70c0c4e8a267","secret_incidents_breakdown":{"open_secret_incidents":{"total":28,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":28}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/test%20abc"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '5158' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:33 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 1da577d9ba4958fc60053554538f17f3 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_search_team_invitations.yaml b/tests/cassettes/test_search_team_invitations.yaml new file mode 100644 index 00000000..ef1a29b7 --- /dev/null +++ b/tests/cassettes/test_search_team_invitations.yaml @@ -0,0 +1,117 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":19,"is_global":false,"name":"This is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '438' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 566dae93fc703362ffed5e7ebadf39e1 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/19/team_invitations?incident_permission=can_view + response: + body: + string: '[{"id":7,"team_id":19,"invitation_id":13,"is_team_leader":true,"team_permission":"can_manage","incident_permission":"can_view"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '128' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 29f234c1d53e950611ea39d6f3ab2d4f + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_search_team_members.yaml b/tests/cassettes/test_search_team_members.yaml new file mode 100644 index 00000000..d1314a89 --- /dev/null +++ b/tests/cassettes/test_search_team_members.yaml @@ -0,0 +1,117 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":19,"is_global":false,"name":"This is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '438' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 39c7b8d08138e0512342855676693e4a + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/19/team_memberships?is_team_leader=True + response: + body: + string: '[{"id":23,"team_id":19,"member_id":6,"is_team_leader":true,"team_permission":"can_manage","incident_permission":"full_access"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '127' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:32 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 1b97816da66a643c42f4ff36035d30ac + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_search_teams_sources.yaml b/tests/cassettes/test_search_teams_sources.yaml new file mode 100644 index 00000000..cdb952c8 --- /dev/null +++ b/tests/cassettes/test_search_teams_sources.yaml @@ -0,0 +1,123 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":19,"is_global":false,"name":"This is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '438' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:33 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - c63573489c1f2b6e3d1e7aca48b2a8d2 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams/19/sources?cursor=&per_page=20&type=azure_devops + response: + body: + string: '[{"id":124,"type":"azure_devops","full_name":"gg-integration-test / + gg-test / default_branch","health":"at_risk","source_criticality":"unknown","default_branch":"test","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346395Z","status":"finished","failing_reason":"","commits_scanned":23,"duration":"0.437131","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"05b69081-f346-4022-8784-198f50aed182","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/default_branch"},{"id":125,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / gg-test","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":19,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346418Z","status":"finished","failing_reason":"","commits_scanned":43,"duration":"0.492489","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"7655868e-bb15-47ab-bd62-abd97212e5e8","secret_incidents_breakdown":{"open_secret_incidents":{"total":19,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":19}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/gg-test"},{"id":126,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / huge_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346442Z","status":"finished","failing_reason":"","commits_scanned":1007,"duration":"1.102781","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"455c4e0c-6dc3-48ce-a0e7-819d9a8d7523","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/huge_repo"},{"id":127,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / new_repo","health":"safe","source_criticality":"unknown","default_branch":"master","default_branch_head":null,"open_incidents_count":0,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346464Z","status":"finished","failing_reason":"","commits_scanned":1,"duration":"0.216094","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"f9b583fb-dcfd-46ec-8938-44b427d3e596","secret_incidents_breakdown":{"open_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/new_repo"},{"id":128,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / t e s t i n g","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":115,"closed_incidents_count":1,"last_scan":{"date":"2024-12-06T12:27:52.346299Z","status":"finished","failing_reason":"","commits_scanned":113,"duration":"7.597231","branches_scanned":2,"progress":100},"monitored":true,"visibility":"private","external_id":"8a132329-0c77-4efc-a18a-882bda6ab28b","secret_incidents_breakdown":{"open_secret_incidents":{"total":115,"severity_breakdown":{"critical":0,"high":1,"medium":0,"low":0,"info":0,"unknown":114}},"closed_secret_incidents":{"total":1,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":1}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/t%20e%20s%20t%20i%20n%20g"},{"id":129,"type":"azure_devops","full_name":"gg-integration-test + / gg-test / test abc","health":"at_risk","source_criticality":"unknown","default_branch":"main","default_branch_head":null,"open_incidents_count":28,"closed_incidents_count":0,"last_scan":{"date":"2024-12-06T12:27:52.346367Z","status":"finished","failing_reason":"","commits_scanned":55,"duration":"0.623082","branches_scanned":1,"progress":100},"monitored":true,"visibility":"private","external_id":"2aa16b64-2fb2-4639-afeb-70c0c4e8a267","secret_incidents_breakdown":{"open_secret_incidents":{"total":28,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":28}},"closed_secret_incidents":{"total":0,"severity_breakdown":{"critical":0,"high":0,"medium":0,"low":0,"info":0,"unknown":0}}},"url":"https://dev.azure.com/gg-integration-test/gg-test/_git/test%20abc"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '5158' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:33 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 3dbb304dd6b17f4bedc9d06dc31a9a69 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_send_invitation.yaml b/tests/cassettes/test_send_invitation.yaml new file mode 100644 index 00000000..5a06abd2 --- /dev/null +++ b/tests/cassettes/test_send_invitation.yaml @@ -0,0 +1,58 @@ +interactions: + - request: + body: '{"email": "pygitguardian@example.com", "access_level": "member"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '64' + Content-Type: + - application/json + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: POST + uri: https://api.gitguardian.com/v1/invitations?send_email=False + response: + body: + string: '{"id":15,"date":"2024-12-12T17:00:27.844380Z","email":"pygitguardian@example.com","role":"member","access_level":"member"}' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '122' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 17:00:27 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - ef94d7e9f303dfcd2caf016acde5004b + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 201 + message: Created +version: 1 diff --git a/tests/cassettes/test_update_member.yaml b/tests/cassettes/test_update_member.yaml new file mode 100644 index 00000000..ffacb7b8 --- /dev/null +++ b/tests/cassettes/test_update_member.yaml @@ -0,0 +1,118 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/members?access_level=manager + response: + body: + string: + '[{"id":17,"role":"manager","access_level":"manager","email":"henri.delateamsecretetducorealerting@gg.com","name":"Henri + De la team secret et du core alerting","created_at":"2024-12-12T17:07:32.636754Z","last_login":null,"active":true}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '235' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 17:17:38 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - a7d3115a6902e9453b34b954d81ef5f8 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: '{"access_level": "member", "active": false}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '43' + Content-Type: + - application/json + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: PATCH + uri: https://api.gitguardian.com/v1/members/17 + response: + body: + string: + '{"id":17,"role":"member","access_level":"member","email":"henri.delateamsecretetducorealerting@gg.com","name":"Henri + De la team secret et du core alerting","created_at":"2024-12-12T17:07:32.636754Z","last_login":null,"active":false}' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, PUT, PATCH, DELETE, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '232' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 17:17:38 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - a555addad8ac0fe94b6fe646ced700b4 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_update_team.yaml b/tests/cassettes/test_update_team.yaml new file mode 100644 index 00000000..f34baba7 --- /dev/null +++ b/tests/cassettes/test_update_team.yaml @@ -0,0 +1,120 @@ +interactions: + - request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: GET + uri: https://api.gitguardian.com/v1/teams?cursor=&per_page=20&is_global=False + response: + body: + string: + '[{"id":18,"is_global":false,"name":"Another test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/18"},{"id":19,"is_global":false,"name":"This + is a test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/19"},{"id":20,"is_global":false,"name":"Team + test","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/20"},{"id":21,"is_global":false,"name":"PyGitGuardian + team","description":"","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/21"}]' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, POST, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '582' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:31 GMT + Link: + - '' + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Per-Page: + - '20' + X-Request-ID: + - 7ef813898023adf9bd42d205e8ec4428 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK + - request: + body: '{"name": "New PyGitGuardian team", "description": "New description"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '68' + Content-Type: + - application/json + User-Agent: + - pygitguardian/1.18.0 (Darwin;py3.11.8) + method: PATCH + uri: https://api.gitguardian.com/v1/teams/18 + response: + body: + string: + '{"id":18,"is_global":false,"name":"New PyGitGuardian team","description":"New + description","gitguardian_url":"http://localhost:3000/workspace/6/settings/user/teams/18"}' + headers: + Access-Control-Expose-Headers: + - X-App-Version + Allow: + - GET, PUT, PATCH, DELETE, HEAD, OPTIONS + Connection: + - keep-alive + Content-Length: + - '168' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Thu, 12 Dec 2024 16:59:31 GMT + Referrer-Policy: + - same-origin + Server: + - nginx/1.24.0 + Vary: + - Cookie + X-App-Version: + - dev + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-ID: + - 5d709cb2c3be38948d71eac6b3c4e2d5 + X-SCA-Engine-Version: + - 2.2.0 + X-Secrets-Engine-Version: + - 2.127.0 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_client.py b/tests/test_client.py index d21ff672..0931be3d 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -23,16 +23,41 @@ MULTI_DOCUMENT_LIMIT, ) from pygitguardian.models import ( + AccessLevel, APITokensResponse, + CreateInvitation, + CreateInvitationParameters, + CreateTeam, + CreateTeamInvitation, + CreateTeamMember, + CreateTeamMemberParameters, + DeleteMemberParameters, Detail, HoneytokenResponse, HoneytokenWithContextResponse, + IncidentPermission, + Invitation, JWTResponse, JWTService, + Member, + MembersParameters, MultiScanResult, QuotaResponse, ScanResult, + Source, + SourceParameters, + Team, + TeamInvitation, + TeamInvitationParameters, + TeamMember, + TeamMemberParameters, + TeamSourceParameters, + TeamsParameters, + UpdateMember, + UpdateTeam, + UpdateTeamSource, ) +from pygitguardian.models_utils import CursorPaginatedResponse from pygitguardian.sca_models import ( ComputeSCAFilesResult, SCAScanAllOutput, @@ -42,6 +67,7 @@ ) from .conftest import create_client, my_vcr +from .utils import get_invitation, get_source, get_team FILENAME = ".env" @@ -1391,3 +1417,568 @@ def test_read_metadata_remediation_message(client: GGClient): assert client.remediation_messages.pre_commit == messages["pre_commit"] assert client.remediation_messages.pre_push == messages["pre_push"] assert client.remediation_messages.pre_receive == messages["pre_receive"] + + +LIST_MEMBERS_RESPONSE = [ + { + "id": 3251, + "name": "Owl", + "email": "john.smith@example.org", + "role": "owner", + "access_level": "owner", + "active": True, + "created_at": "2022-04-20T11:07:24.000Z", + "last_login": "2022-04-20T11:07:24.000Z", + }, + { + "id": 3252, + "name": "Owl", + "email": "john.smith@example.org", + "role": "owner", + "access_level": "owner", + "active": True, + "created_at": "2022-04-20T11:07:24.000Z", + "last_login": "2022-04-20T11:07:24.000Z", + }, +] + + +@my_vcr.use_cassette("test_list_members.yaml", ignore_localhost=False) +def test_list_members(client: GGClient): + """ + GIVEN a client + WHEN calling /members endpoint + THEN it returns a paginated list of members + """ + + result = client.list_members() + + assert isinstance(result, CursorPaginatedResponse), result + + +@my_vcr.use_cassette("test_list_members_parameters.yaml", ignore_localhost=False) +def test_search_member(client: GGClient): + """ + GIVEN a client + WHEN calling /members endpoint + AND parameters are passed + THEN it returns a paginated list of members matching the parameters + """ + + result = client.list_members(MembersParameters(access_level=AccessLevel.MANAGER)) + + assert isinstance(result, CursorPaginatedResponse), result + assert all(member.access_level == AccessLevel.MANAGER for member in result.data) + + +@my_vcr.use_cassette("test_update_member.yaml", ignore_localhost=False) +def test_update_member(client: GGClient): + """ + GIVEN a client + WHEN calling PATCH /members/{id} endpoint with a payload + THEN it returns the updated member + """ + + # This assumes there is at least one manager in the first page of members + members = client.list_members(MembersParameters(access_level=AccessLevel.MANAGER)) + assert isinstance(members, CursorPaginatedResponse), "Could not fetch members" + + result = client.update_member( + UpdateMember( + id=members.data[0].id, access_level=AccessLevel.MEMBER, active=False + ) + ) + + assert isinstance(result, Member), result + + assert not result.active + assert result.access_level == AccessLevel.MEMBER + + +@my_vcr.use_cassette("test_delete_member.yaml", ignore_localhost=False) +def test_delete_member(client: GGClient): + """ + GIVEN a client + WHEN calling DELETE /members/{id} endpoint + THEN the member is deleted + """ + + members = client.list_members(MembersParameters(access_level=AccessLevel.MEMBER)) + assert isinstance(members, CursorPaginatedResponse), "Could not fetch members" + + member = members.data[0] + result = client.delete_member(DeleteMemberParameters(id=member.id)) + + assert result is None, result + + +@my_vcr.use_cassette("test_create_team.yaml", ignore_localhost=False) +def test_create_team(client: GGClient): + """ + GIVEN a client + WHEN calling POST /teams endpoint + THEN a team is created + """ + + result = client.create_team(CreateTeam(name="PyGitGuardian team")) + + assert isinstance(result, Team), result + + +@my_vcr.use_cassette("test_get_team.yaml", ignore_localhost=False) +def test_get_team(client: GGClient): + """ + GIVEN a client + WHEN calling GET /teams/{id} endpoint + THEN the corresponding team is returned + """ + + # This test would require a team to be created first and its id + # stored in a config file for this test to simulate a real use case + team = get_team() + result = client.get_team(team.id) + + assert isinstance(result, Team), result + + +@my_vcr.use_cassette("test_update_team.yaml", ignore_localhost=False) +def test_update_team(client: GGClient): + """ + GIVEN a client + WHEN calling PATCH /teams endpoint + THEN the corresponding team is updated + """ + + team = get_team() + result = client.update_team( + UpdateTeam( + id=team.id, name="New PyGitGuardian team", description="New description" + ) + ) + + assert isinstance(result, Team), result + + assert result.name == "New PyGitGuardian team" + assert result.description == "New description" + + +@my_vcr.use_cassette("test_list_teams.yaml", ignore_localhost=False) +def test_list_teams(client: GGClient): + """ + GIVEN a client + WHEN calling GET /teams endpoint + THEN a paginated list of teams is returned + """ + + result = client.list_teams() + + assert isinstance(result, CursorPaginatedResponse), result + assert isinstance(result.data[0], Team) + + +@my_vcr.use_cassette("test_global_team.yaml", ignore_localhost=False) +def test_global_team(client: GGClient): + """ + GIVEN a client + WHEN calling GET /teams endpoint + AND passing is_global parameter + THEN the global team is returned + """ + + result = client.list_teams(parameters=TeamsParameters(is_global=True)) + + assert isinstance(result, CursorPaginatedResponse), result + + assert all(team.is_global for team in result.data) + + +@my_vcr.use_cassette("test_delete_team.yaml", ignore_localhost=False) +def test_delete_team(client: GGClient): + """ + GIVEN a client + WHEN calling DELETE /teams/{id} endpoint + THEN the team is deleted + """ + + team = get_team() + result = client.delete_team(team.id) + + assert result is None + + +@my_vcr.use_cassette("test_create_team_invitation.yaml", ignore_localhost=False) +def test_create_team_invitation(client: GGClient): + """ + GIVEN a client + WHEN calling POST /teams/{id}/invitations endpoint + THEN an invitation is created + """ + + team = get_team() + invitation = get_invitation() + + result = client.create_team_invitation( + team.id, + CreateTeamInvitation( + invitation_id=invitation.id, + is_team_leader=True, + incident_permission=IncidentPermission.VIEW, + ), + ) + + assert isinstance(result, TeamInvitation), result + + +@my_vcr.use_cassette("test_list_team_invitations.yaml", ignore_localhost=False) +def test_list_team_invitations(client: GGClient): + """ + GIVEN a client + WHEN calling GET /teams/{id}/invitations endpoint + THEN a paginated list of invitations is returned + """ + + team = get_team() + result = client.list_team_invitations(team.id) + + assert isinstance(result, CursorPaginatedResponse), result + # This assumes there is at least one team invitation + assert isinstance(result.data[0], TeamInvitation) + + +@my_vcr.use_cassette("test_search_team_invitations.yaml", ignore_localhost=False) +def test_search_team_invitations(client: GGClient): + """ + GIVEN a client + WHEN calling GET /teams/{id}/invitations endpoint + AND parameters are passed + THEN a paginated list of invitations is returned matching the parameters + """ + + team = get_team() + result = client.list_team_invitations( + team.id, + parameters=TeamInvitationParameters( + incident_permission=IncidentPermission.VIEW + ), + ) + + assert isinstance(result, CursorPaginatedResponse), result + assert all( + invitation.incident_permission == "can_view" for invitation in result.data + ) + + +@my_vcr.use_cassette("test_delete_team_invitation.yaml", ignore_localhost=False) +def test_delete_team_invitation(client: GGClient): + """ + GIVEN a client + WHEN calling DELETE /teams/{id}/invitations/{id} endpoint + THEN an invitation is deleted + """ + + team = get_team() + team_invitations = client.list_team_invitations(team.id) + assert isinstance( + team_invitations, CursorPaginatedResponse + ), "Could not fetch team invitations" + + result = client.delete_team_invitation(team.id, team_invitations.data[0].id) + + assert result is None + + +@my_vcr.use_cassette("test_list_team_members.yaml", ignore_localhost=False) +def test_list_team_members(client: GGClient): + """ + GIVEN a client + WHEN calling GET /teams/{id}/members endpoint + THEN a paginated list of members is returned + """ + + team = get_team() + result = client.list_team_members(team.id) + + assert isinstance(result, CursorPaginatedResponse), result + assert isinstance(result.data[0], TeamMember) + + +@my_vcr.use_cassette("test_search_team_members.yaml", ignore_localhost=False) +def test_search_team_members(client: GGClient): + """ + GIVEN a client + WHEN calling GET /teams/{id}/members endpoint + AND parameters are passed + THEN a paginated list of members is returned matching the parameters + """ + + team = get_team() + + # Every team should have at least one team leader, but an account without a team + # will nullify the purpose of this test even though it will pass + result = client.list_team_members( + team.id, parameters=TeamMemberParameters(is_team_leader=True) + ) + + assert isinstance(result, CursorPaginatedResponse), result + assert all(member.is_team_leader for member in result.data) + + +@my_vcr.use_cassette("test_create_team_member.yaml", ignore_localhost=False) +def test_create_team_member(client: GGClient): + """ + GIVEN a client + WHEN calling POST /teams/{id}/members endpoint + THEN a member is created + """ + + all_members = client.list_members() + assert isinstance( + all_members, CursorPaginatedResponse + ), "Could not fetch members from GitGuardian" + + team = get_team() + team_members = client.list_team_members(team.id) + assert isinstance( + team_members, CursorPaginatedResponse + ), "Could not fetch team members from GitGuardian" + team_members_ids = {team_member.member_id for team_member in team_members.data} + + # This assumes there is at least one member in the first page of team members that + # does not belong to the retrieved team + member_to_add = next( + member for member in all_members.data if member.id not in team_members_ids + ) + + result = client.create_team_member( + team.id, + CreateTeamMember(member_to_add.id, False, IncidentPermission.VIEW), + ) + + assert isinstance(result, TeamMember), result + + assert result.incident_permission == IncidentPermission.VIEW + assert not result.is_team_leader + + +@my_vcr.use_cassette("test_create_team_member_parameters.yaml", ignore_localhost=False) +def test_create_team_member_without_mail(client: GGClient): + """ + GIVEN a client + WHEN calling POST /teams/{id}/members endpoint + THEN a member is created + """ + + all_members = client.list_members() + assert isinstance( + all_members, CursorPaginatedResponse + ), "Could not fetch members from GitGuardian" + + team = get_team() + team_members = client.list_team_members(team.id) + assert isinstance( + team_members, CursorPaginatedResponse + ), "Could not fetch team members from GitGuardian" + team_members_ids = {team_member.member_id for team_member in team_members.data} + + # This assumes there is at least one member in the first page of team members that + # does not belong to the retrieved team + member_to_add = next( + member for member in all_members.data if member.id not in team_members_ids + ) + + result = client.create_team_member( + team.id, + CreateTeamMember(member_to_add.id, False, IncidentPermission.VIEW), + CreateTeamMemberParameters(send_email=False), + ) + + assert isinstance(result, TeamMember), result + + +@my_vcr.use_cassette("test_delete_team_member.yaml", ignore_localhost=False) +def test_delete_team_member(client: GGClient): + """ + GIVEN a client + WHEN calling DELETE /teams/{id}/members/{id} endpoint + THEN a member is deleted + """ + + all_members = client.list_members() + assert isinstance( + all_members, CursorPaginatedResponse + ), "Could not fetch members from GitGuardian" + + team = get_team() + team_members = client.list_team_members( + team.id, TeamMemberParameters(is_team_leader=False) + ) + assert isinstance( + team_members, CursorPaginatedResponse + ), "Could not fetch team members from GitGuardian" + + team_member = team_members.data[0] + result = client.delete_team_member(team.id, team_member.id) + + assert result is None + + +@my_vcr.use_cassette("test_list_sources.yaml", ignore_localhost=False) +def test_list_sources(client: GGClient): + """ + GIVEN a client + WHEN calling GET /sources endpoint + THEN a paginated list of sources is returned + """ + + result = client.list_sources() + assert isinstance(result, CursorPaginatedResponse), result + assert isinstance(result.data[0], Source) + + +@my_vcr.use_cassette("test_search_sources.yaml", ignore_localhost=False) +def test_search_sources(client: GGClient): + """ + GIVEN a client + WHEN calling GET /sources endpoint + AND parameters are passed + THEN a paginated list of sources is returned matching the parameters + """ + + result = client.list_sources(parameters=SourceParameters(type="azure_devops")) + + assert isinstance(result, CursorPaginatedResponse), result + assert all(source.type == "azure_devops" for source in result.data) + + +@my_vcr.use_cassette("test_list_teams_sources.yaml", ignore_localhost=False) +def test_list_team_sources(client: GGClient): + """ + GIVEN a client + WHEN calling GET /sources endpoint + THEN a paginated list of sources is returned + """ + + result = client.list_team_sources(get_team().id) + assert isinstance(result, CursorPaginatedResponse), result + + # This assumes at least one source has been installed and is on the perimeter of a team + assert isinstance(result.data[0], Source) + + +@my_vcr.use_cassette("test_search_teams_sources.yaml", ignore_localhost=False) +def test_search_team_sources(client: GGClient): + """ + GIVEN a client + WHEN calling GET /sources endpoint + AND parameters are passed + THEN a paginated list of sources is returned matching the parameters + """ + + result = client.list_team_sources( + get_team().id, TeamSourceParameters(type="azure_devops") + ) + + assert isinstance(result, CursorPaginatedResponse), result + assert all(source.type == "azure_devops" for source in result.data) + + +@my_vcr.use_cassette("test_delete_team_sources.yaml", ignore_localhost=False) +def test_delete_team_sources(client: GGClient): + """ + GIVEN a client + WHEN calling POST /teams/{id}/sources endpoint + THEN a source is deleted + """ + + team = get_team() + team_sources = client.list_team_sources(team.id) + assert isinstance( + team_sources, CursorPaginatedResponse + ), "Could not fetch team sources" + source_to_delete = team_sources.data[0] + result = client.update_team_source( + UpdateTeamSource(team.id, [], [source_to_delete.id]) + ) + + assert result is None + + team_sources = client.list_team_sources(team.id) + assert isinstance(team_sources, CursorPaginatedResponse), team_sources + assert not any(source.id == source_to_delete.id for source in team_sources.data) + + +@my_vcr.use_cassette("test_add_team_sources.yaml", ignore_localhost=False) +def test_add_team_sources(client: GGClient): + """ + GIVEN a client + WHEN calling POST /teams/{id}/sources endpoint + THEN a source is added + """ + + team = get_team() + source = get_source() + + result = client.update_team_source( + UpdateTeamSource(team.id, [source.id], []), + ) + + assert result is None + + team_sources = client.list_team_sources( + team.id, TeamSourceParameters(type="azure_devops") + ) + assert isinstance(team_sources, CursorPaginatedResponse), team_sources + assert any(received_source.id == source.id for received_source in team_sources.data) + + +@my_vcr.use_cassette("test_list_invitations.yaml", ignore_localhost=False) +def test_list_invitations(client: GGClient): + """ + GIVEN a client + WHEN calling GET /invitations endpoint + THEN a paginated list of invitations is returned + """ + + result = client.list_invitations() + assert isinstance(result, CursorPaginatedResponse), result + # This assumes there is at least one invitation sent in the account + assert isinstance(result.data[0], Invitation) + + +@my_vcr.use_cassette("test_send_invitation.yaml", ignore_localhost=False) +def test_send_invitation(client: GGClient): + """ + GIVEN a client + WHEN calling POST /invitations endpoint + THEN an invitation is sent + """ + + result = client.create_invitation( + CreateInvitation( + email="pygitguardian@example.com", access_level=AccessLevel.MEMBER + ), + CreateInvitationParameters(send_email=False), + ) + + assert isinstance(result, Invitation), result + + assert result.email == "pygitguardian@example.com" + assert result.access_level == AccessLevel.MEMBER + + +@my_vcr.use_cassette("test_delete_invitation.yaml", ignore_localhost=False) +def test_delete_invitation(client: GGClient): + """ + GIVEN a client + WHEN calling DELETE /invitations/{id} endpoint + THEN the invitation is deleted + """ + + invitations = client.list_invitations() + assert isinstance( + invitations, CursorPaginatedResponse + ), "Could not fetch invitations" + + result = client.delete_invitation(invitations.data[0].id) + + assert result is None diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 00000000..706ed855 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,52 @@ +from pygitguardian.models import Invitation, Source, Team, TeamsParameters +from pygitguardian.models_utils import CursorPaginatedResponse + +from .conftest import create_client + + +def get_source() -> Source: + """ + Return the first source available in the account, + not all accounts have a source so testing from scratch + will require installing a source first + """ + + client = create_client() + paginated_sources = client.list_sources() + assert isinstance( + paginated_sources, CursorPaginatedResponse + ), "Could not fetch sources from GitGuardian" + return paginated_sources.data[0] + + +def get_invitation() -> Invitation: + """ + Return the first invitation available in the account, + there is no invitation by default, one should be + created to setup the test + """ + + client = create_client() + paginated_teams = client.list_invitations() + assert isinstance( + paginated_teams, CursorPaginatedResponse + ), "Could not fetch members from GitGuardian" + + return paginated_teams.data[0] + + +def get_team() -> Team: + """ + Return the first team available in the account, + every account should have at least one team (all incidents) + but we skip it since we cannot add sources to it + """ + + client = create_client() + + paginated_teams = client.list_teams(TeamsParameters(is_global=False)) + assert isinstance( + paginated_teams, CursorPaginatedResponse + ), "Could not fetch teams from GitGuardian" + + return paginated_teams.data[0]