Files
cve-dashboard/.kiro/specs/granite-loader-export/tasks.md
Jordan Ramos a61d254ff9 Sync .kiro/ from master — v2.2.0 release batch
New specs: archer-template-library, ccp-metrics-view-restructure,
compliance-list-stale-after-sidebar-edit, compliance-metric-estimated-resolution-date,
compliance-remediation-display-fix, flexible-jira-ticket-creation,
forecast-burndown-chart, granite-loader-export, ivanti-queue-clear-completed-fix,
multi-item-jira-ticket, queue-collapsible-sections, vendor-issue-type-dropdown

New steering: archer-template-gen.md

Updated: migration-registration-check hook, remediation-plan-history spec,
gitlab-workflow, tech, versioning steering files
2026-06-04 11:27:31 -06:00

152 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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"] }
]
}
```