--- description: "Task list for Graph Contracts — LIST $expand Parity Fix" --- # Tasks: Graph Contracts — LIST `$expand` Parity Fix **Input**: Design documents from `specs/112-list-expand-parity/` **Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/ **Tests**: REQUIRED (Pest) — this feature changes runtime Graph request shapes. ## Phase 1: Setup (Shared) **Purpose**: Confirm inputs and establish a baseline before changing runtime behavior. - [X] T001 [Shared] Read implementation plan in specs/112-list-expand-parity/plan.md - [X] T002 [P] [Shared] Verify $expand allowlist exists for Entra role assignments in config/graph_contracts.php - [X] T003 [P] [Shared] Identify existing Graph tests to extend in tests/Unit/GraphContractRegistryTest.php and tests/Unit/MicrosoftGraphClientListPoliciesSelectTest.php --- ## Phase 2: Foundational (Blocking Prerequisites) **Purpose**: Establish pre-change confidence and shared test baseline. - [X] T004 [Shared] Run baseline focused tests listed in specs/112-list-expand-parity/quickstart.md **Checkpoint**: Baseline established; safe to implement. --- ## Phase 3: User Story 1 — Admin roles report shows real principal names (Priority: P1) 🎯 MVP **Goal**: Admin Roles report requests `principal` expansion and renders real principal display names when Graph provides them. **Independent Test**: Simulate role assignment list retrieval and assert report output uses `principal.displayName` when present, else falls back to `Unknown`. ### Tests for User Story 1 - [X] T005 [P] [US1] Create report service test scaffolding in tests/Unit/EntraAdminRolesReportServiceTest.php (RefreshDatabase + mock GraphClientInterface) - [X] T006 [US1] Implement tests asserting `entraRoleAssignments` call includes `expand => 'principal'` and payload uses principal display name in tests/Unit/EntraAdminRolesReportServiceTest.php ### Implementation for User Story 1 - [X] T007 [US1] Pass `expand => 'principal'` when fetching assignments in app/Services/EntraAdminRoles/EntraAdminRolesReportService.php - [X] T008 [US1] Forward caller-provided `expand` into LIST query input in app/Services/Graph/MicrosoftGraphClient.php - [X] T009 [US1] Normalize + cap `$expand` in app/Services/Graph/GraphContractRegistry.php (split string on top-level commas only; preserve commas inside balanced parentheses; trim, drop empty, dedupe, maxTokenLen=200, maxItems=10 after allowlist) ### Regression tests for User Story 1 - [X] T010 [US1] Add LIST `$expand` request-shape test coverage in tests/Unit/MicrosoftGraphClientListPoliciesSelectTest.php - [X] T011 [US1] Add `$expand` normalization/cap test coverage in tests/Unit/GraphContractRegistryTest.php - [X] T012 [US1] Run story tests in tests/Unit/EntraAdminRolesReportServiceTest.php plus Graph tests in tests/Unit/MicrosoftGraphClientListPoliciesSelectTest.php **Checkpoint**: Entra Admin Roles report can render real principal names when upstream provides them. --- ## Phase 4: User Story 2 — Engineers can request explicit LIST expansions safely (Priority: P2) **Goal**: LIST supports explicit expansions safely and discoverably (document option shape, allowlist enforcement). **Independent Test**: Inspect outgoing LIST request and confirm `$expand` is included only when explicitly provided and allowlisted. ### Tests for User Story 2 - [X] T013 [P] [US2] Add test asserting LIST omits `$expand` when `expand` is not provided in tests/Unit/MicrosoftGraphClientListPoliciesSelectTest.php - [X] T014 [P] [US2] Add tests asserting (a) disallowed expand tokens are removed (exact-match allowlist, no partial matches) and (b) allowlisted subquery/select-style expand tokens that contain commas inside parentheses are preserved as a single token (no naive comma-splitting) in tests/Unit/GraphContractRegistryTest.php ### Implementation for User Story 2 - [X] T015 [US2] Document supported `listPolicies()` options (select/expand/filter/top/platform + input shapes) in app/Services/Graph/GraphClientInterface.php - [X] T016 [US2] Ensure `GraphContractCheck` query shape remains compatible (select + expand) in app/Console/Commands/GraphContractCheck.php **Checkpoint**: Engineers can safely request allowlisted expansions on LIST without silent capability mismatch. --- ## Phase 5: User Story 3 — Misuse is detectable in non-production (Priority: P3) **Goal**: When `$expand` is sanitized (removed or truncated), maintainers get a diagnostic signal in non-production with low noise in production. **Independent Test**: Trigger mixed allowed/disallowed expand and assert a non-production warning is emitted with policy type + removed values. ### Tests for User Story 3 - [X] T017 [US3] Add Log::spy-based test for non-production diagnostics when `$expand` is sanitized in tests/Unit/GraphContractRegistryTest.php - [X] T018 [US3] Add production-noise guard test (debug-level or suppressed warn) for `$expand` sanitization in tests/Unit/GraphContractRegistryTest.php ### Implementation for User Story 3 - [X] T019 [US3] Emit structured diagnostic logs when `$expand` is removed/truncated in app/Services/Graph/GraphContractRegistry.php **Checkpoint**: Sanitization is visible during dev/staging without spamming production logs. --- ## Phase 6: Polish & Cross-Cutting Concerns **Purpose**: Keep the change safe, consistent, and shippable. - [X] T020 [Shared] Run formatting on touched files (Pint) after changes in app/Services/Graph/GraphContractRegistry.php - [X] T021 [Shared] Run focused suite listed in specs/112-list-expand-parity/quickstart.md --- ## Dependencies & Execution Order ### Phase Dependencies - Setup (Phase 1) → Foundational (Phase 2) → US1 (Phase 3) → US2 (Phase 4) → US3 (Phase 5) → Polish (Phase 6) ### User Story Dependencies - US1 depends on LIST `$expand` parity (implemented as part of US1 via app/Services/Graph/MicrosoftGraphClient.php + app/Services/Graph/GraphContractRegistry.php). - US2 extends US1 with discoverability + additional safety regression coverage. - US3 depends on the sanitization logic (adds diagnostics + tests). ### Parallel Opportunities - Phase 1: T002 and T003 can run in parallel. - US1: T005 can be done in parallel with T008/T009 (different files). - US2: T013 and T014 can run in parallel. --- ## Parallel Example: User Story 1 - In parallel: - T005 (test scaffolding) in tests/Unit/EntraAdminRolesReportServiceTest.php - T008 (LIST forwards expand) in app/Services/Graph/MicrosoftGraphClient.php - T009 (sanitize/normalize/cap expand) in app/Services/Graph/GraphContractRegistry.php --- ## Implementation Strategy ### MVP First (User Story 1 Only) 1. Complete Phase 1–2 2. Complete US1 (Phase 3) 3. Stop and ship if acceptance + regression tests pass ### Then harden (US2 + US3) - Add discoverability + extra regression guards (US2) - Add diagnostics behavior + tests (US3)