Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Generate unicorn dependency graph using PowerShell. Analyse and improve your configurations or easily fix existing issues
function Get-ComputedDependencyGraphUrl {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0 )]
[Hashtable]$allDependenciesClean
)
begin {
Write-Verbose "Cmdlet Get-ComputedDependencyGraphUrl - Begin"
}
process {
$r = ""
$allDependenciesClean.Keys | ?{ $allDependenciesClean[$_] -ne $null } | %{
$configName = $_
$computedDependencies = $allDependenciesClean[$configName] | ? { $_ -ne $configName}
$configReport = ($computedDependencies | % { "[$configName]-->[$_]" }) -join ","
$r = $r, $configReport -join ","
}
"https://yuml.me/diagram/nofunky/class/$r"
}
end {
Write-Verbose "Cmdlet Get-ComputedDependencyGraphUrl - End"
}
}
function Get-ConfigDependencyGraphUrl {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0 )]
[Hashtable]$allDependenciesClean,
[Parameter(Mandatory=$true, Position=1 )]
[object[]]$allUnicornConfigs
)
begin {
Write-Verbose "Cmdlet Get-ConfigDependencyGraphUrl - Begin"
}
process {
$r = ""
$allDependenciesClean.Keys | ?{ $allDependenciesClean[$_] -ne $null } | %{
$configName = $_
$configDependencies = ($allUnicornConfigs | ? { $_.Name -eq $configName } | Select-Object -First 1).Dependencies
$configReport = ($configDependencies | % { "[$configName]-->[$_]" }) -join ","
$r = $r, $configReport -join ","
}
"https://yuml.me/diagram/nofunky/class/$r"
}
end {
Write-Verbose "Cmdlet Get-ConfigDependencyGraphUrl - End"
}
}
function Get-DiffDependencyGraphUrl {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0 )]
[Hashtable]$allDependenciesClean,
[Parameter(Mandatory=$true, Position=1 )]
[object[]]$allUnicornConfigs
)
begin {
Write-Verbose "Cmdlet Get-DiffDependencyGraphUrl - Begin"
}
process {
$r = ""
$allDependenciesClean.Keys | ?{ $allDependenciesClean[$_] -ne $null } | %{
$configName = $_
$computedDependencies = $allDependenciesClean[$configName] | ? { $_ -ne $configName}
$configDependencies = ($allUnicornConfigs | ? { $_.Name -eq $configName } | Select-Object -First 1).Dependencies
$missing = Compare-Object $configDependencies $computedDependencies -IncludeEqual | ? { $_.SideIndicator -eq "=>" } | %{ $_.InputObject }
$missingFormated = ($missing | % { "[$configName]-->[$_]" }) -join ","
$redundant = Compare-Object $configDependencies $computedDependencies -IncludeEqual | ? { $_.SideIndicator -eq "<=" } | %{ $_.InputObject }
$redundantFormated = ($redundant | % { "[$configName]-.-^[$_]" }) -join ","
$configReport = $missingFormated, $redundantFormated -join ","
$r = $r, $configReport -join ","
}
"https://yuml.me/diagram/nofunky/class/$r"
}
end {
Write-Verbose "Cmdlet Get-DiffDependencyGraphUrl - End"
}
}
function Get-ItemDataChildren {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0 )]
[Rainbow.Model.IItemData]$IItemData,
[Parameter(Mandatory=$true, Position=1 )]
[Unicorn.Data.ISourceDataStore]$ISourceDataStore,
[Parameter(Mandatory=$true, Position=2 )]
[Unicorn.Predicates.IPredicate]$IPredicate
)
begin {
Write-Verbose "Cmdlet Get-ItemDataChildren - Begin"
}
process {
Write-Verbose "Cmdlet Get-ItemDataChildren - Process"
$ISourceDataStore.GetChildren($IItemData) | %{
if($IPredicate.Includes($_).IsIncluded){
$_
Get-ItemDataChildren $_ $ISourceDataStore $IPredicate
}
}
}
end {
Write-Verbose "Cmdlet Get-ItemDataChildren - End"
}
}
function Get-ItemsData {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0 )]
[Unicorn.Configuration.IConfiguration]$Configuration
)
begin {
Write-Verbose "Cmdlet Get-ItemsData - Begin"
}
process {
Write-Verbose "Cmdlet Get-ItemsData - Process"
$method = $Configuration.GetType().GetMethods() | ? { $_.Name -eq "Resolve"} | ? { $_.ReturnType.Name -eq "T" } | Select-Object -First 1
$predicateRootPathResolver = $method.MakeGenericMethod([Unicorn.Predicates.PredicateRootPathResolver]).Invoke($Configuration, $null)
$sourceDataStore = $method.MakeGenericMethod([Unicorn.Data.ISourceDataStore]).Invoke($Configuration, $null)
$predicate = $method.MakeGenericMethod([Unicorn.Predicates.IPredicate]).Invoke($Configuration, $null)
foreach($IItemData in $predicateRootPathResolver.GetRootSourceItems()){
$IItemData
Get-ItemDataChildren $IItemData $sourceDataStore $predicate
}
}
end {
Write-Verbose "Cmdlet Get-ItemsData - End"
}
}
function Get-UnicornConfiguration {
[CmdletBinding()]
param(
[Parameter(Mandatory=$false, Position=0 )]
[string]$ConfigurationName = ""
)
begin {
Write-Verbose "Cmdlet Get-UnicornConfiguration - Begin"
}
process {
Write-Verbose "Cmdlet Get-UnicornConfiguration - Process"
[Unicorn.ControlPanel.ControlPanelUtility]::ResolveConfigurationsFromQueryParameter($ConfigurationName)
}
end {
Write-Verbose "Cmdlet Get-UnicornConfiguration - End"
}
}
function Get-PredicateIncludes {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0 )]
[Unicorn.Configuration.IConfiguration]$Configuration
)
begin {
Write-Verbose "Cmdlet Get-PredicateIncludes - Begin"
}
process {
Write-Verbose "Cmdlet Get-PredicateIncludes - Process"
$method = $Configuration.GetType().GetMethods() | ? { $_.Name -eq "Resolve"} | ? { $_.ReturnType.Name -eq "T" } | Select-Object -First 1
$predicateRootPathResolver = $method.MakeGenericMethod([Unicorn.Predicates.PredicateRootPathResolver]).Invoke($Configuration, $null)
$predicateRootPathResolver.GetRootPaths()
}
end {
Write-Verbose "Cmdlet Get-PredicateIncludes - End"
}
}
function Get-ComparableObjects {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0 )]
[Unicorn.Configuration.IConfiguration[]]$Configurations
)
begin {
Write-Verbose "Cmdlet Add-Files - Begin"
}
process {
Write-Verbose "Cmdlet Add-Files - Process"
$result = New-Object System.Collections.ArrayList
$Configurations | ForEach-Object {
$configName = $_
Get-ItemsData $configName | ForEach-Object {
$obj = @{}
$obj.ItemData = $_
$obj.Config = $configName
$result.Add($obj) | Out-Null
}
}
$result
}
end {
Write-Verbose "Cmdlet Add-Files - End"
}
}
function Get-TemplateDependency {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0 )]
$currentItemData
)
begin {
Write-Verbose "Cmdlet Add-Files - Begin"
}
process {
Write-Verbose "Cmdlet Add-Files - Process"
$templateIdKey = $currentItemData.ItemData.TemplateId.ToString()
if ($global_invalidTemplateIDs[$templateIdKey] -eq $null) {
$result = $global_templateIdToItemDataMapping[$templateIdKey]
if ($result -ne $null) {
$result
}else {
$objectForDifferentConfigs = $global_allObjects | Where-Object { $_.Config.Name -ne $currentItemData.Config.Name}
$result = $objectForDifferentConfigs | Where-Object {$_.ItemData.ID -eq $currentItemData.ItemData.TemplateId } | Select-Object -First 1
if ($result -ne $null) {
$global_templateIdToItemDataMapping.Add($templateIdKey, $result)
$result
}else {
$global_invalidTemplateIDs.Add($templateIdKey,1)
}
}
}
}
end {
Write-Verbose "Cmdlet Add-Files - End"
}
}
function Get-Depependencies($configName, $allDependencies){
$start2 = Get-Date
# check depndencies by path include
$global_predicateIncludes[$configName].Path | % {
$currentPath = $_
$global_predicateIncludes.Keys | %{
$globalConfig = $_
if($globalConfig -ne $configName){
$global_predicateIncludes[$globalConfig].Path | %{
if($currentPath.Contains($_)){
$allDependencies[$configName].Add($globalConfig) | Out-Null
}
}
}
}
}
# check depndencies by template
$configurationObjects = $global_allObjects | Where-Object { $_.Config.Name -eq $configName}
$configurationObjects | ForEach-Object {
$result = Get-TemplateDependency $_
if ($result -ne $null) {
$allDependencies[$configName].Add($result.Config.Name) | Out-Null
}
}
Write-Verbose "Processing
[$((Get-Date) - $start2)] $configName"
}
function Show-Dialog {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0 )]
[Unicorn.Configuration.IConfiguration[]]$Configurations
)
begin {
Write-Verbose "Cmdlet Add-Files - Begin"
}
process {
Write-Verbose "Cmdlet Add-Files - Process"
$dialogOptions = @{}
$Configurations.Name | ForEach-Object { $dialogOptions.Add($_, $_) }
$preSelectedConfigurations = $Configurations.Name
$result = Read-Variable -Parameters `
@{ Name = "preSelectedConfigurations"; Title = "Configurations"; Options = $dialogOptions; Editor = "checklist"; Tip = "Select which configuration should be used to generate dependency graph"; Height = "330px"; Tab = "General"; } `
-Description "This script will go through the selected unicorn configurations and generate dependency report" `
-Title "Generate Unicorn configurations dependency graph" -Width 500 -Height 600 `
-OkButtonName "OK" -CancelButtonName "Cancel" `
if ($result -ne "ok") {
Close-Window
exit
}
$Configurations | Where-Object {$preSelectedConfigurations.Contains($_.Name)}
}
end {
Write-Verbose "Cmdlet Add-Files - End"
}
}
function Show-ResultsDialog {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0 )]
[Hashtable]$allDependenciesClean,
[Parameter(Mandatory=$true, Position=1 )]
[object[]]$allUnicornConfigs
)
begin {
Write-Verbose "Cmdlet Show-ResultsDialog - Begin"
}
process {
$result= "ok"
$graphModes = [ordered]@{}
$graphModes['Mixed dependency graph (redundant, missing dependencies)'] = "mixed"
$graphModes['Computed dependency graph'] = "computed"
$graphModes['Configuration based dependency graph'] = "config"
while($result -eq "ok"){
$result = Read-Variable -Parameters `
@{ Name = "Description"; Title=" "; Value="Please select graph you want to see and click Show button"; editor="info";}, `
@{ Name = "graphMode"; Options=$graphModes; Value=""; Title="Graph mode"; } `
-Description "This script will go through the selected unicorn configurations and generate dependency report" `
-Title "Generate Unicorn configurations dependency graph" -Width 500 -Height 300 `
-OkButtonName "Show" -CancelButtonName "Exit"
if($result -eq "cancel"){
Exit
}
switch ($graphMode) {
"computed" {
$url = Get-ComputedDependencyGraphUrl $allDependenciesClean
$description = "Dependency graph based on item path/id dependency"
$legend = "This graph represents desired dependency determined based on item's id/path dependencies between each module."
}
"config" {
$url = Get-ConfigDependencyGraphUrl $allDependenciesClean $allUnicornConfigs
$description = "Configuration based dependency graph"
$legend = "This graph represents dependency that is currently set in your unicorn configuration files"
}
"mixed" {
$url = Get-DiffDependencyGraphUrl $allDependenciesClean $allUnicornConfigs
$description = "Mixed graph"
$legend = "Shows what you should do with your config files to fix dependency erros <br/> <b>Solid line</b> - missing dependency <br/> <b>Dotted line</b> - redundant dependency"
}
Default {
$url = "https://alan-null.github.io"
}
}
$result2 = Read-Variable -Parameters `
@{ Name="Info0"; Title=""; Value='<img src="'+ $url +'""/>'; editor="info" }, `
@{ Name="Info1"; Title="Legend:"; Value=$legend; editor="info" } `
-Description $description -Title "Dependency Graph" -Width 1000 -Height 800 `
-OkButtonName "Show different" -CancelButtonName "Exit"
if($result2 -eq "cancel"){
Exit
}
}
}
end {
Write-Verbose "Cmdlet Show-ResultsDialog - End"
}
}
# initialize global variables
$start = Get-Date
$global_templateIdToItemDataMapping = @{}
$global_invalidTemplateIDs = @{}
$global_invalidTemplateIDs.Add("962b53c4-f93b-4df9-9821-415c867b8903",1)
$global_invalidTemplateIDs.Add("f1828a2c-7e5d-4bbd-98ca-320474871548",1)
[System.Collections.ArrayList]$dependencies = New-Object "System.Collections.ArrayList"
[System.Xml.XmlNode[]]$allConfigs = [Sitecore.Configuration.Factory]::GetConfigNodes("/sitecore/unicorn/configurations/configuration")
$allUnicornConfigs = $allConfigs | ForEach-Object { Get-UnicornConfiguration $_.Name }
$configsToProcess = Show-Dialog $allUnicornConfigs
$global_progress = 0.0
$global_allObjects = $allUnicornConfigs | ForEach-Object {
Write-Progress -Activity "Initializing objects" `
-Status "Creating comparable objects for $($_.Name)" `
-PercentComplete ($global_progress)
$global_progress= $global_progress + 100.0 / ($allUnicornConfigs.Count)
Get-ComparableObjects $_
}
$global_predicateIncludes = @{}
$global_progress = 0.0
$allUnicornConfigs | ForEach-Object{ $global_predicateIncludes.Add("$($_.Name)", $(Get-PredicateIncludes $_)) }
(Get-Date) - $start
# initialize result tables
$allDependencies = @{}
$allDependenciesClean = @{}
$allUnicornConfigs.Name | ForEach-Object { $allDependencies.Add("$($_)", $(New-Object "System.Collections.ArrayList"))}
$allUnicornConfigs.Name | ForEach-Object { $allDependenciesClean.Add("$($_)", $null)}
$start = Get-Date
$configsToProcess | ForEach-Object {
Write-Progress -Activity "Analysing dependencies for $($_.Name)" `
-Status "Checking path/id dependencies" `
-PercentComplete ($global_progress)
$global_progress= $global_progress + 100.0 / ($configsToProcess.Count)
Get-Depependencies $_.Name $allDependencies
}
(Get-Date) - $start
# print results
$allDependencies.Keys | %{
$allDependenciesClean[$_] = $allDependencies[$_] | Sort-Object | Get-Unique -AsString
}
if((Show-Confirm -Title "Would you like to generate dependency report which can be then exported?") -eq "yes"){
$allDependenciesClean.Keys | ?{ $allDependenciesClean[$_] -ne $null } | Show-ListView -InfoTitle "Desired unicorn dependency configuration" `
-PageSize 25 `
-Property `
@{Label='Configuration'; Expression={$_} },
@{Label='Dependencies'; Expression={$allDependenciesClean[$_] -join ','} }
}
Show-ResultsDialog $allDependenciesClean $allUnicornConfigs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment