feat: add FP attachment library — attach existing CVE documents to FP submissions
- Add GET /api/ivanti/fp-workflow/documents/search endpoint for querying the document library - Update POST /api/ivanti/fp-workflow to accept libraryDocIds for attaching library documents on create - Update POST .../submissions/:id/attachments to accept libraryDocIds on edit - Add AttachmentSourcePicker component with local upload and library search modes - Integrate picker into FpWorkflowModal (create) and FpEditModal (edit) - Track attachment source (local/library) in attachment_results_json for traceability
This commit is contained in:
95
.kiro/specs/fp-attachment-library/tasks.md
Normal file
95
.kiro/specs/fp-attachment-library/tasks.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Implementation Plan: FP Attachment Library
|
||||
|
||||
## Overview
|
||||
|
||||
This plan implements the FP Attachment Library feature, which allows users to attach existing CVE document library files to FP workflow submissions alongside traditional local file uploads. The implementation adds a new Document Search API endpoint, modifies two existing backend endpoints to handle library document references, and creates a shared AttachmentSourcePicker component used in both the create and edit modals.
|
||||
|
||||
## Tasks
|
||||
|
||||
- [x] 1. Add Document Search API endpoint
|
||||
- [x] 1.1 Add `GET /api/documents/search` route in `backend/routes/ivantiFpWorkflow.js`
|
||||
- Add a new GET route handler for `/documents/search` inside `createIvantiFpWorkflowRouter`
|
||||
- Accept optional `q` query parameter for search term
|
||||
- When `q` is provided, query the `documents` table with `LIKE` matching against `name`, `cve_id`, and `vendor` columns (case-insensitive)
|
||||
- When `q` is empty or missing, return the most recent documents ordered by `uploaded_at DESC`
|
||||
- Limit results to 50 records maximum
|
||||
- Return each record with fields: `id`, `cve_id`, `vendor`, `name`, `type`, `file_size`, `mime_type`, `uploaded_at`
|
||||
- Protect with `requireAuth(db)` middleware
|
||||
- Return 500 with `{ error: 'Database error.' }` on DB failure
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6_
|
||||
|
||||
- [x] 2. Modify backend to handle library document attachments on create
|
||||
- [x] 2.1 Update `POST /api/ivanti/fp-workflow` in `backend/routes/ivantiFpWorkflow.js` to accept `libraryDocIds`
|
||||
- Parse `libraryDocIds` from `req.body` as a JSON-encoded array (default to `[]` if absent)
|
||||
- Return 400 if `libraryDocIds` is not valid JSON
|
||||
- Validate each ID is a positive integer; return 400 identifying any invalid ID
|
||||
- Query the `documents` table for all referenced IDs; return 400 if any ID is not found
|
||||
- Read each library file from disk using `fs.readFileSync(file_path)`; if a file is missing on disk, log a warning and include `{ success: false, error: 'File not found on disk', source: 'library', documentId: id }` in attachment results, skip that file
|
||||
- Combine local file buffers (`req.files`) and library file buffers into a single `formFiles` array passed to `ivantiFormPost`
|
||||
- Record attachment results with `source: "local"` for uploaded files and `source: "library"` plus `documentId` for library files
|
||||
- Use the `name` field from the `documents` record as the `filename` in attachment results for library files
|
||||
- _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7_
|
||||
|
||||
- [x] 3. Modify backend to handle library document attachments on edit
|
||||
- [x] 3.1 Update `POST /api/ivanti/fp-workflow/submissions/:id/attachments` in `backend/routes/ivantiFpWorkflow.js` to accept `libraryDocIds`
|
||||
- Apply the same `libraryDocIds` parsing, validation, disk-read, and combined upload logic as task 2.1
|
||||
- Combine local file buffers and library file buffers into a single `formFiles` array for the Ivanti API call
|
||||
- Record attachment results with `source` and `documentId` fields matching the create endpoint behavior
|
||||
- _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7_
|
||||
|
||||
- [x] 4. Checkpoint — Verify backend changes
|
||||
- Ensure all backend changes are syntactically correct and consistent with existing patterns. Ask the user if questions arise.
|
||||
|
||||
- [x] 5. Create AttachmentSourcePicker component
|
||||
- [x] 5.1 Implement `AttachmentSourcePicker` inline in `frontend/src/components/pages/ReportingPage.js`
|
||||
- Define the component above `FpWorkflowModal` in the file
|
||||
- Accept props: `files`, `onFilesChange`, `libraryDocs`, `onLibraryDocsChange`, `disabled`
|
||||
- Implement a mode toggle with two tab-style buttons: "Local Upload" and "Library" (default to "Local Upload")
|
||||
- In Local Upload mode, render the existing drag-and-drop zone with file input, file validation (extension + size), and file list
|
||||
- In Library mode, render a search input that queries `GET /api/documents/search?q=...` with 300ms debounce using `setTimeout`/`clearTimeout`
|
||||
- Display search results in a scrollable list showing document name, CVE ID, vendor, and file size
|
||||
- Show already-selected library documents as disabled/checked in search results to prevent duplicates
|
||||
- When a search result is clicked, add it to `libraryDocs` via `onLibraryDocsChange` (skip if already selected by `id`)
|
||||
- When a library doc is removed from the attachment list, re-enable it in search results
|
||||
- Render a unified attachment list below the mode-specific UI showing all attachments (local + library)
|
||||
- Each attachment row displays: source badge ("Local" or "Library"), filename, file size, and a remove button (Trash2 icon)
|
||||
- Library attachment rows additionally display CVE ID and vendor name
|
||||
- Disable all interactions when `disabled` prop is true
|
||||
- Style consistently with existing modal components using inline style objects, monospace font, dark theme colors from DESIGN_SYSTEM.md
|
||||
- Handle network errors on search by showing an inline error message in the results area
|
||||
- Show "No documents found" when search returns empty results
|
||||
- _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 5.1, 5.2, 5.3, 6.1, 6.2, 6.3, 6.4_
|
||||
|
||||
- [x] 6. Integrate AttachmentSourcePicker into FpWorkflowModal (create flow)
|
||||
- [x] 6.1 Replace the file upload section in `FpWorkflowModal` with `AttachmentSourcePicker`
|
||||
- Add `libraryDocs` state (`useState([])`) alongside existing `files` state
|
||||
- Reset `libraryDocs` to `[]` when modal opens (in the existing `useEffect` on `open`)
|
||||
- Replace the current drag-and-drop zone and file list section with `<AttachmentSourcePicker>` passing `files`, `setFiles`, `libraryDocs`, `setLibraryDocs`, and `disabled={submitting}`
|
||||
- Remove the inline `addFiles`, `removeFile`, `handleDrop`, `handleDragOver` functions and `fileInputRef`/`dropRef` refs (these are now handled inside AttachmentSourcePicker)
|
||||
- On submit, append `libraryDocIds` as a JSON string to the FormData: `formData.append('libraryDocIds', JSON.stringify(libraryDocs.map(d => d.id)))`
|
||||
- Update the progress message to reflect combined attachment count
|
||||
- Update the result view to show source badges on attachment results (use `source` field, default to `"local"` for backward compatibility)
|
||||
- _Requirements: 2.1, 2.2, 2.3, 2.5, 2.6, 2.7_
|
||||
|
||||
- [x] 7. Integrate AttachmentSourcePicker into FpEditModal (edit flow)
|
||||
- [x] 7.1 Replace the static "upload in Ivanti" message on the attachments tab with `AttachmentSourcePicker`
|
||||
- Add `libraryDocs` state (`useState([])`) alongside existing `files` state
|
||||
- Reset `libraryDocs` to `[]` when submission changes (in the existing `useEffect` on `submission`)
|
||||
- Keep the existing attachment display section (showing attachments from initial submission) above the picker
|
||||
- Render `<AttachmentSourcePicker>` below existing attachments, passing `files`, `setFiles`, `libraryDocs`, `setLibraryDocs`, and `disabled={isApproved}`
|
||||
- Update `handleUploadAttachments` to build FormData with both local files and `libraryDocIds` JSON field
|
||||
- Enable the upload button when either `files.length > 0` or `libraryDocs.length > 0`
|
||||
- Disable the picker when `lifecycle_status === 'approved'`
|
||||
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6_
|
||||
|
||||
- [x] 8. Final checkpoint — Verify all changes
|
||||
- Ensure all changes are complete and consistent across backend and frontend. Ensure no hanging or orphaned code. Ask the user if questions arise.
|
||||
|
||||
## Notes
|
||||
|
||||
- No testing tasks included per user request — testing will be done on the dev server
|
||||
- The project uses plain JavaScript (no TypeScript) throughout
|
||||
- All frontend styling uses inline style objects consistent with the existing dark theme design system
|
||||
- The `documents` table already exists — no database migrations are needed
|
||||
- The `libraryDocIds` field is optional in both endpoints, preserving full backward compatibility
|
||||
- Existing `attachment_results_json` entries without a `source` field are treated as `"local"` by the frontend
|
||||
Reference in New Issue
Block a user