In ReportPortal, a failure's issue can carry a set of `externalSystemIssues`. What does a single `ExternalSystemIssue` entry hold, and what does attaching one change about that failure?
answer
- a pointer, not a mirror
- nested inside the issue, not the item
- six fields, four of them required
- nothing on it carries the ticket's state
basics
~10 sOne externalSystemIssues entry records where a tracker item lives: ticketId, btsUrl, btsProject, a direct url, plus submitDate and pluginName. It carries no ticket status. Attaching one labels the failure without changing its defect type.
solid answer
~40 sIn ReportPortal, a test item's `Issue` carries `externalSystemIssues`, a set of `ExternalSystemIssue` entries - a nested class declared inside `Issue`, not a standalone type. Each entry has six fields: `ticketId`, `submitDate`, `btsUrl`, `btsProject`, `url` and `pluginName`. Four of them - `ticketId`, `btsUrl`, `btsProject` and `url` - must be non-blank; `submitDate` and `pluginName` may be omitted. Notice what is not there: no status, no summary, no resolution. The link is a pointer, not a mirror. Attaching one is also orthogonal to the defect type the failure already carries: the issue keeps its type and comment, and the ticket set hangs beside them. The practical effect is that the failure now names the work item somebody opened for it, and a reader can go straight from the red result to the tracker.
code
json · 13 lines{
"issueType": "pb001",
"comment": "Recurring checkout timeout",
"externalSystemIssues": [
{
"ticketId": "TEAM-4417",
"btsUrl": "https://tracker.example.com",
"btsProject": "TEAM",
"url": "https://tracker.example.com/browse/TEAM-4417",
"pluginName": "example-bts"
}
]
}go deeper
Be able to name the six fields and say plainly that none of them is a status. Recall that the entry lives on the issue rather than the test item, and that one failure can carry several of them at once.
Explain which fields are required and which are optional, why btsUrl and btsProject are stored separately from url, and that the server keeps one record per ticket key and joins it to every issue naming it.
Show you know the consequence in production: because nothing on the entry ages or refreshes, the age of a link is often your only signal about whether it is still true, and finding dead ones takes a sweep you build yourself.
Own the question of whether ticket links are data your organisation migrates. A tracker rename or instance move leaves every stored link carrying the old spelling, and nothing in the server rewrites them for you.
## Where the entry lives In ReportPortal a failed test item's verdict is an `Issue`: the defect type it was placed in, a free-text comment field, and a set named `externalSystemIssues`. Each member of that set is an `ExternalSystemIssue`, and the first thing to get right is that this is **not a standalone type**. It is declared as a nested `public static class` inside `Issue`, so an entry only ever exists as part of an issue. There is no top-level class of that name to import and no way to hold one on its own - which also means a ticket link is attached to a *verdict about a failure*, never to the raw test item. ## The six fields | field | what it holds | must be set | |---|---|---| | `ticketId` | the tracker's own key for the item | yes | | `btsUrl` | base URL of the tracker instance | yes | | `btsProject` | the project inside that instance | yes | | `url` | a direct link straight to the item | yes | | `submitDate` | when the link was attached | no | | `pluginName` | which plugin implementation serves that tracker | no | Four of the six are required and must be non-blank. The two optional ones behave differently from each other: `submitDate` is filled in with the current time when a caller omits it, so a stored link ends up carrying one either way; `pluginName` simply stays empty if nobody supplied it. Note that `btsUrl`, `btsProject` and `url` are three separate strings, not one derived from the others. `url` is the human link a report renders. `btsUrl` and `btsProject` together are the machine key: they identify *which configured tracker integration* this link belongs to. ## What is deliberately absent Read the field list again for what is not there: - no status or workflow state - no summary or title - no assignee - no resolution and no close date - no severity and no priority The stored entry is a **pointer, not a mirror**. ReportPortal can ask a tracker for an item's current summary and state through the configured plugin, but that is a live call made while somebody is looking at the screen, and its answer is not written back into the stored entry. Nothing about the six fields changes when the tracker item moves, is renamed, or is closed. That is the single most important consequence of this shape, and it is where most misreadings start: people read "has a ticket link" as "is being worked on", when the artefact only says *somebody once decided this failure belongs with that key*. ## What attaching one changes, and what it does not Attaching a link is **orthogonal to the defect type**. The issue keeps whatever type and comment it already had, and the ticket set hangs beside them. So a failure can sit in a defect group *and* name a tracker item, or sit in a group with no ticket at all, or - because `externalSystemIssues` is a set, not a single field - name several tracker items at once, which is what you want when one failure is genuinely waiting on two separate fixes. Three mechanics matter in practice: 1. **Links are stored once and shared.** The server keeps one record per ticket and joins it to every issue that names it. A key attached across fifty failures is one stored record with fifty links, not fifty copies of the same strings. 2. **Re-attaching an id reuses the existing record.** Rather than duplicating it, the server looks the key up, reuses what it finds, and refreshes that record's URL and plugin fields from the newest request. Two people linking the same key with slightly different URLs therefore end up with last-write-wins on the URL every reader sees. 3. **Linking is a bulk operation.** The server-side handler takes a list of issues and a list of links and wires them together in one pass - which is exactly the shape you need to label a whole bucket of failures with one action rather than clicking through them one at a time. ## Reading it back When you consume a failure's `externalSystemIssues`, use `url` for the link you render to a human, and use the `btsUrl` plus `btsProject` pair when you need to reach the tracker programmatically. `submitDate` is the only time information the entry carries, so it is your only handle on how old a link is. Given that nothing else about the entry ages or updates, the age of a link is frequently the most useful signal available about whether it is still true - which is an uncomfortable thing to discover the first time you audit a project's links and find keys attached years ago to failures nobody has looked at since.
- Two testers attach the same ticket key to different failures, with slightly different URLs. What ends up stored?One shared record, not two. The server looks the key up, reuses the record it finds, and refreshes that record's URL and plugin fields from the newest request - so the later write decides the URL every reader sees, on both failures. The links themselves remain separate joins onto that one record.
- Can one failure name more than one tracker item?Yes. `externalSystemIssues` is a set, not a single field, so a failure can carry several `ExternalSystemIssue` entries at once. That is the honest representation when a failure is genuinely waiting on two separate fixes, rather than picking one ticket and quietly dropping the other.
saying these in an interview costs you the question
- Saying the entry mirrors the ticket's current status
- Calling ExternalSystemIssue a standalone top-level class
- Thinking attaching a link changes the failure's defect type
- Assuming url can be derived from btsUrl and ticketId
- Reading a ticket link as proof the failure is being worked on