feat(jira): route API client through effective host for scoped tokens#1489
Merged
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
nhopeatall
approved these changes
Jul 10, 2026
nhopeatall
left a comment
Collaborator
There was a problem hiding this comment.
Summary
APPROVE — clean, well-scoped change that routes the jira.js client through the effective REST v3 host for scoped tokens while leaving classic basic-auth behavior byte-for-byte unchanged. Verified against all 7 work-item acceptance criteria; 63/63 tests pass locally and 7/7 CI checks are green.
Verification performed
- Complete method coverage: the private
getClient()is fully removed and all 22jiraClient.*methods nowawait getClientForRequest(). Confirmed there is exactly onenew Version3Client(...)construction insrc/, so every JIRA REST v3 call — including the wizard discovery path viajiraClient.searchProjects()/getFields()— funnels through the new routing. - Scoped path is reachable, not dormant:
authTypeflows from config at the runtime call sitesrc/pm/jira/integration.ts:71(withJiraCredentials({ email, apiToken, baseUrl, authType: jiraConfig?.authType }, fn)), so a project configured withauthType: 'scoped'actually exercises the gateway branch. - Default behavior unchanged: for basic/absent auth,
resolveJiraApiBaseUrlreturnscreds.baseUrlwith no network call — the "no fetch" test assertions confirm this. - Invariants hold:
getCloudId()stays on the site/_edge/tenant_infoURL,downloadAttachment()stays on absolute site URLs, andaddCommentReactiondegrades quietly (log + return) under scoped auth without ever failing the run. - Circular import (
client.ts↔api-host.ts) is benign — both cross-references run only inside function bodies at call time, never at module init, so no temporal-dead-zone risk. - Test hygiene: the new blocks correctly precede the
downloadAttachmentblock (the only one callingvi.restoreAllMocks()), preserving theVersion3Clientmock implementation. The constraint is documented in-code.
Auth-scheme decision (HTTP Basic for both modes) is soundly backed by the documented MNG-1735 live probes on the work item. No blocking or should-fix issues found.
🕵️ claude-code · claude-opus-4-8 · run details
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Routes the jira.js
Version3Clientthrough the effective REST v3 host so scoped API tokens reach the Atlassian gateway, while leaving classic (basic) site-token behavior unchanged.Implements MNG-1738: https://linear.app/mongrel/issue/MNG-1738/as-a-developer-i-want-the-jira-api-client-to-route-requests-through
Depends on (both already merged): MNG-1736 (
JiraCredentials.authType) and MNG-1737 (resolveJiraApiBaseUrl).What changed
src/jira/client.tsgetClient()(host: creds.baseUrl) with an asyncgetClientForRequest()that setshosttoawait resolveJiraApiBaseUrl(creds).basic/ absentauthType→ tenant site URL (default behavior, unchanged).authType === 'scoped'→ Atlassian gatewayhttps://api.atlassian.com/ex/jira/{cloudId}.jiraClient.*methods toawait getClientForRequest().authentication.basic(email / apiToken) for both modes — the MNG-1735 research note confirmed scoped API tokens authenticate via HTTP Basic against the gateway and Bearer did not authenticate as the user.authTypeselects the host, not the scheme. JSDoc documents the localizedoauth2.accessTokenfallback path should a future spike require it.getCloudId()is left on the site URL (${creds.baseUrl}/_edge/tenant_info) regardless ofauthType— it is how we discover the cloudId that scoped requests route through. Reinforced with a JSDoc invariant note.downloadAttachment()is left as-is — attachmentcontentURLs are absolute site URLs, so they are intentionally not routed through the gateway. Added an explanatory comment.addCommentReaction()degrades quietly under scoped auth: the internal/rest/reactions/1.0/API is not exposed through the scoped gateway, so underauthType === 'scoped'it logs one line (JIRA reactions are unavailable under scoped API tokens; skipping comment reaction) and returns — it never fails the run. Basic / absent auth keeps the existing site-URL behavior (and the existing throw-on-non-OK test stays green).tests/unit/jira/client.test.tseffective host routingdescribe block:authType→Version3Clientconstructed with the site host + Basic auth, no network call.authType→Version3Clientconstructed with the Atlassian gateway host + Basic auth (coveringgetIssue,updateIssue,createIssue), with cloudId discovered from the site/_edge/tenant_infoendpoint.getCloudId site-host invariantblock: still hits the site/_edge/tenant_infounder scoped auth.addCommentReaction scoped degradationblock: skips quietly (no fetch, no throw) and logs under scoped auth.Testing
npx vitest run --project unit-core tests/unit/jira/client.test.ts→ 63 passedtests/unit/jira/,tests/unit/pm/jira/) → 277 passeddownload-and-prepare→ passednpx tsc --noEmit→ cleanbiome checkon changed files → cleanOut of scope
Router ack-comment routing and dashboard webhook-management routing (separate stories).
🤖 Generated with Claude Code
🕵️ claude-code · claude-opus-4-8 · run details