Skip to content

Commit

Permalink
Chore: Made event handling robust
Browse files Browse the repository at this point in the history
  • Loading branch information
lordsarcastic committed Nov 7, 2024
1 parent 72862ad commit e572c33
Show file tree
Hide file tree
Showing 5 changed files with 340 additions and 31 deletions.
2 changes: 1 addition & 1 deletion integrations/jira/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ async def get_single_project(self, project: str) -> dict[str, Any]:
return await self._get_single_item(f"{self.detail_base_url}/project/{project}")

async def get_single_issue(
self, issue: str, fields: dict[str, Any] = {}
self, issue: str, fields: dict[str, Any] = {}, jql: str | None = None
) -> dict[str, Any]:
return await self._get_single_item(f"{self.agile_url}/issue/{issue}")

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

from client import CREATE_UPDATE_WEBHOOK_EVENTS, DELETE_WEBHOOK_EVENTS, JiraClient
from integration import JiraIssueResourceConfig
from integration import JiraIssueResourceConfig, JiraIssueSelector, JiraPortAppConfig


class ObjectKind(StrEnum):
Expand Down Expand Up @@ -71,6 +71,7 @@ async def handle_webhook_request(data: dict[str, Any]) -> dict[str, Any]:
client = initialize_client()
webhook_event: str = data.get("webhookEvent", "")
logger.info(f"Received webhook event of type: {webhook_event}")
logger.info(f"Data: {data}")
ocean_action = None
delete_action = False

Expand All @@ -96,7 +97,41 @@ async def handle_webhook_request(data: dict[str, Any]) -> dict[str, Any]:
if delete_action:
issue = data["issue"]
else:
issue = await client.get_single_issue(data["issue"]["key"])
resource_configs = typing.cast(
JiraPortAppConfig, event.port_app_config
).resources
logger.info(resource_configs)

matching_resource_configs = [
resource_config
for resource_config in resource_configs
if (
resource_config.kind == ObjectKind.ISSUE
and isinstance(resource_config.selector, JiraIssueSelector)
)
]
logger.info(f"Matching resource configs: {matching_resource_configs}")

matching_resource_config = matching_resource_configs[0]
logger.info(f"Matching resource config: {matching_resource_config}")

config = typing.cast(
JiraIssueResourceConfig, matching_resource_config
).selector
params = {}
if config.jql:
params["jql"] = f"{config.jql} AND key = {data['issue']['key']}"
else:
params["jql"] = f"key = {data['issue']['key']}"
issues = await anext(client.get_all_issues(params))
if not issues:
logger.warning(
f"Issue {data['issue']['key']} not found."
"This is likely due to JQL filter"
)
await ocean.unregister_raw(ObjectKind.ISSUE, [data["issue"]])
return {"ok": True}
issue = issues[0]
await ocean_action(ObjectKind.ISSUE, [issue])
logger.info("Webhook event processed")
return {"ok": True}
Expand Down
53 changes: 51 additions & 2 deletions integrations/jira/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
from typing import Generator
import os
from typing import Any, Generator
from unittest.mock import MagicMock, patch

import pytest
from port_ocean import Ocean
from port_ocean.context.event import EventContext
from port_ocean.context.ocean import initialize_port_ocean_context
from port_ocean.exceptions.context import PortOceanContextAlreadyInitializedError
from port_ocean.tests.helpers.ocean_app import get_integration_ocean_app

from integration import JiraPortAppConfig

from .fixtures import ISSUES, PROJECTS

INTEGRATION_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "../"))


@pytest.fixture()
Expand All @@ -24,14 +33,54 @@ def mock_ocean_context() -> None:
pass


@pytest.fixture
@pytest.fixture(scope="session")
def mock_event_context() -> Generator[MagicMock, None, None]:
"""Fixture to mock the event context."""
mock_event = MagicMock(spec=EventContext)
mock_event.event_type = "test_event"
mock_event.trigger_type = "manual"
mock_event.attributes = {}
mock_event._aborted = False
mock_event._port_app_config = JiraPortAppConfig

with patch("port_ocean.context.event.event", mock_event):
yield mock_event


def app() -> Ocean:
config = {
"event_listener": {"type": "POLLING"},
"integration": {
"config": {
"jira_host": "https://getport.atlassian.net",
"atlassian_user_email": "[email protected]",
"atlassian_user_token": "asdf",
}
},
"port": {
"client_id": "bla",
"client_secret": "bla",
},
}
application = get_integration_ocean_app(INTEGRATION_PATH, config)
return application


@pytest.fixture
def ocean_app() -> Ocean:
return app()


@pytest.fixture(scope="session")
def integration_path() -> str:
return INTEGRATION_PATH


@pytest.fixture(scope="session")
def projects() -> list[dict[str, Any]]:
return PROJECTS


@pytest.fixture(scope="session")
def issues() -> list[dict[str, Any]]:
return ISSUES
242 changes: 242 additions & 0 deletions integrations/jira/tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -1089,3 +1089,245 @@
"properties": {},
},
]

PROJECT_WEBHOOK = {
"timestamp": 1730997478952,
# replace this as needed in test cases
"webhookEvent": "project_created",
"project": {
"self": "https://myorg.atlassian.net/rest/api/2/project/10002",
"id": 10002,
"key": "SEC",
"name": "Second",
"avatarUrls": {
"48x48": "https://myorg.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10415",
"24x24": "https://myorg.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10415?size=small",
"16x16": "https://myorg.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10415?size=xsmall",
"32x32": "https://myorg.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10415?size=medium",
},
"projectLead": {
"self": "https://myorg.atlassian.net/rest/api/2/user?accountId=5fdd28d8332c3a010744da75",
"accountId": "5fdd28d8332c3a010744da75",
"avatarUrls": {
"48x48": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"24x24": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"16x16": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"32x32": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
},
"displayName": "Baba Yetu",
"active": True,
"timeZone": "Africa/Khartoum",
"accountType": "atlassian",
},
"assigneeType": "admin.assignee.type.unassigned",
},
}

ISSUE_WEBHOOK = {
"timestamp": 1730998726980,
# replace this as needed in test cases
"webhookEvent": "jira:issue_updated",
"issue_event_type_name": "issue_assigned",
"user": {
"self": "https://myorg.atlassian.net/rest/api/2/user?accountId=5fdd28d8332c3a010744da75",
"accountId": "5fdd28d8332c3a010744da75",
"avatarUrls": {
"48x48": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"24x24": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"16x16": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"32x32": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
},
"displayName": "Baba Yetu",
"active": True,
"timeZone": "Africa/Khartoum",
"accountType": "atlassian",
},
"issue": {
"id": "10006",
"self": "https://myorg.atlassian.net/rest/api/2/10006",
"key": "PTP-7",
"fields": {
"statuscategorychangedate": "2024-09-05T02:59:02.700+0100",
"issuetype": {
"self": "https://myorg.atlassian.net/rest/api/2/issuetype/10001",
"id": "10001",
"description": "A small, distinct piece of work.",
"iconUrl": "https://myorg.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10318?size=medium",
"name": "Task",
"subtask": False,
"avatarId": 10318,
"entityId": "2bf26269-9628-45a4-a659-6f457adc9c43",
"hierarchyLevel": 0,
},
"timespent": None,
"customfield_10030": None,
"project": {
"self": "https://myorg.atlassian.net/rest/api/2/project/10000",
"id": "10000",
"key": "PTP",
"name": "Port Test Project",
"projectTypeKey": "business",
"simplified": True,
"avatarUrls": {
"48x48": "https://myorg.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10416",
"24x24": "https://myorg.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10416?size=small",
"16x16": "https://myorg.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10416?size=xsmall",
"32x32": "https://myorg.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10416?size=medium",
},
},
"customfield_10032": None,
"fixVersions": [],
"customfield_10033": None,
"aggregatetimespent": None,
"resolution": None,
"customfield_10035": None,
"customfield_10036": None,
"customfield_10037": None,
"customfield_10027": None,
"customfield_10028": None,
"customfield_10029": None,
"resolutiondate": None,
"workratio": -1,
"issuerestriction": {"issuerestrictions": {}, "shouldDisplay": True},
"watches": {
"self": "https://myorg.atlassian.net/rest/api/2/issue/PTP-7/watchers",
"watchCount": 1,
"isWatching": True,
},
"lastViewed": "2024-11-07T13:48:43.893+0100",
"created": "2024-09-05T02:59:02.314+0100",
"customfield_10020": None,
"customfield_10021": None,
"customfield_10022": None,
"priority": {
"self": "https://myorg.atlassian.net/rest/api/2/priority/3",
"iconUrl": "https://myorg.atlassian.net/images/icons/priorities/medium.svg",
"name": "Medium",
"id": "3",
},
"customfield_10023": None,
"customfield_10024": None,
"customfield_10025": None,
"customfield_10026": None,
"labels": [],
"customfield_10016": None,
"customfield_10017": None,
"customfield_10018": {
"hasEpicLinkFieldDependency": False,
"showField": False,
"NoneditableReason": {
"reason": "PLUGIN_LICENSE_ERROR",
"message": "The Parent Link is only available to Jira Premium users.",
},
},
"customfield_10019": "0|i00007:",
"timeestimate": None,
"aggregatetimeoriginalestimate": None,
"versions": [],
"issuelinks": [],
"assignee": {
"self": "https://myorg.atlassian.net/rest/api/2/user?accountId=5fdd28d8332c3a010744da75",
"accountId": "5fdd28d8332c3a010744da75",
"avatarUrls": {
"48x48": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"24x24": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"16x16": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"32x32": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
},
"displayName": "Baba Yetu",
"active": True,
"timeZone": "Africa/Khartoum",
"accountType": "atlassian",
},
"updated": "2024-11-07T17:58:46.980+0100",
"status": {
"self": "https://myorg.atlassian.net/rest/api/2/status/10000",
"description": "",
"iconUrl": "https://myorg.atlassian.net/",
"name": "Open",
"id": "10000",
"statusCategory": {
"self": "https://myorg.atlassian.net/rest/api/2/statuscategory/2",
"id": 2,
"key": "new",
"colorName": "blue-gray",
"name": "New",
},
},
"components": [],
"timeoriginalestimate": None,
"description": None,
"customfield_10010": None,
"customfield_10014": None,
"timetracking": {},
"customfield_10015": None,
"customfield_10005": None,
"customfield_10006": None,
"security": None,
"customfield_10007": None,
"customfield_10008": None,
"customfield_10009": None,
"aggregatetimeestimate": None,
"attachment": [],
"summary": "API documentation not displaying on backend home page",
"creator": {
"self": "https://myorg.atlassian.net/rest/api/2/user?accountId=5fdd28d8332c3a010744da75",
"accountId": "5fdd28d8332c3a010744da75",
"avatarUrls": {
"48x48": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"24x24": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"16x16": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"32x32": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
},
"displayName": "Baba Yetu",
"active": True,
"timeZone": "Africa/Khartoum",
"accountType": "atlassian",
},
"subtasks": [],
"reporter": {
"self": "https://myorg.atlassian.net/rest/api/2/user?accountId=5fdd28d8332c3a010744da75",
"accountId": "5fdd28d8332c3a010744da75",
"avatarUrls": {
"48x48": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"24x24": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"16x16": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
"32x32": "https://secure.gravatar.com/avatar/50f9a774bbe1e46f3a790ae4c077f7c1?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAA-2.png",
},
"displayName": "Baba Yetu",
"active": True,
"timeZone": "Africa/Khartoum",
"accountType": "atlassian",
},
"aggregateprogress": {"progress": 0, "total": 0},
"customfield_10001": None,
"customfield_10002": [],
"customfield_10003": None,
"customfield_10004": None,
"environment": None,
"duedate": None,
"progress": {"progress": 0, "total": 0},
"votes": {
"self": "https://myorg.atlassian.net/rest/api/2/issue/PTP-7/votes",
"votes": 0,
"hasVoted": False,
},
},
},
"changelog": {
"id": "10016",
"items": [
{
"field": "assignee",
"fieldtype": "jira",
"fieldId": "assignee",
"from": None,
"fromString": None,
"to": "5fdd28d8332c3a010744da75",
"toString": "Baba Yetu",
"tmpFromAccountId": None,
"tmpToAccountId": "5fdd28d8332c3a010744da75",
}
],
},
}
Loading

0 comments on commit e572c33

Please sign in to comment.