diff --git a/.gitignore b/.gitignore index 95deb82..c57dc06 100644 --- a/.gitignore +++ b/.gitignore @@ -397,3 +397,7 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml + +*.sln + +testResults.xml diff --git a/Changelog.md b/Changelog.md index 27da0c3..7cd0540 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ # Changelog +## v0.2 + +- Adds `json` support + ## v0.1 - [6c12e8d](https://github.com/cdhunt/potel/commit/6c12e8d6dcce2c44b1c70232268217794dd89696) Adds test for [httpunitPS](https://github.com/cdhunt/httpunitPS) configs and fixes related issues diff --git a/README.md b/README.md index 0d97433..621c43a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,14 @@ # Import-ConfigData Load configuration data from multiple file types. +The returned _Hashtable_ should have the same structure regardless of the source format. + +Currently supported types: + +- [PSD1](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_data_files?view=powershell-7.4) +- [YAML](https://yaml.org/) +- [JSON](https://www.json.org/) +- [TOML](https://toml.io/) ## CI Status diff --git a/build.ps1 b/build.ps1 index 1a1e3b2..4f1a194 100644 --- a/build.ps1 +++ b/build.ps1 @@ -195,24 +195,25 @@ function Publish { function Docs { param () + Import-Module C:\source\Build-Docs\publish\Build-Docs\ Import-Module $publish -Force - $commands = Get-Command -Module $module - $HelpToMd = [System.IO.Path]::Combine($src, 'internal', 'Export-HelpToMd.ps1') - . $HelpToMd + $help = Get-HelpModuleData $module - @('# Import-ConfigData', [System.Environment]::NewLine) | Set-Content -Path "$docs/README.md" - $($manifest.Description) | Add-Content -Path "$docs/README.md" - @('## Cmdlets', [System.Environment]::NewLine) | Add-Content -Path "$docs/README.md" + # docs/README.md + $help | New-HelpDoc | + Add-ModuleProperty Name -H1 | + Add-ModuleProperty Description | + Add-HelpDocText "Commands" -H2 | + Add-ModuleCommands -AsLinks | + Out-HelpDoc -Path 'docs/README.md' - foreach ($command in $Commands | Sort-Object -Property Verb) { + # Individual Commands + foreach ($command in $help.Commands) { $name = $command.Name - $docPath = Join-Path -Path $docs -ChildPath "$name.md" - $help = Get-Help -Name $name - - Export-HelpToMd $help | Set-Content -Path $docPath - - "- [$name]($name.md) $($help.Synopsis)" | Add-Content -Path "$docs/README.md" + $doc = New-HelpDoc -HelpModuleData $help + $doc.Text = $command.ToMD() + $doc | Out-HelpDoc -Path "docs/$name.md" } ChangeLog | Set-Content -Path "$parent/Changelog.md" diff --git a/docs/Import-ConfigData.md b/docs/Import-ConfigData.md index 3ec7264..41bd193 100644 --- a/docs/Import-ConfigData.md +++ b/docs/Import-ConfigData.md @@ -1,61 +1,45 @@ # Import-ConfigData - Load configuration data from multiple file types. The returned object should look the same regardless of the source format. -## Parameters +## Parameters ### Parameter Set 1 - -- `[String]` **Path** _Specifies a path to a configuration file with an extention of psd1, toml, yaml, or yml._ Mandatory, ValueFromPipeline - +- `[String]` **Path** _Specifies a path to a configuration file with an extention of psd1, toml, json, yaml, or yml._ Mandatory, ValueFromPipeline ## Examples - ### Example 1 - Return an object representing the contents of a PowerShell Data File. - ```powershell $config = Import-ConfigData -Path config.psd1 $config.DriveName data ``` - - ### Example 2 - Return an object representing the contents of a TOML File. - ```powershell $config = Import-ConfigData -Path config.toml $config.DriveName data ``` - - ### Example 3 - Return an object representing the contents of a YAML File. - ```powershell $config = Import-ConfigData -Path config.yaml $config.DriveName data ``` - ## Links - - [https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_data_files?view=powershell-7.4](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_data_files?view=powershell-7.4) - [https://toml.io/](https://toml.io/) - [https://yaml.org/](https://yaml.org/) diff --git a/docs/README.md b/docs/README.md index cb8942e..9e7bea0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,8 +1,8 @@ # Import-ConfigData - Load configuration data from multiple file types. -## Cmdlets +## Commands + +- [Import-ConfigData](Import-ConfigData.md) _Load configuration data from multiple file types._ -- [Import-ConfigData](Import-ConfigData.md) Load configuration data from multiple file types. diff --git a/src/internal/Export-HelpToMd.ps1 b/src/internal/Export-HelpToMd.ps1 deleted file mode 100644 index f943d60..0000000 --- a/src/internal/Export-HelpToMd.ps1 +++ /dev/null @@ -1,148 +0,0 @@ -function Export-HelpToMd { - [CmdletBinding()] - param ( - [Parameter(Mandatory, Position = 0, ValueFromPipeline)] - [PSCustomObject] - $HelpInfo - ) - - begin { - function GetText { - param ([string]$text, [string]$default) - - $text = $text.Trim() - if ([string]::IsNullOrEmpty($text)) { - if ([string]::IsNullOrEmpty($default)) { - $default = 'No description' - } - return $default - } - return $text - } - - function GetName ([PSCustomObject]$help) { - $lines = @() - $lines += '# {0}' -f $help.Name - $lines += [System.Environment]::NewLine - return $lines - } - - function GetDescription { - param ($description, [string]$noDesc) - - $description = $description.Description.Text | Out-String - $line = '{0}' -f (GetText $description $noDesc) - - return $line - } - - function GetParameterSet ([PSCustomObject]$help) { - $lines = @() - $setNum = 1 - - $lines += '## Parameters' - - foreach ($set in $help.syntax.syntaxItem) { - $lines += [System.Environment]::NewLine - $lines += '### Parameter Set {0}' -f $setNum - $lines += [System.Environment]::NewLine - - foreach ($param in $set.Parameter) { - $paramStringParts = @() - - $paramStringParts += '- `[{0}]`' -f (GetText $param.parameterValue 'switch') - - $paramStringParts += '**{0}**' -f $param.Name - - $paramStringParts += '_{0}_ ' -f (GetDescription -description $param -noDesc 'Parameter help description') - - $attributes = @() - if ($param.required -eq 'true') { $attributes += 'Mandatory' } - if ($param.pipelineInput -like '*ByValue*') { $attributes += 'ValueFromPipeline' } - - $paramStringParts += $attributes -join ', ' - - $lines += $paramStringParts -join ' ' - } - - $setNum++ - } - - return $lines - } - - function GetExample ([PSCustomObject]$help) { - $lines = @() - $exNum = 1 - - $lines += [System.Environment]::NewLine - $lines += '## Examples' - - foreach ($exampleList in $help.examples.example) { - foreach ($example in $exampleList) { - $lines += [System.Environment]::NewLine - $lines += '### Example {0}' -f $exNum - $lines += [System.Environment]::NewLine - - $lines += $example.remarks.Text.Where({ ![string]::IsNullOrEmpty($_) }) - $lines += [System.Environment]::NewLine - - $lines += '```powershell' - $lines += $example.code.Trim("`t") - $lines += '```' - - } - $exNum++ - } - - return $lines - } - - function GetLink ([PSCustomObject]$help, $Commands) { - if ($help.relatedLinks.count -gt 0) { - $lines = @() - - $lines += [System.Environment]::NewLine - $lines += '## Links' - $lines += [System.Environment]::NewLine - - foreach ($link in $help.relatedLinks) { - - foreach ($text in $link.navigationLink.linkText) { - - if ($text -match '\w{3,}-\w{3,}') { - $uri = $text - $lines += '- [{0}]({0}.md)' -f $uri - } - - if ($text -match 'images\/.+\.png') { - $uri = $text - $lines += '- [{0}]({0})' -f $uri - } - - } - foreach ($uri in $link.navigationLink.uri) { - if (![string]::IsNullOrEmpty($uri)) { - $lines += '- [{0}]({0})' -f $uri - } - } - } - - return $lines - } - } - } - - process { - - GetName $HelpInfo - GetDescription $HelpInfo $HelpInfo.Synopsis - GetParameterSet $HelpInfo - GetExample $HelpInfo - GetLink $HelpInfo - } - - end { - - } -} diff --git a/src/private/ConvertFrom-PSCustomObject.ps1 b/src/private/ConvertFrom-PSCustomObject.ps1 new file mode 100644 index 0000000..1a35634 --- /dev/null +++ b/src/private/ConvertFrom-PSCustomObject.ps1 @@ -0,0 +1,23 @@ +function ConvertFrom-PSCustomObject ($Object) { + + foreach ($item in $Object) { + + $itemType = $item.GetType() + + if ($itemType.Name -eq 'PSCustomObject') { + $keys = $item | Get-Member -MemberType NoteProperty + + $hash = @{} + foreach ($key in $keys) { + $name = $key.Name + $value = @(ConvertFrom-PSCustomObject $item.$name) + $hash.Add($name, $value) + } + $hash | Write-Output + } + + if ($itemType.IsValueType -or $itemType.Name -eq 'String') { + $item | Write-Output + } + } +} diff --git a/src/private/Import-JsonConfigData.ps1 b/src/private/Import-JsonConfigData.ps1 new file mode 100644 index 0000000..27d0a9e --- /dev/null +++ b/src/private/Import-JsonConfigData.ps1 @@ -0,0 +1,27 @@ +function Import-JsonConfigData { + [CmdletBinding()] + param ( + [Parameter(Mandatory, + Position = 0, + ValueFromPipeline, + ValueFromPipelineByPropertyName)] + [Alias("PSPath")] + [ValidateNotNullOrEmpty()] + [string] + $Path + ) + + $content = Get-Content -Path $Path -Raw + + switch ($PSVersionTable.PSVersion.Major) { + 5 { + $psObject = $content | ConvertFrom-Json + ConvertFrom-PSCustomObject $psObject + break + } + Default { + $content | ConvertFrom-Json -AsHashtable + } + } + +} \ No newline at end of file diff --git a/src/public/Import-ConfigData.ps1 b/src/public/Import-ConfigData.ps1 index 5755b4d..fe613c0 100644 --- a/src/public/Import-ConfigData.ps1 +++ b/src/public/Import-ConfigData.ps1 @@ -5,7 +5,7 @@ function Import-ConfigData { .DESCRIPTION Load configuration data from multiple file types. The returned object should look the same regardless of the source format. .PARAMETER Path - Specifies a path to a configuration file with an extention of psd1, toml, yaml, or yml. + Specifies a path to a configuration file with an extention of psd1, toml, json, yaml, or yml. .LINK https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_data_files?view=powershell-7.4 .LINK @@ -57,6 +57,7 @@ function Import-ConfigData { '.toml' { Import-TomlConfigData -Path $Path; break } '.yaml' { Import-YamlConfigData -Path $Path; break } '.yml' { Import-YamlConfigData -Path $Path; break } + '.json' { Import-JsonConfigData -Path $Path; break } } } diff --git a/test/Import-ConfigData.Tests.ps1 b/test/Import-ConfigData.Tests.ps1 index c9faa91..30621d6 100644 --- a/test/Import-ConfigData.Tests.ps1 +++ b/test/Import-ConfigData.Tests.ps1 @@ -4,12 +4,37 @@ BeforeAll { } +Describe 'ConvertFrom-PSCustomObject' { + Context 'Object Array' { + BeforeAll { + . "$PSScriptRoot/../publish/Import-ConfigData/private/ConvertFrom-PSCustomObject.ps1" + } + + AfterAll { + Remove-Item "function:/ConvertFrom-PSCustomObject" + } + + It 'Should return an array of two objects' { + $sut = Get-Content -Path "$PSScriptRoot/test2/TestConfig.json" -Raw | ConvertFrom-Json + $result = ConvertFrom-PSCustomObject $sut + $result.ObjectArray.Count | Should -Be 2 + } + + It 'Should return an array of one object' { + $sut = Get-Content -Path "$PSScriptRoot/test3/TestConfig.json" -Raw | ConvertFrom-Json + $result = ConvertFrom-PSCustomObject $sut + $result.plan.Count | Should -Be 1 + } + } +} + Describe 'Import-ConfigData' { Context 'Simple Object' { It 'Should import a file' -ForEach @( @{ config = "$PSScriptRoot/test1/TestConfig.psd1"; type = 'PSD1' } @{ config = "$PSScriptRoot/test1/TestConfig.toml"; type = 'TOML' } @{ config = "$PSScriptRoot/test1/TestConfig.yml"; type = 'YAML' } + @{ config = "$PSScriptRoot/test1/TestConfig.json"; type = 'JSON' } ) { $results = Import-ConfigData -Path $config @@ -23,6 +48,7 @@ Describe 'Import-ConfigData' { @{ config = "$PSScriptRoot/test2/TestConfig.psd1"; type = 'PSD1' } @{ config = "$PSScriptRoot/test2/TestConfig.toml"; type = 'TOML' } @{ config = "$PSScriptRoot/test2/TestConfig.yml"; type = 'YAML' } + @{ config = "$PSScriptRoot/test2/TestConfig.json"; type = 'JSON' } ) { $results = Import-ConfigData -Path $config @@ -48,6 +74,7 @@ Describe 'Import-ConfigData' { @{ config = "$PSScriptRoot/test3/TestConfig.psd1"; type = 'PSD1' } @{ config = "$PSScriptRoot/test3/TestConfig.toml"; type = 'TOML' } @{ config = "$PSScriptRoot/test3/TestConfig.yml"; type = 'YAML' } + @{ config = "$PSScriptRoot/test3/TestConfig.json"; type = 'JSON' } ) { $results = Import-ConfigData -Path $config diff --git a/test/test1/TestConfig.json b/test/test1/TestConfig.json new file mode 100644 index 0000000..0dda417 --- /dev/null +++ b/test/test1/TestConfig.json @@ -0,0 +1,3 @@ +{ + "DriveName": "data" +} \ No newline at end of file diff --git a/test/test2/TestConfig.json b/test/test2/TestConfig.json new file mode 100644 index 0000000..80f8899 --- /dev/null +++ b/test/test2/TestConfig.json @@ -0,0 +1,23 @@ +{ + "ObjectArray": [ + { + "PropertyInt": 1, + "PropertyList": [ + 4, + 5, + 6 + ], + "PropertyString": "test_value", + "PropertyBool": true + }, + { + "PropertyInt": 2, + "PropertyList": [ + "one", + "two" + ], + "PropertyString": "test_value_2", + "PropertyBool": false + } + ] +} \ No newline at end of file diff --git a/test/test3/TestConfig.json b/test/test3/TestConfig.json new file mode 100644 index 0000000..18fd875 --- /dev/null +++ b/test/test3/TestConfig.json @@ -0,0 +1,13 @@ +{ + "plan": [ + { + "timeout": "0:0:10", + "headers": { + "Server": "gws" + }, + "label": "google", + "code": 200, + "url": "https://www.google.com" + } + ] +} \ No newline at end of file diff --git a/test/test3/TestConfig.psd1 b/test/test3/TestConfig.psd1 index 75eee34..6df7ae8 100644 --- a/test/test3/TestConfig.psd1 +++ b/test/test3/TestConfig.psd1 @@ -1,5 +1,5 @@ @{ - Plan = @( + plan = @( @{ label = "google" url = "https://www.google.com"