Repository navigation
feat(admin): /admin/integrations, the Jira setup page - #246
Merged
Merged
Conversation
selluxhenner
added a commit
that referenced
this pull request
Oct 29, 2026
WP-A of `docs/plans/2026-10-29_jira-in-app.md`: the Jira sync engine, moved out of n8n into the app. Builds on the contract (#245), the admin page (#246) and the lead UI (#247), which are all on main. **What it does** - `server/jira-sync.ts`: a loop once a minute (`JIRA_SYNC_INTERVAL_MS` overrides it, the e2e run uses 2000) for each company with Jira on. It takes the lease, pushes, then pulls, and records the result on the Integration row. An hourly reconcile reads every linked issue. `kickJira(slug)` starts a round early; it is called after a person's project event (and a `case.decided`), and from the webhook. - `server/jira/round.ts`: one round. It only knows a `RoundStore`, so the unit tests run whole rounds against the fake Jira with an in-memory log. - Pure core in `features/integrations/jira/`: `map` (issue key to project/step, sync mode), `plan` (state and events to jobs), `pull` (Jira JSON to events), `status` (transition picking, hopping through In Progress when the workflow has no direct way), `adf`. - `server/jira/cloud.ts` (API v3, Basic auth, `/search/jql` with `nextPageToken`, one inline wait on a short 429), `dc.ts` (stub that says "not supported yet"), `test.ts` (`testJira`, a failed call is a failed check). - `POST /api/[company]/integrations/jira/webhook`: HMAC check, then only `kickJira`. The header name (`x-hub-signature`) is marked TBD in `server/jira/webhook.ts`; confirm it on the live site. - One-line hooks in `instrumentation.ts` and `server/actions/events.ts`. **Echo-loop rules, as built** 1. Push acts only on human actors (`isSystemActor`), and the last move of a step in a batch decides. 2. Pull skips changes whose latest history is by the integration account, and writes nothing when NextUp already shows the state (Blocked against In Progress counts as the same). 3. idemKeys `jira:epic:<projectId>`, `jira:issue:<stepId>`, `jira:<key>:status:<historyId>`, `jira:<key>:summary:<historyId>`, `jira:added:<key>`, `jira:comment:<id>`. Creates look for the label first and adopt an existing issue. 4. At most 20 creates per round. A round with nothing to do writes no events and makes no write call to Jira (`jira-sync.test.ts` runs 5 silent rounds after each scenario). **Decisions worth a look** - The first round after switch-on does not replay old events (`pulledAt` null and `pushedSeq` 0); creates come from the state, not the cursor. - Imported comments read `Jira NUP-2: <text>` (cut to 2000), with no author and mentions as `@someone`. - A failed job that Jira refused for good (no transition, a 4xx) is reported as `lastError` but does not hold the cursor; transient failures (401, 429, 5xx, no answer) end the round and move nothing. - Added beyond the file list: `server/jira/round.ts` and `server/jira/webhook.ts`, so the engine and the HMAC check are testable without a database. - Not covered here: the e2e-db spec and the live test on `nextup-test.atlassian.net`; and the Labels check in "Test connection" can only prove the create/edit permission and that JQL on labels parses, not that the create screen has the labels field. **Run the fake Jira:** `node tests/fake-jira/server.mjs --port 4010` (Node 22.18 or newer). It also takes `POST /__fake/{move,rename,comment,child,429}`. Checked: typecheck, eslint, `npm test` (88 files, 945 tests). `next build` was not run.
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.
Part of the Jira-in-app plan (
docs/plans/2026-10-29_jira-in-app.md), WP-B: the admin setup page. #245 (the contract) is merged. This branch also carries the engine's public surface (feat/jira-engine, a2ab924:testJira,kickJira), because the page imports them; those files drop out of the diff once the engine PR lands.What it adds
/admin/integrations(company stack only; the platform admin gets a 404, like/admin/assistant):testJiraand lists eachJiraCheck. The epic and step type dropdowns fill from its answer; Epic/Epos and Task/Aufgabe are preselected by name.enabledAt), Sync now, last round, last error (401/403 shown as "token expired"), linked projects and steps, and the projects/steps still without a Jira twin, read from the log.Notes for review
components/admin/AdminNav.tsx(an icon for the newAdminPageid; the type forces it) andserver/admin-context.ts(reads the Jira row for the nav badge; failure is swallowed so the layout never breaks).features/admin/jira-admin.ts: form to settings, webhook URL, link summary, headline. Tests are intests/unit/admin-jira.test.ts, andadmin-nav.test.tscovers the new nav entry and badge rules.integrationContext; the actions are admin-only and limited to the stack's own company (same guard asactions/m365.ts).JiraApihas no "list statuses" call, so there is nothing to fill a dropdown from.updatedAt: settings and token are saved together.Checks
tsc --noEmit,eslint,stylelintandvitest run(837 tests) pass. Not run:next build, and the page has not been looked at in a browser (needs a company stack with a database).