TenantAtlas/specs/051-entra-group-directory-cache/tasks.md
2026-01-11 22:02:06 +01:00

9.3 KiB
Raw Blame History

description
Task list for implementing Entra Group Directory Cache (Groups v1)

Tasks: Entra Group Directory Cache (Groups v1)

Input: Design documents from /specs/051-entra-group-directory-cache/ Prerequisites: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/

Tests: Required (Pest), per FR-012 in spec.md.

Organization: Tasks are grouped by user story so each story can be implemented and tested independently.

Format: - [ ] T### [P?] [US#?] Description (with file path)

  • [P]: Can run in parallel (different files, no dependencies)
  • [US#]: User story mapping (US1/US2/US3)

Path Conventions (Laravel)

  • App code: app/
  • Config: config/
  • DB: database/migrations/, database/factories/
  • Filament admin: app/Filament/Resources/
  • Console + scheduler wiring: app/Console/Commands/, routes/console.php
  • Tests (Pest): tests/Feature/, tests/Unit/

Phase 1: Setup (Shared Infrastructure)

Purpose: Introduce feature configuration and permission metadata.

  • T001 Create feature config defaults in config/directory_groups.php (staleness_days=30, retention_days=90, schedule enabled/interval, page_size)
  • T002 Update Group.Read.All feature tagging in config/intune_permissions.php (include directory-groups / group-directory-cache)

Phase 2: Foundational (Blocking Prerequisites)

Purpose: Database schema + core domain objects required by all user stories.

⚠️ CRITICAL: No user story work can begin until this phase is complete.

  • T003 Create migrations for Entra groups + sync runs in database/migrations/_create_entra_groups_table.php and database/migrations/_create_entra_group_sync_runs_table.php
  • T004 [P] Create EntraGroup model in app/Models/EntraGroup.php (tenant-scoped, casts for group_types)
  • T005 [P] Create EntraGroupSyncRun model in app/Models/EntraGroupSyncRun.php (status constants, counters, safe error fields)
  • T006 [P] Create EntraGroup factory in database/factories/EntraGroupFactory.php
  • T007 [P] Create EntraGroupSyncRun factory in database/factories/EntraGroupSyncRunFactory.php
  • T008 Add tenant relationships in app/Models/Tenant.php (entraGroups(), entraGroupSyncRuns())
  • T009 Add directory-groups contract metadata in config/graph_contracts.php and accessor(s) in app/Services/Graph/GraphContractRegistry.php (select fields + base path for /groups)

Checkpoint: Foundation ready — user stories can now proceed.


Phase 3: User Story 1 - Sync groups into a tenant-scoped cache (Priority: P1) 🎯 MVP

Goal: Manual + scheduled async sync writes tenant-scoped group cache and run records (including safe error summaries).

Independent Test: Trigger a sync for a tenant and verify a run record exists and group rows are populated for that tenant.

Tests for User Story 1 (REQUIRED) ⚠️

Write these tests FIRST and ensure they fail before implementation.

  • T010 [P] [US1] Add test for manual sync creating a run + dispatching a job in tests/Feature/DirectoryGroups/StartSyncTest.php

  • T011 [P] [US1] Add test that sync job upserts groups + updates counters in tests/Feature/DirectoryGroups/SyncJobUpsertsGroupsTest.php

  • T012 [P] [US1] Add test that retention purge deletes groups older than retention window in tests/Feature/DirectoryGroups/SyncRetentionPurgeTest.php

  • T013 [P] [US1] Add test that scheduled dispatcher creates a run without a user initiator in tests/Feature/DirectoryGroups/ScheduledSyncDispatchTest.php

  • T014 [P] [US1] Implement deterministic selection key helper in app/Services/Directory/EntraGroupSelection.php

  • T015 [US1] Implement sync service in app/Services/Directory/EntraGroupSyncService.php (Graph paging via GraphClientInterface, upsert, counters, retention purge)

  • T016 [US1] Implement queued job in app/Jobs/EntraGroupSyncJob.php (run lifecycle: pending→running→succeeded/failed; safe error_category + error_summary)

  • T017 [US1] Add audit logging for sync start/completion in app/Jobs/EntraGroupSyncJob.php using app/Services/Intune/AuditLogger.php

  • T018 [P] [US1] Create Filament run resource in app/Filament/Resources/EntraGroupSyncRunResource.php and app/Filament/Resources/EntraGroupSyncRunResource/Pages/{ListEntraGroupSyncRuns,ViewEntraGroupSyncRun}.php

  • T019 [US1] Add “Sync Groups” action on List page in app/Filament/Resources/EntraGroupSyncRunResource/Pages/ListEntraGroupSyncRuns.php (creates run, dispatches job, shows notification)

  • T020 [US1] Implement scheduled dispatcher command in app/Console/Commands/TenantpilotDispatchDirectoryGroupsSync.php (idempotent per tenant + minute-slot, respects config/directory_groups.php)

  • T021 [US1] Register scheduler entry in routes/console.php for tenantpilot:directory-groups:dispatch (every minute)

Checkpoint: US1 complete — cache population works, runs are visible, and scheduled runs can be observed.


Phase 4: User Story 2 - Browse groups (Priority: P2)

Goal: Provide a cached-only “Directory → Groups” browse/search/filter/detail UI.

Independent Test: After a sync run, open the groups list and verify search/filter/detail views work using only cached data.

Tests for User Story 2 (REQUIRED) ⚠️

  • T022 [P] [US2] Add test for listing/searching/filtering cached groups in tests/Feature/DirectoryGroups/BrowseGroupsTest.php

Implementation for User Story 2

  • T023 [P] [US2] Create Filament groups resource in app/Filament/Resources/EntraGroupResource.php and app/Filament/Resources/EntraGroupResource/Pages/{ListEntraGroups,ViewEntraGroup}.php
  • T024 [US2] Implement list query + filters (q search, stale filter, group type filter) in app/Filament/Resources/EntraGroupResource.php
  • T025 [US2] Add “Sync Groups” header action that links to run list or starts a sync in app/Filament/Resources/EntraGroupResource/Pages/ListEntraGroups.php

Checkpoint: US2 complete — operators can browse/search cached groups and triage staleness.


Phase 5: User Story 3 - Name resolution across the suite (Priority: P3)

Goal: Any UI that displays group IDs shows a friendly cached label (or unresolved fallback) without live directory calls during render.

Independent Test: Load a page that includes group GUID references and verify it renders with names from DB cache (and fallbacks when missing) without calling Graph.

Tests for User Story 3 (REQUIRED) ⚠️

  • T026 [P] [US3] Add unit test for label resolver formatting + fallbacks in tests/Unit/DirectoryGroups/EntraGroupLabelResolverTest.php
  • T027 [P] [US3] Add feature test ensuring Tenant/Restore/PolicyVersion UI renders without Graph calls during render in tests/Feature/DirectoryGroups/NoLiveGraphOnRenderTest.php

Implementation for User Story 3

  • T028 [US3] Implement DB-backed label resolver in app/Services/Directory/EntraGroupLabelResolver.php (resolveOne/resolveMany, tenant-scoped, stable formatting)
  • T029 [US3] Refactor group label rendering in app/Filament/Resources/TenantResource.php to use EntraGroupLabelResolver instead of Graph lookups
  • T030 [US3] Refactor Livewire assignments widget to use cached labels: app/Livewire/PolicyVersionAssignmentsWidget.php and resources/views/livewire/policy-version-assignments-widget.blade.php
  • T031 [US3] Refactor restore results to show cached labels where possible: resources/views/filament/infolists/entries/restore-results.blade.php
  • T032 [US3] Refactor restore group-mapping inputs/labels to prefer cached labels: app/Filament/Resources/RestoreRunResource.php

Checkpoint: US3 complete — name resolution is consistent and render-safe across key pages.


Phase 6: Polish & Cross-Cutting Concerns

Purpose: Validation, cleanup, and operational readiness.

  • T033 [P] Run formatting on changed files with vendor/bin/pint --dirty
  • T034 Run targeted Pest suite for this feature (e.g., php artisan test tests/Feature/DirectoryGroups and tests/Unit/DirectoryGroups)
  • T035 Validate operator workflow against specs/051-entra-group-directory-cache/quickstart.md

Dependencies & Execution Order

Phase Dependencies

  • Setup (Phase 1) → blocks nothing, but should be done first.
  • Foundational (Phase 2) → BLOCKS all user stories.
  • User Stories (Phase 35) → depend on Foundational; proceed in priority order (US1 → US2 → US3).
  • Polish (Phase 6) → depends on all desired user stories.

User Story Dependencies

  • US1 (P1): Depends on Phase 2 only.
  • US2 (P2): Depends on US1 (needs data to browse).
  • US3 (P3): Depends on US1 (needs cache populated); can begin in parallel with US2 once US1 is stable.

Parallel Execution Examples

US1

  • Parallel tests: T010T013
  • Parallel foundational code: T014 and T018 can be developed alongside T015T016 once schema/models exist.

US2

  • T023 can start while T022 is being written (different files).

US3

  • T028 can start while T026/T027 are being written; integration tasks T029T032 can be split across different files.

Implementation Strategy

MVP (US1 only)

  1. Phase 1 → Phase 2
  2. Phase 3 (US1): tests → sync service/job → run UI → scheduler
  3. Stop and validate via quickstart workflow

Incremental Delivery

  • Add US2 (browse) after US1 is stable.
  • Add US3 (name resolution) after US1, then refactor page-by-page.