Files
cve-dashboard/.kiro/specs/granite-loader-export/tasks.md

152 lines
8.7 KiB
Markdown
Raw Normal View History

# Implementation Plan: Granite Loader Sheet Export
## Overview
Add a Granite Team_Device Loader xlsx generator accessible from the Ivanti Queue (for CARD/GRANITE items) and as a standalone tool. The system enriches device data from the CARD API, allows bulk defaults with per-row overrides in an editable preview table, and generates a properly formatted xlsx for upload to SNIP XperLoad. Implementation proceeds from column configuration utility → backend enrichment endpoint → frontend modal → queue integration → standalone access.
## Tasks
- [ ] 1. Create column configuration utility module
- [ ] 1.1 Create `frontend/src/utils/graniteLoaderConfig.js`
- Define `LOADER_COLUMNS` array with all 41 columns: id, label (exact Granite header name), group, requiredFor array
- Define `COLUMN_GROUPS` ordered array for UI grouping
- Define `OPERATION_TYPES` array: Change, Add, Delete, Move
- Export `getRequiredColumns(operationType)` helper that returns column IDs required for the given operation
- Export `getColumnsByGroup(group)` helper that returns columns in a group
- _Requirements: 2.12.6, 3.13.5_
- [ ] 1.2 Create `frontend/src/utils/graniteLoaderExport.js`
- Export `generateLoaderXlsx(config)` function that accepts `{ operationType, columnIds, rows }` and returns a Blob
- Use the `xlsx` library (already in frontend dependencies) to create a workbook with a single "Load_Sheet" worksheet
- First row contains exact canonical column headers from LOADER_COLUMNS (matched by columnIds, in canonical order)
- Subsequent rows contain device data; empty values become empty cells (not "null")
- DELETE column auto-filled with "X" when operationType is "Delete"
- EQUIPMENT CLASS defaults to "S" unless overridden
- Export `generateFilename(operationType, teamName)` helper returning `Loader_{op}_{team}_{YYYY-MM-DD}.xlsx`
- _Requirements: 6.16.8_
- [ ] 2. Backend CARD enrichment endpoint
- [ ] 2.1 Add `POST /api/card/enrich-batch` endpoint in `backend/routes/cardApi.js`
- Accept `{ ips: string[] }` in request body
- Validate: ips is a non-empty array, max 200 items, each item is a non-empty string
- Require Admin or Standard_User group
- Require CARD API to be configured (return 503 if not)
- For each IP, attempt owner lookup with known suffixes (CTEC, NATL, CHTR, etc.)
- Extract from asset record: equip_inst_id (from ncim_discovery, netops_granite_allips, or ise_granite_equipment), hostname, site_name, mgmt_ip_asn, responsible_team, equipment_class, equip_template, equip_status
- Return `{ results: [...], enriched_count, not_found_count, total }`
- Handle per-IP errors gracefully (mark as not-found, continue with others)
- Handle CARD API auth failures (return 502 with error message)
- _Requirements: 5.15.8_
- [ ] 2.2 Add `GET /api/card/configured` endpoint (or extend existing `/api/card/status`)
- Return `{ configured: boolean }` so the frontend knows whether to show the "Enrich from CARD" option
- This may already exist as `GET /api/card/status` — verify and reuse if so
- _Requirements: 5.8_
- [ ] 3. Frontend LoaderModal component
- [ ] 3.1 Create `frontend/src/components/LoaderModal.js`
- Accept props: `isOpen`, `onClose`, `initialDevices` (array of `{ ip_address, hostname }` or null)
- Render modal overlay with header "Generate Granite Loader Sheet"
- Include Operation Type selector (dropdown, defaults to "Change")
- Include Column Selection panel with collapsible groups and checkboxes
- Required columns for selected operation are pre-checked and disabled
- Include "Enrich from CARD" button (hidden if CARD not configured, checked via `/api/card/status` on mount)
- Include Bulk Defaults section: one input per selected column, setting value applies to all non-overridden rows
- Include editable Preview Table: rows = devices, columns = selected columns
- Cells are inline-editable on click; overridden cells show amber dot indicator
- Right-click or clear button on overridden cell reverts to bulk default
- Sticky column headers and bulk default row when scrolling
- Validation: highlight missing required fields in red, show warning count
- Download button: merges bulk defaults + overrides into final row data, calls `generateLoaderXlsx`, triggers browser download
- Cancel button closes modal
- _Requirements: 1.2, 2.12.6, 3.13.5, 4.14.7, 6.16.8, 8.18.5_
- [ ] 3.2 Implement CARD enrichment flow in LoaderModal
- On "Enrich from CARD" click, collect all device IPs, POST to `/api/card/enrich-batch`
- Show progress indicator during request
- On response, populate enriched fields into device rows (equip_inst_id, hostname, site_name, mgmt_ip_asn, etc.)
- Do NOT overwrite values the user has already manually entered
- Show warning indicators on rows where IP was not found
- Show error toast if CARD API auth fails
- _Requirements: 5.15.7_
- [ ] 3.3 Implement standalone mode (paste IPs)
- When `initialDevices` is null, show a textarea for pasting IPs (one per line or comma-separated)
- Parse input into device rows on "Load" button click
- Allow manually adding/removing rows via + and trash icons
- _Requirements: 7.17.4_
- [ ] 4. Integrate with Ivanti Queue page
- [ ] 4.1 Add "Generate Loader Sheet" button to IvantiTodoQueuePage floating action bar
- Show button when one or more selected items have workflow_type CARD or GRANITE
- Button label: "Generate Loader Sheet" with a FileSpreadsheet icon
- On click, open LoaderModal with `initialDevices` populated from selected items' ip_address and hostname
- _Requirements: 1.11.5_
- [ ] 4.2 Add standalone access point
- Add "Granite Loader" link in the navigation drawer under Tools section (or similar)
- Clicking opens LoaderModal in standalone mode (initialDevices = null)
- Alternatively, add a "Generate Loader Sheet" button on the CARD status section if one exists
- _Requirements: 7.1_
- [ ] 5. Checkpoint — Verify build and basic functionality
- Build frontend: `cd frontend && npm run build`
- Verify no lint errors or build failures
- Ensure all existing tests still pass
- Ask the user if questions arise
- [ ]* 6. Property-based tests for enrichment endpoint
- [ ]* 6.1 Write property test: Enrichment result count
- **Property 1: Result count equals input count** — For any array of N IPs (1 ≤ N ≤ 200), the response `results` array has exactly N elements
- File: `backend/__tests__/granite-loader-enrichment.property.test.js`
- **Validates: Requirements 5.1, 5.2**
- [ ]* 6.2 Write property test: Found results have equip_inst_id
- **Property 2: Found results have required fields** — For any result where `found === true`, `equip_inst_id` is a non-empty string
- **Validates: Requirements 5.2**
- [ ]* 6.3 Write property test: Not-found results have null fields
- **Property 3: Not-found results have null equip_inst_id** — For any result where `found === false`, `equip_inst_id` is null
- **Validates: Requirements 5.4**
- [ ]* 7. Unit tests for xlsx generation
- [ ]* 7.1 Write unit tests for `generateLoaderXlsx`
- Test correct column headers in canonical order
- Test DELETE column auto-fill for Delete operation
- Test EQUIPMENT CLASS defaults to "S"
- Test empty values produce empty cells (not "null" string)
- Test bulk default + override merge produces correct row values
- File: `backend/__tests__/granite-loader-xlsx-generation.test.js`
- **Validates: Requirements 6.16.8**
- [ ] 8. Final checkpoint
- Build frontend and verify no regressions
- Ensure all tests pass
- Ask the user if questions arise
## Notes
- Tasks marked with `*` are optional property-based and unit tests that can be skipped for faster MVP
- The `xlsx` library is already a frontend dependency — no new packages needed for xlsx generation
- The CARD API enrichment reuses the existing `cardApi.js` helper (token management, TLS skip, etc.)
- No database schema changes are required — this feature reads from queue items and CARD API only
- The LoaderModal follows the same pattern as ConsolidationModal (modal overlay, form state, action buttons)
- The preview table follows the same inline-edit pattern as the Reporting page (click to edit, amber dot for overrides)
- Maximum 200 devices per batch aligns with CARD API pagination limits and practical XperLoad batch sizes
## Task Dependency Graph
```json
{
"waves": [
{ "id": 0, "tasks": ["1.1", "1.2"] },
{ "id": 1, "tasks": ["2.1", "2.2"] },
{ "id": 2, "tasks": ["3.1"] },
{ "id": 3, "tasks": ["3.2", "3.3"] },
{ "id": 4, "tasks": ["4.1", "4.2"] },
{ "id": 5, "tasks": ["5"] },
{ "id": 6, "tasks": ["6.1", "6.2", "6.3", "7.1"] }
]
}
```