Description
_find_issue_link_id (plugins/workflow/scripts/issue/jira/provider.py:1475-1495) locates the issuelinks entry to delete by selecting one of outwardIssue / inwardIssue based on the mapping direction:
direction_field = "outwardIssue" if operation.mapping.direction == "outward" else "inwardIssue"
target_issue = link.get(direction_field)
The Jira REST API populates only one of those two fields per link entry — the side that is not the current issue. So from the source issue's perspective, looking for the target on the field matching operation.mapping.direction can miss: when the target is on the opposite side from what the mapping nominally says, the populated field is the other one and the lookup returns None.
Practical effect: when a directional link (related, blocked_by, blocking, etc.) was created in a way that places the target on the inward side from the current issue's perspective, _find_issue_link_id returns None, and --remove-related <target> (or the other directional removes) fails with:
Jira issue link not found for <relationship> <target>
The link is visible in the issue's issuelinks payload, but the find function looks at the wrong direction field. The user has to remove the link manually in the Jira UI.
The bug affects every directional relationship that routes through _find_issue_link_id, not only related. The exact reproduction depends on which side of the link the source issue lives on at remove-time.
Reproduction
- Configure
providers.issues.relationship_mappings.related with a fixed direction (e.g., direction: outward).
- Add a
related link between two issues A and B (any creation path).
- From whichever of
A / B ends up on the opposite side of the link from the mapping direction, run:
"$WORKFLOW" jira_issue_relationships.py <that issue> --remove-related <other issue>
- Observe the error:
Jira issue link not found for related <other issue> — even though the link is present in the issue's issuelinks payload.
A regression repro does not require live Jira: invoke _find_issue_link_id directly with a synthesized issue payload that mirrors a real Jira response (single populated direction field per link entry).
Unit Test Strategy
Unit test _find_issue_link_id directly. Cover the four combinations of (mapping.direction, populated field):
| mapping.direction |
populated field in link |
should match |
| outward |
outwardIssue (target on outward side from source) |
yes |
| outward |
inwardIssue (target on inward side from source) |
yes |
| inward |
outwardIssue |
yes |
| inward |
inwardIssue |
yes |
The function should match the target when either populated field's key equals the target and the link type matches, regardless of mapping direction.
Acceptance Criteria
Affected Paths
plugins/workflow/scripts/issue/jira/provider.py:1475-1495 (_find_issue_link_id)
plugins/workflow/tests/ (new regression coverage)
Resume
- Approach.
_find_issue_link_id now inspects both outwardIssue and inwardIssue after the type.name check and returns the link id for whichever side carries the target. operation.mapping.direction is preserved on the create path; only the read filter on remove changed. Regression coverage in plugins/workflow/tests/test_workflow_jira_issue_link_lookup.py exercises all four (direction, populated field) combinations plus link-type and target-key negative cases — the two cross-direction cases failed on the unfixed code and pass on the fix; full workflow suite (508 tests) green.
- Waiting for. —
- Open questions. None.
- Next. —
Description
_find_issue_link_id(plugins/workflow/scripts/issue/jira/provider.py:1475-1495) locates theissuelinksentry to delete by selecting one ofoutwardIssue/inwardIssuebased on the mapping direction:The Jira REST API populates only one of those two fields per link entry — the side that is not the current issue. So from the source issue's perspective, looking for the target on the field matching
operation.mapping.directioncan miss: when the target is on the opposite side from what the mapping nominally says, the populated field is the other one and the lookup returnsNone.Practical effect: when a directional link (
related,blocked_by,blocking, etc.) was created in a way that places the target on the inward side from the current issue's perspective,_find_issue_link_idreturnsNone, and--remove-related <target>(or the other directional removes) fails with:The link is visible in the issue's
issuelinkspayload, but the find function looks at the wrong direction field. The user has to remove the link manually in the Jira UI.The bug affects every directional relationship that routes through
_find_issue_link_id, not onlyrelated. The exact reproduction depends on which side of the link the source issue lives on at remove-time.Reproduction
providers.issues.relationship_mappings.relatedwith a fixed direction (e.g.,direction: outward).relatedlink between two issuesAandB(any creation path).A/Bends up on the opposite side of the link from the mapping direction, run:Jira issue link not found for related <other issue>— even though the link is present in the issue'sissuelinkspayload.A regression repro does not require live Jira: invoke
_find_issue_link_iddirectly with a synthesized issue payload that mirrors a real Jira response (single populated direction field per link entry).Unit Test Strategy
Unit test
_find_issue_link_iddirectly. Cover the four combinations of(mapping.direction, populated field):outwardIssue(target on outward side from source)inwardIssue(target on inward side from source)outwardIssueinwardIssueThe function should match the target when either populated field's key equals the target and the link type matches, regardless of mapping direction.
Acceptance Criteria
_find_issue_link_idmatches links whose target appears in eitheroutwardIssueorinwardIssue, not only the field tied tooperation.mapping.direction.--remove-related,--remove-blocked-by,--remove-blocking, and any other directional removes succeed regardless of which side of the link the source issue was on at create time.(direction, populated field)combinations using synthesized payloads.Affected Paths
plugins/workflow/scripts/issue/jira/provider.py:1475-1495(_find_issue_link_id)plugins/workflow/tests/(new regression coverage)Resume
_find_issue_link_idnow inspects bothoutwardIssueandinwardIssueafter thetype.namecheck and returns the link id for whichever side carries the target.operation.mapping.directionis preserved on the create path; only the read filter on remove changed. Regression coverage inplugins/workflow/tests/test_workflow_jira_issue_link_lookup.pyexercises all four(direction, populated field)combinations plus link-type and target-key negative cases — the two cross-direction cases failed on the unfixed code and pass on the fix; full workflow suite (508 tests) green.