Skip to content

Commit a4c9880

Browse files
committed
Add validation functions for Plaster manifest content and parameters
- Implemented `Test-JsonManifestContent` to validate content actions in Plaster manifests, ensuring required properties are present for each action type. - Created `Test-JsonManifestParameters` to validate parameters in Plaster manifests, checking for required properties, valid names, and types. - Added `Test-PlasterCondition` to validate the syntax of conditions in manifests. - Updated `Invoke-Plaster` to streamline parameter handling and improve manifest loading logic. - Removed the deprecated `Write-PlasterLog` function to simplify logging. - Enhanced documentation for `Invoke-Plaster` and `New-PlasterManifest` to reflect new parameter options and usage. - Added comprehensive tests for `New-PlasterManifest` to ensure correct manifest generation and validation. - Adjusted tests for `RequireModule` and `TestPlasterManifest` to align with new validation logic and output expectations.
1 parent 694a9c5 commit a4c9880

25 files changed

+1286
-1388
lines changed

Plaster/JsonManifestHandler.ps1

Lines changed: 0 additions & 1004 deletions
This file was deleted.

Plaster/Plaster.psm1

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -115,42 +115,6 @@ $ParameterDefaultValueStoreRootPath = switch ($true) {
115115
"$Home/.plaster"
116116
}
117117
}
118-
# Dot source the individual module command scripts with error handling
119-
$commandFiles = @(
120-
'writePlasterLog.ps1'
121-
'PlasterVariables.ps1'
122-
'JsonManifestHandler.ps1'
123-
'NewPlasterManifest.ps1'
124-
'TestPlasterManifest.ps1'
125-
'GetPlasterTemplate.ps1'
126-
'InvokePlaster.ps1'
127-
128-
)
129-
foreach ($file in $commandFiles) {
130-
$filePath = Join-Path $PSScriptRoot $file
131-
if (Test-Path $filePath) {
132-
try {
133-
Write-Verbose "Loading command file: $file"
134-
. $filePath
135-
Write-Verbose "Successfully loaded: $file"
136-
} catch {
137-
$errorMessage = "Failed to load command file '$file': $($_.Exception.Message)"
138-
if (Get-Command Write-PlasterLog -ErrorAction SilentlyContinue) {
139-
Write-PlasterLog -Level Error -Message $errorMessage
140-
} else {
141-
Write-Error $errorMessage
142-
}
143-
throw $_
144-
}
145-
} else {
146-
$warningMessage = "Command file not found: $filePath"
147-
if (Get-Command Write-PlasterLog -ErrorAction SilentlyContinue) {
148-
Write-PlasterLog -Level Warning -Message $warningMessage
149-
} else {
150-
Write-Warning $warningMessage
151-
}
152-
}
153-
}
154118

155119
# Enhanced platform detection with fallback
156120
if (-not (Get-Variable -Name 'IsWindows' -ErrorAction SilentlyContinue)) {
@@ -175,6 +139,26 @@ if (-not $script:XmlSchemaValidationSupported) {
175139
# Module logging configuration
176140
$script:LogLevel = if ($env:PLASTER_LOG_LEVEL) { $env:PLASTER_LOG_LEVEL } else { 'Information' }
177141

142+
# Global variables and constants for Plaster 2.0
143+
144+
# Enhanced $TargetNamespace definition with proper scoping
145+
if (-not (Get-Variable -Name 'TargetNamespace' -Scope Script -ErrorAction SilentlyContinue)) {
146+
Set-Variable -Name 'TargetNamespace' -Value 'http://www.microsoft.com/schemas/PowerShell/Plaster/v1' -Scope Script -Option ReadOnly
147+
}
148+
149+
# Enhanced $DefaultEncoding definition
150+
if (-not (Get-Variable -Name 'DefaultEncoding' -Scope Script -ErrorAction SilentlyContinue)) {
151+
Set-Variable -Name 'DefaultEncoding' -Value 'UTF8-NoBOM' -Scope Script -Option ReadOnly
152+
}
153+
154+
# JSON Schema version for new manifests
155+
if (-not (Get-Variable -Name 'JsonSchemaVersion' -Scope Script -ErrorAction SilentlyContinue)) {
156+
Set-Variable -Name 'JsonSchemaVersion' -Value '2.0' -Scope Script -Option ReadOnly
157+
}
158+
159+
# Export the variables that need to be available globally
160+
Export-ModuleMember -Variable @('TargetNamespace', 'DefaultEncoding', 'JsonSchemaVersion')
161+
178162
# Module cleanup on removal
179163
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
180164
Write-PlasterLog -Level Information -Message "Plaster module is being removed"

Plaster/PlasterVariables.ps1

Lines changed: 0 additions & 22 deletions
This file was deleted.
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
function ConvertFrom-JsonContentAction {
2+
[CmdletBinding()]
3+
[OutputType([System.Xml.XmlElement])]
4+
param(
5+
[Parameter(Mandatory)]
6+
[object]$Action,
7+
8+
[Parameter(Mandatory)]
9+
[System.Xml.XmlDocument]$XmlDocument
10+
)
11+
12+
switch ($Action.type) {
13+
'message' {
14+
$element = $XmlDocument.CreateElement('message', $TargetNamespace)
15+
$element.InnerText = $Action.text
16+
17+
if ($Action.noNewline) {
18+
$element.SetAttribute('nonewline', 'true')
19+
}
20+
}
21+
'file' {
22+
$element = $XmlDocument.CreateElement('file', $TargetNamespace)
23+
$element.SetAttribute('source', $Action.source)
24+
$element.SetAttribute('destination', $Action.destination)
25+
26+
if ($Action.encoding) {
27+
$element.SetAttribute('encoding', $Action.encoding)
28+
}
29+
30+
if ($Action.openInEditor) {
31+
$element.SetAttribute('openInEditor', 'true')
32+
}
33+
}
34+
'templateFile' {
35+
$element = $XmlDocument.CreateElement('templateFile', $TargetNamespace)
36+
$element.SetAttribute('source', $Action.source)
37+
$element.SetAttribute('destination', $Action.destination)
38+
39+
if ($Action.encoding) {
40+
$element.SetAttribute('encoding', $Action.encoding)
41+
}
42+
43+
if ($Action.openInEditor) {
44+
$element.SetAttribute('openInEditor', 'true')
45+
}
46+
}
47+
'directory' {
48+
$element = $XmlDocument.CreateElement('file', $TargetNamespace)
49+
$element.SetAttribute('source', '')
50+
$element.SetAttribute('destination', $Action.destination)
51+
}
52+
'newModuleManifest' {
53+
$element = $XmlDocument.CreateElement('newModuleManifest', $TargetNamespace)
54+
$element.SetAttribute('destination', $Action.destination)
55+
56+
$manifestProperties = @('moduleVersion', 'rootModule', 'author', 'companyName', 'description', 'powerShellVersion', 'copyright', 'encoding')
57+
foreach ($property in $manifestProperties) {
58+
if ($Action.PSObject.Properties[$property]) {
59+
$element.SetAttribute($property, $Action.$property)
60+
}
61+
}
62+
63+
if ($Action.openInEditor) {
64+
$element.SetAttribute('openInEditor', 'true')
65+
}
66+
}
67+
'modify' {
68+
$element = $XmlDocument.CreateElement('modify', $TargetNamespace)
69+
$element.SetAttribute('path', $Action.path)
70+
71+
if ($Action.encoding) {
72+
$element.SetAttribute('encoding', $Action.encoding)
73+
}
74+
75+
# Add modifications
76+
foreach ($modification in $Action.modifications) {
77+
if ($modification.type -eq 'replace') {
78+
$replaceElement = $XmlDocument.CreateElement('replace', $TargetNamespace)
79+
80+
$originalElement = $XmlDocument.CreateElement('original', $TargetNamespace)
81+
$originalElement.InnerText = $modification.search
82+
if ($modification.isRegex) {
83+
$originalElement.SetAttribute('expand', 'true')
84+
}
85+
$replaceElement.AppendChild($originalElement)
86+
87+
$substituteElement = $XmlDocument.CreateElement('substitute', $TargetNamespace)
88+
$substituteElement.InnerText = $modification.replace
89+
$substituteElement.SetAttribute('expand', 'true')
90+
$replaceElement.AppendChild($substituteElement)
91+
92+
if ($modification.condition) {
93+
$replaceElement.SetAttribute('condition', $modification.condition)
94+
}
95+
96+
$element.AppendChild($replaceElement)
97+
}
98+
}
99+
}
100+
'requireModule' {
101+
$element = $XmlDocument.CreateElement('requireModule', $TargetNamespace)
102+
$element.SetAttribute('name', $Action.name)
103+
104+
$moduleProperties = @('minimumVersion', 'maximumVersion', 'requiredVersion', 'message')
105+
foreach ($property in $moduleProperties) {
106+
if ($Action.PSObject.Properties[$property]) {
107+
$element.SetAttribute($property, $Action.$property)
108+
}
109+
}
110+
}
111+
'execute' {
112+
# Execute action doesn't have direct XML equivalent, convert to message with warning
113+
$element = $XmlDocument.CreateElement('message', $TargetNamespace)
114+
$element.InnerText = "Warning: Execute action not supported in XML format. Script: $($Action.script)"
115+
Write-PlasterLog -Level Warning -Message "Execute action converted to message - not supported in XML format"
116+
}
117+
default {
118+
throw "Unknown action type: $($Action.type)"
119+
}
120+
}
121+
122+
# Add condition if present
123+
if ($Action.condition) {
124+
$element.SetAttribute('condition', $Action.condition)
125+
}
126+
127+
return $element
128+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
function ConvertFrom-JsonManifest {
2+
[CmdletBinding()]
3+
[OutputType([System.Xml.XmlDocument])]
4+
param(
5+
[Parameter(Mandatory, ValueFromPipeline)]
6+
[string]$JsonContent,
7+
8+
[Parameter()]
9+
[switch]$Validate = $true
10+
)
11+
12+
begin {
13+
Write-PlasterLog -Level Debug -Message "Converting JSON manifest to internal format"
14+
}
15+
16+
process {
17+
try {
18+
# Validate JSON if requested
19+
if ($Validate) {
20+
$isValid = Test-JsonManifest -JsonContent $JsonContent -Detailed
21+
if (-not $isValid) {
22+
throw "JSON manifest validation failed"
23+
}
24+
}
25+
26+
# Parse JSON
27+
$jsonObject = $JsonContent | ConvertFrom-Json
28+
29+
# Create XML document
30+
$xmlDoc = New-Object System.Xml.XmlDocument
31+
$xmlDoc.LoadXml('<?xml version="1.0" encoding="utf-8"?><plasterManifest xmlns="http://www.microsoft.com/schemas/PowerShell/Plaster/v1"></plasterManifest>')
32+
33+
$manifest = $xmlDoc.DocumentElement
34+
$manifest.SetAttribute('schemaVersion', '1.2') # Use XML schema version for compatibility
35+
36+
if ($jsonObject.metadata.templateType) {
37+
$manifest.SetAttribute('templateType', $jsonObject.metadata.templateType)
38+
}
39+
40+
# Add metadata
41+
$metadataElement = $xmlDoc.CreateElement('metadata', $TargetNamespace)
42+
$manifest.AppendChild($metadataElement)
43+
44+
# Add metadata properties
45+
$metadataProperties = @('name', 'id', 'version', 'title', 'description', 'author', 'tags')
46+
foreach ($property in $metadataProperties) {
47+
if ($jsonObject.metadata.PSObject.Properties[$property]) {
48+
$element = $xmlDoc.CreateElement($property, $TargetNamespace)
49+
$value = $jsonObject.metadata.$property
50+
51+
if ($property -eq 'tags' -and $value -is [array]) {
52+
$element.InnerText = $value -join ', '
53+
} else {
54+
$element.InnerText = $value
55+
}
56+
$metadataElement.AppendChild($element)
57+
}
58+
}
59+
60+
# Add parameters
61+
$parametersElement = $xmlDoc.CreateElement('parameters', $TargetNamespace)
62+
$manifest.AppendChild($parametersElement)
63+
64+
if ($jsonObject.parameters) {
65+
foreach ($param in $jsonObject.parameters) {
66+
$paramElement = $xmlDoc.CreateElement('parameter', $TargetNamespace)
67+
$paramElement.SetAttribute('name', $param.name)
68+
$paramElement.SetAttribute('type', $param.type)
69+
70+
if ($param.prompt) {
71+
$paramElement.SetAttribute('prompt', $param.prompt)
72+
}
73+
74+
if ($param.default) {
75+
if ($param.default -is [array]) {
76+
$paramElement.SetAttribute('default', ($param.default -join ','))
77+
} else {
78+
$paramElement.SetAttribute('default', $param.default)
79+
}
80+
}
81+
82+
if ($param.condition) {
83+
$paramElement.SetAttribute('condition', $param.condition)
84+
}
85+
86+
if ($param.store) {
87+
$paramElement.SetAttribute('store', $param.store)
88+
}
89+
90+
# Add choices for choice/multichoice parameters
91+
if ($param.choices) {
92+
foreach ($choice in $param.choices) {
93+
$choiceElement = $xmlDoc.CreateElement('choice', $TargetNamespace)
94+
$choiceElement.SetAttribute('label', $choice.label)
95+
$choiceElement.SetAttribute('value', $choice.value)
96+
97+
if ($choice.help) {
98+
$choiceElement.SetAttribute('help', $choice.help)
99+
}
100+
101+
$paramElement.AppendChild($choiceElement)
102+
}
103+
}
104+
105+
$parametersElement.AppendChild($paramElement)
106+
}
107+
}
108+
109+
# Add content
110+
$contentElement = $xmlDoc.CreateElement('content', $TargetNamespace)
111+
$manifest.AppendChild($contentElement)
112+
113+
foreach ($action in $jsonObject.content) {
114+
$actionElement = ConvertFrom-JsonContentAction -Action $action -XmlDocument $xmlDoc
115+
$contentElement.AppendChild($actionElement)
116+
}
117+
118+
Write-PlasterLog -Level Debug -Message "JSON to XML conversion completed successfully"
119+
return $xmlDoc
120+
} catch {
121+
$errorMessage = "Failed to convert JSON manifest: $($_.Exception.Message)"
122+
Write-PlasterLog -Level Error -Message $errorMessage
123+
throw $_
124+
}
125+
}
126+
}

0 commit comments

Comments
 (0)