Plan — BJ-9: JiraService (issue-oriented surface)#
Summary#
Add a JiraService (NestJS injectable) exposing 11 issue-oriented methods backed by the Atlassian REST API v3, authenticated via PlatformAuthProvider, with automatic 429 retry and 401-refresh. Register it under the JIRA_SERVICE token via a factory in a new JiraModule.
Files#
| Path | Action | Purpose |
|---|---|---|
src/jira/jira.service.ts | create | Injectable service implementing the 11 public methods + private request() helper |
src/jira/jira.module.ts | create | Registers JIRA_SERVICE factory; imports ConfigModule + PlatformAuthProvider source module |
src/jira/index.ts | create | Barrel export (JiraService, JiraModule, JIRA_SERVICE) |
src/jira/jira.service.spec.ts | create | Unit tests: method shapes, 429 retry, 401 refresh, config error on missing JIRA_CLOUD_ID |
src/config/config.schema.ts | modify | Add JIRA_CLOUD_ID: Joi.string().required() |
src/app.module.ts | modify | Add JiraModule to imports |
.env.example | modify | Document JIRA_CLOUD_ID |
CLAUDE.md | modify | Add jira to Key modules list and JIRA_CLOUD_ID to env var table |
Steps#
- Add
JIRA_CLOUD_ID(required string) tosrc/config/config.schema.tsso startup fails fast when unset. - Implement
src/jira/jira.service.ts: injectPlatformAuthProvidertoken +ConfigService; resolvebaseUrl = https://api.atlassian.com/ex/jira/${JIRA_CLOUD_ID}; privaterequest<T>(method, path, body?)that attachesAuthorization: Bearer <await authProvider.getToken()>andAccept: application/json; on 429 sleep perRetry-Afterand retry once; on 401 re-fetch token once and retry (propagate if the retry also 401s). - Implement the 11 public methods using REST v3 endpoints:
postComment/editComment(/rest/api/3/issue/{key}/comment[/{id}]),transitionIssue/getTransitions(/rest/api/3/issue/{key}/transitions),addLabel/removeLabel(PUT/rest/api/3/issue/{key}withupdate.labels),setAssignee/setPriority(PUT/rest/api/3/issue/{key}withfields),linkIssues(/rest/api/3/issueLink),searchJql(/rest/api/3/search/jql),getIssue(/rest/api/3/issue/{key}). - Create
src/jira/jira.module.tswith a factory provider forJIRA_SERVICE(useFactory: (auth, config) => new JiraService(auth, config),inject: [PLATFORM_AUTH_PROVIDER, ConfigService]); import the module that exportsPLATFORM_AUTH_PROVIDER(see open question) andConfigModule; exportJIRA_SERVICE+JiraService. - Register
JiraModuleinsrc/app.module.tsimports. - Write
jira.service.spec.tswith mockedPlatformAuthProvider(getTokenreturns"tok1"then"tok2") andfetchstub; cover: method URL/body shape for all 11 methods, 429 →Retry-Afterhonored and one retry, 401 →getTokencalled twice then retry, config error whenJIRA_CLOUD_IDmissing,getTransitionsempty list returns[],searchJqlzero results returns[]. - Update
.env.exampleandCLAUDE.mdto documentJIRA_CLOUD_IDand the newjiramodule.
Verification#
npm run test— all new specs pass; existing suites unaffected.npm run lint— zero warnings (strict mode; noany).npm run build— compiles cleanly.
Risks#
- BJ-3/4/5 dependency token shape is unknown. Waves are in flight; the Atlassian auth provider may be registered under
PLATFORM_AUTH_PROVIDER(reusing GitHub's) or under a newATLASSIAN_AUTH_PROVIDERtoken. We will import whichever module exports the Atlassian-backedPlatformAuthProvideronrc/atlassian-integrationat implementation time. If none is merged yet, provide a minimal inline stub provider per AGENTS.md Wave-dependency guidance and remove it when BJ-3/4/5 land.
Open Questions#
- Does BJ-3/4/5 expose the Atlassian auth under the existing
PLATFORM_AUTH_PROVIDERtoken, or a new Atlassian-specific token? Resolved by inspecting the branch at implementation time.