Skip to content

Commit

Permalink
[Integration][Jira] Fix webhook does not respect selector parameters (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
lordsarcastic authored Jan 9, 2025
1 parent aafb26c commit 895fa7e
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 34 deletions.
8 changes: 8 additions & 0 deletions integrations/jira/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

<!-- towncrier release notes start -->

## 0.2.18 (2025-01-08)


### Bug Fixes

- Fixed a bug where webhook processes issues that are not allowed in a user's integration due to JQL filters


## 0.2.17 (2025-01-08)


Expand Down
2 changes: 1 addition & 1 deletion integrations/jira/jira/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from port_ocean.context.ocean import ocean
from port_ocean.utils import http_async_client


PAGE_SIZE = 50
WEBHOOK_NAME = "Port-Ocean-Events-Webhook"

Expand Down Expand Up @@ -144,7 +145,6 @@ async def get_paginated_issues(
self, params: dict[str, Any] = {}
) -> AsyncGenerator[list[dict[str, Any]], None]:
logger.info("Getting issues from Jira")

params.update(self._generate_base_req_params())
total_issues = (await self._get_paginated_issues(params))["total"]

Expand Down
95 changes: 63 additions & 32 deletions integrations/jira/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
from port_ocean.core.ocean_types import ASYNC_GENERATOR_RESYNC_TYPE

from jira.client import JiraClient
from jira.overrides import JiraIssueConfig, JiraProjectResourceConfig
from jira.overrides import (
JiraIssueConfig,
JiraIssueSelector,
JiraPortAppConfig,
JiraProjectResourceConfig,
)


class ObjectKind(StrEnum):
Expand All @@ -17,6 +22,15 @@ class ObjectKind(StrEnum):
USER = "user"


def create_jira_client() -> JiraClient:
"""Create JiraClient with current configuration."""
return JiraClient(
ocean.integration_config["jira_host"],
ocean.integration_config["atlassian_user_email"],
ocean.integration_config["atlassian_user_token"],
)


async def setup_application() -> None:
logic_settings = ocean.integration_config
app_host = logic_settings.get("app_host")
Expand All @@ -27,11 +41,7 @@ async def setup_application() -> None:
)
return

jira_client = JiraClient(
logic_settings["jira_host"],
logic_settings["atlassian_user_email"],
logic_settings["atlassian_user_token"],
)
jira_client = create_jira_client()

await jira_client.create_events_webhook(
logic_settings["app_host"],
Expand All @@ -40,11 +50,7 @@ async def setup_application() -> None:

@ocean.on_resync(ObjectKind.PROJECT)
async def on_resync_projects(kind: str) -> ASYNC_GENERATOR_RESYNC_TYPE:
client = JiraClient(
ocean.integration_config["jira_host"],
ocean.integration_config["atlassian_user_email"],
ocean.integration_config["atlassian_user_token"],
)
client = create_jira_client()

selector = cast(JiraProjectResourceConfig, event.resource_config).selector
params = {"expand": selector.expand}
Expand All @@ -56,11 +62,7 @@ async def on_resync_projects(kind: str) -> ASYNC_GENERATOR_RESYNC_TYPE:

@ocean.on_resync(ObjectKind.ISSUE)
async def on_resync_issues(kind: str) -> ASYNC_GENERATOR_RESYNC_TYPE:
client = JiraClient(
ocean.integration_config["jira_host"],
ocean.integration_config["atlassian_user_email"],
ocean.integration_config["atlassian_user_token"],
)
client = create_jira_client()

params = {}
config = typing.cast(JiraIssueConfig, event.resource_config)
Expand All @@ -81,11 +83,7 @@ async def on_resync_issues(kind: str) -> ASYNC_GENERATOR_RESYNC_TYPE:

@ocean.on_resync(ObjectKind.USER)
async def on_resync_users(kind: str) -> ASYNC_GENERATOR_RESYNC_TYPE:
client = JiraClient(
ocean.integration_config["jira_host"],
ocean.integration_config["atlassian_user_email"],
ocean.integration_config["atlassian_user_token"],
)
client = create_jira_client()

async for users in client.get_paginated_users():
logger.info(f"Received users batch with {len(users)} users")
Expand All @@ -94,11 +92,7 @@ async def on_resync_users(kind: str) -> ASYNC_GENERATOR_RESYNC_TYPE:

@ocean.router.post("/webhook")
async def handle_webhook_request(data: dict[str, Any]) -> dict[str, Any]:
client = JiraClient(
ocean.integration_config["jira_host"],
ocean.integration_config["atlassian_user_email"],
ocean.integration_config["atlassian_user_token"],
)
client = create_jira_client()

webhook_event = data.get("webhookEvent")
if not webhook_event:
Expand All @@ -108,21 +102,58 @@ async def handle_webhook_request(data: dict[str, Any]) -> dict[str, Any]:
logger.info(f"Processing webhook event: {webhook_event}")

match webhook_event:
case event if event.startswith("user_"):
case event_data if event_data.startswith("user_"):
account_id = data["user"]["accountId"]
logger.debug(f"Fetching user with accountId: {account_id}")
item = await client.get_single_user(account_id)
kind = ObjectKind.USER
case event if event.startswith("project_"):
case event_data if event_data.startswith("project_"):
project_key = data["project"]["key"]
logger.debug(f"Fetching project with key: {project_key}")
item = await client.get_single_project(project_key)
kind = ObjectKind.PROJECT
case event if event.startswith("jira:issue_"):
case event_data if event_data.startswith("jira:issue_"):
issue_key = data["issue"]["key"]
logger.debug(f"Fetching issue with key: {issue_key}")
item = await client.get_single_issue(issue_key)
kind = ObjectKind.ISSUE
logger.info(
f"Fetching issue with key: {issue_key} and applying specified JQL filter"
)
resource_configs = cast(JiraPortAppConfig, event.port_app_config).resources

matching_resource_configs: list[JiraIssueConfig] = [
resource_config # type: ignore
for resource_config in resource_configs
if (
resource_config.kind == ObjectKind.ISSUE
and isinstance(resource_config.selector, JiraIssueSelector)
)
]

for config in matching_resource_configs:

params = {}

if config.selector.jql:
params["jql"] = (
f"{config.selector.jql} AND key = {data['issue']['key']}"
)
else:
params["jql"] = f"key = {data['issue']['key']}"

issues: list[dict[str, Any]] = []
async for issues in client.get_paginated_issues(params):
issues.extend(issues)

if not issues:
logger.warning(
f"Issue {data['issue']['key']} not found"
f" using the following query: {params['jql']},"
" trying to remove..."
)
await ocean.unregister_raw(ObjectKind.ISSUE, [data["issue"]])
else:
await ocean.register_raw(ObjectKind.ISSUE, issues)

return {"ok": True}
case _:
logger.error(f"Unknown webhook event type: {webhook_event}")
return {
Expand Down
2 changes: 1 addition & 1 deletion integrations/jira/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "jira"
version = "0.2.17"
version = "0.2.18"
description = "Integration to bring information from Jira into Port"
authors = ["Mor Paz <[email protected]>"]

Expand Down

0 comments on commit 895fa7e

Please sign in to comment.