Skip to content

Commit

Permalink
Redesign Items using OpenAPI spec
Browse files Browse the repository at this point in the history
  • Loading branch information
fvanroie committed May 26, 2018
1 parent 9af38e1 commit a0dbaf9
Show file tree
Hide file tree
Showing 25 changed files with 2,652 additions and 578 deletions.
2,303 changes: 2,303 additions & 0 deletions Formats/Items.format.ps1xml

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion PS_OPNsense.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@
'Formats/HAProxy.format.ps1xml',
'Formats/Packages.format.ps1xml',
'Formats/Services.format.ps1xml',
'Formats/ClamAV.format.ps1xml'
'Formats/ClamAV.format.ps1xml',
'Formats/Items.format.ps1xml'
)

# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
Expand Down
18 changes: 18 additions & 0 deletions PS_OPNsense.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,24 @@ Try {
Throw ("Unable to load the API maps :`nError : {0}" -f $_)
}

# Compare Classes in OpenApi with Classes in PowerShell Module

# Load objectmap of api calls
$FullPath = ("{0}/{1}" -f $PSScriptRoot, 'Data/opnsense.json')
$OpenApiSpec = Get-Content $FullPath | ConvertFrom-Json

$cppClasses = [AppDomain]::CurrentDomain.GetAssemblies().ExportedTypes.Fullname |
Where-Object {$_ -like 'OPnsense.*'} | Select-Object -Unique
$apiClasses = $OpenApiSpec.components.schemas.psobject.Properties.name
$diff = Compare-Object $cppClasses $apiClasses
foreach ($Class in $diff) {
if ($Class.SideIndicator -eq '<=') {
Write-Warning ("{0} is not defined in the API specification." -f $Class.InputObject)
} else {
Write-Warning ("{0} is not defined in the Class Definitions." -f $Class.InputObject)
}
}

##### Static Export of Module Fucntions (Temporary situation untill all function get a proper script file)
Export-ModuleMember -Function Connect-OPNsense, Disconnect-OPNsense, Invoke-OPNsenseCommand
$f = @(########## PLUGINS ##########
Expand Down
28 changes: 22 additions & 6 deletions Private/Import-OpenApiData.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ Function Import-OpenApiData {
}

# NameSpace of the data object
$NamespaceIn = $prop.value.requestBody.content.'application/json'.schema.'$ref'
$NamespaceOut = $prop.value.responses.'200'.content.'application/json'.schema.'$ref'
#$RequestType = $prop.value.requestBody.content.'application/json'.schema.'$ref'
#$ResponseType = $prop.value.responses.'200'.content.'application/json'.schema.'$ref'

# Names of the Sublevel keys in the JSON to get to the data object
$levelsOut, $NamespaceOut = Find-OpenApiRef $prop.value.responses.'200'.content.'application/json'.schema
$levelsIn, $NamespaceIn = Find-OpenApiRef $prop.value.requestBody.content.'application/json'.schema
$levelsOut, $ResponseType = Find-OpenApiRef $prop.value.responses.'200'.content.'application/json'.schema
$levelsIn, $RequestType = Find-OpenApiRef $prop.value.requestBody.content.'application/json'.schema

# Find parameter objects from the schema based on the parameter reference in the path
$param = $OpenApiData.components.parameters.psobject.Properties |
Expand All @@ -69,8 +69,8 @@ Function Import-OpenApiData {
Method = $Prop.name
Path = $Path.Name
Parameters = $Param
NameSpaceIn = $NamespaceIn
NamespaceOut = $NamespaceOut
RequestType = $RequestType
ResponseType = $ResponseType
LevelsIn = $LevelsIn
LevelsOut = $LevelsOut
}
Expand All @@ -81,5 +81,21 @@ Function Import-OpenApiData {
}
}
}

# Set RequestType for toggle, delete and set to ReturnType of get to enable easy pipeline type discovery
$commands = $OpenApiHash.values.values.values | Where-Object { $_.action -eq 'get'}
foreach ($item in $commands) {
if ($item.ResponseType) {
$Module = $Item.Module
$Object = $Item.Object
foreach ($Action in 'toggle', 'delete', 'set') {
if ($OpenApiHash.$Module.$Action.$Object) {
if (-Not $OpenApiHash.$Module.$Action.$Object.RequestType) {
$OpenApiHash.$Module.$Action.$Object.RequestType = $Item.ResponseType
}
}
}
}
}
return $OpenApiHash
}
3 changes: 2 additions & 1 deletion Private/Invoke-OPNsenseCommand.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ Function Invoke-OPNsenseCommand {

# Drill down to the requested property level
foreach ($prop in $Property) {
if ($prop -in (Get-NoteProperty $result)) {
#if ($prop -in (Get-NoteProperty $result)) {
if ($prop -in $result.psobject.Properties.name) {
$result = Select-Object -InputObject $result -ExpandProperty $prop
} else {
Throw "$prop is an invalid property for object $Module/$Controller/$Command"
Expand Down
25 changes: 12 additions & 13 deletions Private/Invoke-OPNsenseOpenApiPath.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,10 @@ Function Invoke-OPNsenseOpenApiPath {

# [parameter(Mandatory = $false)][HashTable]$AddProperty,
[parameter(Mandatory = $false)][String]$Uuid,
[parameter(Mandatory = $false)][Boolean]$Enabled
[parameter(Mandatory = $false)][Switch]$Enabled
)

# Search the Appropriate API Call for this Action
#$call = Search-OPNsenseOpenApiPath -Module $Module -Action $Action -Object $Object
$call = $OPNsenseOpenApi.$Module.$Action.$Object
if (!$Call) {
Write-Error ("{0} {2} does not implement the {1} action. (#17)" -f $Module, $Action, $Object)
Expand All @@ -64,8 +63,8 @@ Function Invoke-OPNsenseOpenApiPath {
$key = '{{{0}}}' -f $parameter.Value.Name
switch ($key) {
'{uuid}' { $value = $uuid }
'{enabled}' { $value = $enabled }
'{disabled}' { $value = -Not $enabled }
'{enabled}' { $value = If ($PSBoundParameters.ContainsKey('Enabled')) { ConvertTo-Boolean $Enabled } else { '' } }
'{disabled}' { $value = If ($PSBoundParameters.ContainsKey('Enabled')) { ConvertTo-Boolean (-Not $Enabled) } else { '' } }
default { $value = '' }
}
$CmdParams = $CmdParams.replace($key, $value)
Expand All @@ -80,13 +79,13 @@ Function Invoke-OPNsenseOpenApiPath {
'Method' = $Call.Method
'Verbose' = $VerbosePreference
'Debug' = $DebugPreference
'Property' = $call.LevelsOut
'Property' = $call.LevelsOut
}
# Switch ($Action) {
# 'get' { $Splat.Add('Property', $Object) }
# 'search' { $Splat.Add('Property', 'rows') }
# default {}
# }

if ($Action -eq 'search') {
$splat.Property = 'rows'
}

if ($Body) {
$Splat.Add('Json', $Body)
}
Expand All @@ -104,9 +103,9 @@ Function Invoke-OPNsenseOpenApiPath {
}

# Can we cast this into our own object?
if ($returntype) {
Write-Verbose "Converting object to $returntype"
return ConvertTo-OPNsenseObject -TypeName $returntype -InputObject $result
if ($call.ResponseType) {
Write-Verbose ("Converting object to {0}" -f $call.ResponseType)
return ConvertTo-OPNsenseObject -TypeName $call.ResponseType -InputObject $result
}

# Return PSCustomObject instead
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<# MIT License
<# MIT License
Copyright (c) 2018 fvanroie, NetwiZe.be
Expand All @@ -21,28 +21,24 @@
SOFTWARE.
#>

Function Get-OPNsenseHAProxyLuaScript {
Function ConvertFrom-OPNsenseOptionList {
# .EXTERNALHELP ../PS_OPNsense.psd1-Help.xml
[OutputType([Object[]])]
[CmdletBinding()]
param (
[parameter(Mandatory = $false, ValueFromPipelineByPropertyname = $true)]
[AllowEmptyCollection()][string[]]$Uuid,
[parameter(Mandatory = $false, ValueFromPipelineByPropertyname = $true)]
[AllowEmptyCollection()][string[]]$Name,
[parameter(Mandatory = $false, ValueFromPipelineByPropertyname = $true)]
[AllowEmptyCollection()][string[]]$Description,

[parameter(Mandatory = $false)]
[ValidateSet(0, 1, '0', '1', $False, $True)]$Enabled
Param(
[parameter(Mandatory = $true, position = 0)][PSObject]$InputObject # Object
)
BEGIN {
$allobj = Invoke-OPNsenseFunction HAProxy search Lua
$result = @()
}
PROCESS {
$result += Select-OPNsenseItem -InputObject $allobj @PSBoundParameters # Filter on properties
}
END {
return $result | Add-ObjectDetail -TypeName (Get-OPNsenseItemType HAProxy Lua)

$OptionList = New-Object -TypeName System.Collections.Hashtable
foreach ($prop in $InputObject.PSObject.properties) {
# Property names with a signle space were a workaround to get the JSON parsed properly
# Restore the original key by replacing the space with an empty string
if ($prop.name -eq ' ') {
$key = ''
} else {
$key = $prop.name
}
$OptionList.Add($key, $prop.value)
}
}
return $OptionList
}
11 changes: 8 additions & 3 deletions Public/ConvertTo-OPNsenseObject.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Function ConvertTo-OPNsenseObject {
# Property List of the InputObject
$objprop = Get-Member -InputObject $obj -MemberType NoteProperty

# Check if the needed reference parameters exist in the input object, add missing properties with default values
# Make sure ALL reference properties exist in the input object, add missing properties with default values
$diff = Compare-Object $objprop.name $refprop.name | Where-Object { $_.SideIndicator -eq '=>' }
foreach ($item in $diff) {
Write-Warning ("Property '{0}' was expected but not found. Using the default value instead." -f $item.inputObject)
Expand All @@ -57,8 +57,13 @@ Function ConvertTo-OPNsenseObject {
# Build argument list collection
$arglist = New-Object System.Collections.Generic.List[System.Object]
$refprop.name | Foreach-Object {
Write-Verbose ("* {0} : {1}" -f $_, $obj.$_)
$arglist.Add($obj.$_)
Write-Verbose (" * {0} : {1}" -f $_, $obj.$_)
if ($obj.$_.gettype().name -eq 'PSCustomObject') {
# ConvertTo an Array of Dictionary Entries
$arglist.Add( $(ConvertFrom-OPNsenseOptionList $obj.$_) )
} else {
$arglist.Add($obj.$_)
}
# WARNING: Cannot use += here because you can't do ( $arglist += $null ) to add values that are NULL !!
# Using '+=' when the value is $null would result in the property being skipped and the Contructor will fail due to missing arguments
}
Expand Down
50 changes: 21 additions & 29 deletions Public/Items/Disable-OPNsenseItem.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

Function Disable-OPNsenseItem {
# .EXTERNALHELP ../PS_OPNsense.psd1-Help.xml
[OutputType([Object[]])]
[CmdletBinding(
SupportsShouldProcess = $true,
ConfirmImpact = "Low"
Expand All @@ -31,44 +32,35 @@ Function Disable-OPNsenseItem {
[parameter(Mandatory = $true, position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Object[]]$InputObject,

[parameter(Mandatory = $false)]
[Switch]$Force,

[parameter(Mandatory = $false)]
[Switch]$PassThru
)
BEGIN {
# Get the Verb of the cmdlet
$verb, $null = $MyInvocation.MyCommand -split "-", 2
$command = "toggle"
$enabled = ($verb -eq "Enable")
$commands = Get-OPNsenseItemMap | Where-Object { $_.command -eq $command }
# Get the Verb of this cmdlet
$Verb, $null = $MyInvocation.MyCommand -split "-", 2
$Action = 'toggle'
$Enabled = ($verb -eq "Enable")

# Pre-filter all matching api calls for this action
$Commands = $OPNsenseOpenApi.values.values.values | Where-Object { $_.action -eq $action }
}
PROCESS {
foreach ($obj in $InputObject) {
$cmd = $commands | Where-Object { $_.returntype -eq $obj.gettype().FullName }

# Check if an api command was found for this TypeName
if (!$cmd) {
Write-Warning ("Unable to process object of type {0}" -f $obj.gettype().FullName)
Continue # With next object
}

# Ask confirmation before removal, if needed
if ($pscmdlet.ShouldProcess( ("{{{0}}}" -f $obj.Uuid) , ("{0} {1}" -f $verb, $cmd.ObjectName) ) ) {

# Be Verbose
Write-Verbose ("{0} {1} {{{2}}}" -f $verb, $cmd.ObjectName, $obj.Uuid)

$result = Invoke-OPNsenseFunction -Module $cmd.Module -Command $command -Object $cmd.Object -Uuid $obj.Uuid -Enable $enabled
foreach ($Obj in $InputObject) {

if (Test-OPnsenseApiResult $result) {
# Output results
if ($PassThru) {
Invoke-OPNsenseFunction -Module $cmd.Module -Command get -Object $cmd.Object -Uuid $obj.Uuid
}
# Check if an api command was found for this action and returntype
$Call = $Commands | Where-Object { $_.RequestType -eq $Obj.gettype().FullName }

} else {
Write-Warning ("Failed to {0} {1} {{{2}}}" -f $verb.toLower(), $cmd.ObjectName, $obj.Uuid)
}
# Ask confirmation before performing action, if needed
if ($Force -Or $PSCmdlet.ShouldProcess(
("{{{0}}}" -f $Obj.Uuid) ,
("{0} {1}" -f $Verb, $Call.Object)
) ) {

Invoke-OPNsenseItem -Call $Call -InputObject $Obj -Enabled:$Enabled -Verb $Verb -PassThru:$PassThru

} # ShouldProcess

}
Expand Down
50 changes: 21 additions & 29 deletions Public/Items/Enable-OPNsenseItem.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

Function Enable-OPNsenseItem {
# .EXTERNALHELP ../PS_OPNsense.psd1-Help.xml
[OutputType([Object[]])]
[CmdletBinding(
SupportsShouldProcess = $true,
ConfirmImpact = "Low"
Expand All @@ -31,44 +32,35 @@ Function Enable-OPNsenseItem {
[parameter(Mandatory = $true, position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Object[]]$InputObject,

[parameter(Mandatory = $false)]
[Switch]$Force,

[parameter(Mandatory = $false)]
[Switch]$PassThru
)
BEGIN {
# Get the Verb of the cmdlet
$verb, $null = $MyInvocation.MyCommand -split "-", 2
$command = "toggle"
$enabled = ($verb -eq "Enable")
$commands = Get-OPNsenseItemMap | Where-Object { $_.command -eq $command }
# Get the Verb of this cmdlet
$Verb, $null = $MyInvocation.MyCommand -split "-", 2
$Action = 'toggle'
$Enabled = ($verb -eq "Enable")

# Pre-filter all matching api calls for this action
$Commands = $OPNsenseOpenApi.values.values.values | Where-Object { $_.action -eq $action }
}
PROCESS {
foreach ($obj in $InputObject) {
$cmd = $commands | Where-Object { $_.returntype -eq $obj.gettype().FullName }

# Check if an api command was found for this TypeName
if (!$cmd) {
Write-Warning ("Unable to process object of type {0}" -f $obj.gettype().FullName)
Continue # With next object
}

# Ask confirmation before removal, if needed
if ($pscmdlet.ShouldProcess( ("{{{0}}}" -f $obj.Uuid) , ("{0} {1}" -f $verb, $cmd.ObjectName) ) ) {

# Be Verbose
Write-Verbose ("{0} {1} {{{2}}}" -f $verb, $cmd.ObjectName, $obj.Uuid)

$result = Invoke-OPNsenseFunction -Module $cmd.Module -Command $command -Object $cmd.Object -Uuid $obj.Uuid -Enable $enabled
foreach ($Obj in $InputObject) {

if (Test-OPnsenseApiResult $result) {
# Output results
if ($PassThru) {
Invoke-OPNsenseFunction -Module $cmd.Module -Command get -Object $cmd.Object -Uuid $obj.Uuid
}
# Check if an api command was found for this action and returntype
$Call = $Commands | Where-Object { $_.RequestType -eq $Obj.gettype().FullName }

} else {
Write-Warning ("Failed to {0} {1} {{{2}}}" -f $verb.toLower(), $cmd.ObjectName, $obj.Uuid)
}
# Ask confirmation before performing action, if needed
if ($Force -Or $PSCmdlet.ShouldProcess(
("{{{0}}}" -f $Obj.Uuid) ,
("{0} {1}" -f $Verb, $Call.Object)
) ) {

Invoke-OPNsenseItem -Call $Call -InputObject $Obj -Enabled:$Enabled -Verb $Verb -PassThru:$PassThru

} # ShouldProcess

}
Expand Down
5 changes: 4 additions & 1 deletion Public/Items/Get-OPNsenseItem.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@ InModuleScope PS_OPNsense {

Context "Module $Module" {

It "Get <item> should not throw" -TestCases $testcases {
It "<item>" -TestCases $testcases {
param($module, $item)
if ($module -eq 'relayd') {
Set-TestInconclusive "$Module is under development"
}
{
$result = Get-OPNsenseItem -Module $module -Item $item
} | should Not Throw
{
$result = New-OPNsenseItem -Module $module -Item $item
} | should Not Throw
}
}
}
Expand Down

0 comments on commit a0dbaf9

Please sign in to comment.