Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 15 additions & 4 deletions src/backend/integrations/internal_mcp/catalogue.py
Original file line number Diff line number Diff line change
Expand Up @@ -36126,7 +36126,11 @@ def _normalise_jira_labels(raw_labels: Any) -> list[str]:

def _task_import_jira_issues(**kwargs):
"""Import Jira issues into Von tasks with dry-run and idempotent reruns."""
from .jira_proxy_mcp import get_jira_proxy, JiraProxyError
from .jira_proxy_mcp import (
get_jira_proxy,
jira_resource_binding_for_user,
JiraProxyError,
)
from ...services.jira_task_import_service import (
import_jira_issues_to_tasks,
list_imported_jira_issue_keys,
Expand Down Expand Up @@ -36250,12 +36254,19 @@ async def _fetch_jira_issues() -> dict[str, Any]:

if (
auto_map_namespace_to_jira_user
and isinstance(namespace_value, str)
and namespace_value.strip()
and hasattr(proxy, "get_myself")
):
try:
myself_payload = await proxy.get_myself()
# Bulk imports supply an actor without a legacy namespace.
# Map that actor only when the governed account binding proves
# ownership; a local operator can also import for somebody else.
map_current_user = bool(namespace_value) or bool(
actor_concept_id
and jira_resource_binding_for_user(actor_concept_id)
)
myself_payload = (
await proxy.get_myself() if map_current_user else None
)
account_id_value = (
myself_payload.get("accountId")
if isinstance(myself_payload, Mapping)
Expand Down
76 changes: 76 additions & 0 deletions tests/backend/test_internal_mcp_task_parity_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

from __future__ import annotations

import pytest

from src.backend.integrations.internal_mcp import build_default_catalogue
from src.backend.integrations.internal_mcp.gateway import InternalMCPGateway
from src.backend.integrations.internal_mcp.schemas import validate_payload
Expand Down Expand Up @@ -635,6 +637,80 @@ def _fake_import_jira_issues_to_tasks(**kwargs):
_assert_schema_conformance(gateway, "task_import_jira_issues", payload)


@pytest.mark.parametrize(
"owns_account,auto_map,explicit_mapping,expected",
[
(True, True, None, "#V#current_user"),
(False, True, None, None),
(True, False, None, None),
(True, True, "#V#recorded_person", "#V#recorded_person"),
],
)
def test_source_import_maps_actor_without_namespace_only_for_account_owner(
monkeypatch, owns_account, auto_map, explicit_mapping, expected
):
captured = {}
myself_calls = []

class Proxy:
async def get_myself(self):
myself_calls.append(True)
return {"accountId": "jira-current-user"}

async def get_proxy():
return Proxy()

async def capture_issue(*_args, **_kwargs):
return {"complete": True}, {
"key": "TEST-1",
"id": "1",
"fields": {"project": {"key": "TEST"}},
}

def import_issues(**kwargs):
captured.update(kwargs)
return {"success": True, "dry_run": True, "summary": {}, "issues": []}

monkeypatch.setattr(
"src.backend.integrations.internal_mcp.jira_proxy_mcp.get_jira_proxy",
get_proxy,
)
monkeypatch.setattr(
"src.backend.integrations.internal_mcp.jira_proxy_mcp.jira_resource_binding_for_user",
lambda actor: "jira-owned-account" if owns_account and actor == "#V#current_user" else None,
)
monkeypatch.setattr(
"src.backend.services.jira_source_retention_service.capture_issue",
capture_issue,
)
monkeypatch.setattr(
"src.backend.services.jira_task_import_service.import_jira_issues_to_tasks",
import_issues,
)
monkeypatch.setattr(
"src.backend.services.workflow_event_integration_service.resolve_event_actor_context",
lambda **_kwargs: ("#V#current_user", None),
)
payload = _build_gateway().invoke(
"task_import_jira_issues",
{
"issue_keys": ["TEST-1"],
"actor_concept_id": "#V#current_user",
"preserve_source": True,
"dry_run": True,
"auto_map_namespace_to_jira_user": auto_map,
"jira_account_id_to_concept_id": (
{"jira-current-user": explicit_mapping} if explicit_mapping else {}
),
},
).payload
assert payload.get("success") is True
assert captured["jira_account_id_to_concept_id"].get("jira-current-user") == expected
assert bool(myself_calls) == (owns_account and auto_map)
assert captured["actor_concept_id"] == "#V#current_user"
assert captured["namespace"] is None


def test_task_import_jira_issues_gateway_resolves_org_scope_for_backfill_and_import(
monkeypatch,
):
Expand Down
Loading