Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Integration][Gitlab] Added support for gitlab member ingestion #767

Merged
merged 67 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
d3dec00
Added support for gitlab user ingestion
mk-armah Jul 3, 2024
b88d530
edited changelog
mk-armah Jul 3, 2024
cfeddb0
edited changelog
mk-armah Jul 3, 2024
8a48e1b
Merge branch 'main' into improvement/gitlab
mk-armah Jul 12, 2024
81905c5
updated group -> member relationship
mk-armah Jul 12, 2024
58665c6
Merge branch 'main' into improvement/gitlab
mk-armah Jul 12, 2024
2917e45
Lint
mk-armah Jul 12, 2024
9820b9a
Added error handling to get_all_group_members
mk-armah Jul 12, 2024
24b3551
Added error handling to get_all_group_members
mk-armah Jul 12, 2024
7d324db
Merge branch 'main' into improvement/gitlab
mk-armah Jul 25, 2024
12ba800
updated blueprints
mk-armah Jul 25, 2024
191292b
Merge branch 'improvement/gitlab' of github.com:port-labs/ocean into …
mk-armah Jul 25, 2024
9f9b706
related service to groups
mk-armah Jul 26, 2024
9df3202
lint
mk-armah Jul 26, 2024
5daceeb
fixed lint issues
mk-armah Jul 26, 2024
4d98b1d
change default behavior for filterBots flag
mk-armah Jul 26, 2024
1b69d29
made the filterBots and publicEmailVisibility flags unset by default,…
mk-armah Jul 26, 2024
2a81da0
Merge branch 'main' into improvement/gitlab
mk-armah Jul 31, 2024
35ee321
Merge branch 'main' into improvement/gitlab
mk-armah Jul 31, 2024
ff737e6
gitlab member webhook development
mk-armah Aug 1, 2024
00617e7
Merge branch 'main' into improvement/gitlab
mk-armah Aug 2, 2024
47e2e24
support for member webhook
mk-armah Aug 2, 2024
5ce70f3
Merge branch 'improvement/gitlab' of github.com:port-labs/ocean into …
mk-armah Aug 2, 2024
e4c1238
Merge branch 'main' into improvement/gitlab
Tankilevitch Aug 4, 2024
3a8e14a
Merge branch 'main' into improvement/gitlab
mk-armah Aug 7, 2024
4457ba3
updated scripts for syncing members
mk-armah Aug 7, 2024
952fac7
Merge branch 'improvement/gitlab' of github.com:port-labs/ocean into …
mk-armah Aug 7, 2024
9721fd9
Update CHANGELOG.md
mk-armah Aug 7, 2024
62a11f8
removed groups and members from spec.yaml
mk-armah Aug 7, 2024
33e9b19
refactored fetch group members function in members resync
mk-armah Aug 7, 2024
d5b4ea1
renamed fetch_group_members function to process_group_members
mk-armah Aug 8, 2024
4882b3f
Merge branch 'main' into improvement/gitlab
mk-armah Aug 11, 2024
7d35201
Merge branch 'main' into improvement/gitlab
mk-armah Aug 12, 2024
b15ba21
bumped ocean version
mk-armah Aug 12, 2024
cad6210
Merge branch 'main' into improvement/gitlab
mk-armah Aug 21, 2024
1458fac
updated resync groups with members
mk-armah Aug 21, 2024
56f7b44
updated resync with groups
mk-armah Aug 21, 2024
b9f7e25
merged main
mk-armah Aug 21, 2024
c534466
removed group hook
mk-armah Aug 21, 2024
386b318
Merge branch 'main' into improvement/gitlab
mk-armah Aug 30, 2024
6ae7156
lint
mk-armah Aug 30, 2024
0a91673
Merge branch 'improvement/gitlab' of https://github.com/port-labs/oce…
mk-armah Aug 30, 2024
44c3b7f
rephrased comments
mk-armah Sep 2, 2024
fe2e7a2
updated group webhook to cater for groupswithmembers kind
mk-armah Sep 5, 2024
68354d1
Merge branch 'main' into improvement/gitlab
mk-armah Nov 6, 2024
0a279ae
remove debug logs
mk-armah Nov 6, 2024
99e3724
test fix
mk-armah Nov 6, 2024
82bfc94
updated webhook logic
mk-armah Nov 8, 2024
c81ab10
revert get_group function, stick with optional response
mk-armah Nov 8, 2024
bb7f146
added support for project members
mk-armah Nov 11, 2024
0056a27
checking for bot members in username works for both webhooks and requ…
mk-armah Nov 11, 2024
be3dc5a
clean unused functions
mk-armah Nov 11, 2024
c1289c7
refactored codebase against DRY
mk-armah Nov 11, 2024
2073632
Merge branch 'main' into improvement/gitlab
mk-armah Nov 11, 2024
56b031c
added tests
mk-armah Nov 12, 2024
936f781
Merge branch 'improvement/gitlab' of https://github.com/port-labs/oce…
mk-armah Nov 12, 2024
7f98500
Update integrations/gitlab/gitlab_integration/git_integration.py
mk-armah Nov 13, 2024
8df02ae
addressed comments
mk-armah Nov 13, 2024
48329c0
all enrichments are performed on object level directly
mk-armah Nov 13, 2024
986e9fa
remove unnecessary comment
mk-armah Nov 13, 2024
5b0d7f9
Merge branch 'main' into improvement/gitlab
mk-armah Nov 13, 2024
57ad257
removed public email querying
mk-armah Nov 13, 2024
632d8c2
Merge branch 'improvement/gitlab' of https://github.com/port-labs/oce…
mk-armah Nov 13, 2024
56aaaef
lint
mk-armah Nov 13, 2024
f42ee30
control resync batch size to avoid hitting rate limits
mk-armah Nov 14, 2024
62db938
Added error handling for group not found
mk-armah Nov 14, 2024
4718261
Update integrations/gitlab/CHANGELOG.md
Tankilevitch Nov 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions integrations/gitlab/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

<!-- towncrier release notes start -->

0.1.141 (2024-11-13)
===================

### Features

- Added support for gitlab member ingestion (PORT-7708)


0.1.140 (2024-11-12)
====================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
ProjectPipeline,
Issue,
Group,
User,
GroupMember,
ProjectFile,
)
from loguru import logger
Expand All @@ -36,6 +38,8 @@ async def fetch_single(
Issue,
Project,
Group,
User,
GroupMember,
],
],
*args,
Expand Down
68 changes: 65 additions & 3 deletions integrations/gitlab/gitlab_integration/events/hooks/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
from abc import ABC, abstractmethod
from typing import List, Any
from typing import List, Any, Dict, Optional
import typing
from loguru import logger
from gitlab.v4.objects import Project

from gitlab.v4.objects import Project, Group
from gitlab.base import RESTObject
from gitlab_integration.gitlab_service import GitlabService
from port_ocean.context.ocean import ocean
from port_ocean.context.event import event
from gitlab_integration.git_integration import (
GitlabPortAppConfig,
GitlabMemberSelector,
)


class HookHandler(ABC):
Expand All @@ -20,6 +27,41 @@ def __init__(
async def on_hook(self, event: str, body: dict[str, Any]) -> None:
pass

async def _register_object_with_members(self, kind: str, gitlab_object: RESTObject):
resource_configs = typing.cast(
GitlabPortAppConfig, event.port_app_config
).resources

matching_resource_configs = [
resource_config
for resource_config in resource_configs
if (
resource_config.kind == kind
and isinstance(resource_config.selector, GitlabMemberSelector)
)
]

if not matching_resource_configs:
logger.info(
"Resource not found in port app config, update port app config to include the resource type"
)
return

for resource_config in matching_resource_configs:
include_bot_members = resource_config.selector.include_bot_members
include_inherited_members = (
resource_config.selector.include_inherited_members
)

object_result: RESTObject = (
await self.gitlab_service.enrich_object_with_members(
gitlab_object,
include_bot_members,
include_inherited_members,
)
)
await ocean.register_raw(resource_config.kind, [object_result.asdict()])


class ProjectHandler(HookHandler):
async def on_hook(self, event: str, body: dict[str, Any]) -> None:
Expand Down Expand Up @@ -49,3 +91,23 @@ async def on_hook(self, event: str, body: dict[str, Any]) -> None:
@abstractmethod
async def _on_hook(self, body: dict[str, Any], gitlab_project: Project) -> None:
pass


class GroupHandler(HookHandler):
async def on_hook(self, event: str, body: dict[str, Any]) -> None:
logger.info(f"Handling {event}")

group_id = body.get("group_id", body.get("group", {}).get("id"))
group = await self.gitlab_service.get_group(group_id)
await self._on_hook(body, group)
group_path = body.get("full_path", body.get("group_path"))
logger.info(f"Finished handling {event} for group {group_path}")

@abstractmethod
async def _on_hook(
self, body: dict[str, Any], gitlab_group: Optional[Group]
) -> None:
pass

async def _register_group(self, kind: str, gitlab_group: Dict[str, Any]) -> None:
await ocean.register_raw(kind, [gitlab_group])
51 changes: 30 additions & 21 deletions integrations/gitlab/gitlab_integration/events/hooks/group.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,48 @@
from typing import Any
from typing import Any, Optional

from loguru import logger

from gitlab_integration.events.hooks.base import HookHandler
from gitlab_integration.utils import ObjectKind
from gitlab_integration.events.hooks.base import GroupHandler
from port_ocean.context.ocean import ocean
from gitlab.v4.objects import Group


class GroupHook(HookHandler):
class Groups(GroupHandler):
events = ["Subgroup Hook"]
system_events = [
"group_destroy",
"group_create",
"group_rename",
]
system_events = ["group_destroy", "group_create", "group_rename"]

async def on_hook(self, event: str, body: dict[str, Any]) -> None:
event_name = body["event_name"]

logger.info(f"Handling {event_name} for {event}")

group_id = body["group_id"] if "group_id" in body else body["group"]["id"]

logger.info(f"Handling hook {event} for group {group_id}")

group = await self.gitlab_service.get_group(group_id)
async def _on_hook(
self, body: dict[str, Any], gitlab_group: Optional[Group]
) -> None:

group_id = body.get("group_id")
group_full_path = body.get("full_path")
if group:
await ocean.register_raw(ObjectKind.GROUP, [group.asdict()])
event_name = body["event_name"]

logger.info(
f"Handling event '{event_name}' for group with ID '{group_id}' and full path '{group_full_path}'"
)
if gitlab_group:
await self._register_group(
ObjectKind.GROUP,
gitlab_group.asdict(),
)
await self._register_object_with_members(
ObjectKind.GROUPWITHMEMBERS, gitlab_group
)
logger.info(f"Registered group {group_id}")
elif (
group_full_path
and self.gitlab_service.should_run_for_path(group_full_path)
and event_name in ("subgroup_destroy", "group_destroy")
):
await ocean.unregister_raw(ObjectKind.GROUP, [body])
await ocean.unregister_raw(ObjectKind.GROUPWITHMEMBERS, [body])
logger.info(f"Unregistered group {group_id}")
return

else:
logger.info(f"Group {group_id} was filtered for event {event}. Skipping...")
logger.info(
f"Group {group_id} was filtered for event {event_name}. Skipping..."
)
29 changes: 29 additions & 0 deletions integrations/gitlab/gitlab_integration/events/hooks/members.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from typing import Any, Optional
from loguru import logger

from gitlab_integration.utils import ObjectKind
from gitlab_integration.events.hooks.base import GroupHandler
from gitlab.v4.objects import Group


class Members(GroupHandler):
events = ["Member Hook"]
system_events = [
"user_remove_from_group",
"user_update_for_group",
"user_add_to_group",
]

async def _on_hook(
self, body: dict[str, Any], gitlab_group: Optional[Group]
) -> None:
if gitlab_group:
event_name, user_username = (body["event_name"], body["user_username"])
logger.info(f"Handling {event_name} for group member {user_username}")
await self._register_object_with_members(
ObjectKind.GROUPWITHMEMBERS, gitlab_group
)
else:
logger.info(
f"Group member's group {body['group_id']} was filtered for event {body['event_name']}. Skipping..."
)
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ async def _on_hook(self, body: dict[str, Any], gitlab_project: Project) -> None:
enriched_project = await self.gitlab_service.enrich_project_with_extras(
gitlab_project
)
await ocean.register_raw(ObjectKind.PROJECT, [enriched_project])
await ocean.register_raw(ObjectKind.PROJECT, [enriched_project.asdict()])
await self._register_object_with_members(
ObjectKind.PROJECTWITHMEMBERS, enriched_project
)

else:
logger.debug(
Expand Down
9 changes: 6 additions & 3 deletions integrations/gitlab/gitlab_integration/events/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
from gitlab_integration.events.hooks.merge_request import MergeRequest
from gitlab_integration.events.hooks.pipelines import Pipelines
from gitlab_integration.events.hooks.push import PushHook
from gitlab_integration.events.hooks.group import GroupHook
from gitlab_integration.events.hooks.members import Members
from gitlab_integration.events.hooks.group import Groups
from gitlab_integration.events.hooks.project_files import ProjectFiles
from gitlab_integration.gitlab_service import GitlabService
from gitlab_integration.models.webhook_groups_override_config import (
Expand Down Expand Up @@ -122,7 +123,8 @@ def setup_listeners(gitlab_service: GitlabService, group_id: str) -> None:
Job(gitlab_service),
Issues(gitlab_service),
Pipelines(gitlab_service),
GroupHook(gitlab_service),
Groups(gitlab_service),
Members(gitlab_service),
ProjectFiles(gitlab_service),
]
for handler in handlers:
Expand All @@ -140,7 +142,8 @@ def setup_system_listeners(gitlab_clients: list[GitlabService]) -> None:
Job,
Issues,
Pipelines,
GroupHook,
Groups,
Members,
ProjectFiles,
]
for handler in handlers:
Expand Down
21 changes: 20 additions & 1 deletion integrations/gitlab/gitlab_integration/git_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,25 @@ class GitlabResourceConfig(ResourceConfig):
selector: GitlabSelector


class GitlabMemberSelector(Selector):

include_inherited_members: bool = Field(
alias="includeInheritedMembers",
default=False,
description="If set to true, the integration will include inherited members in the group members list. Default value is false",
)
include_bot_members: bool = Field(
alias="includeBotMembers",
default=True,
description="If set to false, bots will be filtered out from the members list. Default value is true",
)


class GitlabObjectWithMembersResourceConfig(ResourceConfig):
kind: Literal["project-with-members", "group-with-members"]
selector: GitlabMemberSelector


class FilesSelector(BaseModel):
path: str = Field(description="The path to get the files from")
repos: List[str] = Field(
Expand All @@ -146,7 +165,7 @@ class GitlabPortAppConfig(PortAppConfig):
project_visibility_filter: str | None = Field(
alias="projectVisibilityFilter", default=None
)
resources: list[GitLabFilesResourceConfig | GitlabResourceConfig] = Field(default_factory=list) # type: ignore
resources: list[GitlabObjectWithMembersResourceConfig | GitLabFilesResourceConfig | GitlabResourceConfig] = Field(default_factory=list) # type: ignore


def _get_project_from_cache(project_id: int) -> Project | None:
Expand Down
Loading