185 lines
9.3 KiB
Markdown
185 lines
9.3 KiB
Markdown
---
|
||
|
||
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.
|
||
|
||
- [x] T001 Create feature config defaults in config/directory_groups.php (staleness_days=30, retention_days=90, schedule enabled/interval, page_size)
|
||
- [x] 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.
|
||
|
||
- [x] 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
|
||
- [x] T004 [P] Create EntraGroup model in app/Models/EntraGroup.php (tenant-scoped, casts for group_types)
|
||
- [x] T005 [P] Create EntraGroupSyncRun model in app/Models/EntraGroupSyncRun.php (status constants, counters, safe error fields)
|
||
- [x] T006 [P] Create EntraGroup factory in database/factories/EntraGroupFactory.php
|
||
- [x] T007 [P] Create EntraGroupSyncRun factory in database/factories/EntraGroupSyncRunFactory.php
|
||
- [x] T008 Add tenant relationships in app/Models/Tenant.php (entraGroups(), entraGroupSyncRuns())
|
||
- [x] 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.
|
||
|
||
- [x] T010 [P] [US1] Add test for manual sync creating a run + dispatching a job in tests/Feature/DirectoryGroups/StartSyncTest.php
|
||
- [x] T011 [P] [US1] Add test that sync job upserts groups + updates counters in tests/Feature/DirectoryGroups/SyncJobUpsertsGroupsTest.php
|
||
- [x] T012 [P] [US1] Add test that retention purge deletes groups older than retention window in tests/Feature/DirectoryGroups/SyncRetentionPurgeTest.php
|
||
- [x] T013 [P] [US1] Add test that scheduled dispatcher creates a run without a user initiator in tests/Feature/DirectoryGroups/ScheduledSyncDispatchTest.php
|
||
|
||
- [x] T014 [P] [US1] Implement deterministic selection key helper in app/Services/Directory/EntraGroupSelection.php
|
||
- [x] T015 [US1] Implement sync service in app/Services/Directory/EntraGroupSyncService.php (Graph paging via GraphClientInterface, upsert, counters, retention purge)
|
||
- [x] T016 [US1] Implement queued job in app/Jobs/EntraGroupSyncJob.php (run lifecycle: pending→running→succeeded/failed; safe error_category + error_summary)
|
||
- [x] T017 [US1] Add audit logging for sync start/completion in app/Jobs/EntraGroupSyncJob.php using app/Services/Intune/AuditLogger.php
|
||
- [x] T018 [P] [US1] Create Filament run resource in app/Filament/Resources/EntraGroupSyncRunResource.php and app/Filament/Resources/EntraGroupSyncRunResource/Pages/{ListEntraGroupSyncRuns,ViewEntraGroupSyncRun}.php
|
||
- [x] T019 [US1] Add “Sync Groups” action on List page in app/Filament/Resources/EntraGroupSyncRunResource/Pages/ListEntraGroupSyncRuns.php (creates run, dispatches job, shows notification)
|
||
- [x] T020 [US1] Implement scheduled dispatcher command in app/Console/Commands/TenantpilotDispatchDirectoryGroupsSync.php (idempotent per tenant + minute-slot, respects config/directory_groups.php)
|
||
- [x] 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) ⚠️
|
||
|
||
- [x] T022 [P] [US2] Add test for listing/searching/filtering cached groups in tests/Feature/DirectoryGroups/BrowseGroupsTest.php
|
||
|
||
### Implementation for User Story 2
|
||
|
||
- [x] T023 [P] [US2] Create Filament groups resource in app/Filament/Resources/EntraGroupResource.php and app/Filament/Resources/EntraGroupResource/Pages/{ListEntraGroups,ViewEntraGroup}.php
|
||
- [x] T024 [US2] Implement list query + filters (q search, stale filter, group type filter) in app/Filament/Resources/EntraGroupResource.php
|
||
- [x] 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) ⚠️
|
||
|
||
- [x] T026 [P] [US3] Add unit test for label resolver formatting + fallbacks in tests/Unit/DirectoryGroups/EntraGroupLabelResolverTest.php
|
||
- [x] 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
|
||
|
||
- [x] T028 [US3] Implement DB-backed label resolver in app/Services/Directory/EntraGroupLabelResolver.php (resolveOne/resolveMany, tenant-scoped, stable formatting)
|
||
- [x] T029 [US3] Refactor group label rendering in app/Filament/Resources/TenantResource.php to use EntraGroupLabelResolver instead of Graph lookups
|
||
- [x] T030 [US3] Refactor Livewire assignments widget to use cached labels: app/Livewire/PolicyVersionAssignmentsWidget.php and resources/views/livewire/policy-version-assignments-widget.blade.php
|
||
- [x] T031 [US3] Refactor restore results to show cached labels where possible: resources/views/filament/infolists/entries/restore-results.blade.php
|
||
- [x] 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.
|
||
|
||
- [x] T033 [P] Run formatting on changed files with vendor/bin/pint --dirty
|
||
- [x] T034 Run targeted Pest suite for this feature (e.g., php artisan test tests/Feature/DirectoryGroups and tests/Unit/DirectoryGroups)
|
||
- [x] 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 3–5)** → 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: T010–T013
|
||
- Parallel foundational code: T014 and T018 can be developed alongside T015–T016 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 T029–T032 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.
|