In ReportPortal, a stored ticket link carries `btsUrl` and `btsProject` rather than the id of a configured integration. What does that pair resolve to when the server needs the tracker, and why must the integration sit in the `BTS` group?
answer
- no foreign key on the link
- the stored pair is a lookup key
- project integration before global one
- group checked at use, not at save
- durable but inert
basics
~20 sThe btsUrl and btsProject pair is a lookup key: ReportPortal resolves it to a configured integration, the project's own before a global one, and rejects any integration whose type is not in the BTS group.
solid answer
~40 sReportPortal stores a ticket link as plain strings - `ticketId`, `btsUrl`, `btsProject`, `url`, `pluginName` - with no foreign key to an integration. When it actually has to reach the tracker it resolves the `btsUrl` and `btsProject` pair against the integrations configured for that project, falling back to a global one, and then checks that the resolved integration's type belongs to the `BTS` group of `IntegrationGroupEnum` (`BTS`, `NOTIFICATION`, `AUTH`, `OTHER`, `IMPORT`). An integration from any other group is refused, which is what stops a mail or authentication integration being handed tracker work because its URL happened to match. The consequence is that a link is durable but inert: the strings keep rendering after the integration is deleted or its URL changes, and only the operations that need the tracker begin to fail.
go deeper
Recall that the link holds URL and project strings rather than a reference, and that ReportPortal sorts its integrations into groups with bug trackers in the BTS group.
Walk the resolution: match the stored pair against the project's integrations, fall back to global, then verify the group. Explain why a URL spelling difference alone is enough to break the match.
Diagnose the real symptom - old links stop working after a tracker move while new ones are fine - and say that stored links are rows you have to migrate, because nothing rewrites them for you.
Decide whether tracker instance and project names are stable enough to serve as a join key across your estate, and what the migration story is for stored links when they are not.
## The link is text, not a foreign key A stored ticket link in ReportPortal holds `btsUrl`, `btsProject`, `url`, `ticketId` and `pluginName` - five strings. What it does **not** hold is a reference to the integration row that configures the tracker. There is no integration id on the link at all. That is a deliberate choice with consequences in both directions, and understanding it explains most of the surprises people hit when a tracker is moved or reconfigured. ## Resolving the pair When the server actually has to talk to the tracker - to fetch an item's current summary, or to file a new one - it does not follow a pointer. It performs a lookup, using the `btsUrl` and `btsProject` pair as the key: 1. Look for an integration configured **on that project** whose URL and linked project match the pair. 2. Failing that, look among the **global** integrations for the same match. 3. Whichever is found, check that its type belongs to the `BTS` group before using it. That last step is not a formality. `IntegrationGroupEnum` sorts every integration ReportPortal knows about into exactly five groups - `BTS`, `NOTIFICATION`, `AUTH`, `OTHER` and `IMPORT` - and an integration resolved for ticket work is rejected outright if its type sits in any group other than `BTS`. The group is the type system for integrations: it is what stops a mail integration or an authentication provider being handed a request to create a defect just because its URL happened to match. ## What `pluginName` adds `pluginName` records which plugin implementation serves that tracker. It is the optional sixth field on the link, and it is descriptive rather than authoritative - the resolution above goes through the integration, and the plugin follows from the integration's type. Its practical value is on the read side: given a set of links spanning several trackers, `pluginName` tells you which family each one came from without re-resolving anything. ## Why the design, and what it costs | property | consequence | |---|---| | link stores strings, not an id | it survives the integration being deleted or replaced | | link stores strings, not an id | it silently stops resolving when the tracker's URL changes | | `(btsUrl, btsProject)` is the key | two projects on one tracker instance are distinct links | | `BTS` group is checked on resolve | a mis-grouped integration fails at use time, not config time | The single sentence worth remembering is that **a link is durable but inert**. Nothing about the stored strings depends on the integration still existing. Delete the integration and every link keeps rendering exactly as before: the `url` still opens in a browser, `ticketId` still reads correctly, `btsProject` still names a project. Only the operations that need to *reach* the tracker start failing, and they fail at the moment somebody tries one, not at the moment the configuration changed. ## The failure modes this produces - **The tracker moves to a new hostname.** Every previously stored link keeps its old `btsUrl`, so none of them resolve to the new integration any more, while the raw `url` strings may or may not redirect. New links resolve; old ones do not; nothing announces the split. - **A project-scoped integration is added over a global one.** Resolution prefers the project's own, so behaviour for identical links can differ between two projects on the same server. - **An integration is re-created with a different URL spelling.** A trailing slash or an `http`-versus-`https` difference is enough to make the pair stop matching, because the match is on the stored strings. - **A tracker project is renamed.** `btsProject` is part of the key, so the pair no longer resolves, even though the tracker itself is perfectly healthy. ## What to do about it Treat `btsUrl` and `btsProject` as data you own and may need to migrate, not as opaque provenance. If a tracker instance or project is being renamed, the stored links are part of the migration: they are rows carrying the old spelling, and nothing in the server will rewrite them for you. And when you are diagnosing "the tracker button does nothing" on an old failure, check the pair against the configured integrations first - the most common answer is not a broken plugin but a link whose key no longer matches anything in the `BTS` group.
- The tracker moves to a new hostname and the integration is updated. What happens to links stored before the move?They keep their old `btsUrl`, so the pair no longer matches the updated integration and none of them resolve. Newly created links resolve fine, so the project ends up with two populations behaving differently. Nothing announces the split - you find it when somebody clicks a tracker action on an old failure.
- Why check the group at resolution time rather than refusing the link when it is stored?Because storing a link does not require an integration at all - the fields are just strings, and a link can outlive or predate any configuration. The group check belongs where the integration is actually used, which is the only point at which a wrong group could do damage.
saying these in an interview costs you the question
- Saying the stored link holds an integration id
- Assuming deleting an integration deletes or breaks stored links
- Thinking any configured integration can serve ticket operations
- Believing a global integration wins over a project-scoped one
- Treating pluginName as the thing that resolves the tracker