feat: support showing risks from evidence - #282
Conversation
feat: better evidence forms Signed-off-by: Gustavo Carvalho <gustavo.carvalho@container-solutions.com>
📝 WalkthroughWalkthroughEvidence forms now support structured properties, links, expiry, and re-submission. Evidence views add lazy-loaded risk association and creation. Lineage risk nodes gain SSP-aware routing and grouped rendering ranked by risk severity. ChangesEvidence workflows
SSP-aware lineage
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant EvidenceView
participant RiskCreateForm
participant EvidenceAPI
EvidenceView->>EvidenceAPI: Load risks and SSP options
EvidenceView->>RiskCreateForm: Open create-risk dialog
RiskCreateForm->>EvidenceView: Emit created risk
EvidenceView->>EvidenceAPI: Link risk to evidence
EvidenceView->>EvidenceAPI: Refresh associated risks
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR enhances the UI for evidence and lineage views by (1) adding an “Associated Risks” tab and “Create Risk from Evidence” flow, and (2) improving lineage risk handling by scoping risk-detail navigation and grouping risk nodes by owning SSP.
Changes:
- Add an Evidence “Risks” tab that loads associated risks on-demand and supports creating + linking a new risk to an evidence stream.
- Group lineage “risk” nodes by owning SSP in the graph view and add SSP-scoped routing for risk detail navigation.
- Improve evidence create/update UX (re-submit terminology, richer form sections, expiry date, properties/links editors).
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/views/lineage/ranking.ts | Adds helpers to split structural nodes and bucket risk nodes into SSP groups. |
| src/views/lineage/LineageTreeView.vue | Updates node selection routing to pass an SSP id into nodeDetailRoute. |
| src/views/lineage/LineageGraphView.vue | Renders structural nodes + SSP risk buckets and updates detail routing calls. |
| src/views/lineage/tests/ranking.spec.ts | Adds unit tests for structuralNodes and riskGroups. |
| src/views/lineage/tests/LineageGraphView.spec.ts | Adds a smoke test verifying SSP risk grouping display/order. |
| src/views/evidence/ViewView.vue | Adds “Risks” tab UI, lazy loading of risks, and risk creation/linking dialog. |
| src/views/evidence/UpdateView.vue | Updates copy to “Re-submit Evidence” and clarifies revision semantics. |
| src/views/evidence/partial/EvidenceForm.vue | Restructures evidence form, adds expiry, properties, and links editors; uses uuid() helper. |
| src/views/evidence/CreateView.vue | Removes client-side UUID prefill (now generated inside form). |
| src/views/evidence/tests/ViewView.spec.ts | Extends tests to cover risks tab behavior and PrimeVue toast setup. |
| src/composables/useLineage/types.ts | Adds sspId/sspTitle to lineage nodes (for risk ownership). |
| src/composables/useLineage/fixtures.ts | Populates fixture risks with sspId/sspTitle. |
| src/components/risk/RiskCreateForm.vue | Adds likelihood/impact selection and uses them in risk creation payload. |
| src/components/lineage/nodeMeta.ts | Extends nodeDetailRoute to optionally return SSP-scoped risk detail routes. |
| src/components/lineage/tests/nodeMeta.spec.ts | Adds test coverage for SSP-scoped risk routing. |
| src/components/forms/PropsEditor.vue | New editor component for OSCAL props. |
| src/components/forms/LinksEditor.vue | New editor component for OSCAL links. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/views/lineage/LineageGraphView.vue (1)
300-375: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winExtract the duplicated node-box template into a shared component.
The structural node box (L300-328) and the risk node box (L344-371) render identical markup — same
LineageNodeRow, children/leaf indicator, details button, selection ring, andsetBoxRefwiring. The only difference is thev-forsource (structuralNodes(col)vsgroup.nodes). Future changes to node rendering will require updating both locations, risking divergence.Extract a
LineageNodeBoxcomponent acceptingnode,colIndex, andselectedKeyas props, emittingboxClickanddetailsClickevents.♻️ Proposed extraction
+ <!-- LineageNodeBox.vue --> + <script setup lang="ts"> + import LineageNodeRow from '`@/components/lineage/LineageNodeRow.vue`'; + import { nodeCardClass } from '`@/components/lineage/nodeMeta`'; + import type { LineageNode } from '`@/composables/useLineage/types`'; + + defineProps<{ + node: LineageNode; + colIndex: number; + selectedKey: string | null; + }>(); + + const emit = defineEmits<{ + boxClick: [colIndex: number, node: LineageNode]; + detailsClick: [node: LineageNode]; + }>(); + </script> + + <template> + <div + class="cursor-pointer rounded-lg border border-l-4 border-surface-200 p-3 transition hover:shadow-md dark:border-surface-700" + :class="[ + nodeCardClass(node), + selectedKey === node.key + ? 'ring-2 ring-primary ring-offset-1 dark:ring-offset-surface-900' + : '', + ]" + `@click`="emit('boxClick', colIndex, node)" + > + <LineageNodeRow :node="node" card /> + <div + class="mt-1 flex items-center justify-between pl-5 text-xs text-surface-500 dark:text-surface-400" + > + <span v-if="node.hasChildren">{{ node.childrenCount }} children ›</span> + <span v-else class="italic">leaf</span> + <button + class="rounded px-1.5 py-0.5 hover:bg-surface-200 dark:hover:bg-surface-700" + `@click.stop`="emit('detailsClick', node)" + > + details + </button> + </div> + </div> + </template>Then in
LineageGraphView.vue, replace both sections:- <div - v-for="node in structuralNodes(col)" - :key="node.key" - :ref="(el) => setBoxRef(i, node.key, el)" - class="cursor-pointer rounded-lg border border-l-4 border-surface-200 p-3 transition hover:shadow-md dark:border-surface-700" - :class="[ - nodeCardClass(node), - col.selectedKey === node.key - ? 'ring-2 ring-primary ring-offset-1 dark:ring-offset-surface-900' - : '', - ]" - `@click`="onBoxClick(i, node)" - > - <LineageNodeRow :node="node" card /> - <div - class="mt-1 flex items-center justify-between pl-5 text-xs text-surface-500 dark:text-surface-400" - > - <span v-if="node.hasChildren">{{ node.childrenCount }} children ›</span> - <span v-else class="italic">leaf</span> - <button - class="rounded px-1.5 py-0.5 hover:bg-surface-200 dark:hover:bg-surface-700" - `@click.stop`="openDetails(node)" - > - details - </button> - </div> - </div> + <LineageNodeBox + v-for="node in structuralNodes(col)" + :key="node.key" + :node="node" + :col-index="i" + :selected-key="col.selectedKey" + :ref="(el) => setBoxRef(i, node.key, el?.$el : null)" + `@box-click`="onBoxClick" + `@details-click`="openDetails" + />And similarly for the risk node loop inside each SSP group.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/views/lineage/LineageGraphView.vue` around lines 300 - 375, Extract the duplicated node-card markup into a shared LineageNodeBox component accepting node, colIndex, and selectedKey props and emitting boxClick and detailsClick events. Move the existing nodeCardClass, selection-ring class, setBoxRef wiring, LineageNodeRow, child/leaf indicator, and details button behavior into that component, then replace both the structuralNodes(col) and risk group.nodes templates in LineageGraphView with it while preserving their existing loops and event behavior.src/components/risk/RiskCreateForm.vue (1)
302-310: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win
createdemit is typed as OSCALRisk, but the actual response has a different shape — forces an unsafe cast downstream.
returnedRisk(useDataApi<Risk>) and thecreated: [risk: Risk]emit type both claim an OSCALRisk, but the SSP-scoped register-create endpoint actually returns a register risk with anidfield.src/views/evidence/ViewView.vue'sonRiskCreatedhas to work around this withrisk as unknown as { id?: string }(documented via a comment there). Typing the response/emit against the real register-risk shape would remove the need for that cast and prevent future unsafe casts by other consumers.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/risk/RiskCreateForm.vue` around lines 302 - 310, Update the register-risk creation flow in RiskCreateForm, including the useDataApi response type and created emit payload, to use the actual register-risk shape containing the id field rather than OSCAL Risk. Propagate that type to consumers such as onRiskCreated so they access the typed id directly and remove the unsafe cast.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/forms/LinksEditor.vue`:
- Around line 50-57: Update the TertiaryButton class in LinksEditor.vue and the
corresponding button class in PropsEditor.vue to use Tailwind v4 trailing
important syntax, changing !px-1 to px-1! while preserving the existing padding
utility.
In `@src/components/lineage/nodeMeta.ts`:
- Around line 432-444: Update nodeDetailRoute’s risk branch to prefer node.sspId
when selecting the SSP route parameter, falling back to the provided sspId only
when the node has no SSP identifier. Preserve the existing unscoped risks:detail
route when neither value is available.
In `@src/views/evidence/__tests__/ViewView.spec.ts`:
- Around line 666-727: Extend the risks-tab tests around mountView and
clickButtonByText to set refs.risksError, assert the “Failed to load risks”
message, and verify loadEvidenceRisks is retried after switching away from and
back to the Risks tab following the failure. Ensure the test covers the failed
request without marking the current evidence UUID as successfully loaded.
In `@src/views/evidence/partial/EvidenceForm.vue`:
- Around line 305-329: Update the propsList and linksList initialization in the
evidence form to deep-clone each Property and Link element rather than only
copying the outer arrays. Ensure PropsEditor and LinksEditor mutate form-local
objects, leaving props.evidence and the fetched source data unchanged until
submission.
In `@src/views/evidence/ViewView.vue`:
- Around line 1305-1322: Update refreshEvidenceRisks so risksLoadedForUuid is
assigned the current streamUuid only after loadEvidenceRisks completes
successfully. Keep the existing UUID validation and ensureEvidenceRisksLoaded
retry behavior unchanged, allowing failed requests to be attempted again.
---
Outside diff comments:
In `@src/components/risk/RiskCreateForm.vue`:
- Around line 302-310: Update the register-risk creation flow in RiskCreateForm,
including the useDataApi response type and created emit payload, to use the
actual register-risk shape containing the id field rather than OSCAL Risk.
Propagate that type to consumers such as onRiskCreated so they access the typed
id directly and remove the unsafe cast.
In `@src/views/lineage/LineageGraphView.vue`:
- Around line 300-375: Extract the duplicated node-card markup into a shared
LineageNodeBox component accepting node, colIndex, and selectedKey props and
emitting boxClick and detailsClick events. Move the existing nodeCardClass,
selection-ring class, setBoxRef wiring, LineageNodeRow, child/leaf indicator,
and details button behavior into that component, then replace both the
structuralNodes(col) and risk group.nodes templates in LineageGraphView with it
while preserving their existing loops and event behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 20d2b7f7-3653-4a59-813d-19abdba69e83
📒 Files selected for processing (17)
src/components/forms/LinksEditor.vuesrc/components/forms/PropsEditor.vuesrc/components/lineage/__tests__/nodeMeta.spec.tssrc/components/lineage/nodeMeta.tssrc/components/risk/RiskCreateForm.vuesrc/composables/useLineage/fixtures.tssrc/composables/useLineage/types.tssrc/views/evidence/CreateView.vuesrc/views/evidence/UpdateView.vuesrc/views/evidence/ViewView.vuesrc/views/evidence/__tests__/ViewView.spec.tssrc/views/evidence/partial/EvidenceForm.vuesrc/views/lineage/LineageGraphView.vuesrc/views/lineage/LineageTreeView.vuesrc/views/lineage/__tests__/LineageGraphView.spec.tssrc/views/lineage/__tests__/ranking.spec.tssrc/views/lineage/ranking.ts
gusfcarvalho
left a comment
There was a problem hiding this comment.
(Would be REQUEST_CHANGES — GitHub blocks that on your own PR, so filing as COMMENT.) Findings inline. Nice PR overall — clean editor extraction, good defensive nodeMeta/ranking helpers, solid test coverage.
Medium: lineage nodeDetailRoute routes risk nodes through the active scope's SSP rather than the risk's own node.sspId (which this PR added and uses everywhere else), sending cross-SSP risks to the wrong/guessed SSP — your own ViewView test documents the correct behavior ("the risk's own SSP, not the active one").
Low: premature risksLoadedForUuid caching blocks retry after a failed risk load.
Out-of-scope note: re-submit drops existing backMatter attachments while keeping their #resource links, but that's pre-existing, not introduced here.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/views/evidence/ViewView.vue (1)
1338-1348: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAdd user feedback when
riskIdis missing inonRiskCreated.The
id?: stringtype explicitly accounts for the API not returning anid, but whenriskIdisundefinedthe function closes the dialog and returns silently — the risk was created but not linked, with no toast or indication to the user.🛡️ Proposed fix: warn the user before returning
if (!riskId || !sspId || !streamUuid) { + toast.add({ + severity: 'warn', + summary: 'Risk not linked', + detail: + 'The risk was created, but it could not be linked to this evidence.', + life: 5000, + }); return; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/views/evidence/ViewView.vue` around lines 1338 - 1348, The onRiskCreated function currently returns silently when riskId is missing, leaving the created risk unlinked without user feedback. Before the early return for a missing riskId, show a warning toast or equivalent user notification explaining that the risk was created but could not be linked; preserve the existing return behavior for missing sspId or streamUuid.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/views/evidence/ViewView.vue`:
- Around line 1338-1348: The onRiskCreated function currently returns silently
when riskId is missing, leaving the created risk unlinked without user feedback.
Before the early return for a missing riskId, show a warning toast or equivalent
user notification explaining that the risk was created but could not be linked;
preserve the existing return behavior for missing sspId or streamUuid.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 0ec0e2e0-884b-4114-8f1c-a6a312555f91
📒 Files selected for processing (8)
src/components/forms/LinksEditor.vuesrc/components/forms/PropsEditor.vuesrc/components/lineage/__tests__/nodeMeta.spec.tssrc/components/lineage/nodeMeta.tssrc/components/risk/RiskCreateForm.vuesrc/views/evidence/ViewView.vuesrc/views/evidence/__tests__/ViewView.spec.tssrc/views/evidence/partial/EvidenceForm.vue
feat: better evidence forms
Summary by CodeRabbit
New Features
Bug Fixes