internal/configurationschemata/metajson.ps1
Register-PSFConfigSchema -Name MetaJson -Schema { param ( [string] $Resource, [System.Collections.Hashtable] $Settings ) Write-PSFMessage -String 'Configuration.Schema.MetaJson.ProcessResource' -StringValues $Resource -ModuleName PSFramework #region Converting parameters $Peek = $Settings["Peek"] $ExcludeFilter = $Settings["ExcludeFilter"] $IncludeFilter = $Settings["IncludeFilter"] $AllowDelete = $Settings["AllowDelete"] $script:EnableException = $Settings["EnableException"] $script:cmdlet = $Settings["Cmdlet"] Set-Location -Path $Settings["Path"] $PassThru = $Settings["PassThru"] #endregion Converting parameters #region Utility Function function Read-V1Node { [CmdletBinding()] param ( $NodeData, [string] $Path, [Hashtable] $Result, [string] $Type, [Hashtable] $Settings ) Write-PSFMessage -String 'Configuration.Schema.MetaJson.ProcessFile' -StringValues $Path -ModuleName PSFramework $basePath = switch ($Type) { 'file' { Split-Path -Path $Path } 'weblink' { $Path -replace '/[^/]+?$' } default { $null } } if ($NodeData.ModuleName) { $moduleName = "{0}." -f $NodeData.ModuleName } else { $moduleName = "" } #region Import Resources foreach ($property in $NodeData.Static.PSObject.Properties) { $Result["$($moduleName)$($property.Name)"] = $property.Value } foreach ($property in $NodeData.Object.PSObject.Properties) { $Result["$($moduleName)$($property.Name)"] = $property.Value | ConvertFrom-PSFClixml } foreach ($property in $NodeData.Dynamic.PSObject.Properties) { $Result["$($moduleName)$(Resolve-V1String -String $property.Name)"] = Resolve-V1String -String $property.Value } foreach ($property in $NodeData.Tree.PSObject.Properties) { Resolve-V1Tree -Property $property -Result $Result -BaseElement @() } foreach ($property in $NodeData.DynamicTree.PSObject.Properties) { Resolve-V1Tree -Property $property -Result $Result -BaseElement @() -Dynamic $true } #endregion Import Resources #region Import included / linked configuration files :includes foreach ($include in $NodeData.Include) { $resolvedInclude = Resolve-V1String -String $include $uri = [uri]$resolvedInclude # Skip relative paths if we do not have a base path to place it relative to if (-not $uri.IsAbsoluteUri -and -not $basePath) { continue } #region Calculate the new include path if ($uri.IsAbsoluteUri) { $includePath = $resolvedInclude } else { $includePath = switch ($Type) { 'file' { $joinedPath = Join-Path -Path $basePath -ChildPath ($resolvedInclude -replace '^\.\\', '\') try { Resolve-PSFPath -Path $joinedPath -Provider FileSystem -SingleItem } catch { Stop-PSFFunction -String 'Configuration.Schema.MetaJson.ResolveFile' -StringValues $joinedPath -EnableException $script:EnableException -ModuleName PSFramework -ErrorRecord $_ -Continue -ContinueLabel includes -Cmdlet $script:cmdlet } } 'weblink' { $newPath = $basePath $relParts = $resolvedInclude -split "/" foreach ($part in $relParts) { if ($part -eq '..') { $newPath = $newPath -replace '/[^/]+$' } else { $newPath = $newPath, $part -join '/' } } $newPath } } } #endregion Calculate the new include path $newSettings = $Settings | ConvertTo-PSFHashtable -Include ExcludeFilter, IncludeFilter try { $configData = Import-PSFConfig -Path $includePath -Peek @newSettings -Schema MetaJson -EnableException -ErrorAction Stop } catch { Stop-PSFFunction -String 'Configuration.Schema.MetaJson.ExecuteInclude.Error' -StringValues $includePath -EnableException $script:EnableException -ModuleName PSFramework -ErrorRecord $_ -Continue -ContinueLabel includes -Cmdlet $script:cmdlet } foreach ($configDatum in $configData) { $Result[$configDatum.FullName] = $configDatum.Value } } #endregion Import included / linked configuration files $Result } function Resolve-V1String { <# .SYNOPSIS Resolves a string by inserting placeholders for environment variables. .DESCRIPTION Resolves a string by inserting placeholders for environment variables. .PARAMETER String The string to resolve. .EXAMPLE PS C:\> Resolve-V1String -String '.\%COMPUTERNAME%\config.json' Resolves the specified string, inserting the local computername for %COMPUTERNAME%. #> [CmdletBinding()] param ( $String ) if ($String -isnot [string]) { return $String } $scriptblock = { param ( $Match ) $script:envData[$Match.Value] } [regex]::Replace($String, $script:envDataNamesRGX, $scriptblock) } function Resolve-V1Tree { [CmdletBinding()] param ( $Property, [Hashtable] $Result, [bool] $Dynamic, [AllowEmptyCollection()] [string[]] $BaseElement ) if ($Property.TypeNameOfValue -notin 'System.Management.Automation.PSCustomObject', 'System.Collections.Hashtable') { $name = (@($BaseElement) + @($Property.Name)) -join "." if ($Dynamic) { $Result[(Resolve-V1String -String $name)] = Resolve-V1String -String $Property.Value } else { $Result[$name] = $Property.Value } return } $value = $Property.Value if ($value -is [System.Collections.Hashtable]) { $value = [pscustomobject]$value } if ($value.'!Condition') { $conditionSet = $null if ($value.'!ConditionSet') { $module, $name = $value.'!ConditionSet' -split ' ', 2 $conditionSet = Get-PSFFilterConditionSet -Module $module -Name $name | Select-Object -First 1 } else { $conditionSet = Get-PSFFilterConditionSet -Module PSFramework -Name Environment } if (-not $conditionSet) { throw "Unable to resolve Condition Set: $($value.'!ConditionSet')" } $filter = New-PSFFilter -Expression $value.'!Condition' -ConditionSet $conditionSet if (-not $filter.Evaluate()) { return } } foreach ($propertyObject in $value.PSObject.Properties) { if ($propertyObject.Name -eq '!Condition') { continue } if ($propertyObject.Name -eq '!ConditionSet') { continue } if ($value.'!Condition') { Resolve-V1Tree -Property $propertyObject -Result $Result -Dynamic $Dynamic -BaseElement $BaseElement } else { Resolve-V1Tree -Property $propertyObject -Result $Result -Dynamic $Dynamic -BaseElement (@($BaseElement) + @($Property.Name)) } } } #endregion Utility Function #region Utility Computation $script:envData = @{ } foreach ($envItem in (Get-ChildItem env:\)) { $script:envData["%$($envItem.Name)%"] = $envItem.Value } $script:envDataNamesRGX = $script:envData.Keys -join '|' #endregion Utility Computation #region Accessing Content $resourceUri = [uri]$Resource switch ($resourceUri.Scheme) { #region Weblink { $_ -in 'http', 'https' } { try { $importData = Invoke-WebRequest -Uri $Resource -UseBasicParsing -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop } catch { Stop-PSFFunction -String 'Configuration.Schema.MetaJson.WebError' -StringValues $Resource -ModuleName PSFramework -FunctionName 'Schema: MetaJson' -EnableException $EnableException -ErrorRecord $_ -Cmdlet $script:cmdlet return } $resolvedPath = $Resource $resourceType = 'weblink' } #endregion Weblink #region File 'file' { try { $resolvedPath = Resolve-PSFPath -Path $Resource -Provider FileSystem -SingleItem } catch { Stop-PSFFunction -String 'Configuration.Schema.MetaJson.ResolveFile' -StringValues $Resource -ModuleName PSFramework -FunctionName 'Schema: MetaJson' -EnableException $EnableException -ErrorRecord $_ -Cmdlet $script:cmdlet return } switch -regex ($resolvedPath) { '\.psd1$' { try { $importData = [pscustomobject](Import-PSFPowerShellDataFile -Path $resolvedPath -ErrorAction Stop) } catch { Stop-PSFFunction -String 'Configuration.Schema.MetaJson.InvalidPsd1' -StringValues $Resource -ModuleName PSFramework -FunctionName 'Schema: MetaJson' -EnableException $EnableException -ErrorRecord $_ -Cmdlet $script:cmdlet return } if ($importData.Static -is [hashtable]) { $importData.Static = [pscustomobject]$importData.Static } if ($importData.Object -is [hashtable]) { $importData.Object = [pscustomobject]$importData.Object } if ($importData.Dynamic -is [hashtable]) { $importData.Dynamic = [pscustomobject]$importData.Dynamic } if ($importData.Tree -is [hashtable]) { $importData.Tree = [pscustomobject]$importData.Tree } if ($importData.DynamicTree -is [hashtable]) { $importData.DynamicTree = [pscustomobject]$importData.DynamicTree } } default { try { $importData = Get-Content -Path $resolvedPath -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop } catch { Stop-PSFFunction -String 'Configuration.Schema.MetaJson.InvalidJson' -StringValues $Resource -ModuleName PSFramework -FunctionName 'Schema: MetaJson' -EnableException $EnableException -ErrorRecord $_ -Cmdlet $script:cmdlet return } } } $resourceType = 'file' } #endregion File #region Straight Json default { try { $importData = $Resource | ConvertFrom-Json -ErrorAction Stop $resolvedPath = '' $resourceType = 'Json' } catch { Stop-PSFFunction -String 'Configuration.Schema.MetaJson.InvalidJson' -StringValues $Resource -ModuleName PSFramework -FunctionName 'Schema: MetaJson' -EnableException $EnableException -ErrorRecord $_ -Cmdlet $script:cmdlet return } } #endregion Straight Json } #endregion Accessing Content switch ($importData.Version) { 1 { $configurationHash = Read-V1Node -NodeData $importData -Path $resolvedPath -Type $resourceType -Result @{ } -Settings $Settings $configurationItems = $configurationHash.Keys | ForEach-Object { [pscustomobject]@{ FullName = $_ Value = $configurationHash[$_] } } foreach ($configItem in $configurationItems) { if ($ExcludeFilter | Where-Object { $configItem.FullName -like $_ }) { continue } if ($IncludeFilter -and -not ($IncludeFilter | Where-Object { $configItem.FullName -like $_ })) { continue } if ($Peek) { $configItem continue } Set-PSFConfig -FullName $configItem.FullName -Value $configItem.Value -AllowDelete:$AllowDelete -PassThru:$PassThru } } default { Stop-PSFFunction -String 'Configuration.Schema.MetaJson.UnknownVersion' -StringValues $Resource, $importData.Version -ModuleName PSFramework -FunctionName 'Schema: MetaJson' -EnableException $EnableException -Cmdlet $script:cmdlet return } } } |