# Research — Remove Legacy Tenant Graph Options

## Goal
Remove usage of the deprecated tenant-based Graph options accessor (`$tenant->graphOptions()` / `Tenant::graphOptions()`) and make provider-based resolution (`ProviderConnectionResolver` + `ProviderGateway`) the single source of truth for Microsoft Graph configuration.

## Decisions

### Decision: Provider connection is canonical for Graph options
- **Chosen**: Resolve `ProviderConnection` via `App\Services\Providers\ProviderConnectionResolver::resolveDefault($tenant, 'microsoft')`, then derive options via `App\Services\Providers\ProviderGateway::graphOptions($connection, $overrides)`.
- **Rationale**: Existing services already follow this path (e.g. onboarding/health); it centralizes credential storage and validation.
- **Alternatives considered**:
  - Keep reading tenant columns (`Tenant.app_client_id` / `Tenant.app_client_secret`) as fallback — rejected (explicitly forbidden; introduces mixed credential sources).

### Decision: Kill-switch behavior for `Tenant::graphOptions()`
- **Chosen**: Keep method for now, but make it throw a clear exception (fail-fast).
- **Rationale**: Ensures any missed call site fails deterministically in CI/runtime.
- **Alternatives considered**:
  - Delete method outright — rejected (higher risk / more disruptive API break).

### Decision: Guardrail scope and matching rules
- **Chosen**: Add a CI guard test that scans **`app/` only** for these forbidden patterns:
  - `\$tenant->graphOptions(`
  - `Tenant::graphOptions(`
- **Rationale**: Matches the agreed scope and avoids false positives on unrelated `graphOptions(...)` methods (e.g. `ProviderGateway::graphOptions`).
- **Alternatives considered**:
  - Scan for all `->graphOptions(` — rejected (would flag many legitimate methods).
  - Scan `tests/` too — rejected (would create noisy failures during refactors).

## Current Code Findings (Call Sites)

### Deprecated tenant accessor call sites (must be removed)
These are current `app/` references to `$tenant->graphOptions()`:
- `app/Services/AssignmentRestoreService.php`
- `app/Services/AssignmentBackupService.php`
- `app/Services/Intune/RestoreRiskChecker.php`
- `app/Services/Intune/TenantPermissionService.php`
- `app/Services/Intune/VersionService.php`
- `app/Services/Intune/FoundationMappingService.php`
- `app/Services/Intune/PolicyCaptureOrchestrator.php`
- `app/Services/Intune/FoundationSnapshotService.php`
- `app/Services/Intune/TenantConfigService.php` (wraps + returns `$tenant->graphOptions()`)
- `app/Services/Intune/ConfigurationPolicyTemplateResolver.php`
- `app/Services/Directory/EntraGroupSyncService.php`
- `app/Services/Directory/RoleDefinitionsSyncService.php`
- `app/Services/Graph/AssignmentFilterResolver.php`

### Canonical provider-based examples to copy
- `app/Services/Intune/RbacOnboardingService.php`
- `app/Services/Intune/RbacHealthService.php`
- `app/Services/Intune/RestoreService.php`
- `app/Services/Intune/PolicySnapshotService.php`

### Source of legacy accessor
- `app/Models/Tenant.php` defines the deprecated `graphOptions()` method.

## Recommended Refactor Pattern

```php
$resolution = $this->providerConnections()->resolveDefault($tenant, 'microsoft');

if (! $resolution->resolved || ! $resolution->connection instanceof ProviderConnection) {
    // Fail fast with actionable reason.
}

$connection = $resolution->connection;
$graphOptions = $this->providerGateway()->graphOptions($connection, $overrides);
```

## Notes / Non-goals
- No database schema changes (tenant credential columns stay for now).
- No changes to Graph contract registry (`config/graph_contracts.php`) are required for this feature; this work changes configuration sourcing only.
