docs(02-03): complete SiteListService plan

- 02-03-SUMMARY.md created
- STATE.md: progress updated 67%, decisions added, session recorded
- ROADMAP.md: phase 2 progress updated (2/7 summaries)
This commit is contained in:
Dev
2026-04-02 13:52:17 +02:00
parent c04d88882d
commit d17689cc46

View File

@@ -0,0 +1,137 @@
---
phase: 02-permissions
plan: 03
subsystem: api
tags: [sharepoint, pnp-framework, tenant-admin, site-listing, csharp]
# Dependency graph
requires:
- phase: 01-foundation
provides: SessionManager.GetOrCreateContextAsync, TenantProfile, OperationProgress
provides:
- ISiteListService interface for ViewModel mocking
- SiteListService tenant admin API wrapper enumerating all sites
- SiteInfo record model (Url, Title)
affects: [02-06-site-picker-dialog, 02-permissions-viewmodel]
# Tech tracking
tech-stack:
added: []
patterns:
- "Admin URL derivation: Regex transform contoso.sharepoint.com → contoso-admin.sharepoint.com"
- "ServerException wrapping: Access denied → InvalidOperationException with actionable message"
- "InternalsVisibleTo pattern for testing internal static helpers without making them public"
key-files:
created:
- SharepointToolbox/Core/Models/SiteInfo.cs
- SharepointToolbox/Services/ISiteListService.cs
- SharepointToolbox/Services/SiteListService.cs
modified:
- SharepointToolbox/AssemblyInfo.cs
- SharepointToolbox.Tests/Services/SiteListServiceTests.cs
key-decisions:
- "DeriveAdminUrl is internal static (not private) so tests can call it directly without a live tenant"
- "InternalsVisibleTo added to AssemblyInfo.cs — plan specified internal visibility but omitted the assembly attribute needed to test it"
- "OneDrive personal sites filtered by -my.sharepoint.com URL pattern in addition to Active status check"
patterns-established:
- "Admin URL derivation: use Regex.Replace with (https://[^.]+)(\\.sharepoint\\.com) pattern"
- "Tenant admin access: pass synthetic TenantProfile with admin URL to SessionManager.GetOrCreateContextAsync"
requirements-completed: [PERM-02]
# Metrics
duration: 1min
completed: 2026-04-02
---
# Phase 2 Plan 3: SiteListService Summary
**ISiteListService + SiteListService wrapper for SharePoint tenant admin API using PnP.Framework Tenant.GetSitePropertiesFromSharePoint, with admin URL regex derivation and ServerException-to-InvalidOperationException wrapping**
## Performance
- **Duration:** 1 min
- **Started:** 2026-04-02T11:48:57Z
- **Completed:** 2026-04-02T11:50:40Z
- **Tasks:** 1 (TDD: RED + GREEN)
- **Files modified:** 5
## Accomplishments
- SiteInfo record model created in Core/Models
- ISiteListService interface defined — enables ViewModel mocking in Plan 06 (SitePickerDialog)
- SiteListService derives admin URL via Regex, connects via SessionManager to tenant admin endpoint
- Active-only filtering with OneDrive personal site exclusion (-my.sharepoint.com)
- DeriveAdminUrl tested with 2 unit tests (standard URL, trailing-slash URL)
## Task Commits
Each task was committed atomically:
1. **Task 1 RED: SiteListServiceTests (failing)** - `5c10840` (test)
2. **Task 1 GREEN: ISiteListService, SiteListService, SiteInfo** - `78b3d4f` (feat)
**Plan metadata:** _(pending)_
_Note: TDD task has two commits (test RED → feat GREEN); no REFACTOR step needed — code is clean as written_
## Files Created/Modified
- `SharepointToolbox/Core/Models/SiteInfo.cs` - Simple record with Url and Title properties
- `SharepointToolbox/Services/ISiteListService.cs` - Interface contract for GetSitesAsync
- `SharepointToolbox/Services/SiteListService.cs` - Implementation: admin URL derivation, tenant query, filtering, error wrapping
- `SharepointToolbox/AssemblyInfo.cs` - Added InternalsVisibleTo("SharepointToolbox.Tests")
- `SharepointToolbox.Tests/Services/SiteListServiceTests.cs` - Two unit tests for DeriveAdminUrl
## Decisions Made
- DeriveAdminUrl marked `internal static` rather than `private static` to allow direct unit testing without mocking a full SessionManager
- `InternalsVisibleTo("SharepointToolbox.Tests")` added to AssemblyInfo.cs — this is the standard .NET approach for testing internal members (Rule 3 deviation, see below)
- OneDrive sites excluded by URL pattern (`-my.sharepoint.com`) in addition to `Status == "Active"` to avoid returning personal storage sites in the picker
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 3 - Blocking] Added InternalsVisibleTo to expose internal DeriveAdminUrl to test project**
- **Found during:** Task 1 GREEN (test compilation failed with CS0117)
- **Issue:** Plan specified `internal static` for DeriveAdminUrl for testability, but did not include the `InternalsVisibleTo` assembly attribute required for the test project to access it
- **Fix:** Added `[assembly: InternalsVisibleTo("SharepointToolbox.Tests")]` to AssemblyInfo.cs
- **Files modified:** SharepointToolbox/AssemblyInfo.cs
- **Verification:** Tests compile and both DeriveAdminUrl tests pass (2/2)
- **Committed in:** 78b3d4f (GREEN commit)
---
**Total deviations:** 1 auto-fixed (1 blocking)
**Impact on plan:** Necessary for test infrastructure — plan's intent was clearly to test internal method; InternalsVisibleTo is the standard mechanism. No scope creep.
## Issues Encountered
None
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- ISiteListService ready for injection into SitePickerDialog (Plan 06)
- SiteListService compiles and DeriveAdminUrl verified; live tenant testing requires admin credentials (handled at runtime via SessionManager interactive login)
- Full test suite: 53 pass, 4 skip, 0 fail
## Self-Check: PASSED
- FOUND: SharepointToolbox/Core/Models/SiteInfo.cs
- FOUND: SharepointToolbox/Services/ISiteListService.cs
- FOUND: SharepointToolbox/Services/SiteListService.cs
- FOUND: .planning/phases/02-permissions/02-03-SUMMARY.md
- FOUND: commit 5c10840 (test RED)
- FOUND: commit 78b3d4f (feat GREEN)
---
*Phase: 02-permissions*
*Completed: 2026-04-02*