Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7205552

Browse files
authoredJun 8, 2023
Refactor how Computed values are calculated. (#44)
# Pull Request ## Issue #40 ## Description The previous implementation had computed calculations happening in several places, that was quite bad(tm). Making the calculation of 'Computed' values it's own step after values are calculated making things easier to test. ## License By submitting this pull request, I confirm that my contribution is made under the terms of the projects associated license.
1 parent 9b2aa2a commit 7205552

8 files changed

+200
-302
lines changed
 

‎src/ALZ/Private/Build-ALZDeploymentEnvFile.ps1

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ function Build-ALZDeploymentEnvFile {
2626
if ($target.Destination -eq "Environment") {
2727

2828
$formattedValue = $configurationValue.Value.Value
29-
if ($configurationValue.Value.Type -eq "Computed") {
30-
$formattedValue = Format-TokenizedConfigurationString -tokenizedString $configurationValue.Value.Value -configuration $configuration
31-
}
32-
3329
Add-Content -Path $envFile -Value "$($($target.Name))=`"$formattedValue`"" | Out-String | Write-Verbose
3430
}
3531
}

‎src/ALZ/Private/Edit-ALZConfigurationFilesInPlace.ps1

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -64,37 +64,7 @@ function Edit-ALZConfigurationFilesInPlace {
6464
# If we're here, we can modify this file and we've got an actual object specified by the Name path value - and we can modify values on it.
6565
if ($target.Destination -eq "Parameters" -and $null -ne $bicepConfigNode) {
6666
$leafPropertyName = $propertyNames[-1]
67-
68-
if ($configKey.Value.Type -eq "Computed") {
69-
# If the value type is computed we replace the value with another which already exists in the configuration hierarchy.
70-
if ($configKey.Value.Value -is [array]) {
71-
$formattedValues = @()
72-
foreach($formatString in $configKey.Value.Value) {
73-
$formattedValues += Format-TokenizedConfigurationString -tokenizedString $formatString -configuration $configuration
74-
}
75-
76-
if ($null -ne $configKey.Value.Process) {
77-
$scriptBlock = [ScriptBlock]::Create($configKey.Value.Process)
78-
$formattedValues = Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $formattedValues
79-
$formattedValues = @($formattedValues)
80-
}
81-
82-
$bicepConfigNode[$leafPropertyName] = $formattedValues
83-
} else {
84-
85-
$formattedValue = Format-TokenizedConfigurationString -tokenizedString $configKey.Value.Value -configuration $configuration
86-
87-
if ($null -ne $configKey.Value.Process) {
88-
$scriptBlock = [ScriptBlock]::Create($configKey.Value.Process)
89-
$formattedValue = Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $formattedValue
90-
}
91-
92-
$bicepConfigNode[$leafPropertyName] = $formattedValue
93-
}
94-
} else {
95-
$bicepConfigNode[$leafPropertyName] = $configKey.Value.Value
96-
}
97-
67+
$bicepConfigNode[$leafPropertyName] = $configKey.Value.Value
9868
$modified = $true
9969
}
10070
}

‎src/ALZ/Private/New-ALZDirectoryEnvironment.ps1

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,4 @@ function New-ALZDirectoryEnvironment {
1818
New-Item -ItemType Directory -Path $config -Force | Out-String | Write-Verbose
1919
New-Item -ItemType Directory -Path $upstream -Force | Out-String | Write-Verbose
2020
New-Item -ItemType Directory -Path $configModules -Force | Out-String | Write-Verbose
21-
2221
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# If the value type is computed we replace the value with another which already exists in the configuration hierarchy.
2+
function Set-ComputedConfiguration {
3+
[CmdletBinding(SupportsShouldProcess = $true)]
4+
param (
5+
[Parameter(Mandatory = $true)]
6+
[PSCustomObject] $configuration
7+
)
8+
9+
if ($PSCmdlet.ShouldProcess("ALZ-Bicep computed configuration.", "calculate computed values")) {
10+
foreach ($configKey in $configuration.PsObject.Properties) {
11+
if ($configKey.Value.Type -ne "Computed") {
12+
continue;
13+
}
14+
15+
if ($configKey.Value.Value -is [array]) {
16+
$formattedValues = @()
17+
foreach($formatString in $configKey.Value.Value) {
18+
$formattedValues += Format-TokenizedConfigurationString -tokenizedString $formatString -configuration $configuration
19+
}
20+
21+
if ($null -ne $configKey.Value.Process) {
22+
$scriptBlock = [ScriptBlock]::Create($configKey.Value.Process)
23+
$formattedValues = Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $formattedValues
24+
$formattedValues = @($formattedValues)
25+
}
26+
27+
$configKey.Value.Value = $formattedValues
28+
} else {
29+
30+
$formattedValue = Format-TokenizedConfigurationString -tokenizedString $configKey.Value.Value -configuration $configuration
31+
32+
if ($null -ne $configKey.Value.Process) {
33+
$scriptBlock = [ScriptBlock]::Create($configKey.Value.Process)
34+
$formattedValue = Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $formattedValue
35+
}
36+
37+
$configKey.Value.Value = $formattedValue
38+
}
39+
}
40+
}
41+
}

‎src/ALZ/Public/New-ALZEnvironment.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,14 @@ function New-ALZEnvironment {
5454
$alzEnvironmentDestinationInternalCode = Join-Path $alzEnvironmentDestination "upstream-releases"
5555

5656
Get-ALZGithubRelease -directoryForReleases $alzEnvironmentDestinationInternalCode -githubRepoUrl $bicepConfig.module_url -releases @($bicepConfig.version) | Out-String | Write-Verbose
57-
57+
5858
Write-InformationColored "Copying ALZ-Bicep module to $alzEnvironmentDestinationInternalCode" -ForegroundColor Green -InformationAction Continue
5959
Copy-ALZParametersFile -alzEnvironmentDestination $alzEnvironmentDestination -upstreamReleaseDirectory $(Join-Path $alzEnvironmentDestinationInternalCode $bicepConfig.version) -configFiles $bicepConfig.config_files | Out-String | Write-Verbose
6060
Write-InformationColored "ALZ-Bicep source directory: $alzBicepSourceDirectory" -ForegroundColor Green -InformationAction Continue
6161

6262
$configuration = Request-ALZEnvironmentConfig -configurationParameters $bicepConfig.parameters
6363

64+
Set-ComputedConfiguration -configuration $configuration | Out-String | Write-Verbose
6465
Edit-ALZConfigurationFilesInPlace -alzEnvironmentDestination $alzEnvironmentDestination -configuration $configuration | Out-String | Write-Verbose
6566
Build-ALZDeploymentEnvFile -configuration $configuration -Destination $alzEnvironmentDestination | Out-String | Write-Verbose
6667

‎src/Tests/Unit/Private/Build-ALZDeploymentEnvFile.Tests.ps1

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -77,38 +77,6 @@ InModuleScope 'ALZ' {
7777
Should -Invoke New-Item -ParameterFilter { $Path -match ".env$" } -Scope It -Times 1 -Exactly
7878
Should -Invoke Add-Content -Scope It -Times 1 -Exactly
7979
}
80-
81-
It 'Handles Computed values correctly and adds to the .env file.' {
82-
83-
Mock -CommandName New-Item
84-
Mock -CommandName Add-Content
85-
86-
$configuration = [pscustomobject]@{
87-
Setting1 = [pscustomobject]@{
88-
Targets = @(
89-
[pscustomobject]@{
90-
Name = "Setting1"
91-
Destination = "Environment"
92-
})
93-
Value = "Test"
94-
}
95-
Setting2 = [pscustomobject]@{
96-
Targets = @(
97-
[pscustomobject]@{
98-
Name = "Setting2"
99-
Destination = "Environment"
100-
})
101-
Type = "Computed"
102-
Value = "{%Setting1%}"
103-
}
104-
}
105-
106-
Build-ALZDeploymentEnvFile -configuration $configuration -destination "test"
107-
108-
Should -Invoke New-Item -ParameterFilter { $Path -match ".env$" } -Scope It -Times 1 -Exactly
109-
Should -Invoke Add-Content -ParameterFilter { $Value -match "^Setting1=`"Test`"$" } -Scope It -Times 1 -Exactly
110-
Should -Invoke Add-Content -ParameterFilter { $Value -match "^Setting2=`"Test`"$" } -Scope It -Times 1 -Exactly
111-
}
11280
}
11381
}
11482
}

‎src/Tests/Unit/Private/Edit-ALZConfigurationFilesInPlace.Tests.ps1

Lines changed: 1 addition & 233 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@ InModuleScope 'ALZ' {
447447
$secondFileContent
448448
}
449449

450+
Set-ComputedConfiguration -configuration $defaultConfig
450451
Edit-ALZConfigurationFilesInPlace -alzEnvironmentDestination '.' -configuration $defaultConfig
451452

452453
Should -Invoke -CommandName Out-File -Scope It -Times 2
@@ -469,239 +470,6 @@ InModuleScope 'ALZ' {
469470
Should -Invoke -CommandName Out-File -ParameterFilter { $FilePath -eq "test2.parameters.json" -and $InputObject -eq $contentStringAfterParsing } -Scope It
470471
}
471472

472-
It 'Computed, Processed array values replace values correctly' {
473-
$config = [pscustomobject]@{
474-
Nested = [pscustomobject]@{
475-
Type = "Computed"
476-
Description = "A Test Value"
477-
Process = '@($args | Select-Object -Unique)'
478-
Value = @(
479-
"1",
480-
"1",
481-
"3"
482-
)
483-
Targets = @(
484-
[pscustomobject]@{
485-
Name = "parValue.value"
486-
Destination = "Parameters"
487-
})
488-
}
489-
}
490-
491-
$fileContent = '{
492-
"parameters": {
493-
"parValue": {
494-
"value": []
495-
}
496-
}
497-
}'
498-
499-
$expectedContent = '{
500-
"parameters": {
501-
"parValue": {
502-
"value": [ "1", "3" ]
503-
}
504-
}
505-
}'
506-
507-
Mock -CommandName Get-Content -ParameterFilter { $Path -eq $testFile1Name } -MockWith {
508-
$fileContent
509-
}
510-
511-
$expectedContent = Format-ExpectedResult -expectedJson $expectedContent
512-
513-
Edit-ALZConfigurationFilesInPlace -alzEnvironmentDestination '.' -configuration $config
514-
515-
Should -Invoke -CommandName Out-File `
516-
-ParameterFilter { $FilePath -eq $testFile1Name -and $InputObject -eq $expectedContent } `
517-
-Scope It
518-
}
519-
520-
It 'Computed, Processed array values replace values correctly in a case insensitive deduplication.' {
521-
$config = [pscustomobject]@{
522-
Nested = [pscustomobject]@{
523-
Type = "Computed"
524-
Description = "A Test Value"
525-
Process = '@($args | ForEach-Object { $_.ToLower() } | Select-Object -Unique)'
526-
Value = @(
527-
"A",
528-
"a",
529-
"A",
530-
"a"
531-
)
532-
Targets = @(
533-
[pscustomobject]@{
534-
Name = "parValue.value"
535-
Destination = "Parameters"
536-
})
537-
}
538-
}
539-
540-
$fileContent = '{
541-
"parameters": {
542-
"parValue": {
543-
"value": []
544-
}
545-
}
546-
}'
547-
548-
$expectedContent = '{
549-
"parameters": {
550-
"parValue": {
551-
"value": [ "a" ]
552-
}
553-
}
554-
}'
555-
556-
Mock -CommandName Get-Content -ParameterFilter { $Path -eq $testFile1Name } -MockWith {
557-
$fileContent
558-
}
559-
560-
$expectedContent = Format-ExpectedResult -expectedJson $expectedContent
561-
562-
Edit-ALZConfigurationFilesInPlace -alzEnvironmentDestination '.' -configuration $config
563-
564-
Should -Invoke -CommandName Out-File `
565-
-ParameterFilter { $FilePath -eq $testFile1Name -and $InputObject -eq $expectedContent } `
566-
-Scope It
567-
}
568-
569-
It 'Computed, Processed array values replace values correctly and keep array type when only one item remains.' {
570-
$config = [pscustomobject]@{
571-
Nested = [pscustomobject]@{
572-
Type = "Computed"
573-
Description = "A Test Value"
574-
Process = '@($args | Select-Object -Unique)'
575-
Value = @(
576-
"1",
577-
"1",
578-
"1"
579-
)
580-
Targets = @(
581-
[pscustomobject]@{
582-
Name = "parValue.value"
583-
Destination = "Parameters"
584-
})
585-
}
586-
}
587-
588-
$fileContent = '{
589-
"parameters": {
590-
"parValue": {
591-
"value": []
592-
}
593-
}
594-
}'
595-
596-
$expectedContent = '{
597-
"parameters": {
598-
"parValue": {
599-
"value": [ "1" ]
600-
}
601-
}
602-
}'
603-
604-
Mock -CommandName Get-Content -ParameterFilter { $Path -eq $testFile1Name } -MockWith {
605-
$fileContent
606-
}
607-
608-
$expectedContent = Format-ExpectedResult -expectedJson $expectedContent
609-
610-
Edit-ALZConfigurationFilesInPlace -alzEnvironmentDestination '.' -configuration $config
611-
612-
Should -Invoke -CommandName Out-File `
613-
-ParameterFilter { $FilePath -eq $testFile1Name -and $InputObject -eq $expectedContent } `
614-
-Scope It
615-
}
616-
617-
It 'Computed, Processed values replace values correctly' {
618-
$config = [pscustomobject]@{
619-
Nested = [pscustomobject]@{
620-
Type = "Computed"
621-
Description = "A Test Value"
622-
Process = '($args[0] -eq "eastus") ? "eastus2" : ($args[0] -eq "eastus2") ? "eastus" : $args[0]'
623-
Value = "eastus"
624-
Targets = @(
625-
[pscustomobject]@{
626-
Name = "parValue.value"
627-
Destination = "Parameters"
628-
})
629-
}
630-
}
631-
632-
$fileContent = '{
633-
"parameters": {
634-
"parValue": {
635-
"value": "replace_me"
636-
}
637-
}
638-
}'
639-
640-
$expectedContent = '{
641-
"parameters": {
642-
"parValue": {
643-
"value": "eastus2"
644-
}
645-
}
646-
}'
647-
648-
Mock -CommandName Get-Content -ParameterFilter { $Path -eq $testFile1Name } -MockWith {
649-
$fileContent
650-
}
651-
652-
$expectedContent = Format-ExpectedResult -expectedJson $expectedContent
653-
654-
Edit-ALZConfigurationFilesInPlace -alzEnvironmentDestination '.' -configuration $config
655-
656-
Should -Invoke -CommandName Out-File `
657-
-ParameterFilter { $FilePath -eq $testFile1Name -and $InputObject -eq $expectedContent } `
658-
-Scope It
659-
}
660-
661-
It 'Computed, Processed values replace values correctly' {
662-
$config = [pscustomobject]@{
663-
Nested = [pscustomobject]@{
664-
Type = "Computed"
665-
Description = "A Test Value"
666-
Process = '($args[0] -eq "goodbye") ? "Hello" : "Goodbye"'
667-
Value = "goodbye"
668-
Targets = @(
669-
[pscustomobject]@{
670-
Name = "parValue.value"
671-
Destination = "Parameters"
672-
})
673-
}
674-
}
675-
676-
$fileContent = '{
677-
"parameters": {
678-
"parValue": {
679-
"value": "replace_me"
680-
}
681-
}
682-
}'
683-
684-
$expectedContent = '{
685-
"parameters": {
686-
"parValue": {
687-
"value": "Hello"
688-
}
689-
}
690-
}'
691-
692-
Mock -CommandName Get-Content -ParameterFilter { $Path -eq $testFile1Name } -MockWith {
693-
$fileContent
694-
}
695-
696-
$expectedContent = Format-ExpectedResult -expectedJson $expectedContent
697-
698-
Edit-ALZConfigurationFilesInPlace -alzEnvironmentDestination '.' -configuration $config
699-
700-
Should -Invoke -CommandName Out-File `
701-
-ParameterFilter { $FilePath -eq $testFile1Name -and $InputObject -eq $expectedContent } `
702-
-Scope It
703-
}
704-
705473
It 'Multiple files with file specific configuration should be changed correctly' {
706474
$defaultConfig = [pscustomobject]@{
707475
Value1 = [pscustomobject]@{
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#-------------------------------------------------------------------------
2+
Set-Location -Path $PSScriptRoot
3+
#-------------------------------------------------------------------------
4+
$ModuleName = 'ALZ'
5+
$PathToManifest = [System.IO.Path]::Combine('..', '..', '..', $ModuleName, "$ModuleName.psd1")
6+
#-------------------------------------------------------------------------
7+
if (Get-Module -Name $ModuleName -ErrorAction 'SilentlyContinue') {
8+
#if the module is already in memory, remove it
9+
Remove-Module -Name $ModuleName -Force
10+
}
11+
Import-Module $PathToManifest -Force
12+
#-------------------------------------------------------------------------
13+
14+
InModuleScope 'ALZ' {
15+
Describe 'Set-ComputedConfiguration Private Function Tests' -Tag Unit {
16+
BeforeAll {
17+
$WarningPreference = 'SilentlyContinue'
18+
$ErrorActionPreference = 'SilentlyContinue'
19+
}
20+
Context 'Set-ComputedConfiguration should update the configuration correctly' {
21+
It 'Handles Computed values correctly.' {
22+
$configuration = [pscustomobject]@{
23+
Setting1 = [pscustomobject]@{
24+
Targets = @(
25+
[pscustomobject]@{
26+
Name = "Setting1"
27+
Destination = "Environment"
28+
})
29+
Value = "Test"
30+
}
31+
Setting2 = [pscustomobject]@{
32+
Targets = @(
33+
[pscustomobject]@{
34+
Name = "Setting2"
35+
Destination = "Environment"
36+
})
37+
Type = "Computed"
38+
Value = "{%Setting1%}"
39+
}
40+
}
41+
42+
Set-ComputedConfiguration -configuration $configuration
43+
$configuration.Setting2.Value | Should -BeExactly "Test"
44+
}
45+
46+
It 'Computed, Processed array values replace values correctly' {
47+
$configuration = [pscustomobject]@{
48+
Nested = [pscustomobject]@{
49+
Type = "Computed"
50+
Description = "A Test Value"
51+
Process = '@($args | Select-Object -Unique)'
52+
Value = @(
53+
"1",
54+
"1",
55+
"3"
56+
)
57+
Targets = @(
58+
[pscustomobject]@{
59+
Name = "parValue.value"
60+
Destination = "Parameters"
61+
})
62+
}
63+
}
64+
65+
Set-ComputedConfiguration -configuration $configuration
66+
$configuration.Nested.Value | Should -BeExactly @("1", "3")
67+
}
68+
69+
It 'Computed, Processed array values replace values correctly in a case insensitive deduplication.' {
70+
$configuration = [pscustomobject]@{
71+
Nested = [pscustomobject]@{
72+
Type = "Computed"
73+
Description = "A Test Value"
74+
Process = '@($args | ForEach-Object { $_.ToLower() } | Select-Object -Unique)'
75+
Value = @(
76+
"A",
77+
"a",
78+
"A",
79+
"a"
80+
)
81+
Targets = @(
82+
[pscustomobject]@{
83+
Name = "parValue.value"
84+
Destination = "Parameters"
85+
})
86+
}
87+
}
88+
89+
Set-ComputedConfiguration -configuration $configuration
90+
$configuration.Nested.Value | Should -BeExactly @("a")
91+
}
92+
93+
It 'Computed, Processed array values replace values correctly and keep array type when only one item remains.' {
94+
$configuration = [pscustomobject]@{
95+
Nested = [pscustomobject]@{
96+
Type = "Computed"
97+
Description = "A Test Value"
98+
Process = '@($args | Select-Object -Unique)'
99+
Value = @(
100+
"1",
101+
"1",
102+
"1"
103+
)
104+
Targets = @(
105+
[pscustomobject]@{
106+
Name = "parValue.value"
107+
Destination = "Parameters"
108+
})
109+
}
110+
}
111+
112+
Set-ComputedConfiguration -configuration $configuration
113+
$configuration.Nested.Value | Should -BeExactly @("1")
114+
}
115+
116+
It 'Computed, Processed values replace values correctly' {
117+
$configuration = [pscustomobject]@{
118+
Nested = [pscustomobject]@{
119+
Type = "Computed"
120+
Description = "A Test Value"
121+
Process = '($args[0] -eq "eastus") ? "eastus2" : ($args[0] -eq "eastus2") ? "eastus" : $args[0]'
122+
Value = "eastus"
123+
Targets = @(
124+
[pscustomobject]@{
125+
Name = "parValue.value"
126+
Destination = "Parameters"
127+
})
128+
}
129+
}
130+
131+
Set-ComputedConfiguration -configuration $configuration
132+
$configuration.Nested.Value | Should -BeExactly "eastus2"
133+
}
134+
135+
It 'Computed, Processed values replace values correctly' {
136+
$configuration = [pscustomobject]@{
137+
Nested = [pscustomobject]@{
138+
Type = "Computed"
139+
Description = "A Test Value"
140+
Process = '($args[0] -eq "goodbye") ? "Hello" : "Goodbye"'
141+
Value = "goodbye"
142+
Targets = @(
143+
[pscustomobject]@{
144+
Name = "parValue.value"
145+
Destination = "Parameters"
146+
})
147+
}
148+
}
149+
150+
Set-ComputedConfiguration -configuration $configuration
151+
$configuration.Nested.Value | Should -BeExactly "Hello"
152+
}
153+
}
154+
}
155+
}

0 commit comments

Comments
 (0)
Please sign in to comment.