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
1 change: 1 addition & 0 deletions src/backend/services/jira_task_import_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -1672,6 +1672,7 @@ def _resolve_existing_task_id(
source_system=_JIRA_SOURCE_SYSTEM,
external_id=jira_issue_key,
organisation_concept_id=organisation_concept_id,
include_details=False,
)
task_id = None
if isinstance(existing_task, Mapping):
Expand Down
15 changes: 12 additions & 3 deletions src/backend/services/task_management_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -5435,7 +5435,9 @@ def find_task_by_external_reference(
source_system: str,
external_id: str,
organisation_concept_id: str | None = None,
include_details: bool = True,
) -> Dict[str, Any] | None:
"""Resolve the same visible task with optional response construction."""
source_key = _normalise_external_reference_source_system(source_system)
external_value = _normalise_external_reference_id(external_id)

Expand All @@ -5451,6 +5453,11 @@ def find_task_by_external_reference(
],
}
doc: Dict[str, Any] | None = None
lookup_options = (
{}
if include_details
else {"projection": {"concept_id": 1, "relationships.is_an_instance_of": 1}}
)
if organisation_concept_id:
org_id = _normalise_optional_concept_id(organisation_concept_id)
if not org_id:
Expand All @@ -5459,18 +5466,20 @@ def find_task_by_external_reference(
)
scoped_query = dict(query_base)
scoped_query[f"metadata.{TASK_METADATA_KEY_ORGANISATION}"] = org_id
doc = ConceptsRepository.find_one(scoped_query)
doc = ConceptsRepository.find_one(scoped_query, **lookup_options)
if not isinstance(doc, dict) or not _is_task_doc(doc):
# Legacy imports were often unscoped; allow a constrained fallback
# to null/absent organisation scope for idempotent repair runs.
legacy_query = dict(query_base)
legacy_query[f"metadata.{TASK_METADATA_KEY_ORGANISATION}"] = None
doc = ConceptsRepository.find_one(legacy_query)
doc = ConceptsRepository.find_one(legacy_query, **lookup_options)
else:
doc = ConceptsRepository.find_one(query_base)
doc = ConceptsRepository.find_one(query_base, **lookup_options)

if not isinstance(doc, dict) or not _is_task_doc(doc):
return None
if not include_details:
return {"task_concept_id": doc.get("concept_id")}
return _build_task_response(doc)


Expand Down
44 changes: 44 additions & 0 deletions tests/backend/test_task_management_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2100,6 +2100,50 @@ def _fake_get_texts(


class TestTaskExternalReferences:
@patch("src.backend.services.task_management_service._build_task_response")
@patch("src.backend.services.task_management_service.ConceptsRepository")
def test_identity_lookup_preserves_alias_and_legacy_scope_without_hydration(
self, mock_repo: MagicMock, build_response: MagicMock
) -> None:
mock_repo.find_one.side_effect = [
None,
{
"concept_id": "#V#legacy_task",
"relationships": {"is_an_instance_of": [TASK_SPECIFICATION_TYPE_ID]},
},
]
result = find_task_by_external_reference(
source_system="jira",
external_id="OLD-1",
organisation_concept_id="#V#org",
include_details=False,
)
assert result == {"task_concept_id": "#V#legacy_task"}
build_response.assert_not_called()
calls = mock_repo.find_one.call_args_list
assert [call.args[0]["metadata.organisation_concept_id"] for call in calls] == [
"#V#org", None
]
for call in calls:
assert {"metadata.external_references.jira.previous_issue_keys": "OLD-1"} in (
call.args[0]["$or"]
)
assert call.kwargs["projection"] == {
"concept_id": 1, "relationships.is_an_instance_of": 1
}

@pytest.mark.parametrize("doc", [None, {"concept_id": "#V#not_a_task"}])
@patch("src.backend.services.task_management_service._build_task_response")
@patch("src.backend.services.task_management_service.ConceptsRepository")
def test_identity_lookup_does_not_return_missing_or_non_task_records(
self, mock_repo: MagicMock, build_response: MagicMock, doc
) -> None:
mock_repo.find_one.return_value = doc
assert find_task_by_external_reference(
source_system="jira", external_id="PROJ-1", include_details=False
) is None
build_response.assert_not_called()

@patch("src.backend.services.task_management_service.ConceptsRepository")
@patch("src.backend.services.task_management_service.get_texts_for_concept")
def test_find_task_by_external_reference_returns_task(
Expand Down
Loading