From 63c6bce3ccf85529c15bfd40256f5ccd9a8b4030 Mon Sep 17 00:00:00 2001 From: Pier-Paolo Mammi Date: Thu, 2 Jul 2026 16:50:33 +0200 Subject: [PATCH] refactor on pwsh script - take account of pwsh quirks... - add missing headers generation - add missing rendering of variables - make 99.9% equal to nodejs script --- scripts/generate-http-requests.ps1 | 274 ++++++++++++++++++----------- 1 file changed, 173 insertions(+), 101 deletions(-) diff --git a/scripts/generate-http-requests.ps1 b/scripts/generate-http-requests.ps1 index c463f4a..b7ee3e3 100644 --- a/scripts/generate-http-requests.ps1 +++ b/scripts/generate-http-requests.ps1 @@ -1,16 +1,19 @@ #requires -Modules powershell-yaml param( - [string]$StartDir = (Split-Path -Parent $MyInvocation.MyCommand.Path) + [string]$StartDir = (Split-Path -LiteralPath $MyInvocation.MyCommand.Path) ) +$DEFAULT_ENV_VAR_VALUE = 'EDIT_VALUE_HERE' +$VARIABLE_NAME_VALUE_SEPARATOR = '=' + function Find-WorkspaceRoot($startDir) { $current = $startDir while ($true) { if (Test-Path -LiteralPath (Join-Path $current 'workspace.yml')) { return $current } - $parent = Split-Path -Parent $current + $parent = Split-Path -LiteralPath $current if ($parent -eq $current) { throw "workspace.yml not found from the provided start directory" } @@ -77,7 +80,6 @@ function Parse-Workspace($workspacePath) { function Sanitize-VarName($name) { $sanitized = ([string]$name).Trim() -replace '[{}]', '' -replace '[^A-Za-z0-9_]', '_' -replace '^([0-9])', '_$1' - if ([string]::IsNullOrWhiteSpace($sanitized)) { return 'value' } @@ -266,6 +268,26 @@ function Merge-RequestConfig ($base, $updates) { $merged.Remove('auth') } + if ($updates.headers -is [System.Collections.IEnumerable]) { + $headers = @() + if ($base.headers -is [System.Collections.IEnumerable]) { + $headers = @($base.headers) + } + # Mappa per nome + $byName = @{} + foreach ($header in $headers) { + if ($header -and $header.name) { + $byName[[string]$header.name] = $header + } + } + foreach ($header in $updates.headers) { + if ($header -and $header.name) { + $byName[[string]$header.name] = $header + } + } + $merged.headers = $byName.Values + } + if ($updates.variables -is [System.Collections.IEnumerable]) { $variables = @() if ($base.variables -is [System.Collections.IEnumerable]) { @@ -290,14 +312,16 @@ function Merge-RequestConfig ($base, $updates) { } function Get-RequestConfigForFile ($yamlFile, $sourceDir) { - $resolved = @() - $seenFiles = New-Object System.Collections.Generic.HashSet[string] + $resolved = [ref] @() + $seenFiles = [ref] (New-Object System.Collections.Generic.HashSet[string]) + + $config = [ref] @{} function Add-File ($FilePath) { - if (-not $FilePath -or $seenFiles.Contains($FilePath)) { + if (-not $FilePath -or $seenFiles.Value.Contains($FilePath)) { return } - $seenFiles.Add($FilePath) + [void]$seenFiles.Value.Add($FilePath) if (-not (Test-Path $FilePath)) { return } @@ -313,28 +337,28 @@ function Get-RequestConfigForFile ($yamlFile, $sourceDir) { } # Caso 2: parsed è un oggetto e contiene auth/variables elseif ($parsed -is [psobject] -or $parsed -is [hashtable]) { - $config = @{} + # $config = @{} # auth if ($parsed.ContainsKey('auth')) { - $config.auth = $parsed.auth + $config.Value.auth = $parsed.auth } elseif ($parsed.http -and ($parsed.http -is [psobject] -or $parsed.http -is [hashtable]) -and $parsed.http.ContainsKey('auth')) { - $config.auth = $parsed.http.auth + $config.Value.auth = $parsed.http.auth } # variables if ($parsed.variables -is [System.Collections.IEnumerable]) { - $config.variables = $parsed.variables + $config.Value.variables = $parsed.variables } - if ($config.Count -gt 0) { - $requestConfig = $config + if ($config.Value.Count -gt 0) { + $requestConfig = $config.Value } } if ($null -ne $requestConfig) { - $resolved += $requestConfig + $resolved.Value += $requestConfig } elseif ((Resolve-Path $FilePath).Path -eq (Resolve-Path $yamlFile).Path) { - $resolved += @{} + $resolved.Value += @{} } } catch { @@ -344,13 +368,14 @@ function Get-RequestConfigForFile ($yamlFile, $sourceDir) { # Costruisci la catena delle directory $dirChain = @() - $currentDir = Split-Path -Parent $yamlFile + $currentDir = Split-Path -LiteralPath $yamlFile + while ($true) { $dirChain = ,$currentDir + $dirChain if ($currentDir -eq $sourceDir) { break } - $parentDir = Split-Path -Parent $currentDir + $parentDir = Split-Path -LiteralPath $currentDir if ($parentDir -eq $currentDir) { break } @@ -366,74 +391,105 @@ function Get-RequestConfigForFile ($yamlFile, $sourceDir) { Add-File $yamlFile $result = @{} - foreach ($config in $resolved) { + foreach ($config in $resolved.Value) { $result = Merge-RequestConfig $result $config } return $result } -function Build-RequestContent ( - $request, - $requestName, - $requestConfig = @{}, - [System.Collections.Generic.HashSet[string]]$dotenvVariables = $(New-Object System.Collections.Generic.HashSet[string]) - ) { - +function Build-RequestContent ($request, $requestName, $requestConfig = @{}, [ref][System.Collections.Generic.HashSet[string]]$dotenvVariables = $(New-Object System.Collections.Generic.HashSet[string])) { $lines = @() - $variableDefinitions = @() - $commentedVariableDefinitions = @() - $parameterVariableDefinitions = @() - $seenVariables = New-Object System.Collections.Generic.HashSet[string] + $variableDefinitions = [ref] @() + $commentedVariableDefinitions = [ref] @() + $parameterVariableDefinitions = [ref] @() + $seenVariables = [ref] (New-Object System.Collections.Generic.HashSet[string]) function Add-Variable ($Name, $Value) { if (-not $Name) { return } $normalized = ([string]$Name).Trim() if (-not $normalized) { return } - if ($seenVariables.Contains($normalized)) { return } - if ($dotenvVariables.Contains($normalized)) { return } - $seenVariables.Add($normalized) - $variableDefinitions += @{ name=$normalized; value=$Value } + if ($seenVariables.Value.Contains($normalized)) { return } + if ($dotenvVariables.Value.Contains($normalized)) { return } + [void]$seenVariables.Value.Add($normalized) + $variableDefinitions.Value += @{ name=$normalized; value=$Value } } function Add-ParameterVariable ($Name, $Value) { if (-not $Name) { return } $normalized = ([string]$Name).Trim() if (-not $normalized) { return } - if ($seenVariables.Contains($normalized)) { return } - if ($dotenvVariables.Contains($normalized)) { return } - $seenVariables.Add($normalized) - $parameterVariableDefinitions += @{ name=$normalized; value=$Value } + if ($seenVariables.Value.Contains($normalized)) { return } + if ($dotenvVariables.Value.Contains($normalized)) { return } + [void]$seenVariables.Value.Add($normalized) + $parameterVariableDefinitions.Value += @{ name=$normalized; value=$Value } } function Add-CommentedVariable ($Name, $Value) { if (-not $Name) { return } $normalized = ([string]$Name).Trim() if (-not $normalized) { return } - $commentedVariableDefinitions += @{ name=$normalized; value=$Value } + $commentedVariableDefinitions.Value += @{ name=$normalized; value=$Value } } - function Add-ReferencedVariables ($Value, $FallbackValue = 'YOUR_VALUE_HERE') { + function Add-ReferencedVariables ($Value, $FallbackValue = $DEFAULT_ENV_VAR_VALUE) { foreach ($placeholder in Collect-Placeholders ([string]$Value)) { Add-Variable $placeholder $FallbackValue } } - function RenderJsonValue ($Value) { - if ($Value -is [string]) { - return Render-Value $Value - } - elseif ($Value -is [System.Collections.IEnumerable]) { - return @($Value | ForEach-Object { RenderJsonValue $_ }) - } - elseif ($Value -is [psobject] -or $Value -is [hashtable]) { - $result = @{} - foreach ($key in $Value.Keys) { - $result[$key] = RenderJsonValue $Value[$key] + function Convert-JsonStructure { + param( + [Parameter(Mandatory)] + $Value + ) + + # Caso 1: array + if ($Value -is [System.Collections.IEnumerable] -and $Value -isnot [string]) { + $result = [System.Collections.IEnumerable]@() + foreach ($item in $Value) { + $innerValue = $null + if ($null -ne $item) { $innerValue = Convert-JsonStructure $item } + $result += $innerValue } return $result } - return $Value + + # Caso 2: PSCustomObject (JSON convertito) + if ($Value -is [PSObject]) { + $dict = [System.Collections.Specialized.OrderedDictionary]::new() + foreach ($prop in $Value.PSObject.Properties) { + if ($null -ne $prop.Value) { + if ($prop.Value -is [System.Collections.IEnumerable] -and $prop.Value.Length -eq 0 -and $prop.Value -isnot [string]) { + $dict[$prop.Name] = [System.Collections.IEnumerable]@() + } + else + { + $dict[$prop.Name] = Convert-JsonStructure $prop.Value + } + } + else { + $dict[$prop.Name] = $null + } + } + return $dict + } + + # Caso 3: hashtable puro + if ($Value -is [hashtable]) { + $dict = [System.Collections.Specialized.OrderedDictionary]::new() + foreach ($key in $Value.Keys) { + $innerValue = $null + if ($null -ne $key -and $null -ne $Value[$key]) { $innerValue = Convert-JsonStructure $Value[$key] } + $dict[$key] = $innerValue + } + return $dict + } + + # Caso 4: valore primitivo → applico la tua funzione + $primitiveValue = $Value + if ($null -ne $Value) { $primitiveValue = Render-Value $Value } + return $primitiveValue } function Add-ParameterVariables ($Name, $Value) { Add-ParameterVariable $Name $Value } @@ -443,14 +499,17 @@ function Build-RequestContent ( function Render-Value ($Value) { if ($Value -isnot [string]) { return $Value } - return ($Value -replace '\{\{([^{}]+)\}\}', { - param($match,$inner) + $regex = [regex]'\{\{([^{}]+)\}\}' + $regex.Replace($Value, { param($match) + $inner = $match.Groups[1].value $placeholder = Parse-PlaceholderContent $inner - if ($placeholder.isDotenv) { return $match } - if ($placeholder.name -and $dotenvVariables.Contains($placeholder.name)) { - return "{{$dotenv $($placeholder.name)}}" + if ($placeholder.isDotenv) { + return $match.Value } - return $match + if ($placeholder.name -and $dotenvVariables.Value.Contains($placeholder.name)) { + return "{{`$dotenv $($placeholder.name)}}" + } + return $match.Value }) } @@ -462,7 +521,7 @@ function Build-RequestContent ( $configVariables = $requestConfig.variables } foreach ($variable in $configVariables) { - if ($variable -and $variable.name) { + if ($null -ne $variable -and $variable.name) { Add-Variable $variable.name $variable.value } } @@ -482,12 +541,12 @@ function Build-RequestContent ( # Headers # ------------------------- $queryParams = @() - $headers = @() + $headers = [ref] @() function Add-Header ($Name, $Value) { if (-not $Name) { return } Add-ReferencedVariables ($Value ?? '') - $headers += @{ + $headers.Value += @{ name = ([string]$Name).Trim() value = Render-Value ($Value ?? '') } @@ -522,7 +581,7 @@ function Build-RequestContent ( Add-ReferencedVariables $value if ($type -eq 'header') { - $headers += @{ name=$name; value=(Render-Value $value) } + $headers.Value += @{ name=$name; value=(Render-Value $value) } } else { $queryParams += @{ name=$name; value="{{$name}}" } @@ -547,7 +606,7 @@ function Build-RequestContent ( Add-Header 'Authorization' ("Basic " + (Render-Value $username) + ":" + (Render-Value $password)) } default { - $headers += @{ + $headers.Value += @{ name = "UNKNOWN_$($requestConfig.auth.type)" value = "Basic $($requestConfig.auth.token)" } @@ -558,9 +617,9 @@ function Build-RequestContent ( # ------------------------- # Commented variables # ------------------------- - if ($commentedVariableDefinitions.Count -gt 0) { + if ($commentedVariableDefinitions.Value.Count -gt 0) { $lines += "# Other variables for $requestName" - foreach ($variable in $commentedVariableDefinitions) { + foreach ($variable in $commentedVariableDefinitions.Value | Sort-Object -Property name, value) { $lines += "# @$(Sanitize-VarName $variable.name)$VARIABLE_NAME_VALUE_SEPARATOR$(Format-VariableValue $variable.value @{ renderValue = { param($v) Render-Value $v } })" } } @@ -568,9 +627,9 @@ function Build-RequestContent ( # ------------------------- # Parameter variables # ------------------------- - if ($parameterVariableDefinitions.Count -gt 0) { + if ($parameterVariableDefinitions.Value.Count -gt 0) { $lines += "# Parameter variables for $requestName" - foreach ($variable in $parameterVariableDefinitions) { + foreach ($variable in $parameterVariableDefinitions.Value | Sort-Object -Property name, value) { $lines += "@$(Sanitize-VarName $variable.name)$VARIABLE_NAME_VALUE_SEPARATOR$(Format-VariableValue $variable.value @{ renderValue = { param($v) Render-Value $v } })" } } @@ -590,10 +649,10 @@ function Build-RequestContent ( $requestBody = Render-Value $jsonData } elseif ($jsonData -is [System.Collections.IEnumerable]) { - $requestBody = (ConvertTo-Json (RenderJsonValue $jsonData) -Depth 20) + $requestBody = (ConvertTo-Json (Convert-JsonStructure $jsonData) -Depth 20) } elseif ($jsonData -is [psobject] -or $jsonData -is [hashtable]) { - $requestBody = (ConvertTo-Json (RenderJsonValue $jsonData) -Depth 20) + $requestBody = (ConvertTo-Json (Convert-JsonStructure $jsonData) -Depth 20) } Add-Header 'Content-Type' 'application/json' @@ -613,9 +672,9 @@ function Build-RequestContent ( # ------------------------- # Variables # ------------------------- - if ($variableDefinitions.Count -gt 0) { + if ($variableDefinitions.Value.Count -gt 0) { $lines += "# Variables for $requestName" - foreach ($variable in $variableDefinitions) { + foreach ($variable in $variableDefinitions.Value | Sort-Object -Property name, value) { $lines += "@$(Sanitize-VarName $variable.name)$VARIABLE_NAME_VALUE_SEPARATOR$(Format-VariableValue $variable.value @{ renderValue = { param($v) Render-Value $v } })" } } @@ -635,7 +694,7 @@ function Build-RequestContent ( $lines += '' $lines += "$method $requestUrl" - foreach ($header in $headers) { + foreach ($header in $headers.Value) { $lines += "$($header.name): $($header.value)" } @@ -655,7 +714,7 @@ function Ensure-Dir($path) { function Walk-YamlFiles($rootDir) { Get-ChildItem -LiteralPath $rootDir -Recurse -File -Include *.yml, *.yaml | - Where-Object { $_.Name -notmatch '^\.|node_modules' } | + Where-Object { $_.Name -notmatch '^\.|node_modules|folder\.yml' } | Select-Object -ExpandProperty FullName } @@ -677,18 +736,22 @@ function Clean-Folder($dir) { } } -function Get-DotenvVariablesForTargetDir ($TargetDir, $OutputRoot, $DotenvVariablesByTarget) { - $variables = New-Object System.Collections.Generic.HashSet[string] +function Get-DotenvVariablesForTargetDir ($TargetDir, $OutputRoot, $DotEnvVarsByTarget) { + $variables = $(New-Object System.Collections.Generic.HashSet[string]) + $variables.Clear() $currentDir = $TargetDir while ($true) { - if ($DotenvVariablesByTarget.ContainsKey($currentDir)) { - foreach ($variable in $DotenvVariablesByTarget[$currentDir]) { - $variables.Add($variable) | Out-Null + if ($DotEnvVarsByTarget.ContainsKey($currentDir)) { + foreach ($variable in $DotEnvVarsByTarget[$currentDir]) { + [void]$variables.Add($variable) } } - $parentDir = Split-Path -Parent $currentDir + if (-not $currentDir) { + Write-warning "Current directory is null while searching for dotenv variables for target dir: $TargetDir" + } + $parentDir = Split-Path -LiteralPath $currentDir if ($currentDir -eq $OutputRoot -or $parentDir -eq $currentDir) { break } @@ -700,17 +763,17 @@ function Get-DotenvVariablesForTargetDir ($TargetDir, $OutputRoot, $DotenvVariab } function Write-EnvironmentTemplates ($sourceDir, $outputRoot) { - $targets = @() - $dotenvVariablesByTarget = @{} # Hashtable: targetDir → HashSet + $targets = [ref] @() + $localDotEnvVarsByTarget = @{} function Visit ([string]$CurrentDir) { $entries = Get-ChildItem -LiteralPath $CurrentDir -Force - $hasEnvironmentsDir = $entries | Where-Object { - $_.PSIsContainer -and $_.Name -eq 'environments' - } + $hasEnvironmentsDir = ($entries | Where-Object { + $_.PSIsContainer -and $_.Name -eq 'environments' -and (Get-ChildItem -LiteralPath $_.FullName -Force | Where-Object { -not $_.PSIsContainer -and $_.Name -match '\.ya?ml$' }).Count -gt 0 + }).Count -gt 0 if ($hasEnvironmentsDir) { - $targets += $CurrentDir + $targets.Value += $CurrentDir } foreach ($entry in $entries) { @@ -724,7 +787,7 @@ function Write-EnvironmentTemplates ($sourceDir, $outputRoot) { Visit $sourceDir - foreach ($dir in $targets) { + foreach ($dir in $targets.Value) { $relativeDir = [System.IO.Path]::GetRelativePath($sourceDir, $dir) if ($relativeDir -and $relativeDir -ne '.') { $targetDir = Join-Path $outputRoot $relativeDir @@ -749,7 +812,7 @@ function Write-EnvironmentTemplates ($sourceDir, $outputRoot) { $seenNames = New-Object System.Collections.Generic.HashSet[string] foreach ($envFile in $envFiles) { - $parsed = Parse-Yaml $envFile + $parsed = Parse-YamlFile $envFile if (-not $parsed) { continue } $variables = @() @@ -764,7 +827,7 @@ function Write-EnvironmentTemplates ($sourceDir, $outputRoot) { if (-not $name) { continue } if ($seenNames.Contains($name)) { continue } - $seenNames.Add($name) + [void]$seenNames.Add($name) # Ma porc $variableNames += $name } } @@ -778,16 +841,16 @@ function Write-EnvironmentTemplates ($sourceDir, $outputRoot) { } $templatePath = Join-Path $targetDir '.env.template' - Set-Content -Path $templatePath -Value $templateContent -Encoding UTF8 + Set-Content -LiteralPath $templatePath -Value $templateContent -Encoding UTF8 -NoNewLine - $dotenvVariablesByTarget[$targetDir] = $seenNames + $localDotEnvVarsByTarget[$targetDir] = $seenNames } - return $dotenvVariablesByTarget + return $localDotEnvVarsByTarget } function Write-JsFiles ($sourceDir, $outputRoot) { - $targets = @() + $targets = [ref] @() function Visit ($CurrentDir) { $entries = Get-ChildItem -LiteralPath $CurrentDir -Force @@ -797,7 +860,7 @@ function Write-JsFiles ($sourceDir, $outputRoot) { } if ($hasJsFiles) { - $targets += $CurrentDir + $targets.Value += $CurrentDir } # Visita ricorsivamente le sottodirectory @@ -812,7 +875,7 @@ function Write-JsFiles ($sourceDir, $outputRoot) { Visit $sourceDir - foreach ($dir in $targets) { + foreach ($dir in $targets.Value) { $relativeDir = [System.IO.Path]::GetRelativePath($sourceDir, $dir) if ($relativeDir -and $relativeDir -ne '.') { $targetDir = Join-Path $outputRoot $relativeDir @@ -842,7 +905,7 @@ function Write-JsFiles ($sourceDir, $outputRoot) { } } - return $targets + return $targets.Value } function Invoke-Main { @@ -859,7 +922,7 @@ function Invoke-Main { throw "No collections found in workspace.yml" } - $outputBaseRoot = Join-Path $workspaceRoot 'autogen/httpyac' + $outputBaseRoot = Join-Path $workspaceRoot 'autogen/httpyac_ps1' Clean-Folder $outputBaseRoot foreach ($collection in $collections) { @@ -880,7 +943,8 @@ function Invoke-Main { $writtenJsFiles = Write-JsFiles $sourceDir $outputRoot # dotenv templates - $dotenvVariablesByTarget = Write-EnvironmentTemplates $sourceDir $outputRoot + $myDotEnvVarsByTarget = @{} + $myDotEnvVarsByTarget = (Write-EnvironmentTemplates $sourceDir $outputRoot) # YAML files $yamlFiles = Walk-YamlFiles $sourceDir @@ -901,19 +965,27 @@ function Invoke-Main { $relativePath = [System.IO.Path]::GetRelativePath($sourceDir, $yamlFile) $parsedPath = [System.IO.Path]::GetFileNameWithoutExtension($relativePath) - $parsedDir = Split-Path $relativePath -Parent + $parsedDir = Split-Path -LiteralPath $relativePath - $targetDir = Join-Path $outputRoot $parsedDir + if ($parsedDir -and $parsedDir -ne '') { + $targetDir = Join-Path $outputRoot $parsedDir + } + else { + $targetDir = $outputRoot + } Ensure-Dir $targetDir $requestConfig = Get-RequestConfigForFile $yamlFile $sourceDir $outputFile = Join-Path $targetDir ("$parsedPath.http") - $dotenvVariables = Get-DotenvVariablesForTargetDir $targetDir $outputRoot $dotenvVariablesByTarget + $myDotenvVariables = Get-DotenvVariablesForTargetDir $targetDir $outputRoot $myDotEnvVarsByTarget + if (-not $myDotenvVariables) { + $myDotenvVariables = $(New-Object System.Collections.Generic.HashSet[string]) + } - $requestContent = Build-RequestContent $httpBlock $requestName $requestConfig $dotenvVariables + $requestContent = Build-RequestContent $httpBlock $requestName $requestConfig ([ref]$myDotenvVariables) - Set-Content -Path $outputFile -Value ($requestContent + "`n") -Encoding UTF8 + Set-Content -LiteralPath $outputFile -Value $requestContent -Encoding UTF8 $processed++ }