diff --git a/src/Arcus.Scripting.ARM/Scripts/Inject-ArmContent.ps1 b/src/Arcus.Scripting.ARM/Scripts/Inject-ArmContent.ps1 index 690516db..f1a60157 100644 --- a/src/Arcus.Scripting.ARM/Scripts/Inject-ArmContent.ps1 +++ b/src/Arcus.Scripting.ARM/Scripts/Inject-ArmContent.ps1 @@ -1,127 +1,127 @@ -<# - Possible injection instructions in ARM templates or recursively referenced files: - - ${ fileToInject.xml } - ${ FileToInject=file.xml } - ${ FileToInject = ".\Parent Directory\file.xml" } - ${ FileToInject = ".\Parent Directory\file.xml", EscapeJson, ReplaceSpecialChars } - ${ FileToInject = '.\Parent Directory\file.json', InjectAsJsonObject } - #> - -param ( - [string] $Path = $PSScriptRoot -) - -function InjectFile { - param( - [string] $filePath - ) - - Write-Host "Checking file $filePath" - - $replaceContentDelegate = { - param($match) - - $completeInjectionInstruction = $match.Groups[1].Value; - $instructionParts = @($completeInjectionInstruction -split "," | foreach { $_.Trim() } ) - - $filePart = $instructionParts[0]; - # Regex uses non-capturing group for 'FileToInject' part, - # afterwards character classes and backreferencing to select the optional single or double quotes - $fileToInjectPathRegex = [regex] "^(?:FileToInject\s*=\s*)?([`"`']?)(?.*?)\1?$"; - $fileMatch = $fileToInjectPathRegex.Match($filePart) - if ($fileMatch.Success -ne $True){ - throw "The file part '$filePart' of the injection instruction could not be parsed correctly" - } - - $relativePathOfFileToInject = $fileMatch.Groups["File"]; - $fullPathOfFileToInject = Join-Path (Split-Path $filePath -Parent) $relativePathOfFileToInject - $fileToInjectIsFound = Test-Path -Path $fullPathOfFileToInject -PathType Leaf - if ($false -eq $fileToInjectIsFound) { - throw "No file can be found at '$fullPathofFileToInject'" - } - - # Inject content recursively first - InjectFile($fullPathOfFileToInject) - - Write-Host "`t Injecting content of $fullPathOfFileToInject into $filePath" - - $newString = Get-Content -Path $fullPathOfFileToInject -Raw - - # XML declaration can only appear on the first line of an XML document, so remove when injecting - $newString = $newString -replace '(<\?xml).+(\?>)(\r)?(\n)?', "" - - # By default: retain double quotes around content-to-inject, if present - $surroundContentWithDoubleQuotes = $match.Value.StartsWith('"') -and $match.Value.EndsWith('"') - - if ($instructionParts.Length -gt 1) { - $optionParts = $instructionParts | select -Skip 1 - - if ($optionParts.Contains("ReplaceSpecialChars")){ - Write-Host "`t Replacing special characters" - - # Replace newline characters with literal equivalents - if ([environment]::OSVersion.VersionString -like "*Windows*") { - $newString = $newString -replace "`n", "\r\n" - } else { - $newString = $newString -replace "`n", "\n" - } - - # Replace tabs with spaces - $newString = $newString -replace "`t", " " - - # Replace " with \" - $newString = $newString -replace """", "\""" - } - - if ($optionParts.Contains("EscapeJson")) { - Write-Host "`t JSON-escaping file content" - - # Use regex negative lookbehind to replace double quotes not preceded by a backslash with escaped quotes - $newString = $newString -replace '(? + +param ( + [string] $Path = $PSScriptRoot +) + +function InjectFile { + param( + [string] $filePath + ) + + Write-Host "Checking file $filePath" + + $replaceContentDelegate = { + param($match) + + $completeInjectionInstruction = $match.Groups[1].Value; + $instructionParts = @($completeInjectionInstruction -split "," | foreach { $_.Trim() } ) + + $filePart = $instructionParts[0]; + # Regex uses non-capturing group for 'FileToInject' part, + # afterwards character classes and backreferencing to select the optional single or double quotes + $fileToInjectPathRegex = [regex] "^(?:FileToInject\s*=\s*)?([`"`']?)(?.*?)\1?$"; + $fileMatch = $fileToInjectPathRegex.Match($filePart) + if ($fileMatch.Success -ne $True){ + throw "The file part '$filePart' of the injection instruction could not be parsed correctly" + } + + $relativePathOfFileToInject = $fileMatch.Groups["File"]; + $fullPathOfFileToInject = Join-Path (Split-Path $filePath -Parent) $relativePathOfFileToInject + $fileToInjectIsFound = Test-Path -Path $fullPathOfFileToInject -PathType Leaf + if ($false -eq $fileToInjectIsFound) { + throw "No file can be found at '$fullPathofFileToInject'" + } + + # Inject content recursively first + InjectFile($fullPathOfFileToInject) + + Write-Host "`t Injecting content of $fullPathOfFileToInject into $filePath" + + $newString = Get-Content -Path $fullPathOfFileToInject -Raw + + # XML declaration can only appear on the first line of an XML document, so remove when injecting + $newString = $newString -replace '(<\?xml).+(\?>)(\r)?(\n)?', "" + + # By default: retain double quotes around content-to-inject, if present + $surroundContentWithDoubleQuotes = $match.Value.StartsWith('"') -and $match.Value.EndsWith('"') + + if ($instructionParts.Length -gt 1) { + $optionParts = $instructionParts | select -Skip 1 + + if ($optionParts.Contains("ReplaceSpecialChars")) { + Write-Host "`t Replacing special characters" + + # Replace newline characters with literal equivalents + if ([Environment]::OSVersion.VersionString -like "*Windows*") { + $newString = $newString -replace "`r`n", "\r\n" + } else { + $newString = $newString -replace "`n", "\n" + } + + # Replace tabs with spaces + $newString = $newString -replace "`t", " " + + # Replace " with \" + $newString = $newString -replace """", "\""" + } + + if ($optionParts.Contains("EscapeJson")) { + Write-Host "`t JSON-escaping file content" + + # Use regex negative lookbehind to replace double quotes not preceded by a backslash with escaped quotes + $newString = $newString -replace '(?" - } finally { - $originalFile = "$PSScriptRoot\Files\arm-template-escape-org.json" - Get-Content $originalFile | Out-File -FilePath $armTemplateFile - } - } - } - } +Import-Module -Name $PSScriptRoot\..\Arcus.Scripting.ARM -ErrorAction Stop + +InModuleScope Arcus.Scripting.ARM { + Describe "Arcus ARM integration tests" { + Context "ARM injection" { + It "Replaces file path with inline file contents" { + # Arrange + $armTemplateFile = "$PSScriptRoot\Files\arm-template-inline.json" + try { + # Act + Inject-ArmContent -Path $armTemplateFile + + # Assert + $expected = Get-Content "$PSScriptRoot\Files\arm-template-inline-value.json" + $actual = Get-Content $armTemplateFile + $actual[7] | Should -Be ' "value": "this is a test value",' + } finally { + $originalFile = "$PSScriptRoot\Files\arm-template-inline-org.json" + Get-Content $originalFile | Out-File -FilePath $armTemplateFile + } + } + if ([Environment]::OSVersion.VersionString -like "*Windows*") { + It "Replaces file path with file contents as JSON object (windows)" { + # Arrange + $armTemplateFile = "$PSScriptRoot\Files\arm-template-object (windows).json" + try { + # Act + Inject-ArmContent -Path $armTemplateFile + + # Assert + $expected = Get-Content "$PSScriptRoot\Files\arm-template-object-value (windows).json" + $actual = Get-Content $armTemplateFile + $actual[7] | Should -Be ' "value": "{\r\n \"test\": \"this is a test value\"\r\n}",' + } finally { + $originalFile = "$PSScriptRoot\Files\arm-template-object-org (windows).json" + Get-Content $originalFile | Out-File -FilePath $armTemplateFile + } + } + } else { + It "Replaces file path with file contents as JSON object (linux)" { + # Arrange + $armTemplateFile = "$PSScriptRoot\Files\arm-template-object (linux).json" + try { + # Act + Inject-ArmContent -Path $armTemplateFile + + # Assert + $expected = Get-Content "$PSScriptRoot\Files\arm-template-object-value (linux).json" + $actual = Get-Content $armTemplateFile + $actual[7] | Should -Be ' "value": "{\n \"test\": \"this is a test value\"\n}",' + } finally { + $originalFile = "$PSScriptRoot\Files\arm-template-object-org (linux).json" + Get-Content $originalFile | Out-File -FilePath $armTemplateFile + } + } + } + It "Replaces file path with file contents as escaped JSON and replaced special characters" { + # Arrange + $armTemplateFile = "$PSScriptRoot\Files\arm-template-escape.json" + try { + # Act + Inject-ArmContent -Path $armTemplateFile + + # Assert + $expected = Get-Content "$PSScriptRoot\Files\arm-template-escape-value.xml" + $actual = Get-Content $armTemplateFile + $actual[7] | Should -Be ' "value": "",' + } finally { + $originalFile = "$PSScriptRoot\Files\arm-template-escape-org.json" + Get-Content $originalFile | Out-File -FilePath $armTemplateFile + } + } + } + } } \ No newline at end of file diff --git a/src/Arcus.Scripting.Tests.Integration/Arcus.Scripting.Tests.Integration.pssproj b/src/Arcus.Scripting.Tests.Integration/Arcus.Scripting.Tests.Integration.pssproj index 336232ec..96eaf304 100644 --- a/src/Arcus.Scripting.Tests.Integration/Arcus.Scripting.Tests.Integration.pssproj +++ b/src/Arcus.Scripting.Tests.Integration/Arcus.Scripting.Tests.Integration.pssproj @@ -1,109 +1,112 @@ - - - Debug - 2.0 - {ba3ff06c-cefc-403d-bccb-82ae90452b89} - Exe - MyApplication - MyApplication - Arcus.Scripting.Tests.Integration - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Arcus.Scripting.ARM - {5499ba14-07a1-40ff-b7b4-17b19e4c9dbe} - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + Debug + 2.0 + {ba3ff06c-cefc-403d-bccb-82ae90452b89} + Exe + MyApplication + MyApplication + Arcus.Scripting.Tests.Integration + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + Arcus.Scripting.ARM + {5499ba14-07a1-40ff-b7b4-17b19e4c9dbe} + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Arcus.Scripting.Tests.Integration/Files/arm-template-inline-value.json b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-inline-value.json index 3fae2469..e2f54591 100644 --- a/src/Arcus.Scripting.Tests.Integration/Files/arm-template-inline-value.json +++ b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-inline-value.json @@ -1 +1 @@ -"this is a test value" \ No newline at end of file +this is a test value \ No newline at end of file diff --git a/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object (linux).json b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object (linux).json new file mode 100644 index 00000000..cb578f38 Binary files /dev/null and b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object (linux).json differ diff --git a/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object.json b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object (windows).json similarity index 86% rename from src/Arcus.Scripting.Tests.Integration/Files/arm-template-object.json rename to src/Arcus.Scripting.Tests.Integration/Files/arm-template-object (windows).json index 08e32cc9..42612e1d 100644 Binary files a/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object.json and b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object (windows).json differ diff --git a/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-org.json b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-org (linux).json similarity index 90% rename from src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-org.json rename to src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-org (linux).json index baa3beec..0bc16d13 100644 --- a/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-org.json +++ b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-org (linux).json @@ -5,7 +5,7 @@ "properties": { "subscriptionRequired": true, "path": "demo", - "value": "${ FileToInject='./../Files/arm-template-object-value.json', InjectAsJsonObject }$", + "value": "${ FileToInject='./../Files/arm-template-object-value (linux).json', InjectAsJsonObject }$", "format": "swagger-json" }, "tags": "[variables('Tags')]", diff --git a/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-org (windows).json b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-org (windows).json new file mode 100644 index 00000000..da0b91bb --- /dev/null +++ b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-org (windows).json @@ -0,0 +1,14 @@ +{ + "type": "Microsoft.ApiManagement/service/apis", + "name": "[concat(parameters('ApiManagement.Name'),'/', parameters('ApiManagement.Api.Name'))]", + "apiVersion": "2019-01-01", + "properties": { + "subscriptionRequired": true, + "path": "demo", + "value": "${ FileToInject='./../Files/arm-template-object-value (windows).json', ReplaceSpecialChars, InjectAsJsonObject }$", + "format": "swagger-json" + }, + "tags": "[variables('Tags')]", + "dependsOn": [ + ] +} \ No newline at end of file diff --git a/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-value (linux).json b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-value (linux).json new file mode 100644 index 00000000..2862a79e --- /dev/null +++ b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-value (linux).json @@ -0,0 +1,3 @@ +{ + "test": "this is a test value" +} \ No newline at end of file diff --git a/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-value (windows).json b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-value (windows).json new file mode 100644 index 00000000..849aa382 --- /dev/null +++ b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-value (windows).json @@ -0,0 +1,3 @@ +{ + "test": "this is a test value" +} \ No newline at end of file diff --git a/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-value.json b/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-value.json deleted file mode 100644 index 5fc0e148..00000000 --- a/src/Arcus.Scripting.Tests.Integration/Files/arm-template-object-value.json +++ /dev/null @@ -1 +0,0 @@ -{ "test": "this is a test value" } \ No newline at end of file