internal/functions/New-Json.ps1


function New-Json {
    [CmdletBinding(SupportsShouldProcess)]
    Param()
    # Parse repo for tags and descriptions then write json
    $script:localapp = Get-DbcConfigValue -Name app.localapp
    $repos = Get-CheckRepo
    $collection = $groups = $repofiles = @()
    foreach ($repo in $repos) {
        $repofiles += (Get-ChildItem "$repo\*.Tests.ps1")
    }
    $tokens = $null
    $errors = $null
    foreach ($file in $repofiles) {
        $Check = $null
        # We dont want to mess with v5 files - although we will need to write the json for them
        if ($file.Name -notmatch 'v5') {
            $message = "We are going to look at this file {0}" -f $file.Name
            Write-PSFMessage -Message $message -Level Verbose
            $filename = $file.Name.Replace(".Tests.ps1", "")
            # Write-Verbose "Processing $FileName"
            # Write-Verbose "Getting Content of File"
            $Check = Get-Content $file -Raw

            # because custom checks if they are not coded correctly will break this json creation
            # and they wont get added nicely so that they can be targetted with tags (checks)
            # this part will check all of the files and ensure that they have the filename variabel at the top and that
            # each describe is using Tags not Tag and the last tag is the $filename

            if ($Filename -notin ('Agent', 'Database', 'Domain', 'HADR', 'Instance', 'LogShipping', 'MaintenanceSolution', 'Server')) {

                #all checks files MUST have this at the top
                if ($Check -notmatch '\$filename = \$MyInvocation\.MyCommand\.Name\.Replace\("\.Tests\.ps1", ""\)') {
                    Write-Verbose "$Filename does not have the correct value at the top so we will add it"
                    $filecontent = @"
  `$filename = `$MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")

"@

                    $filecontent = $filecontent + $Check
                    if ($PSCmdlet.ShouldProcess("$($File.Name)" , "Adding the filename variable to the file")) {
                        $Check = $null
                        Set-Content -Path $file -Value $filecontent
                        Write-Verbose "Getting Content of File again"
                        $Check = Get-Content $file -Raw
                    }

                }

                ## Parse the file with AST
                $CheckFileAST = [Management.Automation.Language.Parser]::ParseInput($check, [ref]$tokens, [ref]$errors)

                #Check that the tags are set correctly otherwise the json doesnt get properly created
                $Statements = $CheckFileAST.EndBlock.statements.Extent
                ## Ignore the filename line
                @($Statements.Where{ $PSItem.StartLineNumber -ne 1 }).ForEach{
                    # Write-Verbose "Checking the Describe Tag $($PSItem.Text.SubString(0,50) )"
                    if ($PSItem.Text -notmatch 'Describe ".*" -Tags .*,.*\$filename \{') {
                        $RogueDescribe = $PSItem.Text.SubString(0, $PSitem.Text.IndexOf('{'))
                        Write-Warning "The Describe Tag $RogueDescribe in $($File.Name) is not set up correctly - we will try to fix it for you"
                        $replace = $RogueDescribe + ', $Filename '
                        $Check = $Check -replace $RogueDescribe , $replace
                        $Check = $Check -replace '-Tag ', '-Tags '
                        if ($PSCmdlet.ShouldProcess("$($File.Name)" , "Fixing the tags on the files")) {
                            Set-Content $file -Value $Check
                            $Check = $null
                        }
                        # Write-Verbose "Getting Content of File again"
                        $Check = Get-Content $file -Raw

                    }
                }
            }

            ## Parse the file with AST
            $CheckFileAST = [Management.Automation.Language.Parser]::ParseInput($check, [ref]$tokens, [ref]$errors)

            ## New code uses a Computer Name loop to speed up execution so need to find that as well
            $ComputerNameForEach = $CheckFileAST.FindAll([Func[Management.Automation.Language.Ast, bool]] {
                    param ($ast)
                    $ast -is [System.Management.Automation.Language.InvokeMemberExpressionAst] -and $ast.expression.Subexpression.Extent.Text -eq 'Get-ComputerName'
                }, $true).Extent

            ## New code uses a Computer Name loop to speed up execution so need to find that as well
            $InstanceNameForEach = $CheckFileAST.FindAll([Func[Management.Automation.Language.Ast, bool]] {
                    param ($ast)
                    $ast -is [System.Management.Automation.Language.InvokeMemberExpressionAst] -and $ast.expression.Subexpression.Extent.Text -eq 'Get-Instance'
                }, $true).Extent


            ## Old code we can use the describes
            $Describes = $CheckFileAST.FindAll([Func[Management.Automation.Language.Ast, bool]] {
                    param ($ast)
                    $ast.CommandElements -and
                    $ast.CommandElements[0].Value -eq 'Describe'
                }, $true)

            @($describes).ForEach{
                $groups += $filename
                $Describe = $_.CommandElements.Where{ $PSItem.StaticType.name -eq 'string' }[1]
                $title = $Describe.Value
                $Tags = $PSItem.CommandElements.Where{ $PSItem.StaticType.name -eq 'Object[]' -and $null -eq $psitem.Value }.Extent.Text.ToString().Replace(', $filename', '')
                # CHoose the type
                if ($Describe.Parent -match "Get-Instance") {
                    $type = "Sqlinstance"
                }
                elseif ($Describe.Parent -match "Get-ComputerName" -or $Describe.Parent -match "AllServerInfo") {
                    $type = "ComputerName"
                }
                elseif ($Describe.Parent -match "Get-ClusterObject") {
                    $Type = "ClusterNode"
                }
                else {
                    #Choose the type from the new way from inside the foreach
                    if ($ComputerNameForEach -match $title) {
                        $type = "ComputerName"
                    }
                    elseif ($InstanceNameForEach -match $title) {
                        $type = "Sqlinstance"
                    }
                    else {
                        $type = $null
                    }
                }

                if ($filename -eq 'HADR') {
                    ## HADR configs are outside of describe
                    $configs = [regex]::matches($check, "Get-DbcConfigValue\s([a-zA-Z\d]*.[a-zA-Z\d]*.[a-zA-Z\d]*.[a-zA-Z\d]*\b)").groups.Where{ $_.Name -eq 1 }.Value
                }
                else {
                    $configs = [regex]::matches($describe.Parent.Extent.Text, "Get-DbcConfigValue\s([a-zA-Z\d]*.[a-zA-Z\d]*.[a-zA-Z\d]*.[a-zA-Z\d]*\b)").groups.Where{ $_.Name -eq 1 }.Value
                }
                $Config = ''
                foreach ($c in $Configs) { $config += "$c " } # DON't DELETE THE SPACE in "$c "
                if ($filename -eq 'MaintenanceSolution') {
                    # The Maintenance Solution needs a bit of faffing as the configs for the jobnames are used to create the titles
                    switch ($tags -match $PSItem) {
                        { $Tags.Contains('SystemFull') } {
                            $config = 'ola.JobName.SystemFull ' + $config
                            $title = 'Ola - ' + (Get-DbcConfigValue -Name ola.jobname.systemfull)
                        }
                        { $Tags.Contains('UserFull') } {
                            $config = 'ola.JobName.UserFull ' + $config
                            $title = 'Ola - ' + (Get-DbcConfigValue -Name ola.jobname.userfull)
                        }
                        { $Tags.Contains('UserDiff') } {
                            $config = 'ola.JobName.UserDiff ' + $config
                            $title = 'Ola - ' + (Get-DbcConfigValue -Name ola.jobname.userdiff)
                        }
                        { $Tags.Contains('UserLog') } {
                            $config = 'ola.JobName.UserLog ' + $config
                            $title = 'Ola - ' + (Get-DbcConfigValue -Name ola.jobname.userlog)
                        }
                        { $Tags.Contains('CommandLog') } {
                            $config = 'ola.JobName.CommandLogCleanup ' + $config
                            $title = 'Ola - ' + (Get-DbcConfigValue -Name ola.jobname.commandlogcleanup)
                        }
                        { $Tags.Contains('SystemIntegrityCheck') } {
                            $config = 'ola.JobName.SystemIntegrity ' + $config
                            $title = 'Ola - ' + (Get-DbcConfigValue -Name ola.jobname.systemintegrity)
                        }
                        { $Tags.Contains('UserIntegrityCheck') } {
                            $config = 'ola.JobName.UserIntegrity ' + $config
                            $title = 'Ola - ' + (Get-DbcConfigValue -Name ola.jobname.userintegrity)
                        }
                        { $Tags.Contains('UserIndexOptimize') } {
                            $config = 'ola.JobName.UserIndex ' + $config
                            $title = 'Ola - ' + (Get-DbcConfigValue -Name ola.jobname.userindex)
                        }
                        { $Tags.Contains('OutputFileCleanup') } {
                            $config = 'ola.JobName.OutputFileCleanup ' + $config
                            $title = 'Ola - ' + (Get-DbcConfigValue -Name ola.jobname.outputfilecleanup)
                        }
                        { $Tags.Contains('DeleteBackupHistory') } {
                            $config = 'ola.JobName.DeleteBackupHistory ' + $config
                            $title = 'Ola - ' + (Get-DbcConfigValue -Name ola.jobname.deletebackuphistory)
                        }
                        { $Tags.Contains('PurgeJobHistory') } {
                            $config = 'ola.JobName.PurgeBackupHistory ' + $config
                            $title = 'Ola - ' + (Get-DbcConfigValue -Name ola.jobname.purgebackuphistory)
                        }
                        Default {}
                    }
                }
                # add the config for the type
                switch ($type) {
                    SqlInstance { $config = 'app.sqlinstance ' + $config }
                    ComputerName { $config = 'app.computername ' + $config }
                    ClusterNode { $config = 'app.sqlinstance ' + $config }
                    Default {}
                }
                if (-not $config) { $config = "None" }
                $collection += [pscustomobject]@{
                    Group       = $filename
                    Type        = $type
                    UniqueTag   = $null
                    AllTags     = "$tags, $filename"
                    Config      = $config
                    Description = $null
                    Describe    = $title
                }
            }
        }
    }
    $singletags = (($collection.AllTags -split ",").Trim() | Group-Object | Where-Object { $_.Count -eq 1 -and $_.Name -notin $groups })
    $Descriptions = Get-Content $script:ModuleRoot\internal\configurations\DbcCheckDescriptions.json -Raw | ConvertFrom-Json
    foreach ($check in $collection) {
        $unique = $singletags | Where-Object { $_.Name -in ($check.AllTags -split ",").Trim() }
        $check.UniqueTag = $unique.Name
        $Check.Description = $Descriptions.Where{ $_.UniqueTag -eq $Check.UniqueTag }.Description
    }
    try {
        if ($PSCmdlet.ShouldProcess("$script:localapp\checks.json" , "Convert Json and write to file")) {
            ConvertTo-Json -InputObject $collection | Out-File "$script:localapp\checks.json"
        }
    }
    catch {
        Write-PSFMessage "Failed to create the json, something weird might happen now with tags and things" -Level Significant
    }

}