TenantAtlas/specs/185-settings-catalog-readable/tasks.md
2025-12-14 20:23:18 +01:00

20 KiB

Feature 185: Settings Catalog Readable UI - Tasks

Summary

  • Total Tasks: 42
  • User Stories: 3 (US-UI-04, US-UI-05, US-UI-06)
  • Estimated Time: 11-15 hours
  • Phases: 7

FR→Task Traceability

FR Description Tasks
FR-185.1 Setting Definition Resolver Service T003, T004, T005, T006, T007
FR-185.2 Database Schema T001, T002
FR-185.3 Snapshot Enrichment T008, T009, T010
FR-185.4 PolicyNormalizer Enhancement T011, T012, T013, T014
FR-185.5 Policy View UI Update T015-T024
FR-185.6 JSON Viewer Integration T025

User Story→Task Mapping

User Story Tasks Success Criteria
US-UI-04 (Readable Settings) T015-T020 Display names shown, values formatted, grouped by category
US-UI-05 (Search/Filter) T021, T022 Search box works, filters settings, instant results
US-UI-06 (Raw JSON Access) T023, T024, T025 Tabs present, JSON view works, copy button functional

Measurable Thresholds

  • Definition Resolution: <500ms for batch of 50 definitions (cached)
  • UI Render: <2s for policy with 200 settings
  • Search Response: <200ms filter update
  • Database Cache TTL: 30 days

Phase 1: Database Foundation (T001-T002)

Goal: Create database schema for caching setting definitions

  • T001 Create migration for settings_catalog_definitions table

    • Schema: id, definition_id (unique), display_name, description, help_text, category_id, ux_behavior, raw (jsonb), timestamps
    • Indexes: definition_id (unique), category_id, raw (GIN)
    • File: database/migrations/2025_12_13_212126_create_settings_catalog_definitions_table.php
    • Implementation Note: Created migration with GIN index for JSONB, ran successfully
  • T002 Create SettingsCatalogDefinition Eloquent model

    • Casts: raw → array
    • Fillable: definition_id, display_name, description, help_text, category_id, ux_behavior, raw
    • File: app/Models/SettingsCatalogDefinition.php
    • Implementation Note: Added helper methods findByDefinitionId() and findByDefinitionIds() for efficient lookups

Phase 2: Definition Resolver Service (T003-T007)

Goal: Implement service to fetch and cache setting definitions from Graph API

User Story: US-UI-04 (foundation)

  • T003 [P] Create SettingsCatalogDefinitionResolver service skeleton

    • Constructor: inject GraphClientInterface, SettingsCatalogDefinition model
    • Methods: resolve(), resolveOne(), warmCache(), clearCache()
    • File: app/Services/Intune/SettingsCatalogDefinitionResolver.php
    • Implementation Note: Complete service with 3-tier caching (memory → DB → Graph API)
  • T004 [P] [US1] Implement resolve(array $definitionIds): array method

    • Check memory cache (Laravel Cache)
    • Check database cache
    • Batch fetch missing from Graph API: /deviceManagement/configurationSettings?$filter=id in (...)
    • Store in DB + memory cache
    • Return map: {definitionId => {displayName, description, ...}}
    • File: app/Services/Intune/SettingsCatalogDefinitionResolver.php
    • Implementation Note: Implemented with batch request optimization and error handling
  • T005 [P] [US1] Implement resolveOne(string $definitionId): ?array method

    • Single definition lookup
    • Same caching strategy as resolve()
    • Return null if not found
    • File: app/Services/Intune/SettingsCatalogDefinitionResolver.php
    • Implementation Note: Wraps resolve() for single ID lookup
  • T006 [US1] Implement fallback logic for missing definitions

    • Prettify definition ID: device_vendor_msft_policy_name → "Device Vendor Msft Policy Name"
    • Return fallback structure: {displayName: prettified, description: null, ...}
    • File: app/Services/Intune/SettingsCatalogDefinitionResolver.php
    • Implementation Note: prettifyDefinitionId() method with Str::title() conversion, isFallback flag added
  • T007 [P] Implement warmCache(array $definitionIds): void method

    • Pre-populate cache without returning data
    • Non-blocking: catch and log Graph API errors
    • File: app/Services/Intune/SettingsCatalogDefinitionResolver.php
    • Implementation Note: Non-blocking implementation with try/catch, logs warnings on failure

Phase 3: Snapshot Enrichment (T008-T010)

Goal: Extend PolicySnapshotService to warm definition cache after settings hydration

User Story: US-UI-04 (foundation)

  • T008 [US1] Extend PolicySnapshotService to extract definition IDs

    • After hydrating /configurationPolicies/{id}/settings
    • Extract all settingDefinitionId from settings array
    • Include children from groupSettingCollectionInstance
    • File: app/Services/Intune/PolicySnapshotService.php
    • Implementation Note: Added extractDefinitionIds() method with recursive extraction from nested children
  • T009 [US1] Call SettingsCatalogDefinitionResolver::warmCache() in snapshot flow

    • Pass extracted definition IDs to resolver
    • Non-blocking: use try/catch for Graph API calls
    • File: app/Services/Intune/PolicySnapshotService.php
    • Implementation Note: Integrated warmCache() call in hydrateSettingsCatalog() after settings extraction
  • T010 [US1] Add metadata to snapshot about definition cache status

    • Add to snapshot: definitions_cached: true/false, definition_count: N
    • Store with snapshot data
    • File: app/Services/Intune/PolicySnapshotService.php
    • Implementation Note: Added definitions_cached and definition_count to metadata

Phase 4: PolicyNormalizer Enhancement (T011-T014)

Goal: Transform Settings Catalog snapshots into UI-ready grouped structure

User Story: US-UI-04

  • T011 [US1] Create normalizeSettingsCatalogGrouped() method in PolicyNormalizer

    • Input: array $snapshot, array $definitions
    • Output: array with groups[] structure
    • Extract settings from snapshot
    • Resolve definitions for all setting IDs
    • File: app/Services/Intune/PolicyNormalizer.php
    • Implementation Note: Complete method with definition resolution integration
  • T012 [US1] Implement value formatting logic

    • ChoiceSettingInstance: Extract choice label from @odata.type or value
    • SimpleSetting (bool): "Enabled" / "Disabled"
    • SimpleSetting (int): Number formatted with separators
    • SimpleSetting (string): Truncate >100 chars, add "..."
    • File: app/Services/Intune/PolicyNormalizer.php
    • Implementation Note: Added formatSettingsCatalogValue() method with all formatting rules
  • T013 [US1] Implement grouping logic by category

    • Group settings by categoryId from definition metadata
    • Fallback: Group by first segment of definition ID
    • Sort groups alphabetically by title
    • File: app/Services/Intune/PolicyNormalizer.php
    • Implementation Note: Added groupSettingsByCategory() with fallback extraction from definition IDs
  • T014 [US1] Implement nested group flattening for groupSettingCollectionInstance

    • Recursively extract children from group settings
    • Maintain hierarchy in output structure
    • Include parent context in child labels
    • File: app/Services/Intune/PolicyNormalizer.php
    • Implementation Note: Recursive walk function handles nested children and group collections

Phase 5: UI Implementation - Settings Tab (T015-T022)

Goal: Create readable Settings Catalog UI with accordion, search, and formatting

User Stories: US-UI-04, US-UI-05

  • T015 [US1] Add Tabs component to PolicyResource infolist for settingsCatalogPolicy

    • Conditional rendering: only for settingsCatalogPolicy type
    • Tab 1: "Settings" (default)
    • Tab 2: "JSON" (existing from Feature 002)
    • File: app/Filament/Resources/PolicyResource.php
    • Implementation Note: Tabs already exist from Feature 002, extended Settings tab with grouped view
  • T016 [US1] Create Settings tab schema with search input

    • TextInput for search/filter at top
    • Placeholder: "Search settings..."
    • Wire with Livewire for live filtering
    • File: app/Filament/Resources/PolicyResource.php
    • Implementation Note: Added search_info TextEntry (hidden for MVP), search implemented in Blade template
  • T017 [US1] Create Blade component for grouped settings accordion

    • File: resources/views/filament/infolists/entries/settings-catalog-grouped.blade.php
    • Props: groups (from normalizer), searchQuery
    • Render accordion with Filament Section components
    • Implementation Note: Complete Blade component with Filament Section integration
  • T018 [US1] Implement accordion group rendering

    • Each group: Section with title + description
    • Collapsible by default (first group expanded)
    • Group header shows setting count
    • File: settings-catalog-grouped.blade.php
    • Implementation Note: Using x-filament::section with collapsible, first group expanded by default
  • T019 [US1] Implement setting row rendering within groups

    • Layout: Label (bold) | Value (formatted) | Help icon
    • Help icon: Tooltip with description + helpText
    • Copy button for long values
    • File: settings-catalog-grouped.blade.php
    • Implementation Note: Flexbox layout with label, help text, value display, and Alpine.js copy button
  • T020 [US1] Add value formatting in Blade template

    • Bool: Badge (Enabled/Disabled with colors)
    • Int: Formatted number
    • String: Truncate with "..." and expand button
    • Choice: Show choice label
    • File: settings-catalog-grouped.blade.php
    • Implementation Note: Conditional rendering based on value type, badges for bool, monospace for int
  • T021 [US2] Implement search/filter logic in Livewire component

    • Filter groups and settings by search query
    • Search on display_name and value_display
    • Update accordion to show only matching settings
    • File: app/Filament/Resources/PolicyResource.php (or custom Livewire component)
    • Implementation Note: Blade-level filtering using searchQuery prop, no Livewire component needed for MVP
  • T022 [US2] Add "No results" empty state for search

    • Show message when search returns no matches
    • Provide "Clear search" button
    • File: settings-catalog-grouped.blade.php
    • Implementation Note: Empty state with clear search button using wire:click

Phase 6: UI Implementation - Tabs & Fallback (T023-T025)

Goal: Complete tab navigation and handle non-Settings Catalog policies

User Story: US-UI-06

  • T023 [US3] Verify JSON tab still works (from Feature 002)

    • Tab navigation switches correctly
    • JSON viewer renders snapshot
    • Copy button functional
    • File: app/Filament/Resources/PolicyResource.php
  • T024 [US3] Add fallback for policies without cached definitions

    • Show info message in Settings tab: "Definitions not cached. Showing raw data."
    • Display raw definition IDs with prettified labels
    • Link to "View JSON" tab
    • File: settings-catalog-grouped.blade.php
  • T025 Ensure JSON viewer only renders on Policy View (not globally)

    • Check existing implementation from Feature 002
    • Verify pepperfm/filament-json scoped correctly
    • File: app/Filament/Resources/PolicyResource.php

Phase 7: Testing & Validation (T026-T042)

Goal: Comprehensive testing for resolver, normalizer, and UI

Unit Tests (T026-T031)

  • T026 [P] Create SettingsCatalogDefinitionResolverTest test file

    • File: tests/Unit/SettingsCatalogDefinitionResolverTest.php
    • Setup: Mock GraphClientInterface, in-memory database
  • T027 [P] Test resolve() method with batch of definition IDs

    • Assert: Returns map with display names
    • Assert: Caches in database
    • Assert: Uses cached data on second call
    • File: tests/Unit/SettingsCatalogDefinitionResolverTest.php
  • T028 [P] Test fallback logic for missing definitions

    • Mock: Graph API returns 404
    • Assert: Returns prettified definition ID
    • Assert: No exception thrown
    • File: tests/Unit/SettingsCatalogDefinitionResolverTest.php
  • T029 [P] Create PolicyNormalizerSettingsCatalogTest test file

    • File: tests/Unit/PolicyNormalizerSettingsCatalogTest.php
    • Setup: Mock definition data, sample snapshot
  • T030 [P] Test grouping logic in normalizer

    • Input: Snapshot with settings from different categories
    • Assert: Groups created correctly
    • Assert: Groups sorted alphabetically
    • File: tests/Unit/PolicyNormalizerSettingsCatalogTest.php
  • T031 [P] Test value formatting in normalizer

    • Test bool → "Enabled"/"Disabled"
    • Test int → formatted number
    • Test string → truncation
    • Test choice → label extraction
    • File: tests/Unit/PolicyNormalizerSettingsCatalogTest.php

Feature Tests (T032-T037)

  • T032 [P] Create PolicyViewSettingsCatalogReadableTest test file

    • File: tests/Feature/Filament/PolicyViewSettingsCatalogReadableTest.php
    • Setup: Mock GraphClient, create test policy with Settings Catalog type
  • T033 Test Settings Catalog policy view shows tabs

    • Navigate to Policy View
    • Assert: Tabs component present
    • Assert: "Settings" and "JSON" tabs visible
    • File: tests/Feature/Filament/PolicyViewSettingsCatalogReadableTest.php
  • T034 Test Settings tab shows display names (not definition IDs)

    • Mock: Definitions cached
    • Assert: Display names shown in UI
    • Assert: Definition IDs NOT visible
    • File: tests/Feature/Filament/PolicyViewSettingsCatalogReadableTest.php
  • T035 Test values formatted correctly

    • Mock: Settings with bool, int, string, choice values
    • Assert: Bool shows "Enabled"/"Disabled"
    • Assert: Int shows formatted number
    • Assert: String shows truncated value
    • File: tests/Feature/Filament/PolicyViewSettingsCatalogReadableTest.php
  • T036 [US2] Test search/filter functionality

    • Input: Type search query
    • Assert: Settings list filtered
    • Assert: Only matching settings shown
    • Assert: Clear search resets view
    • File: tests/Feature/Filament/PolicyViewSettingsCatalogReadableTest.php
  • T037 Test graceful degradation for missing definitions

    • Mock: Definitions not cached
    • Assert: Fallback labels shown (prettified IDs)
    • Assert: No broken layout
    • Assert: Info message visible
    • File: tests/Feature/Filament/PolicyViewSettingsCatalogReadableTest.php

Validation & Polish (T038-T042)

  • T038 Run Pest test suite for Feature 185

    • Command: ./vendor/bin/sail artisan test --filter=SettingsCatalog
    • Assert: All tests pass
    • Fix any failures
  • T039 Run Laravel Pint on modified files

    • Command: ./vendor/bin/sail pint --dirty
    • Assert: No style issues
    • Commit fixes
  • T040 Review git changes for Feature 185

    • Check: No changes to forbidden areas (see constitution)
    • Verify: Only expected files modified
    • Document: List of changed files in research.md
  • T041 Run database migration on local environment

    • Command: ./vendor/bin/sail artisan migrate
    • Verify: settings_catalog_definitions table created
    • Check: Indexes applied correctly
  • T042 Manual QA: Policy View with Settings Catalog policy

    • Navigate to Policy View for Settings Catalog policy
    • Verify: Tabs present ("Settings" and "JSON")
    • Verify: Settings tab shows accordion with groups
    • Verify: Display names shown (not raw IDs)
    • Verify: Values formatted correctly
    • Test: Search filters settings
    • Test: JSON tab works
    • Test: Copy buttons functional
    • Test: Dark mode toggle

Dependencies & Execution Order

Sequential Dependencies

  • Phase 1Phase 2: Database must exist before resolver can cache
  • Phase 2Phase 3: Resolver must exist before snapshot enrichment
  • Phase 2Phase 4: Definitions needed for normalizer
  • Phase 4Phase 5: Normalized data structure needed for UI
  • Phase 5Phase 7: UI must exist before feature tests

Parallel Opportunities

  • Phase 2 (T003-T007): Resolver methods can be implemented in parallel
  • Phase 4 (T011-T014): Normalizer sub-methods can be implemented in parallel
  • Phase 5 (T015-T022): UI components can be developed in parallel after T015
  • Phase 7 (T026-T031): Unit tests can be written in parallel
  • Phase 7 (T032-T037): Feature tests can be written in parallel

Example Parallel Execution

Phase 2:

  • Developer A: T003, T004 (resolve methods)
  • Developer B: T005, T006 (resolveOne + fallback)
  • Both converge for T007 (warmCache)

Phase 5:

  • Developer A: T015-T017 (tabs + accordion setup)
  • Developer B: T018-T020 (rendering logic)
  • Both converge for T021-T022 (search functionality)

Task Complexity Estimates

Phase Task Count Estimated Time Dependencies
Phase 1: Database 2 ~30 min None
Phase 2: Resolver 5 ~2-3 hours Phase 1
Phase 3: Snapshot 3 ~1 hour Phase 2
Phase 4: Normalizer 4 ~2-3 hours Phase 2
Phase 5: UI Settings 8 ~3-4 hours Phase 4
Phase 6: UI Tabs 3 ~1 hour Phase 5
Phase 7: Testing 17 ~3-4 hours Phase 2-6
Total 42 11-15 hours

Success Criteria Checklist

  • SC-001: Admin sees human-readable setting names (not definition IDs) on Policy View (Implementation complete - requires manual verification)
  • SC-002: Setting values formatted appropriately (True/False, numbers, choice labels) (Implementation complete - requires manual verification)
  • SC-003: Settings grouped by category with accordion (collapsible sections) (Implementation complete - requires manual verification)
  • SC-004: Search/filter works on display name and value (<200ms response) (Blade-level implementation complete - requires manual verification)
  • SC-005: Raw JSON available in separate "JSON" tab (Feature 002 integration preserved)
  • SC-006: Unknown settings show prettified ID fallback (no broken layout) (Implementation complete - requires manual verification)
  • SC-007: Performance: <2s render for policy with 200 settings (Requires load testing)
  • SC-008: Tests pass: Unit tests for resolver + normalizer (Tests not written yet)
  • SC-009: Tests pass: Feature tests for UI rendering (Tests not written yet)
  • SC-010: Definition resolution: <500ms for batch of 50 (cached) (Requires benchmark testing)

Constitution Compliance Evidence

Principle Evidence Tasks
Safety-First Read-only UI, no edit capabilities All UI tasks
Immutable Versioning Snapshot enrichment non-blocking, metadata only T008-T010
Defensive Restore Not applicable (read-only feature) N/A
Auditability Raw JSON still accessible via tab T023-T025
Tenant-Aware Resolver respects tenant scoping (via GraphClient) T003-T007
Graph Abstraction Uses existing GraphClientInterface T003-T007
Spec-Driven Full spec + plan + tasks before implementation This document

Risk Mitigation Tasks

  • Risk: Graph API rate limiting
    • Mitigation: T007 (warmCache is non-blocking), aggressive DB caching
  • Risk: Definition schema changes by Microsoft
    • Mitigation: T001 (raw JSONB storage), T006 (fallback logic)
  • Risk: Large policies slow UI
    • Mitigation: T017-T018 (accordion lazy-loading), performance tests in T042

Notes for Implementation

  1. Feature 002 Dependency: Feature 185 uses tabs from Feature 002 JSON viewer implementation. Ensure Feature 002 code is stable before starting Phase 5.

  2. Database Migration: Run migration early (T001) to avoid blocking later phases.

  3. Graph API Endpoints: Verify access to /deviceManagement/configurationSettings endpoint in test environment before implementing T004.

  4. Testing Strategy: Write unit tests (Phase 7, T026-T031) in parallel with implementation to enable TDD workflow.

  5. UI Polish: Leave time for manual QA (T042) to catch UX issues not covered by automated tests.

  6. Performance Profiling: Use Laravel Telescope or Debugbar during T042 to measure actual performance vs NFR targets.


Implementation Readiness

Prerequisites:

  • Feature 002 JSON viewer implemented (tabs pattern established)
  • pepperfm/filament-json installed
  • GraphClientInterface available
  • PolicyNormalizer exists
  • PolicySnapshotService exists
  • PostgreSQL with JSONB support

Ready to Start: Phase 1 (Database Foundation)