source/Private/RJFeatures.ps1
|
function Get-RJConfiguration { [CmdletBinding()] param() $actualEnvironment = Get-RJCurrentEnvironment $configFileName = "RJFeatures.$actualEnvironment.psd1" $scriptDir = if ($PSScriptRoot) { $PSScriptRoot } elseif ($MyInvocation.MyCommand.Path) { Split-Path -Parent $MyInvocation.MyCommand.Path } else { # Fallback: try to resolve relative to current location (Resolve-Path ".\source\Private" -ErrorAction SilentlyContinue).Path } if ($scriptDir) { $configPath = Join-Path -Path $scriptDir -ChildPath $configFileName } else { throw "Could not determine script directory to locate configuration files" } if (-not (Test-Path $configPath)) { throw "Configuration file not found: $configPath" } $config = Import-PowerShellDataFile -Path $configPath return $config } function Get-RJFeatureDefinitionList { # Load feature definitions from environment-specific PSD1 files [CmdletBinding()] [OutputType([System.Object[]])] param() $config = Get-RJConfiguration $features = $config.Features | ForEach-Object { [PSCustomObject]$_ } # Ensure we always return an array, even for single results return ,@($features) } function Get-RJFeatureByName { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$FeatureName ) $features = Get-RJFeatureDefinitionList return $features | Where-Object { $_.Name -eq $FeatureName } } function Get-RJMandatoryFeatureList { [CmdletBinding()] [OutputType([System.Object[]])] param() $features = Get-RJFeatureDefinitionList $mandatoryFeatures = $features | Where-Object { $_.IsMandatory -eq $true } # Ensure we always return an array, even for single results return ,@($mandatoryFeatures) } function Test-RJFeatureExists { [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', 'Test-RJFeatureExists')] param( [Parameter(Mandatory)] [string]$FeatureName ) $feature = Get-RJFeatureByName -FeatureName $FeatureName return $null -ne $feature } function Resolve-RJFeature { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param( [string[]]$SelectedFeatures, [PSCustomObject]$CurrentConfig, [string[]]$FeaturesToAdd = @(), [string[]]$FeaturesToRemove = @(), [switch]$ReadOnly ) # Parameter validation $hasSelectedFeatures = $SelectedFeatures -and $SelectedFeatures.Count -gt 0 $hasIncrementalFeatures = ($FeaturesToAdd -and $FeaturesToAdd.Count -gt 0) -or ($FeaturesToRemove -and $FeaturesToRemove.Count -gt 0) # Validate parameter combinations if (-not $hasSelectedFeatures -and -not $hasIncrementalFeatures) { throw "Either SelectedFeatures or at least one of FeaturesToAdd/FeaturesToRemove must be specified" } if ($hasSelectedFeatures -and $hasIncrementalFeatures) { throw "Cannot use SelectedFeatures together with FeaturesToAdd or FeaturesToRemove" } if ($hasIncrementalFeatures -and -not $CurrentConfig) { throw "CurrentConfig is required when using FeaturesToAdd or FeaturesToRemove" } $mandatoryFeatures = Get-RJMandatoryFeatureList # Determine final feature list based on mode if ($hasSelectedFeatures) { # Traditional mode: validate and use selected features foreach ($featureName in $SelectedFeatures) { if (-not (Test-RJFeatureExists -FeatureName $featureName)) { $availableFeatures = (Get-RJFeatureDefinitionList).Name throw "Feature '$featureName' does not exist. Available features: $($availableFeatures -join ', ')" } } $enabledFeatureNames = @($mandatoryFeatures.Name) foreach ($featureName in $SelectedFeatures) { if ($featureName -notin $enabledFeatureNames) { $enabledFeatureNames += $featureName } } } else { # Incremental mode: build from current config + add - remove $currentFeatureNames = @($CurrentConfig.ConfiguredFeatures | ForEach-Object { $_.Name }) # Validate features to add foreach ($featureName in $FeaturesToAdd) { if (-not (Test-RJFeatureExists -FeatureName $featureName)) { $availableFeatures = (Get-RJFeatureDefinitionList).Name throw "Feature '$featureName' does not exist. Available features: $($availableFeatures -join ', ')" } # Check if feature is already configured if ($featureName -in $currentFeatureNames) { throw "Feature '$featureName' is already configured and cannot be added again" } } # Validate features to remove foreach ($featureName in $FeaturesToRemove) { if (-not (Test-RJFeatureExists -FeatureName $featureName)) { $availableFeatures = (Get-RJFeatureDefinitionList).Name throw "Feature '$featureName' does not exist. Available features: $($availableFeatures -join ', ')" } # Check if feature is currently configured if ($featureName -notin $currentFeatureNames) { throw "Feature '$featureName' is not currently configured and cannot be removed" } # Check if feature is mandatory if ($featureName -in $mandatoryFeatures.Name) { throw "Cannot remove mandatory feature '$featureName'" } } # Calculate final feature state: current + add - remove $enabledFeatureNames = @(@($currentFeatureNames) + @($FeaturesToAdd) | Where-Object { $_ -notin $FeaturesToRemove } | Select-Object -Unique) } return @{ FeatureNames = $enabledFeatureNames ReadOnly = $ReadOnly.IsPresent IsIncrementalMode = $hasIncrementalFeatures FeaturesToAdd = $FeaturesToAdd FeaturesToRemove = $FeaturesToRemove } } |